gmailer 0.0.5 → 0.0.6
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/CHANGES +16 -11
- data/README +44 -12
- data/gmailer.rb +1314 -0
- metadata +2 -1
data/CHANGES
CHANGED
@@ -1,20 +1,25 @@
|
|
1
|
+
== 0.0.6 - 29-Aug-2005
|
2
|
+
- add edit star method
|
3
|
+
- add edit archive method
|
4
|
+
- rename get_ method
|
5
|
+
|
1
6
|
== 0.0.5 - 28-Aug-2005
|
2
|
-
|
3
|
-
|
4
|
-
|
7
|
+
- add edit label mothods
|
8
|
+
- add edit preference method
|
9
|
+
- fix some bugs
|
5
10
|
|
6
11
|
== 0.0.4 - 26-Aug-2005
|
7
|
-
|
8
|
-
|
9
|
-
|
12
|
+
- remove camelCase methods
|
13
|
+
- fix some bugs
|
14
|
+
- modify some key name
|
10
15
|
|
11
16
|
== 0.0.3 - 25-Aug-2005
|
12
|
-
|
13
|
-
|
17
|
+
- add some methods
|
18
|
+
- add support for proxy
|
14
19
|
|
15
20
|
== 0.0.2 - 20-Aug-2005
|
16
|
-
|
17
|
-
|
21
|
+
- add some methods
|
22
|
+
- make more compact and simple
|
18
23
|
|
19
24
|
== 0.0.1 - 17-Aug-2005
|
20
|
-
|
25
|
+
- Initial release
|
data/README
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
gmail.set_login_info(name, pwd)
|
10
10
|
if gmail.connect
|
11
11
|
gmail.fetch_box(GM_LABEL, "my_label", 0)
|
12
|
-
snapshot = gmail.
|
12
|
+
snapshot = gmail.snapshot(GM_LABEL)
|
13
13
|
if snapshot
|
14
14
|
puts "Total # of conversations of my_label = " + snapshot.box_total.to_s
|
15
15
|
end
|
@@ -65,7 +65,7 @@ Or shorter
|
|
65
65
|
gmail.set_login_info(name, pwd)
|
66
66
|
if gmail.connect
|
67
67
|
gmail.fetch_box(GM_CONTACT, "freq", 0)
|
68
|
-
snapshot = gmail.
|
68
|
+
snapshot = gmail.snapshot(GM_CONTACT)
|
69
69
|
puts "Your frequently used addresses:"
|
70
70
|
snapshot.contacts.each { |item|
|
71
71
|
puts "Name: " + item["name"] + ", Email: " + item["email"]
|
@@ -84,8 +84,8 @@ Or shorter
|
|
84
84
|
=== Get list of labels and get list of messages
|
85
85
|
|
86
86
|
GMailer.connect(:username=>name,:password=>pwd) do |g|
|
87
|
-
labels = g.
|
88
|
-
g.
|
87
|
+
labels = g.labels
|
88
|
+
g.messages(:label=>labels[0]).each {|m|
|
89
89
|
puts "Subject: #{m['subject']} / Snippet: #{m['snippet']}" if m['new?']
|
90
90
|
}
|
91
91
|
end
|
@@ -121,10 +121,30 @@ GMailer.connect(:username=>name,:password=>pwd) do |g|
|
|
121
121
|
:reply_to=>'return@foo.bar',
|
122
122
|
:snippets=>true,
|
123
123
|
:display_name=>'Display Name')
|
124
|
-
pref = g.
|
124
|
+
pref = g.preference
|
125
125
|
puts "Display language:#{pref['display_language']}, Max Page Size:#{pref['max_page_size']}"
|
126
126
|
end
|
127
127
|
|
128
|
+
=== Apply star and remove star
|
129
|
+
|
130
|
+
GMailer.connect(:username=>name,:password=>pwd) do |g|
|
131
|
+
#apply star to a message
|
132
|
+
g.apply_star(msgid)
|
133
|
+
|
134
|
+
#remove star from a message
|
135
|
+
g.remove_star(msgid)
|
136
|
+
end
|
137
|
+
|
138
|
+
=== Archive and unarchive a message
|
139
|
+
|
140
|
+
GMailer.connect(:username=>name,:password=>pwd) do |g|
|
141
|
+
#archive a message
|
142
|
+
g.archive(msgid)
|
143
|
+
|
144
|
+
#unarchive a message
|
145
|
+
g.unarchive(msgid)
|
146
|
+
end
|
147
|
+
|
128
148
|
== Class Methods
|
129
149
|
GMailer.new(charset='UTF-8')
|
130
150
|
Returns a new GMailer object and set up charset.
|
@@ -168,24 +188,24 @@ fetch_box(type,box,position)
|
|
168
188
|
box: name of "box" (e.g. Inbox, your_label, "all"/"freq" of contacts)
|
169
189
|
position: cursor for paged result.
|
170
190
|
|
171
|
-
|
191
|
+
snapshot(type)
|
172
192
|
To get a "snapshot", an object (see GMailSnapshot below) for you to
|
173
193
|
access query result at ease.
|
174
194
|
|
175
|
-
|
195
|
+
labels()
|
176
196
|
To get list of labels.
|
177
197
|
|
178
|
-
|
198
|
+
preference()
|
179
199
|
To get hash of preference setting.
|
180
200
|
|
181
|
-
|
201
|
+
messages()
|
182
202
|
To get list of messages.
|
183
203
|
|
184
|
-
|
204
|
+
attachment(attachment_id,message_id,filename,zipped)
|
185
205
|
To download an attachment of a message. If zipped is true, download ALL
|
186
206
|
attachements of message_id in a zip file.
|
187
207
|
|
188
|
-
|
208
|
+
attachments_of(conv, path_to_store_files)
|
189
209
|
To download ALL files attached to a conversation. The full path of
|
190
210
|
downloaded files will be returned (as array).
|
191
211
|
|
@@ -204,6 +224,18 @@ apply_label(msgid,label)
|
|
204
224
|
remove_label(msgid,label)
|
205
225
|
To remove a label to a message
|
206
226
|
|
227
|
+
apply_star(msgid)
|
228
|
+
To apply starring to a message
|
229
|
+
|
230
|
+
remove_star(msgid)
|
231
|
+
To remove starring from a message
|
232
|
+
|
233
|
+
archive(msgid)
|
234
|
+
To archive a message
|
235
|
+
|
236
|
+
unarchive(msgid)
|
237
|
+
To unarchive a message
|
238
|
+
|
207
239
|
update_preference(preference hash)
|
208
240
|
To update preference setting
|
209
241
|
hash is one of :max_page_size(25,50,100),:keyboard_shortcuts(true,false),:indicators(true,false),
|
@@ -233,7 +265,7 @@ disconnect()
|
|
233
265
|
dump(query)
|
234
266
|
To dump ALL it gets from URL query string, including headers.
|
235
267
|
|
236
|
-
|
268
|
+
standard_box()
|
237
269
|
To get an array of names of the "standard box" (Inbox, Starred, etc.)
|
238
270
|
|
239
271
|
invite(email)
|
data/gmailer.rb
ADDED
@@ -0,0 +1,1314 @@
|
|
1
|
+
#
|
2
|
+
# = gmailer.rb: A class for interface to Google's webmail service
|
3
|
+
#
|
4
|
+
# Author:: Park Heesob
|
5
|
+
#
|
6
|
+
# project home page: http://rubyforge.org/projects/gmailutils
|
7
|
+
#
|
8
|
+
|
9
|
+
# $D_ON = false
|
10
|
+
|
11
|
+
require 'net/https'
|
12
|
+
|
13
|
+
GM_LNK_GMAIL = "/mail/"
|
14
|
+
GM_LNK_HOST = "mail.google.com"
|
15
|
+
GM_LNK_LOGIN = "/accounts/ServiceLoginAuth"
|
16
|
+
GM_LNK_LOGOUT = "/mail/?logout"
|
17
|
+
GM_LNK_ATTACHMENT = "/mail/?view=att&disp=att"
|
18
|
+
GM_LNK_ATTACHMENT_ZIPPED = "/mail/?view=att&disp=zip"
|
19
|
+
|
20
|
+
GM_USER_AGENT = "Mozilla/5.0 (X11 U Linux i686 en-US rv:1.4b) Gecko/20040612 Mozilla Firebird/0.9"
|
21
|
+
|
22
|
+
GM_STANDARD = 0x001
|
23
|
+
GM_LABEL = 0x002
|
24
|
+
GM_CONVERSATION = 0x004
|
25
|
+
GM_QUERY = 0x008
|
26
|
+
GM_CONTACT = 0x010
|
27
|
+
GM_PREFERENCE = 0x020
|
28
|
+
|
29
|
+
GM_ACT_CREATELABEL = 1
|
30
|
+
GM_ACT_DELETELABEL = 2
|
31
|
+
GM_ACT_RENAMELABEL = 3
|
32
|
+
GM_ACT_APPLYLABEL = 4
|
33
|
+
GM_ACT_REMOVELABEL = 5
|
34
|
+
GM_ACT_PREFERENCE = 6
|
35
|
+
GM_ACT_STAR = 7
|
36
|
+
GM_ACT_UNSTAR = 8
|
37
|
+
GM_ACT_SPAM = 9
|
38
|
+
GM_ACT_UNSPAM = 10
|
39
|
+
GM_ACT_READ = 11
|
40
|
+
GM_ACT_UNREAD = 12
|
41
|
+
GM_ACT_TRASH = 13
|
42
|
+
GM_ACT_DELFOREVER = 14
|
43
|
+
GM_ACT_ARCHIVE = 15
|
44
|
+
GM_ACT_INBOX = 16
|
45
|
+
GM_ACT_UNTRASH = 17
|
46
|
+
GM_ACT_UNDRAFT = 18
|
47
|
+
GM_ACT_TRASHMSG = 19 # trash individual message
|
48
|
+
GM_ACT_DELSPAM = 20 # delete spam, forever
|
49
|
+
GM_ACT_DELTRASHED = 21 # delete trash message, forever
|
50
|
+
|
51
|
+
class GMailer
|
52
|
+
|
53
|
+
VERSION = "0.0.6"
|
54
|
+
|
55
|
+
@cookie_str
|
56
|
+
@login
|
57
|
+
@pwd
|
58
|
+
@raw # raw packets
|
59
|
+
@contact_raw # raw packets for address book
|
60
|
+
@timezone
|
61
|
+
@created
|
62
|
+
@proxy_host
|
63
|
+
@proxy_port
|
64
|
+
@proxy_user
|
65
|
+
@proxy_pass
|
66
|
+
|
67
|
+
def encode(str)
|
68
|
+
return str if @charset.upcase == 'UTF-8'
|
69
|
+
begin
|
70
|
+
require 'Win32API'
|
71
|
+
str += "\0"
|
72
|
+
ostr = "\0" * 256
|
73
|
+
multiByteToWideChar = Win32API.new('kernel32','MultiByteToWideChar',['L','L','P','L','P','L'],'L')
|
74
|
+
multiByteToWideChar.Call(0,0,str,-1,ostr,128)
|
75
|
+
(ostr.strip + "\0").unpack("S*").pack("U*")
|
76
|
+
rescue LoadError
|
77
|
+
require 'iconv'
|
78
|
+
Iconv::iconv('UTF-8',@charset,str)[0]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# return GMailer
|
84
|
+
# desc Constructor
|
85
|
+
#
|
86
|
+
def initialize(*param)
|
87
|
+
Debugger::say("Constructing GMailer...")
|
88
|
+
@login = ''
|
89
|
+
@pwd = ''
|
90
|
+
@raw = {}
|
91
|
+
@proxy_host = nil
|
92
|
+
@proxy_port = 0
|
93
|
+
@proxy_user = nil
|
94
|
+
@proxy_pass = nil
|
95
|
+
@proxy = false
|
96
|
+
if param.length==1 && param[0].class==Hash
|
97
|
+
param = param[0]
|
98
|
+
set_login_info(param[:username]||'',param[:password]||'')
|
99
|
+
@proxy_host = param[:proxy_host]
|
100
|
+
@proxy = !@proxy_host.nil?
|
101
|
+
@proxy_port = param[:proxy_port] || 80
|
102
|
+
@proxy_user = param[:proxy_user]
|
103
|
+
@proxy_pass = param[:proxy_pass]
|
104
|
+
@charset = param[:charset] || 'UTF-8'
|
105
|
+
elsif param.length==0
|
106
|
+
@charset = 'UTF-8'
|
107
|
+
elsif param.length==1
|
108
|
+
@charset = param[0]
|
109
|
+
elsif param.length==2
|
110
|
+
@charset = 'UTF-8'
|
111
|
+
set_login_info(param[0],param[1])
|
112
|
+
elsif param.length==3
|
113
|
+
@charset = param[2]
|
114
|
+
set_login_info(param[0],param[1])
|
115
|
+
else
|
116
|
+
raise ArgumentError, 'Invalid argument'
|
117
|
+
end
|
118
|
+
Debugger::say("Constructing completed.")
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# return void
|
123
|
+
# param string my_login
|
124
|
+
# param string my_pwd
|
125
|
+
# param int my_tz
|
126
|
+
# desc Set GMail login information.
|
127
|
+
#
|
128
|
+
def set_login_info(my_login, my_pwd, my_tz=0)
|
129
|
+
@login = my_login
|
130
|
+
@pwd = my_pwd
|
131
|
+
@timezone = (my_tz*-60).to_i.to_s
|
132
|
+
Debugger::say("LoginInfo set.")
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# return bool
|
137
|
+
# desc Connect to GMail without setting any session/cookie.
|
138
|
+
#
|
139
|
+
def connect_no_cookie()
|
140
|
+
Debugger::say("Start connecting without cookie...")
|
141
|
+
|
142
|
+
postdata = "service=mail&Email=" + URI.escape(@login) + "&Passwd=" + URI.escape(@pwd) + "&null=Sign%20in&continue=" +
|
143
|
+
URI.escape('https://mail.google.com/mail/?ui=html&zy=l') + "&rm=false&PersistentCookie=yes"
|
144
|
+
|
145
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 443)
|
146
|
+
np.use_ssl = true
|
147
|
+
np.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
148
|
+
result = ''
|
149
|
+
response = nil
|
150
|
+
# np.set_debug_output($stderr)
|
151
|
+
np.start { |http|
|
152
|
+
response = http.post(GM_LNK_LOGIN, postdata,{'User-agent' => GM_USER_AGENT} )
|
153
|
+
result = response.body
|
154
|
+
}
|
155
|
+
|
156
|
+
if result.include?("errormsg")
|
157
|
+
@cookie_str = ''
|
158
|
+
Debugger::say("Connect failed: " + result)
|
159
|
+
return false
|
160
|
+
end
|
161
|
+
|
162
|
+
cookies = __cookies(response['set-cookie'])
|
163
|
+
arr = URI::split(response["Location"])
|
164
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
|
165
|
+
np.use_ssl = true
|
166
|
+
np.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
167
|
+
result = ''
|
168
|
+
response = nil
|
169
|
+
# np.set_debug_output($stderr)
|
170
|
+
np.start { |http|
|
171
|
+
response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
|
172
|
+
result = response.body
|
173
|
+
}
|
174
|
+
|
175
|
+
arr = URI::split(response["Location"])
|
176
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
|
177
|
+
np.use_ssl = true
|
178
|
+
np.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
179
|
+
result = ''
|
180
|
+
response = nil
|
181
|
+
# np.set_debug_output($stderr)
|
182
|
+
np.start { |http|
|
183
|
+
response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
|
184
|
+
result = response.body
|
185
|
+
}
|
186
|
+
|
187
|
+
@loc = response["Location"]
|
188
|
+
cookies += ';' + __cookies(response['set-cookie'])
|
189
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
|
190
|
+
np.use_ssl = true
|
191
|
+
np.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
192
|
+
# np.set_debug_output($stderr)
|
193
|
+
np.start { |http|
|
194
|
+
response = http.get(@loc, {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
|
195
|
+
result = response.body
|
196
|
+
}
|
197
|
+
cookies += ';' + __cookies(response['set-cookie'])
|
198
|
+
|
199
|
+
@cookie_str = cookies + ";TZ=" + @timezone
|
200
|
+
|
201
|
+
Debugger::say("Finished connecting without cookie.")
|
202
|
+
return true
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# return bool
|
207
|
+
# desc Connect to GMail with default session management settings.
|
208
|
+
#
|
209
|
+
def connect()
|
210
|
+
connect_no_cookie()
|
211
|
+
end
|
212
|
+
|
213
|
+
# Singleton method
|
214
|
+
# return bool
|
215
|
+
# desc Connect to GMail with default session management settings.
|
216
|
+
#
|
217
|
+
|
218
|
+
def GMailer.connect(*param)
|
219
|
+
g = GMailer.new(*param)
|
220
|
+
g.connect_no_cookie()
|
221
|
+
if block_given?
|
222
|
+
yield(g)
|
223
|
+
g.disconnect()
|
224
|
+
else
|
225
|
+
g
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
#
|
231
|
+
# return bool
|
232
|
+
# desc See if it is connected to GMail.
|
233
|
+
#
|
234
|
+
def is_connected()
|
235
|
+
!@cookie_str.empty?
|
236
|
+
end
|
237
|
+
|
238
|
+
#
|
239
|
+
# return bool
|
240
|
+
# param string query
|
241
|
+
# desc Fetch contents by URL query.
|
242
|
+
#
|
243
|
+
def __fetch(query)
|
244
|
+
if is_connected()
|
245
|
+
Debugger::say("Start fetching query: " + query)
|
246
|
+
query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
|
247
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
248
|
+
# np.set_debug_output($stderr)
|
249
|
+
inbox = ''
|
250
|
+
np.start { |http|
|
251
|
+
response = http.get(GM_LNK_GMAIL + "?" + query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
|
252
|
+
inbox = response.body
|
253
|
+
}
|
254
|
+
|
255
|
+
inbox = inbox.gsub("\n",'').gsub("D([","\nD([").gsub("])","])\n")
|
256
|
+
|
257
|
+
matches = inbox.scan(/D\((.*)\)/).flatten
|
258
|
+
packets = {}
|
259
|
+
matches.each {|x|
|
260
|
+
tmp = eval(x.gsub(",,",",'',").gsub(",,",",'',"))
|
261
|
+
if (packets[tmp[0]] || (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di"))
|
262
|
+
if (tmp[0]=="t"||tmp[0]=="ts"||tmp[0]=="a"||tmp[0]=="cl")
|
263
|
+
packets[tmp[0]] += tmp[1..-1]
|
264
|
+
end
|
265
|
+
if (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di")
|
266
|
+
if !packets["mg"].nil?
|
267
|
+
packets["mg"].push(tmp)
|
268
|
+
else
|
269
|
+
packets["mg"] = [tmp]
|
270
|
+
end
|
271
|
+
end
|
272
|
+
else
|
273
|
+
packets[tmp[0]] = tmp
|
274
|
+
end
|
275
|
+
}
|
276
|
+
if packets["cl"] && packets["cl"].length > 1
|
277
|
+
packets["ce"] = []
|
278
|
+
for i in 1 .. packets["cl"].length-1
|
279
|
+
packets["ce"].push(packets["cl"][i])
|
280
|
+
end
|
281
|
+
end
|
282
|
+
@raw = packets
|
283
|
+
|
284
|
+
Debugger::say("Fetch completed.")
|
285
|
+
true
|
286
|
+
else # not logged in yet
|
287
|
+
Debugger::say("Fetch failed: not connected.")
|
288
|
+
false
|
289
|
+
end
|
290
|
+
end
|
291
|
+
private :__fetch
|
292
|
+
|
293
|
+
#
|
294
|
+
# return bool
|
295
|
+
# param constant type
|
296
|
+
# param mixed box
|
297
|
+
# param int pos
|
298
|
+
# desc Fetch contents from GMail by type.
|
299
|
+
#
|
300
|
+
def fetch_box(type, box, pos)
|
301
|
+
|
302
|
+
if is_connected()
|
303
|
+
case type
|
304
|
+
when GM_STANDARD
|
305
|
+
q = "search=" + box.downcase + "&view=tl&start=" + pos.to_s
|
306
|
+
|
307
|
+
when GM_LABEL
|
308
|
+
q = "search=cat&cat=" + box.to_s + "&view=tl&start=" + pos.to_s
|
309
|
+
|
310
|
+
when GM_CONVERSATION
|
311
|
+
q = "search=inbox&ser=1&view=cv"
|
312
|
+
if (box.class==Array)
|
313
|
+
q += "&th=" + box[0].to_s
|
314
|
+
for i in 1 .. box.length
|
315
|
+
q += "&msgs=" + box[i].to_s
|
316
|
+
end
|
317
|
+
else
|
318
|
+
q += "&th=" + box.to_s
|
319
|
+
end
|
320
|
+
|
321
|
+
when GM_QUERY
|
322
|
+
q = "search=query&q=" + URI.escape(box.to_s) + "&view=tl&start=" + pos.to_s
|
323
|
+
|
324
|
+
when GM_PREFERENCE
|
325
|
+
q = "view=pr&pnl=g"
|
326
|
+
|
327
|
+
when GM_CONTACT
|
328
|
+
if box.downcase == "all"
|
329
|
+
q = "view=cl&search=contacts&pnl=a"
|
330
|
+
else
|
331
|
+
q = "view=cl&search=contacts&pnl=d"
|
332
|
+
end
|
333
|
+
|
334
|
+
else
|
335
|
+
q = "search=inbox&view=tl&start=0&init=1"
|
336
|
+
end
|
337
|
+
|
338
|
+
__fetch(q)
|
339
|
+
else
|
340
|
+
false
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
#
|
345
|
+
# return snapshot
|
346
|
+
# param constant type
|
347
|
+
# param mixed box
|
348
|
+
# param int pos
|
349
|
+
# desc Fetch contents from GMail by type.
|
350
|
+
#
|
351
|
+
def fetch(hash_param)
|
352
|
+
type = GM_STANDARD
|
353
|
+
box = "inbox"
|
354
|
+
pos = 0
|
355
|
+
hash_param.keys.each do |k|
|
356
|
+
case k
|
357
|
+
when :label
|
358
|
+
type = GM_LABEL
|
359
|
+
box = hash_param[k]
|
360
|
+
when :standard
|
361
|
+
type = GM_STANDARD
|
362
|
+
box = hash_param[k]
|
363
|
+
when :conversation
|
364
|
+
type = GM_CONVERSATION
|
365
|
+
box = hash_param[k]
|
366
|
+
when :preference
|
367
|
+
type = GM_PREFERENCE
|
368
|
+
box = hash_param[k]
|
369
|
+
when :contact
|
370
|
+
type = GM_CONTACT
|
371
|
+
box = hash_param[k]
|
372
|
+
when :query
|
373
|
+
type = GM_QUERY
|
374
|
+
box = hash_param[k]
|
375
|
+
when :pos
|
376
|
+
pos = hash_param[k].to_i
|
377
|
+
else
|
378
|
+
raise ArgumentError, 'Invalid hash argument'
|
379
|
+
end
|
380
|
+
end
|
381
|
+
box = "inbox" unless box
|
382
|
+
fetch_box(type,box,pos)
|
383
|
+
snapshot = snapshot(type)
|
384
|
+
if block_given?
|
385
|
+
yield(snapshot)
|
386
|
+
elsif type == GM_CONTACT
|
387
|
+
snapshot.contacts
|
388
|
+
else
|
389
|
+
snapshot
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
#
|
394
|
+
# return string[]
|
395
|
+
# param string[] convs
|
396
|
+
# param string path
|
397
|
+
# desc Save all attaching files of conversations to a path.
|
398
|
+
#
|
399
|
+
def attachments_of(convs, path)
|
400
|
+
|
401
|
+
if is_connected()
|
402
|
+
if (convs.class != Array)
|
403
|
+
convs = [convs] # array wrapper
|
404
|
+
end
|
405
|
+
final = []
|
406
|
+
convs.each { |v|
|
407
|
+
if v["attachment"]
|
408
|
+
v["attachment"].each { |vv|
|
409
|
+
f = path+"/"+vv["filename"]
|
410
|
+
f = path+"/"+vv["filename"]+"."+rand(2000).to_s while (FileTest.exist?(f))
|
411
|
+
if attachment(vv["id"],v["id"],f,false)
|
412
|
+
final.push(f)
|
413
|
+
end
|
414
|
+
}
|
415
|
+
end
|
416
|
+
}
|
417
|
+
final
|
418
|
+
else
|
419
|
+
nil
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
#
|
424
|
+
# return string[]
|
425
|
+
# param string[] convs
|
426
|
+
# param string path
|
427
|
+
# desc Save all attaching files of conversations to a path.
|
428
|
+
#
|
429
|
+
def zip_attachments_of(convs, path)
|
430
|
+
|
431
|
+
if is_connected()
|
432
|
+
if (convs.class != Array)
|
433
|
+
convs = [convs] # array wrapper
|
434
|
+
end
|
435
|
+
final = []
|
436
|
+
convs.each {|v|
|
437
|
+
if v["attachment"]
|
438
|
+
f = path+"/attachment.zip"
|
439
|
+
f = path+"/attachment."+rand(2000).to_s+".zip" while (FileTest.exist?(f))
|
440
|
+
if attachment(v["attachment"][0]["id"],v["id"],f,true)
|
441
|
+
final.push(f)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
}
|
445
|
+
final
|
446
|
+
else
|
447
|
+
nil
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
#
|
452
|
+
# return bool
|
453
|
+
# param string attid
|
454
|
+
# param string msgid
|
455
|
+
# param string filename
|
456
|
+
# desc Save attachment with attachment ID attid and message ID msgid to file with name filename.
|
457
|
+
#
|
458
|
+
def attachment(attid, msgid, filename, zipped=false)
|
459
|
+
|
460
|
+
if is_connected()
|
461
|
+
Debugger::say("Start getting attachment...")
|
462
|
+
|
463
|
+
if !zipped
|
464
|
+
query = GM_LNK_ATTACHMENT + "&attid=" + URI.escape(attid) + "&th=" + URI.escape(msgid)
|
465
|
+
else
|
466
|
+
query = GM_LNK_ATTACHMENT_ZIPPED + "&th=" + URI.escape(msgid)
|
467
|
+
end
|
468
|
+
|
469
|
+
File.open(filename,"wb") { |f|
|
470
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
471
|
+
np.start { |http|
|
472
|
+
response = http.get(query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
|
473
|
+
f.write(response.body)
|
474
|
+
}
|
475
|
+
}
|
476
|
+
Debugger::say("Completed getting attachment.")
|
477
|
+
true
|
478
|
+
else
|
479
|
+
Debugger::say("Failed to get attachment: not connected.")
|
480
|
+
false
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
#
|
485
|
+
# return array of labels
|
486
|
+
# desc get label lists
|
487
|
+
#
|
488
|
+
def labels()
|
489
|
+
if is_connected()
|
490
|
+
fetch(:standard=>"inbox") {|s| s.label_list }
|
491
|
+
else
|
492
|
+
nil
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
#
|
497
|
+
# return string
|
498
|
+
# param string label
|
499
|
+
# desc validate label
|
500
|
+
#
|
501
|
+
def validate_label(label)
|
502
|
+
label.strip!
|
503
|
+
if label==''
|
504
|
+
raise ArgumentError, 'Error: Labels cannot empty string'
|
505
|
+
end
|
506
|
+
if label.length > 40
|
507
|
+
raise ArgumentError, 'Error: Labels cannot contain more than 40 characters'
|
508
|
+
end
|
509
|
+
if label.include?('^')
|
510
|
+
raise ArgumentError, "Error: Labels cannot contain the character '^'"
|
511
|
+
end
|
512
|
+
label
|
513
|
+
end
|
514
|
+
|
515
|
+
#
|
516
|
+
# return boolean
|
517
|
+
# param string label
|
518
|
+
# desc create label
|
519
|
+
#
|
520
|
+
def create_label(label)
|
521
|
+
if is_connected()
|
522
|
+
perform_action(GM_ACT_CREATELABEL, '', validate_label(label))
|
523
|
+
else
|
524
|
+
false
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
#
|
529
|
+
# return boolean
|
530
|
+
# param string label
|
531
|
+
# desc create label
|
532
|
+
#
|
533
|
+
def delete_label(label)
|
534
|
+
if is_connected()
|
535
|
+
perform_action(GM_ACT_DELETELABEL, '', validate_label(label))
|
536
|
+
else
|
537
|
+
false
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
#
|
542
|
+
# return boolean
|
543
|
+
# param string old_label
|
544
|
+
# param string new_label
|
545
|
+
# desc create label
|
546
|
+
#
|
547
|
+
def rename_label(old_label,new_label)
|
548
|
+
if is_connected()
|
549
|
+
perform_action(GM_ACT_RENAMELABEL, '',
|
550
|
+
validate_label(old_label) +'^' + validate_label(new_label))
|
551
|
+
else
|
552
|
+
false
|
553
|
+
end
|
554
|
+
end
|
555
|
+
|
556
|
+
#
|
557
|
+
# return boolean
|
558
|
+
# param string msgid
|
559
|
+
# param string label
|
560
|
+
# desc apply label to message
|
561
|
+
#
|
562
|
+
def apply_label(id,label)
|
563
|
+
if is_connected()
|
564
|
+
perform_action(GM_ACT_APPLYLABEL, id, validate_label(label))
|
565
|
+
else
|
566
|
+
false
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
#
|
571
|
+
# return boolean
|
572
|
+
# param string msgid
|
573
|
+
# param string label
|
574
|
+
# desc remove label from message
|
575
|
+
#
|
576
|
+
def remove_label(id,label)
|
577
|
+
if is_connected()
|
578
|
+
perform_action(GM_ACT_REMOVELABEL, id, validate_label(label))
|
579
|
+
else
|
580
|
+
false
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
#
|
585
|
+
# return boolean
|
586
|
+
# param string hash_param
|
587
|
+
# desc remove label from message
|
588
|
+
#
|
589
|
+
def update_preference(hash_param)
|
590
|
+
if is_connected()
|
591
|
+
args = {}
|
592
|
+
hash_param.keys.each do |k|
|
593
|
+
case k
|
594
|
+
when :max_page_size
|
595
|
+
args['p_ix_nt'] = hash_param[k]
|
596
|
+
when :keyboard_shortcuts
|
597
|
+
args['p_bx_hs'] = hash_param[k] ? '1' : '0'
|
598
|
+
when :indicators
|
599
|
+
args['p_bx_sc'] = hash_param[k] ? '1' : '0'
|
600
|
+
when :display_language
|
601
|
+
args['p_sx_dl'] = hash_param[k]
|
602
|
+
when :signature
|
603
|
+
args['p_sx_sg'] = hash_param[k]
|
604
|
+
when :reply_to
|
605
|
+
args['p_sx_rt'] = hash_param[k]
|
606
|
+
when :snippets
|
607
|
+
args['p_bx_ns'] = hash_param[k] ? '0' : '1'
|
608
|
+
when :display_name
|
609
|
+
args['p_sx_dn'] = hash_param[k]
|
610
|
+
end
|
611
|
+
end
|
612
|
+
param = '&' + args.to_a.map {|x| x.join('=')}.join('&')
|
613
|
+
perform_action(GM_ACT_PREFERENCE,'', param)
|
614
|
+
else
|
615
|
+
false
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
#
|
620
|
+
# return boolean
|
621
|
+
# param string msgid
|
622
|
+
# desc apply star to a message
|
623
|
+
#
|
624
|
+
def apply_star(msgid)
|
625
|
+
if is_connected()
|
626
|
+
perform_action(GM_ACT_STAR,msgid, '')
|
627
|
+
else
|
628
|
+
false
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
#
|
633
|
+
# return boolean
|
634
|
+
# param string msgid
|
635
|
+
# desc remove star from a message
|
636
|
+
#
|
637
|
+
def remove_star(msgid)
|
638
|
+
if is_connected()
|
639
|
+
perform_action(GM_ACT_UNSTAR,msgid, '')
|
640
|
+
else
|
641
|
+
false
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
#
|
646
|
+
# return boolean
|
647
|
+
# param string msgid
|
648
|
+
# desc archive a message
|
649
|
+
#
|
650
|
+
def archive(msgid)
|
651
|
+
if is_connected()
|
652
|
+
perform_action(GM_ACT_ARCHIVE,msgid, '')
|
653
|
+
else
|
654
|
+
false
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
#
|
659
|
+
# return boolean
|
660
|
+
# param string msgid
|
661
|
+
# desc archive a message
|
662
|
+
#
|
663
|
+
def unarchive(msgid)
|
664
|
+
if is_connected()
|
665
|
+
perform_action(GM_ACT_INBOX,msgid, '')
|
666
|
+
else
|
667
|
+
false
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
#
|
672
|
+
# return hash of preference
|
673
|
+
# desc get preferences
|
674
|
+
#
|
675
|
+
def preference()
|
676
|
+
if is_connected()
|
677
|
+
fetch(:preference=>"all").preference
|
678
|
+
else
|
679
|
+
nil
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
#
|
684
|
+
# return array of messages
|
685
|
+
# desc get message lists
|
686
|
+
#
|
687
|
+
def messages(hash_param)
|
688
|
+
if is_connected()
|
689
|
+
fetch(hash_param) {|s| s.box }
|
690
|
+
else
|
691
|
+
nil
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
#
|
696
|
+
# return string
|
697
|
+
# param string query
|
698
|
+
# desc Dump everything to output.
|
699
|
+
#
|
700
|
+
def dump(query)
|
701
|
+
page = ''
|
702
|
+
if is_connected()
|
703
|
+
Debugger::say("Dumping...")
|
704
|
+
query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
|
705
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
706
|
+
np.start { |http|
|
707
|
+
response = http.get(GM_LNK_GMAIL + "?"+query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
|
708
|
+
page = response.body
|
709
|
+
}
|
710
|
+
|
711
|
+
Debugger::say("Finished dumping " + page.length.to_s + " bytes.")
|
712
|
+
else # not logged in yet
|
713
|
+
Debugger::say("Failed to dump: not connected.")
|
714
|
+
end
|
715
|
+
page
|
716
|
+
end
|
717
|
+
|
718
|
+
def stripslashes(string)
|
719
|
+
encode(string.gsub("\\\"", "\"").gsub("\\'", "'").gsub("\\\\", "\\"))
|
720
|
+
end
|
721
|
+
|
722
|
+
|
723
|
+
#
|
724
|
+
# return bool
|
725
|
+
# param string to
|
726
|
+
# param string subject
|
727
|
+
# param string body
|
728
|
+
# param string cc
|
729
|
+
# param string bcc
|
730
|
+
# param string msg_id
|
731
|
+
# param string thread_id
|
732
|
+
# param string[] files
|
733
|
+
# desc Send GMail.
|
734
|
+
#
|
735
|
+
def send(*param)
|
736
|
+
if param.length==1 && param[0].class==Hash
|
737
|
+
param = param[0]
|
738
|
+
to = param[:to] || ''
|
739
|
+
subject = param[:subject] || ''
|
740
|
+
body = param[:body] || ''
|
741
|
+
cc = param[:cc] || ''
|
742
|
+
bcc = param[:bcc] || ''
|
743
|
+
msg_id = param[:msg_id] || ''
|
744
|
+
thread_id = param[:msg_id] || ''
|
745
|
+
files = param[:files] || []
|
746
|
+
draft = param[:draft] || false
|
747
|
+
draft_id = param[:draft_id] || ''
|
748
|
+
elsif param.length==10
|
749
|
+
to, subject, body, cc, bcc, msg_id, thread_id, files, draft, draft_id = param
|
750
|
+
else
|
751
|
+
raise ArgumentError, 'Invalid argument'
|
752
|
+
end
|
753
|
+
|
754
|
+
if is_connected()
|
755
|
+
Debugger::say("Starting to send mail...")
|
756
|
+
|
757
|
+
postdata = {}
|
758
|
+
if draft
|
759
|
+
postdata["view"] = "sd"
|
760
|
+
postdata["draft"] = draft_id
|
761
|
+
postdata["rm"] = msg_id
|
762
|
+
postdata["th"] = thread_id
|
763
|
+
else
|
764
|
+
postdata["view"] = "sm"
|
765
|
+
postdata["draft"] = draft_id
|
766
|
+
postdata["rm"] = msg_id
|
767
|
+
postdata["th"] = thread_id
|
768
|
+
end
|
769
|
+
postdata["msgbody"] = stripslashes(body)
|
770
|
+
postdata["to"] = stripslashes(to)
|
771
|
+
postdata["subject"] = stripslashes(subject)
|
772
|
+
postdata["cc"] = stripslashes(cc)
|
773
|
+
postdata["bcc"] = stripslashes(bcc)
|
774
|
+
|
775
|
+
postdata["cmid"] = 1
|
776
|
+
postdata["ishtml"] = 0
|
777
|
+
|
778
|
+
cc = @cookie_str.split(';')
|
779
|
+
cc.each {|cc_part|
|
780
|
+
cc_parts = cc_part.split('=')
|
781
|
+
if(cc_parts[0]=='GMAIL_AT')
|
782
|
+
postdata["at"] = cc_parts[1]
|
783
|
+
break
|
784
|
+
end
|
785
|
+
}
|
786
|
+
|
787
|
+
boundary = "----#{Time.now.to_i}#{(rand(2000)*2147483.648).to_i}"
|
788
|
+
postdata2 = []
|
789
|
+
postdata.each {|k,v|
|
790
|
+
postdata2.push("Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n")
|
791
|
+
}
|
792
|
+
|
793
|
+
files.each_with_index {|f,i|
|
794
|
+
content = File.open(f,'rb') { |c| c.read }
|
795
|
+
postdata2.push("Content-Disposition: form-data; name=\"file#{i}\"; filename=\"#{File.basename(f)}\"\r\n" +
|
796
|
+
"Content-Transfer-Encoding: binary\r\n" +
|
797
|
+
"Content-Type: application/octet-stream\r\n\r\n" + content + "\r\n")
|
798
|
+
}
|
799
|
+
|
800
|
+
postdata = postdata2.collect { |p|
|
801
|
+
"--" + boundary + "\r\n" + p
|
802
|
+
}.join('') + "--" + boundary + "--\r\n"
|
803
|
+
|
804
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
805
|
+
response = nil
|
806
|
+
# np.set_debug_output($stderr)
|
807
|
+
np.start { |http|
|
808
|
+
response = http.post(GM_LNK_GMAIL, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT,'Content-type' => 'multipart/form-data; boundary=' + boundary } )
|
809
|
+
}
|
810
|
+
Debugger::say("Finished sending email.")
|
811
|
+
true
|
812
|
+
else
|
813
|
+
Debugger::say("Failed to send email: not connected.")
|
814
|
+
false
|
815
|
+
end
|
816
|
+
|
817
|
+
end
|
818
|
+
|
819
|
+
#
|
820
|
+
# return bool
|
821
|
+
# param constant act
|
822
|
+
# param string[] id
|
823
|
+
# param string para
|
824
|
+
# desc Perform action on messages.
|
825
|
+
#
|
826
|
+
def perform_action(act, id, para)
|
827
|
+
|
828
|
+
if is_connected()
|
829
|
+
Debugger::say("Start performing action...")
|
830
|
+
|
831
|
+
if (act == GM_ACT_DELFOREVER)
|
832
|
+
perform_action(GM_ACT_TRASH, id, 0) # trash it before
|
833
|
+
end
|
834
|
+
postdata = "act="
|
835
|
+
|
836
|
+
action_codes = ["ib", "cc_", "dc_", "nc_", "ac_", "rc_", "prefs", "st", "xst",
|
837
|
+
"sp", "us", "rd", "ur", "tr", "dl", "rc_^i", "ib", "ib", "dd", "dm", "dl", "dl"]
|
838
|
+
postdata += action_codes[act] ? action_codes[act] : action_codes[GM_ACT_INBOX]
|
839
|
+
if ([GM_ACT_APPLYLABEL,GM_ACT_REMOVELABEL,GM_ACT_CREATELABEL,
|
840
|
+
GM_ACT_DELETELABEL,GM_ACT_RENAMELABEL,GM_ACT_PREFERENCE].include?(act))
|
841
|
+
postdata += para.to_s
|
842
|
+
end
|
843
|
+
|
844
|
+
cc = @cookie_str.split(';')
|
845
|
+
cc.each {|cc_part|
|
846
|
+
cc_parts = cc_part.split('=')
|
847
|
+
if(cc_parts[0]=='GMAIL_AT')
|
848
|
+
postdata += "&at=" + cc_parts[1]
|
849
|
+
break
|
850
|
+
end
|
851
|
+
}
|
852
|
+
|
853
|
+
if (act == GM_ACT_TRASHMSG)
|
854
|
+
postdata += "&m=" + id.to_s
|
855
|
+
else
|
856
|
+
if id.class==Array
|
857
|
+
id.each {|t| postdata += "&t="+t.to_s }
|
858
|
+
else
|
859
|
+
postdata += "&t="+id.to_s
|
860
|
+
end
|
861
|
+
end
|
862
|
+
postdata += "&vp="
|
863
|
+
|
864
|
+
if [GM_ACT_UNTRASH,GM_ACT_DELFOREVER,GM_ACT_DELTRASHED].include?(act)
|
865
|
+
link = GM_LNK_GMAIL+"?search=trash&view=tl&start=0"
|
866
|
+
elsif (act == GM_ACT_DELSPAM)
|
867
|
+
link = GM_LNK_GMAIL+"?search=spam&view=tl&start=0"
|
868
|
+
else
|
869
|
+
link = GM_LNK_GMAIL+"?search=query&q=&view=tl&start=0"
|
870
|
+
end
|
871
|
+
|
872
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
873
|
+
# np.set_debug_output($stderr)
|
874
|
+
np.start { |http|
|
875
|
+
response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
|
876
|
+
result = response.body
|
877
|
+
}
|
878
|
+
|
879
|
+
Debugger::say("Finished performing action.")
|
880
|
+
return true
|
881
|
+
else
|
882
|
+
Debugger::say("Failed to perform action: not connected.")
|
883
|
+
return false
|
884
|
+
end
|
885
|
+
|
886
|
+
end
|
887
|
+
|
888
|
+
#
|
889
|
+
# return void
|
890
|
+
# desc Disconnect from GMail.
|
891
|
+
#
|
892
|
+
def disconnect()
|
893
|
+
Debugger::say("Start disconnecting...")
|
894
|
+
|
895
|
+
response = nil
|
896
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
897
|
+
# np.set_debug_output($stderr)
|
898
|
+
np.start { |http|
|
899
|
+
response = http.get(GM_LNK_LOGOUT,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
|
900
|
+
}
|
901
|
+
arr = URI::split(response["Location"])
|
902
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 80)
|
903
|
+
# np.set_debug_output($stderr)
|
904
|
+
np.start { |http|
|
905
|
+
response = http.get(arr[5]+'?'+arr[7], {'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
|
906
|
+
}
|
907
|
+
|
908
|
+
Debugger::say("Logged-out from GMail.")
|
909
|
+
@cookie_str = ''
|
910
|
+
Debugger::say("Completed disconnecting.")
|
911
|
+
end
|
912
|
+
|
913
|
+
#
|
914
|
+
# return GMailSnapshot
|
915
|
+
# param constant type
|
916
|
+
# desc Get GMSnapshot by type.
|
917
|
+
#
|
918
|
+
def snapshot(type)
|
919
|
+
if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY,GM_PREFERENCE,GM_CONTACT].include?(type)
|
920
|
+
return GMailSnapshot.new(type, @raw, @charset)
|
921
|
+
else
|
922
|
+
return GMailSnapshot.new(GM_STANDARD, @raw, @charset) # assuming normal by default
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
def snap(type)
|
927
|
+
action = GM_STANDARD
|
928
|
+
case type
|
929
|
+
when :standard
|
930
|
+
action = GM_STANDARD
|
931
|
+
when :label
|
932
|
+
action = GM_LABEL
|
933
|
+
when :conversation
|
934
|
+
action = GM_CONVERSATION
|
935
|
+
when :query
|
936
|
+
action = GM_QUERY
|
937
|
+
when :preference
|
938
|
+
action = GM_PREFERENCE
|
939
|
+
when :contact
|
940
|
+
action = GM_CONTACT
|
941
|
+
else
|
942
|
+
raise ArgumentError, 'Invalid type'
|
943
|
+
end
|
944
|
+
snapshot(action)
|
945
|
+
end
|
946
|
+
|
947
|
+
#
|
948
|
+
# return bool
|
949
|
+
# param string email
|
950
|
+
# desc Send Gmail invite to email
|
951
|
+
#
|
952
|
+
def invite(email)
|
953
|
+
|
954
|
+
if is_connected()
|
955
|
+
Debugger::say("Start sending invite...")
|
956
|
+
|
957
|
+
postdata = "act=ii&em=" + URI.escape(email)
|
958
|
+
|
959
|
+
cc = @cookie_str.split(';')
|
960
|
+
cc.each {|cc_part|
|
961
|
+
cc_parts = cc_part.split('=')
|
962
|
+
if(cc_parts[0]=='GMAIL_AT')
|
963
|
+
postdata += "&at=" + cc_parts[1]
|
964
|
+
break
|
965
|
+
end
|
966
|
+
}
|
967
|
+
|
968
|
+
link = GM_LNK_GMAIL + "?view=ii"
|
969
|
+
|
970
|
+
np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
|
971
|
+
np.use_ssl = true
|
972
|
+
np.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
973
|
+
# np.set_debug_output($stderr)
|
974
|
+
np.start { |http|
|
975
|
+
response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
|
976
|
+
}
|
977
|
+
|
978
|
+
Debugger::say("Finished sending invite.")
|
979
|
+
true
|
980
|
+
else
|
981
|
+
Debugger::say("Failed to send invite: not connected.")
|
982
|
+
false
|
983
|
+
end
|
984
|
+
|
985
|
+
end
|
986
|
+
|
987
|
+
#
|
988
|
+
# return string[]
|
989
|
+
# desc (Static) Get names of standard boxes.
|
990
|
+
#
|
991
|
+
def standard_box()
|
992
|
+
["Inbox","Starred","Sent","Drafts","All","Spam","Trash"]
|
993
|
+
end
|
994
|
+
|
995
|
+
#
|
996
|
+
# return string
|
997
|
+
# param string header
|
998
|
+
# desc (Static Private) Extract cookies from header.
|
999
|
+
#
|
1000
|
+
def __cookies(header)
|
1001
|
+
return '' unless header
|
1002
|
+
arr = []
|
1003
|
+
header.split(', ').each { |x|
|
1004
|
+
if x.include?('GMT')
|
1005
|
+
arr[-1] += ", " + x
|
1006
|
+
else
|
1007
|
+
arr.push x
|
1008
|
+
end
|
1009
|
+
}
|
1010
|
+
arr.delete_if {|x| x.include?('LSID=EXPIRED') }
|
1011
|
+
arr.map! {|x| x.split(';')[0]}
|
1012
|
+
arr.join(';')
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
|
1018
|
+
class GMailSnapshot
|
1019
|
+
|
1020
|
+
GM_STANDARD = 0x001
|
1021
|
+
GM_LABEL = 0x002
|
1022
|
+
GM_CONVERSATION = 0x004
|
1023
|
+
GM_QUERY = 0x008
|
1024
|
+
GM_CONTACT = 0x010
|
1025
|
+
GM_PREFERENCE = 0x020
|
1026
|
+
|
1027
|
+
def decode(str)
|
1028
|
+
return str if @charset.upcase == 'UTF-8'
|
1029
|
+
begin
|
1030
|
+
require 'Win32API'
|
1031
|
+
str = str.unpack("U*").pack("S*") + "\0"
|
1032
|
+
ostr = "\0" * 256
|
1033
|
+
wideCharToMultiByte = Win32API.new('kernel32','WideCharToMultiByte',['L','L','P','L','P','L','L','L'],'L')
|
1034
|
+
wideCharToMultiByte.Call(0,0,str,-1,ostr,256,0,0)
|
1035
|
+
ostr.strip
|
1036
|
+
rescue LoadError
|
1037
|
+
require 'iconv'
|
1038
|
+
Iconv::iconv(@charset,'UTF-8',str)[0]
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def strip_tags(str,allowable_tags='')
|
1043
|
+
str.gsub(/<[^>]*>/, '')
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
#
|
1047
|
+
# return GMailSnapshot
|
1048
|
+
# param constant type
|
1049
|
+
# param array raw
|
1050
|
+
# desc Constructor.
|
1051
|
+
#
|
1052
|
+
attr_reader :gmail_ver, :quota_mb, :quota_per, :preference
|
1053
|
+
attr_reader :std_box_new, :have_invit, :label_list, :label_new
|
1054
|
+
attr_reader :box_name, :box_total, :box_pos, :box
|
1055
|
+
attr_reader :conv_title, :conv_total, :conv_id, :conv_labels, :conv_starred, :conv
|
1056
|
+
attr_reader :contacts
|
1057
|
+
|
1058
|
+
def initialize(type, raw, charset='UTF-8')
|
1059
|
+
|
1060
|
+
@charset = charset
|
1061
|
+
if (raw.class!=Hash)
|
1062
|
+
@created = 0
|
1063
|
+
return nil
|
1064
|
+
elsif (raw.empty?)
|
1065
|
+
@created = 0
|
1066
|
+
return nil
|
1067
|
+
end
|
1068
|
+
if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY].include?(type)
|
1069
|
+
@gmail_ver = raw["v"][1]
|
1070
|
+
if raw["qu"]
|
1071
|
+
@quota_mb = raw["qu"][1]
|
1072
|
+
@quota_per = raw["qu"][3]
|
1073
|
+
end
|
1074
|
+
if (raw["ds"].class!=Array)
|
1075
|
+
@created = 0
|
1076
|
+
return nil
|
1077
|
+
end
|
1078
|
+
@std_box_new = raw["ds"][1..-1]
|
1079
|
+
#if (isset(raw["p"])) {
|
1080
|
+
# @owner = raw["p"][5][1]
|
1081
|
+
# @owner_email = raw["p"][6][1]
|
1082
|
+
#end
|
1083
|
+
@have_invit = raw["i"][1]
|
1084
|
+
@label_list = []
|
1085
|
+
@label_new = []
|
1086
|
+
raw["ct"][1].each {|v|
|
1087
|
+
@label_list.push(v[0])
|
1088
|
+
@label_new.push(v[1])
|
1089
|
+
}
|
1090
|
+
if raw["ts"]
|
1091
|
+
@view = (GM_STANDARD|GM_LABEL|GM_QUERY)
|
1092
|
+
@box_name = raw["ts"][5]
|
1093
|
+
@box_total = raw["ts"][3]
|
1094
|
+
@box_pos = raw["ts"][1]
|
1095
|
+
end
|
1096
|
+
@box = []
|
1097
|
+
if raw["t"]
|
1098
|
+
raw["t"].each {|t|
|
1099
|
+
if (t != "t")
|
1100
|
+
b = {
|
1101
|
+
"id"=>t[0],
|
1102
|
+
"is_read"=>(t[1] != 1 ? 1 : 0),
|
1103
|
+
"new?"=>(t[1] == 1),
|
1104
|
+
"is_starred"=>(t[2] == 1 ? 1 : 0),
|
1105
|
+
"star?"=>(t[2] == 1),
|
1106
|
+
"date"=>decode(strip_tags(t[3])),
|
1107
|
+
"sender"=>decode(strip_tags(t[4])),
|
1108
|
+
"flag"=>t[5],
|
1109
|
+
"subject"=>decode(strip_tags(t[6])),
|
1110
|
+
"snippet"=>decode(t[7]),
|
1111
|
+
"labels"=>t[8].empty? ? [] : t[8].map{|tt|decode(tt)},
|
1112
|
+
"attachment"=>t[9].empty? ? []: decode(t[9]).split(","),
|
1113
|
+
"msgid"=>t[10]
|
1114
|
+
}
|
1115
|
+
@box.push(b)
|
1116
|
+
end
|
1117
|
+
}
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
if (!raw["cs"].nil?)
|
1121
|
+
@view = GM_CONVERSATION
|
1122
|
+
@conv_title = raw["cs"][2]
|
1123
|
+
@conv_total = raw["cs"][8]
|
1124
|
+
@conv_id = raw["cs"][1]
|
1125
|
+
@conv_labels = raw["cs"][5].empty? ? '' : raw["cs"][5]
|
1126
|
+
if !@conv_labels.empty?
|
1127
|
+
ij = @conv_labels.index("^i")
|
1128
|
+
if !ij.nil?
|
1129
|
+
@conv_labels[ij] = "Inbox"
|
1130
|
+
end
|
1131
|
+
ij = @conv_labels.index("^s")
|
1132
|
+
if !ij.nil?
|
1133
|
+
@conv_labels[ij] = "Spam"
|
1134
|
+
end
|
1135
|
+
ij = @conv_labels.index("^k")
|
1136
|
+
if !ij.nil?
|
1137
|
+
@conv_labels[ij] = "Trash"
|
1138
|
+
end
|
1139
|
+
ij = @conv_labels.index("^t")
|
1140
|
+
if !ij.nil?
|
1141
|
+
@conv_labels[ij] = '' # Starred
|
1142
|
+
end
|
1143
|
+
ij = @conv_labels.index("^r")
|
1144
|
+
if !ij.nil?
|
1145
|
+
@conv_labels[ij] = "Drafts"
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
@conv_starred = false
|
1150
|
+
|
1151
|
+
@conv = []
|
1152
|
+
b = {}
|
1153
|
+
raw["mg"].each {|r|
|
1154
|
+
if (r[0] == "mb" && !b.empty?)
|
1155
|
+
b["body"] += r[1]
|
1156
|
+
if (r[2] == 0)
|
1157
|
+
@conv.push(b)
|
1158
|
+
b = {}
|
1159
|
+
end
|
1160
|
+
elsif (r[0] == "mi")
|
1161
|
+
if !b.empty?
|
1162
|
+
@conv.push(b)
|
1163
|
+
b = {}
|
1164
|
+
end
|
1165
|
+
b = {}
|
1166
|
+
b["index"] = r[2]
|
1167
|
+
b["id"] = r[3]
|
1168
|
+
b["is_star"] = r[4]
|
1169
|
+
if (b["is_star"] == 1)
|
1170
|
+
@conv_starred = true
|
1171
|
+
end
|
1172
|
+
b["sender"] = r[6]
|
1173
|
+
b["sender_email"] = r[8].gsub("\"",'') # remove annoying d-quotes in address
|
1174
|
+
b["recv"] = r[9]
|
1175
|
+
b["recv_email"] = r[11].to_s.gsub("\"",'')
|
1176
|
+
b["reply_email"] = r[14].to_s.gsub("\"",'')
|
1177
|
+
b["dt_easy"] = r[10]
|
1178
|
+
b["dt"] = r[15]
|
1179
|
+
b["subject"] = r[16]
|
1180
|
+
b["snippet"] = r[17]
|
1181
|
+
b["attachment"] = []
|
1182
|
+
r[18].each {|bb|
|
1183
|
+
b["attachment"].push({"id"=>bb[0],"filename"=>bb[1],"type"=>bb[2],"size"=>bb[3]})
|
1184
|
+
}
|
1185
|
+
b["is_draft"] = false
|
1186
|
+
b["body"] = ''
|
1187
|
+
elsif (r[0] == "di")
|
1188
|
+
if !b.empty?
|
1189
|
+
@conv.push(b)
|
1190
|
+
b = {}
|
1191
|
+
end
|
1192
|
+
b = {}
|
1193
|
+
b["index"] = r[2]
|
1194
|
+
b["id"] = r[3]
|
1195
|
+
b["is_star"] = r[4]
|
1196
|
+
@conv_starred = (b["is_star"] == 1)
|
1197
|
+
b["sender"] = r[6]
|
1198
|
+
b["sender_email"] = r[8].gsub("\"",'') # remove annoying d-quotes in address
|
1199
|
+
b["recv"] = r[9]
|
1200
|
+
b["recv_email"] = r[11].gsub("\"",'')
|
1201
|
+
b["reply_email"] = r[14].gsub("\"",'')
|
1202
|
+
b["cc_email"] = r[12].gsub("\"",'')
|
1203
|
+
b["bcc_email"] = r[13].gsub("\"",'')
|
1204
|
+
b["dt_easy"] = r[10]
|
1205
|
+
b["dt"] = r[15]
|
1206
|
+
b["subject"] = r[16]
|
1207
|
+
b["snippet"] = r[17]
|
1208
|
+
b["attachment"] = []
|
1209
|
+
r[18].each {|bb|
|
1210
|
+
b["attachment"].push({"id"=>bb[0],"filename"=>bb[1],"type"=>bb[2],"size"=>bb[3]})
|
1211
|
+
}
|
1212
|
+
b["is_draft"] = true
|
1213
|
+
b["draft_parent"] = r[5]
|
1214
|
+
b["body"] = r[20]
|
1215
|
+
end
|
1216
|
+
}
|
1217
|
+
@conv.push(b) unless b.empty?
|
1218
|
+
end
|
1219
|
+
elsif type == GM_CONTACT
|
1220
|
+
@contacts = []
|
1221
|
+
if raw["ce"]
|
1222
|
+
raw["ce"].each {|a|
|
1223
|
+
b = {
|
1224
|
+
"name"=>a[2],
|
1225
|
+
"email"=>a[4].gsub("\"",'')
|
1226
|
+
}
|
1227
|
+
if !a[5].empty?
|
1228
|
+
b["notes"] = a[5]
|
1229
|
+
end
|
1230
|
+
@contacts.push(b)
|
1231
|
+
}
|
1232
|
+
end
|
1233
|
+
@view = GM_CONTACT
|
1234
|
+
elsif type == GM_PREFERENCE
|
1235
|
+
prefs = {}
|
1236
|
+
raw["p"].each_with_index { |v,i|
|
1237
|
+
prefs[v[0]] = v[1] if i>0
|
1238
|
+
}
|
1239
|
+
@preference = {'max_page_size'=>'10',
|
1240
|
+
'keyboard_shortcuts'=>false,
|
1241
|
+
'indicators'=>false,
|
1242
|
+
'display_language'=>'en',
|
1243
|
+
'signature'=>'',
|
1244
|
+
'reply_to'=>'',
|
1245
|
+
'snippets'=>false,
|
1246
|
+
'display_name'=>'',
|
1247
|
+
}
|
1248
|
+
prefs.each do |k,v|
|
1249
|
+
case k
|
1250
|
+
when 'ix_nt'
|
1251
|
+
@preference['max_page_size'] = v
|
1252
|
+
when 'bx_hs'
|
1253
|
+
@preference['keyboard_shortcuts'] = (v == '1')
|
1254
|
+
when 'bx_sc'
|
1255
|
+
@preference['indicators'] = (v == '1')
|
1256
|
+
when 'sx_dl'
|
1257
|
+
@preference['display_language'] = v
|
1258
|
+
when 'sx_sg'
|
1259
|
+
@preference['signature'] = (v == '0') ? '' : v
|
1260
|
+
when 'sx_rt'
|
1261
|
+
@preference['reply_to'] = v
|
1262
|
+
when 'bx_ns'
|
1263
|
+
@preference['snippets'] = (v == '0')
|
1264
|
+
when 'sx_dn'
|
1265
|
+
@preference['display_name'] = v
|
1266
|
+
end
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
@label_list = []
|
1270
|
+
@label_total = []
|
1271
|
+
raw["cta"][1].each { |v|
|
1272
|
+
@label_list.push(v[0])
|
1273
|
+
@label_total.push(v[1])
|
1274
|
+
}
|
1275
|
+
@filter = []
|
1276
|
+
@filter_rules = []
|
1277
|
+
raw["fi"][1].each { |fi|
|
1278
|
+
b = {
|
1279
|
+
"id" => fi[0],
|
1280
|
+
"query" => fi[1],
|
1281
|
+
"star" => fi[3],
|
1282
|
+
"label" => fi[4],
|
1283
|
+
"archive" => fi[5],
|
1284
|
+
"trash" => fi[6] #,"forward" => fi[7]
|
1285
|
+
}
|
1286
|
+
bb = {
|
1287
|
+
"from" => fi[2][0],
|
1288
|
+
"to" => fi[2][1],
|
1289
|
+
"subject" => fi[2][2],
|
1290
|
+
"has" => fi[2][3],
|
1291
|
+
"hasnot" => fi[2][4],
|
1292
|
+
"attach" => fi[2][5]
|
1293
|
+
}
|
1294
|
+
|
1295
|
+
@filter.push(b)
|
1296
|
+
@filter_rules.push(bb)
|
1297
|
+
}
|
1298
|
+
@view = GM_PREFERENCE
|
1299
|
+
else
|
1300
|
+
@created = 0
|
1301
|
+
return nil
|
1302
|
+
end
|
1303
|
+
@created = 1
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
|
1309
|
+
class Debugger
|
1310
|
+
def Debugger.say(str)
|
1311
|
+
puts str if $D_ON
|
1312
|
+
end
|
1313
|
+
end
|
1314
|
+
|