Merge branch 'develop' of git.znuny.com:zammad/zammad into develop
This commit is contained in:
commit
4b8e615640
219 changed files with 1629 additions and 2261 deletions
|
@ -251,10 +251,7 @@ returns
|
||||||
def self.day_and_comment_by_event(event, start_time)
|
def self.day_and_comment_by_event(event, start_time)
|
||||||
day = "#{start_time.year}-#{format('%02d', start_time.month)}-#{format('%02d', start_time.day)}"
|
day = "#{start_time.year}-#{format('%02d', start_time.month)}-#{format('%02d', start_time.day)}"
|
||||||
comment = event.summary || event.description
|
comment = event.summary || event.description
|
||||||
comment = Encode.conv( 'utf8', comment.to_s.force_encoding('utf-8') )
|
comment = comment.to_utf8(fallback: :read_as_sanitized_binary)
|
||||||
if !comment.valid_encoding?
|
|
||||||
comment = comment.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
|
|
||||||
# ignore daylight saving time entries
|
# ignore daylight saving time entries
|
||||||
return if comment.match?(/(daylight saving|sommerzeit|summertime)/i)
|
return if comment.match?(/(daylight saving|sommerzeit|summertime)/i)
|
||||||
|
|
|
@ -10,11 +10,11 @@ process emails from STDIN
|
||||||
|
|
||||||
e. g.
|
e. g.
|
||||||
|
|
||||||
cat test/fixtures/mail1.box | rails r 'Channel::Driver::MailStdin.new'
|
cat test/data/mail/mail001.box | rails r 'Channel::Driver::MailStdin.new'
|
||||||
|
|
||||||
e. g. if you want to trust on mail headers
|
e. g. if you want to trust on mail headers
|
||||||
|
|
||||||
cat test/fixtures/mail1.box | rails r 'Channel::Driver::MailStdin.new(trusted: true)'
|
cat test/data/mail/mail001.box | rails r 'Channel::Driver::MailStdin.new(trusted: true)'
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
|
|
||||||
class Channel::EmailParser
|
class Channel::EmailParser
|
||||||
|
EMAIL_REGEX = /.+@.+/
|
||||||
|
RECIPIENT_FIELDS = %w[to cc delivered-to x-original-to envelope-to].freeze
|
||||||
|
SENDER_FIELDS = %w[from reply-to return-path].freeze
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
@ -67,418 +70,16 @@ class Channel::EmailParser
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def parse(msg)
|
def parse(msg)
|
||||||
data = {}
|
mail = Mail.new(msg.utf8_encode)
|
||||||
|
|
||||||
# Rectify invalid encodings
|
message_attributes = [
|
||||||
encoding = CharDet.detect(msg)['encoding']
|
{ mail_instance: mail },
|
||||||
msg.force_encoding(encoding) if !msg.valid_encoding?
|
message_header_hash(mail),
|
||||||
|
message_body_hash(mail),
|
||||||
|
self.class.sender_attributes(mail),
|
||||||
|
]
|
||||||
|
|
||||||
mail = Mail.new(msg)
|
message_attributes.reduce({}.with_indifferent_access, &:merge)
|
||||||
|
|
||||||
# set all headers
|
|
||||||
mail.header.fields.each do |field|
|
|
||||||
|
|
||||||
# full line, encode, ready for storage
|
|
||||||
begin
|
|
||||||
value = Encode.conv('utf8', field.to_s)
|
|
||||||
if value.blank?
|
|
||||||
value = field.raw_value
|
|
||||||
end
|
|
||||||
data[field.name.to_s.downcase.to_sym] = value
|
|
||||||
rescue => e
|
|
||||||
data[field.name.to_s.downcase.to_sym] = field.raw_value
|
|
||||||
end
|
|
||||||
|
|
||||||
# if we need to access the lines by objects later again
|
|
||||||
data["raw-#{field.name.downcase}".to_sym] = field
|
|
||||||
end
|
|
||||||
|
|
||||||
# verify content, ignore recipients with non email address
|
|
||||||
['to', 'cc', 'delivered-to', 'x-original-to', 'envelope-to'].each do |field|
|
|
||||||
next if data[field.to_sym].blank?
|
|
||||||
next if data[field.to_sym].match?(/@/)
|
|
||||||
data[field.to_sym] = ''
|
|
||||||
end
|
|
||||||
|
|
||||||
# get sender with @ / email address
|
|
||||||
from = nil
|
|
||||||
['from', 'reply-to', 'return-path'].each do |item|
|
|
||||||
next if data[item.to_sym].blank?
|
|
||||||
next if data[item.to_sym] !~ /@/
|
|
||||||
from = data[item.to_sym]
|
|
||||||
break if from
|
|
||||||
end
|
|
||||||
|
|
||||||
# in case of no sender with email address - get sender
|
|
||||||
if !from
|
|
||||||
['from', 'reply-to', 'return-path'].each do |item|
|
|
||||||
next if data[item.to_sym].blank?
|
|
||||||
from = data[item.to_sym]
|
|
||||||
break if from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# set x-any-recipient
|
|
||||||
data['x-any-recipient'.to_sym] = ''
|
|
||||||
['to', 'cc', 'delivered-to', 'x-original-to', 'envelope-to'].each do |item|
|
|
||||||
next if data[item.to_sym].blank?
|
|
||||||
if data['x-any-recipient'.to_sym] != ''
|
|
||||||
data['x-any-recipient'.to_sym] += ', '
|
|
||||||
end
|
|
||||||
data['x-any-recipient'.to_sym] += mail[item.to_sym].to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
# set extra headers
|
|
||||||
data = data.merge(Channel::EmailParser.sender_properties(from))
|
|
||||||
|
|
||||||
# do extra encoding (see issue#1045)
|
|
||||||
if data[:subject].present?
|
|
||||||
data[:subject].sub!(/^=\?us-ascii\?Q\?(.+)\?=$/, '\1')
|
|
||||||
end
|
|
||||||
|
|
||||||
# compat headers
|
|
||||||
data[:message_id] = data['message-id'.to_sym]
|
|
||||||
|
|
||||||
# body
|
|
||||||
# plain_part = mail.multipart? ? (mail.text_part ? mail.text_part.body.decoded : nil) : mail.body.decoded
|
|
||||||
# html_part = message.html_part ? message.html_part.body.decoded : nil
|
|
||||||
data[:attachments] = []
|
|
||||||
|
|
||||||
# multi part email
|
|
||||||
if mail.multipart?
|
|
||||||
|
|
||||||
# html attachment/body may exists and will be converted to strict html
|
|
||||||
if mail.html_part&.body
|
|
||||||
data[:body] = mail.html_part.body.to_s
|
|
||||||
data[:body] = Encode.conv(mail.html_part.charset.to_s, data[:body])
|
|
||||||
data[:body] = data[:body].html2html_strict.to_s.force_encoding('utf-8')
|
|
||||||
|
|
||||||
if !data[:body].force_encoding('UTF-8').valid_encoding?
|
|
||||||
data[:body] = data[:body].encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
data[:content_type] = 'text/html'
|
|
||||||
end
|
|
||||||
|
|
||||||
# text attachment/body exists
|
|
||||||
if data[:body].blank? && mail.text_part
|
|
||||||
data[:body] = mail.text_part.body.decoded
|
|
||||||
data[:body] = Encode.conv(mail.text_part.charset, data[:body])
|
|
||||||
data[:body] = data[:body].to_s.force_encoding('utf-8')
|
|
||||||
|
|
||||||
if !data[:body].valid_encoding?
|
|
||||||
data[:body] = data[:body].encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
data[:content_type] = 'text/plain'
|
|
||||||
end
|
|
||||||
|
|
||||||
# any other attachments
|
|
||||||
if data[:body].blank?
|
|
||||||
data[:body] = 'no visible content'
|
|
||||||
data[:content_type] = 'text/plain'
|
|
||||||
end
|
|
||||||
|
|
||||||
# add html attachment/body as real attachment
|
|
||||||
if mail.html_part
|
|
||||||
filename = 'message.html'
|
|
||||||
headers_store = {
|
|
||||||
'content-alternative' => true,
|
|
||||||
'original-format' => true,
|
|
||||||
}
|
|
||||||
if mail.mime_type
|
|
||||||
headers_store['Mime-Type'] = mail.html_part.mime_type
|
|
||||||
end
|
|
||||||
if mail.charset
|
|
||||||
headers_store['Charset'] = mail.html_part.charset
|
|
||||||
end
|
|
||||||
attachment = {
|
|
||||||
data: mail.html_part.body.to_s,
|
|
||||||
filename: mail.html_part.filename || filename,
|
|
||||||
preferences: headers_store
|
|
||||||
}
|
|
||||||
data[:attachments].push attachment
|
|
||||||
end
|
|
||||||
|
|
||||||
# get attachments
|
|
||||||
mail.parts&.each do |part|
|
|
||||||
|
|
||||||
# protect process to work fine with spam emails, see test/fixtures/mail15.box
|
|
||||||
begin
|
|
||||||
attachs = _get_attachment(part, data[:attachments], mail)
|
|
||||||
data[:attachments].concat(attachs)
|
|
||||||
rescue
|
|
||||||
attachs = _get_attachment(part, data[:attachments], mail)
|
|
||||||
data[:attachments].concat(attachs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# not multipart email
|
|
||||||
|
|
||||||
# html part only, convert to text and add it as attachment
|
|
||||||
elsif mail.mime_type && mail.mime_type.to_s.casecmp('text/html').zero?
|
|
||||||
filename = 'message.html'
|
|
||||||
data[:body] = mail.body.decoded
|
|
||||||
data[:body] = Encode.conv(mail.charset, data[:body])
|
|
||||||
data[:body] = data[:body].html2html_strict.to_s.force_encoding('utf-8')
|
|
||||||
|
|
||||||
if !data[:body].valid_encoding?
|
|
||||||
data[:body] = data[:body].encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
data[:content_type] = 'text/html'
|
|
||||||
|
|
||||||
# add body as attachment
|
|
||||||
headers_store = {
|
|
||||||
'content-alternative' => true,
|
|
||||||
'original-format' => true,
|
|
||||||
}
|
|
||||||
if mail.mime_type
|
|
||||||
headers_store['Mime-Type'] = mail.mime_type
|
|
||||||
end
|
|
||||||
if mail.charset
|
|
||||||
headers_store['Charset'] = mail.charset
|
|
||||||
end
|
|
||||||
attachment = {
|
|
||||||
data: mail.body.decoded,
|
|
||||||
filename: mail.filename || filename,
|
|
||||||
preferences: headers_store
|
|
||||||
}
|
|
||||||
data[:attachments].push attachment
|
|
||||||
|
|
||||||
# text part only
|
|
||||||
elsif !mail.mime_type || mail.mime_type.to_s == '' || mail.mime_type.to_s.casecmp('text/plain').zero?
|
|
||||||
data[:body] = mail.body.decoded
|
|
||||||
data[:body] = Encode.conv(mail.charset, data[:body])
|
|
||||||
|
|
||||||
if !data[:body].force_encoding('UTF-8').valid_encoding?
|
|
||||||
data[:body] = data[:body].encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
data[:content_type] = 'text/plain'
|
|
||||||
else
|
|
||||||
filename = '-no name-'
|
|
||||||
data[:body] = 'no visible content'
|
|
||||||
data[:content_type] = 'text/plain'
|
|
||||||
|
|
||||||
# add body as attachment
|
|
||||||
headers_store = {
|
|
||||||
'content-alternative' => true,
|
|
||||||
}
|
|
||||||
if mail.mime_type
|
|
||||||
headers_store['Mime-Type'] = mail.mime_type
|
|
||||||
end
|
|
||||||
if mail.charset
|
|
||||||
headers_store['Charset'] = mail.charset
|
|
||||||
end
|
|
||||||
attachment = {
|
|
||||||
data: mail.body.decoded,
|
|
||||||
filename: mail.filename || filename,
|
|
||||||
preferences: headers_store
|
|
||||||
}
|
|
||||||
data[:attachments].push attachment
|
|
||||||
end
|
|
||||||
|
|
||||||
# strip not wanted chars
|
|
||||||
data[:body].gsub!(/\n\r/, "\n")
|
|
||||||
data[:body].gsub!(/\r\n/, "\n")
|
|
||||||
data[:body].tr!("\r", "\n")
|
|
||||||
|
|
||||||
# get mail date
|
|
||||||
begin
|
|
||||||
if mail.date
|
|
||||||
data[:date] = Time.zone.parse(mail.date.to_s)
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
data[:date] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# remember original mail instance
|
|
||||||
data[:mail_instance] = mail
|
|
||||||
|
|
||||||
data
|
|
||||||
end
|
|
||||||
|
|
||||||
def _get_attachment(file, attachments, mail)
|
|
||||||
|
|
||||||
# check if sub parts are available
|
|
||||||
if file.parts.present?
|
|
||||||
list = []
|
|
||||||
file.parts.each do |p|
|
|
||||||
attachment = _get_attachment(p, attachments, mail)
|
|
||||||
list.concat(attachment)
|
|
||||||
end
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
# ignore text/plain attachments - already shown in view
|
|
||||||
return [] if mail.text_part&.body.to_s == file.body.to_s
|
|
||||||
|
|
||||||
# ignore text/html - html part, already shown in view
|
|
||||||
return [] if mail.html_part&.body.to_s == file.body.to_s
|
|
||||||
|
|
||||||
# get file preferences
|
|
||||||
headers_store = {}
|
|
||||||
file.header.fields.each do |field|
|
|
||||||
|
|
||||||
# full line, encode, ready for storage
|
|
||||||
begin
|
|
||||||
value = Encode.conv('utf8', field.to_s)
|
|
||||||
if value.blank?
|
|
||||||
value = field.raw_value
|
|
||||||
end
|
|
||||||
headers_store[field.name.to_s] = value
|
|
||||||
rescue => e
|
|
||||||
headers_store[field.name.to_s] = field.raw_value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# cleanup content id, <> will be added automatically later
|
|
||||||
if headers_store['Content-ID']
|
|
||||||
headers_store['Content-ID'].gsub!(/^</, '')
|
|
||||||
headers_store['Content-ID'].gsub!(/>$/, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
# get filename from content-disposition
|
|
||||||
filename = nil
|
|
||||||
|
|
||||||
# workaround for: NoMethodError: undefined method `filename' for #<Mail::UnstructuredField:0x007ff109e80678>
|
|
||||||
begin
|
|
||||||
filename = file.header[:content_disposition].filename
|
|
||||||
rescue
|
|
||||||
begin
|
|
||||||
if file.header[:content_disposition].to_s =~ /filename="(.+?)"/i
|
|
||||||
filename = $1
|
|
||||||
elsif file.header[:content_disposition].to_s =~ /filename='(.+?)'/i
|
|
||||||
filename = $1
|
|
||||||
elsif file.header[:content_disposition].to_s =~ /filename=(.+?);/i
|
|
||||||
filename = $1
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
Rails.logger.debug { 'Unable to get filename' }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# as fallback, use raw values
|
|
||||||
if filename.blank?
|
|
||||||
if headers_store['Content-Disposition'].to_s =~ /filename="(.+?)"/i
|
|
||||||
filename = $1
|
|
||||||
elsif headers_store['Content-Disposition'].to_s =~ /filename='(.+?)'/i
|
|
||||||
filename = $1
|
|
||||||
elsif headers_store['Content-Disposition'].to_s =~ /filename=(.+?);/i
|
|
||||||
filename = $1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# for some broken sm mail clients (X-MimeOLE: Produced By Microsoft Exchange V6.5)
|
|
||||||
filename ||= file.header[:content_location].to_s
|
|
||||||
|
|
||||||
# generate file name based on content-id
|
|
||||||
if filename.blank? && headers_store['Content-ID'].present?
|
|
||||||
if headers_store['Content-ID'] =~ /(.+?)@.+?/i
|
|
||||||
filename = $1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# generate file name based on content type
|
|
||||||
if filename.blank? && headers_store['Content-Type'].present?
|
|
||||||
if headers_store['Content-Type'].match?(%r{^message/rfc822}i)
|
|
||||||
begin
|
|
||||||
parser = Channel::EmailParser.new
|
|
||||||
mail_local = parser.parse(file.body.to_s)
|
|
||||||
filename = if mail_local[:subject].present?
|
|
||||||
"#{mail_local[:subject]}.eml"
|
|
||||||
elsif headers_store['Content-Description'].present?
|
|
||||||
"#{headers_store['Content-Description']}.eml".to_s.force_encoding('utf-8')
|
|
||||||
else
|
|
||||||
'Mail.eml'
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
filename = 'Mail.eml'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# e. g. Content-Type: video/quicktime; name="Video.MOV";
|
|
||||||
if filename.blank?
|
|
||||||
['name="(.+?)"(;|$)', "name='(.+?)'(;|$)", 'name=(.+?)(;|$)'].each do |regexp|
|
|
||||||
if headers_store['Content-Type'] =~ /#{regexp}/i
|
|
||||||
filename = $1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# e. g. Content-Type: video/quicktime
|
|
||||||
if filename.blank?
|
|
||||||
map = {
|
|
||||||
'message/delivery-status': ['txt', 'delivery-status'],
|
|
||||||
'text/plain': %w[txt document],
|
|
||||||
'text/html': %w[html document],
|
|
||||||
'video/quicktime': %w[mov video],
|
|
||||||
'image/jpeg': %w[jpg image],
|
|
||||||
'image/jpg': %w[jpg image],
|
|
||||||
'image/png': %w[png image],
|
|
||||||
'image/gif': %w[gif image],
|
|
||||||
}
|
|
||||||
map.each do |type, ext|
|
|
||||||
next if headers_store['Content-Type'] !~ /^#{Regexp.quote(type)}/i
|
|
||||||
filename = if headers_store['Content-Description'].present?
|
|
||||||
"#{headers_store['Content-Description']}.#{ext[0]}".to_s.force_encoding('utf-8')
|
|
||||||
else
|
|
||||||
"#{ext[1]}.#{ext[0]}"
|
|
||||||
end
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if filename.blank?
|
|
||||||
filename = 'file'
|
|
||||||
end
|
|
||||||
|
|
||||||
attachment_count = 0
|
|
||||||
local_filename = ''
|
|
||||||
local_extention = ''
|
|
||||||
if filename =~ /^(.*?)\.(.+?)$/
|
|
||||||
local_filename = $1
|
|
||||||
local_extention = $2
|
|
||||||
end
|
|
||||||
|
|
||||||
(1..1000).each do |count|
|
|
||||||
filename_exists = false
|
|
||||||
attachments.each do |attachment|
|
|
||||||
if attachment[:filename] == filename
|
|
||||||
filename_exists = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
break if filename_exists == false
|
|
||||||
filename = if local_extention.present?
|
|
||||||
"#{local_filename}#{count}.#{local_extention}"
|
|
||||||
else
|
|
||||||
"#{local_filename}#{count}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# get mime type
|
|
||||||
if file.header[:content_type]&.string
|
|
||||||
headers_store['Mime-Type'] = file.header[:content_type].string
|
|
||||||
end
|
|
||||||
|
|
||||||
# get charset
|
|
||||||
if file.header&.charset
|
|
||||||
headers_store['Charset'] = file.header.charset
|
|
||||||
end
|
|
||||||
|
|
||||||
# remove not needed header
|
|
||||||
headers_store.delete('Content-Transfer-Encoding')
|
|
||||||
headers_store.delete('Content-Disposition')
|
|
||||||
|
|
||||||
# workaround for mail gem
|
|
||||||
# https://github.com/zammad/zammad/issues/928
|
|
||||||
filename = Mail::Encodings.value_decode(filename)
|
|
||||||
|
|
||||||
attach = {
|
|
||||||
data: file.body.to_s,
|
|
||||||
filename: filename,
|
|
||||||
preferences: headers_store,
|
|
||||||
}
|
|
||||||
[attach]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
@ -669,7 +270,7 @@ returns
|
||||||
mail[:attachments]&.each do |attachment|
|
mail[:attachments]&.each do |attachment|
|
||||||
filename = attachment[:filename].force_encoding('utf-8')
|
filename = attachment[:filename].force_encoding('utf-8')
|
||||||
if !filename.force_encoding('UTF-8').valid_encoding?
|
if !filename.force_encoding('UTF-8').valid_encoding?
|
||||||
filename = filename.encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
filename = filename.utf8_encode(fallback: :read_as_sanitized_binary)
|
||||||
end
|
end
|
||||||
Store.add(
|
Store.add(
|
||||||
object: 'Ticket::Article',
|
object: 'Ticket::Article',
|
||||||
|
@ -728,45 +329,51 @@ returns
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sender_properties(from)
|
def self.sender_attributes(from)
|
||||||
data = {}
|
if from.is_a?(Mail::Message)
|
||||||
return data if from.blank?
|
from = SENDER_FIELDS.map { |f| from.header[f] }.compact
|
||||||
begin
|
.map(&:to_utf8).reject(&:blank?)
|
||||||
list = Mail::AddressList.new(from)
|
.partition { |address| address.match?(EMAIL_REGEX) }
|
||||||
list.addresses.each do |address|
|
.flatten.first
|
||||||
data[:from_email] = address.address
|
|
||||||
data[:from_local] = address.local
|
|
||||||
data[:from_domain] = address.domain
|
|
||||||
data[:from_display_name] = address.display_name ||
|
|
||||||
(address.comments && address.comments[0])
|
|
||||||
break if data[:from_email].present? && data[:from_email] =~ /@/
|
|
||||||
end
|
|
||||||
rescue => e
|
|
||||||
if from =~ /<>/ && from =~ /<.+?>/
|
|
||||||
data = sender_properties(from.gsub(/<>/, ''))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if data.blank? || data[:from_email].blank?
|
data = {}.with_indifferent_access
|
||||||
from.strip!
|
return data if from.blank?
|
||||||
if from =~ /^(.+?)<(.+?)@(.+?)>$/
|
|
||||||
data[:from_email] = "#{$2}@#{$3}"
|
from = from.gsub('<>', '').strip
|
||||||
data[:from_local] = $2
|
mail_address = begin
|
||||||
data[:from_domain] = $3
|
Mail::AddressList.new(from).addresses
|
||||||
|
.select { |a| a.address.present? }
|
||||||
|
.partition { |a| a.address.match?(EMAIL_REGEX) }
|
||||||
|
.flatten.first
|
||||||
|
rescue Mail::Field::ParseError => e
|
||||||
|
STDOUT.puts e
|
||||||
|
end
|
||||||
|
|
||||||
|
if mail_address&.address.present?
|
||||||
|
data[:from_email] = mail_address.address
|
||||||
|
data[:from_local] = mail_address.local
|
||||||
|
data[:from_domain] = mail_address.domain
|
||||||
|
data[:from_display_name] = mail_address.display_name || mail_address.comments&.first
|
||||||
|
elsif from =~ /^(.+?)<((.+?)@(.+?))>$/
|
||||||
|
data[:from_email] = $2
|
||||||
|
data[:from_local] = $3
|
||||||
|
data[:from_domain] = $4
|
||||||
data[:from_display_name] = $1
|
data[:from_display_name] = $1
|
||||||
else
|
else
|
||||||
data[:from_email] = from
|
data[:from_email] = from
|
||||||
data[:from_local] = from
|
data[:from_local] = from
|
||||||
data[:from_domain] = from
|
data[:from_domain] = from
|
||||||
end
|
data[:from_display_name] = from
|
||||||
end
|
end
|
||||||
|
|
||||||
# do extra decoding because we needed to use field.value
|
# do extra decoding because we needed to use field.value
|
||||||
data[:from_display_name] = Mail::Field.new('X-From', Encode.conv('utf8', data[:from_display_name])).to_s
|
data[:from_display_name] =
|
||||||
data[:from_display_name].delete!('"')
|
Mail::Field.new('X-From', data[:from_display_name].to_utf8)
|
||||||
data[:from_display_name].strip!
|
.to_s
|
||||||
data[:from_display_name].gsub!(/^'/, '')
|
.delete('"')
|
||||||
data[:from_display_name].gsub!(/'$/, '')
|
.strip
|
||||||
|
.gsub(/(^'|'$)/, '')
|
||||||
|
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
@ -855,6 +462,270 @@ process unprocessable_mails (tmp/unprocessable_mail/*.eml) again
|
||||||
files
|
files
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def message_header_hash(mail)
|
||||||
|
imported_fields = mail.header.fields.map do |f|
|
||||||
|
value = begin
|
||||||
|
f.to_utf8
|
||||||
|
rescue NameError # handle bug #1238 in Mail 2.7.1.rc1
|
||||||
|
'' # swap out for commented line below once upgrade is available
|
||||||
|
end
|
||||||
|
|
||||||
|
[f.name.downcase, value]
|
||||||
|
end.to_h
|
||||||
|
|
||||||
|
# imported_fields = mail.header.fields.map { |f| [f.name.downcase, f.to_utf8] }.to_h
|
||||||
|
raw_fields = mail.header.fields.map { |f| ["raw-#{f.name.downcase}", f] }.to_h
|
||||||
|
custom_fields = {}.tap do |h|
|
||||||
|
validated_recipients = imported_fields.slice(*RECIPIENT_FIELDS)
|
||||||
|
.transform_values { |v| v.match?(EMAIL_REGEX) ? v : '' }
|
||||||
|
h.merge!(validated_recipients)
|
||||||
|
|
||||||
|
h['date'] = Time.zone.parse(mail.date.to_s) || imported_fields['date']
|
||||||
|
h['message_id'] = imported_fields['message-id']
|
||||||
|
h['subject'] = imported_fields['subject']&.sub(/^=\?us-ascii\?Q\?(.+)\?=$/, '\1')
|
||||||
|
h['x-any-recipient'] = validated_recipients.values.select(&:present?).join(', ')
|
||||||
|
end
|
||||||
|
|
||||||
|
[imported_fields, raw_fields, custom_fields].reduce({}.with_indifferent_access, &:merge)
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_body_hash(mail)
|
||||||
|
message = [mail.html_part, mail.text_part, mail].find { |m| m&.body.present? }
|
||||||
|
|
||||||
|
if message.mime_type.nil? || message.mime_type.match?(%r{^text/(plain|html)$})
|
||||||
|
content_type = message.mime_type || 'text/plain'
|
||||||
|
body = body_text(message, strict_html: content_type.eql?('text/html'))
|
||||||
|
end
|
||||||
|
|
||||||
|
content_type = 'text/plain' if body.blank?
|
||||||
|
|
||||||
|
{
|
||||||
|
attachments: collect_attachments(mail),
|
||||||
|
content_type: content_type || 'text/plain',
|
||||||
|
body: body.presence || 'no visible content'
|
||||||
|
}.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
def body_text(message, **options)
|
||||||
|
body_text = begin
|
||||||
|
message.body.to_s
|
||||||
|
rescue Mail::UnknownEncodingType # see test/data/mail/mail043.box / issue #348
|
||||||
|
message.body.raw_source
|
||||||
|
end
|
||||||
|
|
||||||
|
body_text = body_text.utf8_encode(from: message.charset, fallback: :read_as_sanitized_binary)
|
||||||
|
body_text = Mail::Utilities.to_lf(body_text)
|
||||||
|
|
||||||
|
return body_text.html2html_strict if options[:strict_html]
|
||||||
|
body_text
|
||||||
|
end
|
||||||
|
|
||||||
|
def collect_attachments(mail)
|
||||||
|
attachments = []
|
||||||
|
|
||||||
|
# Add non-plaintext body as an attachment
|
||||||
|
if mail.html_part&.body.present? ||
|
||||||
|
(!mail.multipart? && mail.mime_type.present? && mail.mime_type != 'text/plain')
|
||||||
|
message = mail.html_part || mail
|
||||||
|
|
||||||
|
filename = message.filename.presence ||
|
||||||
|
(message.mime_type.eql?('text/html') ? 'message.html' : '-no name-')
|
||||||
|
|
||||||
|
headers_store = {
|
||||||
|
'content-alternative' => true,
|
||||||
|
'original-format' => message.mime_type.eql?('text/html'),
|
||||||
|
'Mime-Type' => message.mime_type,
|
||||||
|
'Charset' => message.charset,
|
||||||
|
}.reject { |_, v| v.blank? }
|
||||||
|
|
||||||
|
attachments.push({ data: body_text(message),
|
||||||
|
filename: filename,
|
||||||
|
preferences: headers_store })
|
||||||
|
end
|
||||||
|
|
||||||
|
mail.parts.each do |part|
|
||||||
|
begin
|
||||||
|
new_attachments = get_attachments(part, attachments, mail).flatten.compact
|
||||||
|
attachments.push(*new_attachments)
|
||||||
|
rescue => e # Protect process to work with spam emails (see test/fixtures/mail15.box)
|
||||||
|
raise e if (fail_count ||= 0).positive?
|
||||||
|
(fail_count += 1) && retry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attachments
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_attachments(file, attachments, mail)
|
||||||
|
return file.parts.map { |p| get_attachments(p, attachments, mail) } if file.parts.any?
|
||||||
|
return [] if [mail.text_part, mail.html_part].include?(file)
|
||||||
|
|
||||||
|
# get file preferences
|
||||||
|
headers_store = {}
|
||||||
|
file.header.fields.each do |field|
|
||||||
|
|
||||||
|
# full line, encode, ready for storage
|
||||||
|
begin
|
||||||
|
value = field.to_utf8
|
||||||
|
if value.blank?
|
||||||
|
value = field.raw_value
|
||||||
|
end
|
||||||
|
headers_store[field.name.to_s] = value
|
||||||
|
rescue => e
|
||||||
|
headers_store[field.name.to_s] = field.raw_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# cleanup content id, <> will be added automatically later
|
||||||
|
if headers_store['Content-ID']
|
||||||
|
headers_store['Content-ID'].gsub!(/^</, '')
|
||||||
|
headers_store['Content-ID'].gsub!(/>$/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
# get filename from content-disposition
|
||||||
|
|
||||||
|
# workaround for: NoMethodError: undefined method `filename' for #<Mail::UnstructuredField:0x007ff109e80678>
|
||||||
|
filename = file.header[:content_disposition].try(:filename)
|
||||||
|
|
||||||
|
begin
|
||||||
|
if file.header[:content_disposition].to_s =~ /filename="(.+?)"/i
|
||||||
|
filename = $1
|
||||||
|
elsif file.header[:content_disposition].to_s =~ /filename='(.+?)'/i
|
||||||
|
filename = $1
|
||||||
|
elsif file.header[:content_disposition].to_s =~ /filename=(.+?);/i
|
||||||
|
filename = $1
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
Rails.logger.debug { 'Unable to get filename' }
|
||||||
|
end
|
||||||
|
|
||||||
|
# as fallback, use raw values
|
||||||
|
if filename.blank?
|
||||||
|
if headers_store['Content-Disposition'].to_s =~ /filename="(.+?)"/i
|
||||||
|
filename = $1
|
||||||
|
elsif headers_store['Content-Disposition'].to_s =~ /filename='(.+?)'/i
|
||||||
|
filename = $1
|
||||||
|
elsif headers_store['Content-Disposition'].to_s =~ /filename=(.+?);/i
|
||||||
|
filename = $1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# for some broken sm mail clients (X-MimeOLE: Produced By Microsoft Exchange V6.5)
|
||||||
|
filename ||= file.header[:content_location].to_s
|
||||||
|
|
||||||
|
# generate file name based on content-id
|
||||||
|
if filename.blank? && headers_store['Content-ID'].present?
|
||||||
|
if headers_store['Content-ID'] =~ /(.+?)@.+?/i
|
||||||
|
filename = $1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# generate file name based on content type
|
||||||
|
if filename.blank? && headers_store['Content-Type'].present?
|
||||||
|
if headers_store['Content-Type'].match?(%r{^message/rfc822}i)
|
||||||
|
begin
|
||||||
|
parser = Channel::EmailParser.new
|
||||||
|
mail_local = parser.parse(file.body.to_s)
|
||||||
|
filename = if mail_local[:subject].present?
|
||||||
|
"#{mail_local[:subject]}.eml"
|
||||||
|
elsif headers_store['Content-Description'].present?
|
||||||
|
"#{headers_store['Content-Description']}.eml".to_s.force_encoding('utf-8')
|
||||||
|
else
|
||||||
|
'Mail.eml'
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
filename = 'Mail.eml'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# e. g. Content-Type: video/quicktime; name="Video.MOV";
|
||||||
|
if filename.blank?
|
||||||
|
['name="(.+?)"(;|$)', "name='(.+?)'(;|$)", 'name=(.+?)(;|$)'].each do |regexp|
|
||||||
|
if headers_store['Content-Type'] =~ /#{regexp}/i
|
||||||
|
filename = $1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# e. g. Content-Type: video/quicktime
|
||||||
|
if filename.blank?
|
||||||
|
map = {
|
||||||
|
'message/delivery-status': ['txt', 'delivery-status'],
|
||||||
|
'text/plain': %w[txt document],
|
||||||
|
'text/html': %w[html document],
|
||||||
|
'video/quicktime': %w[mov video],
|
||||||
|
'image/jpeg': %w[jpg image],
|
||||||
|
'image/jpg': %w[jpg image],
|
||||||
|
'image/png': %w[png image],
|
||||||
|
'image/gif': %w[gif image],
|
||||||
|
}
|
||||||
|
map.each do |type, ext|
|
||||||
|
next if headers_store['Content-Type'] !~ /^#{Regexp.quote(type)}/i
|
||||||
|
filename = if headers_store['Content-Description'].present?
|
||||||
|
"#{headers_store['Content-Description']}.#{ext[0]}".to_s.force_encoding('utf-8')
|
||||||
|
else
|
||||||
|
"#{ext[1]}.#{ext[0]}"
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if filename.blank?
|
||||||
|
filename = 'file'
|
||||||
|
end
|
||||||
|
|
||||||
|
local_filename = ''
|
||||||
|
local_extention = ''
|
||||||
|
if filename =~ /^(.*?)\.(.+?)$/
|
||||||
|
local_filename = $1
|
||||||
|
local_extention = $2
|
||||||
|
end
|
||||||
|
|
||||||
|
1.upto(1000) do |i|
|
||||||
|
filename_exists = false
|
||||||
|
attachments.each do |attachment|
|
||||||
|
if attachment[:filename] == filename
|
||||||
|
filename_exists = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break if filename_exists == false
|
||||||
|
filename = if local_extention.present?
|
||||||
|
"#{local_filename}#{i}.#{local_extention}"
|
||||||
|
else
|
||||||
|
"#{local_filename}#{i}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# get mime type
|
||||||
|
if file.header[:content_type]&.string
|
||||||
|
headers_store['Mime-Type'] = file.header[:content_type].string
|
||||||
|
end
|
||||||
|
|
||||||
|
# get charset
|
||||||
|
if file.header&.charset
|
||||||
|
headers_store['Charset'] = file.header.charset
|
||||||
|
end
|
||||||
|
|
||||||
|
# remove not needed header
|
||||||
|
headers_store.delete('Content-Transfer-Encoding')
|
||||||
|
headers_store.delete('Content-Disposition')
|
||||||
|
|
||||||
|
# workaround for mail gem
|
||||||
|
# https://github.com/zammad/zammad/issues/928
|
||||||
|
filename = Mail::Encodings.value_decode(filename)
|
||||||
|
|
||||||
|
attach = {
|
||||||
|
data: file.body.to_s,
|
||||||
|
filename: filename,
|
||||||
|
preferences: headers_store,
|
||||||
|
}
|
||||||
|
|
||||||
|
[attach]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module Mail
|
module Mail
|
||||||
|
@ -862,7 +733,7 @@ module Mail
|
||||||
# workaround to get content of no parseable headers - in most cases with non 7 bit ascii signs
|
# workaround to get content of no parseable headers - in most cases with non 7 bit ascii signs
|
||||||
class Field
|
class Field
|
||||||
def raw_value
|
def raw_value
|
||||||
value = Encode.conv('utf8', @raw_value)
|
value = @raw_value.try(:utf8_encode)
|
||||||
return value if value.blank?
|
return value if value.blank?
|
||||||
value.sub(/^.+?:(\s|)/, '')
|
value.sub(/^.+?:(\s|)/, '')
|
||||||
end
|
end
|
||||||
|
@ -903,19 +774,4 @@ module Mail
|
||||||
end.join('')
|
end.join('')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# issue#348 - IMAP mail fetching stops because of broken spam email (e. g. broken Content-Transfer-Encoding value see test/fixtures/mail43.box)
|
|
||||||
# https://github.com/zammad/zammad/issues/348
|
|
||||||
class Body
|
|
||||||
def decoded
|
|
||||||
if !Encodings.defined?(encoding)
|
|
||||||
#raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
|
|
||||||
Rails.logger.info "UnknownEncodingType: Don't know how to decode #{encoding}!"
|
|
||||||
raw_source
|
|
||||||
else
|
|
||||||
Encodings.get_encoding(encoding).decode(raw_source)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@ module Channel::Filter::ReplyToBasedSender
|
||||||
mail['origin_from_display_name'.to_sym] = mail[:from_display_name]
|
mail['origin_from_display_name'.to_sym] = mail[:from_display_name]
|
||||||
|
|
||||||
# get properties of reply-to header
|
# get properties of reply-to header
|
||||||
result = Channel::EmailParser.sender_properties(reply_to)
|
result = Channel::EmailParser.sender_attributes(reply_to)
|
||||||
|
|
||||||
if setting == 'as_sender_of_email'
|
if setting == 'as_sender_of_email'
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ end
|
||||||
|
|
||||||
if connection.execute("SHOW tables LIKE 'settings';").any? &&
|
if connection.execute("SHOW tables LIKE 'settings';").any? &&
|
||||||
Setting.get('postmaster_max_size').present? &&
|
Setting.get('postmaster_max_size').present? &&
|
||||||
Setting.get('postmaster_max_size') > max_allowed_packet_mb
|
Setting.get('postmaster_max_size').to_i > max_allowed_packet_mb
|
||||||
printf "\e[33m" # ANSI yellow
|
printf "\e[33m" # ANSI yellow
|
||||||
puts <<~MSG
|
puts <<~MSG
|
||||||
Warning: Database config value 'max_allowed_packet' less than Zammad setting 'Maximum Email Size'
|
Warning: Database config value 'max_allowed_packet' less than Zammad setting 'Maximum Email Size'
|
||||||
|
|
5
lib/core_ext/object.rb
Normal file
5
lib/core_ext/object.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class Object
|
||||||
|
def to_utf8(**options)
|
||||||
|
to_s.utf8_encode(**options)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'rchardet'
|
||||||
|
|
||||||
class String
|
class String
|
||||||
alias old_strip strip
|
alias old_strip strip
|
||||||
alias old_strip! strip!
|
alias old_strip! strip!
|
||||||
|
@ -8,7 +10,7 @@ class String
|
||||||
sub!(/[[[:space:]]\u{200B}\u{FEFF}]+\Z/, '')
|
sub!(/[[[:space:]]\u{200B}\u{FEFF}]+\Z/, '')
|
||||||
|
|
||||||
# if incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError), use default
|
# if incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError), use default
|
||||||
rescue
|
rescue Encoding::CompatibilityError
|
||||||
old_strip!
|
old_strip!
|
||||||
end
|
end
|
||||||
self
|
self
|
||||||
|
@ -20,7 +22,7 @@ class String
|
||||||
new_string.sub!(/[[[:space:]]\u{200B}\u{FEFF}]+\Z/, '')
|
new_string.sub!(/[[[:space:]]\u{200B}\u{FEFF}]+\Z/, '')
|
||||||
|
|
||||||
# if incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError), use default
|
# if incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError), use default
|
||||||
rescue
|
rescue Encoding::CompatibilityError
|
||||||
new_string = old_strip
|
new_string = old_strip
|
||||||
end
|
end
|
||||||
new_string
|
new_string
|
||||||
|
@ -109,7 +111,7 @@ class String
|
||||||
string = "#{self}" # rubocop:disable Style/UnneededInterpolation
|
string = "#{self}" # rubocop:disable Style/UnneededInterpolation
|
||||||
|
|
||||||
# in case of invalid encoding, strip invalid chars
|
# in case of invalid encoding, strip invalid chars
|
||||||
# see also test/fixtures/mail21.box
|
# see also test/data/mail/mail021.box
|
||||||
# note: string.encode!('UTF-8', 'UTF-8', :invalid => :replace, :replace => '?') was not detecting invalid chars
|
# note: string.encode!('UTF-8', 'UTF-8', :invalid => :replace, :replace => '?') was not detecting invalid chars
|
||||||
if !string.valid_encoding?
|
if !string.valid_encoding?
|
||||||
string = string.chars.select(&:valid_encoding?).join
|
string = string.chars.select(&:valid_encoding?).join
|
||||||
|
@ -451,4 +453,59 @@ class String
|
||||||
|
|
||||||
string
|
string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a copied string whose encoding is UTF-8.
|
||||||
|
# If both the provided and current encodings are invalid,
|
||||||
|
# an auto-detected encoding is tried.
|
||||||
|
#
|
||||||
|
# Supports some fallback strategies if a valid encoding cannot be found.
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
#
|
||||||
|
# * from: An encoding to try first.
|
||||||
|
# Takes precedence over the current and auto-detected encodings.
|
||||||
|
#
|
||||||
|
# * fallback: The strategy to follow if no valid encoding can be found.
|
||||||
|
# * `:output_to_binary` returns an ASCII-8BIT-encoded string.
|
||||||
|
# * `:read_as_sanitized_binary` returns a UTF-8-encoded string with all
|
||||||
|
# invalid byte sequences replaced with "?" characters.
|
||||||
|
def utf8_encode(**options)
|
||||||
|
dup.utf8_encode!(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def utf8_encode!(**options)
|
||||||
|
return self if (encoding == Encoding::UTF_8) && valid_encoding?
|
||||||
|
|
||||||
|
input_encoding = viable_encodings(try_first: options[:from]).first
|
||||||
|
return encode!('utf-8', input_encoding) if input_encoding.present?
|
||||||
|
|
||||||
|
case options[:fallback]
|
||||||
|
when :output_to_binary
|
||||||
|
force_encoding('ascii-8bit')
|
||||||
|
when :read_as_sanitized_binary
|
||||||
|
encode!('utf-8', 'ascii-8bit', invalid: :replace, undef: :replace, replace: '?')
|
||||||
|
else
|
||||||
|
raise EncodingError, 'could not find a valid input encoding'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def viable_encodings(try_first: nil)
|
||||||
|
return dup.viable_encodings(try_first: try_first) if frozen?
|
||||||
|
|
||||||
|
provided = Encoding.find(try_first) if try_first.present?
|
||||||
|
original = encoding
|
||||||
|
detected = CharDet.detect(self)['encoding']
|
||||||
|
|
||||||
|
[provided, original, detected]
|
||||||
|
.compact
|
||||||
|
.reject { |e| Encoding.find(e) == Encoding::ASCII_8BIT }
|
||||||
|
.select { |e| force_encoding(e).valid_encoding? }
|
||||||
|
.tap { force_encoding(original) } # clean up changes from previous line
|
||||||
|
|
||||||
|
# if `try_first` is not a valid encoding, try_first again without it
|
||||||
|
rescue ArgumentError
|
||||||
|
try_first.present? ? viable_encodings : raise
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
module Encode
|
|
||||||
def self.conv (charset, string)
|
|
||||||
|
|
||||||
# return if string is false
|
|
||||||
return string if !string
|
|
||||||
|
|
||||||
# if no charset is given, use LATIN1 as default
|
|
||||||
if !charset || charset == 'US-ASCII' || charset == 'ASCII-8BIT'
|
|
||||||
charset = 'ISO-8859-15'
|
|
||||||
end
|
|
||||||
|
|
||||||
# validate already existing utf8 strings
|
|
||||||
if charset.casecmp('utf8').zero? || charset.casecmp('utf-8').zero?
|
|
||||||
begin
|
|
||||||
# return if encoding is valid
|
|
||||||
utf8 = string.dup.force_encoding('UTF-8')
|
|
||||||
return utf8 if utf8.valid_encoding?
|
|
||||||
|
|
||||||
# try to encode from Windows-1252 to utf8
|
|
||||||
string.encode!('UTF-8', 'Windows-1252')
|
|
||||||
rescue EncodingError => e
|
|
||||||
Rails.logger.error "Bad encoding: #{string.inspect}"
|
|
||||||
string = string.encode!('UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
|
||||||
end
|
|
||||||
return string
|
|
||||||
end
|
|
||||||
|
|
||||||
# convert string
|
|
||||||
begin
|
|
||||||
string.encode!('UTF-8', charset)
|
|
||||||
rescue => e
|
|
||||||
Rails.logger.error 'ERROR: ' + e.inspect
|
|
||||||
string
|
|
||||||
end
|
|
||||||
#Iconv.conv( 'UTF8', charset, string)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -119,9 +119,9 @@ module Enrichment
|
||||||
|
|
||||||
def fetch
|
def fetch
|
||||||
if !Rails.env.production?
|
if !Rails.env.production?
|
||||||
filename = Rails.root.join('test', 'fixtures', 'clearbit', "#{@local_user.email}.json")
|
filename = Rails.root.join('test', 'data', 'clearbit', "#{@local_user.email}.json")
|
||||||
if File.exist?(filename)
|
if File.exist?(filename)
|
||||||
data = IO.binread(filename)
|
data = File.binread(filename)
|
||||||
return JSON.parse(data) if data
|
return JSON.parse(data) if data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -373,40 +373,35 @@ cleanup html string:
|
||||||
string.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"').gsub(' ', ' ')
|
string.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"').gsub(' ', ' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.cleanup_target(string, keep_spaces: false)
|
def self.cleanup_target(string, **options)
|
||||||
string = CGI.unescape(string).encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
cleaned_string = CGI.unescape(string).utf8_encode(fallback: :read_as_sanitized_binary)
|
||||||
blank_regex = if keep_spaces
|
cleaned_string = cleaned_string.gsub(/[[:space:]]/, '') if !options[:keep_spaces]
|
||||||
/\t|\n|\r/
|
cleaned_string = cleaned_string.strip
|
||||||
else
|
.delete("\t\n\r\u0000")
|
||||||
/[[:space:]]|\t|\n|\r/
|
.gsub(%r{/\*.*?\*/}, '')
|
||||||
end
|
.gsub(/<!--.*?-->/, '')
|
||||||
cleaned_string = string.strip.gsub(blank_regex, '').gsub(%r{/\*.*?\*/}, '').gsub(/<!--.*?-->/, '').gsub(/\[.+?\]/, '').delete("\u0000")
|
.gsub(/\[.+?\]/, '')
|
||||||
|
|
||||||
sanitize_attachment_disposition(cleaned_string)
|
sanitize_attachment_disposition(cleaned_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sanitize_attachment_disposition(url)
|
def self.sanitize_attachment_disposition(url)
|
||||||
uri = URI(url)
|
uri = URI(url)
|
||||||
return url if uri.host != Setting.get('fqdn')
|
|
||||||
|
|
||||||
|
if uri.host == Setting.get('fqdn') && uri.query.present?
|
||||||
params = CGI.parse(uri.query || '')
|
params = CGI.parse(uri.query || '')
|
||||||
if params.key?('disposition')
|
.tap { |p| p.merge!('disposition' => 'attachment') if p.include?('disposition') }
|
||||||
params['disposition'] = 'attachment'
|
uri.query = URI.encode_www_form(params)
|
||||||
end
|
|
||||||
|
|
||||||
uri.query = if params.blank?
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
URI.encode_www_form(params)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
uri.to_s
|
uri.to_s
|
||||||
rescue URI::InvalidURIError
|
rescue URI::Error
|
||||||
url
|
url
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.url_same?(url_new, url_old)
|
def self.url_same?(url_new, url_old)
|
||||||
url_new = CGI.unescape(url_new.to_s).encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?').downcase.gsub(%r{/$}, '').gsub(/[[:space:]]|\t|\n|\r/, '').strip
|
url_new = CGI.unescape(url_new.to_s).utf8_encode(fallback: :read_as_sanitized_binary).downcase.gsub(%r{/$}, '').gsub(/[[:space:]]|\t|\n|\r/, '').strip
|
||||||
url_old = CGI.unescape(url_old.to_s).encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?').downcase.gsub(%r{/$}, '').gsub(/[[:space:]]|\t|\n|\r/, '').strip
|
url_old = CGI.unescape(url_old.to_s).utf8_encode(fallback: :read_as_sanitized_binary).downcase.gsub(%r{/$}, '').gsub(/[[:space:]]|\t|\n|\r/, '').strip
|
||||||
url_new = html_decode(url_new).sub('/?', '?')
|
url_new = html_decode(url_new).sub('/?', '?')
|
||||||
url_old = html_decode(url_old).sub('/?', '?')
|
url_old = html_decode(url_old).sub('/?', '?')
|
||||||
return true if url_new == url_old
|
return true if url_new == url_old
|
||||||
|
@ -485,6 +480,7 @@ satinize style of img tags
|
||||||
end
|
end
|
||||||
|
|
||||||
private_class_method :cleanup_target
|
private_class_method :cleanup_target
|
||||||
|
private_class_method :sanitize_attachment_disposition
|
||||||
private_class_method :add_link
|
private_class_method :add_link
|
||||||
private_class_method :url_same?
|
private_class_method :url_same?
|
||||||
private_class_method :html_decode
|
private_class_method :html_decode
|
||||||
|
|
|
@ -22,8 +22,8 @@ module Import
|
||||||
def utf8_encode(data)
|
def utf8_encode(data)
|
||||||
data.each do |key, value|
|
data.each do |key, value|
|
||||||
next if !value
|
next if !value
|
||||||
next if value.class != String
|
next if !value.respond_to?(:utf8_encode)
|
||||||
data[key] = Encode.conv('utf8', value)
|
data[key] = value.utf8_encode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ module Import
|
||||||
|
|
||||||
next if value.nil?
|
next if value.nil?
|
||||||
|
|
||||||
example = value.to_s.force_encoding('UTF-8').encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
example = value.to_utf8(fallback: :read_as_sanitized_binary)
|
||||||
example.gsub!(/^(.{20,}?).*$/m, '\1...')
|
example.gsub!(/^(.{20,}?).*$/m, '\1...')
|
||||||
|
|
||||||
@examples[attribute] = "#{attribute} (e. g. #{example})"
|
@examples[attribute] = "#{attribute} (e. g. #{example})"
|
||||||
|
|
|
@ -94,9 +94,9 @@ module Import
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_response(response)
|
def handle_response(response)
|
||||||
encoded_body = Encode.conv('utf8', response.body.to_s)
|
encoded_body = response.body.to_utf8
|
||||||
# remove null bytes otherwise PostgreSQL will fail
|
# remove null bytes otherwise PostgreSQL will fail
|
||||||
encoded_body.gsub!('\u0000', '')
|
encoded_body.delete('\u0000')
|
||||||
JSON.parse(encoded_body)
|
JSON.parse(encoded_body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Ldap
|
||||||
next if value.blank?
|
next if value.blank?
|
||||||
next if value[0].blank?
|
next if value[0].blank?
|
||||||
|
|
||||||
example_value = value[0].force_encoding('UTF-8').encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
|
example_value = value[0].force_encoding('UTF-8').utf8_encode(fallback: :read_as_sanitized_binary)
|
||||||
attributes[attribute] = "#{attribute} (e. g. #{example_value})"
|
attributes[attribute] = "#{attribute} (e. g. #{example_value})"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
58
spec/lib/core_ext/string_spec.rb
Normal file
58
spec/lib/core_ext/string_spec.rb
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe String do
|
||||||
|
describe '#utf8_encode' do
|
||||||
|
context 'for valid, UTF-8-encoded strings' do
|
||||||
|
let(:subject) { 'hello' }
|
||||||
|
|
||||||
|
it 'returns an identical copy' do
|
||||||
|
expect(subject.utf8_encode).to eq(subject)
|
||||||
|
expect(subject.utf8_encode.encoding).to be(subject.encoding)
|
||||||
|
expect(subject.utf8_encode).not_to be(subject)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for strings in other encodings' do
|
||||||
|
let(:subject) { original_string.encode(input_encoding) }
|
||||||
|
|
||||||
|
context 'with no from: option' do
|
||||||
|
let(:original_string) { 'Tschüss!' }
|
||||||
|
let(:input_encoding) { Encoding::ISO_8859_2 }
|
||||||
|
|
||||||
|
it 'detects the input encoding' do
|
||||||
|
expect(subject.utf8_encode).to eq(original_string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a valid from: option' do
|
||||||
|
let(:original_string) { 'Tschüss!' }
|
||||||
|
let(:input_encoding) { Encoding::ISO_8859_2 }
|
||||||
|
|
||||||
|
it 'uses the specified input encoding' do
|
||||||
|
expect(subject.utf8_encode(from: 'iso-8859-2')).to eq(original_string)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses any valid input encoding, even if not correct' do
|
||||||
|
expect(subject.utf8_encode(from: 'gb18030')).to eq('Tsch黶s!')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an invalid from: option' do
|
||||||
|
let(:original_string) { '―陈志' }
|
||||||
|
let(:input_encoding) { Encoding::GB18030 }
|
||||||
|
|
||||||
|
it 'does not try it' do
|
||||||
|
expect { subject.encode('utf-8', 'gb2312') }
|
||||||
|
.to raise_error(Encoding::InvalidByteSequenceError)
|
||||||
|
|
||||||
|
expect { subject.utf8_encode(from: 'gb2312') }
|
||||||
|
.not_to raise_error(Encoding::InvalidByteSequenceError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses the detected input encoding instead' do
|
||||||
|
expect(subject.utf8_encode(from: 'gb2312')).to eq(original_string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -45,7 +45,8 @@ class AgentTicketAttachmentTest < TestCase
|
||||||
# add attachment, attachment check should quiet
|
# add attachment, attachment check should quiet
|
||||||
file_upload(
|
file_upload(
|
||||||
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
||||||
files: ['test/fixtures/upload2.jpg', 'test/fixtures/upload1.txt'],
|
files: [Rails.root.join('test', 'data', 'upload', 'upload1.txt'),
|
||||||
|
Rails.root.join('test', 'data', 'upload', 'upload2.jpg')],
|
||||||
)
|
)
|
||||||
|
|
||||||
# submit form
|
# submit form
|
||||||
|
@ -91,7 +92,7 @@ class AgentTicketAttachmentTest < TestCase
|
||||||
# add attachment, attachment check should quiet
|
# add attachment, attachment check should quiet
|
||||||
file_upload(
|
file_upload(
|
||||||
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
||||||
files: ['test/fixtures/upload1.txt'],
|
files: [Rails.root.join('test', 'data', 'upload', 'upload1.txt')],
|
||||||
)
|
)
|
||||||
|
|
||||||
# submit form
|
# submit form
|
||||||
|
@ -132,7 +133,8 @@ class AgentTicketAttachmentTest < TestCase
|
||||||
# add attachment without body
|
# add attachment without body
|
||||||
file_upload(
|
file_upload(
|
||||||
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
||||||
files: ['test/fixtures/upload2.jpg', 'test/fixtures/upload1.txt'],
|
files: [Rails.root.join('test', 'data', 'upload', 'upload1.txt'),
|
||||||
|
Rails.root.join('test', 'data', 'upload', 'upload2.jpg')],
|
||||||
)
|
)
|
||||||
|
|
||||||
# submit form
|
# submit form
|
||||||
|
|
|
@ -1226,7 +1226,7 @@ set type of task (closeTab, closeNextInOverview, stayOnTab)
|
||||||
file_upload(
|
file_upload(
|
||||||
browser: browser1,
|
browser: browser1,
|
||||||
css: '.content.active .attachmentPlaceholder-inputHolder input'
|
css: '.content.active .attachmentPlaceholder-inputHolder input'
|
||||||
files: ['path/in/home/some_file.ext'], # 'test/fixtures/test1.pdf'
|
files: ['path/in/home/some_file.ext'], # 'test/data/pdf/test1.pdf'
|
||||||
)
|
)
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
|
@ -531,7 +531,8 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
||||||
|
|
||||||
# invalid file
|
# invalid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple_col_not_existing.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'organization_simple_col_not_existing.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -545,7 +546,8 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal("Line 2: unknown attribute 'name2' for Organization.", result['errors'][1])
|
assert_equal("Line 2: unknown attribute 'name2' for Organization.", result['errors'][1])
|
||||||
|
|
||||||
# valid file try
|
# valid file try
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'organization_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/organizations/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -559,7 +561,8 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_nil(Organization.find_by(name: 'organization-member-import2'))
|
assert_nil(Organization.find_by(name: 'organization-member-import2'))
|
||||||
|
|
||||||
# valid file
|
# valid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'organization_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'organization_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/organizations/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/organizations/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
|
|
@ -101,7 +101,8 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
||||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
||||||
|
|
||||||
# invalid file
|
# invalid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple_col_not_existing.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'text_module_simple_col_not_existing.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -115,7 +116,8 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal("Line 2: unknown attribute 'keywords2' for TextModule.", result['errors'][1])
|
assert_equal("Line 2: unknown attribute 'keywords2' for TextModule.", result['errors'][1])
|
||||||
|
|
||||||
# valid file try
|
# valid file try
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'text_module_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/text_modules/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -129,7 +131,8 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_nil(TextModule.find_by(name: 'some name2'))
|
assert_nil(TextModule.find_by(name: 'some name2'))
|
||||||
|
|
||||||
# valid file
|
# valid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'text_module_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'text_module_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/text_modules/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/text_modules/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
|
|
@ -144,7 +144,8 @@ class TicketArticleAttachmentsControllerTest < ActionDispatch::IntegrationTest
|
||||||
test '01.02 test attachments for split' do
|
test '01.02 test attachments for split' do
|
||||||
headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
|
headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
|
||||||
|
|
||||||
email_raw_string = IO.binread('test/fixtures/mail24.box')
|
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail024.box')
|
||||||
|
email_raw_string = File.read(email_file_path)
|
||||||
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
||||||
|
|
||||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw')
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw')
|
||||||
|
@ -161,7 +162,8 @@ class TicketArticleAttachmentsControllerTest < ActionDispatch::IntegrationTest
|
||||||
test '01.03 test attachments for forward' do
|
test '01.03 test attachments for forward' do
|
||||||
headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
|
headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
|
||||||
|
|
||||||
email_raw_string = IO.binread('test/fixtures/mail8.box')
|
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail008.box')
|
||||||
|
email_raw_string = File.read(email_file_path)
|
||||||
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
||||||
|
|
||||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw')
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw')
|
||||||
|
@ -177,7 +179,8 @@ class TicketArticleAttachmentsControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal(result['attachments'].class, Array)
|
assert_equal(result['attachments'].class, Array)
|
||||||
assert(result['attachments'].blank?)
|
assert(result['attachments'].blank?)
|
||||||
|
|
||||||
email_raw_string = IO.binread('test/fixtures/mail24.box')
|
email_file_path = Rails.root.join('test', 'data', 'mail', 'mail024.box')
|
||||||
|
email_raw_string = File.read(email_file_path)
|
||||||
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
|
||||||
|
|
||||||
post "/api/v1/ticket_attachment_upload_clone_by_article/#{article_p.id}", params: { form_id: '1234-2' }.to_json, headers: headers.merge('Authorization' => credentials)
|
post "/api/v1/ticket_attachment_upload_clone_by_article/#{article_p.id}", params: { form_id: '1234-2' }.to_json, headers: headers.merge('Authorization' => credentials)
|
||||||
|
|
|
@ -978,7 +978,8 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
||||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials('rest-admin@example.com', 'adminpw')
|
||||||
|
|
||||||
# invalid file
|
# invalid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple_col_not_existing.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'user_simple_col_not_existing.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -992,7 +993,8 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal("Line 2: unknown attribute 'firstname2' for User.", result['errors'][1])
|
assert_equal("Line 2: unknown attribute 'firstname2' for User.", result['errors'][1])
|
||||||
|
|
||||||
# valid file try
|
# valid file try
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'user_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/users/import?try=true', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
@ -1006,7 +1008,8 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_nil(User.find_by(login: 'user-simple-import2'))
|
assert_nil(User.find_by(login: 'user-simple-import2'))
|
||||||
|
|
||||||
# valid file
|
# valid file
|
||||||
csv_file = ::Rack::Test::UploadedFile.new(Rails.root.join('test', 'fixtures', 'csv', 'user_simple.csv'), 'text/csv')
|
csv_file_path = Rails.root.join('test', 'data', 'csv', 'user_simple.csv')
|
||||||
|
csv_file = ::Rack::Test::UploadedFile.new(csv_file_path, 'text/csv')
|
||||||
post '/api/v1/users/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
post '/api/v1/users/import', params: { file: csv_file, col_sep: ';' }, headers: { 'Authorization' => credentials }
|
||||||
assert_response(200)
|
assert_response(200)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
|
|
Can't render this file because it has a wrong number of fields in line 2.
|
Can't render this file because it has a wrong number of fields in line 2.
|
13
test/data/mail/mail001.yml
Normal file
13
test/data/mail/mail001.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: John.Smith@example.com
|
||||||
|
from_email: John.Smith@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: 'CI Daten für PublicView '
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<div>
|
||||||
|
<div>Hallo Martin,</div><p> </p><div>wie besprochen hier noch die Daten für die Intranetseite:</div><p> </p><div>Schriftart/-größe: Verdana 11 Pt wenn von Browser nicht unterstützt oder nicht vorhanden wird Arial 11 Pt genommen</div><div>Schriftfarbe: Schwarz</div><div>Farbe für die Balken in der Grafik: D7DDE9 (Blau)</div><p> </p><div>Wenn noch was fehlt oder du was brauchst sag mir Bescheid.</div><p> </p><div>Mit freundlichem Gruß<br><br>John Smith<br>Service und Support<br><br>Example Service AG & Co. </div><div>Management OHG<br>Someware-Str. 4<br>xxxxx Someware<br><br>
|
||||||
|
</div><div>Tel.: +49 001 7601 462<br>Fax: +49 001 7601 472 </div><div>john.smith@example.com</div><div>
|
||||||
|
<a href="http://www.example.com" rel="nofollow noreferrer noopener" target="_blank">www.example.com</a>
|
||||||
|
</div><div>
|
||||||
|
<br>OHG mit Sitz in Someware<br>AG: Someware - HRA 4158<br>Geschäftsführung: Tilman Test, Klaus Jürgen Test, </div><div>Bernhard Test, Ulrich Test<br>USt-IdNr. DE 1010101010<br><br>Persönlich haftende geschäftsführende Gesellschafterin: </div><div>Marie Test Example Stiftung, Someware<br>Vorstand: Rolf Test<br><br>Persönlich haftende Gesellschafterin: </div><div>Example Service AG, Someware<br>AG: Someware - HRB xxx<br>Vorstand: Marie Test </div><p> </p></div>
|
13
test/data/mail/mail002.yml
Normal file
13
test/data/mail/mail002.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Martin Edenhofer <martin@example.com>
|
||||||
|
from_email: martin@example.com
|
||||||
|
from_display_name: Martin Edenhofer
|
||||||
|
subject: aaäöüßad asd
|
||||||
|
content_type: text/plain
|
||||||
|
body: |
|
||||||
|
äöüß ad asd
|
||||||
|
|
||||||
|
-Martin
|
||||||
|
|
||||||
|
--
|
||||||
|
Old programmers never die. They just branch to a new address.
|
26
test/data/mail/mail003.yml
Normal file
26
test/data/mail/mail003.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"Günther John | Example GmbH" <k.guenther@example.com>'
|
||||||
|
from_email: k.guenther@example.com
|
||||||
|
from_display_name: Günther John | Example GmbH
|
||||||
|
subject: Ticket Templates
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<div>
|
||||||
|
<p>Hallo Martin,</p><p> </p><p>ich möchte mich gern für den Beta-Test für die Ticket Templates unter XXXX 2.4 anmelden.</p><p> </p><div> <p> </p><p>Mit freundlichen Grüßen</p><p>John Günther</p><p> </p><p><a href="http://www.GeoFachDatenServer.de" rel="nofollow noreferrer noopener" target="_blank" title="http://www.GeoFachDatenServer.de">example.com</a> – profitieren Sie vom umfangreichen Daten-Netzwerk </p><p> </p><p>_ __ ___ ____________________________ ___ __ _</p><p> </p><p>Example GmbH</p><p>Some What</p><p> </p><p>Sitz: Someware-Straße 9, XXXXX Someware</p><p> </p><p>M: +49 (0) XXX XX XX 70</p><p>T: +49 (0) XXX XX XX 22</p><p>F: +49 (0) XXX XX XX 11</p><p>W: <a href="http://www.example.de" rel="nofollow noreferrer noopener" target="_blank">http://www.example.de</a></p><p> </p><p>Geschäftsführer: John Smith</p><p>HRB XXXXXX AG Someware</p><p>St.-Nr.: 112/107/05858</p><p> </p><p>ISO 9001:2008 Zertifiziert -Qualitätsstandard mit Zukunft</p><p>_ __ ___ ____________________________ ___ __ _</p><p> </p><p>Diese Information ist ausschließlich für den Adressaten bestimmt und kann vertrauliche oder gesetzlich geschützte Informationen enthalten. Wenn Sie nicht der bestimmungsgemäße Adressat sind, unterrichten Sie bitte den Absender und vernichten Sie diese Mail. Anderen als dem bestimmungsgemäßen Adressaten ist es untersagt, diese E-Mail zu lesen, zu speichern, weiterzuleiten oder ihren Inhalt auf welche Weise auch immer zu verwenden.</p></div><p> </p><div>
|
||||||
|
<span class="js-signatureMarker"></span><p><b>Von:</b> Fritz Bauer [mailto:me@example.com] <br><b>Gesendet:</b> Donnerstag, 3. Mai 2012 11:51<br><b>An:</b> John Smith<br><b>Cc:</b> Smith, John Marian; johnel.fratczak@example.com; ole.brei@example.com; Günther John | Example GmbH; bkopon@example.com; john.heisterhagen@team.example.com; sven.rocked@example.com; michael.house@example.com; tgutzeit@example.com<br><b>Betreff:</b> Re: OTRS::XXX Erweiterung - Anhänge an CI's</p></div><p> </p><p>Hallo,</p><div> <p> </p></div><div>
|
||||||
|
<p>ich versuche an den Punkten anzuknüpfen.</p></div><div> <p> </p></div><div>
|
||||||
|
<p><b>a) LDAP Muster Konfigdatei</b></p></div><div> <p> </p></div><div>
|
||||||
|
<p><a href="https://wiki.lab.example.com/doku.php?id=xxxx:start&#ldap" rel="nofollow noreferrer noopener" target="_blank">https://wiki.lab.example.com/doku.php?id=xxxx:start&#ldap</a></p></div><div> <p> </p></div><div>
|
||||||
|
<p>PS: Es gibt noch eine Reihe weiterer Möglichkeiten, vor allem im Bezug auf Agenten-Rechte/LDAP Gruppen Synchronisation. Wenn Ihr hier weitere Informationen benötigt, einfach im Wiki die Aufgabenbeschreibung rein machen und ich kann eine Beispiel-Config dazu legen.</p></div><div>
|
||||||
|
<p> </p></div><div> <p> </p></div><div>
|
||||||
|
<p><b>b) Ticket Templates</b></p></div><div>
|
||||||
|
<p>Wir haben das Paket vom alten Maintainer übernommen, es läuft nun auf XXXX 2.4, XXXX 3.0 und XXXX 3.1. Wir haben das Paket um weitere Funktionen ergänzt und würden es gerne hier in diesen Kreis zum Beta-Test bereit stellen.</p></div><div> <p> </p></div><div>
|
||||||
|
<p>Vorgehen:</p></div><div>
|
||||||
|
<p>Wer Interesse hat, bitte eine Email an mich und ich versende Zugänge zu den Beta-Test-Systemen. Nach ca. 2 Wochen werden wir die Erweiterungen in der Version 1.0 veröffentlichen.</p></div><div> <p> </p></div><div> <p> </p></div><div>
|
||||||
|
<p><b>c) XXXX Entwickler Schulung</b></p></div><div>
|
||||||
|
<p>Weil es immer wieder Thema war, falls jemand Interesse hat, das XXXX bietet nun auch OTRS Entwickler Schulungen an (<a href="http://www.example.com/kurs/xxxx_entwickler/" rel="nofollow noreferrer noopener" target="_blank">http://www.example.com/kurs/xxxx_entwickler/</a>).</p></div><div> <p> </p></div><div> <p> </p></div><div>
|
||||||
|
<p><b>d) Genelle Fragen?</b></p></div><div>
|
||||||
|
<p>Haben sich beim ein oder anderen generell noch Fragen aufgetan?</p></div><div> <p> </p></div><div> <p> </p></div><div>
|
||||||
|
<p>Viele Grüße!</p></div><div> <p> </p></div><div>
|
||||||
|
<div>
|
||||||
|
<p>-Fritz</p></div><p>On May 2, 2012, at 14:25 , John Smith wrote:<br><br></p><p>Moin Moin,<br><br>die Antwort ist zwar etwas spät, aber nach der Schulung war ich krank und danach<br>hatte ich viel zu tun auf der Arbeit, sodass ich keine Zeit für XXXX hatte.<br>Ich denke das ist allgemein das Problem, wenn sowas nebenbei gemacht werden muss.<br><br>Wie auch immer, danke für die mail mit dem ITSM Zusatz auch wenn das zur Zeit bei der Example nicht relevant ist.<br><br>Ich habe im XXXX Wiki den Punkt um die Vorlagen angefügt.<br>Ticket Template von John Bäcker<br>Bei uns habe ich das Ticket Template von John Bäcker in der Version 0.1.96 unter XXXX 3.0.10 implementiert. <br><br>Fritz wollte sich auch um das andere Ticket Template Modul kümmern und uns zur Verfügung stellen, welches unter XXXX 3.0 nicht lauffähig sein sollte.<br><br>Im Wiki kann ich die LDAP Muster Konfigdatei nicht finden.<br>Hat die jemand von euch zufälligerweise ?<br><br>Danke und Gruß<br>John Smith<br><br>Am 4. April 2012 08:24 schrieb Smith, John Marian <john.smith@example.com>:<br>Hallo zusammen,<br><br>ich hoffe Ihr seid noch gut nach Hause gekommen am Mittwoch. Der XXX Kurs Donnerstag und Freitag war noch ganz gut, wobei ich mir den letzten halben Tag eigentlich hätte schenken können.<br><br>Soweit ich weiß arbeitet Ihr nicht mit XXX? Falls doch habe ich hier eine tolle (eigentlich) kostenpflichtige Erweiterung für Euch.<br><br>Es handelt sich um eine programmiertes Paket von der XXXX AG. Die Weitergabe ist legal.<br><br>Mit dem Paket kann man Anhänge an CI’s (Configuration Items) verknüpfen. Das ist sehr praktisch wenn man zum Beispiel Rechnungen an Server, Computern und und und anhängen möchte.<br><br>Der Dank geht an Frank Linden, der uns das Paket kostenlos zur Verfügung gestellt hat.<br><br>Viele Grüße aus Someware<br><br>John<br><br>_________________________<br>SysAdmin<br>John Marian Smith<br>IT-Management<br><br>Example GmbH & Co. KG<br>Der Provider für<br>Mehrwertdienste & YYY<br><br>Someware 23<br>XXXXX Someware<br><br>Tel. (01802) XX XX XX - 42<br>Fax (01802) XX XX XX - 99<br>nur 6 Cent je Anruf aus dem dt. Festnetz,<br>max. 42 Cent pro Min. aus dem Mobilfunknetz<br><br>E-Mail john.smith@Example.de<br>Web <a href="http://www.Example.de" rel="nofollow noreferrer noopener" target="_blank">www.Example.de</a><br>Amtsgericht Hannover HRA xxxxxxxx<br>Komplementärin: Example Verwaltungs- GmbH<br>Vertreten durch: Somebody, Somebody<br>Amtsgericht Someware HRB XXX XXX<br><br>_________________________ <br>Highlights der Example Contact Center-Suite:<br>Virtual XXX&Power-XXX, Self-Services&XXX-Portale,<br>XXX-/Web-Kundenbefragungen, CRM, PEP, YYY</p></div></div>
|
27
test/data/mail/mail004.yml
Normal file
27
test/data/mail/mail004.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"Günther Katja | Example GmbH" <k.guenther@example.com>'
|
||||||
|
from_email: k.guenther@example.com
|
||||||
|
from_display_name: Günther Katja | Example GmbH
|
||||||
|
subject: 'AW: Ticket Templates [Ticket#11168]'
|
||||||
|
content_type: text/plain
|
||||||
|
body: |+
|
||||||
|
Hallo Katja,
|
||||||
|
|
||||||
|
super! Ich freu mich!
|
||||||
|
|
||||||
|
Wir würden gerne die Präsentation/Einführung in die Ticket Templates per Screensharing oder zumindest per Telefon machen.
|
||||||
|
|
||||||
|
Mögliche Termine:
|
||||||
|
o Do, 10.05.2012 15:00-16:00
|
||||||
|
o Fr, 11.05.2012 13:00-14:00
|
||||||
|
o Di, 15.05.2012 17:00-18:00
|
||||||
|
|
||||||
|
Über Feedback würde ich mich freuen!
|
||||||
|
|
||||||
|
PS: Zur besseren Übersicht habe ich ein Ticket erstellt. :) Im Footer sind unsere geschäftlichen Kontaktdaten (falls diese irgendwann einmal benötigt werden sollten), mehr dazu in ein paar Tagen.
|
||||||
|
|
||||||
|
Liebe Grüße!
|
||||||
|
|
||||||
|
-Martin
|
||||||
|
|
||||||
|
|
6
test/data/mail/mail005.yml
Normal file
6
test/data/mail/mail005.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: marc.smith@example.com (Marc Smith)
|
||||||
|
from_email: marc.smith@example.com
|
||||||
|
from_display_name: Marc Smith
|
||||||
|
subject: 'Re: XXXX Betatest Ticket Templates [Ticket#11162]'
|
||||||
|
content_type: text/plain
|
10
test/data/mail/mail006.yml
Normal file
10
test/data/mail/mail006.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"Hans BÄKOSchönland" <me@bogen.net>'
|
||||||
|
from_email: me@bogen.net
|
||||||
|
from_display_name: Hans BÄKOSchönland
|
||||||
|
subject: 'utf8: 使って / ISO-8859-1: Priorität" / cp-1251: Сергей Углицких'
|
||||||
|
content_type: text/html
|
||||||
|
body: '<p>this is a test</p><br><hr> <a href="http://localhost/8HMZENUS/2737??PS="
|
||||||
|
rel="nofollow noreferrer noopener" target="_blank" title="http://localhost/8HMZENUS/2737??PS=">Compare
|
||||||
|
Cable, DSL or Satellite plans: As low as $2.95. </a> <br> <br> Test1:– <br> Test2:&
|
||||||
|
<br> Test3:∋ <br> Test4:& <br> Test5:='
|
22
test/data/mail/mail007.yml
Normal file
22
test/data/mail/mail007.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Eike.Ehringer@example.com
|
||||||
|
from_email: Eike.Ehringer@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: AW:Installation [Ticket#11392]
|
||||||
|
content_type: text/html
|
||||||
|
body: "Hallo.<br>Jetzt muss ich dir noch kurzfristig absagen für morgen.<br>Lass uns
|
||||||
|
evtl morgen Tel.<br><br>Mfg eike <br><br><div>\n<div>Martin Edenhofer via Znuny
|
||||||
|
Team --- Installation [Ticket#11392] ---</div><div>\n<br><table border=\"0\" cellspacing=\"0\"
|
||||||
|
cellpadding=\"0\">\n<tr>\n<td>Von:</td>\n<td>\"Martin Edenhofer via Znuny Team\"
|
||||||
|
<support@example.com></td>\n</tr>\n<tr>\n<td>An</td>\n<td>eike.xx@xx-corpxx.com</td>\n</tr>\n<tr>\n<td>Datum:</td>\n<td>Mi.,
|
||||||
|
13.06.2012 14:30</td>\n</tr>\n<tr>\n<td>Betreff</td>\n<td>Installation [Ticket#11392]</td>\n</tr>\n</table>\n<hr>\n<br><pre>Hi
|
||||||
|
Eike,\n\nanbei wie gestern telefonisch besprochen Informationen zur Vorbereitung.\n\na)
|
||||||
|
Installation von <a href=\"http://ftp.gwdg.de/pub/misc/zammad/RPMS/fedora/4/zammad-3.0.13-01.noarch.rpm\"
|
||||||
|
rel=\"nofollow noreferrer noopener\" target=\"_blank\">http://ftp.gwdg.de/pub/misc/zammad/RPMS/fedora/4/zammad-3.0.13-01.noarch.rpm</a>
|
||||||
|
(dieses RPM ist RHEL kompatible) und dessen Abhängigkeiten.\n\nb) Installation von
|
||||||
|
\"mysqld\" und \"perl-DBD-MySQL\".\n\nDas wäre es zur Vorbereitung!\n\nBei Fragen
|
||||||
|
nur zu!\n\n -Martin\n\n--\nMartin Edenhofer\n\nZnuny GmbH // Marienstraße 11 //
|
||||||
|
10117 Berlin // Germany\n\nP: +49 (0) 30 60 98 54 18-0\nF: +49 (0) 30 60 98 54 18-8\nW:
|
||||||
|
<a href=\"http://example.com\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">http://example.com</a>
|
||||||
|
\n\nLocation: Berlin - HRB 139852 B Amtsgericht Berlin-Charlottenburg\nManaging
|
||||||
|
Director: Martin Edenhofer\n\n</pre>\n</div></div>"
|
72
test/data/mail/mail008.yml
Normal file
72
test/data/mail/mail008.yml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Franz.Schaefer@example.com
|
||||||
|
from_email: Franz.Schaefer@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: 'could not rename: ZZZAAuto'
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<img src="cid:_1_08FC9B5808FC7D5C004AD64FC1257A28">
|
||||||
|
<br>
|
||||||
|
<br>Gravierend?<br> <table>
|
||||||
|
<tr>
|
||||||
|
<td>Mit freundlichen Grüßen</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<b>Franz Schäfer</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manager Information Systems</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td> Telefon </td>
|
||||||
|
<td> +49 000 000 8565 </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">christian.schaefer@example.com</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<b>Example Stoff GmbH</b>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Fakultaet </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Düsseldorfer Landstraße 395 </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> D-00000 Hof </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="http://www.example.com" rel="nofollow noreferrer noopener" target="_blank"><u>www.example.com</u></a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<hr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Geschäftsführung/Management Board: Jan Bauer (Vorsitzender/Chairman), Oliver Bauer, Heiko Bauer, Boudewijn Bauer </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> Sitz der Gesellschaft / Registered Office: Hof </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Registergericht / Commercial Register of the Local Court: HRB 0000 AG Hof</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
10
test/data/mail/mail009.yml
Normal file
10
test/data/mail/mail009.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Martin Edenhofer <martin@example.de>
|
||||||
|
from_email: martin@example.de
|
||||||
|
from_display_name: Martin Edenhofer
|
||||||
|
subject: 'AW: OTRS / Anfrage OTRS Einführung/Präsentation [Ticket#11545]'
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
Enjoy!<div>
|
||||||
|
<br><div>-Martin<br><span class="js-signatureMarker"></span><br>--<br>Old programmers never die. They just branch to a new address.<br>
|
||||||
|
</div><br><div><img src="cid:485376C9-2486-4351-B932-E2010998F579@home" style="width:640px;height:425px;"></div></div>
|
9
test/data/mail/mail010.yml
Normal file
9
test/data/mail/mail010.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Smith Sepp <smith@example.com>
|
||||||
|
from_email: smith@example.com
|
||||||
|
from_display_name: Smith Sepp
|
||||||
|
subject: Gruß aus Oberalteich
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<div>
|
||||||
|
<p>Herzliche Grüße aus Oberalteich sendet Herrn Smith</p><p> </p><p>Sepp Smith - Dipl.Ing. agr. (FH)</p><p>Geschäftsführer der example Straubing-Bogen</p><p>Klosterhof 1 | 94327 Bogen-Oberalteich</p><p>Tel: 09422-505601 | Fax: 09422-505620</p><p>Internet: <a href="http://example-straubing-bogen.de/" rel="nofollow noreferrer noopener" target="_blank">http://example-straubing-bogen.de</a></p><p>Facebook: <a href="http://facebook.de/examplesrbog" rel="nofollow noreferrer noopener" target="_blank">http://facebook.de/examplesrbog</a></p><p><b><img border="0" src="cid:image001.jpg@01CDB132.D8A510F0" alt="Beschreibung: Beschreibung: efqmLogo" style="width:60px;height:19px;"></b><b> - European Foundation für Quality Management</b></p><p> </p></div>
|
40
test/data/mail/mail011.yml
Normal file
40
test/data/mail/mail011.yml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
reply-to: serviceteam@cylex.de
|
||||||
|
from: CYLEX Newsletter <carina.merkant@cylex.de>
|
||||||
|
from_email: carina.merkant@cylex.de
|
||||||
|
from_display_name: CYLEX Newsletter
|
||||||
|
subject: Eine schöne Adventszeit für ZNUNY GMBH - ENTERPRISE SERVICES FÜR OTRS
|
||||||
|
to: enjoy_us@znuny.com
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<table border="0" cellpadding="0" style=" font-size: 14px;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p>
|
||||||
|
<a href="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-2/http://web2.cylex.de/advent2012?b2b" rel="nofollow noreferrer noopener" target="_blank" title="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-2/http://web2.cylex.de/advent2012?b2b"></a></p><p>Lieber CYLEX Eintragsinhaber,</p><p>das Jahr neigt sich dem Ende und die besinnliche Zeit beginnt laut Kalender mit dem<br> 1. Advent. Und wie immer wird es in der vorweihnachtlichen Zeit meist beruflich und privat<br> so richtig schön hektisch.</p><p>Um Ihre Weihnachtsstimmung in Schwung zu bringen kommen wir nun mit unserem Adventskalender ins Spiel. Denn 24 Tage werden Sie unsere netten Geschichten, Rezepte und Gewinnspiele sowie ausgesuchte Geschenktipps und Einkaufsgutscheine online begleiten. Damit lässt sich Ihre Freude auf das Fest garantiert mit jedem Tag steigern.</p><table style=" font-size: 14px;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="left" valign="middle"> Einen gemütlichen Start in die Adventszeit wünscht Ihnen</td>
|
||||||
|
<td align="right" valign="middle">
|
||||||
|
<a href="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-1/http://web2.cylex.de/advent2012?b2b" rel="nofollow noreferrer noopener" target="_blank" title="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-1/http://web2.cylex.de/advent2012?b2b"></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>Ihr CYLEX Team<br>
|
||||||
|
<br>
|
||||||
|
<strong>P.S.</strong> Damit Sie keinen Tag versäumen, empfehlen wir Ihnen den <a href="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-3/http://web2.cylex.de/advent2012?b2b" rel="nofollow noreferrer noopener" target="_blank" title="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-3/http://web2.cylex.de/advent2012?b2b">Link des Adventkalenders</a> in<br> Ihrer Lesezeichen-Symbolleiste zu ergänzen.</p><p> </p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table> <table cellspacing="0" cellpadding="0" style="color:#6578a0; font-size:10px;">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="left" style="text-align:left;"> Impressum <br> S.C. CYLEX INTERNATIONAL S.N.C.<br> Sat. Palota 119/A RO 417516 Palota Romania <br> Tel.: +49 208/62957-0 | <br> Geschäftsführer: Francisc Osvald<br> Handelsregister: J05/1591/2009<br> USt.IdNr.: RO26332771 <br>
|
||||||
|
<br> serviceteam@cylex.de<br>
|
||||||
|
<a href="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-98/http://web2.cylex.de/Homepage/Home.asp" rel="nofollow noreferrer noopener" target="_blank" title="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-98/http://web2.cylex.de/Homepage/Home.asp">Homepage</a><br>
|
||||||
|
<a href="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-99/http://newsletters.cylex.de/unsubscribe.aspx?uid=4134001&d=www.cylex.de&e=enjoy@znuny.com&sc=3009&l=d" rel="nofollow noreferrer noopener" target="_blank" title="http://newsletters.cylex.de/ref/www.cylex.de/sid-105/uid-4134001/lid-99/http://newsletters.cylex.de/unsubscribe.aspx?uid=4134001&d=www.cylex.de&e=enjoy@znuny.com&sc=3009&l=d">Newsletter abbestellen</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
16
test/data/mail/mail012.yml
Normal file
16
test/data/mail/mail012.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Alex.Smith@example.com
|
||||||
|
from_email: Alex.Smith@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: 'AW: Agenda [Ticket#11995]'
|
||||||
|
to: example@znuny.com
|
||||||
|
content_type: text/html
|
||||||
|
body: |-
|
||||||
|
<div>
|
||||||
|
<p>Hallo Herr Edenhofer,</p><p> </p><p>möglicherweise haben wir für unsere morgige Veranstaltung ein Problem mit unserer Develop-Umgebung.<br> Der Kollege Smith wollte uns noch die Möglichkeit geben, direkt auf die Datenbank zugreifen zu können, hierzu hat er Freitag noch einige Einstellungen vorgenommen und uns die Zugangsdaten mitgeteilt. Eine der Änderungen hatte aber offenbar zur Folge, dass ein Starten der Develop-Anwendung nicht mehr möglich ist (s. Fehlermeldung)<br>
|
||||||
|
<img src="cid:image002.png@01CDD14F.29D467A0" style="width:577px;height:345px;"></p><p> </p><p>Herr Smith ist im Urlaub, er wurde von seinen Datenbank-Kollegen kontaktiert aber offenbar lässt sich nicht mehr 100%ig rekonstruieren, was am Freitag noch verändert wurde.<br> Meinen Sie, dass Sie uns bei der Behebung der o. a. Störung morgen helfen können? Die Datenbank-Kollegen werden uns nach besten Möglichkeiten unterstützen, Zugriff erhalten wir auch.</p><p> </p><p>Mit freundlichen Grüßen</p><p> </p><p>Alex Smith<br>
|
||||||
|
<br> Abteilung IT-Strategie, Steuerung & Support<br> im Bereich Informationstechnologie<br>
|
||||||
|
<br> Example – Example GmbH<br> (Deutsche Example)<br> Longstreet 5<br> 11111 Frankfurt am Main<br>
|
||||||
|
<br> Telefon: (069) 11 1111 – 11 30</p><p>Telefon ServiceDesk: (069) 11 1111 – 12 22<br> Telefax: (069) 11 1111 – 14 85<br> Internet: <a href="http://www.example.com/" title="http://www.example.com/" rel="nofollow noreferrer noopener" target="_blank">www.example.com</a></p><p> </p><span class="js-signatureMarker"></span><p>-----Ursprüngliche Nachricht-----<br> Von: Martin Edenhofer via Znuny Sales [mailto:example@znuny.com] <br> Gesendet: Freitag, 30. November 2012 13:50<br> An: Smith, Alex<br> Betreff: Agenda [Ticket#11995]</p><p> </p><p>Sehr geehrte Frau Smith,</p><p> </p><p>ich habe (wie telefonisch avisiert) versucht eine Agenda für nächste Woche zusammen zu stellen.</p><p> </p><p>Leider ist es mir dies Inhaltlich nur unzureichend gelungen (es gibt zu wenig konkrete Anforderungen im Vorfeld :) ).</p><p> </p><p>Dadurch würde ich gerne am Dienstag als erste Amtshandlung (mit Herrn Molitor im Boot) die Anforderungen und Ziele der zwei Tage, Mittelfristig und Langfristig definieren. Aufgrund dessen können wir die Agenda der zwei Tage fixieren. Inhaltlich können wir (ich) alles abdecken, von daher gibt es hier keine Probleme. ;)</p><p> </p><p>Ist dies für Sie so in Ordnung?</p><p> </p><p>Für Fragen stehe ich gerne zur Verfügung!</p><p> </p><p>Ich freue mich auf Dienstag,</p><p> </p><p>Martin Edenhofer</p><p> </p><p>--</p><p>Enterprise Services for OTRS</p><p> </p><p>Znuny GmbH // Marienstraße 11 // 10117 Berlin // Germany</p><p> </p><p>P: +49 (0) 30 60 98 54 18-0</p><p>F: +49 (0) 30 60 98 54 18-8</p><p>W: <a href="http://znuny.com" rel="nofollow noreferrer noopener" target="_blank">http://znuny.com</a>
|
||||||
|
</p><p> </p><p>Location: Berlin - HRB 139852 B Amtsgericht Berlin-Charlottenburg Managing Director: Martin Edenhofer</p></div><div>
|
||||||
|
<p>-------------------------------------------------------------------------------------------------</p><p>Rechtsform: GmbH</p><p>Geschaeftsfuehrer: Dr. Carl Heinz Smith, Dr. Carsten Smith</p><p>Sitz der Gesellschaft und Registergericht: Frankfurt/Main, HRB 11111</p><p>Alleiniger Gesellschafter: Bundesrepublik Deutschland,</p><p>vertreten durch das XXX der Finanzen.</p></div>
|
8
test/data/mail/mail013.yml
Normal file
8
test/data/mail/mail013.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: thomas.smith@example.com
|
||||||
|
from_email: thomas.smith@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: 'Antwort: Probleme ADB / Anlegen von Tickets [Ticket#111079]'
|
||||||
|
to: q1@znuny.com
|
||||||
|
content_type: text/html
|
||||||
|
body: "<p>JA</p>"
|
14
test/data/mail/mail014.yml
Normal file
14
test/data/mail/mail014.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"Müller, Bernd" <Bernd.Mueller@example.com>'
|
||||||
|
from_email: Bernd.Mueller@example.com
|
||||||
|
from_display_name: Müller, Bernd
|
||||||
|
subject: 'AW: OTRS [Ticket#118192]'
|
||||||
|
to: "'Martin Edenhofer via Znuny Sales' <sales@znuny.com>"
|
||||||
|
content_type: text/plain
|
||||||
|
body: |
|
||||||
|
äöüß ad asd
|
||||||
|
|
||||||
|
-Martin
|
||||||
|
|
||||||
|
--
|
||||||
|
Old programmers never die. They just branch to a new address.
|
7
test/data/mail/mail015.yml
Normal file
7
test/data/mail/mail015.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"Sara.Gang" <ynbe.ctrhk@gmail.com>'
|
||||||
|
from_email: ynbe.ctrhk@gmail.com
|
||||||
|
from_display_name: Sara.Gang
|
||||||
|
subject: 绩效管理,究竟谁错了
|
||||||
|
to: info42@znuny.com
|
||||||
|
content_type: text/plain
|
5
test/data/mail/mail016.yml
Normal file
5
test/data/mail/mail016.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from_email: vipyimin@126.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: "【 直通美国排名第49大学 成功后付费 】"
|
||||||
|
to: '"enterprisemobility.apacservice" <enterprisemobility.apacservice@motorola.com>'
|
8
test/data/mail/mail017.yml
Normal file
8
test/data/mail/mail017.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"都琹" <ghgbwum@185.com.cn>'
|
||||||
|
from_email: ghgbwum@185.com.cn
|
||||||
|
from_display_name: 都琹
|
||||||
|
subject: "【专业为您注册香港及海外公司(好处多多)】 人物
|
||||||
|
\ 互联网事百度新闻独家出品传媒换一批捷克戴维斯杯决赛前任命临时领队 前领队因病住院最新:盖世汽车讯 11月6日,通用汽车宣布今年10月份在华销量...减持三特索道
|
||||||
|
孟凯将全力发展湘鄂情江青摄影作品科技日报讯 (记者过国忠 通讯员陈飞燕)江苏省无线电科学研究所有限公司院士工作站日前正式建...[详细]"
|
||||||
|
to: info@znuny.com
|
6
test/data/mail/mail018.yml
Normal file
6
test/data/mail/mail018.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: postmaster@example.com
|
||||||
|
from_email: postmaster@example.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: "Benachrichtung \tzum \t=?unicode-1-1-utf-7?Q?+ANw-bermittlungsstatus \t(Fehlgeschlagen)?="
|
||||||
|
to: sales@znuny.org
|
6
test/data/mail/mail019.yml
Normal file
6
test/data/mail/mail019.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: '"我" <>'
|
||||||
|
from_email: vipyiming@126.com
|
||||||
|
from_display_name: ''
|
||||||
|
subject: "《欧美简讯》"
|
||||||
|
to: 377861373 <377861373@qq.com>
|
85
test/data/mail/mail020.yml
Normal file
85
test/data/mail/mail020.yml
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Health and Care-Mall <drugs-cheapest8@sicor.com>
|
||||||
|
from_email: drugs-cheapest8@sicor.com
|
||||||
|
from_display_name: Health and Care-Mall
|
||||||
|
subject: The Highest Grade Drugs And EXTRA LOW Price .
|
||||||
|
to: info2@znuny.com
|
||||||
|
body: |-
|
||||||
|
________________________________________________________________________Yeah but even when they. Beth liî ed her neck as well <br>
|
||||||
|
<div>
|
||||||
|
<table border="0" cellspacing="5" style="color:#e3edea; background-color:#eee0ec; font-size:1px;">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">óû5a<span style="color:#dd7f6f;">H</span>w5³½<span style="color:#dd7f6f;">I</span>ΨµÁx<span style="color:#dd7f6f;">G</span>⌊o8K<span style="color:#dd7f6f;">H</span>Cmς9<span style="color:#dd7f6f;">-</span>Ö½23<span style="color:#dd7f6f;">Q</span>gñV6<span style="color:#dd7f6f;">U</span>AD¿ù<span style="color:#dd7f6f;">A</span>X←t¨<span style="color:#dd7f6f;">L</span>f7⊕®<span style="color:#dd7f6f;">I</span>r²r½<span style="color:#dd7f6f;">T</span>LA5p<span style="color:#dd7f6f;">Y</span>JhjV<span style="color:#dd7f6f;"> </span>gPnã<span style="color:#dd7f6f;">M</span>36V®<span style="color:#dd7f6f;">E</span>89RU<span style="color:#dd7f6f;">D</span>ΤÅ©È<span style="color:#dd7f6f;">I</span>9æsà<span style="color:#dd7f6f;">C</span>ΘYEϒ<span style="color:#dd7f6f;">A</span>fg∗b<span style="color:#dd7f6f;">T</span>¡1∫r<span style="color:#dd7f6f;">I</span>oiš¦<span style="color:#dd7f6f;">O</span>5oUI<span style="color:#dd7f6f;">N</span>±Isæ<span style="color:#dd7f6f;">S</span>عPp<span style="color:#dd7f6f;"> </span>Ÿÿq1<span style="color:#dd7f6f;">F</span>Χ⇑eG<span style="color:#dd7f6f;">O</span>z⌈F³<span style="color:#dd7f6f;">R</span>98y§<span style="color:#dd7f6f;"> </span>74”l<span style="color:#dd7f6f;">T</span>r8r§<span style="color:#dd7f6f;">H</span>ÐæuØ<span style="color:#dd7f6f;">E</span>ÛPËq<span style="color:#dd7f6f;"> </span>Vmkf<span style="color:#dd7f6f;">B</span>∫SKN<span style="color:#dd7f6f;">E</span>lst4<span style="color:#dd7f6f;">S</span>∃Á8ü<span style="color:#dd7f6f;">T</span>ðG°í<span style="color:#dd7f6f;"> </span>lY9å<span style="color:#dd7f6f;">P</span>u×8><span style="color:#dd7f6f;">R</span>Ò¬⊕Μ<span style="color:#dd7f6f;">I</span>ÙzÙC<span style="color:#dd7f6f;">C</span>4³ÌQ<span style="color:#dd7f6f;">E</span>ΡºSè<span style="color:#dd7f6f;">!</span>XgŒs.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" colspan="2">çγ⇓B<span style="color:#2a0984;"><a href="http://pxmzcgy.storeprescription.ru?zz=fkxffti" rel="nofollow noreferrer noopener" target="_blank" title="http://pxmzcgy.storeprescription.ru?zz=fkxffti"><b><span style="color:#fbe6bf;">cwsp</span>C L I C K H E R E<span style="color:#fdedb6;">ëe3¸</span> !</b></a></span>Calm dylan for school today.<br>Closing the nursery with you down. Here and made the mess. Maybe the oï from under his mother. Song of course beth touched his pants.<br>When someone who gave up from here. Feel of god knows what.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">TBϖ∃<span style="color:#2a0984;">M</span>5T5Ε<span style="color:#2a0984;">E</span>f2û–<span style="color:#2a0984;">N</span>¶ÁvΖ<span style="color:#2a0984;">'</span>®⇓∝5<span style="color:#2a0984;">S</span>ÐçË5<span style="color:#2a0984;"> </span>Χ0jΔ<span style="color:#2a0984;">H</span>bAgþ<span style="color:#2a0984;">E</span>—2i6<span style="color:#2a0984;">A</span>2lD⇑<span style="color:#2a0984;">L</span>GjÓn<span style="color:#2a0984;">T</span>Oy»¦<span style="color:#2a0984;">H</span>ëτ9’<span style="color:#2a0984;">:</span>Their mother and tugged it seemed like</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>d3Rs<span style="color:#2a0984;">V</span>¶HÓΘ<span style="color:#2a0984;">i</span>¯B∂g<span style="color:#2a0984;">a</span>x1bî<span style="color:#2a0984;">g</span>dHä3<span style="color:#2a0984;">r</span>ýJÿ1<span style="color:#2a0984;">a</span>IKDz<span style="color:#2a0984;"> </span>n1jf<span style="color:#2a0984;">a</span>Tk³V<span style="color:#2a0984;">s</span>395ß<span style="color:#2a0984;"> </span>C˜lB<span style="color:#2a0984;">l</span>‘mxG<span style="color:#2a0984;">o</span>0√úX<span style="color:#2a0984;">w</span>T8Ya<span style="color:#2a0984;"> </span>õ8ks<span style="color:#2a0984;">a</span>∫f·ℵ<span style="color:#2a0984;">s</span>”6ÑQ<span style="color:#2a0984;"> </span>ÍAd7<span style="color:#2a0984;">$</span>p32d<span style="color:#2a0984;">1</span>e∏æe<span style="color:#2a0984;">.</span>0”×6<span style="color:#2a0984;">1</span>aîΚ6<span style="color:#2a0984;">3</span>αSMû</td>
|
||||||
|
<td>Nf5É<span style="color:#2a0984;">C</span>dL∪1<span style="color:#2a0984;">i</span>↔xca<span style="color:#2a0984;">a</span>5êR3<span style="color:#2a0984;">l</span>6Lc3<span style="color:#2a0984;">i</span>ãz16<span style="color:#2a0984;">s</span>ó9èU<span style="color:#2a0984;"> </span>zDE²<span style="color:#2a0984;">a</span>EȨg<span style="color:#2a0984;">s</span>25ËÞ<span style="color:#2a0984;"> </span>hE§c<span style="color:#2a0984;">l</span>⊃¢¢Â<span style="color:#2a0984;">o</span>ÒµB<span style="color:#2a0984;">w</span>²zF©<span style="color:#2a0984;"> </span>qÏkõ<span style="color:#2a0984;">a</span>XUiu<span style="color:#2a0984;">s</span>1r0⊆<span style="color:#2a0984;"> </span>d•∈ø<span style="color:#2a0984;">$</span>¢Z2F<span style="color:#2a0984;">1</span>28l<span style="color:#2a0984;">.</span>07d5<span style="color:#2a0984;">6</span>PÚl2<span style="color:#2a0984;">5</span>JAO6</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>45lo<span style="color:#2a0984;">V</span>óiv1<span style="color:#2a0984;">i</span>2ãΥ⌊<span style="color:#2a0984;">a</span>ð⊃d2<span style="color:#2a0984;">g</span>ÃΥ3™<span style="color:#2a0984;">r</span>ÎÍu¸<span style="color:#2a0984;">a</span>WjO8<span style="color:#2a0984;"> </span>n40–<span style="color:#2a0984;">S</span>oyè2<span style="color:#2a0984;">u</span>¡∅Î3<span style="color:#2a0984;">p</span>¢JΜN<span style="color:#2a0984;">e</span>Ìé×j<span style="color:#2a0984;">r</span>áÒrΚ<span style="color:#2a0984;"> </span>1ÌÓ9<span style="color:#2a0984;">A</span>úrAk<span style="color:#2a0984;">c</span>8nuE<span style="color:#2a0984;">t</span>l22a<span style="color:#2a0984;">i</span>‡OB8<span style="color:#2a0984;">v</span>Sbéσ<span style="color:#2a0984;">e</span>ιõq1<span style="color:#2a0984;">+</span>65cw<span style="color:#2a0984;"> </span>Òs8U<span style="color:#2a0984;">a</span>ò4Pr<span style="color:#2a0984;">s</span>E1y8<span style="color:#2a0984;"> </span>〈fME<span style="color:#2a0984;">l</span>hϒ⋅J<span style="color:#2a0984;">o</span>8pmz<span style="color:#2a0984;">w</span>jˆN¥<span style="color:#2a0984;"> </span>wv39<span style="color:#2a0984;">a</span>W¡Wt<span style="color:#2a0984;">s</span>vuU3<span style="color:#2a0984;"> </span>1aœ³<span style="color:#2a0984;">$</span>éΝnR<span style="color:#2a0984;">2</span>OÏ⌉B<span style="color:#2a0984;">.</span>∀þc→<span style="color:#2a0984;">5</span>Ê9χw<span style="color:#2a0984;">5</span>pÃ⁄N</td>
|
||||||
|
<td>fHGF<span style="color:#2a0984;">V</span>fE³ã<span style="color:#2a0984;">i</span>σjGp<span style="color:#2a0984;">a</span>5¶kg<span style="color:#2a0984;">g</span>¡ìcW<span style="color:#2a0984;">r</span>Uq5æ<span style="color:#2a0984;">a</span>kx2h<span style="color:#2a0984;"> </span>0Fè4<span style="color:#2a0984;">P</span>¸ÕLñ<span style="color:#2a0984;">r</span>n22Ï<span style="color:#2a0984;">o</span>þÝÐH<span style="color:#2a0984;">f</span>oRb2<span style="color:#2a0984;">e</span>Uαw6<span style="color:#2a0984;">s</span>ñN‾w<span style="color:#2a0984;">s</span>¶§3Β<span style="color:#2a0984;">i</span>òX¶¸<span style="color:#2a0984;">o</span>fgtH<span style="color:#2a0984;">n</span>R⊥3â<span style="color:#2a0984;">a</span>se9á<span style="color:#2a0984;">l</span>F¿H5<span style="color:#2a0984;"> </span>à6BÁ<span style="color:#2a0984;">a</span>⊃2iϒ<span style="color:#2a0984;">s</span>ô¡ói<span style="color:#2a0984;"> </span>ÅkMy<span style="color:#2a0984;">l</span>ÚJ¾Ä<span style="color:#2a0984;">o</span>Q–0ℑ<span style="color:#2a0984;">w</span>vmùþ<span style="color:#2a0984;"> </span>ˈμ"<span style="color:#2a0984;">a</span>Q7jV<span style="color:#2a0984;">s</span>e6Ðf<span style="color:#2a0984;"> </span>«hÜp<span style="color:#2a0984;">$</span>Lâr£<span style="color:#2a0984;">3</span>i1tÚ<span style="color:#2a0984;">.</span>323h<span style="color:#2a0984;">5</span>qP8g<span style="color:#2a0984;">0</span>♥÷R÷</td> </tr>
|
||||||
|
<tr>
|
||||||
|
<td>·iƒP<span style="color:#2a0984;">V</span>1Β∋ø<span style="color:#2a0984;">i</span>F¤RÃ<span style="color:#2a0984;">a</span>4v3â<span style="color:#2a0984;">g</span>L9¢w<span style="color:#2a0984;">r</span>¨7ø×<span style="color:#2a0984;">a</span>Ïû0η<span style="color:#2a0984;"> </span>þ1àß<span style="color:#2a0984;">S</span>tuÞ³<span style="color:#2a0984;">u</span>7á¡l<span style="color:#2a0984;">p</span>ÑocE<span style="color:#2a0984;">e</span>·SLl<span style="color:#2a0984;">r</span>VàXj<span style="color:#2a0984;"> </span>⊥Uµ¢<span style="color:#2a0984;">F</span>¬48ð<span style="color:#2a0984;">o</span>v7¨A<span style="color:#2a0984;">r</span>m×4Í<span style="color:#2a0984;">c</span>ùVwÞ<span style="color:#2a0984;">e</span>1§⊇N<span style="color:#2a0984;"> </span>ÂÛ4ä<span style="color:#2a0984;">a</span>LþZ2<span style="color:#2a0984;">s</span>ki×5<span style="color:#2a0984;"> </span>c€pB<span style="color:#2a0984;">l</span>ûù6∂<span style="color:#2a0984;">o</span>lÃfÚ<span style="color:#2a0984;">w</span>Kß3Ñ<span style="color:#2a0984;"> </span>4iíl<span style="color:#2a0984;">a</span>4C³ê<span style="color:#2a0984;">s</span>REÕ1<span style="color:#2a0984;"> </span>ãeIó<span style="color:#2a0984;">$</span>âz8t<span style="color:#2a0984;">4</span>42fG<span style="color:#2a0984;">.</span>¸1≤¸<span style="color:#2a0984;">2</span>F’Ã1<span style="color:#2a0984;">5</span>2in⊄</td>
|
||||||
|
<td>Tl©ë<span style="color:#2a0984;">C</span>2v7C<span style="color:#2a0984;">i</span>7·X8<span style="color:#2a0984;">a</span>×ú5N<span style="color:#2a0984;">l</span>þU〉ι<span style="color:#2a0984;">i</span>cO∑«<span style="color:#2a0984;">s</span>·iKN<span style="color:#2a0984;"> </span>Uuϒj<span style="color:#2a0984;">S</span>Ãj5Ý<span style="color:#2a0984;">u</span>÷Jü§<span style="color:#2a0984;">p</span>n5°§<span style="color:#2a0984;">e</span>¥Û3℘<span style="color:#2a0984;">r</span>ÆW‡ò<span style="color:#2a0984;"> </span>J‹S7<span style="color:#2a0984;">A</span>1j0s<span style="color:#2a0984;">c</span>&ºpk<span style="color:#2a0984;">t</span>·qqø<span style="color:#2a0984;">i</span>Z56½<span style="color:#2a0984;">v</span>n8¨∗<span style="color:#2a0984;">e</span>îØQ3<span style="color:#2a0984;">+</span>7Î3Š<span style="color:#2a0984;"> </span>∑RkL<span style="color:#2a0984;">a</span>KXËa<span style="color:#2a0984;">s</span>ÐsÌ2<span style="color:#2a0984;"> </span>ïǶ<span style="color:#2a0984;">l</span>Däz8<span style="color:#2a0984;">o</span>ã78w<span style="color:#2a0984;">w</span>U–ÀC<span style="color:#2a0984;"> </span>T6Uû<span style="color:#2a0984;">a</span>ϒ938<span style="color:#2a0984;">s</span>Ì0Gÿ<span style="color:#2a0984;"> </span>Oxó∈<span style="color:#2a0984;">$</span>98‘R<span style="color:#2a0984;">2</span>ÂHï5<span style="color:#2a0984;">.</span>ÒL6b<span style="color:#2a0984;">9</span>θrδÜ<span style="color:#2a0984;">9</span>2f9j</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Please matt on his neck. Okay matt huï ed into your mind</td>
|
||||||
|
<td>Since her head to check dylan. Where dylan matt got up there</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">1ȱΑ<span style="color:#2a0984;">A</span>YQªd<span style="color:#2a0984;">N</span>¬ÚϒX<span style="color:#2a0984;">T</span>00Àv<span style="color:#2a0984;">I</span>∨ío8<span style="color:#2a0984;">-</span>½b®8<span style="color:#2a0984;">A</span>ΕºV4<span style="color:#2a0984;">L</span>gÕ↑7<span style="color:#2a0984;">L</span>Ktgc<span style="color:#2a0984;">E</span>iwy<span style="color:#2a0984;">R</span>5Yýæ<span style="color:#2a0984;">G</span>RA1°<span style="color:#2a0984;">I</span>¿0Cï<span style="color:#2a0984;">C</span>àTiü<span style="color:#2a0984;">/</span>þwc0<span style="color:#2a0984;">A</span>x211<span style="color:#2a0984;">S</span>ÜÂùŒ<span style="color:#2a0984;">T</span>Á2êò<span style="color:#2a0984;">H</span>pNâù<span style="color:#2a0984;">M</span>6Ⱦ0<span style="color:#2a0984;">A</span>5Tb»<span style="color:#2a0984;">:</span>Simmons and now you really is what. Matt picked up this moment later that.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>25¯y<span style="color:#2a0984;">V</span>9ÙßY<span style="color:#2a0984;">e</span>g·↑D<span style="color:#2a0984;">n</span>J3l4<span style="color:#2a0984;">t</span>Ýæb1<span style="color:#2a0984;">o</span>s∏jl<span style="color:#2a0984;">l</span>÷iSÐ<span style="color:#2a0984;">i</span>wBÎ4<span style="color:#2a0984;">n</span>0ú1Ö<span style="color:#2a0984;"> </span>ªf÷Ñ<span style="color:#2a0984;">a</span>§1lø<span style="color:#2a0984;">s</span>uÚ8ê<span style="color:#2a0984;"> </span>2LCb<span style="color:#2a0984;">l</span>gvN½<span style="color:#2a0984;">o</span>¼oP3<span style="color:#2a0984;">w</span>n♠90<span style="color:#2a0984;"> </span>FZor<span style="color:#2a0984;">a</span>&M™x<span style="color:#2a0984;">s</span>ΚbbÂ<span style="color:#2a0984;"> </span>ç5Ãξ<span style="color:#2a0984;">$</span>Âô·×<span style="color:#2a0984;">2</span>iGæ∇<span style="color:#2a0984;">1</span>⊇Ξ¬3<span style="color:#2a0984;">.</span>0P0κ<span style="color:#2a0984;">5</span>3VÁö<span style="color:#2a0984;">0</span>3ÝYz</td>
|
||||||
|
<td>øX¢B<span style="color:#2a0984;">A</span>Z4Kw<span style="color:#2a0984;">d</span>duÜv<span style="color:#2a0984;">v</span>uB↑Β<span style="color:#2a0984;">a</span>Ä’TH<span style="color:#2a0984;">i</span>0—93<span style="color:#2a0984;">r</span>Zεj0<span style="color:#2a0984;"> </span>§rΜÅ<span style="color:#2a0984;">a</span>2·§<span style="color:#2a0984;">s</span>7¸Ιf<span style="color:#2a0984;"> </span>8⇓þo<span style="color:#2a0984;">l</span>W„6Ý<span style="color:#2a0984;">o</span>6yH¥<span style="color:#2a0984;">w</span>KZ∧6<span style="color:#2a0984;"> </span>21hÒ<span style="color:#2a0984;">a</span>KJ“ℜ<span style="color:#2a0984;">s</span>48IÌ<span style="color:#2a0984;"> </span>ÔÀ¬<span style="color:#2a0984;">$</span>ZΣ¹ü<span style="color:#2a0984;">2</span>ñÙ6B<span style="color:#2a0984;">4</span>2YMZ<span style="color:#2a0984;">.</span>Ô¹V¼<span style="color:#2a0984;">9</span>f·0å<span style="color:#2a0984;">5</span>4⌈R8</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>÷w"9<span style="color:#2a0984;">N</span>2gBÀ<span style="color:#2a0984;">a</span>ðSê¢<span style="color:#2a0984;">s</span>≅gGÔ<span style="color:#2a0984;">o</span>0Dn4<span style="color:#2a0984;">n</span>↵γ7⊗<span style="color:#2a0984;">e</span>S7eý<span style="color:#2a0984;">x</span>f3Jd<span style="color:#2a0984;"> </span>q÷CM<span style="color:#2a0984;">a</span>Íä³i<span style="color:#2a0984;">s</span>NMZp<span style="color:#2a0984;"> </span>zz0˜<span style="color:#2a0984;">l</span>ΚLw8<span style="color:#2a0984;">o</span>ë29w<span style="color:#2a0984;">w</span>¤§Qu<span style="color:#2a0984;"> </span>¥D⌈í<span style="color:#2a0984;">a</span>ýË¢é<span style="color:#2a0984;">s</span>J8Á¬<span style="color:#2a0984;"> </span>3oùÙ<span style="color:#2a0984;">$</span>¦1Nℜ<span style="color:#2a0984;">1</span>>Rét<span style="color:#2a0984;">7</span>WPM¨<span style="color:#2a0984;">.</span>¶8¹D<span style="color:#2a0984;">9</span>2k5D<span style="color:#2a0984;">9</span>∗8≈R</td>
|
||||||
|
<td>l©3ª<span style="color:#2a0984;">S</span>j·Ψ8<span style="color:#2a0984;">p</span>ΣïKù<span style="color:#2a0984;">i</span>6rrÔ<span style="color:#2a0984;">r</span>bÛu¬<span style="color:#2a0984;">i</span>2V∗∏<span style="color:#2a0984;">v</span>5ª10<span style="color:#2a0984;">a</span>27BÁ<span style="color:#2a0984;"> </span>Ú♦Ξs<span style="color:#2a0984;">a</span>9j3χ<span style="color:#2a0984;">s</span>a¯iΟ<span style="color:#2a0984;"> </span>Oi℘m<span style="color:#2a0984;">l</span>6ófé<span style="color:#2a0984;">o</span>wbz∀<span style="color:#2a0984;">w</span>A6ù→<span style="color:#2a0984;"> </span>ñ×bà<span style="color:#2a0984;">a</span>i´wb<span style="color:#2a0984;">s</span>♦βGs<span style="color:#2a0984;"> </span>Ù81i<span style="color:#2a0984;">$</span>iÀˆ1<span style="color:#2a0984;">2</span>⊃2wC<span style="color:#2a0984;">8</span>2n8o<span style="color:#2a0984;">.</span>µ3NJ<span style="color:#2a0984;">9</span>S1©Θ<span style="color:#2a0984;">0</span>P1Sd</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>What made no one in each time.</td>
|
||||||
|
<td>Mommy was thinking of course beth. Everything you need the same thing</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">PïEV<span style="color:#2a0984;">G</span>ÿ9sr<span style="color:#2a0984;">E</span>x⇐9o<span style="color:#2a0984;">N</span>3U®y<span style="color:#2a0984;">E</span>Îi2O<span style="color:#2a0984;">R</span>5kÇÿ<span style="color:#2a0984;">A</span>ΤηνU<span style="color:#2a0984;">L</span>P¿∧q<span style="color:#2a0984;"> </span>R5¿F<span style="color:#2a0984;">H</span>t7J6<span style="color:#2a0984;">E</span>»¯C∅<span style="color:#2a0984;">A</span>å∃aV<span style="color:#2a0984;">L</span>u∗¢t<span style="color:#2a0984;">T</span>〈2Ú<span style="color:#2a0984;">H</span>q9Né<span style="color:#2a0984;">:</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>⊥ÞÞ¨<span style="color:#2a0984;">T</span>¦ªBr<span style="color:#2a0984;">r</span>C7³2<span style="color:#2a0984;">a</span>dš6l<span style="color:#2a0984;">m</span>zb¨6<span style="color:#2a0984;">a</span>i07t<span style="color:#2a0984;">d</span>Bo×K<span style="color:#2a0984;">o</span>píΡÄ<span style="color:#2a0984;">l</span>j4Hy<span style="color:#2a0984;"> </span>ÝaÓ1<span style="color:#2a0984;">a</span>Öí∉Ó<span style="color:#2a0984;">s</span>1aá’<span style="color:#2a0984;"> </span>4Dk<span style="color:#2a0984;">l</span>eowË<span style="color:#2a0984;">o</span>3–1Í<span style="color:#2a0984;">w</span>jR≤Π<span style="color:#2a0984;"> </span>£RhÈ<span style="color:#2a0984;">a</span>fà7≅<span style="color:#2a0984;">s</span>ù6u2<span style="color:#2a0984;"> </span>8NLV<span style="color:#2a0984;">$</span>∪⇓»↓<span style="color:#2a0984;">1</span>Y¶2µ<span style="color:#2a0984;">.</span>vßÈ2<span style="color:#2a0984;">3</span>ÖS7û<span style="color:#2a0984;">0</span>Ün¬Ä</td>
|
||||||
|
<td>m5VK<span style="color:#2a0984;">Z</span>y3KÎ<span style="color:#2a0984;">i</span>ñë¹D<span style="color:#2a0984;">t</span>Ú2Hr<span style="color:#2a0984;">h</span>GaMv<span style="color:#2a0984;">r</span>5ïR«<span style="color:#2a0984;">o</span>Â1na<span style="color:#2a0984;">m</span>ΜwÐã<span style="color:#2a0984;">a</span>nFu8<span style="color:#2a0984;">x</span>7⌈sU<span style="color:#2a0984;"> </span>E4cv<span style="color:#2a0984;">a</span>£Âε™<span style="color:#2a0984;">s</span>7ΑGO<span style="color:#2a0984;"> </span>dA35<span style="color:#2a0984;">l</span>dñÌè<span style="color:#2a0984;">o</span>AξI1<span style="color:#2a0984;">w</span>XKïn<span style="color:#2a0984;"> </span>f¼x¾<span style="color:#2a0984;">a</span>∏7ff<span style="color:#2a0984;">s</span>†ìÖð<span style="color:#2a0984;"> </span>5msC<span style="color:#2a0984;">$</span>7Ët¦<span style="color:#2a0984;">0</span>z„n÷<span style="color:#2a0984;">.</span>it¡T<span style="color:#2a0984;">7</span>O8vt<span style="color:#2a0984;">5</span>¼8å·</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Jï1Ï<span style="color:#2a0984;">P</span>káO¶<span style="color:#2a0984;">r</span>nùrA<span style="color:#2a0984;">o</span>8s5∅<span style="color:#2a0984;">z</span>—4Rh<span style="color:#2a0984;">a</span>1®t˜<span style="color:#2a0984;">c</span>q5YΧ<span style="color:#2a0984;"> </span>ΤQÍr<span style="color:#2a0984;">a</span>Ñ⌋4¹<span style="color:#2a0984;">s</span>Ü5²§<span style="color:#2a0984;"> </span>ûVBι<span style="color:#2a0984;">l</span>uwói<span style="color:#2a0984;">o</span>L3ëB<span style="color:#2a0984;">w</span>£±1¶<span style="color:#2a0984;"> </span>5∈àá<span style="color:#2a0984;">a</span>1IÊ2<span style="color:#2a0984;">s</span>šÛÛÂ<span style="color:#2a0984;"> </span>G´7ρ<span style="color:#2a0984;">$</span>kJM8<span style="color:#2a0984;">0</span>∼∠ℵl<span style="color:#2a0984;">.</span>J1Km<span style="color:#2a0984;">3</span>2µÚ⊃<span style="color:#2a0984;">5</span>ã鼧</td>
|
||||||
|
<td>p°ÿ<span style="color:#2a0984;">A</span>¹NU0<span style="color:#2a0984;">c</span>¥xçf<span style="color:#2a0984;">o</span>〈Øác<span style="color:#2a0984;">m</span>14QG<span style="color:#2a0984;">p</span>HEj7<span style="color:#2a0984;">l</span>nDPV<span style="color:#2a0984;">i</span>eV2¶<span style="color:#2a0984;">a</span>Π2H7<span style="color:#2a0984;"> </span>²j26<span style="color:#2a0984;">a</span>zBSe<span style="color:#2a0984;">s</span>ë1c9<span style="color:#2a0984;"> </span>´2Ù¬<span style="color:#2a0984;">l</span>0nò¤<span style="color:#2a0984;">o</span>õâRV<span style="color:#2a0984;">w</span>¦X´Ï<span style="color:#2a0984;"> </span>αVõ<span style="color:#2a0984;">a</span>≅σ¼Z<span style="color:#2a0984;">s</span>§jJå<span style="color:#2a0984;"> </span>3pFN<span style="color:#2a0984;">$</span>¾Kf8<span style="color:#2a0984;">2</span>1YΟ7<span style="color:#2a0984;">.</span>3ÍY9<span style="color:#2a0984;">5</span>JΑqŸ<span style="color:#2a0984;">0</span>v9ÄQ</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ñ↑yj<span style="color:#2a0984;">P</span>Τ1u6<span style="color:#2a0984;">r</span>FwhN<span style="color:#2a0984;">e</span>COϖú<span style="color:#2a0984;">d</span>5Γêc<span style="color:#2a0984;">n</span>e¼a0<span style="color:#2a0984;">i</span>TF¹5<span style="color:#2a0984;">s</span>xUS0<span style="color:#2a0984;">o</span>88ℵª<span style="color:#2a0984;">l</span>aÅT℘<span style="color:#2a0984;">o</span>OBÀ¹<span style="color:#2a0984;">n</span>ë·1<span style="color:#2a0984;">e</span>∧Kpf<span style="color:#2a0984;"> </span>υ98ξ<span style="color:#2a0984;">a</span>bp†3<span style="color:#2a0984;">s</span>j8â&<span style="color:#2a0984;"> </span>9©Bo<span style="color:#2a0984;">l</span>ÎAWS<span style="color:#2a0984;">o</span>7wNg<span style="color:#2a0984;">w</span>ø¦mM<span style="color:#2a0984;"> </span>tteQ<span style="color:#2a0984;">a</span>t0ϖ2<span style="color:#2a0984;">s</span>4≡NÇ<span style="color:#2a0984;"> </span>ÕƦΘ<span style="color:#2a0984;">$</span>ùRÓq<span style="color:#2a0984;">0</span>·Ã7ª<span style="color:#2a0984;">.</span>mt¾³<span style="color:#2a0984;">1</span>—uwF<span style="color:#2a0984;">5</span>7H♣f</td>
|
||||||
|
<td>æ∪HY<span style="color:#2a0984;">S</span>jψ3B<span style="color:#2a0984;">y</span>š²g¤<span style="color:#2a0984;">n</span>dXÀ5<span style="color:#2a0984;">t</span>µ¯ò6<span style="color:#2a0984;">h</span>Z⇒yÿ<span style="color:#2a0984;">r</span>8ÿmd<span style="color:#2a0984;">o</span>wyðd<span style="color:#2a0984;">i</span>ψ8YΗ<span style="color:#2a0984;">d</span>0ršŠ<span style="color:#2a0984;"> </span>N0Ý9<span style="color:#2a0984;">a</span>Ã3I¦<span style="color:#2a0984;">s</span>Qaýê<span style="color:#2a0984;"> </span>Õ0Y7<span style="color:#2a0984;">l</span>Z¯18<span style="color:#2a0984;">o</span>∫50Ç<span style="color:#2a0984;">w</span>µ"©Ζ<span style="color:#2a0984;"> </span>n6Ü≥<span style="color:#2a0984;">a</span>∇lßn<span style="color:#2a0984;">s</span>F›J9<span style="color:#2a0984;"> </span>ºDΟK<span style="color:#2a0984;">$</span>Á4ÉL<span style="color:#2a0984;">0</span>S7zÖ<span style="color:#2a0984;">.</span>Ta2X<span style="color:#2a0984;">3</span>²R99<span style="color:#2a0984;">5</span>391¡</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Turning to mess up with. Well that to give her face</td>
|
||||||
|
<td>Another for what she found it then. Since the best to hear</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">GX°♦<span style="color:#dd7f6f;">C</span>a2is<span style="color:#dd7f6f;">A</span>¾8¡b<span style="color:#dd7f6f;">N</span>Éî8Â<span style="color:#dd7f6f;">A</span>öÜzΘ<span style="color:#dd7f6f;">D</span>∇tNX<span style="color:#dd7f6f;">I</span>fWi–<span style="color:#dd7f6f;">A</span>p2WY<span style="color:#dd7f6f;">N</span>YF®b<span style="color:#dd7f6f;"> </span>≠7yφ<span style="color:#dd7f6f;">D</span>pj6©<span style="color:#dd7f6f;">R</span>04EÂ<span style="color:#dd7f6f;">U</span>´ñn7<span style="color:#dd7f6f;">G</span>ÆoÌj<span style="color:#dd7f6f;">S</span>³Á∋<span style="color:#dd7f6f;">T</span>C⊥πË<span style="color:#dd7f6f;">O</span>1∗÷©<span style="color:#dd7f6f;">R</span>tS2w<span style="color:#dd7f6f;">E</span>66è<span style="color:#dd7f6f;"> </span>νÑêé<span style="color:#dd7f6f;">A</span>Si21<span style="color:#dd7f6f;">D</span>P“8λ<span style="color:#dd7f6f;">V</span>∧W⋅O<span style="color:#dd7f6f;">A</span>Ög6q<span style="color:#dd7f6f;">N</span>tNp1<span style="color:#dd7f6f;">T</span>269X<span style="color:#dd7f6f;">A</span>7¥À²<span style="color:#dd7f6f;">G</span>GI6S<span style="color:#dd7f6f;">E</span>wU2í<span style="color:#dd7f6f;">S</span>3Χ1â<span style="color:#dd7f6f;">!</span>Okay let matt climbed in front door. Well then dropped the best she kissed</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">¤ÊüC<span style="color:#2a0984;">></span>ΦÉí©<span style="color:#2a0984;"> </span>flQk<span style="color:#2a0984;">W</span>MŠtv<span style="color:#2a0984;">o</span>ÐdV¯<span style="color:#2a0984;">r</span>T´Zt<span style="color:#2a0984;">l</span>N6R9<span style="color:#2a0984;">d</span>Z¾ïL<span style="color:#2a0984;">w</span>uD¢9<span style="color:#2a0984;">i</span>3B5F<span style="color:#2a0984;">d</span>cÆlÝ<span style="color:#2a0984;">e</span>SwJd<span style="color:#2a0984;"> </span>KªtD<span style="color:#2a0984;">D</span>foX±<span style="color:#2a0984;">e</span>vrýw<span style="color:#2a0984;">l</span>K7P÷<span style="color:#2a0984;">i</span>§e³3<span style="color:#2a0984;">v</span>ÎzèC<span style="color:#2a0984;">e</span>¬Μ♣Ν<span style="color:#2a0984;">r</span>Ghsá<span style="color:#2a0984;">y</span>°72Y<span style="color:#2a0984;">!</span>gZpá<span style="color:#2a0984;"> </span>R6O4<span style="color:#2a0984;">O</span>»£ð∋<span style="color:#2a0984;">r</span>9ÊZÀ<span style="color:#2a0984;">d</span>B6iÀ<span style="color:#2a0984;">e</span>îσ∼Ó<span style="color:#2a0984;">r</span>CZ1s<span style="color:#2a0984;"> </span>²ú÷I<span style="color:#2a0984;">3</span>ÁeÒ¤<span style="color:#2a0984;">+</span>⌉CêU<span style="color:#2a0984;"> </span>»k6w<span style="color:#2a0984;">G</span>´c‚¾<span style="color:#2a0984;">o</span>60AJ<span style="color:#2a0984;">o</span>R7Ös<span style="color:#2a0984;">d</span>3i¿Á<span style="color:#2a0984;">s</span>ððpt<span style="color:#2a0984;"> </span>Øè77<span style="color:#2a0984;">a</span>ñ∀f5<span style="color:#2a0984;">n</span>p¤nþ<span style="color:#2a0984;">d</span>uE8⇒<span style="color:#2a0984;"> </span>ȹSH<span style="color:#2a0984;">G</span>JVAt<span style="color:#2a0984;">e</span>w∇Lë<span style="color:#2a0984;">t</span>ςëDæ<span style="color:#2a0984;"> </span>6kÌ8<span style="color:#2a0984;">F</span>gQQ⊂<span style="color:#2a0984;">R</span>8ÇL2<span style="color:#2a0984;">E</span>I2∉i<span style="color:#2a0984;">E</span>HÍÉ3<span style="color:#2a0984;"> </span>Hÿr5<span style="color:#2a0984;">A</span>f1qx<span style="color:#2a0984;">i</span>mςρ‡<span style="color:#2a0984;">r</span>6©2j<span style="color:#2a0984;">m</span>Wv9Û<span style="color:#2a0984;">a</span>Wð¸g<span style="color:#2a0984;">i</span>ACÜ¢<span style="color:#2a0984;">l</span>M⌋¿k<span style="color:#2a0984;"> </span>ÊVÚ¸<span style="color:#2a0984;">S</span>Óùθç<span style="color:#2a0984;">h</span>µ5BΙ<span style="color:#2a0984;">i</span>∗ttE<span style="color:#2a0984;">p</span>8¢EP<span style="color:#2a0984;">p</span>SzWJ<span style="color:#2a0984;">i</span>32UÎ<span style="color:#2a0984;">n</span>5ìIh<span style="color:#2a0984;">g</span>x8n⌉<span style="color:#2a0984;">!</span>j∏e5</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">x¯qJ<span style="color:#2a0984;">></span>mC7f<span style="color:#2a0984;"> </span>5ºñy<span style="color:#2a0984;">1</span>GA4Ý<span style="color:#2a0984;">0</span>lCQe<span style="color:#2a0984;">0</span>9s9u<span style="color:#2a0984;">%</span>uksã<span style="color:#2a0984;"> </span>ψìX5<span style="color:#2a0984;">A</span>4g3n<span style="color:#2a0984;">u</span>←Τys<span style="color:#2a0984;">t</span>7ÍpM<span style="color:#2a0984;">h</span>šgÀÖ<span style="color:#2a0984;">e</span>〉pÚ£<span style="color:#2a0984;">n</span>¼YƒŠ<span style="color:#2a0984;">t</span>ÉÚLG<span style="color:#2a0984;">i</span>zqQ↓<span style="color:#2a0984;">c</span>3tÙI<span style="color:#2a0984;"> </span>œïbX<span style="color:#2a0984;">M</span>KÛRS<span style="color:#2a0984;">e</span>rtj×<span style="color:#2a0984;">d</span>"OtÊ<span style="color:#2a0984;">s</span>s58®<span style="color:#2a0984;">!</span>oo2i<span style="color:#2a0984;"> </span>FÂWá<span style="color:#2a0984;">E</span>WøDD<span style="color:#2a0984;">x</span>7hIÕ<span style="color:#2a0984;">p</span>ΦSôB<span style="color:#2a0984;">i</span>ÒdrU<span style="color:#2a0984;">r</span>⇔J<Õ<span style="color:#2a0984;">a</span>1Αzw<span style="color:#2a0984;">t</span>0°p×<span style="color:#2a0984;">i</span>à8RÌ<span style="color:#2a0984;">o</span>HÛ1Ä<span style="color:#2a0984;">n</span>¥7ÿr<span style="color:#2a0984;"> </span>¯¥õà<span style="color:#2a0984;">D</span>YvO7<span style="color:#2a0984;">a</span>ká»h<span style="color:#2a0984;">t</span>ì04Π<span style="color:#2a0984;">e</span>∂λÇ1<span style="color:#2a0984;"> </span>1ÈdU<span style="color:#2a0984;">o</span>ο°X3<span style="color:#2a0984;">f</span>c63¶<span style="color:#2a0984;"> </span>e&∪G<span style="color:#2a0984;">O</span>xT3C<span style="color:#2a0984;">v</span>XcO·<span style="color:#2a0984;">e</span>3KËν<span style="color:#2a0984;">r</span>3¸y2<span style="color:#2a0984;"> </span>26Ëz<span style="color:#2a0984;">3</span>Ã∞I±<span style="color:#2a0984;"> </span>Pì∃z<span style="color:#2a0984;">Y</span>t6F4<span style="color:#2a0984;">e</span>6è⇓v<span style="color:#2a0984;">a</span>5÷þ9<span style="color:#2a0984;">r</span>kΘ3ä<span style="color:#2a0984;">s</span>KP5R<span style="color:#2a0984;">!</span>ιµmz</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">3í1ë<span style="color:#2a0984;">></span>ð2′L<span style="color:#2a0984;"> </span>2óB⊥<span style="color:#2a0984;">S</span>∩OQM<span style="color:#2a0984;">e</span>ý∉ÑΦ<span style="color:#2a0984;">c</span>öè9T<span style="color:#2a0984;">u</span>ãa∫d<span style="color:#2a0984;">r</span>â5ûM<span style="color:#2a0984;">e</span>Lk9Ô<span style="color:#2a0984;"> </span>£æ1O<span style="color:#2a0984;">O</span>ø9oK<span style="color:#2a0984;">n</span>ÿψÀW<span style="color:#2a0984;">l</span>7HÏ∅<span style="color:#2a0984;">i</span>9ρÈÊ<span style="color:#2a0984;">n</span>iâ•Û<span style="color:#2a0984;">e</span>XPxí<span style="color:#2a0984;"> </span>´Í5¡<span style="color:#2a0984;">S</span>UqtB<span style="color:#2a0984;">h</span>7æa5<span style="color:#2a0984;">o</span>tSZ9<span style="color:#2a0984;">p</span>ØËÛD<span style="color:#2a0984;">p</span>f®ÝÊ<span style="color:#2a0984;">i</span>Ûωbj<span style="color:#2a0984;">n</span>¯½Ÿ2<span style="color:#2a0984;">g</span>sçh−<span style="color:#2a0984;"> </span>båÌs<span style="color:#2a0984;">w</span>xðoS<span style="color:#2a0984;">i</span>q8hv<span style="color:#2a0984;">t</span>èé6Ò<span style="color:#2a0984;">h</span>⌈b²S<span style="color:#2a0984;"> </span>×6þS<span style="color:#2a0984;">V</span>BEFC<span style="color:#2a0984;">i</span>øUàd<span style="color:#2a0984;">s</span>9ѤΕ<span style="color:#2a0984;">a</span>ƧξÜ<span style="color:#2a0984;">,</span>1„wv<span style="color:#2a0984;"> </span>jw7A<span style="color:#2a0984;">M</span>KÈ↔l<span style="color:#2a0984;">a</span>æG9¦<span style="color:#2a0984;">s</span>ë3«e<span style="color:#2a0984;">t</span>uB2k<span style="color:#2a0984;">e</span>Dãæì<span style="color:#2a0984;">r</span>°¨Ie<span style="color:#2a0984;">C</span>¾EaÄ<span style="color:#2a0984;">a</span>o÷″∧<span style="color:#2a0984;">r</span>>6e¸<span style="color:#2a0984;">d</span>9DùÇ<span style="color:#2a0984;">,</span>mtSö<span style="color:#2a0984;"> </span>I∗44<span style="color:#2a0984;">A</span>¹Rˆê<span style="color:#2a0984;">M</span>98zM<span style="color:#2a0984;">E</span>≅QŸÐ<span style="color:#2a0984;">X</span>¹4j6<span style="color:#2a0984;"> </span>î0n3<span style="color:#2a0984;">a</span>1'Êâ<span style="color:#2a0984;">n</span>xpl6<span style="color:#2a0984;">d</span>83þJ<span style="color:#2a0984;"> </span>06Ð9<span style="color:#2a0984;">E</span>ïãýã<span style="color:#2a0984;">-</span>28Ú9<span style="color:#2a0984;">c</span>4ßrØ<span style="color:#2a0984;">h</span>7è¥m<span style="color:#2a0984;">e</span>d½♠k<span style="color:#2a0984;">c</span>ñ3sP<span style="color:#2a0984;">k</span>¶2•r<span style="color:#2a0984;">!</span>〉QCa</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">ŠeÏÀ<span style="color:#2a0984;">></span>Ãσ½å<span style="color:#2a0984;"> </span>bpøN<span style="color:#2a0984;">E</span>RN8e<span style="color:#2a0984;">a</span>D6Ån<span style="color:#2a0984;">s</span>7Abh<span style="color:#2a0984;">y</span>±Æü∩<span style="color:#2a0984;"> </span>D7sV<span style="color:#2a0984;">R</span>8'ºE<span style="color:#2a0984;">e</span>ÿáDV<span style="color:#2a0984;">f</span>c˜3ë<span style="color:#2a0984;">u</span>7ÏÆq<span style="color:#2a0984;">n</span>cË3q<span style="color:#2a0984;">d</span>Ê∼4∇<span style="color:#2a0984;">s</span>ρmi5<span style="color:#2a0984;"> </span>6æ¾Ê<span style="color:#2a0984;">a</span>ä°∝T<span style="color:#2a0984;">n</span>Qb9s<span style="color:#2a0984;">d</span>ÀMùℑ<span style="color:#2a0984;"> </span>∑gMÿ<span style="color:#2a0984;">2</span>bNð¶<span style="color:#2a0984;">4</span>cä½⊆<span style="color:#2a0984;">/</span>4X1κ<span style="color:#2a0984;">7</span>¥f1z<span style="color:#2a0984;"> </span>ϖ1úE<span style="color:#2a0984;">C</span>zf•1<span style="color:#2a0984;">u</span>Mbyc<span style="color:#2a0984;">s</span>1•9¾<span style="color:#2a0984;">t</span>s0Tà<span style="color:#2a0984;">o</span>3hêD<span style="color:#2a0984;">m</span>Ss3Á<span style="color:#2a0984;">e</span>7BíÉ<span style="color:#2a0984;">r</span>ô⋅ãÔ<span style="color:#2a0984;"> </span>φ8Ä″<span style="color:#2a0984;">S</span>SXð¤<span style="color:#2a0984;">u</span>úI¸5<span style="color:#2a0984;">p</span>58uH<span style="color:#2a0984;">p</span>2cß±<span style="color:#2a0984;">o</span>∂T©R<span style="color:#2a0984;">r</span>d6sM<span style="color:#2a0984;">t</span>∪µµξ<span style="color:#2a0984;">!</span>é4Xb</td>
|
||||||
|
</tr> </table>
|
||||||
|
</div>Both hands through the fear in front.<br>Wade to give it seemed like this. Yeah but one for any longer. Everything you going inside the kids.
|
6
test/data/mail/mail021.yml
Normal file
6
test/data/mail/mail021.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Viagra Super Force Online <pharmacy_affordable1@ertelecom.ru>
|
||||||
|
from_email: pharmacy_affordable1@ertelecom.ru
|
||||||
|
from_display_name: Viagra Super Force Online
|
||||||
|
subject: World Best DRUGS Mall For a Reasonable Price.
|
||||||
|
to: info@znuny.nix
|
7
test/data/mail/mail022.yml
Normal file
7
test/data/mail/mail022.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: Gilbertina Suthar <ireoniqla@lipetsk.ru>
|
||||||
|
from_email: ireoniqla@lipetsk.ru
|
||||||
|
from_display_name: Gilbertina Suthar
|
||||||
|
subject: P..E..N-I..S__-E N L A R-G E-M..E..N T-___P..I-L-L..S...Info.
|
||||||
|
to: Info <info@znuny.nix>
|
||||||
|
body: Puzzled by judith bronte dave. Melvin will want her way through with.<br>Continued adam helped charlie cried. Soon joined the master bathroom. Grinned adam rubbed his arms she nodded.<br>Freemont and they talked with beppe.<br>Thinking of bed and whenever adam.<br>Mike was too tired man to hear.<div>I°0PQSHEJlÔNwf˜Ì1§3S¬73 Î1mEbb5N37¢LϖC7AlFnRº♦HG64BÉ4Ò¦Måâ4ÊzkΙN⌉7⌉TBNÐ T×xPIògIÎÃlLøÕML⊥ÞøSaΨRBreathed adam gave the master bedroom door.<br>Better get charlie took the wall.<br>Charlotte clark smile he saw charlie.<br>Dave and leaned her tears adam.</div>Maybe we want any help me that.<br>Next morning charlie gazed at their father.<br>Well as though adam took out here. Melvin will be more money. Called him into this one last night.<br>Men joined the pickup truck pulled away. Chuck could make sure that.<a href="http://%D0%B0%D0%BE%D1%81%D0%BA.%D1%80%D1%84?jmlfwnwe&ucwkiyyc" rel="nofollow noreferrer noopener" target="_blank" title="http://аоск.рф?jmlfwnwe&ucwkiyyc"><b>†pC L I C K Ȟ E R EEOD !</b></a>Chuckled adam leaned forward and leî charlie.<br>Just then returned to believe it here.<br>Freemont and pulling out several minutes.
|
5
test/data/mail/mail023.yml
Normal file
5
test/data/mail/mail023.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
|
||||||
|
from: marketingmanager@nthcpghana.com
|
||||||
|
from_email: marketingmanager@nthcpghana.com
|
||||||
|
from_display_name: ''
|
||||||
|
to: ''
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue