aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/info_request.rb
blob: e7f9831d6144d11c047a5825db370a603c7a75c5 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# == Schema Information
# Schema version: 24
#
# Table name: info_requests
#
#  id             :integer         not null, primary key
#  title          :text            
#  user_id        :integer         
#  public_body_id :integer         
#  created_at     :datetime        
#  updated_at     :datetime        
#

# models/info_request.rb:
# A Freedom of Information request.
#
# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
#
# $Id: info_request.rb,v 1.24 2008-01-04 11:19:18 francis Exp $

require 'digest/sha1'

class InfoRequest < ActiveRecord::Base
    validates_presence_of :title, :message => "^Please enter a summary of your request"

    belongs_to :user
    #validates_presence_of :user_id # breaks during construction of new ones :(

    belongs_to :public_body
    validates_presence_of :public_body_id

    has_many :outgoing_messages
    has_many :incoming_messages
    has_many :info_request_events

public
    # Email which public body should use to respond to request. This is in
    # the format PREFIXrequest-ID-HASH@DOMAIN. Here ID is the id of the 
    # FOI request, and HASH is a signature for that id.
    def incoming_email
        return self.magic_email("request-")
    end

    # Modified version of incoming_email to use in the envelope from, for
    # bounce messages.
    def envelope_email
        return self.magic_email("request-bounce-")
    end

    # Return info request corresponding to an incoming email address, or nil if
    # none found. Checks the hash to ensure the email came from the public body -
    # only they are sent the email address with the has in it.
    def self.find_by_incoming_email(incoming_email)
        incoming_email =~ /request-(\d+)-([a-z0-9]+)/
        id = $1.to_i
        hash = $2

        return self.find_by_magic_email(id, hash)
    end

    def self.find_by_envelope_email(incoming_email)
        incoming_email =~ /request-bounce-(\d+)-([a-z0-9]+)/
        id = $1.to_i
        hash = $2

        return self.find_by_magic_email(id, hash)
    end

    # A new incoming email to this request
    def receive(email, raw_email, is_bounce)
        incoming_message = IncomingMessage.new
        incoming_message.raw_data = raw_email
        incoming_message.is_bounce = is_bounce
        incoming_message.info_request = self
        incoming_message.save!

        RequestMailer.deliver_new_response(self, incoming_message)
    end

    # Work out what the situation of the request is
    def calculate_status
        # Extract aggregate information for any incoming messages all together
        contains_information = false
        missing_classification = false
        self.incoming_messages.each do |msg|
            if msg.user_classified
                if msg.contains_information
                    contains_information = true
                end
            else
                missing_classification = true
            end
        end

        # See if response would be overdue 
        overdue = false
        # XXX if a second outgoing message is really a new request, then this
        # is no good
        # We use the last_sent_at date for each outgoing message, as fair
        # enough if the first email bounced or something and it got recent.
        earliest_sent = self.outgoing_messages.map { |om| om.last_sent_at }.min
        time_left = Time.now - earliest_sent
        # XXX use working days 
        if time_left > 20.days
            overdue = true
        end

        # Return appropriate status string
        if self.incoming_messages.size == 0
            if overdue
                return "overdue"
            else
                return "awaiting"
            end
        end
        if missing_classification
            return "unknown"
        end
        if contains_information
            return "information"
        else
            return "none"
        end
    end

    # Return array of unclassified responses
    def unclassified_responses
        return self.incoming_messages.select do |msg|
            not msg.user_classified
        end
    end

    # Where the initial request is sent to
    def recipient_email
        if MySociety::Config.getbool("STAGING_SITE", 1)
            return self.user.email
        else
            return self.public_body.request_email
        end
    end

    # History of some things that have happened
    def log_event(type, params)
        info_request_event = InfoRequestEvent.new
        info_request_event.event_type = type 
        info_request_event.params = params
        info_request_event.info_request = self
        info_request_event.save!
    end

    # Text from the the initial request, for use in summary display
    def initial_request_text
        if outgoing_messages.empty? # mainly for use with incomplete fixtures
            return ""
        end
        excerpt = outgoing_messages[0].body
        excerpt.sub!(/Dear .+,/, "")
        return excerpt
    end

    protected

    # Called by incoming_email and envelope_email
    def magic_email(prefix_part)
        raise "id required to make magic" if not self.id
        magic_email = MySociety::Config.get("INCOMING_EMAIL_PREFIX", "") 
        magic_email += prefix_part + self.id.to_s 
        magic_email += "-" + Digest::SHA1.hexdigest(self.id.to_s + MySociety::Config.get("INCOMING_EMAIL_SECRET", 'dummysecret'))[0,8]
        magic_email += "@" + MySociety::Config.get("INCOMING_EMAIL_DOMAIN", "localhost")
        return magic_email
    end

    # Called by find_by_incoming_email and find_by_envelope_email
    def self.find_by_magic_email(id, hash)
        expected_hash = Digest::SHA1.hexdigest(id.to_s + MySociety::Config.get("INCOMING_EMAIL_SECRET", 'dummysecret'))[0,8]
        #print "expected: " + expected_hash + "\nhash: " + hash + "\n"
        if hash != expected_hash
            return nil
        else
            return self.find(id)
        end
    end


end