gmailer 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGES +4 -0
  2. data/README +40 -0
  3. data/gmailer-0.1.5.gem +0 -0
  4. data/gmailer.gempsec +1 -1
  5. data/gmailer.rb +2165 -1729
  6. metadata +4 -3
data/CHANGES CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.1.6 - 16-May-2007
2
+ - some source code refactoring
3
+ - add edit_concact, add_contact, delete_contact, add_sender_to_contact method
4
+
1
5
  == 0.1.5 - 25-Jan-2007
2
6
  - fix bad URI problem connection problem for some accounts
3
7
  - add :ishtml parameter to specify the mail should be sent as text or as HTML
data/README CHANGED
@@ -41,6 +41,46 @@
41
41
  g.fetch(:contact=>"freq").each do |item|
42
42
  puts "Name: #{item.name} Email: #{item.email}"
43
43
  end
44
+
45
+ puts "Your all contact addresses:"
46
+ g.fetch(:contact=>"all").each do |item|
47
+ puts "Name: #{item.name} Email: #{item.email}"
48
+ end
49
+
50
+ puts "Your groups:"
51
+ g.fetch(:contact=>"group").each do |item|
52
+ puts "ID: #{item.id} Name: #{item.name} Email: #{item.email}"
53
+ g.fetch(:contact=>"group_detail",:param=>item.id).each do |detail|
54
+ puts " Group Name: #{detail.group_names} Group Email: #{detail.group_email}"
55
+ detail.members.each do |m|
56
+ puts " Member ID: #{m.id}, Name: #{m.name}, Email: #{m.email}"
57
+ end
58
+ end
59
+ end
60
+
61
+ puts "Search and Detail Contact:"
62
+ g.fetch(:contact=>"search",:param=>'phasis').each do |item|
63
+ puts "Name: #{item.name} Email: #{item.email}"
64
+ g.fetch(:contact=>"detail",:param=>item.id).each do |detail|
65
+ puts "ID: #{item.id} Name: #{item.name} Email: #{item.email} Notes: #{item.notes} Details: #{item.details}"
66
+ end
67
+ end
68
+
69
+ puts "Edit contact info:"
70
+ contact_id = "7"
71
+ g.edit_contact(contact_id, "John", "john@company.com", 'Supervisor of project X',[{:phone=>'642-5678',:mobile=>'837-1233',:fax=>'111-1111'}])
72
+
73
+ puts "Add contact info:"
74
+ g.add_contact('Mike', 'mike@company.com', 'Mike',[{:phone=>'642-5678',:mobile=>'837-1233',:fax=>'111-1111'},{:phone=>'1-23-5378',:mobile=>'8-31-1233',:fax=>'122-1321'}])
75
+
76
+ puts "Delete Contact:"
77
+ contact_id = "18"
78
+ g.delete_contact(contact_id)
79
+
80
+ puts "Add sender to Contact:"
81
+ msg_id = '11276ede68783f04'
82
+ g.add_sender_to_contact(msg_id)
83
+
44
84
  end
45
85
 
46
86
  === Get list of labels and get list of messages
data/gmailer-0.1.5.gem ADDED
Binary file
data/gmailer.gempsec CHANGED
@@ -2,7 +2,7 @@ require "rubygems"
2
2
 
3
3
  spec = Gem::Specification.new do |gem|
4
4
  gem.name = "gmailer"
5
- gem.version = "0.1.5"
5
+ gem.version = "0.1.6"
6
6
  gem.author = "Park Heesob"
7
7
  gem.email = "phasis_AT_gmail.com"
8
8
  gem.homepage = "http://rubyforge.org/projects/gmailutils"
data/gmailer.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  # project home page: http://rubyforge.org/projects/gmailutils
7
7
  #
8
8
 
9
- # $D_ON = false
9
+ DEBUG = false
10
10
 
11
11
  require 'net/https'
12
12
 
@@ -52,1741 +52,2177 @@ GM_ACT_DELSPAM = 20 # delete spam, forever
52
52
  GM_ACT_DELTRASH = 21 # delete trash message, forever
53
53
 
54
54
  module GMailer
55
- VERSION = "0.1.5"
55
+ VERSION = "0.1.6"
56
56
  attr_accessor :connection
57
57
 
58
58
  # the main class.
59
59
  class Connection
60
- @cookie_str
61
- @login
62
- @pwd
63
- @raw # raw packets
64
- @contact_raw # raw packets for address book
65
- @timezone
66
- @created
67
- @proxy_host
68
- @proxy_port
69
- @proxy_user
70
- @proxy_pass
71
-
72
- def encode(str)
73
- return str if @charset.upcase == 'UTF-8'
74
- begin
75
- require 'Win32API'
76
- str += "\0"
77
- ostr = "\0" * str.length*2
78
- multiByteToWideChar = Win32API.new('kernel32','MultiByteToWideChar',['L','L','P','L','P','L'],'L')
79
- multiByteToWideChar.Call(0,0,str,-1,ostr,str.length*2)
80
- (ostr.strip + "\0").unpack("S*").pack("U*")
81
- rescue LoadError
82
- require 'iconv'
83
- Iconv::iconv('UTF-8',@charset,str)[0]
84
- end
85
- end
86
-
87
- #
88
- # return GMailer
89
- # desc Constructor
90
- #
91
- def initialize(*param)
92
- @login = ''
93
- @pwd = ''
94
- @raw = {}
95
- @proxy_host = nil
96
- @proxy_port = 0
97
- @proxy_user = nil
98
- @proxy_pass = nil
99
- @proxy = false
100
- @type = 0
101
- if param.length==1 && param[0].class==Hash
102
- param = param[0]
103
- set_login_info(param[:username]||'',param[:password]||'')
104
- @proxy_host = param[:proxy_host]
105
- @proxy = !@proxy_host.nil?
106
- @proxy_port = param[:proxy_port] || 80
107
- @proxy_user = param[:proxy_user]
108
- @proxy_pass = param[:proxy_pass]
109
- @charset = param[:charset] || 'UTF-8'
110
- elsif param.length==0
111
- @charset = 'UTF-8'
112
- elsif param.length==1
113
- @charset = param[0]
114
- elsif param.length==2
115
- @charset = 'UTF-8'
116
- set_login_info(param[0],param[1])
117
- elsif param.length==3
118
- @charset = param[2]
119
- set_login_info(param[0],param[1])
120
- else
121
- raise ArgumentError, 'Invalid argument'
122
- end
123
- raise 'Not connected, verify the credentials.' unless connect_no_cookie
124
- end
125
-
126
- #
127
- # return void
128
- # param string my_login
129
- # param string my_pwd
130
- # param int my_tz
131
- # desc Set GMail login information.
132
- #
133
- def set_login_info(my_login, my_pwd, my_tz=0)
134
- @login = my_login
135
- @pwd = my_pwd
136
- @timezone = (my_tz*-60).to_i.to_s
137
- end
138
-
139
- #
140
- # return bool
141
- # desc Connect to GMail without setting any session/cookie.
142
- #
143
- def connect_no_cookie()
144
-
145
- postdata = "service=mail&Email=" + URI.escape(@login) + "&Passwd=" + URI.escape(@pwd) + "&null=Sign%20in&continue=" +
146
- URI.escape('https://mail.google.com/mail/?ui=html&zy=l') + "&rm=false&PersistentCookie=yes"
147
-
148
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new("www.google.com", 443)
149
- np.use_ssl = true
150
- np.verify_mode = OpenSSL::SSL::VERIFY_NONE
151
- result = ''
152
- response = nil
153
- # np.set_debug_output($stderr)
154
- np.start { |http|
155
- response = http.post(GM_LNK_LOGIN, postdata,{'Content-Type' => 'application/x-www-form-urlencoded','User-agent' => GM_USER_AGENT} )
156
- result = response.body
157
- }
158
-
159
- if result.include?("errormsg")
160
- @cookie_str = ''
161
- return false
162
- end
163
-
164
- cookies = __cookies(response['set-cookie'])
165
- arr = URI::split(response["Location"])
166
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
167
- np.use_ssl = true
168
- np.verify_mode = OpenSSL::SSL::VERIFY_NONE
169
- result = ''
170
- response = nil
171
- # np.set_debug_output($stderr)
172
- np.start { |http|
173
- response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
174
- result = response.body
175
- }
176
-
177
- if result.include?("Redirecting") && result =~ /url='(.+?)'/
178
- url = $1.gsub("&","&")
179
- result = ''
180
- cookies += ';'+ __cookies(response['set-cookie'])
181
- response = nil
182
- # np.set_debug_output($stderr)
183
- np.start { |http|
184
- response = http.get(url, {'Cookie' => cookies,'User-agent'=> GM_USER_AGENT} )
185
- result = response.body
186
- }
187
- end
60
+ @cookie_str
61
+ @login
62
+ @pwd
63
+ @raw # raw packets
64
+ @contact_raw # raw packets for address book
65
+ @timezone
66
+ @created
67
+ @proxy_host
68
+ @proxy_port
69
+ @proxy_user
70
+ @proxy_pass
188
71
 
189
- if response["Location"]!=nil
190
- arr = URI::split(response["Location"])
191
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
192
- np.use_ssl = true
193
- np.verify_mode = OpenSSL::SSL::VERIFY_NONE
194
- result = ''
195
- response = nil
196
- # np.set_debug_output($stderr)
197
- np.start { |http|
198
- response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
199
- result = response.body
200
- }
201
- end
202
-
203
- @loc = "http://mail.google.com/mail/"
204
- cookies += ';' + __cookies(response['set-cookie'])
205
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
206
- np.use_ssl = true
207
- np.verify_mode = OpenSSL::SSL::VERIFY_NONE
208
- # np.set_debug_output($stderr)
209
- np.start { |http|
210
- response = http.get(@loc, {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
211
- result = response.body
212
- }
213
- cookies += ';' + __cookies(response['set-cookie'])
214
-
215
- @cookie_str = cookies + ";TZ=" + @timezone
216
-
217
- return true
218
- end
219
-
220
- #
221
- # return bool
222
- # desc Connect to GMail with default session management settings.
223
- #
224
- def connect()
225
- raise 'Not connected, verify the credentials.' unless connect_no_cookie()
226
- end
227
-
228
-
229
- #
230
- # return bool
231
- # desc See if it is connected to GMail.
232
- #
233
- def connected?
234
- !@cookie_str.empty?
235
- end
236
-
237
- #
238
- # return bool
239
- # param string query
240
- # desc Fetch contents by URL query.
241
- #
242
- def __fetch(query)
243
- if connected?
244
- query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
245
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
246
- # np.set_debug_output($stderr)
247
- inbox = ''
248
- np.start { |http|
249
- response = http.get(GM_LNK_GMAIL + "?" + query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
250
- inbox = response.body
251
- }
252
- if @type == GM_SHOWORIGINAL
253
- @raw = inbox
254
- else
255
- state = 0
256
- tmp = ''
257
- inbox.each do |l|
258
- if /^D\(.*\);$/.match(l)
259
- state = 0
260
- tmp += l
261
- elsif l[0,3]=='D(['
262
- state = 1
263
- tmp += l.chomp
264
- elsif l[0,2]==');'
265
- state = 2
266
- tmp += ")\n"
267
- elsif state == 1
268
- tmp += l.chomp
269
- end
270
- end
271
- inbox = tmp
272
- matches = inbox.scan(/D\((.*)\)/).flatten
273
- packets = {}
274
- matches.each {|x|
275
- tmp = eval(x.gsub(",,",",'',").gsub(",,",",'',").gsub(",]",",'']").gsub('#{','#\{'))
276
- if (packets[tmp[0]] || (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di"))
277
- if (tmp[0]=="t"||tmp[0]=="ts"||tmp[0]=="a"||tmp[0]=="cl")
278
- packets[tmp[0]] += tmp[1..-1]
279
- end
280
- if (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di")
281
- if !packets["mg"].nil?
282
- packets["mg"].push(tmp)
283
- else
284
- packets["mg"] = [tmp]
285
- end
286
- end
287
- else
288
- packets[tmp[0]] = tmp
289
- end
290
- }
291
- if packets["cl"] && packets["cl"].length > 1
292
- packets["ce"] = []
293
- for i in 1 .. packets["cl"].length-1
294
- packets["ce"].push(packets["cl"][i])
295
- end
296
- end
297
- @raw = packets
298
- end
299
-
300
- true
301
- else # not logged in yet
302
- false
303
- end
304
- end
305
- private :__fetch
306
-
307
- #
308
- # return bool
309
- # param constant type
310
- # param mixed box
311
- # param int pos
312
- # desc Fetch contents from GMail by type.
313
- #
314
- def fetch_box(type, box, pos)
315
- @type = type
316
- if connected?
317
- case type
318
- when GM_STANDARD
319
- q = "search=" + box.downcase + "&view=tl&start=" + pos.to_s
320
-
321
- when GM_LABEL
322
- q = "search=cat&cat=" + box.to_s + "&view=tl&start=" + pos.to_s
323
-
324
- when GM_CONVERSATION
325
- q = "search=all&ser=1&view=cv"
326
- if (box.class==Array)
327
- q += "&th=" + box[0].to_s
328
- for i in 1 .. box.length
329
- q += "&msgs=" + box[i].to_s
330
- end
331
- else
332
- q += "&th=" + box.to_s
333
- end
334
-
335
- when GM_CONV_SPAM
336
- q = "search=spam&ser=1&view=cv"
337
- if (box.class==Array)
338
- q += "&th=" + box[0].to_s
339
- for i in 1 .. box.length
340
- q += "&msgs=" + box[i].to_s
341
- end
342
- else
343
- q += "&th=" + box.to_s
344
- end
345
-
346
- when GM_CONV_TRASH
347
- q = "search=trash&ser=1&view=cv"
348
- if (box.class==Array)
349
- q += "&th=" + box[0].to_s
350
- for i in 1 .. box.length
351
- q += "&msgs=" + box[i].to_s
352
- end
353
- else
354
- q += "&th=" + box.to_s
355
- end
356
-
357
- when GM_SHOWORIGINAL
358
- q = "view=om&th=" + box.to_s
359
-
360
- when GM_QUERY
361
- q = "search=query&q=" + URI.escape(box.to_s) + "&view=tl&start=" + pos.to_s
362
-
363
- when GM_PREFERENCE
364
- q = "view=pr&pnl=g"
365
-
366
- when GM_CONTACT
367
- if box.downcase == "all"
368
- q = "view=cl&search=contacts&pnl=a"
369
- else
370
- q = "view=cl&search=contacts&pnl=d"
371
- end
372
-
373
- else
374
- q = "search=inbox&view=tl&start=0&init=1"
375
- end
376
-
377
- __fetch(q)
378
- else
379
- false
380
- end
381
- end
382
-
383
- #
384
- # return snapshot
385
- # param constant type
386
- # param mixed box
387
- # param int pos
388
- # desc Fetch contents from GMail by type.
389
- #
390
- def fetch(hash_param)
391
- type = GM_STANDARD
392
- box = "inbox"
393
- pos = 0
394
- @filter = {}
395
- hash_param.keys.each do |k|
396
- case k
397
- when :label
398
- type = GM_LABEL
399
- box = hash_param[k]
400
- when :standard
401
- type = GM_STANDARD
402
- box = hash_param[k]
403
- when :conversation
404
- type = GM_CONVERSATION
405
- box = hash_param[k]
406
- when :show_original
407
- type = GM_SHOWORIGINAL
408
- box = hash_param[k]
409
- when :preference
410
- type = GM_PREFERENCE
411
- box = hash_param[k]
412
- when :contact
413
- type = GM_CONTACT
414
- box = hash_param[k]
415
- when :query
416
- type = GM_QUERY
417
- box = hash_param[k]
418
- when :pos
419
- pos = hash_param[k].to_i
420
- when :read
421
- @filter[:read] = hash_param[k]
422
- when :star
423
- @filter[:star] = hash_param[k]
424
- else
425
- raise ArgumentError, 'Invalid hash argument'
426
- end
427
- end
428
- box = "inbox" unless box
429
- fetch_box(type,box,pos)
430
- if type == GM_CONVERSATION
431
- fetch_box(GM_CONV_SPAM,box,pos) if @raw['mg'].nil?
432
- fetch_box(GM_CONV_TRASH,box,pos) if @raw['mg'].nil?
433
- end
434
-
435
- if type == GM_SHOWORIGINAL
436
- ss = @raw
437
- else
438
- ss = snapshot(type)
439
- end
440
- if block_given?
441
- yield(ss)
442
- elsif type == GM_CONTACT
443
- ss.contacts
444
- else
445
- ss
446
- end
447
- end
448
-
449
- #
450
- # return message
451
- # param message id
452
- # desc Fetch a message with message id
453
- #
454
- def msg(msgid)
455
- m = fetch(:conversation=>msgid).message
456
- if m.nil? then
457
- raise ArgumentError, 'Message Not Exist'
458
- end
459
- m.connection = self
460
- if block_given?
461
- yield(m)
462
- else
463
- m
464
- end
465
-
466
- end
467
-
468
- #
469
- # return string[]
470
- # param string[] convs
471
- # param string path
472
- # desc Save all attaching files of conversations to a path.
473
- #
474
- def attachments_of(convs, path)
475
-
476
- if connected?
477
- if (convs.class != Array)
478
- convs = [convs] # array wrapper
479
- end
480
- final = []
481
- convs.each { |v|
482
- if v.attachment
483
- v.attachment.each { |vv|
484
- f = path+"/"+vv.filename
485
- f = path+"/"+vv.filename+"."+rand(2000).to_s while (FileTest.exist?(f))
486
- if attachment(vv.id,v.id,f,false)
487
- final.push(f)
488
- end
489
- }
490
- end
491
- }
492
- final
493
- else
494
- nil
495
- end
496
- end
497
-
498
- #
499
- # return string[]
500
- # param string[] convs
501
- # param string path
502
- # desc Save all attaching files of conversations to a path.
503
- #
504
- def zip_attachments_of(convs, path)
505
-
506
- if connected?
507
- if (convs.class != Array)
508
- convs = [convs] # array wrapper
509
- end
510
- final = []
511
- convs.each {|v|
512
- if v.attachment
513
- f = path+"/attachment.zip"
514
- f = path+"/attachment."+rand(2000).to_s+".zip" while (FileTest.exist?(f))
515
- if attachment(v["attachment"][0]["id"],v["id"],f,true)
516
- final.push(f)
517
- end
518
- end
519
- }
520
- final
521
- else
522
- nil
523
- end
524
- end
525
-
526
- #
527
- # return bool
528
- # param string attid
529
- # param string msgid
530
- # param string filename
531
- # desc Save attachment with attachment ID attid and message ID msgid to file with name filename.
532
- #
533
- def attachment(attid, msgid, filename, zipped=false)
534
-
535
- if connected?
536
-
537
- if !zipped
538
- query = GM_LNK_ATTACHMENT + "&attid=" + URI.escape(attid) + "&th=" + URI.escape(msgid)
539
- else
540
- query = GM_LNK_ATTACHMENT_ZIPPED + "&th=" + URI.escape(msgid)
541
- end
542
-
543
- File.open(filename,"wb") { |f|
544
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
545
- np.start { |http|
546
- response = http.get(query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
547
- f.write(response.body)
548
- }
549
- }
550
- true
551
- else
552
- false
553
- end
554
- end
555
-
556
- #
557
- # return array of labels
558
- # desc get label lists
559
- #
560
- def labels()
561
- if connected?
562
- fetch(:standard=>"inbox") {|s| s.label_list }
563
- else
564
- nil
565
- end
566
- end
567
-
568
- #
569
- # return string
570
- # param string label
571
- # desc validate label
572
- #
573
- def validate_label(label)
574
- label.strip!
575
- if label==''
576
- raise ArgumentError, 'Error: Labels cannot empty string'
577
- end
578
- if label.length > 40
579
- raise ArgumentError, 'Error: Labels cannot contain more than 40 characters'
580
- end
581
- if label.include?('^')
582
- raise ArgumentError, "Error: Labels cannot contain the character '^'"
583
- end
584
- label
585
- end
586
-
587
- #
588
- # return boolean
589
- # param string label
590
- # desc create label
591
- #
592
- def create_label(label)
593
- if connected?
594
- perform_action(GM_ACT_CREATELABEL, '', validate_label(label))
595
- else
596
- false
597
- end
598
- end
599
-
600
- #
601
- # return boolean
602
- # param string label
603
- # desc create label
604
- #
605
- def delete_label(label)
606
- if connected?
607
- perform_action(GM_ACT_DELETELABEL, '', validate_label(label))
608
- else
609
- false
610
- end
611
- end
612
-
613
- #
614
- # return boolean
615
- # param string old_label
616
- # param string new_label
617
- # desc create label
618
- #
619
- def rename_label(old_label,new_label)
620
- if connected?
621
- perform_action(GM_ACT_RENAMELABEL, '',
622
- validate_label(old_label) +'^' + validate_label(new_label))
623
- else
624
- false
625
- end
626
- end
627
-
628
- #
629
- # return boolean
630
- # param string msgid
631
- # param string label
632
- # desc apply label to message
633
- #
634
- def apply_label(id,label)
635
- if connected?
636
- perform_action(GM_ACT_APPLYLABEL, id, validate_label(label))
637
- else
638
- false
639
- end
640
- end
641
-
642
- #
643
- # return boolean
644
- # param string msgid
645
- # param string label
646
- # desc remove label from message
647
- #
648
- def remove_label(id,label)
649
- if connected?
650
- perform_action(GM_ACT_REMOVELABEL, id, validate_label(label))
651
- else
652
- false
653
- end
654
- end
655
-
656
- #
657
- # return boolean
658
- # param string hash_param
659
- # desc remove label from message
660
- #
661
- def update_preference(hash_param)
662
- if connected?
663
- args = {}
664
- hash_param.keys.each do |k|
665
- case k
666
- when :max_page_size
667
- args['p_ix_nt'] = hash_param[k]
668
- when :keyboard_shortcuts
669
- args['p_bx_hs'] = hash_param[k] ? '1' : '0'
670
- when :indicators
671
- args['p_bx_sc'] = hash_param[k] ? '1' : '0'
672
- when :display_language
673
- args['p_sx_dl'] = hash_param[k]
674
- when :signature
675
- args['p_sx_sg'] = hash_param[k]
676
- when :reply_to
677
- args['p_sx_rt'] = hash_param[k]
678
- when :snippets
679
- args['p_bx_ns'] = hash_param[k] ? '0' : '1'
680
- when :display_name
681
- args['p_sx_dn'] = hash_param[k]
682
- end
683
- end
684
- param = '&' + args.to_a.map {|x| x.join('=')}.join('&')
685
- perform_action(GM_ACT_PREFERENCE,'', param)
686
- else
687
- false
688
- end
689
- end
690
-
691
- #
692
- # return boolean
693
- # param string msgid
694
- # desc apply star to a message
695
- #
696
- def apply_star(msgid)
697
- if connected?
698
- perform_action(GM_ACT_STAR,msgid, '')
699
- else
700
- false
701
- end
702
- end
703
-
704
- #
705
- # return boolean
706
- # param string msgid
707
- # desc remove star from a message
708
- #
709
- def remove_star(msgid)
710
- if connected?
711
- perform_action(GM_ACT_UNSTAR,msgid, '')
712
- else
713
- false
714
- end
715
- end
716
-
717
- #
718
- # return boolean
719
- # param string msgid
720
- # desc report a message as spam
721
- #
722
- def report_spam(msgid)
723
- if connected?
724
- perform_action(GM_ACT_SPAM,msgid, '')
725
- else
726
- false
727
- end
728
- end
729
-
730
- #
731
- # return boolean
732
- # param string msgid
733
- # desc report a message as not spam
734
- #
735
- def report_not_spam(msgid)
736
- if connected?
737
- perform_action(GM_ACT_UNSPAM,msgid, '')
738
- else
739
- false
740
- end
741
- end
742
-
743
- #
744
- # return boolean
745
- # param string msgid
746
- # desc delete a spam message forever
747
- #
748
- def delete_spam(msgid)
749
- if connected?
750
- perform_action(GM_ACT_DELSPAM,msgid, '')
751
- else
752
- false
753
- end
754
- end
755
-
756
- #
757
- # return boolean
758
- # param string msgid
759
- # desc mark a message as read
760
- #
761
- def mark_read(msgid)
762
- if connected?
763
- perform_action(GM_ACT_READ,msgid, '')
764
- else
765
- false
766
- end
767
- end
768
-
769
- #
770
- # return boolean
771
- # param string msgid
772
- # desc mark a message as unread
773
- #
774
- def mark_unread(msgid)
775
- if connected?
776
- perform_action(GM_ACT_UNREAD,msgid, '')
777
- else
778
- false
779
- end
780
- end
781
-
782
- #
783
- # return original message string
784
- # param string msgid
785
- # desc show original message format
786
- #
787
- def show_original(msgid)
788
- if connected?
789
- fetch(:show_original=>msgid)
790
- else
791
- false
792
- end
793
- end
794
-
795
- #
796
- # return boolean
797
- # param string msgid
798
- # desc move a message to trash
799
- #
800
- def trash(msgid)
801
- if connected?
802
- perform_action(GM_ACT_TRASH,msgid, '')
803
- else
804
- false
805
- end
806
- end
807
-
808
- #
809
- # return boolean
810
- # param string msgid
811
- # desc move a message from trash to inbox
812
- #
813
- def untrash(msgid)
814
- if connected?
815
- perform_action(GM_ACT_UNTRASH,msgid, '')
816
- else
817
- false
818
- end
819
- end
820
-
821
- #
822
- # return boolean
823
- # param string msgid
824
- # desc delete a trash message forever
825
- #
826
- def delete_trash(msgid)
827
- if connected?
828
- perform_action(GM_ACT_DELTRASH,msgid, '')
829
- else
830
- false
831
- end
832
- end
833
-
834
- #
835
- # return boolean
836
- # param string msgid
837
- # desc delete a message forever
838
- #
839
- def delete_message(msgid)
840
- if connected?
841
- perform_action(GM_ACT_DELFOREVER,msgid, '')
842
- else
843
- false
844
- end
845
- end
846
-
847
- #
848
- # return boolean
849
- # param string msgid
850
- # desc archive a message
851
- #
852
- def archive(msgid)
853
- if connected?
854
- perform_action(GM_ACT_ARCHIVE,msgid, '')
855
- else
856
- false
857
- end
858
- end
859
-
860
- #
861
- # return boolean
862
- # param string msgid
863
- # desc archive a message
864
- #
865
- def unarchive(msgid)
866
- if connected?
867
- perform_action(GM_ACT_INBOX,msgid, '')
868
- else
869
- false
870
- end
871
- end
872
-
873
-
874
- #
875
- # return hash of preference
876
- # desc get preferences
877
- #
878
- def preference()
879
- if connected?
880
- fetch(:preference=>"all").preference
881
- else
882
- nil
883
- end
884
- end
885
-
886
- #
887
- # return array of messages
888
- # desc get message lists
889
- #
890
- def messages(hash_param)
891
- if connected?
892
- ml = fetch(hash_param).message_list
893
- @filter.keys.each do |k|
894
- case k
895
- when :read
896
- ml.box = ml.box.find_all { |x| x.read? == @filter[k] }
897
- when :star
898
- ml.box = ml.box.find_all { |x| x.star? == @filter[k] }
899
- end
900
- end
901
- ml.connection = self
902
- if block_given?
903
- yield(ml)
904
- else
905
- ml
906
- end
907
- else
908
- nil
909
- end
910
- end
911
-
912
- #
913
- # return string
914
- # param string query
915
- # desc Dump everything to output.
916
- #
917
- def dump(query)
918
- page = ''
919
- if connected?
920
- query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
921
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
922
- np.start { |http|
923
- response = http.get(GM_LNK_GMAIL + "?"+query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
924
- page = response.body
925
- }
926
- else # not logged in yet
927
-
928
- end
929
- page
930
- end
931
-
932
- def stripslashes(string)
933
- encode(string.gsub("\\\"", "\"").gsub("\\'", "'").gsub("\\\\", "\\"))
934
- end
935
-
936
-
937
- #
938
- # return bool
939
- # param string to
940
- # param string subject
941
- # param string body
942
- # param string cc
943
- # param string bcc
944
- # param string msg_id
945
- # param string thread_id
946
- # param string[] files
947
- # desc Send GMail.
948
- #
949
- def send(*param)
950
- if param.length==1 && param[0].class==Hash
951
- param = param[0]
952
- from = param[:from] || ''
953
- to = param[:to] || ''
954
- subject = param[:subject] || ''
955
- body = param[:body] || ''
956
- cc = param[:cc] || ''
957
- bcc = param[:bcc] || ''
958
- msg_id = param[:msg_id] || ''
959
- thread_id = param[:msg_id] || ''
960
- files = param[:files] || []
961
- draft = param[:draft] || false
962
- draft_id = param[:draft_id] || ''
963
- elsif param.length==10
964
- to, subject, body, cc, bcc, msg_id, thread_id, files, draft, draft_id = param
965
- elsif param.length==11
966
- from, to, subject, body, cc, bcc, msg_id, thread_id, files, draft, draft_id = param
967
- else
968
- raise ArgumentError, 'Invalid argument'
969
- end
970
-
971
- if connected?
972
- other_emails = fetch(:preference=>"all").other_emails
973
- if other_emails.length>0
974
- other_emails.each {|v|
975
- from = v['email'] if from=='' && v['default']
976
- }
977
- from = @login + '@gmail.com' if from==''
978
- else
979
- from = nil
980
- end
981
-
982
- postdata = {}
983
- if draft
984
- postdata["view"] = "sd"
985
- postdata["draft"] = draft_id
986
- postdata["rm"] = msg_id
987
- postdata["th"] = thread_id
988
- else
989
- postdata["view"] = "sm"
990
- postdata["draft"] = draft_id
991
- postdata["rm"] = msg_id
992
- postdata["th"] = thread_id
993
- end
994
- postdata["msgbody"] = stripslashes(body)
995
- postdata["from"] = stripslashes(from) if from
996
- postdata["to"] = stripslashes(to)
997
- postdata["subject"] = stripslashes(subject)
998
- postdata["cc"] = stripslashes(cc)
999
- postdata["bcc"] = stripslashes(bcc)
1000
-
1001
- postdata["cmid"] = 1
1002
- postdata["ishtml"] = param[:ishtml] || 0
1003
-
1004
- cc = @cookie_str.split(';')
1005
- cc.each {|cc_part|
1006
- cc_parts = cc_part.split('=')
1007
- if(cc_parts[0]=='GMAIL_AT')
1008
- postdata["at"] = cc_parts[1]
1009
- break
1010
- end
1011
- }
1012
-
1013
- boundary = "----#{Time.now.to_i}#{(rand(2000)*2147483.648).to_i}"
1014
- postdata2 = []
1015
- postdata.each {|k,v|
1016
- postdata2.push("Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n")
1017
- }
1018
-
1019
- files.each_with_index {|f,i|
1020
- content = File.open(f,'rb') { |c| c.read }
1021
- postdata2.push("Content-Disposition: form-data; name=\"file#{i}\"; filename=\"#{File.basename(f)}\"\r\n" +
1022
- "Content-Transfer-Encoding: binary\r\n" +
1023
- "Content-Type: application/octet-stream\r\n\r\n" + content + "\r\n")
1024
- }
1025
-
1026
- postdata = postdata2.collect { |p|
1027
- "--" + boundary + "\r\n" + p
1028
- }.join('') + "--" + boundary + "--\r\n"
1029
-
1030
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1031
- response = nil
1032
- # np.set_debug_output($stderr)
1033
- np.start { |http|
1034
- response = http.post(GM_LNK_GMAIL, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT,'Content-type' => 'multipart/form-data; boundary=' + boundary } )
1035
- }
1036
- true
1037
- else
1038
- false
1039
- end
1040
-
1041
- end
1042
-
1043
- #
1044
- # return bool
1045
- # param constant act
1046
- # param string[] id
1047
- # param string para
1048
- # desc Perform action on messages.
1049
- #
1050
- def perform_action(act, id, para)
1051
-
1052
- if connected?
1053
-
1054
- if (act == GM_ACT_DELFOREVER)
1055
- perform_action(GM_ACT_TRASH, id, 0) # trash it before
1056
- end
1057
- postdata = "act="
1058
-
1059
- action_codes = ["ib", "cc_", "dc_", "nc_", "ac_", "rc_", "prefs", "st", "xst",
1060
- "sp", "us", "rd", "ur", "tr", "dl", "rc_^i", "ib", "ib", "dd", "dm", "dl", "dl"]
1061
- postdata += action_codes[act] ? action_codes[act] : action_codes[GM_ACT_INBOX]
1062
- if act == GM_ACT_RENAMELABEL then
1063
- paras = para.split('^')
1064
- para = validate_label(paras[0])+'^'+validate_label(paras[1])
1065
- elsif ([GM_ACT_APPLYLABEL,GM_ACT_REMOVELABEL,GM_ACT_CREATELABEL,
1066
- GM_ACT_DELETELABEL,].include?(act))
1067
- para = validate_label(para)
1068
- end
1069
- if ([GM_ACT_APPLYLABEL,GM_ACT_REMOVELABEL,GM_ACT_CREATELABEL,
1070
- GM_ACT_DELETELABEL,GM_ACT_RENAMELABEL,GM_ACT_PREFERENCE].include?(act))
1071
- postdata += para.to_s
1072
- end
1073
-
1074
- cc = @cookie_str.split(';')
1075
- cc.each {|cc_part|
1076
- cc_parts = cc_part.split('=')
1077
- if(cc_parts[0]=='GMAIL_AT')
1078
- postdata += "&at=" + cc_parts[1]
1079
- break
1080
- end
1081
- }
1082
-
1083
- if (act == GM_ACT_TRASHMSG)
1084
- postdata += "&m=" + id.to_s
1085
- else
1086
- if id.class==Array
1087
- id.each {|t| postdata += "&t="+t.to_s }
1088
- else
1089
- postdata += "&t="+id.to_s
1090
- end
1091
- end
1092
- postdata += "&vp="
1093
-
1094
- if [GM_ACT_UNTRASH,GM_ACT_DELFOREVER,GM_ACT_DELTRASH].include?(act)
1095
- link = GM_LNK_GMAIL+"?search=trash&view=tl&start=0"
1096
- elsif (act == GM_ACT_DELSPAM)
1097
- link = GM_LNK_GMAIL+"?search=spam&view=tl&start=0"
1098
- else
1099
- link = GM_LNK_GMAIL+"?search=query&q=&view=tl&start=0"
1100
- end
1101
-
1102
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1103
- # np.set_debug_output($stderr)
1104
- np.start { |http|
1105
- response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1106
- result = response.body
1107
- }
1108
-
1109
- return true
1110
- else
1111
- return false
1112
- end
1113
-
1114
- end
1115
-
1116
- #
1117
- # return void
1118
- # desc Disconnect from GMail.
1119
- #
1120
- def disconnect()
1121
-
1122
- response = nil
1123
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1124
- # np.set_debug_output($stderr)
1125
- np.start { |http|
1126
- response = http.get(GM_LNK_LOGOUT,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
1127
- }
1128
- arr = URI::split(response["Location"])
1129
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 80)
1130
- # np.set_debug_output($stderr)
1131
- np.start { |http|
1132
- response = http.get(arr[5]+'?'+arr[7], {'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1133
- }
1134
-
1135
- @cookie_str = ''
1136
- end
1137
-
1138
- #
1139
- # return GMailSnapshot
1140
- # param constant type
1141
- # desc Get GMSnapshot by type.
1142
- #
1143
- def snapshot(type)
1144
- if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY,GM_PREFERENCE,GM_CONTACT].include?(type)
1145
- return GMailSnapshot.new(type, @raw, @charset)
1146
- else
1147
- return GMailSnapshot.new(GM_STANDARD, @raw, @charset) # assuming normal by default
1148
- end
1149
- end
1150
-
1151
- def snap(type)
1152
- action = GM_STANDARD
1153
- case type
1154
- when :standard
1155
- action = GM_STANDARD
1156
- when :label
1157
- action = GM_LABEL
1158
- when :conversation
1159
- action = GM_CONVERSATION
1160
- when :query
1161
- action = GM_QUERY
1162
- when :preference
1163
- action = GM_PREFERENCE
1164
- when :contact
1165
- action = GM_CONTACT
1166
- else
1167
- raise ArgumentError, 'Invalid type'
1168
- end
1169
- snapshot(action)
1170
- end
1171
-
1172
- #
1173
- # return bool
1174
- # param string email
1175
- # desc Send Gmail invite to email
1176
- #
1177
- def invite(email)
1178
-
1179
- if connected?
1180
-
1181
- postdata = "act=ii&em=" + URI.escape(email)
1182
-
1183
- cc = @cookie_str.split(';')
1184
- cc.each {|cc_part|
1185
- cc_parts = cc_part.split('=')
1186
- if(cc_parts[0]=='GMAIL_AT')
1187
- postdata += "&at=" + cc_parts[1]
1188
- break
1189
- end
1190
- }
1191
-
1192
- link = GM_LNK_GMAIL + "?view=ii"
1193
-
1194
- np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 443)
1195
- np.use_ssl = true
1196
- np.verify_mode = OpenSSL::SSL::VERIFY_NONE
1197
- # np.set_debug_output($stderr)
1198
- np.start { |http|
1199
- response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1200
- }
1201
-
1202
- true
1203
- else
1204
- false
1205
- end
1206
-
1207
- end
1208
-
1209
- #
1210
- # return string[]
1211
- # desc (Static) Get names of standard boxes.
1212
- #
1213
- def standard_box()
1214
- ["Inbox","Starred","Sent","Drafts","All","Spam","Trash"]
1215
- end
1216
-
1217
- #
1218
- # return string
1219
- # param string header
1220
- # desc (Static Private) Extract cookies from header.
1221
- #
1222
- def __cookies(header)
1223
- return '' unless header
1224
- arr = []
1225
- header.split(', ').each { |x|
1226
- if x.include?('GMT')
1227
- arr[-1] += ", " + x
1228
- else
1229
- arr.push x
1230
- end
1231
- }
1232
- arr.delete_if {|x| x.include?('LSID=EXPIRED') }
1233
- arr.map! {|x| x.split(';')[0]}
1234
- arr.join(';')
1235
- end
1236
-
1237
- end
1238
-
1239
-
1240
- class GMailSnapshot
1241
-
1242
- GM_STANDARD = 0x001
1243
- GM_LABEL = 0x002
1244
- GM_CONVERSATION = 0x004
1245
- GM_QUERY = 0x008
1246
- GM_CONTACT = 0x010
1247
- GM_PREFERENCE = 0x020
1248
-
1249
- def decode(str)
1250
- return str if @charset.upcase == 'UTF-8'
1251
- begin
1252
- require 'Win32API'
1253
- str = str.unpack("U*").pack("S*") + "\0"
1254
- ostr = "\0" * str.length*2
1255
- wideCharToMultiByte = Win32API.new('kernel32','WideCharToMultiByte',['L','L','P','L','P','L','L','L'],'L')
1256
- wideCharToMultiByte.Call(0,0,str,-1,ostr,str.length*2,0,0)
1257
- ostr.strip
1258
- rescue LoadError
1259
- require 'iconv'
1260
- Iconv::iconv(@charset,'UTF-8',str)[0]
1261
- end
1262
- end
1263
-
1264
- def strip_tags(str,allowable_tags='')
1265
- str.gsub(/<[^>]*>/, '')
1266
- end
1267
-
1268
- #
1269
- # return GMailSnapshot
1270
- # param constant type
1271
- # param array raw
1272
- # desc Constructor.
1273
- #
1274
- attr_reader :gmail_ver, :quota_mb, :quota_per, :preference
1275
- attr_reader :std_box_new, :have_invit, :label_list, :label_new
1276
- attr_reader :message_list
1277
- attr_reader :message
1278
- attr_reader :contacts, :other_emails
1279
-
1280
- def initialize(type, raw, charset='UTF-8')
1281
- @charset = charset
1282
- if (raw.class!=Hash)
1283
- @created = 0
1284
- return nil
1285
- elsif (raw.empty?)
1286
- @created = 0
1287
- return nil
1288
- end
1289
- if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY].include?(type)
1290
- @gmail_ver = raw["v"][1]
1291
- if raw["qu"]
1292
- @quota_mb = raw["qu"][1]
1293
- @quota_per = raw["qu"][3]
1294
- end
1295
- if (raw["ds"].class!=Array)
1296
- @created = 0
1297
- return nil
1298
- end
1299
- @std_box_new = raw["ds"][1..-1]
1300
- #if (isset(raw["p"])) {
1301
- # @owner = raw["p"][5][1]
1302
- # @owner_email = raw["p"][6][1]
1303
- #end
1304
- @have_invit = raw["i"][1]
1305
- @label_list = []
1306
- @label_new = []
1307
- raw["ct"][1].each {|v|
1308
- @label_list.push(v[0])
1309
- @label_new.push(v[1])
1310
- }
1311
- if raw["ts"]
1312
- @view = (GM_STANDARD|GM_LABEL|GM_QUERY)
1313
- @message_list = MessageList.new(raw["ts"][5],raw["ts"][3],raw["ts"][1])
1314
- end
1315
- @box = []
1316
- if raw["t"]
1317
- raw["t"].each do |t|
1318
- if (t != "t")
1319
- b = Box.new
1320
- b.id = t[0]
1321
- b.is_read = (t[1] != 1 ? 1 : 0)
1322
- b.is_starred = (t[2] == 1 ? 1 : 0)
1323
- b.date = decode(strip_tags(t[3]))
1324
- b.sender = decode(strip_tags(t[4]))
1325
- b.flag = t[5]
1326
- b.subject = decode(strip_tags(t[6]))
1327
- b.snippet = decode(t[7].gsub('&quot;','"').gsub('&hellip;','...'))
1328
- b.labels = t[8].empty? ? [] : t[8].map{|tt|decode(tt)}
1329
- b.attachment = t[9].empty? ? []: decode(t[9]).split(",")
1330
- b.msgid = t[10]
1331
- @box.push(b)
1332
- end
1333
- end
1334
- end
1335
-
1336
- if (!raw["cs"].nil?)
1337
- @view = GM_CONVERSATION
1338
- @conv_title = raw["cs"][2]
1339
- @conv_total = raw["cs"][8]
1340
- @conv_id = raw["cs"][1]
1341
- @conv_labels = raw["cs"][5].empty? ? '' : raw["cs"][5]
1342
- if !@conv_labels.empty?
1343
- ij = @conv_labels.index("^i")
1344
- if !ij.nil?
1345
- @conv_labels[ij] = "Inbox"
1346
- end
1347
- ij = @conv_labels.index("^s")
1348
- if !ij.nil?
1349
- @conv_labels[ij] = "Spam"
1350
- end
1351
- ij = @conv_labels.index("^k")
1352
- if !ij.nil?
1353
- @conv_labels[ij] = "Trash"
1354
- end
1355
- ij = @conv_labels.index("^t")
1356
- if !ij.nil?
1357
- @conv_labels[ij] = '' # Starred
1358
- end
1359
- ij = @conv_labels.index("^r")
1360
- if !ij.nil?
1361
- @conv_labels[ij] = "Drafts"
1362
- end
1363
- end
1364
-
1365
- @conv_starred = false
1366
-
1367
- @conv = []
1368
- b = Conversation.new
1369
- raw["mg"].each do |r|
1370
- if (r[0] == "mb")
1371
- b.body = '' if b.body.nil?
1372
- b.body += r[1]
1373
- if (r[2] == 0)
1374
- b.body = decode(b.body)
1375
- @conv.push(b)
1376
- b = Conversation.new
1377
- end
1378
- elsif (r[0] == "mi")
1379
- if !b.id.nil?
1380
- @conv.push(b)
1381
- b = Conversation.new
1382
- end
1383
- b = Conversation.new
1384
- b.index = r[2]
1385
- b.id = r[3]
1386
- b.is_star = r[4]
1387
- @conv_starred = (b.is_star == 1)
1388
- b.sender = decode(r[6])
1389
- b.sender_email = r[8].gsub("\"",'') # remove annoying d-quotes in address
1390
- b.recv = r[9]
1391
- b.recv_email = r[11].to_s.gsub("\"",'')
1392
- b.reply_email = r[14].to_s.gsub("\"",'')
1393
- b.dt_easy = r[10]
1394
- b.dt = r[15]
1395
- b.subject = decode(r[16])
1396
- b.snippet = decode(r[17])
1397
- b.attachment = []
1398
- r[18].each do |bb|
1399
- at = Attachment.new
1400
- at.id = bb[0]
1401
- at.filename = bb[1]
1402
- at.type = bb[2]
1403
- at.size = bb[3]
1404
- b.attachment.push(at)
1405
- end
1406
- b.is_draft = false
1407
- b.body = ''
1408
- elsif (r[0] == "di")
1409
- if !b.id.nil?
1410
- @conv.push(b)
1411
- b = Conversation.new
1412
- end
1413
- b = Conversation.new
1414
- b.index = r[2]
1415
- b.id = r[3]
1416
- b.is_star = r[4]
1417
- @conv_starred = (b.is_star == 1)
1418
- b.sender = decode(r[6])
1419
- b.sender_email = r[8].gsub("\"",'') # remove annoying d-quotes in address
1420
- b.recv = r[9]
1421
- b.recv_email = r[11].gsub("\"",'')
1422
- b.reply_email = r[14].gsub("\"",'')
1423
- b.cc_email = r[12].gsub("\"",'')
1424
- b.bcc_email = r[13].gsub("\"",'')
1425
- b.dt_easy = r[10]
1426
- b.dt = r[15]
1427
- b.subject = decode(r[16])
1428
- b.snippet = decode(r[17])
1429
- b.attachment = []
1430
- r[18].each do |bb|
1431
- at = Attachment.new
1432
- at.id = bb[0]
1433
- at.filename = bb[1]
1434
- at.type = bb[2]
1435
- at.size = bb[3]
1436
- b.attachment.push(at)
1437
- end
1438
- b.is_draft = true
1439
- b.draft_parent = r[5]
1440
- b.body = decode(r[20])
1441
- end
1442
- end
1443
- @conv.push(b) unless !b.id.nil?
1444
- @message = Message.new(@conv_tile,@conv_total,@conv_id,@conv_labels,@conv_starred)
1445
- @message.conv = @conv
1446
- end
1447
- elsif type == GM_CONTACT
1448
- @contacts = []
1449
- if raw["ce"]
1450
- raw["ce"].each {|a|
1451
- c = Contact.new
1452
- c.name = a[2]
1453
- c.email = a[4].gsub("\"",'')
1454
- c.notes = a[5] if !a[5].empty?
1455
- @contacts.push(c)
1456
- }
1457
- end
1458
- @view = GM_CONTACT
1459
- elsif type == GM_PREFERENCE
1460
- prefs = {}
1461
- raw["p"].each_with_index { |v,i|
1462
- prefs[v[0]] = v[1] if i>0
1463
- }
1464
- @preference = Preference.new
1465
- prefs.each do |k,v|
1466
- case k
1467
- when 'ix_nt'
1468
- @preference.max_page_size = v
1469
- when 'bx_hs'
1470
- @preference.keyboard_shortcuts = (v == '1')
1471
- when 'bx_sc'
1472
- @preference.indicators = (v == '1')
1473
- when 'sx_dl'
1474
- @preference.display_language = v
1475
- when 'sx_sg'
1476
- @preference.signature = (v == '0') ? '' : v
1477
- when 'sx_rt'
1478
- @preference.reply_to = v
1479
- when 'bx_ns'
1480
- @preference.snippets = (v == '0')
1481
- when 'sx_dn'
1482
- @preference.display_name = v
1483
- end
1484
- end
1485
-
1486
- @label_list = []
1487
- @label_total = []
1488
- raw["cta"][1].each { |v|
1489
- @label_list.push(v[0])
1490
- @label_total.push(v[1])
1491
- }
1492
- @other_emails = []
1493
- if raw["cfs"]
1494
- raw["cfs"][1].each {|v|
1495
- @other_emails.push({"name"=>decode(v[0]),"email"=>v[1],"default"=>(v[2]==1)})
1496
- }
1497
- end
1498
- @filter = []
1499
- raw["fi"][1].each do |fi|
1500
- f = Filter.new
1501
- f.id = fi[0]
1502
- f.query = fi[1]
1503
- f.star = fi[3]
1504
- f.label = fi[4]
1505
- f.archive = fi[5]
1506
- f.trash = fi[6]
1507
- f.from = fi[2][0]
1508
- f.to = fi[2][1]
1509
- f.subject = fi[2][2]
1510
- f.has = fi[2][3]
1511
- f.hasnot = fi[2][4]
1512
- f.attach = fi[2][5]
1513
-
1514
- @filter.push(b)
1515
- end
1516
- @view = GM_PREFERENCE
1517
- else
1518
- @created = 0
1519
- return nil
1520
- end
1521
- @message_list.box = @box if @message_list
1522
-
1523
- @created = 1
1524
- end
1525
-
1526
- end
1527
-
1528
- # a single filter
1529
- class Filter
1530
- attr_accessor :id,:query,:star,:label,:archive,:trash
1531
- attr_accessor :from,:to,:subject,:has,:hasnot,:attach
1532
- end
1533
-
1534
- # a single box
1535
- class Box
1536
- attr_accessor :id, :is_read, :is_starred, :date, :msgid
1537
- attr_accessor :sender, :flag, :subject, :snippet, :labels, :attachment
1538
-
1539
- def read?
1540
- is_read == 1
1541
- end
1542
-
1543
- def new?
1544
- is_read != 1
1545
- end
1546
-
1547
- def star?
1548
- is_starred == 1
1549
- end
1550
-
1551
- end
1552
-
1553
- # a list of messages.
1554
- class MessageList
1555
- attr_reader :name, :total, :pos
1556
- attr_accessor :box, :connection
1557
- def initialize(name,total,pos)
1558
- @name = name
1559
- @total = total
1560
- @pos = pos
1561
- end
1562
-
1563
- def each(&block)
1564
- @box.each do |b|
1565
- block.call(b)
1566
- end
1567
- end
1568
-
1569
- def each_msg(&block)
1570
- @box.each do |b|
1571
- m = connection.fetch(:conversation=>b.id).message
1572
- m.connection = @connection
1573
- block.call(m)
1574
- end
1575
- end
1576
-
1577
- end
1578
-
1579
- # a Preference
1580
- class Preference
1581
- attr_accessor :max_page_size, :keyboard_shortcuts, :indicators, :display_language
1582
- attr_accessor :signature, :reply_to, :snippets, :display_name
1583
- def initialize
1584
- @max_page_size = '10'
1585
- @keyboard_shortcuts = false
1586
- @indicators = false
1587
- @display_language = 'en'
1588
- @signature = ''
1589
- @reply_to = ''
1590
- @snippets = false
1591
- @display_name = ''
1592
- end
1593
- end
1594
-
1595
- # a Preference
1596
- class Contact
1597
- attr_accessor :name,:email,:notes
1598
- def initialize
1599
- @name = ''
1600
- @email = ''
1601
- @notes = ''
1602
- end
1603
- end
1604
-
1605
- # a single attachment
1606
- class Attachment
1607
- attr_accessor :id, :filename, :type, :size
1608
- end
1609
-
1610
- # a single conversation
1611
- class Conversation
1612
- attr_accessor :id, :index, :body, :draft_parent, :is_draft, :attachment
1613
- attr_accessor :snippet, :subject, :dt, :dt_easy, :bcc_email, :cc_email
1614
- attr_accessor :reply_email, :recv_email, :recv, :sender_email, :sender, :is_star
1615
- def read?
1616
- is_read == 1
1617
- end
1618
-
1619
- def new?
1620
- is_read != 1
1621
- end
1622
-
1623
- def star?
1624
- is_starred == 1
1625
- end
1626
-
1627
- end
1628
-
1629
- # a single message
1630
- class Message
1631
- attr_reader :title, :total, :id, :labels, :starred
1632
- attr_accessor :conv, :connection
1633
- def initialize(title,total,id,labels,starred)
1634
- @title = title
1635
- @total = total
1636
- @id = id
1637
- @labels = labels
1638
- @starred = starred
1639
- end
1640
-
1641
- def method_missing(methId)
1642
- str = methId.id2name
1643
- @conv[0].method(str).call
1644
- end
1645
-
1646
- #
1647
- # return boolean
1648
- # param string label
1649
- # desc apply label to message
1650
- #
1651
- def apply_label(label)
1652
- @connection.perform_action(GM_ACT_APPLYLABEL, @id, label)
1653
- end
1654
-
1655
- #
1656
- # return boolean
1657
- # param string label
1658
- # desc remove label from message
1659
- #
1660
- def remove_label(label)
1661
- @connection.perform_action(GM_ACT_REMOVELABEL, @id, label)
1662
- end
1663
-
1664
- #
1665
- # return boolean
1666
- # desc apply star to a message
1667
- #
1668
- def apply_star()
1669
- @connection.perform_action(GM_ACT_STAR,@id, '')
1670
- end
1671
-
1672
- #
1673
- # return boolean
1674
- # param string msgid
1675
- # desc remove star from a message
1676
- #
1677
- def remove_star()
1678
- @connection.perform_action(GM_ACT_UNSTAR,@id, '')
1679
- end
1680
-
1681
- #
1682
- # return boolean
1683
- # desc archive a message
1684
- #
1685
- def archive()
1686
- @connection.perform_action(GM_ACT_ARCHIVE,@id, '')
1687
- end
1688
-
1689
- #
1690
- # return boolean
1691
- # param string msgid
1692
- # desc archive a message
1693
- #
1694
- def unarchive()
1695
- @connection.perform_action(GM_ACT_INBOX,@id, '')
1696
- end
1697
-
1698
- #
1699
- # return boolean
1700
- # param string msgid
1701
- # desc mark a message as read
1702
- #
1703
- def mark_read()
1704
- @connection.perform_action(GM_ACT_READ,@id, '')
1705
- end
1706
-
1707
- #
1708
- # return boolean
1709
- # param string msgid
1710
- # desc mark a message as unread
1711
- #
1712
- def mark_unread()
1713
- @connection.perform_action(GM_ACT_UNREAD,@id, '')
1714
- end
1715
-
1716
- def read?()
1717
- @conv[0].read?
1718
- end
1719
-
1720
- def new?()
1721
- @conv[0].new?
1722
- end
1723
-
1724
- #
1725
- # return boolean
1726
- # param string msgid
1727
- # desc report a message as spam
1728
- #
1729
- def report_spam()
1730
- @connection.perform_action(GM_ACT_SPAM,@id, '')
1731
- end
1732
-
1733
- #
1734
- # return boolean
1735
- # param string msgid
1736
- # desc report a message as not spam
1737
- #
1738
- def report_not_spam()
1739
- @connection.perform_action(GM_ACT_UNSPAM,@id, '')
1740
- end
1741
-
1742
- #
1743
- # return boolean
1744
- # desc move a message to trash
1745
- #
1746
- def trash()
1747
- @connection.perform_action(GM_ACT_TRASH,@id, '')
1748
- end
1749
-
1750
- #
1751
- # return boolean
1752
- # desc move a message from trash to inbox
1753
- #
1754
- def untrash()
1755
- @connection.perform_action(GM_ACT_UNTRASH,@id, '')
1756
- end
1757
-
1758
- #
1759
- # return boolean
1760
- # desc delete a message forever
1761
- #
1762
- def delete()
1763
- if connected?
1764
- @connection.perform_action(GM_ACT_DELFOREVER,@id, '')
1765
- else
1766
- false
1767
- end
1768
- end
1769
-
1770
- def original()
1771
- @connection.fetch(:show_original=>@id)
1772
- end
1773
- end
1774
-
1775
- # Singleton method
1776
- # return bool
1777
- # desc Connect to GMail with default session management settings.
1778
- #
1779
-
1780
- def GMailer.connect(*param)
1781
- g = Connection.new(*param)
1782
- @connection = g
1783
- if block_given?
1784
- yield(g)
1785
- g.disconnect()
1786
- else
1787
- g
72
+ # Reserved mailbox names
73
+ def gmail_reserved_names
74
+ ["inbox", "star", "starred", "chat", "chats", "draft", "drafts",
75
+ "sent", "sentmail", "sent-mail", "sent mail", "all", "allmail", "all-mail", "all mail",
76
+ "anywhere", "archive", "spam", "trash", "read", "unread"]
1788
77
  end
78
+
79
+ def encode(str)
80
+ return str if @charset.upcase == 'UTF-8'
81
+ begin
82
+ require 'Win32API'
83
+ str += "\0"
84
+ ostr = "\0" * str.length*2
85
+ multiByteToWideChar = Win32API.new('kernel32','MultiByteToWideChar',['L','L','P','L','P','L'],'L')
86
+ multiByteToWideChar.Call(0,0,str,-1,ostr,str.length*2)
87
+ (ostr.strip + "\0").unpack("S*").pack("U*")
88
+ rescue LoadError
89
+ require 'iconv'
90
+ Iconv::iconv('UTF-8',@charset,str)[0]
91
+ end
92
+ end
93
+
94
+ #
95
+ # return GMailer
96
+ # desc Constructor
97
+ #
98
+ def initialize(*param)
99
+ @login = ''
100
+ @pwd = ''
101
+ @raw = {}
102
+ @proxy_host = nil
103
+ @proxy_port = 0
104
+ @proxy_user = nil
105
+ @proxy_pass = nil
106
+ @proxy = false
107
+ @type = 0
108
+ if param.length==1 && param[0].is_a?(Hash)
109
+ param = param[0]
110
+ set_login_info(param[:username]||'',param[:password]||'')
111
+ @proxy_host = param[:proxy_host]
112
+ @proxy = !@proxy_host.nil?
113
+ @proxy_port = param[:proxy_port] || 80
114
+ @proxy_user = param[:proxy_user]
115
+ @proxy_pass = param[:proxy_pass]
116
+ @charset = param[:charset] || 'UTF-8'
117
+ elsif param.length==0
118
+ @charset = 'UTF-8'
119
+ elsif param.length==1
120
+ @charset = param[0]
121
+ elsif param.length==2
122
+ @charset = 'UTF-8'
123
+ set_login_info(param[0],param[1])
124
+ elsif param.length==3
125
+ @charset = param[2]
126
+ set_login_info(param[0],param[1])
127
+ else
128
+ raise ArgumentError, 'Invalid argument'
129
+ end
130
+ raise 'Not connected, verify the credentials.' unless connect_no_cookie
131
+ end
132
+
133
+ #
134
+ # return void
135
+ # param string my_login
136
+ # param string my_pwd
137
+ # param int my_tz
138
+ # desc Set GMail login information.
139
+ #
140
+ def set_login_info(my_login, my_pwd, my_tz=0)
141
+ @login = my_login
142
+ @pwd = my_pwd
143
+ @timezone = (my_tz*-60).to_i.to_s
144
+ end
145
+
146
+ #
147
+ # return bool
148
+ # desc Connect to GMail without setting any session/cookie.
149
+ #
150
+ def connect_no_cookie()
151
+
152
+ postdata = "service=mail&Email=" + URI.escape(@login) + "&Passwd=" + URI.escape(@pwd) + "&null=Sign%20in&continue=" +
153
+ URI.escape('https://mail.google.com/mail/?ui=html&amp;zy=l') + "&rm=false&PersistentCookie=yes"
154
+
155
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new("www.google.com", 443)
156
+ np.use_ssl = true
157
+ np.verify_mode = OpenSSL::SSL::VERIFY_NONE
158
+ result = ''
159
+ response = nil
160
+ np.set_debug_output($stderr) if DEBUG
161
+ np.start { |http|
162
+ response = http.post(GM_LNK_LOGIN, postdata,{'Content-Type' => 'application/x-www-form-urlencoded','User-agent' => GM_USER_AGENT} )
163
+ result = response.body
164
+ }
165
+
166
+ if result.include?("errormsg")
167
+ @cookie_str = ''
168
+ return false
169
+ end
170
+
171
+ cookies = __cookies(response['set-cookie'])
172
+ arr = URI::split(response["Location"])
173
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
174
+ np.use_ssl = true
175
+ np.verify_mode = OpenSSL::SSL::VERIFY_NONE
176
+ result = ''
177
+ response = nil
178
+ np.set_debug_output($stderr) if DEBUG
179
+ np.start { |http|
180
+ response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
181
+ result = response.body
182
+ }
183
+
184
+ if result.include?("Redirecting") && result =~ /url='(.+?)'/
185
+ url = $1.gsub("&amp;","&")
186
+ result = ''
187
+ cookies += ';'+ __cookies(response['set-cookie'])
188
+ response = nil
189
+ np.set_debug_output($stderr) if DEBUG
190
+ np.start { |http|
191
+ response = http.get(url, {'Cookie' => cookies,'User-agent'=> GM_USER_AGENT} )
192
+ result = response.body
193
+ }
194
+ end
195
+
196
+ if response["Location"]!=nil
197
+ arr = URI::split(response["Location"])
198
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
199
+ np.use_ssl = true
200
+ np.verify_mode = OpenSSL::SSL::VERIFY_NONE
201
+ result = ''
202
+ response = nil
203
+ np.set_debug_output($stderr) if DEBUG
204
+ np.start { |http|
205
+ response = http.get(arr[5]+'?'+arr[7], {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
206
+ result = response.body
207
+ }
208
+ end
209
+
210
+ @loc = "http://mail.google.com/mail/"
211
+ cookies += ';' + __cookies(response['set-cookie'])
212
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 443)
213
+ np.use_ssl = true
214
+ np.verify_mode = OpenSSL::SSL::VERIFY_NONE
215
+ np.set_debug_output($stderr) if DEBUG
216
+ np.start { |http|
217
+ response = http.get(@loc, {'Cookie' => cookies,'User-agent' => GM_USER_AGENT} )
218
+ result = response.body
219
+ }
220
+ cookies += ';' + __cookies(response['set-cookie'])
221
+
222
+ @cookie_str = cookies + ";TZ=" + @timezone
223
+
224
+ return true
225
+ end
226
+
227
+ #
228
+ # return bool
229
+ # desc Connect to GMail with default session management settings.
230
+ #
231
+ def connect()
232
+ raise 'Not connected, verify the credentials.' unless connect_no_cookie()
233
+ end
234
+
235
+
236
+ #
237
+ # return bool
238
+ # desc See if it is connected to GMail.
239
+ #
240
+ def connected?
241
+ !@cookie_str.empty?
242
+ end
243
+
244
+ #
245
+ # return bool
246
+ # param string query
247
+ # desc Fetch contents by URL query.
248
+ #
249
+ def __fetch(query)
250
+ if connected?
251
+ query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
252
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
253
+ np.set_debug_output($stderr) if DEBUG
254
+ inbox = ''
255
+ np.start { |http|
256
+ response = http.get(GM_LNK_GMAIL + "?" + query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
257
+ inbox = response.body
258
+ }
259
+ if @type == GM_SHOWORIGINAL
260
+ @raw = inbox
261
+ else
262
+ state = 0
263
+ tmp = ''
264
+ inbox.each do |l|
265
+ if /^D\(.*\);$/.match(l)
266
+ state = 0
267
+ tmp += l
268
+ elsif l[0,3]=='D(['
269
+ state = 1
270
+ tmp += l.chomp
271
+ elsif l[0,2]==');'
272
+ state = 2
273
+ tmp += ")\n"
274
+ elsif state == 1
275
+ tmp += l.chomp
276
+ end
277
+ end
278
+ inbox = tmp
279
+ matches = inbox.scan(/D\((.*)\)/).flatten
280
+ packets = {}
281
+ matches.each do |x|
282
+ tmp = eval(x.gsub(",,",",'',").gsub(",,",",'',").gsub(",]",",'']").gsub('#{','#\{'))
283
+ if (packets[tmp[0]] || (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di"))
284
+ if (tmp[0]=="t"||tmp[0]=="ts"||tmp[0]=="a"||tmp[0]=="cl")
285
+ packets[tmp[0]] += tmp[1..-1]
286
+ end
287
+ if (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di")
288
+ if packets["mg"]
289
+ packets["mg"].push(tmp)
290
+ else
291
+ packets["mg"] = [tmp]
292
+ end
293
+ end
294
+ else
295
+ packets[tmp[0]] = tmp
296
+ end
297
+ end
298
+ if packets["cl"] && packets["cl"].length > 1
299
+ packets["ce"] = []
300
+ for i in 1 .. packets["cl"].length-1
301
+ packets["ce"].push(packets["cl"][i])
302
+ end
303
+ end
304
+ @raw = packets
305
+ end
306
+
307
+ true
308
+ else # not logged in yet
309
+ false
310
+ end
311
+ end
312
+ private :__fetch
313
+
314
+ def parse_response(response)
315
+ inbox = response
316
+ state = 0
317
+ tmp = ''
318
+ inbox.each do |l|
319
+ if /^D\(.*\);$/.match(l)
320
+ state = 0
321
+ tmp += l
322
+ elsif l[0,3]=='D(['
323
+ state = 1
324
+ tmp += l.chomp
325
+ elsif l[0,2]==');'
326
+ state = 2
327
+ tmp += ")\n"
328
+ elsif state == 1
329
+ tmp += l.chomp
330
+ end
331
+ end
332
+ inbox = tmp
333
+ matches = inbox.scan(/D\((.*)\)/).flatten
334
+ packets = {}
335
+ matches.each do |x|
336
+ tmp = eval(x.gsub(",,",",'',").gsub(",,",",'',").gsub(",]",",'']").gsub('#{','#\{'))
337
+ if (packets[tmp[0]] || (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di"))
338
+ if (tmp[0]=="t"||tmp[0]=="ts"||tmp[0]=="a"||tmp[0]=="cl")
339
+ packets[tmp[0]] += tmp[1..-1]
340
+ end
341
+ if (tmp[0]=="mi"||tmp[0]=="mb"||tmp[0]=="di")
342
+ if packets["mg"]
343
+ packets["mg"].push(tmp)
344
+ else
345
+ packets["mg"] = [tmp]
346
+ end
347
+ end
348
+ else
349
+ packets[tmp[0]] = tmp
350
+ end
351
+ end
352
+ if packets["cl"] && packets["cl"].length > 1
353
+ packets["ce"] = []
354
+ for i in 1 .. packets["cl"].length-1
355
+ packets["ce"].push(packets["cl"][i])
356
+ end
357
+ end
358
+ @raw = packets
359
+ end
360
+
361
+ #
362
+ # return bool
363
+ # param constant type
364
+ # param mixed box
365
+ # param int pos
366
+ # desc Fetch contents from GMail by type.
367
+ #
368
+ def fetch_box(type, box, pos)
369
+ @type = type
370
+ if connected?
371
+ case type
372
+ when GM_STANDARD
373
+ q = "search=" + box.downcase + "&view=tl&start=" + pos.to_s
374
+
375
+ when GM_LABEL
376
+ q = "search=cat&cat=" + box.to_s + "&view=tl&start=" + pos.to_s
377
+
378
+ when GM_CONVERSATION
379
+ pos = "inbox" if (pos.to_s=='' || pos.to_i == 0)
380
+ if gmail_reserved_names.include?(pos.downcase)
381
+ q = "search="+URI.escape(pos)+"&ser=1&view=cv"
382
+ else
383
+ q = "search=cat&cat="+URI.escape(pos)+"&ser=1&view=cv"
384
+ end
385
+ if (box.is_a?(Array))
386
+ q += "&th=" + box[0].to_s
387
+ for i in 1 .. box.length
388
+ q += "&msgs=" + box[i].to_s
389
+ end
390
+ else
391
+ q += "&th=" + box.to_s
392
+ end
393
+
394
+ when GM_CONV_SPAM
395
+ q = "search=spam&ser=1&view=cv"
396
+ if (box.is_a?(Array))
397
+ q += "&th=" + box[0].to_s
398
+ for i in 1 .. box.length
399
+ q += "&msgs=" + box[i].to_s
400
+ end
401
+ else
402
+ q += "&th=" + box.to_s
403
+ end
404
+
405
+ when GM_CONV_TRASH
406
+ q = "search=trash&ser=1&view=cv"
407
+ if (box.is_a?(Array))
408
+ q += "&th=" + box[0].to_s
409
+ for i in 1 .. box.length
410
+ q += "&msgs=" + box[i].to_s
411
+ end
412
+ else
413
+ q += "&th=" + box.to_s
414
+ end
415
+
416
+ when GM_SHOWORIGINAL
417
+ q = "view=om&th=" + box.to_s
418
+
419
+ when GM_QUERY
420
+ q = "search=query&q=" + URI.escape(box.to_s) + "&view=tl&start=" + pos.to_s
421
+
422
+ when GM_PREFERENCE
423
+ q = "view=pr&pnl=g"
424
+
425
+ when GM_CONTACT
426
+ if box.downcase == "all"
427
+ q = "view=cl&search=contacts&pnl=a"
428
+ elsif box.downcase == "search"
429
+ q = "view=cl&search=contacts&pnl=s&q=" + URI.escape(pos.to_s)
430
+ elsif box.downcase == "detail"
431
+ q = "search=contacts&ct_id=" + URI.escape(pos.to_s) + "&cvm=2&view=ct"
432
+ elsif box.downcase == "group_detail"
433
+ q = "search=contacts&ct_id=" + URI.escape(pos.to_s) + "&cvm=1&view=ctl"
434
+ elsif box.downcase == "group"
435
+ q = "view=cl&search=contacts&pnl=l"
436
+ else # frequently mailed
437
+ q = "view=cl&search=contacts&pnl=p"
438
+ end
439
+
440
+ else
441
+ q = "search=inbox&view=tl&start=0&init=1"
442
+ end
443
+
444
+ __fetch(q)
445
+ else
446
+ false
447
+ end
448
+ end
449
+
450
+ #
451
+ # return snapshot
452
+ # param constant type
453
+ # param mixed box
454
+ # param int pos
455
+ # desc Fetch contents from GMail by type.
456
+ #
457
+ def fetch(hash_param)
458
+ type = GM_STANDARD
459
+ box = "inbox"
460
+ pos = 0
461
+ @filter = {}
462
+ hash_param.keys.each do |k|
463
+ case k
464
+ when :label
465
+ type = GM_LABEL
466
+ box = hash_param[k]
467
+ when :standard
468
+ type = GM_STANDARD
469
+ box = hash_param[k]
470
+ when :conversation
471
+ type = GM_CONVERSATION
472
+ box = hash_param[k]
473
+ when :show_original
474
+ type = GM_SHOWORIGINAL
475
+ box = hash_param[k]
476
+ when :preference
477
+ type = GM_PREFERENCE
478
+ box = hash_param[k]
479
+ when :contact
480
+ type = GM_CONTACT
481
+ box = hash_param[k]
482
+ when :param
483
+ pos = hash_param[k]
484
+ when :query
485
+ type = GM_QUERY
486
+ box = hash_param[k]
487
+ when :pos
488
+ pos = hash_param[k].to_i
489
+ when :read
490
+ @filter[:read] = hash_param[k]
491
+ when :star
492
+ @filter[:star] = hash_param[k]
493
+ else
494
+ raise ArgumentError, 'Invalid hash argument'
495
+ end
496
+ end
497
+ box = "inbox" unless box
498
+ fetch_box(type,box,pos)
499
+ if type == GM_CONVERSATION
500
+ fetch_box(GM_CONV_SPAM,box,pos) if @raw['mg'].nil?
501
+ fetch_box(GM_CONV_TRASH,box,pos) if @raw['mg'].nil?
502
+ end
503
+
504
+ if type == GM_SHOWORIGINAL
505
+ ss = @raw
506
+ else
507
+ ss = snapshot(type)
508
+ end
509
+ if block_given?
510
+ yield(ss)
511
+ elsif type == GM_CONTACT
512
+ ss.contacts
513
+ else
514
+ ss
515
+ end
516
+ end
517
+
518
+ #
519
+ # return message
520
+ # param message id
521
+ # desc Fetch a message with message id
522
+ #
523
+ def msg(msgid)
524
+ m = fetch(:conversation=>msgid).message
525
+ if m.nil? then
526
+ raise ArgumentError, 'Message Not Exist'
527
+ end
528
+ m.connection = self
529
+ if block_given?
530
+ yield(m)
531
+ else
532
+ m
533
+ end
534
+
535
+ end
536
+
537
+ #
538
+ # return string[]
539
+ # param string[] convs
540
+ # param string path
541
+ # desc Save all attaching files of conversations to a path.
542
+ #
543
+ def attachments_of(convs, path)
544
+
545
+ if connected?
546
+ if (!convs.is_a?(Array))
547
+ convs = [convs] # array wrapper
548
+ end
549
+ final = []
550
+ convs.each do |v|
551
+ if v.attachment
552
+ v.attachment.each do |vv|
553
+ f = path+"/"+vv.filename
554
+ f = path+"/"+vv.filename+"."+rand(2000).to_s while (FileTest.exist?(f))
555
+ if attachment(vv.id,v.id,f,false)
556
+ final.push(f)
557
+ end
558
+ end
559
+ end
560
+ end
561
+ final
562
+ else
563
+ nil
564
+ end
565
+ end
566
+
567
+ #
568
+ # return string[]
569
+ # param string[] convs
570
+ # param string path
571
+ # desc Save all attaching files of conversations to a path.
572
+ #
573
+ def zip_attachments_of(convs, path)
574
+
575
+ if connected?
576
+ if (!convs.is_a?(Array))
577
+ convs = [convs] # array wrapper
578
+ end
579
+ final = []
580
+ convs.each do |v|
581
+ if v.attachment
582
+ f = path+"/attachment.zip"
583
+ f = path+"/attachment."+rand(2000).to_s+".zip" while (FileTest.exist?(f))
584
+ if attachment(v["attachment"][0]["id"],v["id"],f,true)
585
+ final.push(f)
586
+ end
587
+ end
588
+ end
589
+ final
590
+ else
591
+ nil
592
+ end
593
+ end
594
+
595
+ #
596
+ # return bool
597
+ # param string attid
598
+ # param string msgid
599
+ # param string filename
600
+ # desc Save attachment with attachment ID attid and message ID msgid to file with name filename.
601
+ #
602
+ def attachment(attid, msgid, filename, zipped=false)
603
+
604
+ if connected?
605
+
606
+ if !zipped
607
+ query = GM_LNK_ATTACHMENT + "&attid=" + URI.escape(attid) + "&th=" + URI.escape(msgid)
608
+ else
609
+ query = GM_LNK_ATTACHMENT_ZIPPED + "&th=" + URI.escape(msgid)
610
+ end
611
+
612
+ File.open(filename,"wb") do |f|
613
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
614
+ np.start { |http|
615
+ response = http.get(query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
616
+ f.write(response.body)
617
+ }
618
+ end
619
+ true
620
+ else
621
+ false
622
+ end
623
+ end
624
+
625
+ #
626
+ # return array of labels
627
+ # desc get label lists
628
+ #
629
+ def labels()
630
+ if connected?
631
+ fetch(:standard=>"inbox") {|s| s.label_list }
632
+ else
633
+ nil
634
+ end
635
+ end
636
+
637
+ #
638
+ # return string
639
+ # param string label
640
+ # desc validate label
641
+ #
642
+ def validate_label(label)
643
+ label.strip!
644
+ if label==''
645
+ raise ArgumentError, 'Error: Labels cannot empty string'
646
+ end
647
+ if label.length > 40
648
+ raise ArgumentError, 'Error: Labels cannot contain more than 40 characters'
649
+ end
650
+ if label.include?('^')
651
+ raise ArgumentError, "Error: Labels cannot contain the character '^'"
652
+ end
653
+ label
654
+ end
655
+
656
+ #
657
+ # return boolean
658
+ # param string label
659
+ # desc create label
660
+ #
661
+ def create_label(label)
662
+ if connected?
663
+ perform_action(GM_ACT_CREATELABEL, '', validate_label(label))
664
+ else
665
+ false
666
+ end
667
+ end
668
+
669
+ #
670
+ # return boolean
671
+ # param string label
672
+ # desc create label
673
+ #
674
+ def delete_label(label)
675
+ if connected?
676
+ perform_action(GM_ACT_DELETELABEL, '', validate_label(label))
677
+ else
678
+ false
679
+ end
680
+ end
681
+
682
+ #
683
+ # return boolean
684
+ # param string old_label
685
+ # param string new_label
686
+ # desc create label
687
+ #
688
+ def rename_label(old_label,new_label)
689
+ if connected?
690
+ perform_action(GM_ACT_RENAMELABEL, '',
691
+ validate_label(old_label) +'^' + validate_label(new_label))
692
+ else
693
+ false
694
+ end
695
+ end
696
+
697
+ #
698
+ # return boolean
699
+ # param string msgid
700
+ # param string label
701
+ # desc apply label to message
702
+ #
703
+ def apply_label(id,label)
704
+ if connected?
705
+ perform_action(GM_ACT_APPLYLABEL, id, validate_label(label))
706
+ else
707
+ false
708
+ end
709
+ end
710
+
711
+ #
712
+ # return boolean
713
+ # param string msgid
714
+ # param string label
715
+ # desc remove label from message
716
+ #
717
+ def remove_label(id,label)
718
+ if connected?
719
+ perform_action(GM_ACT_REMOVELABEL, id, validate_label(label))
720
+ else
721
+ false
722
+ end
723
+ end
724
+
725
+ #
726
+ # return boolean
727
+ # param string hash_param
728
+ # desc remove label from message
729
+ #
730
+ def update_preference(hash_param)
731
+ if connected?
732
+ args = {}
733
+ hash_param.keys.each do |k|
734
+ case k
735
+ when :max_page_size
736
+ args['p_ix_nt'] = hash_param[k]
737
+ when :keyboard_shortcuts
738
+ args['p_bx_hs'] = hash_param[k] ? '1' : '0'
739
+ when :indicators
740
+ args['p_bx_sc'] = hash_param[k] ? '1' : '0'
741
+ when :display_language
742
+ args['p_sx_dl'] = hash_param[k]
743
+ when :signature
744
+ args['p_sx_sg'] = hash_param[k]
745
+ when :reply_to
746
+ args['p_sx_rt'] = hash_param[k]
747
+ when :snippets
748
+ args['p_bx_ns'] = hash_param[k] ? '0' : '1'
749
+ when :display_name
750
+ args['p_sx_dn'] = hash_param[k]
751
+ end
752
+ end
753
+ param = '&' + args.to_a.map {|x| x.join('=')}.join('&')
754
+ perform_action(GM_ACT_PREFERENCE,'', param)
755
+ else
756
+ false
757
+ end
758
+ end
759
+
760
+ #
761
+ # return boolean
762
+ # param string msgid
763
+ # desc apply star to a message
764
+ #
765
+ def apply_star(msgid)
766
+ if connected?
767
+ perform_action(GM_ACT_STAR,msgid, '')
768
+ else
769
+ false
770
+ end
771
+ end
772
+
773
+ #
774
+ # return boolean
775
+ # param string msgid
776
+ # desc remove star from a message
777
+ #
778
+ def remove_star(msgid)
779
+ if connected?
780
+ perform_action(GM_ACT_UNSTAR,msgid, '')
781
+ else
782
+ false
783
+ end
784
+ end
785
+
786
+ #
787
+ # return boolean
788
+ # param string msgid
789
+ # desc report a message as spam
790
+ #
791
+ def report_spam(msgid)
792
+ if connected?
793
+ perform_action(GM_ACT_SPAM,msgid, '')
794
+ else
795
+ false
796
+ end
797
+ end
798
+
799
+ #
800
+ # return boolean
801
+ # param string msgid
802
+ # desc report a message as not spam
803
+ #
804
+ def report_not_spam(msgid)
805
+ if connected?
806
+ perform_action(GM_ACT_UNSPAM,msgid, '')
807
+ else
808
+ false
809
+ end
810
+ end
811
+
812
+ #
813
+ # return boolean
814
+ # param string msgid
815
+ # desc delete a spam message forever
816
+ #
817
+ def delete_spam(msgid)
818
+ if connected?
819
+ perform_action(GM_ACT_DELSPAM,msgid, '')
820
+ else
821
+ false
822
+ end
823
+ end
824
+
825
+ #
826
+ # return boolean
827
+ # param string msgid
828
+ # desc mark a message as read
829
+ #
830
+ def mark_read(msgid)
831
+ if connected?
832
+ perform_action(GM_ACT_READ,msgid, '')
833
+ else
834
+ false
835
+ end
836
+ end
837
+
838
+ #
839
+ # return boolean
840
+ # param string msgid
841
+ # desc mark a message as unread
842
+ #
843
+ def mark_unread(msgid)
844
+ if connected?
845
+ perform_action(GM_ACT_UNREAD,msgid, '')
846
+ else
847
+ false
848
+ end
849
+ end
850
+
851
+ #
852
+ # return original message string
853
+ # param string msgid
854
+ # desc show original message format
855
+ #
856
+ def show_original(msgid)
857
+ if connected?
858
+ fetch(:show_original=>msgid)
859
+ else
860
+ false
861
+ end
862
+ end
863
+
864
+ #
865
+ # return boolean
866
+ # param string msgid
867
+ # desc move a message to trash
868
+ #
869
+ def trash(msgid)
870
+ if connected?
871
+ perform_action(GM_ACT_TRASH,msgid, '')
872
+ else
873
+ false
874
+ end
875
+ end
876
+
877
+ #
878
+ # return boolean
879
+ # param string msgid
880
+ # desc move a message from trash to inbox
881
+ #
882
+ def untrash(msgid)
883
+ if connected?
884
+ perform_action(GM_ACT_UNTRASH,msgid, '')
885
+ else
886
+ false
887
+ end
888
+ end
889
+
890
+ #
891
+ # return boolean
892
+ # param string msgid
893
+ # desc delete a trash message forever
894
+ #
895
+ def delete_trash(msgid)
896
+ if connected?
897
+ perform_action(GM_ACT_DELTRASH,msgid, '')
898
+ else
899
+ false
900
+ end
901
+ end
902
+
903
+ #
904
+ # return boolean
905
+ # param string msgid
906
+ # desc delete a message forever
907
+ #
908
+ def delete_message(msgid)
909
+ if connected?
910
+ perform_action(GM_ACT_DELFOREVER,msgid, '')
911
+ else
912
+ false
913
+ end
914
+ end
915
+
916
+ #
917
+ # return boolean
918
+ # param string msgid
919
+ # desc archive a message
920
+ #
921
+ def archive(msgid)
922
+ if connected?
923
+ perform_action(GM_ACT_ARCHIVE,msgid, '')
924
+ else
925
+ false
926
+ end
927
+ end
928
+
929
+ #
930
+ # return boolean
931
+ # param string msgid
932
+ # desc archive a message
933
+ #
934
+ def unarchive(msgid)
935
+ if connected?
936
+ perform_action(GM_ACT_INBOX,msgid, '')
937
+ else
938
+ false
939
+ end
940
+ end
941
+
942
+
943
+ #
944
+ # return hash of preference
945
+ # desc get preferences
946
+ #
947
+ def preference()
948
+ if connected?
949
+ fetch(:preference=>"all").preference
950
+ else
951
+ nil
952
+ end
953
+ end
954
+
955
+ #
956
+ # return array of messages
957
+ # desc get message lists
958
+ #
959
+ def messages(hash_param)
960
+ if connected?
961
+ ml = fetch(hash_param).message_list
962
+ @filter.keys.each do |k|
963
+ case k
964
+ when :read
965
+ ml.box = ml.box.find_all { |x| x.read? == @filter[k] }
966
+ when :star
967
+ ml.box = ml.box.find_all { |x| x.star? == @filter[k] }
968
+ end
969
+ end
970
+ ml.connection = self
971
+ if block_given?
972
+ yield(ml)
973
+ else
974
+ ml
975
+ end
976
+ else
977
+ nil
978
+ end
979
+ end
980
+
981
+ #
982
+ # return string
983
+ # param string query
984
+ # desc Dump everything to output.
985
+ #
986
+ def dump(query)
987
+ page = ''
988
+ if connected?
989
+ query += "&zv=" + (rand(2000)*2147483.648).to_i.to_s
990
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
991
+ np.start { |http|
992
+ response = http.get(GM_LNK_GMAIL + "?"+query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
993
+ page = response.body
994
+ }
995
+ else # not logged in yet
996
+
997
+ end
998
+ page
999
+ end
1000
+
1001
+ def stripslashes(string)
1002
+ encode(string.gsub("\\\"", "\"").gsub("\\'", "'").gsub("\\\\", "\\"))
1003
+ end
1004
+
1005
+
1006
+ #
1007
+ # return bool
1008
+ # param string to
1009
+ # param string subject
1010
+ # param string body
1011
+ # param string cc
1012
+ # param string bcc
1013
+ # param string msg_id
1014
+ # param string thread_id
1015
+ # param string[] files
1016
+ # desc Send GMail.
1017
+ #
1018
+ def send(*param)
1019
+ if param.length==1 && param[0].is_a?(Hash)
1020
+ param = param[0]
1021
+ from = param[:from] || ''
1022
+ to = param[:to] || ''
1023
+ subject = param[:subject] || ''
1024
+ body = param[:body] || ''
1025
+ cc = param[:cc] || ''
1026
+ bcc = param[:bcc] || ''
1027
+ msg_id = param[:msg_id] || ''
1028
+ thread_id = param[:msg_id] || ''
1029
+ files = param[:files] || []
1030
+ draft = param[:draft] || false
1031
+ draft_id = param[:draft_id] || ''
1032
+ elsif param.length==10
1033
+ to, subject, body, cc, bcc, msg_id, thread_id, files, draft, draft_id = param
1034
+ elsif param.length==11
1035
+ from, to, subject, body, cc, bcc, msg_id, thread_id, files, draft, draft_id = param
1036
+ else
1037
+ raise ArgumentError, 'Invalid argument'
1038
+ end
1039
+
1040
+ if connected?
1041
+ other_emails = fetch(:preference=>"all").other_emails
1042
+ if other_emails.length>0
1043
+ other_emails.each {|v|
1044
+ from = v['email'] if from=='' && v['default']
1045
+ }
1046
+ from = @login + '@gmail.com' if from==''
1047
+ else
1048
+ from = nil
1049
+ end
1050
+
1051
+ postdata = {}
1052
+ if draft
1053
+ postdata["view"] = "sd"
1054
+ postdata["draft"] = draft_id
1055
+ postdata["rm"] = msg_id
1056
+ postdata["th"] = thread_id
1057
+ else
1058
+ postdata["view"] = "sm"
1059
+ postdata["draft"] = draft_id
1060
+ postdata["rm"] = msg_id
1061
+ postdata["th"] = thread_id
1062
+ end
1063
+ postdata["msgbody"] = stripslashes(body)
1064
+ postdata["from"] = stripslashes(from) if from
1065
+ postdata["to"] = stripslashes(to)
1066
+ postdata["subject"] = stripslashes(subject)
1067
+ postdata["cc"] = stripslashes(cc)
1068
+ postdata["bcc"] = stripslashes(bcc)
1069
+
1070
+ postdata["cmid"] = 1
1071
+ postdata["ishtml"] = param[:ishtml] || 0
1072
+
1073
+ postdata["at"] = at_value
1074
+
1075
+ boundary = "----#{Time.now.to_i}#{(rand(2000)*2147483.648).to_i}"
1076
+ postdata2 = []
1077
+ postdata.each {|k,v|
1078
+ postdata2.push("Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n#{v}\r\n")
1079
+ }
1080
+
1081
+ files.each_with_index do |f,i|
1082
+ content = File.open(f,'rb') { |c| c.read }
1083
+ postdata2.push("Content-Disposition: form-data; name=\"file#{i}\"; filename=\"#{File.basename(f)}\"\r\n" +
1084
+ "Content-Transfer-Encoding: binary\r\n" +
1085
+ "Content-Type: application/octet-stream\r\n\r\n" + content + "\r\n")
1086
+ end
1087
+
1088
+ postdata = postdata2.collect { |p|
1089
+ "--" + boundary + "\r\n" + p
1090
+ }.join('') + "--" + boundary + "--\r\n"
1091
+
1092
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1093
+ response = nil
1094
+ np.set_debug_output($stderr) if DEBUG
1095
+ np.start { |http|
1096
+ response = http.post(GM_LNK_GMAIL, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT,'Content-type' => 'multipart/form-data; boundary=' + boundary } )
1097
+ }
1098
+ true
1099
+ else
1100
+ false
1101
+ end
1102
+
1103
+ end
1104
+
1105
+ #
1106
+ # return bool
1107
+ # param constant act
1108
+ # param string[] id
1109
+ # param string para
1110
+ # desc Perform action on messages.
1111
+ #
1112
+ def perform_action(act, id, para)
1113
+
1114
+ if connected?
1115
+
1116
+ if (act == GM_ACT_DELFOREVER)
1117
+ perform_action(GM_ACT_TRASH, id, 0) # trash it before
1118
+ end
1119
+ postdata = "act="
1120
+
1121
+ action_codes = ["ib", "cc_", "dc_", "nc_", "ac_", "rc_", "prefs", "st", "xst",
1122
+ "sp", "us", "rd", "ur", "tr", "dl", "rc_^i", "ib", "ib", "dd", "dm", "dl", "dl"]
1123
+ postdata += action_codes[act] ? action_codes[act] : action_codes[GM_ACT_INBOX]
1124
+ if act == GM_ACT_RENAMELABEL then
1125
+ paras = para.split('^')
1126
+ para = validate_label(paras[0])+'^'+validate_label(paras[1])
1127
+ elsif ([GM_ACT_APPLYLABEL,GM_ACT_REMOVELABEL,GM_ACT_CREATELABEL,
1128
+ GM_ACT_DELETELABEL,].include?(act))
1129
+ para = validate_label(para)
1130
+ end
1131
+ if ([GM_ACT_APPLYLABEL,GM_ACT_REMOVELABEL,GM_ACT_CREATELABEL,
1132
+ GM_ACT_DELETELABEL,GM_ACT_RENAMELABEL,GM_ACT_PREFERENCE].include?(act))
1133
+ postdata += para.to_s
1134
+ end
1135
+
1136
+ postdata += "&at=" + at_value
1137
+
1138
+ if (act == GM_ACT_TRASHMSG)
1139
+ postdata += "&m=" + id.to_s
1140
+ else
1141
+ if id.is_a?(Array)
1142
+ id.each {|t| postdata += "&t="+t.to_s }
1143
+ else
1144
+ postdata += "&t="+id.to_s
1145
+ end
1146
+ end
1147
+ postdata += "&vp="
1148
+
1149
+ if [GM_ACT_UNTRASH,GM_ACT_DELFOREVER,GM_ACT_DELTRASH].include?(act)
1150
+ link = GM_LNK_GMAIL+"?search=trash&view=tl&start=0"
1151
+ elsif (act == GM_ACT_DELSPAM)
1152
+ link = GM_LNK_GMAIL+"?search=spam&view=tl&start=0"
1153
+ else
1154
+ link = GM_LNK_GMAIL+"?search=query&q=&view=tl&start=0"
1155
+ end
1156
+
1157
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1158
+ np.set_debug_output($stderr) if DEBUG
1159
+ np.start { |http|
1160
+ response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1161
+ result = response.body
1162
+ }
1163
+
1164
+ true
1165
+ else
1166
+ false
1167
+ end
1168
+
1169
+ end
1170
+
1171
+ #
1172
+ # return void
1173
+ # desc Disconnect from GMail.
1174
+ #
1175
+ def disconnect()
1176
+
1177
+ response = nil
1178
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1179
+ np.set_debug_output($stderr) if DEBUG
1180
+ np.start { |http|
1181
+ response = http.get(GM_LNK_LOGOUT,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
1182
+ }
1183
+ arr = URI::split(response["Location"])
1184
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(arr[2], 80)
1185
+ np.set_debug_output($stderr) if DEBUG
1186
+ np.start { |http|
1187
+ response = http.get(arr[5]+'?'+arr[7], {'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1188
+ }
1189
+
1190
+ @cookie_str = ''
1191
+ end
1192
+
1193
+ #
1194
+ # return GMailSnapshot
1195
+ # param constant type
1196
+ # desc Get GMSnapshot by type.
1197
+ #
1198
+ def snapshot(type)
1199
+ if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY,GM_PREFERENCE,GM_CONTACT].include?(type)
1200
+ return GMailSnapshot.new(type, @raw, @charset)
1201
+ else
1202
+ return GMailSnapshot.new(GM_STANDARD, @raw, @charset) # assuming normal by default
1203
+ end
1204
+ end
1205
+
1206
+ def snap(type)
1207
+ action = GM_STANDARD
1208
+ case type
1209
+ when :standard
1210
+ action = GM_STANDARD
1211
+ when :label
1212
+ action = GM_LABEL
1213
+ when :conversation
1214
+ action = GM_CONVERSATION
1215
+ when :query
1216
+ action = GM_QUERY
1217
+ when :preference
1218
+ action = GM_PREFERENCE
1219
+ when :contact
1220
+ action = GM_CONTACT
1221
+ else
1222
+ raise ArgumentError, 'Invalid type'
1223
+ end
1224
+ snapshot(action)
1225
+ end
1226
+
1227
+ #
1228
+ # return bool
1229
+ # param string email
1230
+ # desc Send Gmail invite to email
1231
+ #
1232
+ def invite(email)
1233
+
1234
+ if connected?
1235
+
1236
+ postdata = "act=ii&em=" + URI.escape(email)
1237
+
1238
+ postdata += "&at=" + at_value
1239
+
1240
+ link = GM_LNK_GMAIL + "?view=ii"
1241
+
1242
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 443)
1243
+ np.use_ssl = true
1244
+ np.verify_mode = OpenSSL::SSL::VERIFY_NONE
1245
+ np.set_debug_output($stderr) if DEBUG
1246
+ np.start { |http|
1247
+ response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1248
+ }
1249
+
1250
+ true
1251
+ else
1252
+ false
1253
+ end
1254
+
1255
+ end
1256
+
1257
+
1258
+ def at_value
1259
+ val = ''
1260
+ cc = @cookie_str.split(';')
1261
+ cc.each do |cc_part|
1262
+ cc_parts = cc_part.split('=')
1263
+ if(cc_parts[0]=='GMAIL_AT')
1264
+ val = cc_parts[1]
1265
+ break
1266
+ end
1267
+ end
1268
+ val
1269
+ end
1270
+
1271
+ #
1272
+ # return string[]
1273
+ # desc (Static) Get names of standard boxes.
1274
+ #
1275
+ def standard_box
1276
+ ["Inbox","Starred","Sent","Drafts","All","Spam","Trash"]
1277
+ end
1278
+
1279
+ #
1280
+ # return string
1281
+ # param string header
1282
+ # desc (Static Private) Extract cookies from header.
1283
+ #
1284
+ def __cookies(header)
1285
+ return '' unless header
1286
+ arr = []
1287
+ header.split(', ').each { |x|
1288
+ if x.include?('GMT')
1289
+ arr[-1] += ", " + x
1290
+ else
1291
+ arr.push x
1292
+ end
1293
+ }
1294
+ arr.delete_if {|x| x.include?('LSID=EXPIRED') }
1295
+ arr.map! {|x| x.split(';')[0]}
1296
+ arr.join(';')
1297
+ end
1298
+
1299
+ def edit_contact(contact_id, name, email, notes, details=[])
1300
+ if connected?
1301
+ postdata = {}
1302
+ postdata["act"] = "ec"
1303
+ postdata["ct_id"] = contact_id.to_s
1304
+ postdata["ct_nm"] = name
1305
+ postdata["ct_em"] = email
1306
+ postdata["ctf_n"] = notes
1307
+
1308
+ if (details.length > 0)
1309
+ i = 0 # the detail number
1310
+ det_num = '00' # detail number padded to 2 numbers for gmail
1311
+ details.each do |detail1|
1312
+ postdata["ctsn_#{det_num}"] = "Unnamed" # default name if none defined later
1313
+ address = '' # default address if none defined later
1314
+ k = 0 # the field number supplied to Gmail
1315
+ field_num = '00' # must be padded to 2 numbers for gmail
1316
+ detail1.each do |key,value|
1317
+ field_type = ''
1318
+ case key
1319
+ when :phone
1320
+ field_type = "p"
1321
+ when :email
1322
+ field_type = "e"
1323
+ when :mobile
1324
+ field_type = "m"
1325
+ when :fax
1326
+ field_type = "f"
1327
+ when :pager
1328
+ field_type = "b"
1329
+ when :im
1330
+ field_type = "i"
1331
+ when :company
1332
+ field_type = "d"
1333
+ when :position
1334
+ field_type = "t" # t = title
1335
+ when :other
1336
+ field_type = "o"
1337
+ when :address
1338
+ field_type = "a"
1339
+ when :detail_name
1340
+ field_type = "xyz"
1341
+ else
1342
+ field_type = "o"
1343
+ end
1344
+ if (field_type == "xyz")
1345
+ postdata["ctsn_#{det_num}"] = value
1346
+ elsif (field_type == "a")
1347
+ address = value
1348
+ else
1349
+ # e.g. ctsf_00_00_p for phone
1350
+ postdata["ctsf_#{det_num}_#{field_num}_#{field_type}"] = value
1351
+ # increments the field number and pads it
1352
+ k = k + 1
1353
+ field_num = "%02d" % k
1354
+ end
1355
+ end
1356
+ # Address field needs to be last
1357
+ # if more than one address was given, the last one found will be used
1358
+ if (address != '')
1359
+ postdata["ctsf_#{det_num}_#{field_num}_a"] = address
1360
+ end
1361
+
1362
+ # increment detail number
1363
+ i = i + 1
1364
+ det_num = "%02d" % i
1365
+ end
1366
+ end
1367
+
1368
+ postdata["at"] = at_value
1369
+
1370
+ postdata2 = []
1371
+ postdata.each {|k,v|
1372
+ postdata2.push("#{k}=#{v}")
1373
+ }
1374
+ postdata = postdata2.join('&')
1375
+
1376
+ link = GM_LNK_GMAIL + "?view=up"
1377
+
1378
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1379
+ np.set_debug_output($stderr) if DEBUG
1380
+ np.start { |http|
1381
+ response = http.post(link, postdata,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1382
+ parse_response(response.body)
1383
+ }
1384
+
1385
+ orig_contact_id = contact_id.to_i
1386
+ if (orig_contact_id == "-1" && @raw["ar"][1])
1387
+ if (@raw["cov"][1][1])
1388
+ contact_id = @raw["cov"][1][1]
1389
+ elsif (@raw["a"][1][1])
1390
+ contact_id = @raw["a"][1][1]
1391
+ elsif (@raw["cl"][1][1])
1392
+ contact_id = @raw["cl"][1][1]
1393
+ end
1394
+ end
1395
+
1396
+ status = @raw["ar"][1]==1
1397
+ message = @raw["ar"][2]
1398
+ raise message unless status
1399
+
1400
+ return status
1401
+ else
1402
+ raise "Not connected"
1403
+ return false
1404
+ end
1405
+ end
1406
+
1407
+ def add_contact(name, email, notes, details=[])
1408
+ edit_contact(-1,name, email, notes, details)
1409
+ end
1410
+
1411
+ def add_sender_to_contact(message_id)
1412
+ if connected?
1413
+ query = ''
1414
+ query += "&search=inbox"
1415
+ query += "&view=up"
1416
+ query += "&act=astc"
1417
+ query += "&at="+at_value
1418
+ query += "&m="+message_id.to_s
1419
+
1420
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1421
+ np.set_debug_output($stderr) if DEBUG
1422
+ np.start { |http|
1423
+ response = http.get(GM_LNK_GMAIL + "?" + query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
1424
+ }
1425
+
1426
+ return true
1427
+ else
1428
+ return false
1429
+ end
1430
+ end
1431
+
1432
+ def delete_contact(id)
1433
+ if connected?
1434
+ query = ''
1435
+
1436
+ if id.is_a?(Array)
1437
+ #Post: act=dc&at=xxxxx-xxxx&cl_nw=&cl_id=&cl_nm=&c=0&c=3d
1438
+ $query += "&act=dc&cl_nw=&cl_id=&cl_nm="
1439
+ id.each { |contact_id|
1440
+ query += "&c="+contact_id
1441
+ }
1442
+ else
1443
+ query += "search=contacts"
1444
+ query += "&ct_id="+id.to_s
1445
+ query += "&cvm=2"
1446
+ query += "&view=up"
1447
+ query += "&act=dc"
1448
+ end
1449
+
1450
+ query += "&at="+at_value
1451
+ if !(id.is_a?(Array))
1452
+ query += "&c="+id.to_s
1453
+ end
1454
+
1455
+ np = Net::HTTP::Proxy(@proxy_host,@proxy_port,@proxy_user,@proxy_pass).new(GM_LNK_HOST, 80)
1456
+ response = nil
1457
+ np.set_debug_output($stderr) if DEBUG
1458
+ if (id.is_a?(Array))
1459
+ np.start { |http|
1460
+ response = http.post(GM_LNK_GMAIL+"?view=up", query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT} )
1461
+ parse_response(response.body)
1462
+ }
1463
+ else
1464
+ np.start { |http|
1465
+ response = http.get(GM_LNK_GMAIL + "?" + query,{'Cookie' => @cookie_str,'User-agent' => GM_USER_AGENT })
1466
+ parse_response(response.body)
1467
+ }
1468
+ end
1469
+
1470
+ status = @raw["ar"][1]==1
1471
+ message = @raw["ar"][2]
1472
+ raise message unless message
1473
+ return status
1474
+ else
1475
+ return false
1476
+ end
1477
+ end
1478
+
1479
+ end
1480
+
1481
+ class GMailSnapshot
1482
+
1483
+ GM_STANDARD = 0x001
1484
+ GM_LABEL = 0x002
1485
+ GM_CONVERSATION = 0x004
1486
+ GM_QUERY = 0x008
1487
+ GM_CONTACT = 0x010
1488
+ GM_PREFERENCE = 0x020
1489
+
1490
+ def decode(str)
1491
+ return str if @charset.upcase == 'UTF-8'
1492
+ begin
1493
+ require 'Win32API'
1494
+ str = str.unpack("U*").pack("S*") + "\0"
1495
+ ostr = "\0" * str.length*2
1496
+ wideCharToMultiByte = Win32API.new('kernel32','WideCharToMultiByte',['L','L','P','L','P','L','L','L'],'L')
1497
+ wideCharToMultiByte.Call(0,0,str,-1,ostr,str.length*2,0,0)
1498
+ ostr.strip
1499
+ rescue LoadError
1500
+ require 'iconv'
1501
+ Iconv::iconv(@charset,'UTF-8',str)[0]
1502
+ end
1503
+ end
1504
+
1505
+ def strip_tags(str,allowable_tags='')
1506
+ str.gsub(/<[^>]*>/, '')
1507
+ end
1508
+
1509
+ #
1510
+ # return GMailSnapshot
1511
+ # param constant type
1512
+ # param array raw
1513
+ # desc Constructor.
1514
+ #
1515
+ attr_reader :gmail_ver, :quota_mb, :quota_per, :preference
1516
+ attr_reader :std_box_new, :have_invit, :label_list, :label_new
1517
+ attr_reader :message_list
1518
+ attr_reader :message
1519
+ attr_reader :contacts, :other_emails
1520
+
1521
+ def initialize(type, raw, charset='UTF-8')
1522
+ @charset = charset
1523
+ if (!raw.is_a?(Hash))
1524
+ @created = 0
1525
+ return nil
1526
+ elsif (raw.empty?)
1527
+ @created = 0
1528
+ return nil
1529
+ end
1530
+ if [GM_STANDARD,GM_LABEL,GM_CONVERSATION,GM_QUERY].include?(type)
1531
+ @gmail_ver = raw["v"][1]
1532
+ if raw["qu"]
1533
+ @quota_mb = raw["qu"][1]
1534
+ @quota_per = raw["qu"][3]
1535
+ end
1536
+ if (!raw["ds"].is_a?(Array))
1537
+ @created = 0
1538
+ return nil
1539
+ end
1540
+ @std_box_new = raw["ds"][1..-1]
1541
+
1542
+ @have_invit = raw["i"][1]
1543
+ @label_list = []
1544
+ @label_new = []
1545
+ raw["ct"][1].each {|v|
1546
+ @label_list.push(v[0])
1547
+ @label_new.push(v[1])
1548
+ }
1549
+ if raw["ts"]
1550
+ @view = (GM_STANDARD|GM_LABEL|GM_QUERY)
1551
+ @message_list = MessageList.new(raw["ts"][5],raw["ts"][3],raw["ts"][1])
1552
+ end
1553
+ @box = []
1554
+ if raw["t"]
1555
+ raw["t"].each do |t|
1556
+ if (t != "t")
1557
+ b = Box.new
1558
+ b.id = t[0]
1559
+ b.is_read = (t[1] != 1 ? 1 : 0)
1560
+ b.is_starred = (t[2] == 1 ? 1 : 0)
1561
+ b.date = decode(strip_tags(t[3]))
1562
+ b.sender = decode(strip_tags(t[4]))
1563
+ b.flag = t[5]
1564
+ b.subject = decode(strip_tags(t[6]))
1565
+ b.snippet = decode(t[7].gsub('&quot;','"').gsub('&hellip;','...'))
1566
+ b.labels = t[8].empty? ? [] : t[8].map{|tt|decode(tt)}
1567
+ b.attachment = t[9].empty? ? []: decode(t[9]).split(",")
1568
+ b.msgid = t[10]
1569
+ @box.push(b)
1570
+ end
1571
+ end
1572
+ end
1573
+
1574
+ if raw["cs"]
1575
+ @view = GM_CONVERSATION
1576
+ @conv_title = raw["cs"][2]
1577
+ @conv_total = raw["cs"][8]
1578
+ @conv_id = raw["cs"][1]
1579
+ @conv_labels = raw["cs"][5].empty? ? '' : raw["cs"][5]
1580
+ if !@conv_labels.empty?
1581
+ ij = @conv_labels.index("^i")
1582
+ if !ij.nil?
1583
+ @conv_labels[ij] = "Inbox"
1584
+ end
1585
+ ij = @conv_labels.index("^s")
1586
+ if !ij.nil?
1587
+ @conv_labels[ij] = "Spam"
1588
+ end
1589
+ ij = @conv_labels.index("^k")
1590
+ if !ij.nil?
1591
+ @conv_labels[ij] = "Trash"
1592
+ end
1593
+ ij = @conv_labels.index("^t")
1594
+ if !ij.nil?
1595
+ @conv_labels[ij] = '' # Starred
1596
+ end
1597
+ ij = @conv_labels.index("^r")
1598
+ if !ij.nil?
1599
+ @conv_labels[ij] = "Drafts"
1600
+ end
1601
+ end
1602
+
1603
+ @conv_starred = false
1604
+
1605
+ @conv = []
1606
+ b = Conversation.new
1607
+ raw["mg"].each do |r|
1608
+ if (r[0] == "mb")
1609
+ b.body = '' if b.body.nil?
1610
+ b.body += r[1]
1611
+ if (r[2] == 0)
1612
+ b.body = decode(b.body)
1613
+ @conv.push(b)
1614
+ b = Conversation.new
1615
+ end
1616
+ elsif (r[0] == "mi")
1617
+ if b.id
1618
+ @conv.push(b)
1619
+ b = Conversation.new
1620
+ end
1621
+ b = Conversation.new
1622
+ b.index = r[2]
1623
+ b.id = r[3]
1624
+ b.is_star = r[4]
1625
+ @conv_starred = (b.is_star == 1)
1626
+ b.sender = decode(r[6])
1627
+ b.sender_email = r[8].gsub("\"",'') # remove annoying d-quotes in address
1628
+ b.recv = r[9]
1629
+ b.recv_email = r[11].to_s.gsub("\"",'')
1630
+ b.reply_email = r[14].to_s.gsub("\"",'')
1631
+ b.dt_easy = r[10]
1632
+ b.dt = r[15]
1633
+ b.subject = decode(r[16])
1634
+ b.snippet = decode(r[17])
1635
+ b.attachment = []
1636
+ r[18].each do |bb|
1637
+ at = Attachment.new
1638
+ at.id = bb[0]
1639
+ at.filename = bb[1]
1640
+ at.type = bb[2]
1641
+ at.size = bb[3]
1642
+ b.attachment.push(at)
1643
+ end
1644
+ b.is_draft = false
1645
+ b.body = ''
1646
+ elsif (r[0] == "di")
1647
+ if b.id
1648
+ @conv.push(b)
1649
+ b = Conversation.new
1650
+ end
1651
+ b = Conversation.new
1652
+ b.index = r[2]
1653
+ b.id = r[3]
1654
+ b.is_star = r[4]
1655
+ @conv_starred = (b.is_star == 1)
1656
+ b.sender = decode(r[6])
1657
+ b.sender_email = r[8].gsub("\"",'') # remove annoying d-quotes in address
1658
+ b.recv = r[9]
1659
+ b.recv_email = r[11].gsub("\"",'')
1660
+ b.reply_email = r[14].gsub("\"",'')
1661
+ b.cc_email = r[12].gsub("\"",'')
1662
+ b.bcc_email = r[13].gsub("\"",'')
1663
+ b.dt_easy = r[10]
1664
+ b.dt = r[15]
1665
+ b.subject = decode(r[16])
1666
+ b.snippet = decode(r[17])
1667
+ b.attachment = []
1668
+ r[18].each do |bb|
1669
+ at = Attachment.new
1670
+ at.id = bb[0]
1671
+ at.filename = bb[1]
1672
+ at.type = bb[2]
1673
+ at.size = bb[3]
1674
+ b.attachment.push(at)
1675
+ end
1676
+ b.is_draft = true
1677
+ b.draft_parent = r[5]
1678
+ b.body = decode(r[20])
1679
+ end
1680
+ end
1681
+ @conv.push(b) unless !b.id.nil?
1682
+ @message = Message.new(@conv_tile,@conv_total,@conv_id,@conv_labels,@conv_starred)
1683
+ @message.conv = @conv
1684
+ end
1685
+ elsif type == GM_CONTACT
1686
+ @contacts = []
1687
+ @contact_groups = []
1688
+ @contacts_total = 0
1689
+
1690
+ if raw["cls"]
1691
+ @contacts_toal = (raw["cls"][1]).to_i
1692
+ @contacts_shown = (raw["cls"][3]).to_i
1693
+ end
1694
+
1695
+ type = ''
1696
+ c_grp_det = ''
1697
+ if raw["a"]
1698
+ c_array = "a"
1699
+ # determine is this is a list or contact detail
1700
+ if ((raw["a"]).length == 2 && raw["a"][1][6])
1701
+ type = "detail"
1702
+ c_id = 0
1703
+ c_name = 1
1704
+ c_email = 3
1705
+ c_groups = 4
1706
+ c_notes = 5
1707
+ c_detail = 6
1708
+ else
1709
+ c_email = 3
1710
+ c_notes = 4
1711
+ type = "list"
1712
+ end
1713
+ elsif raw["cl"] # list
1714
+ c_array = "cl"
1715
+ c_email = 4
1716
+ c_notes = 5
1717
+ c_addresses = 6
1718
+ type = "list"
1719
+ elsif raw["cov"] # contact detail in accounts using "cl"
1720
+ c_array = "cov"
1721
+ type = "detail"
1722
+ c_id = 1
1723
+ c_name = 2
1724
+ c_email = 4
1725
+ c_groups = 6
1726
+ c_notes = 7
1727
+ c_detail = 8
1728
+ elsif raw["clv"] # group detail in accounts using "cl"
1729
+ c_array = "clv"
1730
+ type = "detail"
1731
+ c_id = 1
1732
+ c_name = 2
1733
+ c_email = 6
1734
+ c_total = 3
1735
+ c_detail = 5
1736
+ c_members = 4
1737
+ c_notes = 0
1738
+ else
1739
+ c = Contact.new
1740
+ c.id = 'error'
1741
+ c.name = 'gmailer Error'
1742
+ @contacts.push(c)
1743
+ end
1744
+
1745
+ if type == "list"
1746
+ # An ordinary list of contacts
1747
+ for i in (1 ... raw[c_array].length)
1748
+ a = raw[c_array][i]
1749
+ b = Contact.new
1750
+ b.id = a[1] # contact id
1751
+ b.name = a[2] ? a[2] : ''
1752
+ b.email = a[c_email]
1753
+
1754
+ if a[c_notes]
1755
+ if (a[c_notes]).is_a?(Array)
1756
+ b.notes = ''
1757
+ b.is_group = true
1758
+ # email addresses for groups are in a different location and format
1759
+ # "Name" <email@address.net>, "Name2" <email2@address.net>, etc
1760
+ # and needs to be "simply" re-created for backwards compatibility
1761
+ gr_count = a[c_notes].length
1762
+ group_addresses = []
1763
+ a[c_notes].each {|gr_entry|
1764
+ group_addresses.push(gr_entry[1])
1765
+ }
1766
+ b.email = group_addresses.join(", ")
1767
+
1768
+ b.group_names = a[c_email]
1769
+ b.group_total = a[3]
1770
+ b.group_email = (a[c_notes]).length > 0 ? a[c_addresses] : []
1771
+ else
1772
+ b.notes = a[c_notes]
1773
+ b.is_group = false
1774
+ b.groups = a[c_addresses]
1775
+ end
1776
+ end
1777
+ @contacts.push(b)
1778
+ end
1779
+ elsif type == "detail"
1780
+ details = {}
1781
+ if c_array == "clv"
1782
+ # Group details
1783
+ cov = Contact.new
1784
+ cov.is_group = true # is this a group?
1785
+ cov.id = raw[c_array][1][c_id] # group id
1786
+ cov.name = raw[c_array][1][c_name] # group name
1787
+ gr_count = (raw[c_array][1][c_detail]).length
1788
+ cov.group_names = raw[c_array][1][c_members] # string of names of group members
1789
+ cov.group_total = raw[c_array][1][c_total] # string, total number of members in group
1790
+ cov.group_email = raw[c_array][1][c_email] ? raw[c_array][1][c_email] : '' # formatted list of addresses as: Name <address>
1791
+ cov.notes = '' # no notes for groups... yet!
1792
+ group_addresses = [] # string of flattened email addresses
1793
+ cov.members = [] # array of group members
1794
+ (raw[c_array][1][c_detail]).each do |gr_entry|
1795
+ group_addresses.push(gr_entry[1])
1796
+ m = Member.new
1797
+ m.id = gr_entry[0]
1798
+ m.name = (gr_entry[2] ? gr_entry[2] : '')
1799
+ m.email = gr_entry[1]
1800
+ cov.members.push(m)
1801
+ end
1802
+ cov.email = (group_addresses.length > 0) ? group_addresses.join(", ") : ''
1803
+
1804
+ else
1805
+ # Contact details (advanced contact information)
1806
+ # used when a contact id was supplied for retrieval
1807
+ cov = Contact.new
1808
+ cov.is_group = false
1809
+ cov.id = raw[c_array][1][c_id]
1810
+ cov.name = raw[c_array][1][c_name]
1811
+ cov.email = raw[c_array][1][c_email]
1812
+ cov.groups = raw[c_array][1][c_groups]
1813
+ if raw[c_array][1][c_notes][0]
1814
+ cov.notes = (raw[c_array][1][c_notes][0] == "n") ? raw[c_array][1][c_notes][1] : ''
1815
+ else
1816
+ cov.notes = ''
1817
+ end
1818
+ num_details = (raw[c_array][1][c_detail]).length
1819
+ if num_details > 0
1820
+ raw[c_array][1][c_detail].each do |i|
1821
+ details[:detail_name] = i[0]
1822
+ temp = i[1] ? i[1] : []
1823
+ 0.step(temp.length-1,2) do |j|
1824
+ case temp[j]
1825
+ when "p"
1826
+ field = :phone
1827
+ when "e"
1828
+ field = :email
1829
+ when "m"
1830
+ field = :mobile
1831
+ when "f"
1832
+ field = :fax
1833
+ when "b"
1834
+ field = :pager
1835
+ when "i"
1836
+ field = :im
1837
+ when "d"
1838
+ field = :company
1839
+ when "t"
1840
+ field = :position
1841
+ when "o"
1842
+ field = :other
1843
+ when "a"
1844
+ field = :address
1845
+ end
1846
+
1847
+ details[field] = temp[j+1]
1848
+ end
1849
+
1850
+ end
1851
+
1852
+ end
1853
+ cov.details = details
1854
+ end
1855
+
1856
+ @contacts.push(cov)
1857
+ end
1858
+
1859
+ # Contact groups
1860
+ if raw["cla"]
1861
+ for i in (1...(raw["cla"][1]).length)
1862
+ a = raw["cla"][1][i]
1863
+ b = {
1864
+ :id => a[0],
1865
+ :name => a[1],
1866
+ :addresses => a[2] ? a[2] : ''
1867
+ }
1868
+ @contacts_groups.push(b)
1869
+ end
1870
+ end
1871
+ @view = GM_CONTACT
1872
+ elsif type == GM_PREFERENCE
1873
+ prefs = {}
1874
+ raw["p"].each_with_index { |v,i|
1875
+ prefs[v[0]] = v[1] if i>0
1876
+ }
1877
+ @preference = Preference.new
1878
+ prefs.each do |k,v|
1879
+ case k
1880
+ when 'ix_nt'
1881
+ @preference.max_page_size = v
1882
+ when 'bx_hs'
1883
+ @preference.keyboard_shortcuts = (v == '1')
1884
+ when 'bx_sc'
1885
+ @preference.indicators = (v == '1')
1886
+ when 'sx_dl'
1887
+ @preference.display_language = v
1888
+ when 'sx_sg'
1889
+ @preference.signature = (v == '0') ? '' : v
1890
+ when 'sx_rt'
1891
+ @preference.reply_to = v
1892
+ when 'bx_ns'
1893
+ @preference.snippets = (v == '0')
1894
+ when 'sx_dn'
1895
+ @preference.display_name = v
1896
+ end
1897
+ end
1898
+
1899
+ @label_list = []
1900
+ @label_total = []
1901
+ raw["cta"][1].each { |v|
1902
+ @label_list.push(v[0])
1903
+ @label_total.push(v[1])
1904
+ }
1905
+ @other_emails = []
1906
+ if raw["cfs"]
1907
+ raw["cfs"][1].each {|v|
1908
+ @other_emails.push({"name"=>decode(v[0]),"email"=>v[1],"default"=>(v[2]==1)})
1909
+ }
1910
+ end
1911
+ @filter = []
1912
+ raw["fi"][1].each do |fi|
1913
+ f = Filter.new
1914
+ f.id = fi[0]
1915
+ f.query = fi[1]
1916
+ f.star = fi[3]
1917
+ f.label = fi[4]
1918
+ f.archive = fi[5]
1919
+ f.trash = fi[6]
1920
+ f.from = fi[2][0]
1921
+ f.to = fi[2][1]
1922
+ f.subject = fi[2][2]
1923
+ f.has = fi[2][3]
1924
+ f.hasnot = fi[2][4]
1925
+ f.attach = fi[2][5]
1926
+
1927
+ @filter.push(b)
1928
+ end
1929
+ @view = GM_PREFERENCE
1930
+ else
1931
+ @created = 0
1932
+ return nil
1933
+ end
1934
+ @message_list.box = @box if @message_list
1935
+
1936
+ @created = 1
1937
+ end
1938
+
1939
+ end
1940
+
1941
+ # a single filter
1942
+ class Filter
1943
+ attr_accessor :id,:query,:star,:label,:archive,:trash
1944
+ attr_accessor :from,:to,:subject,:has,:hasnot,:attach
1945
+ end
1946
+
1947
+ # a single box
1948
+ class Box
1949
+ attr_accessor :id, :is_read, :is_starred, :date, :msgid
1950
+ attr_accessor :sender, :flag, :subject, :snippet, :labels, :attachment
1951
+
1952
+ def read?
1953
+ is_read == 1
1954
+ end
1955
+
1956
+ def new?
1957
+ is_read != 1
1958
+ end
1959
+
1960
+ def star?
1961
+ is_starred == 1
1962
+ end
1963
+
1964
+ end
1965
+
1966
+ # a list of messages.
1967
+ class MessageList
1968
+ attr_reader :name, :total, :pos
1969
+ attr_accessor :box, :connection
1970
+ def initialize(name,total,pos)
1971
+ @name = name
1972
+ @total = total
1973
+ @pos = pos
1974
+ end
1975
+
1976
+ def each(&block)
1977
+ @box.each do |b|
1978
+ block.call(b)
1979
+ end
1980
+ end
1981
+
1982
+ def each_msg(&block)
1983
+ @box.each do |b|
1984
+ m = connection.fetch(:conversation=>b.id).message
1985
+ m.connection = @connection
1986
+ block.call(m)
1987
+ end
1988
+ end
1989
+
1990
+ end
1991
+
1992
+ # a Preference
1993
+ class Preference
1994
+ attr_accessor :max_page_size, :keyboard_shortcuts, :indicators, :display_language
1995
+ attr_accessor :signature, :reply_to, :snippets, :display_name
1996
+
1997
+ def initialize
1998
+ @max_page_size = '10'
1999
+ @keyboard_shortcuts = false
2000
+ @indicators = false
2001
+ @display_language = 'en'
2002
+ @signature = ''
2003
+ @reply_to = ''
2004
+ @snippets = false
2005
+ @display_name = ''
2006
+ end
2007
+ end
2008
+
2009
+ # Contact
2010
+ class Contact
2011
+ attr_accessor :id,:name,:email,:is_group,:notes,:groups,:group_names,:group_email,:group_total,:members,:details
2012
+ def initialize
2013
+ @id = ''
2014
+ @name = ''
2015
+ @email = ''
2016
+ @notes = ''
2017
+ @groups = ''
2018
+ @group_names = ''
2019
+ @group_email = ''
2020
+ @group_total = ''
2021
+ @members = ''
2022
+ @is_group = false
2023
+ @details = {}
2024
+ end
2025
+
2026
+ def group?
2027
+ @is_group
2028
+ end
2029
+ end
2030
+
2031
+ # a silgle Member
2032
+ class Member
2033
+ attr_accessor :id,:name,:email
2034
+ def initialize
2035
+ @id = ''
2036
+ @name = ''
2037
+ @email = ''
2038
+ end
2039
+ end
2040
+
2041
+ # a single attachment
2042
+ class Attachment
2043
+ attr_accessor :id, :filename, :type, :size
2044
+ end
2045
+
2046
+ # a single conversation
2047
+ class Conversation
2048
+ attr_accessor :id, :index, :body, :draft_parent, :is_draft, :attachment
2049
+ attr_accessor :snippet, :subject, :dt, :dt_easy, :bcc_email, :cc_email
2050
+ attr_accessor :reply_email, :recv_email, :recv, :sender_email, :sender, :is_star
2051
+ def read?
2052
+ is_read == 1
2053
+ end
2054
+
2055
+ def new?
2056
+ is_read != 1
2057
+ end
2058
+
2059
+ def star?
2060
+ is_starred == 1
2061
+ end
2062
+
2063
+ end
2064
+
2065
+ # a single message
2066
+ class Message
2067
+ attr_reader :title, :total, :id, :labels, :starred
2068
+ attr_accessor :conv, :connection
2069
+ def initialize(title,total,id,labels,starred)
2070
+ @title = title
2071
+ @total = total
2072
+ @id = id
2073
+ @labels = labels
2074
+ @starred = starred
2075
+ end
2076
+
2077
+ def method_missing(methId)
2078
+ str = methId.id2name
2079
+ @conv[0].method(str).call
2080
+ end
2081
+
2082
+ #
2083
+ # return boolean
2084
+ # param string label
2085
+ # desc apply label to message
2086
+ #
2087
+ def apply_label(label)
2088
+ @connection.perform_action(GM_ACT_APPLYLABEL, @id, label)
2089
+ end
2090
+
2091
+ #
2092
+ # return boolean
2093
+ # param string label
2094
+ # desc remove label from message
2095
+ #
2096
+ def remove_label(label)
2097
+ @connection.perform_action(GM_ACT_REMOVELABEL, @id, label)
2098
+ end
2099
+
2100
+ #
2101
+ # return boolean
2102
+ # desc apply star to a message
2103
+ #
2104
+ def apply_star()
2105
+ @connection.perform_action(GM_ACT_STAR,@id, '')
2106
+ end
2107
+
2108
+ #
2109
+ # return boolean
2110
+ # param string msgid
2111
+ # desc remove star from a message
2112
+ #
2113
+ def remove_star()
2114
+ @connection.perform_action(GM_ACT_UNSTAR,@id, '')
2115
+ end
2116
+
2117
+ #
2118
+ # return boolean
2119
+ # desc archive a message
2120
+ #
2121
+ def archive()
2122
+ @connection.perform_action(GM_ACT_ARCHIVE,@id, '')
2123
+ end
2124
+
2125
+ #
2126
+ # return boolean
2127
+ # param string msgid
2128
+ # desc archive a message
2129
+ #
2130
+ def unarchive()
2131
+ @connection.perform_action(GM_ACT_INBOX,@id, '')
2132
+ end
2133
+
2134
+ #
2135
+ # return boolean
2136
+ # param string msgid
2137
+ # desc mark a message as read
2138
+ #
2139
+ def mark_read()
2140
+ @connection.perform_action(GM_ACT_READ,@id, '')
2141
+ end
2142
+
2143
+ #
2144
+ # return boolean
2145
+ # param string msgid
2146
+ # desc mark a message as unread
2147
+ #
2148
+ def mark_unread()
2149
+ @connection.perform_action(GM_ACT_UNREAD,@id, '')
2150
+ end
2151
+
2152
+ def read?()
2153
+ @conv[0].read?
2154
+ end
2155
+
2156
+ def new?()
2157
+ @conv[0].new?
2158
+ end
2159
+
2160
+ #
2161
+ # return boolean
2162
+ # param string msgid
2163
+ # desc report a message as spam
2164
+ #
2165
+ def report_spam()
2166
+ @connection.perform_action(GM_ACT_SPAM,@id, '')
2167
+ end
2168
+
2169
+ #
2170
+ # return boolean
2171
+ # param string msgid
2172
+ # desc report a message as not spam
2173
+ #
2174
+ def report_not_spam()
2175
+ @connection.perform_action(GM_ACT_UNSPAM,@id, '')
2176
+ end
2177
+
2178
+ #
2179
+ # return boolean
2180
+ # desc move a message to trash
2181
+ #
2182
+ def trash()
2183
+ @connection.perform_action(GM_ACT_TRASH,@id, '')
2184
+ end
2185
+
2186
+ #
2187
+ # return boolean
2188
+ # desc move a message from trash to inbox
2189
+ #
2190
+ def untrash()
2191
+ @connection.perform_action(GM_ACT_UNTRASH,@id, '')
2192
+ end
2193
+
2194
+ #
2195
+ # return boolean
2196
+ # desc delete a message forever
2197
+ #
2198
+ def delete()
2199
+ if connected?
2200
+ @connection.perform_action(GM_ACT_DELFOREVER,@id, '')
2201
+ else
2202
+ false
2203
+ end
2204
+ end
2205
+
2206
+ def original()
2207
+ @connection.fetch(:show_original=>@id)
2208
+ end
2209
+ end
2210
+
2211
+ # Singleton method
2212
+ # return bool
2213
+ # desc Connect to GMail with default session management settings.
2214
+ #
2215
+
2216
+ def GMailer.connect(*param)
2217
+ g = Connection.new(*param)
2218
+ @connection = g
2219
+ if block_given?
2220
+ yield(g)
2221
+ g.disconnect()
2222
+ else
2223
+ g
1789
2224
  end
2225
+ end
1790
2226
 
1791
2227
  end
1792
2228