aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mail_handler/backends/tmail_backend.rb
blob: 3f77f9f8b7ed8ae2c1b61a29e0242f57e14ad3a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
module MailHandler
    module Backends
        module TmailBackend

            def backend()
                'TMail'
            end

            # Turn raw data into a structured TMail::Mail object
            # Documentation at http://i.loveruby.net/en/projects/tmail/doc/
            def mail_from_raw_email(data, decode=true)
                # Hack round bug in TMail's MIME decoding.
                # Report of TMail bug:
                # http://rubyforge.org/tracker/index.php?func=detail&aid=21810&group_id=4512&atid=17370
                copy_of_raw_data = data.gsub(/; boundary=\s+"/im,'; boundary="')
                mail = TMail::Mail.parse(copy_of_raw_data)
                mail.base64_decode if decode
                mail
            end

            # Extracts all attachments from the given TNEF file as a TMail::Mail object
            def mail_from_tnef(content)
                main = TMail::Mail.new
                main.set_content_type 'multipart', 'mixed', { 'boundary' => TMail.new_boundary }
                tnef_attachments(content).each do |attachment|
                    tmail_attachment = TMail::Mail.new
                    tmail_attachment['content-location'] = attachment[:filename]
                    tmail_attachment.body = attachment[:content]
                    main.parts << tmail_attachment
                end
                main
            end

            # Return a copy of the file name for the mail part
            def get_part_file_name(mail_part)
                part_file_name = TMail::Mail.get_part_file_name(mail_part)
                if part_file_name.nil?
                    return nil
                end
                part_file_name = part_file_name.dup
                return part_file_name
            end

            # Get the body of a mail part
            def get_part_body(mail_part)
                mail_part.body
            end

            # Return the first from address if any
            def get_from_address(mail)
                if mail.from_addrs.nil? || mail.from_addrs.size == 0
                    return nil
                end
                mail.from_addrs[0].spec
            end

            # Return the first from name if any
            def get_from_name(mail)
                mail.from_name_if_present
            end

            def get_all_addresses(mail)
                ((mail.to || []) +
                (mail.cc || []) +
                (mail.envelope_to || [])).uniq
            end

            def empty_return_path?(mail)
                return false if mail['return-path'].nil?
                return true if mail['return-path'].addr.to_s == '<>'
                return false
            end

            def get_auto_submitted(mail)
                mail['auto-submitted'] ? mail['auto-submitted'].body : nil
            end

            def get_content_type(part)
                part.content_type
            end

            def get_header_string(header, mail)
                mail.header_string(header)
            end

            # Number the attachments in depth first tree order, for use in URLs.
            # XXX This fills in part.rfc822_attachment and part.url_part_number within
            # all the parts of the email (see monkeypatches in lib/mail_handler/tmail_extensions and
            # lib/mail_handler/mail_extensions for how these attributes are added). ensure_parts_counted
            # must be called before using the attributes.
            def ensure_parts_counted(mail)
                mail.count_parts_count = 0
                _count_parts_recursive(mail, mail)
                # we carry on using these numeric ids for attachments uudecoded from within text parts
                mail.count_first_uudecode_count = mail.count_parts_count
            end
            def _count_parts_recursive(part, mail)
                if part.multipart?
                    part.parts.each do |p|
                        _count_parts_recursive(p, mail)
                    end
                else
                    part_filename = MailHandler.get_part_file_name(part)
                    begin
                        if part.content_type == 'message/rfc822'
                            # An email attached as text
                            # e.g. http://www.whatdotheyknow.com/request/64/response/102
                            part.rfc822_attachment = MailHandler.mail_from_raw_email(part.body, decode=false)
                        elsif part.content_type == 'application/vnd.ms-outlook' || part_filename && AlaveteliFileTypes.filename_to_mimetype(part_filename) == 'application/vnd.ms-outlook'
                            # An email attached as an Outlook file
                            # e.g. http://www.whatdotheyknow.com/request/chinese_names_for_british_politi
                            msg = Mapi::Msg.open(StringIO.new(part.body))
                            part.rfc822_attachment = MailHandler.mail_from_raw_email(msg.to_mime.to_s, decode=false)
                        elsif part.content_type == 'application/ms-tnef'
                            # A set of attachments in a TNEF file
                            part.rfc822_attachment = MailHandler.mail_from_tnef(part.body)
                        end
                    rescue
                        # If attached mail doesn't parse, treat it as text part
                        part.rfc822_attachment = nil
                    else
                        unless part.rfc822_attachment.nil?
                            _count_parts_recursive(part.rfc822_attachment, mail)
                        end
                    end
                    if part.rfc822_attachment.nil?
                        mail.count_parts_count += 1
                        part.url_part_number = mail.count_parts_count
                    end
                end
            end


            def address_from_name_and_email(name, email)
                if !MySociety::Validate.is_valid_email(email)
                    raise "invalid email " + email + " passed to address_from_name_and_email"
                end
                if name.nil?
                    return TMail::Address.parse(email).to_s
                end
                # Botch an always quoted RFC address, then parse it
                name = name.gsub(/(["\\])/, "\\\\\\1")
                TMail::Address.parse('"' + name + '" <' + email + '>').to_s
            end

            def address_from_string(string)
                TMail::Address.parse(string).address
            end

        end
    end
end