gmailer 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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