gmail_gm_raw 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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