graybook 1.0.22

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 (78) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGES.md +49 -0
  3. data/Manifest.txt +59 -0
  4. data/README.md +66 -0
  5. data/Rakefile +40 -0
  6. data/VERSION +1 -0
  7. data/VERSION.yml +4 -0
  8. data/VERSION_NAME +1 -0
  9. data/debug_graybook.rb +10 -0
  10. data/init.rb +1 -0
  11. data/lib/graybook/exporter/base.rb +16 -0
  12. data/lib/graybook/exporter/vcf.rb +45 -0
  13. data/lib/graybook/exporter/xml.rb +28 -0
  14. data/lib/graybook/importer/aol.rb +87 -0
  15. data/lib/graybook/importer/base.rb +39 -0
  16. data/lib/graybook/importer/csv.rb +74 -0
  17. data/lib/graybook/importer/freenet.rb +62 -0
  18. data/lib/graybook/importer/gmail.rb +84 -0
  19. data/lib/graybook/importer/gmx.rb +68 -0
  20. data/lib/graybook/importer/hotmail.rb +135 -0
  21. data/lib/graybook/importer/page_scraper.rb +86 -0
  22. data/lib/graybook/importer/web.de.rb +67 -0
  23. data/lib/graybook/importer/yahoo.rb +63 -0
  24. data/lib/graybook.rb +83 -0
  25. data/test/config/credentials.yml.example +9 -0
  26. data/test/fixtures/aol_application_page.html +566 -0
  27. data/test/fixtures/aol_bad_login_response_stage_3.html +565 -0
  28. data/test/fixtures/aol_contacts.html +102 -0
  29. data/test/fixtures/aol_login_response_stage_1.html +158 -0
  30. data/test/fixtures/aol_login_response_stage_2.html +559 -0
  31. data/test/fixtures/aol_login_response_stage_3.html +48 -0
  32. data/test/fixtures/aol_login_response_stage_4.html +404 -0
  33. data/test/fixtures/aol_login_response_stage_5.html +404 -0
  34. data/test/fixtures/aol_new_contacts.html +431 -0
  35. data/test/fixtures/gmail.csv +3 -0
  36. data/test/fixtures/gmail_bad_login_response_stage_2.html +560 -0
  37. data/test/fixtures/gmail_contacts.html +228 -0
  38. data/test/fixtures/gmail_login_response_stage_1.html +556 -0
  39. data/test/fixtures/gmail_login_response_stage_2.html +1 -0
  40. data/test/fixtures/gmail_login_response_stage_2a.html +1 -0
  41. data/test/fixtures/gmail_login_response_stage_3.html +249 -0
  42. data/test/fixtures/gmail_redirect_body.html +10 -0
  43. data/test/fixtures/hotmail_bad_login_response_stage_2.html +31 -0
  44. data/test/fixtures/hotmail_contacts.html +262 -0
  45. data/test/fixtures/hotmail_login_response_stage_1.html +31 -0
  46. data/test/fixtures/hotmail_login_response_stage_2.html +1 -0
  47. data/test/fixtures/hotmail_login_response_stage_3.html +519 -0
  48. data/test/fixtures/hotmail_scrape_first_page.html +77 -0
  49. data/test/fixtures/hotmail_scrape_response_stage_1.html +90 -0
  50. data/test/fixtures/hotmail_scrape_response_stage_2.html +77 -0
  51. data/test/fixtures/hotmail_scrape_response_stage_3.html +0 -0
  52. data/test/fixtures/yahoo_bad_login_response_stage_2.html +443 -0
  53. data/test/fixtures/yahoo_contacts.csv +3 -0
  54. data/test/fixtures/yahoo_contacts_not_logged_in.html +432 -0
  55. data/test/fixtures/yahoo_contacts_stage_1.html +399 -0
  56. data/test/fixtures/yahoo_login_response_stage_1.html +433 -0
  57. data/test/fixtures/yahoo_login_response_stage_2.html +16 -0
  58. data/test/fixtures/yahoo_no_user_response_stage_2.html +574 -0
  59. data/test/freenet_importer_test.rb +53 -0
  60. data/test/gmx_importer_test.rb +53 -0
  61. data/test/scripts/live_test.rb +25 -0
  62. data/test/test_graybook.rb +60 -0
  63. data/test/test_graybook_exporter_base.rb +16 -0
  64. data/test/test_graybook_exporter_vcf.rb +52 -0
  65. data/test/test_graybook_exporter_xml.rb +16 -0
  66. data/test/test_graybook_importer_aol.rb +108 -0
  67. data/test/test_graybook_importer_base.rb +24 -0
  68. data/test/test_graybook_importer_csv.rb +60 -0
  69. data/test/test_graybook_importer_gmail.rb +116 -0
  70. data/test/test_graybook_importer_hotmail.rb +165 -0
  71. data/test/test_graybook_importer_page_scraper.rb +51 -0
  72. data/test/test_graybook_importer_yahoo.rb +137 -0
  73. data/test/test_helper.rb +71 -0
  74. data/test/web.de_importer_test.rb +53 -0
  75. data/updater.rb +15 -0
  76. data/vendor/plugins/graybook/lib/autotest/discover.rb +3 -0
  77. data/vendor/plugins/graybook/lib/autotest/graybook.rb +27 -0
  78. metadata +185 -0
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/CHANGES.md ADDED
@@ -0,0 +1,49 @@
1
+ == 1.0.20 / 2009-09-30
2
+
3
+ * lots of changes undocumented here.
4
+ * putting on gemcutter.
5
+
6
+ == 1.0.5 / 2008-11-26
7
+
8
+ * moved to github
9
+ * added patch for gmail. emails were coming in with notes.
10
+ * added german stuff to graybook gem
11
+
12
+ == 1.0.4 / 2008-02-27
13
+
14
+ * 1 minor enhancement
15
+ * fix 1.0.3 dud with AOL issues - Thank you Howard Wong!
16
+
17
+ == 1.0.3 / 2008-02-27
18
+
19
+ * 1 minor enhancement
20
+ * address the change in where AOL contacts are listed
21
+
22
+ == 1.0.2 / 2008-02-20
23
+
24
+ * 1 minor enhancement
25
+ * fixed sign on issue with GMail
26
+
27
+ == 1.0.1 / 2008-02-08
28
+
29
+ * 2 minor enhancement
30
+ * fix edge case sign on issue with GMail
31
+
32
+ == 1.0.0 / 2008-01-30
33
+
34
+ * 2 major enhancements
35
+ * Birthday!
36
+ * Mechanize patch for AOL sign-on! Thanks Mortee!
37
+
38
+ * Importers
39
+ * AOL
40
+ * GMail
41
+ * Hotmail
42
+ * Yahoo!
43
+ * CSV
44
+
45
+ * Exporters
46
+ * Hash
47
+ * XML
48
+ * VCard
49
+
data/Manifest.txt ADDED
@@ -0,0 +1,59 @@
1
+ CHANGES.markdown
2
+ Manifest.txt
3
+ README.markdown
4
+ Rakefile
5
+ debug_graybook.rb
6
+ init.rb
7
+ lib/graybook.rb
8
+ lib/graybook/exporter/base.rb
9
+ lib/graybook/exporter/vcf.rb
10
+ lib/graybook/exporter/xml.rb
11
+ lib/graybook/importer/aol.rb
12
+ lib/graybook/importer/base.rb
13
+ lib/graybook/importer/csv.rb
14
+ lib/graybook/importer/gmail.rb
15
+ lib/graybook/importer/hotmail.rb
16
+ lib/graybook/importer/page_scraper.rb
17
+ lib/graybook/importer/yahoo.rb
18
+ test/fixtures/aol_application_page.html
19
+ test/fixtures/aol_bad_login_response_stage_3.html
20
+ test/fixtures/aol_contacts.html
21
+ test/fixtures/aol_login_response_stage_1.html
22
+ test/fixtures/aol_login_response_stage_2.html
23
+ test/fixtures/aol_login_response_stage_3.html
24
+ test/fixtures/aol_login_response_stage_4.html
25
+ test/fixtures/aol_login_response_stage_5.html
26
+ test/fixtures/aol_new_contacts.html
27
+ test/fixtures/gmail.csv
28
+ test/fixtures/gmail_bad_login_response_stage_2.html
29
+ test/fixtures/gmail_contacts.html
30
+ test/fixtures/gmail_login_response_stage_1.html
31
+ test/fixtures/gmail_login_response_stage_2.html
32
+ test/fixtures/gmail_login_response_stage_2a.html
33
+ test/fixtures/gmail_login_response_stage_3.html
34
+ test/fixtures/hotmail_bad_login_response_stage_2.html
35
+ test/fixtures/hotmail_contacts.html
36
+ test/fixtures/hotmail_login_response_stage_1.html
37
+ test/fixtures/hotmail_login_response_stage_2.html
38
+ test/fixtures/hotmail_login_response_stage_3.html
39
+ test/fixtures/yahoo_bad_login_response_stage_2.html
40
+ test/fixtures/yahoo_contacts.csv
41
+ test/fixtures/yahoo_contacts_not_logged_in.html
42
+ test/fixtures/yahoo_contacts_stage_1.html
43
+ test/fixtures/yahoo_login_response_stage_1.html
44
+ test/fixtures/yahoo_login_response_stage_2.html
45
+ test/scripts/live_test.rb
46
+ test/test_graybook.rb
47
+ test/test_graybook_exporter_base.rb
48
+ test/test_graybook_exporter_vcf.rb
49
+ test/test_graybook_exporter_xml.rb
50
+ test/test_graybook_importer_aol.rb
51
+ test/test_graybook_importer_base.rb
52
+ test/test_graybook_importer_csv.rb
53
+ test/test_graybook_importer_gmail.rb
54
+ test/test_graybook_importer_hotmail.rb
55
+ test/test_graybook_importer_page_scraper.rb
56
+ test/test_graybook_importer_yahoo.rb
57
+ test/test_helper.rb
58
+ vendor/plugins/graybook/lib/autotest/graybook.rb
59
+ vendor/plugins/graybook/lib/autotest/discover.rb
data/README.md ADDED
@@ -0,0 +1,66 @@
1
+ Graybook
2
+
3
+ == Description:
4
+
5
+ Pulls contact lists from EVERYWHERE! (Some sites not included.)
6
+
7
+ == Features:
8
+
9
+ The current list of supported services and file types:
10
+
11
+ import:
12
+ - AOL
13
+ - CSV files
14
+ - Gmail
15
+ - Hotmail
16
+ - Yahoo! Mail
17
+
18
+ export:
19
+ - Simple hash (default)
20
+ - JSON (soon)
21
+
22
+ If you create an additional importer or exporter - or simply find a bug - please consider submitting it as a patch to the project so the community can all benefit from your hard work and ingenuity.
23
+
24
+ == Synopsis:
25
+
26
+ # An example of fetching Gmail contacts - by default, returns an array of hashes with :name and :email
27
+ contacts = Graybook.get :username => 'me@gmail.com', :password => 'whatever'
28
+
29
+ == Requirements:
30
+
31
+ * Mechanize and its dependencies, for interacting with online providers
32
+ * Fastercsv for reading CSV, Mechanize >= 0.7.0 for page scraping
33
+
34
+ == Install:
35
+
36
+ 1. gem install gemcutter
37
+ 1. gem tumble
38
+ 2. gem install graybook
39
+
40
+ == Thanks:
41
+
42
+ Big thanks to Contentfree for making Blackbook.
43
+
44
+ == License:
45
+
46
+ Copyright (c) 2009, kojul.com
47
+ Copyright (c) 2007, Contentfree
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $LOAD_PATH.unshift 'lib'
6
+ require 'graybook'
7
+
8
+ begin
9
+ require 'rcov/rcovtask'
10
+ rescue LoadError
11
+ end
12
+
13
+ begin
14
+ Rcov::RcovTask.new do |t|
15
+ t.test_files = FileList['test/test*.rb']
16
+ t.verbose = true
17
+ #t.rcov_opts << "--exclude rcov.rb,hpricot.rb,hpricot/.*\.rb"
18
+ end
19
+ rescue NameError
20
+ end
21
+
22
+ require 'rubygems'
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |s|
27
+ s.name = "graybook"
28
+ s.summary = "It does things. Like import contacts from EVERYWHERE."
29
+ s.email = "kojul@kojul.com"
30
+ s.homepage = "http://github.com/kojul/gray"
31
+ s.description = "It does things. Like import contacts from EVERYWHERE."
32
+ s.authors = ["kojul"]
33
+ s.add_dependency('hpricot', '>= 0.7.0')
34
+ s.add_dependency('mechanize', '>= 0.7.0')
35
+ s.add_dependency('hoe', '>= 1.5.0')
36
+ s.add_dependency('fastercsv', '>= 1.2.0')
37
+ end
38
+ rescue LoadError
39
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
40
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.22
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 22
data/VERSION_NAME ADDED
@@ -0,0 +1 @@
1
+ Gray Grenade
data/debug_graybook.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'graybook'
2
+
3
+ aol = Graybook.get :username => 'mondragonmichael@aol.com', :password => '123aol0'
4
+ puts "===== aol #{aol.inspect}"
5
+
6
+ unless aol.detect{|c| c[:name].downcase == 'Dave Myron'.downcase && c[:email] == 'dave.myron@contentfree.com'}
7
+ puts "AOL not working"
8
+ exit 1
9
+ end
10
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/lib/graybook'
@@ -0,0 +1,16 @@
1
+ ##
2
+ # base class for exporters of contact information
3
+
4
+ module Graybook::Exporter
5
+
6
+ class Base
7
+ ##
8
+ # Override this to convert +contacts+ (an array of hashes) to something more useful. Here, it
9
+ # just returns Graybook's internal format
10
+ def export( contacts )
11
+ contacts
12
+ end
13
+
14
+ Graybook.register :basic, self
15
+ end
16
+ end
@@ -0,0 +1,45 @@
1
+ ##
2
+ # exports contacts in Vcard format
3
+ class Graybook::Exporter::Vcf < Graybook::Exporter::Base
4
+
5
+ ##
6
+ # representation of a vcard
7
+
8
+ class Vcard
9
+
10
+ attr_accessor :first, :last, :email
11
+
12
+ ##
13
+ # initialize dynamically sets the attributes passed in as accessible
14
+ # attribute on its object
15
+
16
+ def initialize( attributes = {} )
17
+ attributes.each{ |name,value| self.send("#{name}=", value) rescue next }
18
+ end
19
+
20
+ ##
21
+ # text representation of this vcard
22
+ def to_s
23
+ <<-EOVC
24
+ BEGIN:VCARD
25
+ N:#{last};#{first}
26
+ EMAIL:#{email}
27
+ END:VCARD
28
+ EOVC
29
+ end
30
+ end
31
+
32
+ ##
33
+ # exports contacts as Vcards
34
+
35
+ def export( contacts = [] )
36
+ return if contacts.empty?
37
+
38
+ contacts.uniq.compact.collect do |contact|
39
+ first_name, last_name = contact[:name].split(' ', 2)
40
+ Vcard.new( :first => first_name.to_s, :last => last_name.to_s, :email => contact[:email])
41
+ end
42
+ end
43
+
44
+ Graybook.register(:vcf, self)
45
+ end
@@ -0,0 +1,28 @@
1
+ require 'rexml/document'
2
+
3
+ ##
4
+ # exports contacts in xml format
5
+
6
+ class Graybook::Exporter::Xml < Graybook::Exporter::Base
7
+
8
+ ##
9
+ # contacts are an array of hashes that are contacts and returns xml
10
+
11
+ def export( contacts )
12
+ doc = REXML::Document.new
13
+ doc << REXML::XMLDecl.new
14
+
15
+ root = doc.add_element 'contacts'
16
+ contacts.each do |contact|
17
+ el = root.add_element 'contact'
18
+ name = el.add_element 'name'
19
+ name.text = contact[:name]
20
+
21
+ el.add_element('email').text = contact[:email]
22
+ end
23
+
24
+ doc.to_s
25
+ end
26
+
27
+ Graybook.register(:xml, self)
28
+ end
@@ -0,0 +1,87 @@
1
+ require 'graybook/importer/page_scraper'
2
+
3
+ ##
4
+ # Imports contacts from AOL
5
+
6
+ class Graybook::Importer::Aol < Graybook::Importer::PageScraper
7
+
8
+ ##
9
+ # Matches this importer to an user's name/address
10
+
11
+ def =~( options )
12
+ options && options[:username] =~ /@(aol|aim)\.com$/i ? true : false
13
+ end
14
+
15
+ ##
16
+ # Login process:
17
+ # - Get mail.aol.com which redirects to a page containing a javascript redirect
18
+ # - Get the URL that the javascript is supposed to redirect you to
19
+ # - Fill out and submit the login form
20
+ # - Get the URL from *another* javascript redirect
21
+
22
+ def login
23
+ page = agent.get( 'http://webmail.aol.com/' )
24
+
25
+ form = page.forms.find{|form| form.name == 'AOLLoginForm'}
26
+ form.loginId = options[:username].split('@').first # Drop the domain
27
+ form.password = options[:password]
28
+ page = agent.submit(form, form.buttons.first)
29
+
30
+ case page.body
31
+ when /Invalid Screen Name or Password. Please try again./
32
+ raise( Graybook::BadCredentialsError, "That username and password was not accepted. Please check them and try again." )
33
+ when /Terms of Service/
34
+ raise( Graybook::LegacyAccount, "Your AOL account is not setup for WebMail. Please signup: http://webmail.aol.com")
35
+ end
36
+
37
+ # aol bumps to a wait page while logging in. if we can't scrape out the js then its a bad login
38
+ extractor = proc { |var_name| page.body.scan(/var\s*#{var_name}\s*=\s*\"(.*?)\"\s*;/).first.first }
39
+
40
+ base_uri = extractor.call( 'gSuccessPath' )
41
+ raise( Graybook::BadCredentialsError, "You do not appear to be signed in." ) unless base_uri
42
+ page = agent.get base_uri
43
+ end
44
+
45
+ ##
46
+ # must login to prepare
47
+
48
+ def prepare
49
+ login
50
+ end
51
+
52
+ ##
53
+ # The url to scrape contacts from has to be put together from the Auth cookie
54
+ # and a known uri that hosts their contact service. An array of hashes with
55
+ # :name and :email keys is returned.
56
+
57
+ def scrape_contacts
58
+ unless auth_cookie = agent.cookies.find{|c| c.name =~ /^Auth/}
59
+ raise( Graybook::BadCredentialsError, "Must be authenticated to access contacts." )
60
+ end
61
+
62
+ # jump through the hoops of formulating a request to get printable contacts
63
+ uri = agent.current_page.uri.dup
64
+ inputs = agent.current_page.search("//input")
65
+ user = inputs.detect{|i| i['type'] == 'hidden' && i['name'] == 'user'}
66
+ utoken = user['value']
67
+
68
+ path = uri.path.split('/')
69
+ path.pop
70
+ path << 'addresslist-print.aspx'
71
+ uri.path = path.join('/')
72
+ uri.query = "command=all&sort=FirstLastNick&sortDir=Ascending&nameFormat=FirstLastNick&user=#{utoken}"
73
+ page = agent.get uri.to_s
74
+
75
+ # Grab all the contacts
76
+ names = page.body.scan( /<span class="fullName">([^<]+)<\/span>/ ).flatten
77
+ emails = page.body.scan( /<span>Email 1:<\/span> <span>([^<]+)<\/span>/ ).flatten
78
+ (0...[names.size,emails.size].max).collect do |i|
79
+ {
80
+ :name => names[i],
81
+ :email => emails[i]
82
+ }
83
+ end
84
+ end
85
+
86
+ Graybook.register :aol, self
87
+ end
@@ -0,0 +1,39 @@
1
+ #
2
+ # Provides a base template for interface and behavior of contact importers
3
+
4
+ module Graybook::Importer
5
+ class Base
6
+ attr_accessor :options
7
+
8
+ ##
9
+ # Should return true or false/nil depending on whether the +options+ given
10
+ # can be handled by this importer
11
+
12
+ def =~( options ); end # stub
13
+
14
+ ##
15
+ # Does the work of extracting contacts. Returns an Array of Arrays
16
+ # containing the name and email as the first and second elements. Of
17
+ # course, you can override this behavior to meet the needs of a
18
+ # particular service.
19
+
20
+ def fetch_contacts!; end # stub
21
+
22
+ ##
23
+ # Imports the contacts using the given +options+. Returns an array of
24
+ # hashes in the internal format (a hash with at least :name and :email
25
+ # values).
26
+
27
+ def import(options = {})
28
+ self.options = options
29
+ fetch_contacts!
30
+ end
31
+
32
+ ##
33
+ # Name of the importer service.
34
+
35
+ def service_name
36
+ self.class.name.split("::").last
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ require 'fastercsv'
2
+
3
+ ##
4
+ # Imports contacts from a CSV file
5
+
6
+ class Graybook::Importer::Csv < Graybook::Importer::Base
7
+
8
+ DEFAULT_COLUMNS = [:name,:email,:misc]
9
+ DEFAULT_PATTERN = /,/
10
+
11
+ ##
12
+ # Matches this importer to a file that contains CSV values
13
+
14
+ def =~(options)
15
+ options && options[:file].respond_to?(:open) ? true : false
16
+ end
17
+
18
+ ##
19
+ # fetch_contacts! implementation for this importer
20
+
21
+ def fetch_contacts!
22
+ lines = IO.readlines(options[:file].path)
23
+ columns = to_columns(lines.first)
24
+ lines.shift if columns.first == :name
25
+ columns = DEFAULT_COLUMNS.dup unless columns.first == :name
26
+
27
+ contacts = Array.new
28
+ lines.each do |l|
29
+ vals = l.split(pattern)
30
+ next if vals.empty?
31
+ vals.map! {|x| x.gsub( /\A"/m, "" ).strip.gsub( /"\Z/m, "" ) }
32
+ contacts << to_hash(columns, vals)
33
+ end
34
+
35
+ contacts
36
+ end
37
+
38
+ def to_hash(cols, vals) # :nodoc:
39
+ h = Hash.new
40
+ cols.each do |c|
41
+ h[c] = (c == cols.last) ? vals.join(',') : vals.shift
42
+ end
43
+ h
44
+ end
45
+
46
+ def to_columns(line) # :nodoc:
47
+ columns = Array.new
48
+ if line.match(pattern)
49
+ tags = line.split(pattern)
50
+ else
51
+ tags = line.split(DEFAULT_PATTERN)
52
+ end
53
+ # deal with "Name,E-mail..." oddity up front
54
+ if tags.first =~ /^name$/i
55
+ tags.shift
56
+ columns << :name
57
+ if tags.first =~ /^e.?mail/i # E-mail or Email
58
+ tags.shift
59
+ columns << :email
60
+ end
61
+ end
62
+ tags.each do |v|
63
+ v = v.to_s.gsub(/\0/,"")
64
+ columns << v.strip.to_sym unless v.blank?
65
+ end
66
+ columns
67
+ end
68
+
69
+ def pattern
70
+ @pattern ||= (options[:pattern] || DEFAULT_PATTERN) rescue DEFAULT_PATTERN
71
+ end
72
+
73
+ Graybook.register(:csv, self)
74
+ end
@@ -0,0 +1,62 @@
1
+ require 'graybook/importer/page_scraper'
2
+
3
+ class Graybook::Importer::Freenet < Graybook::Importer::PageScraper
4
+ LOGIN_URL = "https://office.freenet.de"
5
+ MESSAGES_URL = "/main_overview.html"
6
+
7
+ def =~( options )
8
+ options && options[:username] =~ /@freenet\.de$/i
9
+ end
10
+
11
+ def login
12
+ username, password = options[:username], options[:password]
13
+
14
+ begin
15
+ page = agent.get LOGIN_URL
16
+
17
+ form = page.forms.with.name("loginform").first
18
+ form.username = username.split("@").first
19
+ form.password = password
20
+ page = form.submit
21
+
22
+ if page.body.match(/Fehlgeschlagene Login-Versuche/)
23
+ page = page.forms.with.name("sicherform").submit
24
+ page = agent.get MESSAGES_URL
25
+ end
26
+
27
+ if page.body.match(/Login (erneut )?fehlgeschlagen/) || page.body.match(/Ich bin bereits Mitglied/)
28
+ raise Graybook::BadCredentialsError.new
29
+ end
30
+ rescue => e
31
+ raise e || Graybook::GraybookError.new
32
+ end
33
+ end
34
+
35
+ def prepare
36
+ login
37
+ end
38
+
39
+ def scrape_contacts
40
+ contacts = ["INBOX", "INBOX.sent"].map do |folder|
41
+ page = agent.get "/messages/mail_mailbox.html?mail_folder=#{folder}"
42
+ find_contacts(page)
43
+ end
44
+
45
+ contacts.inject([]) do |memo, contact|
46
+ memo << contact unless memo.include? contact
47
+ memo
48
+ end.flatten
49
+ end
50
+
51
+
52
+ protected
53
+ def find_contacts(page)
54
+ page.search("tr[@id]/td[9]").map do |cell|
55
+ recp, fullname, email = *cell.attributes["title"].match(/"(.*)"<(.*)>/)
56
+
57
+ { :name => fullname, :email => email }
58
+ end
59
+ end
60
+
61
+ Graybook.register :freenet, self
62
+ end
@@ -0,0 +1,84 @@
1
+ require 'graybook/importer/page_scraper'
2
+
3
+ ##
4
+ # Imports contacts from GMail
5
+
6
+ class Graybook::Importer::Gmail < Graybook::Importer::PageScraper
7
+
8
+ RETRY_THRESHOLD = 5
9
+ ##
10
+ # Matches this importer to an user's name/address
11
+
12
+ def =~(options = {})
13
+ options && options[:username] =~ /@(gmail|googlemail).com$/i ? true : false
14
+ end
15
+
16
+ def import(*args)
17
+ # GMail depends on Hpricot for some reason...
18
+ parser = WWW::Mechanize.html_parser
19
+ WWW::Mechanize.html_parser = Hpricot
20
+ return super do
21
+ WWW::Mechanize.html_parser = parser
22
+ end
23
+ end
24
+
25
+ ##
26
+ # login to gmail
27
+
28
+ def login
29
+ page = agent.get('http://mail.google.com/mail/')
30
+ form = page.forms.first
31
+ form.Email = options[:username]
32
+ form.Passwd = options[:password]
33
+ page = agent.submit(form,form.buttons.first)
34
+
35
+ raise( Graybook::BadCredentialsError, "That username and password was not accepted. Please check them and try again." ) if page.body =~ /Username and password do not match/
36
+
37
+ if page.search('//meta').first.attributes['content'] =~ /url='?(http.+?)'?$/i
38
+ page = agent.get $1
39
+ end
40
+ end
41
+
42
+ ##
43
+ # prepare this importer
44
+
45
+ def prepare
46
+ login
47
+ end
48
+
49
+ ##
50
+ # scrape gmail contacts for this importer
51
+
52
+ def scrape_contacts
53
+ unless agent.cookies.find { |c|
54
+ c.name == 'GAUSR' && c.value.match(/mail(.*?):#{options[:username]}/)
55
+ }
56
+ raise( Graybook::BadCredentialsError, "Must be authenticated to access contacts." )
57
+ end
58
+
59
+ page = agent.get('http://mail.google.com/mail/h/?v=cl&pnl=a')
60
+ title = page.search("//title").inner_text
61
+ if title == 'Redirecting'
62
+ redirect_text = page.search('//meta').first.attributes['content'].inner_text
63
+ url = redirect_text.match(/url='(http.*)'/i)[1]
64
+ page = agent.get(url)
65
+ end
66
+
67
+ contact_rows = page.search("//input[@name='c']/../..")
68
+ contact_rows.collect do |row|
69
+ columns = row/"td"
70
+ email = columns[2].inner_html.gsub( /(\n|&nbsp;)/, '' ) # email
71
+ clean_email = email[/[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4}/]
72
+
73
+ unless clean_email.empty?
74
+ columns = row/"td"
75
+ {
76
+ :name => ( columns[1] / "b" / "a" ).inner_html.strip, # name
77
+ :email => clean_email
78
+ }
79
+ end
80
+ end.compact
81
+ end
82
+
83
+ Graybook.register(:gmail, self)
84
+ end