gmail_gm_raw 0.4.3

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/TODO.md ADDED
@@ -0,0 +1,12 @@
1
+ # TODO
2
+
3
+ * Specs for xoauth
4
+ * Specs for message operations
5
+ * Specs for filters in mailbox
6
+ * Specs for current [Gmail IMAP extensions](http://code.google.com/apis/gmail/imap/)
7
+ * Non-English accounts
8
+ * Further implement [Gmail IMAP extensions](http://code.google.com/apis/gmail/imap/)
9
+ * add custom search (X-GM-RAW)
10
+ * add thread id (X-GM-THRID)
11
+ * Export/Import to mbox format
12
+ * Integration with [contacts](http://rubygems.org/gems/contacts) gem
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ $:.push File.expand_path('../lib', __FILE__)
4
+ require 'gmail/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "gmail_gm_raw"
8
+ s.summary = "A Rubyesque interface to Gmail, with all the tools you will need."
9
+ s.description = "A Rubyesque interface to Gmail, with all the tools you will need.
10
+ Search, read and send multipart emails; archive, mark as read/unread,
11
+ delete emails; and manage labels.
12
+ "
13
+ s.version = Gmail::VERSION
14
+ s.platform = Gem::Platform::RUBY
15
+ s.authors = ["Chris Kowalik"]
16
+ s.email = ["chris@nu7hat.ch"]
17
+ s.homepage = "http://github.com/nu7hatch/gmail"
18
+
19
+ # runtime dependencies
20
+ s.add_dependency "mime", ">= 0.1"
21
+ s.add_dependency "mail", ">= 2.2.1"
22
+ s.add_dependency "gmail_xoauth", ">= 0.3.0"
23
+
24
+ # development dependencies
25
+ s.add_development_dependency "rake"
26
+ s.add_development_dependency "rspec", "~> 2.0"
27
+ s.add_development_dependency "mocha", ">= 0.9"
28
+ s.add_development_dependency "gem-release"
29
+
30
+ s.files = `git ls-files`.split("\n")
31
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
32
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
33
+ s.require_paths = ["lib"]
34
+ end
@@ -0,0 +1,65 @@
1
+ require 'net/imap'
2
+ require 'net/smtp'
3
+ require 'mail'
4
+ require 'date'
5
+ require 'time'
6
+
7
+ if RUBY_VERSION < "1.8.7"
8
+ require "smtp_tls"
9
+ end
10
+
11
+ class Object
12
+ def to_imap_date
13
+ Date.parse(to_s).strftime("%d-%B-%Y")
14
+ end
15
+ end
16
+
17
+ module Gmail
18
+ autoload :Version, "gmail/version"
19
+ autoload :Client, "gmail/client"
20
+ autoload :Labels, "gmail/labels"
21
+ autoload :Mailbox, "gmail/mailbox"
22
+ autoload :Message, "gmail/message"
23
+
24
+ class << self
25
+ # Creates new Gmail connection using given authorization options.
26
+ #
27
+ # ==== Examples
28
+ #
29
+ # Gmail.new(:plain, "foo@gmail.com", "password")
30
+ # Gmail.new(:xoauth, "foo@gmail.com",
31
+ # :consumer_key => "",
32
+ # :consumer_secret => "",
33
+ # :token => "",
34
+ # :secret => "")
35
+ #
36
+ # To use plain authentication mehod you can also call:
37
+ #
38
+ # Gmail.new("foo@gmail.com", "password")
39
+ #
40
+ # You can also use block-style call:
41
+ #
42
+ # Gmail.new("foo@gmail.com", "password") do |client|
43
+ # # ...
44
+ # end
45
+ #
46
+
47
+ ['', '!'].each { |kind|
48
+ define_method("new#{kind}") do |*args, &block| # def new(*args, &block)
49
+ args.unshift(:plain) unless args.first.is_a?(Symbol) # args.unshift(:plain) unless args.first.is_a?(Symbol)
50
+ client = Gmail::Client.new(*args) # client = Gmail::Client.new(*args)
51
+ client.send("connect#{kind}") and client.send("login#{kind}") # client.connect and client.login
52
+ #
53
+ if block_given? # if block_given?
54
+ yield client # yield client
55
+ client.logout # client.logout
56
+ end # end
57
+ #
58
+ client # client
59
+ end # end
60
+ }
61
+
62
+ alias :connect :new
63
+ alias :connect! :new!
64
+ end # << self
65
+ end # Gmail
@@ -0,0 +1,30 @@
1
+ module Gmail
2
+ module Client
3
+ # Raised when connection with GMail IMAP service couldn't be established.
4
+ class ConnectionError < SocketError; end
5
+ # Raised when given username or password are invalid.
6
+ class AuthorizationError < Net::IMAP::NoResponseError; end
7
+ # Raised when delivered email is invalid.
8
+ class DeliveryError < ArgumentError; end
9
+ # Raised when given client is not registered
10
+ class UnknownClient < ArgumentError; end
11
+
12
+ def self.register(name, klass)
13
+ @clients ||= {}
14
+ @clients[name] = klass
15
+ end
16
+
17
+ def self.new(name, *args)
18
+ if client = @clients[name]
19
+ client.new(*args)
20
+ else
21
+ raise UnknownClient, "No such client: #{name}"
22
+ end
23
+ end
24
+
25
+ require 'gmail/client/imap_extensions'
26
+ require 'gmail/client/base'
27
+ require 'gmail/client/plain'
28
+ require 'gmail/client/xoauth'
29
+ end # Client
30
+ end # Gmail
@@ -0,0 +1,225 @@
1
+ require 'thread'
2
+
3
+ module Gmail
4
+ module Client
5
+ class Base
6
+ # GMail IMAP defaults
7
+ GMAIL_IMAP_HOST = 'imap.gmail.com'
8
+ GMAIL_IMAP_PORT = 993
9
+
10
+ # GMail SMTP defaults
11
+ GMAIL_SMTP_HOST = "smtp.gmail.com"
12
+ GMAIL_SMTP_PORT = 587
13
+
14
+ attr_reader :username
15
+ attr_reader :options
16
+
17
+ def initialize(username, options={})
18
+ defaults = {}
19
+ @username = fill_username(username)
20
+ @options = defaults.merge(options)
21
+ @mailbox_mutex = Mutex.new
22
+ end
23
+
24
+ # Connect to gmail service.
25
+ def connect(raise_errors=false)
26
+ @imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
27
+ GmailImapExtensions.patch_net_imap_response_parser
28
+ @imap
29
+ rescue SocketError
30
+ raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
31
+ end
32
+
33
+ # This version of connect will raise error on failure...
34
+ def connect!
35
+ connect(true)
36
+ end
37
+
38
+ # Return current connection. Log in automaticaly to specified account if
39
+ # it is necessary.
40
+ def connection
41
+ login and at_exit { logout } unless logged_in?
42
+ @imap
43
+ end
44
+ alias :conn :connection
45
+
46
+ # Login to specified account.
47
+ def login(*args)
48
+ raise NotImplementedError, "The `#{self.class.name}#login` method is not implemented."
49
+ end
50
+ alias :sign_in :login
51
+
52
+ # This version of login will raise error on failure...
53
+ def login!
54
+ login(true)
55
+ end
56
+ alias :sign_in! :login!
57
+
58
+ # Returns +true+ when you are logged in to specified account.
59
+ def logged_in?
60
+ !!@logged_in
61
+ end
62
+ alias :signed_in? :logged_in?
63
+
64
+ # Logout from GMail service.
65
+ def logout
66
+ @imap && logged_in? and @imap.logout
67
+ ensure
68
+ @logged_in = false
69
+ end
70
+ alias :sign_out :logout
71
+
72
+ # Return labels object, which helps you with managing your GMail labels.
73
+ # See <tt>Gmail::Labels</tt> for details.
74
+ def labels
75
+ @labels ||= Labels.new(conn)
76
+ end
77
+
78
+ # Compose new e-mail.
79
+ #
80
+ # ==== Examples
81
+ #
82
+ # mail = gmail.compose
83
+ # mail.from "test@gmail.org"
84
+ # mail.to "friend@gmail.com"
85
+ #
86
+ # ... or block style:
87
+ #
88
+ # mail = gmail.compose do
89
+ # from "test@gmail.org"
90
+ # to "friend@gmail.com"
91
+ # subject "Hello!"
92
+ # body "Hello my friend! long time..."
93
+ # end
94
+ #
95
+ # Now you can deliver your mail:
96
+ #
97
+ # gmail.deliver(mail)
98
+ def compose(mail=nil, &block)
99
+ if block_given?
100
+ mail = Mail.new(&block)
101
+ elsif !mail
102
+ mail = Mail.new
103
+ end
104
+
105
+ mail.delivery_method(*smtp_settings)
106
+ mail.from = username unless mail.from
107
+ mail
108
+ end
109
+ alias :message :compose
110
+
111
+ # Compose (optionaly) and send given email.
112
+ #
113
+ # ==== Examples
114
+ #
115
+ # gmail.deliver do
116
+ # to "friend@gmail.com"
117
+ # subject "Hello friend!"
118
+ # body "Hi! How are you?"
119
+ # end
120
+ #
121
+ # ... or with already created message:
122
+ #
123
+ # mail = Mail.new { ... }
124
+ # gmail.deliver(mail)
125
+ #
126
+ # mail = gmail.compose { ... }
127
+ # gmail.deliver(mail)
128
+ def deliver(mail=nil, raise_errors=false, &block)
129
+ mail = compose(mail, &block) if block_given?
130
+ mail.deliver!
131
+ rescue Object => ex
132
+ raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
133
+ end
134
+
135
+ # This version of deliver will raise error on failure...
136
+ def deliver!(mail=nil, &block)
137
+ deliver(mail, true, &block)
138
+ end
139
+
140
+ # Do something with given mailbox or within it context.
141
+ #
142
+ # ==== Examples
143
+ #
144
+ # mailbox = gmail.mailbox("INBOX")
145
+ # mailbox.emails(:all)
146
+ # mailbox.count(:unread, :before => Time.now-(20*24*3600))
147
+ #
148
+ # ... or block style:
149
+ #
150
+ # gmail.label("Work") do |mailbox|
151
+ # mailbox.emails(:unread)
152
+ # mailbox.count(:all)
153
+ # ...
154
+ # end
155
+ def mailbox(name, &block)
156
+ @mailbox_mutex.synchronize do
157
+ name = name.to_s
158
+ mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
159
+ switch_to_mailbox(name) if @current_mailbox != name
160
+
161
+ if block_given?
162
+ mailbox_stack << @current_mailbox
163
+ result = block.arity == 1 ? block.call(mailbox) : block.call
164
+ mailbox_stack.pop
165
+ switch_to_mailbox(mailbox_stack.last)
166
+ return result
167
+ end
168
+
169
+ return mailbox
170
+ end
171
+ end
172
+ alias :in_mailbox :mailbox
173
+ alias :in_label :mailbox
174
+ alias :label :mailbox
175
+
176
+ # Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
177
+ # for details.
178
+ def inbox
179
+ mailbox("INBOX")
180
+ end
181
+
182
+ def mailboxes
183
+ @mailboxes ||= {}
184
+ end
185
+
186
+ def inspect
187
+ "#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
188
+ end
189
+
190
+ def fill_username(username)
191
+ username =~ /@/ ? username : "#{username}@gmail.com"
192
+ end
193
+
194
+ def mail_domain
195
+ username.split('@').last
196
+ end
197
+
198
+ private
199
+
200
+ def switch_to_mailbox(mailbox)
201
+ if mailbox
202
+ mailbox = Net::IMAP.encode_utf7(mailbox)
203
+ conn.select(mailbox)
204
+ end
205
+ @current_mailbox = mailbox
206
+ end
207
+
208
+ def mailbox_stack
209
+ @mailbox_stack ||= []
210
+ end
211
+
212
+ def smtp_settings
213
+ [:smtp, {
214
+ :address => GMAIL_SMTP_HOST,
215
+ :port => GMAIL_SMTP_PORT,
216
+ :domain => mail_domain,
217
+ :user_name => username,
218
+ :password => password,
219
+ :authentication => 'plain',
220
+ :enable_starttls_auto => true
221
+ }]
222
+ end
223
+ end # Base
224
+ end # Client
225
+ end # Gmail
@@ -0,0 +1,54 @@
1
+ # Taken from https://github.com/oxos/gmail-oauth-thread-stats/blob/master/gmail_imap_extensions_compatibility.rb
2
+
3
+ module GmailImapExtensions
4
+
5
+ def self.patch_net_imap_response_parser(klass = Net::IMAP::ResponseParser)
6
+ klass.class_eval do
7
+ def msg_att
8
+ match(Net::IMAP::ResponseParser::T_LPAR)
9
+ attr = {}
10
+ while true
11
+ token = lookahead
12
+ case token.symbol
13
+ when Net::IMAP::ResponseParser::T_RPAR
14
+ shift_token
15
+ break
16
+ when Net::IMAP::ResponseParser::T_SPACE
17
+ shift_token
18
+ token = lookahead
19
+ end
20
+ case token.value
21
+ when /\A(?:ENVELOPE)\z/ni
22
+ name, val = envelope_data
23
+ when /\A(?:FLAGS)\z/ni
24
+ name, val = flags_data
25
+ when /\A(?:INTERNALDATE)\z/ni
26
+ name, val = internaldate_data
27
+ when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni
28
+ name, val = rfc822_text
29
+ when /\A(?:RFC822\.SIZE)\z/ni
30
+ name, val = rfc822_size
31
+ when /\A(?:BODY(?:STRUCTURE)?)\z/ni
32
+ name, val = body_data
33
+ when /\A(?:UID)\z/ni
34
+ name, val = uid_data
35
+
36
+ # Gmail extension additions.
37
+ # Cargo-Cult code warning: # I have no idea why the regexp - just copying a pattern
38
+ when /\A(?:X-GM-LABELS)\z/ni
39
+ name, val = flags_data
40
+ when /\A(?:X-GM-MSGID)\z/ni
41
+ name, val = uid_data
42
+ when /\A(?:X-GM-THRID)\z/ni
43
+ name, val = uid_data
44
+ else
45
+ parse_error("unknown attribute `%s'", token.value)
46
+ end
47
+ attr[name] = val
48
+ end
49
+ return attr
50
+ end
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,20 @@
1
+ module Gmail
2
+ module Client
3
+ class Plain < Base
4
+ attr_reader :password
5
+
6
+ def initialize(username, password, options={})
7
+ @password = password
8
+ super(username, options)
9
+ end
10
+
11
+ def login(raise_errors=false)
12
+ @imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
13
+ rescue Net::IMAP::NoResponseError
14
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
15
+ end
16
+ end # Plain
17
+
18
+ register :plain, Plain
19
+ end # Client
20
+ end # Gmail