nosy 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # Nosy
2
+
3
+ **Output, backup, and read all your iPhone text messages.**
4
+
5
+ Nosy extracts text messages from the native iOS (iPhone, iPad) database stored on your computer after each backup and organizes it into an array of easily explorable messages.
6
+
7
+ Nosy returns an array of messages with the fields
8
+
9
+ * receiver
10
+ * sender
11
+ * message
12
+ * imessage (Boolean)
13
+ * date (contains a unix timestamp)
14
+
15
+
16
+ ## Install
17
+
18
+ $ gem install nosy
19
+
20
+ Or add Nosy to your Gemfile and run `bundle install`.
21
+
22
+ ## Find and Parse
23
+ If you are on a Mac OSX system, Nosy (is nosy, and) will try to grab your iOS message database and parse it.
24
+
25
+ all_texts = Nosy.hunt_and_parse
26
+
27
+ all_texts.each do |text|
28
+ text.receiver
29
+ text.sender
30
+ text.message
31
+ text.imessage
32
+ text.date
33
+ end
34
+
35
+
36
+ ## Location
37
+ The location of the folder storing the iOS message backups in Mac OSX is
38
+
39
+ $ ~/Library/Application Support/MobileSync/Backup/
40
+
41
+ Each of the folders will represent an Apple device. Within your desired device's folder, the iOS message database will be the file entitled `3d0d7e5fb2ce288813306e4d4636395e047a3d28`
42
+
43
+
44
+ ### Hunt
45
+
46
+ If you are on Mac OSX running Nosy.hunt will return a String with the location of the first parseable iOS message database if there is one.
47
+
48
+ require 'nosy'
49
+
50
+ new_text_backup_file_location = Nosy.hunt
51
+
52
+
53
+ ## Parsing
54
+
55
+ If you pass in an iOS message database, Nosy can detect if a file can be parsed as an iPhone database.
56
+
57
+ valid = Nosy.can_parse?(iphone_database_location)
58
+
59
+ Also, Nosy can parse the file and return an array of all available text messages.
60
+
61
+ all_texts = Nosy.parse(iphone_database_location)
62
+
63
+ all_texts.each do |text|
64
+ text.receiver
65
+ text.sender
66
+ text.message
67
+ text.imessage
68
+ text.date
69
+ end
70
+
71
+ ## Searching
72
+
73
+ Nosy can search through the texts in your iOS message database. Nosy searching supports any combination of the following keys:
74
+
75
+ * receiver
76
+ * sender
77
+ * message
78
+ * imessage
79
+ * date - supports a Unix timestamp and an optional leading operator [ < , > , <= , >= ]
80
+
81
+ #
82
+
83
+ nosty_texts = Nosy.new(iphone_database_location)
84
+
85
+ # Array of texts where I am the sender
86
+ my_sent_texts = nosty_texts.search(:sender => "me")
87
+
88
+ # Array of texts where the date is less than 1273175931
89
+ before_then_texts = nosty_texts.search(:date => "<1273175931")
90
+
91
+ # Array of texts where I am the sender and the date is less than 1273175931
92
+ before_then_texts = nosty_texts.search(:sender => "me", :date => "<1273175931")
93
+
94
+ ## Contributions
95
+ Contributions, improvements, and suggestions more than welcome! Please!
96
+
97
+ ### Some Ideas
98
+
99
+ Currently, Nosy is yet to...
100
+
101
+ * Properly handle MMS (group and media) messages.
102
+ * Let you search for two values in one key.
103
+ * Let you hunt for the iOS message database on systems other than Mac OSX.
104
+ * Support creating a backup file.
105
+ * Run on Heroku due to its dependency on Sqlite3.
106
+
107
+ ## Acknowledgment
108
+
109
+ * For a similar implementation in Python check out [iPhone SMS Backup](https://github.com/toffer/iphone-sms-backup/)
110
+ * Inspiration from Gabe Berke-Williams's [Chat Stew](https://github.com/gabebw/chat_stew)
111
+ * [Mike Coutermarsh](https://github.com/mscoutermarsh)
112
+ * [Sarah Canieso](https://github.com/scanieso)
113
+ * [Eric Kelly](https://github.com/HeroicEric) - Providing additional SMS Databases
data/lib/.DS_Store ADDED
Binary file
data/lib/nosy/hunter.rb CHANGED
@@ -1,16 +1,16 @@
1
- require 'fileutils'
2
-
3
1
  module Nosy
4
2
 
5
- class Hunter
6
-
7
- def hunt
8
- if RUBY_PLATFORM =~ /darwin/
9
- FileUtils.cp "#{Dir.home}/Library/Application Support/MobileSync/Backup/55e67384ae82dd8a317f29585e1d7b5884e43107/3d0d7e5fb2ce288813306e4d4636395e047a3d28", "#{FileUtils.pwd}/texts.sqlite"
10
- "#{FileUtils.pwd}/texts.sqlite"
11
- end
12
- end
13
-
14
- end
3
+ class Hunter
15
4
 
5
+ def hunt
6
+ if RUBY_PLATFORM =~ /darwin/
7
+ Dir[File.join("#{Dir.home}/Library/Application Support/MobileSync/Backup", "*")].select{|file| File.ftype(file) == "directory"}.each do |folder|
8
+ if Nosy.can_parse?(folder+"/3d0d7e5fb2ce288813306e4d4636395e047a3d28")
9
+ return "#{folder}/3d0d7e5fb2ce288813306e4d4636395e047a3d28"
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
16
  end
@@ -0,0 +1,24 @@
1
+ module Nosy
2
+
3
+ class Parser
4
+
5
+ module Imessage
6
+
7
+ def sent_imessage( parsed_message, message )
8
+ parsed_message.receiver = format_address(message[7])
9
+ parsed_message.sender = "me"
10
+ end
11
+
12
+ def received_imessage( parsed_message, message )
13
+ parsed_message.receiver = "me"
14
+ parsed_message.sender = format_address(message[7])
15
+ end
16
+
17
+ def sent_group_imessage( parsed_message, message )
18
+ parsed_message.sender = "me"
19
+ parsed_message.receiver = "group"
20
+ end
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,50 @@
1
+ module Nosy
2
+
3
+ class Parser
4
+
5
+ module MessageSupport
6
+
7
+ def find_all_messages( file )
8
+ SQLite3::Database.new( file ).execute( "select rowid, date, address, text, flags, is_madrid, madrid_flags, madrid_handle from message" )
9
+ end
10
+
11
+ def parse_imessage( parsed_message, message )
12
+ parsed_message.imessage = true
13
+ parsed_message.date = imessage_time_to_unixtime(message[1])
14
+
15
+ if message[6] == 36869
16
+ sent_imessage( parsed_message, message )
17
+ elsif message[6] == 12289
18
+ received_imessage( parsed_message, message )
19
+ elsif message[6] == 32773
20
+ sent_group_imessage( parsed_message, message )
21
+ end
22
+ end
23
+
24
+ def parse_sms_message( parsed_message, message )
25
+ parsed_message.imessage = false
26
+ parsed_message.date = message[1]
27
+
28
+ if message[4] == 3 || message[4] == 35 || message[4] == 33 || message[4] == 16387
29
+ sent_sms_message( parsed_message, message )
30
+ elsif message[4] == 2
31
+ received_sms_message( parsed_message, message )
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def imessage_time_to_unixtime( imessage_time )
38
+ imessage_time + 978307200
39
+ end
40
+
41
+ def format_address(address)
42
+ if !address.nil?
43
+ address = address.gsub(/\s+|[()-]/, "")
44
+ address =~ /^[0-9]+$/ ? "+"+address : address
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,28 @@
1
+ module Nosy
2
+
3
+ class Parser
4
+
5
+ module ParseChecks
6
+
7
+ def has_messages_table( database )
8
+ begin
9
+ database.execute2( "select rowid, date, address, text, flags, is_madrid, madrid_flags, madrid_handle from message" )
10
+ rescue SQLite3::SQLException
11
+ return false
12
+ rescue SQLite3::NotADatabaseException
13
+ return false
14
+ end
15
+ end
16
+
17
+ def is_iphone_database( results )
18
+ if ( results[0][0] == "ROWID" && results[0][1] == "date" && results[0][2] == "address" && results[0][3] == "text" && results[0][4] == "flags" && results[0][5] == "is_madrid" && results[0][6] == "madrid_flags" && results[0][7] == "madrid_handle") && results.count > 1
19
+ return true
20
+ else
21
+ return false
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,20 @@
1
+ module Nosy
2
+
3
+ class Parser
4
+
5
+ module Smsmessage
6
+
7
+ def sent_sms_message( parsed_message, message )
8
+ parsed_message.receiver = format_address(message[2])
9
+ parsed_message.sender = "me"
10
+ end
11
+
12
+ def received_sms_message( parsed_message, message )
13
+ parsed_message.receiver = "me"
14
+ parsed_message.sender = format_address(message[2])
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ end
data/lib/nosy/parser.rb CHANGED
@@ -1,110 +1,37 @@
1
1
  require 'sqlite3'
2
+ require 'nosy/parser/parse_checks'
3
+ require 'nosy/parser/message_support'
4
+ require 'nosy/parser/imessage'
5
+ require 'nosy/parser/smsmessage'
2
6
 
3
7
  Message = Struct.new(:receiver, :sender, :message, :imessage, :date)
4
8
 
5
9
  module Nosy
6
10
 
7
- class Parser
8
-
9
- def can_parse?(file)
10
- begin
11
- db = SQLite3::Database.new( file )
12
- begin
13
- results = db.execute2( "select * from message LIMIT 1" )
14
- results.map do |header|
15
- if ( header[0] == "ROWID" && header[1] == "address" && header[2] == "date" && header[3] == "text" && header[4] == "flags" && header[16] == "read")
16
- return true
17
- else
18
- return false
19
- end
20
- end
21
- rescue SQLite3::SQLException
22
- return false
23
- end
24
- rescue SQLite3::NotADatabaseException
25
- return false
26
- end
27
-
28
- end
29
-
30
- def parse(file)
31
- db = SQLite3::Database.new( file )
32
- all_messages = db.execute( "select * from message" )
33
-
34
- all_messages.map do |message|
35
-
36
- # http://www.slideshare.net/hrgeeks/iphone-forensics-without-the-iphone
37
- # http://www.scip.ch/?labs.20111103
38
-
39
- parsed_message = Message.new("", "", message[3], "", "")
40
-
41
- ## If it is an iMessage
42
- if message[29] == 1
43
- parsed_message.imessage = true
44
- parsed_message.date = message[2] + 978307200
45
-
46
- # Sent an iMessage
47
- if message[25] == 36869
48
- parsed_message.receiver = format_address(message[18])
49
- parsed_message.sender = "me"
50
-
51
- # Recieved an iMessage
52
- elsif message[25] == 12289
53
- parsed_message.receiver = "me"
54
- parsed_message.sender = format_address(message[18])
55
-
56
- # Sent a group iMessage
57
- elsif message[25] == 32773
58
- parsed_message.sender = "me"
59
- parsed_message.receiver = "group"
60
- end
61
-
62
- ## It is an SMS
63
- else
64
-
65
- parsed_message.imessage = false
66
- parsed_message.date = message[2]
67
-
68
- # Sent SMS
69
- if message[4] == 3
70
- parsed_message.receiver = format_address(message[1])
71
- parsed_message.sender = "me"
72
-
73
- # Recieved an SMS
74
- elsif message[4] == 2
75
- parsed_message.receiver = "me"
76
- parsed_message.sender = format_address(message[1])
77
-
78
- # Sent SMS on retry
79
- elsif message[4] == 35
80
- parsed_message.receiver = format_address(message[1])
81
- parsed_message.sender = "me"
82
-
83
- # SMS Failed
84
- elsif message[4] == 33
85
- parsed_message.receiver = format_address(message[1])
86
- parsed_message.sender = "me"
87
-
88
- # iMessage Failed and sent as SMS
89
- elsif message[4] == 16387
90
- parsed_message.receiver = format_address(message[1])
91
- parsed_message.sender = "me"
92
- end
93
-
94
- end
95
-
96
- parsed_message
97
- end
98
- end
99
-
100
- private
101
- def format_address(address)
102
- if !address.nil?
103
- address = address.gsub(/\s+|[()-]/, "")
104
- address =~ /^[0-9]+$/ ? "+"+address : address
105
- end
106
- end
107
-
108
- end
11
+ class Parser
12
+
13
+ include MessageSupport
14
+ include ParseChecks
15
+ include Imessage
16
+ include Smsmessage
17
+
18
+ def can_parse?(file)
19
+ db = SQLite3::Database.new( file )
20
+ results = has_messages_table(db)
21
+ db != false && results != false ? is_iphone_database( results ) : false
22
+ end
23
+
24
+ def parse(file)
25
+ find_all_messages( file ).map do |message|
26
+ parsed_message = Message.new("", "", message[3], "", "")
27
+ if message[5] == 1
28
+ parse_imessage( parsed_message, message)
29
+ else
30
+ parse_sms_message( parsed_message, message)
31
+ end
32
+ parsed_message
33
+ end
34
+ end
35
+ end
109
36
 
110
37
  end
data/lib/nosy/searcher.rb CHANGED
@@ -1,35 +1,35 @@
1
1
  module Nosy
2
2
 
3
- class Searcher
4
- attr_accessor :texts
3
+ class Searcher
4
+ attr_accessor :texts
5
5
 
6
- def initialize( file )
7
- @texts = Parser.new.parse( file )
8
- end
6
+ def initialize( file )
7
+ @texts = Parser.new.parse( file )
8
+ end
9
9
 
10
- def search(filters={})
10
+ def search(filters={})
11
11
 
12
- @texts.select do |text|
13
- filters.all?{ |key, value| key == :date ? filter_dates(key, value, text) : text[key] == value }
14
- end
15
- end
12
+ @texts.select do |text|
13
+ filters.all?{ |key, value| key == :date ? filter_dates(key, value, text) : text[key] == value }
14
+ end
15
+ end
16
16
 
17
- private
17
+ private
18
18
 
19
- def filter_dates( key, value, text )
20
- if value[1] == "="
21
- value[0] == ">" ? Time.at(text[key]) >= Time.at(value[2..-1].to_i) : Time.at(text[key]) <= Time.at(value[2..-1].to_i)
22
- else
23
- if value[0] == ">"
24
- Time.at(text[key]) > Time.at(value[1..-1].to_i)
25
- elsif value[0] == "<"
26
- Time.at(text[key]) < Time.at(value[1..-1].to_i)
27
- else
28
- Time.at(text[key]) == Time.at(value.to_i)
29
- end
30
- end
31
- end
32
-
33
- end
19
+ def filter_dates( key, value, text )
20
+ if value[1] == "="
21
+ value[0] == ">" ? Time.at(text[key]) >= Time.at(value[2..-1].to_i) : Time.at(text[key]) <= Time.at(value[2..-1].to_i)
22
+ else
23
+ if value[0] == ">"
24
+ Time.at(text[key]) > Time.at(value[1..-1].to_i)
25
+ elsif value[0] == "<"
26
+ Time.at(text[key]) < Time.at(value[1..-1].to_i)
27
+ else
28
+ Time.at(text[key]) == Time.at(value.to_i)
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
34
 
35
35
  end
data/lib/nosy.rb CHANGED
@@ -4,19 +4,23 @@ require "nosy/searcher"
4
4
 
5
5
  module Nosy
6
6
 
7
- def self.hunt
8
- Hunter.new.hunt
9
- end
7
+ def self.hunt
8
+ Hunter.new.hunt
9
+ end
10
10
 
11
- def self.new(file)
12
- Searcher.new(file)
13
- end
11
+ def self.hunt_and_parse
12
+ Parser.new.parse(Hunter.new.hunt)
13
+ end
14
14
 
15
- def self.can_parse?(file)
16
- Parser.new.can_parse?(file)
17
- end
15
+ def self.new(file)
16
+ Searcher.new(file)
17
+ end
18
18
 
19
- def self.parse(file)
20
- Parser.new.parse(file)
21
- end
19
+ def self.can_parse?(file)
20
+ Parser.new.can_parse?(file)
21
+ end
22
+
23
+ def self.parse(file)
24
+ Parser.new.parse(file)
25
+ end
22
26
  end
data/nosy.gemspec ADDED
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'nosy'
3
+ gem.version = '0.0.4'
4
+ gem.date = '2012-01-02'
5
+ gem.summary = "Output, backup, and read all your iPhone text messages"
6
+ gem.description = "Nosy fetches, parses and searches the iPhone's SMS database that is created on your machine each time you make a backup."
7
+ gem.authors = ["Philip Dudley"]
8
+ gem.homepage = "https://github.com/pdud/Nosy"
9
+ gem.email = 'pdudley89@gmail.com'
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.add_dependency("sqlite3", "~> 1.3.4")
12
+ gem.add_development_dependency("rspec", "~> 2.6.0")
13
+ end
data/spec/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,13 @@
1
+ require 'nosy'
2
+
3
+ describe Nosy::Hunter, "#hunt" do
4
+
5
+ context "Mac OSX" do
6
+
7
+ it "returns the location of a valid iphone database" do
8
+ location = subject.hunt
9
+ Nosy::Parser.new.can_parse?(location).should == true
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,207 @@
1
+ require 'nosy'
2
+
3
+ describe Nosy::Parser, "#can_parse?" do
4
+
5
+ let(:acceptable_iphone_database) { File.join('spec', 'support', 'db', 'texts.sqlite') }
6
+ let(:invalid_file_type_iphone_database) { File.join('spec', 'support', 'db', 'invalid_file_type_texts.txt') }
7
+ let(:empty_iphone_database) { File.join('spec', 'support', 'db', 'empty_iphone_database.sqlite') }
8
+ let(:missing_table_iphone_database) { File.join('spec', 'support', 'db', 'missing_table_texts.sqlite') }
9
+ let(:incorrect_format_iphone_database) { File.join('spec', 'support', 'db', 'incorrect_format_texts.sqlite') }
10
+ let(:missing_iphone_database) { File.join('spec', 'support', 'db', 'this_file_does_not_exist.sqlite') }
11
+
12
+ it "returns true for an iPhone Database" do
13
+ subject.can_parse?(acceptable_iphone_database).should == true
14
+ end
15
+
16
+ it "returns false for a file that is not a database" do
17
+ subject.can_parse?(invalid_file_type_iphone_database).should == false
18
+ end
19
+
20
+ it "returns false for a file that is an empty database" do
21
+ subject.can_parse?(empty_iphone_database).should == false
22
+ end
23
+
24
+ it "returns false for a file that is missing the messages table" do
25
+ subject.can_parse?(missing_table_iphone_database).should == false
26
+ end
27
+
28
+ it "returns false for a file that is missing the necessary fields in the messages table" do
29
+ subject.can_parse?(incorrect_format_iphone_database).should == false
30
+ end
31
+
32
+ it "returns false for a file that is missing" do
33
+ subject.can_parse?(missing_iphone_database).should == false
34
+ end
35
+
36
+ end
37
+
38
+ describe Nosy::Parser, "#parse(file)" do
39
+
40
+ let(:iphone_database) { File.join('spec', 'support', 'db', 'texts.sqlite') }
41
+ let(:parsed) { subject.parse(iphone_database) }
42
+
43
+ it "should parse each texts as a Messages" do
44
+ parsed[0].should be_a_kind_of Message
45
+ end
46
+
47
+ it "should parse the collection of texts as an Array" do
48
+ parsed.should be_a_kind_of Array
49
+ end
50
+
51
+
52
+ it "correctly parses the text" do
53
+ parsed[0].message.should == "Hey it's Phil. I'm moving some stuff to next years apartment at the mo. But you left stuff here so you should come round later to grab it."
54
+ parsed[3].message.should == "Hi :)"
55
+ end
56
+
57
+ context "SMS Messages" do
58
+
59
+ it "correctly detects the message as an iMessage" do
60
+ parsed[0].imessage.should == false
61
+ parsed[1].imessage.should == false
62
+ parsed[2].imessage.should == false
63
+ end
64
+
65
+ it "correctly parses the date" do
66
+ parsed[0].date.should == 1273175931
67
+ end
68
+
69
+ describe "Sent from me" do
70
+
71
+ it "correctly parses the sender" do
72
+ parsed[0].sender.should == "me"
73
+ end
74
+
75
+ it "correctly parses the receiver" do
76
+ parsed[0].receiver.should == "+1234567890"
77
+ end
78
+
79
+ end
80
+
81
+ describe "Sent to me" do
82
+
83
+ it "correctly parses the sender" do
84
+ parsed[1].sender.should == "+1234567890"
85
+ end
86
+
87
+ it "correctly parses the receiver" do
88
+ parsed[1].receiver.should == "me"
89
+ end
90
+
91
+ end
92
+
93
+ describe "Sent from me, Failed, then Succeded" do
94
+
95
+ it "correctly parses the sender" do
96
+ parsed[2].sender.should == "me"
97
+ end
98
+
99
+ it "correctly parses the receiver" do
100
+ parsed[2].receiver.should == "+1234567890"
101
+ end
102
+
103
+ end
104
+
105
+ describe "Sent from me and Failed" do
106
+
107
+ it "correctly parses the sender" do
108
+ parsed[7].sender.should == "me"
109
+ end
110
+
111
+ it "correctly parses the receiver" do
112
+ parsed[7].receiver.should == "+1234567890"
113
+ end
114
+
115
+ end
116
+
117
+ describe "iMessage Failed and sent as SMS" do
118
+
119
+ it "correctly parses the sender" do
120
+ parsed[5].sender.should == "me"
121
+ end
122
+
123
+ it "correctly parses the receiver" do
124
+ parsed[5].receiver.should == "+1234567890"
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ context "iMessages" do
132
+
133
+ it "correctly detects the message as an iMessage" do
134
+ parsed[3].imessage.should == true
135
+ parsed[4].imessage.should == true
136
+ end
137
+
138
+ it "correctly parses the date" do
139
+ parsed[3].date.should == 1318524672
140
+ parsed[4].date.should == 1318524796
141
+ end
142
+
143
+ describe "Sent to me" do
144
+
145
+ it "correctly parses the sender" do
146
+ parsed[3].sender.should == "user@gmail.com"
147
+ end
148
+
149
+ it "correctly parses the receiver" do
150
+ parsed[3].receiver.should == "me"
151
+ end
152
+
153
+ end
154
+
155
+ describe "Sent from me" do
156
+
157
+ it "correctly parses the sender" do
158
+ parsed[4].sender.should == "me"
159
+ end
160
+
161
+ it "correctly parses the receiver" do
162
+ parsed[4].receiver.should == "user@gmail.com"
163
+ end
164
+
165
+ end
166
+
167
+ describe "Group message sent from me" do
168
+
169
+ it "correctly parses the sender" do
170
+ parsed[6].sender.should == "me"
171
+ end
172
+
173
+ it "correctly parses the receiver" do
174
+ parsed[6].receiver.should == "group"
175
+ end
176
+
177
+ end
178
+
179
+
180
+ end
181
+
182
+ describe Nosy::Parser, "#format_address" do
183
+
184
+ let(:parser) { subject }
185
+
186
+ it "does not format nil addresses" do
187
+ parser.send(:format_address, nil).should == nil
188
+ end
189
+
190
+ it "removes white space" do
191
+ parser.send(:format_address, "my address").should == "myaddress"
192
+ end
193
+
194
+ it "removes parenthesis" do
195
+ parser.send(:format_address, "my(address)").should == "myaddress"
196
+ end
197
+
198
+ it "removes seperators" do
199
+ parser.send(:format_address, "my-address").should == "myaddress"
200
+ end
201
+
202
+ it "adds a + if it consists of all numbers" do
203
+ parser.send(:format_address, "1 (234) 567-8901").should == "+12345678901"
204
+ end
205
+
206
+ end
207
+ end
@@ -0,0 +1,103 @@
1
+ require 'nosy'
2
+
3
+ describe Nosy::Searcher, "#new" do
4
+
5
+ let(:iphone_database) { File.join('spec', 'support', 'db', 'texts.sqlite') }
6
+ let(:texts) { Nosy::Searcher.new( iphone_database ) }
7
+
8
+ it "should create a Nosy::Searcher class" do
9
+ texts.should be_a_kind_of Nosy::Searcher
10
+ end
11
+
12
+ it "should fill texts" do
13
+ texts.texts.should_not be nil
14
+ end
15
+
16
+ it "should fill texts with Messages" do
17
+ texts.texts[0].should be_a_kind_of Message
18
+ end
19
+
20
+ end
21
+
22
+ describe Nosy::Searcher, "#search" do
23
+
24
+ let(:iphone_database) { File.join('spec', 'support', 'db', 'texts.sqlite') }
25
+ let(:texts) { Nosy::Searcher.new( iphone_database ) }
26
+
27
+ it "should return all texts if no filters are passed in" do
28
+ texts.search().count.should == 8
29
+ texts.search().should == Nosy::Parser.new.parse( iphone_database )
30
+ end
31
+
32
+ it "should be searchable by sender" do
33
+ search = texts.search(:sender => "me").each do |text|
34
+ text.sender.should == "me"
35
+ end
36
+ search.count.should_not == 0
37
+ end
38
+
39
+ it "should be searchable by receiver" do
40
+ search = texts.search(:receiver => "me").each do |text|
41
+ text.receiver.should == "me"
42
+ end
43
+ search.count.should_not == 0
44
+ end
45
+
46
+ it "should be searchable by type of imessage" do
47
+ search = texts.search(:imessage => true).each do |text|
48
+ text.imessage.should == true
49
+ end
50
+ search.count.should_not == 0
51
+ end
52
+
53
+ it "should be searchable by text of message (full text)" do
54
+ search = texts.search(:message => "Here :)").each do |text|
55
+ text.message.should == "Here :)"
56
+ end
57
+ search.count.should_not == 0
58
+ end
59
+
60
+ it "should be searchable by date of message" do
61
+ search = texts.search(:date => "1320880443").each do |text|
62
+ text.date.should == 1320880443
63
+ end
64
+ search.count.should_not == 0
65
+ end
66
+
67
+ it "should be searchable by more than one field" do
68
+ search = texts.search(:sender => "me", :message => "Here :)").each do |text|
69
+ text.sender.should == "me"
70
+ text.message.should == "Here :)"
71
+ end
72
+ search.count.should_not == 0
73
+ end
74
+
75
+ it "should be searchable after a given date" do
76
+ search = texts.search(:date => ">1318524672").each do |text|
77
+ Time.at(text.date).should > Time.at(1318524672)
78
+ end
79
+ search.count.should_not == 0
80
+ end
81
+
82
+ it "should be searchable before a given date" do
83
+ search = texts.search(:date => "<1320880443").each do |text|
84
+ Time.at(text.date).should < Time.at(1320880443)
85
+ end
86
+ search.count.should_not == 0
87
+ end
88
+
89
+ it "should be searchable after and equal to a given date" do
90
+ search = texts.search(:date => ">=1318524672").each do |text|
91
+ Time.at(text.date).should >= Time.at(1318524672)
92
+ end
93
+ search.count.should_not == 0
94
+ end
95
+
96
+ it "should be searchable before and equal to a given date" do
97
+ search = texts.search(:date => "<=1320880443").each do |text|
98
+ Time.at(text.date).should <= Time.at(1320880443)
99
+ end
100
+ search.count.should_not == 0
101
+ end
102
+
103
+ end
data/spec/nosy_spec.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'nosy'
2
+
3
+ describe Nosy do
4
+
5
+ let(:iphone_database) { File.join('spec', 'support', 'db', 'texts.sqlite') }
6
+ let(:texts) { subject.new(iphone_database) }
7
+
8
+ describe "#hunt" do
9
+
10
+ it "should return the same results as a hunt" do
11
+ Nosy.hunt.should == Nosy::Hunter.new.hunt
12
+ end
13
+
14
+ end
15
+
16
+ describe "#new" do
17
+
18
+ it "should return the same results as a search" do
19
+ Nosy.new(iphone_database).texts.should == Nosy::Searcher.new(iphone_database).texts
20
+ end
21
+
22
+ end
23
+
24
+ describe "#hunt_and_parse" do
25
+
26
+ it "should get the database and parse it" do
27
+ Nosy.hunt_and_parse.should == Nosy::Parser.new.parse(Nosy::Hunter.new.hunt)
28
+ end
29
+
30
+ end
31
+
32
+ describe "#parse" do
33
+
34
+ it "should return the same results as a parse" do
35
+ Nosy.parse(iphone_database).should == Nosy::Parser.new.parse(iphone_database)
36
+ end
37
+
38
+ end
39
+
40
+ describe "#can_parse?" do
41
+
42
+ it "should return the same results as a parse" do
43
+ Nosy.can_parse?(iphone_database).should == Nosy::Parser.new.can_parse?(iphone_database)
44
+ end
45
+
46
+ end
47
+
48
+ end
Binary file
Binary file
@@ -0,0 +1 @@
1
+ We can't have this working!
File without changes
File without changes
Binary file
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nosy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-24 00:00:00.000000000Z
12
+ date: 2012-01-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sqlite3
16
- requirement: &70300181810560 !ruby/object:Gem::Requirement
16
+ requirement: &70203490284220 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,21 +21,10 @@ dependencies:
21
21
  version: 1.3.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70300181810560
25
- - !ruby/object:Gem::Dependency
26
- name: fileutils
27
- requirement: &70300181810080 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ~>
31
- - !ruby/object:Gem::Version
32
- version: '0.7'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: *70300181810080
24
+ version_requirements: *70203490284220
36
25
  - !ruby/object:Gem::Dependency
37
26
  name: rspec
38
- requirement: &70300181809620 !ruby/object:Gem::Requirement
27
+ requirement: &70203490283740 !ruby/object:Gem::Requirement
39
28
  none: false
40
29
  requirements:
41
30
  - - ~>
@@ -43,7 +32,7 @@ dependencies:
43
32
  version: 2.6.0
44
33
  type: :development
45
34
  prerelease: false
46
- version_requirements: *70300181809620
35
+ version_requirements: *70203490283740
47
36
  description: Nosy fetches, parses and searches the iPhone's SMS database that is created
48
37
  on your machine each time you make a backup.
49
38
  email: pdudley89@gmail.com
@@ -51,11 +40,36 @@ executables: []
51
40
  extensions: []
52
41
  extra_rdoc_files: []
53
42
  files:
43
+ - .gitignore
44
+ - .rspec
45
+ - README.md
46
+ - lib/.DS_Store
54
47
  - lib/nosy.rb
55
48
  - lib/nosy/hunter.rb
56
49
  - lib/nosy/parser.rb
50
+ - lib/nosy/parser/imessage.rb
51
+ - lib/nosy/parser/message_support.rb
52
+ - lib/nosy/parser/parse_checks.rb
53
+ - lib/nosy/parser/smsmessage.rb
57
54
  - lib/nosy/searcher.rb
58
- homepage: http://phildudley.com
55
+ - nosy-0.0.4.gem
56
+ - nosy.gemspec
57
+ - spec/.DS_Store
58
+ - spec/nosy/.DS_Store
59
+ - spec/nosy/hunter_spec.rb
60
+ - spec/nosy/parser_spec.rb
61
+ - spec/nosy/searcher_spec.rb
62
+ - spec/nosy_spec.rb
63
+ - spec/support/.DS_Store
64
+ - spec/support/db/.DS_Store
65
+ - spec/support/db/empty_iphone_database.sqlite
66
+ - spec/support/db/incorrect_format_texts.sqlite
67
+ - spec/support/db/invalid_file_type_texts.txt
68
+ - spec/support/db/missing_table_texts.sqlite
69
+ - spec/support/db/texts.db
70
+ - spec/support/db/texts.sqlite
71
+ - spec/support/db/this_file_does_not_exist.sqlite
72
+ homepage: https://github.com/pdud/Nosy
59
73
  licenses: []
60
74
  post_install_message:
61
75
  rdoc_options: []
@@ -78,7 +92,5 @@ rubyforge_project:
78
92
  rubygems_version: 1.8.10
79
93
  signing_key:
80
94
  specification_version: 3
81
- summary: Nosy fetches, parses and searches the iPhone's SMS database that is created
82
- on your machine each time you make a backup.
95
+ summary: Output, backup, and read all your iPhone text messages
83
96
  test_files: []
84
- has_rdoc: