gmail_gm_raw 0.4.3

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