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.
@@ -0,0 +1,51 @@
1
+ require 'gmail_xoauth'
2
+
3
+ module Gmail
4
+ module Client
5
+ class XOAuth < Base
6
+ attr_reader :token
7
+ attr_reader :secret
8
+ attr_reader :consumer_key
9
+ attr_reader :consumer_secret
10
+
11
+ def initialize(username, options={})
12
+ @token = options.delete(:token)
13
+ @secret = options.delete(:secret)
14
+ @consumer_key = options.delete(:consumer_key)
15
+ @consumer_secret = options.delete(:consumer_secret)
16
+
17
+ super(username, options)
18
+ end
19
+
20
+ def login(raise_errors=false)
21
+ @imap and @logged_in = (login = @imap.authenticate('XOAUTH', username,
22
+ :consumer_key => consumer_key,
23
+ :consumer_secret => consumer_secret,
24
+ :token => token,
25
+ :token_secret => secret
26
+ )) && login.name == 'OK'
27
+ rescue
28
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
29
+ end
30
+
31
+ def smtp_settings
32
+ [:smtp, {
33
+ :address => GMAIL_SMTP_HOST,
34
+ :port => GMAIL_SMTP_PORT,
35
+ :domain => mail_domain,
36
+ :user_name => username,
37
+ :password => {
38
+ :consumer_key => consumer_key,
39
+ :consumer_secret => consumer_secret,
40
+ :token => token,
41
+ :token_secret => secret
42
+ },
43
+ :authentication => :xoauth,
44
+ :enable_starttls_auto => true
45
+ }]
46
+ end
47
+ end # XOAuth
48
+
49
+ register :xoauth, XOAuth
50
+ end # Client
51
+ end # Gmail
@@ -0,0 +1,62 @@
1
+ module Gmail
2
+ class Labels
3
+ include Enumerable
4
+ attr_reader :connection
5
+ alias :conn :connection
6
+
7
+ def initialize(connection)
8
+ @connection = connection
9
+ end
10
+
11
+ # Get list of all defined labels.
12
+ def all
13
+ @list = []
14
+
15
+ ## check each item in list for subfolders
16
+ conn.list("", "%").each {|l| sublabels_or_label(l)}
17
+
18
+ @list.inject([]) do |labels,label|
19
+ label[:name].each_line {|l| labels << Net::IMAP.decode_utf7(l) }
20
+ labels
21
+ end
22
+ end
23
+ alias :list :all
24
+ alias :to_a :all
25
+
26
+ def sublabels_or_label(label)
27
+ if label.attr.include? :Hasnochildren
28
+ @list << label
29
+ else
30
+ @list << label
31
+ conn.list("#{label.name}/", "%").each {|l| sublabels_or_label(l)}
32
+ end
33
+ end
34
+
35
+ def each(*args, &block)
36
+ all.each(*args, &block)
37
+ end
38
+
39
+ # Returns +true+ when given label defined.
40
+ def exists?(label)
41
+ all.include?(label)
42
+ end
43
+ alias :exist? :exists?
44
+
45
+ # Creates given label in your account.
46
+ def create(label)
47
+ !!conn.create(Net::IMAP.encode_utf7(label)) rescue false
48
+ end
49
+ alias :new :create
50
+ alias :add :create
51
+
52
+ # Deletes given label from your account.
53
+ def delete(label)
54
+ !!conn.delete(Net::IMAP.encode_utf7(label)) rescue false
55
+ end
56
+ alias :remove :delete
57
+
58
+ def inspect
59
+ "#<Gmail::Labels#{'0x%04x' % (object_id << 1)}>"
60
+ end
61
+ end # Labels
62
+ end # Gmail
@@ -0,0 +1,118 @@
1
+ module Gmail
2
+ class Mailbox
3
+ MAILBOX_ALIASES = {
4
+ :all => ['ALL'],
5
+ :seen => ['SEEN'],
6
+ :unseen => ['UNSEEN'],
7
+ :read => ['SEEN'],
8
+ :unread => ['UNSEEN'],
9
+ :flagged => ['FLAGGED'],
10
+ :unflagged => ['UNFLAGGED'],
11
+ :starred => ['FLAGGED'],
12
+ :unstarred => ['UNFLAGGED'],
13
+ :deleted => ['DELETED'],
14
+ :undeleted => ['UNDELETED'],
15
+ :draft => ['DRAFT'],
16
+ :undrafted => ['UNDRAFT']
17
+ }
18
+
19
+ attr_reader :name
20
+ attr_reader :external_name
21
+
22
+ def initialize(gmail, name="INBOX")
23
+ @name = name
24
+ @external_name = Net::IMAP.decode_utf7(name)
25
+ @gmail = gmail
26
+ end
27
+
28
+ # Returns list of emails which meets given criteria.
29
+ #
30
+ # ==== Examples
31
+ #
32
+ # gmail.inbox.emails(:all)
33
+ # gmail.inbox.emails(:unread, :from => "friend@gmail.com")
34
+ # gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
35
+ # gmail.mailbox("Test").emails(:read)
36
+ #
37
+ # gmail.mailbox("Test") do |box|
38
+ # box.emails(:read)
39
+ # box.emails(:unread) do |email|
40
+ # ... do something with each email...
41
+ # end
42
+ # end
43
+ def emails(*args, &block)
44
+ args << :all if args.size == 0
45
+
46
+ if args.first.is_a?(Symbol)
47
+ search = MAILBOX_ALIASES[args.shift].dup
48
+ opts = args.first.is_a?(Hash) ? args.first : {}
49
+
50
+ opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
51
+ opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
52
+ opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
53
+ opts[:from] and search.concat ['FROM', opts[:from]]
54
+ opts[:to] and search.concat ['TO', opts[:to]]
55
+ opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
56
+ opts[:label] and search.concat ['LABEL', opts[:label]]
57
+ opts[:attachment] and search.concat ['HAS', 'attachment']
58
+ opts[:search] and search.concat ['BODY', opts[:search]]
59
+ opts[:body] and search.concat ['BODY', opts[:body]]
60
+ opts[:query] and search.concat opts[:query]
61
+ opts[:gm] and search.concat ['X-GM-RAW', opts[:gm]]
62
+
63
+ @gmail.mailbox(name) do
64
+ @gmail.conn.uid_search(search).collect do |uid|
65
+ message = (messages[uid] ||= Message.new(self, uid))
66
+ block.call(message) if block_given?
67
+ message
68
+ end
69
+ end
70
+ elsif args.first.is_a?(Hash)
71
+ emails(:all, args.first)
72
+ else
73
+ raise ArgumentError, "Invalid search criteria"
74
+ end
75
+ end
76
+ alias :mails :emails
77
+ alias :search :emails
78
+ alias :find :emails
79
+ alias :filter :emails
80
+
81
+ # This is a convenience method that really probably shouldn't need to exist,
82
+ # but it does make code more readable, if seriously all you want is the count
83
+ # of messages.
84
+ #
85
+ # ==== Examples
86
+ #
87
+ # gmail.inbox.count(:all)
88
+ # gmail.inbox.count(:unread, :from => "friend@gmail.com")
89
+ # gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
90
+ def count(*args)
91
+ emails(*args).size
92
+ end
93
+
94
+ # This permanently removes messages which are marked as deleted
95
+ def expunge
96
+ @gmail.mailbox(name) { @gmail.conn.expunge }
97
+ end
98
+
99
+ # Cached messages.
100
+ def messages
101
+ @messages ||= {}
102
+ end
103
+
104
+ def inspect
105
+ "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{external_name}>"
106
+ end
107
+
108
+ def to_s
109
+ name
110
+ end
111
+
112
+ MAILBOX_ALIASES.each_key { |mailbox|
113
+ define_method(mailbox) do |*args, &block|
114
+ emails(mailbox, *args, &block)
115
+ end
116
+ }
117
+ end # Message
118
+ end # Gmail
@@ -0,0 +1,166 @@
1
+ module Gmail
2
+ class Message
3
+ # Raised when given label doesn't exists.
4
+ class NoLabelError < Exception; end
5
+
6
+ attr_reader :uid
7
+
8
+ def initialize(mailbox, uid)
9
+ @uid = uid
10
+ @mailbox = mailbox
11
+ @gmail = mailbox.instance_variable_get("@gmail") if mailbox
12
+ end
13
+
14
+ def labels
15
+ @gmail.conn.uid_fetch(uid, "X-GM-LABELS")[0].attr["X-GM-LABELS"]
16
+ end
17
+
18
+ def uid
19
+ @uid ||= @gmail.conn.uid_search(['HEADER', 'Message-ID', message_id])[0]
20
+ end
21
+
22
+ # Mark message with given flag.
23
+ def flag(name)
24
+ !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "+FLAGS", [name]) }
25
+ end
26
+
27
+ # Unmark message.
28
+ def unflag(name)
29
+ !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "-FLAGS", [name]) }
30
+ end
31
+
32
+ # Do commonly used operations on message.
33
+ def mark(flag)
34
+ case flag
35
+ when :read then read!
36
+ when :unread then unread!
37
+ when :deleted then delete!
38
+ when :spam then spam!
39
+ else
40
+ flag(flag)
41
+ end
42
+ end
43
+
44
+ # Mark this message as a spam.
45
+ def spam!
46
+ move_to('[Gmail]/Spam')
47
+ end
48
+
49
+ # Mark as read.
50
+ def read!
51
+ flag(:Seen)
52
+ end
53
+
54
+ # Mark as unread.
55
+ def unread!
56
+ unflag(:Seen)
57
+ end
58
+
59
+ # Mark message with star.
60
+ def star!
61
+ flag('[Gmail]/Starred')
62
+ end
63
+
64
+ # Remove message from list of starred.
65
+ def unstar!
66
+ unflag('[Gmail]/Starred')
67
+ end
68
+
69
+ # Move to trash / bin.
70
+ def delete!
71
+ @mailbox.messages.delete(uid)
72
+ flag(:deleted)
73
+
74
+ # For some, it's called "Trash", for others, it's called "Bin". Support both.
75
+ trash = @gmail.labels.exist?('[Gmail]/Bin') ? '[Gmail]/Bin' : '[Gmail]/Trash'
76
+ move_to(trash) unless %w[[Gmail]/Spam [Gmail]/Bin [Gmail]/Trash].include?(@mailbox.name)
77
+ end
78
+
79
+ # Archive this message.
80
+ def archive!
81
+ move_to('[Gmail]/All Mail')
82
+ end
83
+
84
+ # Move to given box and delete from others.
85
+ def move_to(name, from=nil)
86
+ label(name, from)
87
+ delete! if !%w[[Gmail]/Bin [Gmail]/Trash].include?(name)
88
+ end
89
+ alias :move :move_to
90
+
91
+ # Move message to given and delete from others. When given mailbox doesn't
92
+ # exist then it will be automaticaly created.
93
+ def move_to!(name, from=nil)
94
+ label!(name, from) && delete!
95
+ end
96
+ alias :move! :move_to!
97
+
98
+ # Mark this message with given label. When given label doesn't exist then
99
+ # it will raise <tt>NoLabelError</tt>.
100
+ #
101
+ # See also <tt>Gmail::Message#label!</tt>.
102
+ def label(name, from=nil)
103
+ @gmail.mailbox(Net::IMAP.encode_utf7(from || @mailbox.external_name)) { @gmail.conn.uid_copy(uid, Net::IMAP.encode_utf7(name)) }
104
+ rescue Net::IMAP::NoResponseError
105
+ raise NoLabelError, "Label '#{name}' doesn't exist!"
106
+ end
107
+
108
+ # Mark this message with given label. When given label doesn't exist then
109
+ # it will be automaticaly created.
110
+ #
111
+ # See also <tt>Gmail::Message#label</tt>.
112
+ def label!(name, from=nil)
113
+ label(name, from)
114
+ rescue NoLabelError
115
+ @gmail.labels.add(Net::IMAP.encode_utf7(name))
116
+ label(name, from)
117
+ end
118
+ alias :add_label :label!
119
+ alias :add_label! :label!
120
+
121
+ # Remove given label from this message.
122
+ def remove_label!(name)
123
+ move_to('[Gmail]/All Mail', name)
124
+ end
125
+ alias :delete_label! :remove_label!
126
+
127
+ def inspect
128
+ "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.external_name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
129
+ end
130
+
131
+ def method_missing(meth, *args, &block)
132
+ # Delegate rest directly to the message.
133
+ if envelope.respond_to?(meth)
134
+ envelope.send(meth, *args, &block)
135
+ elsif message.respond_to?(meth)
136
+ message.send(meth, *args, &block)
137
+ else
138
+ super(meth, *args, &block)
139
+ end
140
+ end
141
+
142
+ def respond_to?(meth, *args, &block)
143
+ if envelope.respond_to?(meth)
144
+ return true
145
+ elsif message.respond_to?(meth)
146
+ return true
147
+ else
148
+ super(meth, *args, &block)
149
+ end
150
+ end
151
+
152
+ def envelope
153
+ @envelope ||= @gmail.mailbox(@mailbox.name) {
154
+ @gmail.conn.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"]
155
+ }
156
+ end
157
+
158
+ def message
159
+ @message ||= Mail.new(@gmail.mailbox(@mailbox.name) {
160
+ @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"] # RFC822
161
+ })
162
+ end
163
+ alias_method :raw_message, :message
164
+
165
+ end # Message
166
+ end # Gmail
@@ -0,0 +1,3 @@
1
+ module Gmail
2
+ VERSION = "0.4.3"
3
+ end # Gmail
@@ -0,0 +1,2 @@
1
+ - "test@gmail.com"
2
+ - "test"
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Gmail client (Plain)" do
4
+ subject { Gmail::Client::Plain }
5
+
6
+ context "on initialize" do
7
+ it "should set username, password and options" do
8
+ client = subject.new("test@gmail.com", "pass", :foo => :bar)
9
+ client.username.should == "test@gmail.com"
10
+ client.password.should == "pass"
11
+ client.options[:foo].should == :bar
12
+ end
13
+
14
+ it "should convert simple name to gmail email" do
15
+ client = subject.new("test", "pass")
16
+ client.username.should == "test@gmail.com"
17
+ end
18
+ end
19
+
20
+ context "instance" do
21
+ def mock_client(&block)
22
+ client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
23
+ if block_given?
24
+ client.connect
25
+ yield client
26
+ client.logout
27
+ end
28
+ client
29
+ end
30
+
31
+ it "should connect to GMail IMAP service" do
32
+ client = mock_client
33
+ client.connect!.should be_true
34
+ end
35
+
36
+ it "should properly login to valid GMail account" do
37
+ client = mock_client
38
+ client.connect.should be_true
39
+ client.login.should be_true
40
+ client.should be_logged_in
41
+ client.logout
42
+ end
43
+
44
+ it "should raise error when given GMail account is invalid and errors enabled" do
45
+ lambda {
46
+ client = Gmail::Client::Plain.new("foo", "bar")
47
+ client.connect.should be_true
48
+ client.login!.should_not be_nil
49
+ }.should raise_error
50
+ ### FIX: can someone dig to the bottom of this? We are getting NoMethodError instead of Gmail::Client::AuthorizationError in 1.9
51
+ end
52
+
53
+ it "shouldn't raise error even though GMail account is invalid" do
54
+ lambda {
55
+ client = Gmail::Client::Plain.new("foo", "bar")
56
+ client.connect.should be_true
57
+ expect(client.login).to_not be_true
58
+ }.should_not raise_error
59
+ end
60
+
61
+ it "shouldn't login when given GMail account is invalid" do
62
+ client = Gmail::Client::Plain.new("foo", "bar")
63
+ client.connect.should be_true
64
+ client.login.should be_false
65
+ end
66
+
67
+ it "should properly logout from GMail" do
68
+ client = mock_client
69
+ client.connect
70
+ client.login.should be_true
71
+ client.logout.should be_true
72
+ client.should_not be_logged_in
73
+ end
74
+
75
+ it "#connection should automatically log in to GMail account when it's called" do
76
+ mock_client do |client|
77
+ client.expects(:login).once.returns(false)
78
+ client.connection.should_not be_nil
79
+ end
80
+ end
81
+
82
+ it "should properly compose message" do
83
+ mail = mock_client.compose do
84
+ from "test@gmail.com"
85
+ to "friend@gmail.com"
86
+ subject "Hello world!"
87
+ end
88
+ mail.from.should == ["test@gmail.com"]
89
+ mail.to.should == ["friend@gmail.com"]
90
+ mail.subject.should == "Hello world!"
91
+ end
92
+
93
+ it "#compose should automatically add `from` header when it is not specified" do
94
+ mail = mock_client.compose
95
+ mail.from.should == [TEST_ACCOUNT[0]]
96
+ mail = mock_client.compose(Mail.new)
97
+ mail.from.should == [TEST_ACCOUNT[0]]
98
+ mail = mock_client.compose {}
99
+ mail.from.should == [TEST_ACCOUNT[0]]
100
+ end
101
+
102
+ it "should deliver inline composed email" do
103
+ mock_client do |client|
104
+ client.deliver do
105
+ to TEST_ACCOUNT[0]
106
+ subject "Hello world!"
107
+ body "Yeah, hello there!"
108
+ end.should be_true
109
+ end
110
+ end
111
+
112
+ it "should not raise error when mail can't be delivered and errors are disabled" do
113
+ lambda {
114
+ client = mock_client
115
+ client.deliver(Mail.new {}).should be_false
116
+ }.should_not raise_error
117
+ end
118
+
119
+ it "should raise error when mail can't be delivered and errors are disabled" do
120
+ lambda {
121
+ client = mock_client
122
+ client.deliver!(Mail.new {})
123
+ }.should raise_error(Gmail::Client::DeliveryError)
124
+ end
125
+
126
+ it "should properly switch to given mailbox" do
127
+ mock_client do |client|
128
+ mailbox = client.mailbox("INBOX")
129
+ mailbox.should be_kind_of(Gmail::Mailbox)
130
+ mailbox.name.should == "INBOX"
131
+ end
132
+ end
133
+
134
+ it "should properly switch to given mailbox using block style" do
135
+ mock_client do |client|
136
+ client.mailbox("INBOX") do |mailbox|
137
+ mailbox.should be_kind_of(Gmail::Mailbox)
138
+ mailbox.name.should == "INBOX"
139
+ end
140
+ end
141
+ end
142
+
143
+ context "labels" do
144
+ subject {
145
+ client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
146
+ client.connect
147
+ client.labels
148
+ }
149
+
150
+ it "should get list of all available labels" do
151
+ labels = subject
152
+ labels.all.should include("INBOX")
153
+ end
154
+
155
+ it "should be able to check if there is given label defined" do
156
+ labels = subject
157
+ labels.exists?("INBOX").should be_true
158
+ labels.exists?("FOOBAR").should be_false
159
+ end
160
+
161
+ it "should be able to create given label" do
162
+ labels = subject
163
+ labels.create("MYLABEL")
164
+ labels.exists?("MYLABEL").should be_true
165
+ labels.create("MYLABEL").should be_false
166
+ labels.delete("MYLABEL")
167
+ end
168
+
169
+ it "should be able to remove existing label" do
170
+ labels = subject
171
+ labels.create("MYLABEL")
172
+ labels.delete("MYLABEL").should be_true
173
+ labels.exists?("MYLABEL").should be_false
174
+ labels.delete("MYLABEL").should be_false
175
+ end
176
+ end
177
+ end
178
+ end