diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/admin_request_controller.rb | 3 | ||||
-rw-r--r-- | app/controllers/application_controller.rb | 25 | ||||
-rw-r--r-- | app/controllers/request_controller.rb | 12 | ||||
-rw-r--r-- | app/controllers/user_controller.rb | 10 | ||||
-rw-r--r-- | app/mailers/outgoing_mailer.rb | 11 | ||||
-rw-r--r-- | app/mailers/request_mailer.rb | 2 | ||||
-rw-r--r-- | app/models/incoming_message.rb | 158 | ||||
-rw-r--r-- | app/models/info_request.rb | 29 | ||||
-rw-r--r-- | app/models/public_body.rb | 5 | ||||
-rw-r--r-- | app/views/comment/new.html.erb | 9 | ||||
-rw-r--r-- | app/views/request/details.html.erb | 9 | ||||
-rw-r--r-- | app/views/request/followup_preview.html.erb | 4 | ||||
-rw-r--r-- | app/views/request/preview.html.erb | 2 |
13 files changed, 100 insertions, 179 deletions
diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb index 8f023bf12..7d2ac2f35 100644 --- a/app/controllers/admin_request_controller.rb +++ b/app/controllers/admin_request_controller.rb @@ -4,9 +4,8 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -require 'ostruct' - class AdminRequestController < AdminController + def index list render :action => 'list' diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1ccf7e5db..a06fa7098 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -30,6 +30,8 @@ class ApplicationController < ActionController::Base before_filter :check_in_post_redirect before_filter :session_remember_me before_filter :set_vary_header + before_filter :validate_session_timestamp + after_filter :persist_session_timestamp def set_vary_header response.headers['Vary'] = 'Cookie' @@ -121,6 +123,29 @@ class ApplicationController < ActionController::Base end end + # Set a TTL for non "remember me" sessions so that the cookie + # is not replayable forever + SESSION_TTL = 3.hours + def validate_session_timestamp + if session[:user_id] && session.key?(:ttl) && session[:ttl] < SESSION_TTL.ago + clear_session_credentials + redirect_to signin_path + end + end + + def persist_session_timestamp + session[:ttl] = Time.now if session[:user_id] && !session[:remember_me] + end + + # Logout form + def clear_session_credentials + session[:user_id] = nil + session[:user_circumstance] = nil + session[:remember_me] = false + session[:using_admin] = nil + session[:admin_name] = nil + end + def render_exception(exception) # In development or the admin interface let Rails handle the exception # with its stack trace templates diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index 346aaf384..d529f8dbb 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -770,13 +770,14 @@ class RequestController < ApplicationController get_attachment_internal(false) return unless @attachment - # Prevent spam to magic request address. Note that the binary - # subsitution method used depends on the content type - @incoming_message.binary_mask_stuff!(@attachment.body, @attachment.content_type) # we don't use @attachment.content_type here, as we want same mime type when cached in cache_attachments above response.content_type = AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || 'application/octet-stream' + # Prevent spam to magic request address. Note that the binary + # subsitution method used depends on the content type + @incoming_message.apply_masks!(@attachment.body, @attachment.content_type) + render :text => @attachment.body end @@ -804,10 +805,9 @@ class RequestController < ApplicationController :body_prefix => render_to_string(:partial => "request/view_html_prefix") } ) - - @incoming_message.html_mask_stuff!(html) - response.content_type = 'text/html' + @incoming_message.apply_masks!(html, response.content_type) + render :text => html end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index baeaab18a..9798ff8e2 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -260,16 +260,8 @@ class UserController < ApplicationController do_post_redirect post_redirect end - # Logout form - def _do_signout - session[:user_id] = nil - session[:user_circumstance] = nil - session[:remember_me] = false - session[:using_admin] = nil - session[:admin_name] = nil - end def signout - self._do_signout + clear_session_credentials if params[:r] redirect_to params[:r] else diff --git a/app/mailers/outgoing_mailer.rb b/app/mailers/outgoing_mailer.rb index 797bf9fdd..19054b4e2 100644 --- a/app/mailers/outgoing_mailer.rb +++ b/app/mailers/outgoing_mailer.rb @@ -21,7 +21,7 @@ class OutgoingMailer < ApplicationMailer mail(:from => info_request.incoming_name_and_email, :to => info_request.recipient_name_and_email, - :subject => info_request.email_subject_request) + :subject => info_request.email_subject_request(:html => false)) end # Later message to public body regarding existing request @@ -32,7 +32,7 @@ class OutgoingMailer < ApplicationMailer mail(:from => info_request.incoming_name_and_email, :to => OutgoingMailer.name_and_email_for_followup(info_request, incoming_message_followup), - :subject => OutgoingMailer.subject_for_followup(info_request, outgoing_message)) + :subject => OutgoingMailer.subject_for_followup(info_request, outgoing_message, :html => false)) end # TODO: the condition checking valid_to_reply_to? also appears in views/request/_followup.html.erb, @@ -67,11 +67,12 @@ class OutgoingMailer < ApplicationMailer end end # Subject to use for followup - def OutgoingMailer.subject_for_followup(info_request, outgoing_message) + def OutgoingMailer.subject_for_followup(info_request, outgoing_message, options = {}) if outgoing_message.what_doing == 'internal_review' - return "Internal review of " + info_request.email_subject_request + return "Internal review of " + info_request.email_subject_request(:html => options[:html]) else - return info_request.email_subject_followup(outgoing_message.incoming_message_followup) + return info_request.email_subject_followup(:incoming_message => outgoing_message.incoming_message_followup, + :html => options[:html]) end end # Whether we have a valid email address for a followup diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb index 768257ba8..89b76fe97 100644 --- a/app/mailers/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -20,7 +20,7 @@ class RequestMailer < ApplicationMailer mail(:from => from_user.name_and_email, :to => info_request.incoming_name_and_email, - :subject => info_request.email_subject_followup) + :subject => info_request.email_subject_followup(:html => false)) end # Used when a response is uploaded using the API diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index db6722976..658ee969a 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -52,17 +52,6 @@ class IncomingMessage < ActiveRecord::Base has_prominence - # See binary_mask_stuff function below. It just test for inclusion - # in this hash, not the value of the right hand side. - DoNotBinaryMask = { - 'image/tiff' => 1, - 'image/gif' => 1, - 'image/jpeg' => 1, - 'image/png' => 1, - 'image/bmp' => 1, - 'application/zip' => 1, - } - # Given that there are in theory many info request events, a convenience method for # getting the response event def response_event @@ -218,111 +207,10 @@ class IncomingMessage < ActiveRecord::Base end end - # Converts email addresses we know about into textual descriptions of them - def mask_special_emails!(text) - # TODO: can later display some of these special emails as actual emails, - # if they are public anyway. For now just be precautionary and only - # put in descriptions of them in square brackets. - if self.info_request.public_body.is_followupable? - text.gsub!(self.info_request.public_body.request_email, _("[{{public_body}} request email]", :public_body => self.info_request.public_body.short_or_long_name)) - end - text.gsub!(self.info_request.incoming_email, _('[FOI #{{request}} email]', :request => self.info_request.id.to_s) ) - text.gsub!(AlaveteliConfiguration::contact_email, _("[{{site_name}} contact email]", :site_name => AlaveteliConfiguration::site_name) ) - end - - # Replaces all email addresses in (possibly binary data) with equal length alternative ones. - # Also replaces censor items - def binary_mask_stuff!(text, content_type) - # See if content type is one that we mask - things like zip files and - # images may get broken if we try to. We err on the side of masking too - # much, as many unknown types will really be text. - if DoNotBinaryMask.include?(content_type) - return - end - - # Special cases for some content types - if content_type == 'application/pdf' - uncompressed_text = nil - uncompressed_text = AlaveteliExternalCommand.run("pdftk", "-", "output", "-", "uncompress", :stdin_string => text) - # if we managed to uncompress the PDF... - if !uncompressed_text.nil? && !uncompressed_text.empty? - # then censor stuff (making a copy so can compare again in a bit) - censored_uncompressed_text = uncompressed_text.dup - self._binary_mask_stuff_internal!(censored_uncompressed_text) - # if the censor rule removed something... - if censored_uncompressed_text != uncompressed_text - # then use the altered file (recompressed) - recompressed_text = nil - if AlaveteliConfiguration::use_ghostscript_compression == true - command = ["gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-sOutputFile=-", "-"] - else - command = ["pdftk", "-", "output", "-", "compress"] - end - recompressed_text = AlaveteliExternalCommand.run(*(command + [{:stdin_string=>censored_uncompressed_text}])) - if recompressed_text.nil? || recompressed_text.empty? - # buggy versions of pdftk sometimes fail on - # compression, I don't see it's a disaster in - # these cases to save an uncompressed version? - recompressed_text = censored_uncompressed_text - logger.warn "Unable to compress PDF; problem with your pdftk version?" - end - if !recompressed_text.nil? && !recompressed_text.empty? - text.replace recompressed_text - end - end - end - return - end - - self._binary_mask_stuff_internal!(text) - end - - # Used by binary_mask_stuff - replace text in place - def _binary_mask_stuff_internal!(text) - # Keep original size, so can check haven't resized it - orig_size = text.mb_chars.size - - # Replace ASCII email addresses... - text.gsub!(MySociety::Validate.email_find_regexp) do |email| - email.gsub(/[^@.]/, 'x') - end - - # And replace UCS-2 ones (for Microsoft Office documents)... - # Find emails, by finding them in parts of text that have ASCII - # equivalents to the UCS-2 - ascii_chars = text.gsub(/\0/, "") - emails = ascii_chars.scan(MySociety::Validate.email_find_regexp) - - # Convert back to UCS-2, making a mask at the same time - if String.method_defined?(:encode) - emails.map! do |email| - # We want the ASCII representation of UCS-2 - [email[0].encode('UTF-16LE').force_encoding('US-ASCII'), - email[0].gsub(/[^@.]/, 'x').encode('UTF-16LE').force_encoding('US-ASCII')] - end - else - emails.map! {|email| [ - Iconv.conv('ucs-2le', 'ascii', email[0]), - Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x')) - ] } - end - - # Now search and replace the UCS-2 email with the UCS-2 mask - for email, mask in emails - text.gsub!(email, mask) - end - - # Replace censor items - self.info_request.apply_censor_rules_to_binary!(text) - - raise "internal error in binary_mask_stuff" if text.mb_chars.size != orig_size - return text - end - - # Removes censored stuff from from HTML conversion of downloaded binaries - def html_mask_stuff!(html) - self.mask_special_emails!(html) - self.remove_privacy_sensitive_things!(html) + def apply_masks!(text, content_type) + mask_options = { :censor_rules => info_request.applicable_censor_rules, + :masks => info_request.masks } + AlaveteliTextMasker.apply_masks!(text, content_type, mask_options) end # Lotus notes quoting yeuch! @@ -346,26 +234,6 @@ class IncomingMessage < ActiveRecord::Base end - # Remove emails, mobile phones and other details FOI officers ask us to remove. - def remove_privacy_sensitive_things!(text) - # Remove any email addresses - we don't want bounce messages to leak out - # either the requestor's email address or the request's response email - # address out onto the internet - text.gsub!(MySociety::Validate.email_find_regexp, "[email address]") - - # Mobile phone numbers - # http://www.whatdotheyknow.com/request/failed_test_purchases_off_licenc#incoming-1013 - # http://www.whatdotheyknow.com/request/selective_licensing_statistics_i#incoming-550 - # http://www.whatdotheyknow.com/request/common_purpose_training_graduate#incoming-774 - text.gsub!(/(Mobile|Mob)([\s\/]*(Fax|Tel))*\s*:?[\s\d]*\d/, "[mobile number]") - - # Remove WhatDoTheyKnow signup links - text.gsub!(/http:\/\/#{AlaveteliConfiguration::domain}\/c\/[^\s]+/, "[WDTK login link]") - - # Remove things from censor rules - self.info_request.apply_censor_rules_to_text!(text) - end - # Remove quoted sections from emails (eventually the aim would be for this # to do as good a job as GMail does) TODO: bet it needs a proper parser @@ -465,9 +333,8 @@ class IncomingMessage < ActiveRecord::Base raise "main body text more than 1 MB, need to implement clipping like for attachment text, or there is some other MIME decoding problem or similar" end - # remove emails for privacy/anti-spam reasons - self.mask_special_emails!(text) - self.remove_privacy_sensitive_things!(text) + # apply masks for this message + apply_masks!(text, 'text/html') # Remove existing quoted sections folded_quoted_text = self.remove_lotus_quoting(text, 'FOLDED_QUOTED_SECTION') @@ -735,7 +602,14 @@ class IncomingMessage < ActiveRecord::Base text = MySociety::Format.simplify_angle_bracketed_urls(text) text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text, :contract => 1) - text.gsub!(/\[(email address|mobile number)\]/, '[<a href="/help/officers#mobiles">\1</a>]') + + # add a helpful link to email addresses and mobile numbers removed + # by apply_masks! + email_pattern = Regexp.escape(_("email address")) + mobile_pattern = Regexp.escape(_("mobile number")) + text.gsub!(/\[(#{email_pattern}|#{mobile_pattern})\]/, + '[<a href="/help/officers#mobiles">\1</a>]') + if collapse_quoted_sections text = text.gsub(/(\s*FOLDED_QUOTED_SECTION\s*)+/m, "FOLDED_QUOTED_SECTION") text.strip! @@ -773,8 +647,8 @@ class IncomingMessage < ActiveRecord::Base # Returns text version of attachment text def get_attachment_text_full text = self._get_attachment_text_internal - self.mask_special_emails!(text) - self.remove_privacy_sensitive_things!(text) + apply_masks!(text, 'text/html') + # This can be useful for memory debugging #STDOUT.puts 'xxx '+ MySociety::DebugHelpers::allocated_string_size_around_gc diff --git a/app/models/info_request.rb b/app/models/info_request.rb index d0052603a..20b7ef9af 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -292,13 +292,18 @@ public end # Subject lines for emails about the request - def email_subject_request - _('{{law_used_full}} request - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title.html_safe) + def email_subject_request(opts = {}) + html = opts.fetch(:html, true) + _('{{law_used_full}} request - {{title}}', + :law_used_full => self.law_used_full, + :title => (html ? title : title.html_safe)) end - def email_subject_followup(incoming_message = nil) + def email_subject_followup(opts = {}) + incoming_message = opts.fetch(:incoming_message, nil) + html = opts.fetch(:html, true) if incoming_message.nil? || !incoming_message.valid_to_reply_to? || !incoming_message.subject - 'Re: ' + self.email_subject_request + 'Re: ' + self.email_subject_request(:html => html) else if incoming_message.subject.match(/^Re:/i) incoming_message.subject @@ -1148,6 +1153,22 @@ public return binary end + # Masks we apply to text associated with this request convert email addresses + # we know about into textual descriptions of them + def masks + masks = [{ :to_replace => incoming_email, + :replacement => _('[FOI #{{request}} email]', + :request => id.to_s) }, + { :to_replace => AlaveteliConfiguration::contact_email, + :replacement => _("[{{site_name}} contact email]", + :site_name => AlaveteliConfiguration::site_name)} ] + if public_body.is_followupable? + masks << { :to_replace => public_body.request_email, + :replacement => _("[{{public_body}} request email]", + :public_body => public_body.short_or_long_name) } + end + end + def is_owning_user?(user) !user.nil? && (user.id == user_id || user.owns_every_request?) end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index 1929272ea..a9cdfeab2 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -454,11 +454,12 @@ class PublicBody < ActiveRecord::Base # of updating them bodies_by_name = {} set_of_existing = Set.new() + internal_admin_body_id = PublicBody.internal_admin_body.id I18n.with_locale(I18n.default_locale) do - bodies = (tag.nil? || tag.empty?) ? PublicBody.find(:all) : PublicBody.find_by_tag(tag) + bodies = (tag.nil? || tag.empty?) ? PublicBody.find(:all, :include => :translations) : PublicBody.find_by_tag(tag) for existing_body in bodies # Hide InternalAdminBody from import notes - next if existing_body.id == PublicBody.internal_admin_body.id + next if existing_body.id == internal_admin_body_id bodies_by_name[existing_body.name] = existing_body set_of_existing.add(existing_body.name) diff --git a/app/views/comment/new.html.erb b/app/views/comment/new.html.erb index 578732cdb..48fe055ac 100644 --- a/app/views/comment/new.html.erb +++ b/app/views/comment/new.html.erb @@ -1,4 +1,5 @@ -<% @title = "Make an annotation on '" + h(@info_request.title) + "'" %> +<% @title = _("Add an annotation to “{{request_title}}”", + :request_title => h(@info_request.title)) %> <% if @existing_comment %> <div class="errorExplanation" id="errorExplanation"><ul> @@ -11,8 +12,10 @@ <%= foi_error_messages_for :comment %> -<h1><%= _('Add an annotation')%></h1> -<h2>to “<%=request_link(@info_request)%>”</h2> +<h1> + <%= _("Add an annotation to “{{request_title}}”", + :request_title => request_link(@info_request)) %> +</h1> <p> <%= _('Annotations are so anyone, including you, can help the requester with their request. For example:')%> diff --git a/app/views/request/details.html.erb b/app/views/request/details.html.erb index 3cb2f5afe..194820da6 100644 --- a/app/views/request/details.html.erb +++ b/app/views/request/details.html.erb @@ -1,5 +1,10 @@ -<% @title = _("Details of request '") + h(@info_request.title) + "'" %> -<h1><%= _("Details of request '") + request_link(@info_request) + "'" %></h1> +<% @title = _("Details of request “{{request_title}}”", + :request_title => h(@info_request.title)) %> + +<h1> + <%= _("Details of request “{{request_title}}”", + :request_title => request_link(@info_request)) %> +</h1> <h2><%= _('Event history') %></h2> diff --git a/app/views/request/followup_preview.html.erb b/app/views/request/followup_preview.html.erb index 55afc0245..83978a2f5 100644 --- a/app/views/request/followup_preview.html.erb +++ b/app/views/request/followup_preview.html.erb @@ -3,7 +3,7 @@ <div id="followup"> <%= form_for(@outgoing_message, :html => { :id => 'preview_form' }, :url => (@incoming_message.nil? ? show_response_no_followup_url(:id => @info_request.id) : show_response_url(:id => @info_request.id, :incoming_message_id => @incoming_message.id)) + "#followup" ) do |o| %> - + <% if @internal_review %> <h1><%= _('Now preview your message asking for an internal review') %></h1> <% else %> @@ -20,7 +20,7 @@ <div class="correspondence" id="outgoing-0"> <p class="preview_subject"> <strong><%= _('To:') %></strong> <%=h OutgoingMailer.name_for_followup(@info_request, @incoming_message) %> - <br><strong><%= _('Subject:') %></strong> <%=h OutgoingMailer.subject_for_followup(@info_request, @outgoing_message) %> + <br><strong><%= _('Subject:') %></strong> <%= OutgoingMailer.subject_for_followup(@info_request, @outgoing_message, :html => true) %> </p> <div class="correspondence_text"> diff --git a/app/views/request/preview.html.erb b/app/views/request/preview.html.erb index 0265d0328..ddd5ab30c 100644 --- a/app/views/request/preview.html.erb +++ b/app/views/request/preview.html.erb @@ -23,7 +23,7 @@ <% else %> <%=h(@info_request.public_body.name)%> <% end %> - <br><strong><%= _('Subject:') %></strong> <%=h @info_request.email_subject_request %> + <br><strong><%= _('Subject:') %></strong> <%= @info_request.email_subject_request %> </p> <div class="correspondence_text"> |