aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml8
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--README.md3
-rw-r--r--app/controllers/admin_request_controller.rb2
-rw-r--r--app/controllers/api_controller.rb72
-rw-r--r--app/controllers/application_controller.rb9
-rw-r--r--app/models/foi_attachment.rb68
-rw-r--r--app/models/incoming_message.rb475
-rw-r--r--app/models/info_request.rb6
-rw-r--r--app/views/general/_frontpage_new_request.rhtml2
-rw-r--r--app/views/general/_frontpage_search_box.rhtml2
-rw-r--r--app/views/help/_sidebar.rhtml18
-rw-r--r--app/views/help/about.rhtml4
-rw-r--r--app/views/help/api.rhtml10
-rw-r--r--app/views/help/contact.rhtml18
-rw-r--r--app/views/help/officers.rhtml38
-rw-r--r--app/views/help/privacy.rhtml28
-rw-r--r--app/views/help/requesting.rhtml38
-rw-r--r--app/views/help/unhappy.rhtml16
-rw-r--r--app/views/layouts/default.rhtml6
m---------commonlib0
-rw-r--r--config/environment.rb2
-rw-r--r--config/general.yml-example4
-rw-r--r--config/initializers/fast_gettext.rb2
-rw-r--r--config/packages2
-rw-r--r--lib/alaveteli_external_command.rb14
-rw-r--r--lib/configuration.rb1
-rw-r--r--lib/mail_handler/backends/mail_backend.rb278
-rw-r--r--lib/mail_handler/backends/mail_extensions.rb60
-rw-r--r--lib/mail_handler/backends/tmail_backend.rb228
-rw-r--r--lib/mail_handler/backends/tmail_extensions.rb2
-rw-r--r--lib/mail_handler/mail_handler.rb131
-rw-r--r--locale/cs/app.po214
-rw-r--r--locale/cy/app.po66
-rw-r--r--locale/es/app.po46
-rw-r--r--public/stylesheets/main.css4
-rwxr-xr-xscript/handle-mail-replies.rb41
-rw-r--r--spec/controllers/api_controller_spec.rb134
-rw-r--r--spec/controllers/general_controller_spec.rb50
-rw-r--r--spec/controllers/request_controller_spec.rb2
-rw-r--r--spec/fixtures/files/dos-linebreaks.email31
-rw-r--r--spec/fixtures/files/humberside-police-odd-mime-type.email25
-rw-r--r--spec/fixtures/files/incoming-request-attachment-headers.email50
-rw-r--r--spec/fixtures/files/incoming-request-attachment-unknown-extension.email5
-rw-r--r--spec/fixtures/files/many-attachments-date-header.email451
-rw-r--r--spec/fixtures/files/rfc822-attachment.email147
-rw-r--r--spec/lib/i18n_interpolation.rb3
-rw-r--r--spec/lib/mail_handler/mail_handler_spec.rb362
-rw-r--r--spec/models/incoming_message_spec.rb108
-rw-r--r--spec/models/info_request_spec.rb16
-rw-r--r--spec/spec_helper.rb9
52 files changed, 2444 insertions, 873 deletions
diff --git a/.travis.yml b/.travis.yml
index d55b63e79..9cc3fd5a3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,12 +3,14 @@ branches:
only:
- develop
- master
+ - feature/fix-travis-ruby-19-errors
rvm:
- 1.8.7
+ - 1.9.3
before_install:
- - sudo gem install rubygems-update --version=1.6.2
- - sudo gem update --system 1.6.2
- - sudo gem install rake --version=0.8.7
+ - gem install rubygems-update --version=1.6.2
+ - gem update --system 1.6.2
+ - gem install rake --version=0.9.2.2
- git submodule update --init --recursive
- psql -c "create database foi_test template template0 encoding 'SQL_ASCII';" -U postgres
- cp config/database.yml-test config/database.yml
diff --git a/Gemfile b/Gemfile
index 8614d3141..2f2e43384 100644
--- a/Gemfile
+++ b/Gemfile
@@ -27,7 +27,7 @@ gem 'recaptcha', '~> 0.3.1', :require => 'recaptcha/rails'
# :require avoids "already initialized constant" warnings
gem 'rmagick', :require => 'RMagick'
gem 'routing-filter', '~> 0.2.4'
-gem 'rake', '~> 0.8.7'
+gem 'rake', '0.9.2.2'
gem 'rspec', '~> 1.3.2'
gem 'rspec-rails', '~> 1.3.4'
gem 'ruby-msg', '~> 1.5.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 41bc79651..6e6f955a9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -84,7 +84,7 @@ GEM
activeresource (= 2.3.14)
activesupport (= 2.3.14)
rake (>= 0.8.3)
- rake (0.8.7)
+ rake (0.9.2.2)
rbx-require-relative (0.0.9)
rdoc (2.4.3)
recaptcha (0.3.4)
@@ -166,7 +166,7 @@ DEPENDENCIES
pg
rack (~> 1.1.0)
rails (= 2.3.14)
- rake (~> 0.8.7)
+ rake (= 0.9.2.2)
rdoc (~> 2.4.3)
recaptcha (~> 0.3.1)
rmagick
diff --git a/README.md b/README.md
index 6c0c15782..7313df071 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,8 @@ move towards a world where governments approach transparency as the
norm, rather than the exception.
Please join our mailing list at
-https://groups.google.com/group/alaveteli-dev and introduce yourself.
+https://groups.google.com/group/alaveteli-dev and introduce yourself, or
+drop a line to hello@alaveteli.org to let us know that you're using Alaveteli.
Some documentation can be found in the
[`doc/` folder](https://github.com/mysociety/alaveteli/tree/master/doc).
diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb
index 1de63be59..c7c8d4972 100644
--- a/app/controllers/admin_request_controller.rb
+++ b/app/controllers/admin_request_controller.rb
@@ -277,7 +277,7 @@ class AdminRequestController < AdminController
if params[:incoming_message_id]
incoming_message = IncomingMessage.find(params[:incoming_message_id])
- email = incoming_message.from_address
+ email = incoming_message.from_email
name = incoming_message.safe_mail_from || info_request.public_body.name
else
email = info_request.public_body.request_email
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index aa5e85db3..15fb4f5f9 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -1,30 +1,30 @@
class ApiController < ApplicationController
before_filter :check_api_key
-
+
def show_request
@request = InfoRequest.find(params[:id])
raise PermissionDenied if @request.public_body_id != @public_body.id
-
+
@request_data = {
:id => @request.id,
:url => make_url("request", @request.url_title),
:title => @request.title,
-
+
:created_at => @request.created_at,
:updated_at => @request.updated_at,
-
+
:status => @request.calculate_status,
-
+
:public_body_url => make_url("body", @request.public_body.url_name),
:requestor_url => make_url("user", @request.user.url_name),
:request_email => @request.incoming_email,
-
+
:request_text => @request.last_event_forming_initial_request.outgoing_message.body,
}
-
+
render :json => @request_data
end
-
+
def create_request
json = ActiveSupport::JSON.decode(params[:request_json])
request = InfoRequest.new(
@@ -34,7 +34,7 @@ class ApiController < ApplicationController
:external_user_name => json["external_user_name"],
:external_url => json["external_url"]
)
-
+
outgoing_message = OutgoingMessage.new(
:status => 'ready',
:message_type => 'initial_request',
@@ -44,7 +44,7 @@ class ApiController < ApplicationController
:info_request => request
)
request.outgoing_messages << outgoing_message
-
+
# Return an error if the request is invalid
# (Can this ever happen?)
if !request.valid?
@@ -53,7 +53,7 @@ class ApiController < ApplicationController
}
return
end
-
+
# Save the request, and add the corresponding InfoRequestEvent
request.save!
request.log_event("sent",
@@ -62,69 +62,69 @@ class ApiController < ApplicationController
:outgoing_message_id => outgoing_message.id,
:smtp_message_id => nil
)
-
+
# Return the URL and ID number.
render :json => {
'url' => make_url("request", request.url_title),
'id' => request.id
}
-
+
end
-
+
def add_correspondence
request = InfoRequest.find_by_id(params[:id])
if request.nil?
render :json => { "errors" => ["Could not find request #{params[:id]}"] }, :status => 404
return
end
-
+
json = ActiveSupport::JSON.decode(params[:correspondence_json])
attachments = params[:attachments]
-
+
direction = json["direction"]
body = json["body"]
sent_at_str = json["sent_at"]
-
+
errors = []
-
+
if !request.is_external?
render :json => { "errors" => ["Request #{params[:id]} cannot be updated using the API"] }, :status => 500
return
end
-
+
if request.public_body_id != @public_body.id
render :json => { "errors" => ["You do not own request #{params[:id]}"] }, :status => 500
return
end
-
+
if !["request", "response"].include?(direction)
errors << "The direction parameter must be 'request' or 'response'"
end
-
+
if body.nil?
errors << "The 'body' is missing"
elsif body.empty?
errors << "The 'body' is empty"
end
-
+
begin
sent_at = Time.iso8601(sent_at_str)
rescue ArgumentError
errors << "Failed to parse 'sent_at' field as ISO8601 time: #{sent_at_str}"
end
-
+
if direction == "request" && !attachments.nil?
errors << "You cannot attach files to messages in the 'request' direction"
end
-
+
if !errors.empty?
render :json => { "errors" => errors }, :status => 500
return
end
-
+
if direction == "request"
# In the 'request' direction, i.e. what we (Alaveteli) regard as outgoing
-
+
outgoing_message = OutgoingMessage.new(
:info_request => request,
:status => 'ready',
@@ -154,19 +154,19 @@ class ApiController < ApplicationController
:filename => filename
)
end
-
+
mail = RequestMailer.create_external_response(request, body, sent_at, attachment_hashes)
request.receive(mail, mail.encoded, true)
end
render :json => {
'url' => make_url("request", request.url_title),
- }
+ }
end
-
+
def body_request_events
feed_type = params[:feed_type]
raise PermissionDenied.new("#{@public_body.id} != #{params[:id]}") if @public_body.id != params[:id].to_i
-
+
since_date_str = params[:since_date]
if since_date_str.nil?
@events = InfoRequestEvent.find_by_sql([
@@ -213,7 +213,7 @@ class ApiController < ApplicationController
@event_data = []
@events.each do |event|
break if event.id == @since_event_id
-
+
request = event.info_request
this_event = {
:request_id => request.id,
@@ -224,13 +224,13 @@ class ApiController < ApplicationController
:request_email => request.incoming_email,
:title => request.title,
:body => event.outgoing_message.body,
-
+
:user_name => request.user_name,
}
if request.user
this_event[:user_url] = main_url(user_url(request.user))
end
-
+
@event_data.push(this_event)
end
render :json => @event_data
@@ -238,14 +238,14 @@ class ApiController < ApplicationController
raise ActiveRecord::RecordNotFound.new("Unrecognised feed type: #{feed_type}")
end
end
-
+
protected
def check_api_key
- raise "Missing required parameter 'k'" if params[:k].nil?
+ raise PermissionDenied.new("Missing required parameter 'k'") if params[:k].nil?
@public_body = PublicBody.find_by_api_key(params[:k].gsub(' ', '+'))
raise PermissionDenied if @public_body.nil?
end
-
+
private
def make_url(*args)
"http://" + Configuration::domain + "/" + args.join("/")
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f9649c868..320d0cc50 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -54,10 +54,15 @@ class ApplicationController < ActionController::Base
end
def set_gettext_locale
+ if Configuration::include_default_locale_in_urls == false
+ params_locale = params[:locale] ? params[:locale] : I18n.default_locale
+ else
+ params_locale = params[:locale]
+ end
if Configuration::use_default_browser_language
- requested_locale = params[:locale] || session[:locale] || cookies[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || I18n.default_locale
+ requested_locale = params_locale || session[:locale] || cookies[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || I18n.default_locale
else
- requested_locale = params[:locale] || session[:locale] || cookies[:locale] || I18n.default_locale
+ requested_locale = params_locale || session[:locale] || cookies[:locale] || I18n.default_locale
end
requested_locale = FastGettext.best_locale_in(requested_locale)
session[:locale] = FastGettext.set_locale(requested_locale)
diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb
index a40898aef..2f8a9ab04 100644
--- a/app/models/foi_attachment.rb
+++ b/app/models/foi_attachment.rb
@@ -38,7 +38,7 @@ class FoiAttachment < ActiveRecord::Base
BODY_MAX_DELAY = 5
def directory
- rails_env = ENV['RAILS_ENV']
+ rails_env = Rails.env
if rails_env.nil? || rails_env.empty?
raise "$RAILS_ENV is not set"
end
@@ -67,9 +67,22 @@ class FoiAttachment < ActiveRecord::Base
file.write d
}
update_display_size!
+ encode_cached_body!
@cached_body = d
end
+ # If the original mail part had a charset, it's some kind of string, so assume that
+ # it should be handled as a string in the stated charset, not a bytearray, and then
+ # convert it our default encoding. For ruby 1.8 this is a noop.
+ def encode_cached_body!
+ if RUBY_VERSION.to_f >= 1.9
+ if charset
+ @cached_body.force_encoding(charset)
+ @cached_body = @cached_body.encode(Encoding.default_internal, charset)
+ end
+ end
+ end
+
def body
if @cached_body.nil?
tries = 0
@@ -90,6 +103,7 @@ class FoiAttachment < ActiveRecord::Base
self.incoming_message.parse_raw_email!(force)
retry
end
+ encode_cached_body!
end
return @cached_body
end
@@ -310,31 +324,41 @@ class FoiAttachment < ActiveRecord::Base
# the extractions will also produce image files, which go in the
# current directory, so change to the directory the function caller
# wants everything in
- Dir.chdir(dir) do
- tempfile = Tempfile.new('foiextract', '.')
- tempfile.print self.body
- tempfile.flush
-
- html = nil
- if self.content_type == 'application/pdf'
- # We set a timeout here, because pdftohtml can spiral out of control
- # on some PDF files and we don’t want to crash the whole server.
- html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", "-noframes", tempfile.path, :timeout => 30)
- elsif self.content_type == 'application/rtf'
- html = AlaveteliExternalCommand.run("unrtf", "--html", tempfile.path, :timeout => 120)
- end
-
- if html.nil?
- if self.has_google_docs_viewer?
- html = '' # force error and using Google docs viewer
+
+ html = nil
+ if ['application/pdf', 'application/rtf'].include?(self.content_type)
+ text = self.body
+ Dir.chdir(dir) do
+ if RUBY_VERSION.to_f >= 1.9
+ tempfile = Tempfile.new('foiextract', '.', :encoding => text.encoding)
else
- raise "No HTML conversion available for type " + self.content_type
+ tempfile = Tempfile.new('foiextract', '.')
end
- end
+ tempfile.print text
+ tempfile.flush
+
- tempfile.close
- tempfile.delete
+ if self.content_type == 'application/pdf'
+ # We set a timeout here, because pdftohtml can spiral out of control
+ # on some PDF files and we don't want to crash the whole server.
+ html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", "-noframes", tempfile.path, :timeout => 30)
+ elsif self.content_type == 'application/rtf'
+ html = AlaveteliExternalCommand.run("unrtf", "--html", tempfile.path, :timeout => 120)
+ end
+
+ tempfile.close
+ tempfile.delete
+ end
end
+ if html.nil?
+ if self.has_google_docs_viewer?
+ html = '' # force error and using Google docs viewer
+ else
+ raise "No HTML conversion available for type " + self.content_type
+ end
+ end
+
+
# We need to look at:
# a) Any error code
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 123319125..a02d2456a 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -70,41 +70,27 @@ class IncomingMessage < ActiveRecord::Base
@mail
end
- def from_address
- self.mail.from_addrs[0].address
- end
-
def empty_from_field?
self.mail.from_addrs.nil? || self.mail.from_addrs.size == 0
end
def from_email
- self.mail.from_addrs[0].spec
+ MailHandler.get_from_address(self.mail)
end
def addresses
- ((self.mail.to || []) +
- (self.mail.cc || []) +
- (self.mail.envelope_to || [])).uniq
+ MailHandler.get_all_addresses(self.mail)
end
def message_id
self.mail.message_id
end
- # Returns the name of the person the incoming message is from, or nil if
- # there isn't one or if there is only an email address. XXX can probably
- # remove from_name_if_present (which is a monkey patch) by just calling
- # .from_addrs[0].name here instead?
-
# Return false if for some reason this is a message that we shouldn't let them reply to
def _calculate_valid_to_reply_to
# check validity of email
- if empty_from_field?
- return false
- end
email = self.from_email
- if !MySociety::Validate.is_valid_email(email)
+ if email.nil? || !MySociety::Validate.is_valid_email(email)
return false
end
@@ -114,13 +100,13 @@ class IncomingMessage < ActiveRecord::Base
prefix = email
prefix =~ /^(.*)@/
prefix = $1
- if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|donotreply|no.reply)$/)
+ if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|do.?not.?reply|no.reply)$/)
return false
end
- if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
+ if MailHandler.empty_return_path?(self.mail)
return false
end
- if !self.mail['auto-submitted'].nil?
+ if !MailHandler.get_auto_submitted(self.mail).nil?
return false
end
return true
@@ -138,13 +124,10 @@ class IncomingMessage < ActiveRecord::Base
self.extract_attachments!
self.sent_at = self.mail.date || self.created_at
self.subject = self.mail.subject
- # XXX can probably remove from_name_if_present (which is a
- # monkey patch) by just calling .from_addrs[0].name here
- # instead?
- self.mail_from = self.mail.from_name_if_present
- begin
+ self.mail_from = MailHandler.get_from_name(self.mail)
+ if self.from_email
self.mail_from_domain = PublicBody.extract_domain_from_email(self.from_email)
- rescue NoMethodError
+ else
self.mail_from_domain = ""
end
self.valid_to_reply_to = self._calculate_valid_to_reply_to
@@ -190,54 +173,8 @@ class IncomingMessage < ActiveRecord::Base
super
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
- @count_parts_count = 0
- _count_parts_recursive(self.mail)
- # we carry on using these numeric ids for attachments uudecoded from within text parts
- @count_first_uudecode_count = @count_parts_count
- end
- def _count_parts_recursive(part)
- if part.multipart?
- part.parts.each do |p|
- _count_parts_recursive(p)
- 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)
- end
- end
- if part.rfc822_attachment.nil?
- @count_parts_count += 1
- part.url_part_number = @count_parts_count
- end
- end
- end
# And look up by URL part number to get an attachment
- # XXX relies on extract_attachments calling ensure_parts_counted
+ # XXX relies on extract_attachments calling MailHandler.ensure_parts_counted
def self.get_attachment_by_url_part_number(attachments, found_url_part_number)
attachments.each do |a|
if a.url_part_number == found_url_part_number
@@ -458,95 +395,6 @@ class IncomingMessage < ActiveRecord::Base
return text
end
- # (This risks losing info if the unchosen alternative is the only one to contain
- # useful info, but let's worry about that another time)
- def get_attachment_leaves
- force = true
- return _get_attachment_leaves_recursive(self.mail(force))
- end
- def _get_attachment_leaves_recursive(curr_mail, within_rfc822_attachment = nil)
- leaves_found = []
- if curr_mail.multipart?
- if curr_mail.parts.size == 0
- raise "no parts on multipart mail"
- end
-
- if curr_mail.sub_type == 'alternative'
- # Choose best part from alternatives
- best_part = nil
- # Take the last text/plain one, or else the first one
- curr_mail.parts.each do |m|
- if not best_part
- best_part = m
- elsif m.content_type == 'text/plain'
- best_part = m
- end
- end
- # Take an HTML one as even higher priority. (They tend
- # to render better than text/plain, e.g. don't wrap links here:
- # http://www.whatdotheyknow.com/request/amount_and_cost_of_freedom_of_in#incoming-72238 )
- curr_mail.parts.each do |m|
- if m.content_type == 'text/html'
- best_part = m
- end
- end
- leaves_found += _get_attachment_leaves_recursive(best_part, within_rfc822_attachment)
- else
- # Add all parts
- curr_mail.parts.each do |m|
- leaves_found += _get_attachment_leaves_recursive(m, within_rfc822_attachment)
- end
- end
- else
- # XXX Yuck. this section alters various content_type's. That puts
- # it into conflict with ensure_parts_counted which it has to be
- # called both before and after. It will fail with cases of
- # attachments of attachments etc.
- charset = curr_mail.charset # save this, because overwriting content_type also resets charset
- # Don't allow nil content_types
- if curr_mail.content_type.nil?
- curr_mail.content_type = 'application/octet-stream'
- end
- # PDFs often come with this mime type, fix it up for view code
- if curr_mail.content_type == 'application/octet-stream'
- part_file_name = MailHandler.get_part_file_name(curr_mail)
- calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, curr_mail.body)
- if calc_mime
- curr_mail.content_type = calc_mime
- end
- end
-
- # Use standard content types for Word documents etc.
- curr_mail.content_type = normalise_content_type(curr_mail.content_type)
- if curr_mail.content_type == 'message/rfc822'
- ensure_parts_counted # fills in rfc822_attachment variable
- if curr_mail.rfc822_attachment.nil?
- # Attached mail didn't parse, so treat as text
- curr_mail.content_type = 'text/plain'
- end
- end
- if curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
- ensure_parts_counted # fills in rfc822_attachment variable
- if curr_mail.rfc822_attachment.nil?
- # Attached mail didn't parse, so treat as binary
- curr_mail.content_type = 'application/octet-stream'
- end
- end
- # If the part is an attachment of email
- if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
- ensure_parts_counted # fills in rfc822_attachment variable
- leaves_found += _get_attachment_leaves_recursive(curr_mail.rfc822_attachment, curr_mail.rfc822_attachment)
- else
- # Store leaf
- curr_mail.within_rfc822_attachment = within_rfc822_attachment
- leaves_found += [curr_mail]
- end
- # restore original charset
- curr_mail.charset = charset
- end
- return leaves_found
- end
-
# Removes anything cached about the object in the database, and saves
def clear_in_database_caches!
self.cached_attachment_text_clipped = nil
@@ -609,7 +457,8 @@ class IncomingMessage < ActiveRecord::Base
text = "[ Email has no body, please see attachments ]"
source_charset = "utf-8"
else
- text = part.body # by default, TMail converts to UTF8 in this call
+ # by default, the body (coming from an foi_attachment) should have been converted to utf-8
+ text = part.body
source_charset = part.charset
if part.content_type == 'text/html'
# e.g. http://www.whatdotheyknow.com/request/35/response/177
@@ -617,42 +466,31 @@ class IncomingMessage < ActiveRecord::Base
# convert to text routine. Could instead call a
# sanitize HTML one.
- # If the text isn't UTF8, it means TMail had a problem
+ # If the text isn't UTF8, it means we had a problem
# converting it (invalid characters, etc), and we
# should instead tell elinks to respect the source
# charset
use_charset = "utf-8"
- begin
- text = Iconv.conv('utf-8', 'utf-8', text)
- rescue Iconv::IllegalSequence
- use_charset = source_charset
- end
- text = self.class._get_attachment_text_internal_one_file(part.content_type, text, use_charset)
- end
- end
-
- # If TMail can't convert text, it just returns it, so we sanitise it.
- begin
- # Test if it's good UTF-8
- text = Iconv.conv('utf-8', 'utf-8', text)
- rescue Iconv::IllegalSequence
- # Text looks like unlabelled nonsense,
- # strip out anything that isn't UTF-8
- begin
- source_charset = 'utf-8' if source_charset.nil?
- text = Iconv.conv('utf-8//IGNORE', source_charset, text) +
- _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
- :site_name => Configuration::site_name)
- rescue Iconv::InvalidEncoding, Iconv::IllegalSequence
- if source_charset != "utf-8"
- source_charset = "utf-8"
- retry
+ if RUBY_VERSION.to_f >= 1.9
+ begin
+ text.encode('utf-8')
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ use_charset = source_charset
+ end
+ else
+ begin
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ use_charset = source_charset
+ end
end
+ text = MailHandler.get_attachment_text_one_file(part.content_type, text, use_charset)
end
end
+ # If text hasn't been converted, we sanitise it.
+ text = _sanitize_text(text)
# Fix DOS style linefeeds to Unix style ones (or other later regexps won't work)
- # Needed for e.g. http://www.whatdotheyknow.com/request/60/response/98
text = text.gsub(/\r\n/, "\n")
# Compress extra spaces down to save space, and to stop regular expressions
@@ -662,6 +500,51 @@ class IncomingMessage < ActiveRecord::Base
return text
end
+
+ def _sanitize_text(text)
+ if RUBY_VERSION.to_f >= 1.9
+ begin
+ # Test if it's good UTF-8
+ text.encode('utf-8')
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ source_charset = 'utf-8' if source_charset.nil?
+ # strip out anything that isn't UTF-8
+ begin
+ text = text.encode("utf-8", :invalid => :replace,
+ :undef => :replace,
+ :replace => "") +
+ _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
+ :site_name => MySociety::Config.get('SITE_NAME', 'Alaveteli'))
+ rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
+ if source_charset != "utf-8"
+ source_charset = "utf-8"
+ retry
+ end
+ end
+ end
+ else
+ begin
+ # Test if it's good UTF-8
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ # Text looks like unlabelled nonsense,
+ # strip out anything that isn't UTF-8
+ begin
+ source_charset = 'utf-8' if source_charset.nil?
+ text = Iconv.conv('utf-8//IGNORE', source_charset, text) +
+ _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
+ :site_name => Configuration::site_name)
+ rescue Iconv::InvalidEncoding, Iconv::IllegalSequence
+ if source_charset != "utf-8"
+ source_charset = "utf-8"
+ retry
+ end
+ end
+ end
+ end
+ text
+ end
+
# Returns part which contains main body text, or nil if there isn't one
def get_main_body_text_part
leaves = self.foi_attachments
@@ -715,7 +598,7 @@ class IncomingMessage < ActiveRecord::Base
filename = uu.match(/^begin\s+[0-9]+\s+(.*)$/)[1]
calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(filename, content)
if calc_mime
- calc_mime = normalise_content_type(calc_mime)
+ calc_mime = MailHandler.normalise_content_type(calc_mime)
content_type = calc_mime
else
content_type = 'application/octet-stream'
@@ -744,55 +627,15 @@ class IncomingMessage < ActiveRecord::Base
end
def extract_attachments!
- leaves = get_attachment_leaves # XXX check where else this is called from
- # XXX we have to call ensure_parts_counted after get_attachment_leaves
- # which is really messy.
- ensure_parts_counted
+ force = true
+ attachment_attributes = MailHandler.get_attachment_attributes(self.mail(force))
attachments = []
- for leaf in leaves
- body = leaf.body
- # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
- # to prevent excess memory use. XXX not really sure if this helps reduce
- # peak RAM use overall. Anyway, maybe there is something better to do than this.
- GC.start
- if leaf.within_rfc822_attachment
- within_rfc822_subject = leaf.within_rfc822_attachment.subject
- # Test to see if we are in the first part of the attached
- # RFC822 message and it is text, if so add headers.
- # XXX should probably use hunting algorithm to find main text part, rather than
- # just expect it to be first. This will do for now though.
- # Example request that needs this:
- # http://www.whatdotheyknow.com/request/2923/response/7013/attach/2/Cycle%20Path%20Bank.txt
- if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
- headers = ""
- for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
- if leaf.within_rfc822_attachment.header.include?(header.downcase)
- header_value = leaf.within_rfc822_attachment.header[header.downcase]
- # Example message which has a blank Date header:
- # http://www.whatdotheyknow.com/request/30747/response/80253/attach/html/17/Common%20Purpose%20Advisory%20Group%20Meeting%20Tuesday%202nd%20March.txt.html
- if !header_value.blank?
- headers = headers + header + ": " + header_value.to_s + "\n"
- end
- end
- end
- # XXX call _convert_part_body_to_text here, but need to get charset somehow
- # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
- body = headers + "\n" + body
-
- # This is quick way of getting all headers, but instead we only add some a) to
- # make it more usable, b) as at least one authority accidentally leaked security
- # information into a header.
- #attachment.body = leaf.within_rfc822_attachment.port.to_s
- end
- end
- hexdigest = Digest::MD5.hexdigest(body)
- attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest)
- attachment.update_attributes(:url_part_number => leaf.url_part_number,
- :content_type => leaf.content_type,
- :filename => MailHandler.get_part_file_name(leaf),
- :charset => leaf.charset,
- :within_rfc822_subject => within_rfc822_subject,
- :body => body)
+ attachment_attributes.each do |attrs|
+ attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => attrs[:hexdigest])
+ body = attrs.delete(:body)
+ attachment.update_attributes(attrs)
+ # Set the body separately as its handling can depend on the value of charset
+ attachment.body = body
attachment.save!
attachments << attachment.id
end
@@ -802,7 +645,7 @@ class IncomingMessage < ActiveRecord::Base
# e.g. for https://secure.mysociety.org/admin/foi/request/show_raw_email/24550
if !main_part.nil?
uudecoded_attachments = _uudecode_and_save_attachments(main_part.body)
- c = @count_first_uudecode_count
+ c = self.mail.count_first_uudecode_count
for uudecode_attachment in uudecoded_attachments
c += 1
uudecode_attachment.url_part_number = c
@@ -894,101 +737,15 @@ class IncomingMessage < ActiveRecord::Base
return self.cached_attachment_text_clipped
end
- def IncomingMessage._get_attachment_text_internal_one_file(content_type, body, charset = 'utf-8')
- # note re. charset: TMail always tries to convert email bodies
- # to UTF8 by default, so normally it should already be that.
- text = ''
- # XXX - tell all these command line tools to return utf-8
- if content_type == 'text/plain'
- text += body + "\n\n"
- else
- tempfile = Tempfile.new('foiextract')
- tempfile.print body
- tempfile.flush
- if content_type == 'application/vnd.ms-word'
- AlaveteliExternalCommand.run("wvText", tempfile.path, tempfile.path + ".txt")
- # Try catdoc if we get into trouble (e.g. for InfoRequestEvent 2701)
- if not File.exists?(tempfile.path + ".txt")
- AlaveteliExternalCommand.run("catdoc", tempfile.path, :append_to => text)
- else
- text += File.read(tempfile.path + ".txt") + "\n\n"
- File.unlink(tempfile.path + ".txt")
- end
- elsif content_type == 'application/rtf'
- # catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf
- AlaveteliExternalCommand.run("catdoc", tempfile.path, :append_to => text)
- elsif content_type == 'text/html'
- # lynx wordwraps links in its output, which then don't
- # get formatted properly by Alaveteli. We use elinks
- # instead, which doesn't do that.
- AlaveteliExternalCommand.run("elinks", "-eval", "set document.codepage.assume = \"#{charset}\"", "-eval", "set document.codepage.force_assumed = 1", "-dump-charset", "utf-8", "-force-html", "-dump",
- tempfile.path, :append_to => text, :env => {"LANG" => "C"})
- elsif content_type == 'application/vnd.ms-excel'
- # Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
- # py_xls2txt only extract text from cells, not from floating
- # notes. catdoc may be fooled by weird character sets, but will
- # probably do for UK FOI requests.
- AlaveteliExternalCommand.run("/usr/bin/strings", tempfile.path, :append_to => text)
- elsif content_type == 'application/vnd.ms-powerpoint'
- # ppthtml seems to catch more text, but only outputs HTML when
- # we want text, so just use catppt for now
- AlaveteliExternalCommand.run("catppt", tempfile.path, :append_to => text)
- elsif content_type == 'application/pdf'
- AlaveteliExternalCommand.run("pdftotext", tempfile.path, "-", :append_to => text)
- elsif content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
- # This is Microsoft's XML office document format.
- # Just pull out the main XML file, and strip it of text.
- xml = AlaveteliExternalCommand.run("/usr/bin/unzip", "-qq", "-c", tempfile.path, "word/document.xml")
- if !xml.nil?
- doc = REXML::Document.new(xml)
- text += doc.each_element( './/text()' ){}.join(" ")
- end
- elsif content_type == 'application/zip'
- # recurse into zip files
- begin
- zip_file = Zip::ZipFile.open(tempfile.path)
- text += IncomingMessage._get_attachment_text_from_zip_file(zip_file)
- zip_file.close()
- rescue
- $stderr.puts("Error processing zip file: #{$!.inspect}")
- end
- end
- tempfile.close
- end
-
- return text
- end
- def IncomingMessage._get_attachment_text_from_zip_file(zip_file)
- text = ""
- for entry in zip_file
- if entry.file?
- filename = entry.to_s
- begin
- body = entry.get_input_stream.read
- rescue
- # move to next attachment silently if there were problems
- # XXX really should reduce this to specific exceptions?
- # e.g. password protected
- next
- end
- calc_mime = AlaveteliFileTypes.filename_to_mimetype(filename)
- if calc_mime
- content_type = calc_mime
- else
- content_type = 'application/octet-stream'
- end
- text += _get_attachment_text_internal_one_file(content_type, body)
- end
- end
- return text
- end
def _get_attachment_text_internal
# Extract text from each attachment
text = ''
attachments = self.get_attachments_for_display
for attachment in attachments
- text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body, attachment.charset)
+ text += MailHandler.get_attachment_text_one_file(attachment.content_type,
+ attachment.body,
+ attachment.charset)
end
# Remove any bad characters
text = Iconv.conv('utf-8//IGNORE', 'utf-8', text)
@@ -1056,65 +813,11 @@ class IncomingMessage < ActiveRecord::Base
return AlaveteliFileTypes.all_extensions.join(" ")
end
- # Return false if for some reason this is a message that we shouldn't let them reply to
- def valid_to_reply_to?
- # check validity of email
- if empty_from_field?
- return false
- end
- email = self.from_email
- if !MySociety::Validate.is_valid_email(email)
- return false
- end
-
- # reject postmaster - authorities seem to nearly always not respond to
- # email to postmaster, and it tends to only happen after delivery failure.
- # likewise Mailer-Daemon, Auto_Reply...
- prefix = email
- prefix =~ /^(.*)@/
- prefix = $1
- if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|do.?not.?reply|no.reply)$/)
- return false
- end
- if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
- return false
- end
- if !self.mail['auto-submitted'].nil?
- return false
- end
- return true
- end
-
- def normalise_content_type(content_type)
- # e.g. http://www.whatdotheyknow.com/request/93/response/250
- if content_type == 'application/excel' or content_type == 'application/msexcel' or content_type == 'application/x-ms-excel'
- content_type = 'application/vnd.ms-excel'
- end
- if content_type == 'application/mspowerpoint' or content_type == 'application/x-ms-powerpoint'
- content_type = 'application/vnd.ms-powerpoint'
+ def for_admin_column
+ self.class.content_columns.each do |column|
+ yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
- if content_type == 'application/msword' or content_type == 'application/x-ms-word'
- content_type = 'application/vnd.ms-word'
- end
- if content_type == 'application/x-zip-compressed'
- content_type = 'application/zip'
- end
-
- # e.g. http://www.whatdotheyknow.com/request/copy_of_current_swessex_scr_opt#incoming-9928
- if content_type == 'application/acrobat'
- content_type = 'application/pdf'
- end
-
- return content_type
- end
-
- def for_admin_column
- self.class.content_columns.each do |column|
- yield(column.human_name, self.send(column.name), column.type.to_s, column.name)
end
- end
-
- private :normalise_content_type
end
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 194f8e105..3355b9443 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -138,7 +138,7 @@ class InfoRequest < ActiveRecord::Base
if external_user_name.nil?
fake_slug = "anonymous"
else
- fake_slug = external_user_name.parameterize
+ fake_slug = MySociety::Format.simplify_url_part(external_user_name, 'external_user', 32)
end
(public_body.url_name || "") + "_" + fake_slug
else
@@ -434,11 +434,11 @@ public
elsif self.allow_new_responses_from == 'anybody'
allow = true
elsif self.allow_new_responses_from == 'authority_only'
- if email.from_addrs.nil? || email.from_addrs.size == 0
+ sender_email = MailHandler.get_from_address(email)
+ if sender_email.nil?
allow = false
reason = _('Only the authority can reply to this request, but there is no "From" address to check against')
else
- sender_email = email.from_addrs[0].spec
sender_domain = PublicBody.extract_domain_from_email(sender_email)
reason = _("Only the authority can reply to this request, and I don't recognise the address this reply was sent from")
allow = false
diff --git a/app/views/general/_frontpage_new_request.rhtml b/app/views/general/_frontpage_new_request.rhtml
index fd4225069..499b60eb5 100644
--- a/app/views/general/_frontpage_new_request.rhtml
+++ b/app/views/general/_frontpage_new_request.rhtml
@@ -4,4 +4,4 @@
Information<br/>
request</strong>") %>
</h1>
-<a class="link_button_green_large" href="/select_authority"><%= _("Start now &raquo;") %></a>
+<a class="link_button_green_large" href="<%= select_authority_path %>"><%= _("Start now &raquo;") %></a>
diff --git a/app/views/general/_frontpage_search_box.rhtml b/app/views/general/_frontpage_search_box.rhtml
index 6de4eae98..d2718b3a3 100644
--- a/app/views/general/_frontpage_search_box.rhtml
+++ b/app/views/general/_frontpage_search_box.rhtml
@@ -4,7 +4,7 @@
<strong>{{number_of_authorities}} authorities</strong>",
:number_of_requests => InfoRequest.visible.count, :number_of_authorities => PublicBody.visible.count) %>
</h2>
-<form id="search_form" method="post" action="/search">
+<form id="search_form" method="post" action="<%= search_redirect_path %>">
<div>
<input id="query" type="text" size="30" name="query">
<input type="submit" value="<%= _('Search') %>">
diff --git a/app/views/help/_sidebar.rhtml b/app/views/help/_sidebar.rhtml
index 783d35983..2b7ed5647 100644
--- a/app/views/help/_sidebar.rhtml
+++ b/app/views/help/_sidebar.rhtml
@@ -1,19 +1,19 @@
<div id="right_column_flip">
<h2>Help pages</h2>
<ul class="no_bullets">
- <li><%= link_to_unless_current "Introduction", "/help/about" %></li>
- <li><%= link_to_unless_current "Making requests", "/help/requesting" %></li>
- <li><%= link_to_unless_current "Your privacy", "/help/privacy" %></li>
- <li><%= link_to_unless_current "FOI officers", "/help/officers" %></li>
- <li><%= link_to_unless_current "About the software", "/help/alaveteli" %></li>
- <li><%= link_to_unless_current "Credits", "/help/credits" %></li>
- <li><%= link_to_unless_current "Programmers API", "/help/api" %></li>
- <li><%= link_to_unless_current "Advanced search", "/advancedsearch" %></li>
+ <li><%= link_to_unless_current "Introduction", help_about_path %></li>
+ <li><%= link_to_unless_current "Making requests", help_requesting_path %></li>
+ <li><%= link_to_unless_current "Your privacy", help_privacy_path %></li>
+ <li><%= link_to_unless_current "FOI officers", help_officers_path %></li>
+ <li><%= link_to_unless_current "About the software", help_alaveteli_path %></li>
+ <li><%= link_to_unless_current "Credits", help_credits_path %></li>
+ <li><%= link_to_unless_current "Programmers API", help_api_path %></li>
+ <li><%= link_to_unless_current "Advanced search", advanced_search_path %></li>
</ul>
<h2 id="contact">Contact us</h2>
<p>If your question isn't answered here, or you just wanted to let us know
- something about the site, <a href="/help/contact">contact&nbsp;us</a>.
+ something about the site, <a href="<%= help_contact_path %>">contact&nbsp;us</a>.
</p>
</div>
diff --git a/app/views/help/about.rhtml b/app/views/help/about.rhtml
index 9f75cac8b..477f0e750 100644
--- a/app/views/help/about.rhtml
+++ b/app/views/help/about.rhtml
@@ -41,13 +41,13 @@
</dd>
<dt id="updates">How can I keep up with news about WhatDoTheyKnow?<a href="#updates">#</a> </dt>
- <dd>We have a <a href="/blog">blog</a> and a <a href="https://twitter.com/whatdotheyknow">twitter feed</a>.
+ <dd>We have a <a href="<%= blog_path %>">blog</a> and a <a href="https://twitter.com/whatdotheyknow">twitter feed</a>.
</dd>
</dl>
- <p><strong>Next</strong>, read about <a href="/help/requesting">making requests</a> --&gt;
+ <p><strong>Next</strong>, read about <a href="<%= help_requesting_path %>">making requests</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/api.rhtml b/app/views/help/api.rhtml
index facddce41..da6253f87 100644
--- a/app/views/help/api.rhtml
+++ b/app/views/help/api.rhtml
@@ -19,7 +19,7 @@
<dt>Linking to new requests</dt>
<dd>
<p>To encourage your users to make links to a particular public authority, use URLs of the form
- <%= link_to new_request_to_body_url(:url_name => "liverpool_city_council") , new_request_to_body_url(:url_name => "liverpool_city_council") %>.
+ <%= link_to new_request_to_body_url(:url_name => "liverpool_city_council") , new_request_to_body_url(:url_name => "liverpool_city_council") %>.
These are the parameters you can add to those URLs, either in the URL or from a form.
<ul>
@@ -34,7 +34,7 @@
<dd>
<p>There are Atom feeds on most pages which list FOI requests, which you can
use to get updates and links in XML format. Find the URL of the Atom feed in
- one of these ways:
+ one of these ways:
<ul>
<li>Look for the <img src="/images/feed-16.png" alt=""> RSS feed links.</li>
<li>Examine the <tt>&lt;link rel="alternate" type="application/atom+xml"&gt;</tt> tag in the head of the HTML. </li>
@@ -43,7 +43,7 @@
<p>In particular, even complicated search queries have Atom feeds.
You can do all sorts of things with them, such as query by authority, by file
- type, by date range, or by status. See the <a href="/search">advanced search
+ type, by date range, or by status. See the <a href="<%= advanced_search_path %>">advanced search
tips</a> for details.
</dd>
@@ -62,7 +62,7 @@
information about the list of events in the feed.
</p>
</dd>
-
+
<dt>Spreadsheet of all authorities</dt>
<dd>
<p>
@@ -73,7 +73,7 @@
</dd>
</dl>
- <p>Please <a href="/help/contact">contact us</a> if you need an API feature that isn't there yet. It's
+ <p>Please <a href="<%= help_contact_path %>">contact us</a> if you need an API feature that isn't there yet. It's
very much a work in progress, and we do add things when people ask us to.</p>
<div id="hash_link_padding"></div>
diff --git a/app/views/help/contact.rhtml b/app/views/help/contact.rhtml
index 37df68f49..fab5017b8 100644
--- a/app/views/help/contact.rhtml
+++ b/app/views/help/contact.rhtml
@@ -9,13 +9,13 @@
<% if !flash[:notice] %>
<h2>Contact an authority to get official information</h2>
<ul>
- <li><a href="/new">Go here</a> to make a request, in public, for information
+ <li><a href="<%= new_request_path %>">Go here</a> to make a request, in public, for information
from public authorities.</li>
<li>
Asking for private information about yourself?
Please read our
- <a href="/help/requesting#data_protection">help page</a>.
+ <a href="<%= help_requesting_path(:anchor => 'data_protection') %>">help page</a>.
</li>
</ul>
@@ -25,10 +25,10 @@
<% if !flash[:notice] %>
<ul>
<li>
- Please read the <a href="/help/about">help page</a> first, as it may
- answer your question quicker.
+ Please read the <a href="<%= help_about_path %>">help page</a> first, as it may
+ answer your question quicker.
</li>
-
+
<li>We'd love to hear how you've found using this site.
Either fill in this form, or send an email to <a
href="mailto:<%=@contact_email%>"><%=@contact_email%></a>
@@ -56,25 +56,25 @@
<% end %>
<p>
- <label class="form_label" for="contact_subject">Subject:</label>
+ <label class="form_label" for="contact_subject">Subject:</label>
<%= f.text_field :subject, :size => 50 %>
</p>
<p>
- <label class="form_label" for="contact_message">Message to website:</label>
+ <label class="form_label" for="contact_message">Message to website:</label>
<%= f.text_area :message, :rows => 10, :cols => 60 %>
</p>
<% if !@last_request.nil? %>
<p>
- <label class="form_label" for="contact_message">Include link to request:</label>
+ <label class="form_label" for="contact_message">Include link to request:</label>
<%=request_link(@last_request) %>
<%= submit_tag "remove", :name => 'remove' %>
</p>
<% end %>
<% if !@last_body.nil? %>
<p>
- <label class="form_label" for="contact_message">Include link to authority:</label>
+ <label class="form_label" for="contact_message">Include link to authority:</label>
<%=public_body_link(@last_body) %>
<%= submit_tag "remove", :name => 'remove' %>
</p>
diff --git a/app/views/help/officers.rhtml b/app/views/help/officers.rhtml
index 3defec62f..b13e225fe 100644
--- a/app/views/help/officers.rhtml
+++ b/app/views/help/officers.rhtml
@@ -19,13 +19,13 @@
</p>
<p>If you have privacy or other concerns, please read the answers below.
You might also like to read the <a
- href="/help/about">introduction to WhatDoTheyKnow</a> to find out more about what
+ href="<%= help_about_path %>">introduction to WhatDoTheyKnow</a> to find out more about what
the site does from the point of view of a user. You can also search the
site to find the authority that you work for, and view the status of
any requests made using the site.
<p>Finally, we welcome comments and
- thoughts from FOI officers, please <a href="/help/contact">get in touch</a>.
+ thoughts from FOI officers, please <a href="<%= help_contact_path %>">get in touch</a>.
</p>
</dd>
@@ -75,13 +75,13 @@
the authority by email. Any delivery failure messages will automatically
appear on the site. You can check the address we're using with the "View FOI
email address" link which appears on the page for the authority. <a
- href="/help/contact">Contact us</a> if there is a better address we can
+ href="<%= help_contact_path %>">Contact us</a> if there is a better address we can
use.</p>
<p>Requests are sometimes not delivered because they are quietly removed by
"spam filters" in the IT department of the authority. Authorities can make
- sure this doesn't happen by asking their IT departments to "whitelist"
+ sure this doesn't happen by asking their IT departments to "whitelist"
any email from <strong>@whatdotheyknow.com</strong>.
- If you <a href="/help/contact">ask us</a> we will resend any request,
+ If you <a href="<%= help_contact_path %>">ask us</a> we will resend any request,
and/or give technical details of delivery so an IT department can chase
up what happened to the message.
</p>
@@ -159,7 +159,7 @@
</li>
</ul>
- <p>If you're getting really nerdy about all this, read the <a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/timeforcompliance.pdf">detailed ICO guidance</a>.
+ <p>If you're getting really nerdy about all this, read the <a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/timeforcompliance.pdf">detailed ICO guidance</a>.
Meanwhile, remember that the law says authorities must respond
<strong>promptly</strong>. That's really what matters.</p>
@@ -173,14 +173,14 @@
extension when applying a <strong>public interest test</strong>. Information
Commissioner guidance says that it should only be used in "exceptionally
complex" cases
- (<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/foi_good_practice_guidance_4.pdf">FOI Good Practice Guidance No. 4</a>).
+ (<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/foi_good_practice_guidance_4.pdf">FOI Good Practice Guidance No. 4</a>).
WhatDoTheyKnow doesn't specifically handle this case, which is why we use the
phrase "should normally have responded by" when the 20 working day time is
- exceeded.
+ exceeded.
</p>
<p>The same guidance says that, even in exceptionally complex cases, no
- Freedom of Information request should take more than <strong>40 working days</strong>
+ Freedom of Information request should take more than <strong>40 working days</strong>
to answer. WhatDoTheyKnow displays requests which are overdue by that much
with stronger wording to indicate they are definitely late.
</p>
@@ -191,7 +191,7 @@
of 40 working days even with the extension (the House of Lords <a
href="http://www.publicwhip.org.uk/division.php?date=2000-10-17&amp;number=1&amp;house=lords">voted
to remove</a> provision for such a time limit during the initial passage
- of the UK Act through Parliament).
+ of the UK Act through Parliament).
</p>
</dd>
@@ -199,7 +199,7 @@
<dd>Instead of email, you can respond to a request directly from your web
browser, including uploading a file. To do this, choose "respond to request" at
- the bottom of the request's page. <a href="/help/contact">Contact us</a> if it
+ the bottom of the request's page. <a href="<%= help_contact_path %>">Contact us</a> if it
is too big for even that (more than, say, 50Mb).
</dd>
@@ -207,17 +207,17 @@
<dd>We consider what officers or servants do in the course of their employment
to be public information. We will only remove content in exceptional
- circumstances, see our <a href="/help/privacy#takedown">take down policy</a>.
+ circumstances, see our <a href="<%= help_privacy_path(:anchor => 'takedown') %>">take down policy</a>.
</dd>
<dt id="mobiles">Do you publish email addresses or mobile phone numbers? <a href="#mobiles">#</a> </dt>
<dd><p>To prevent spam, we automatically remove most emails and some mobile numbers from
- responses to requests. Please <a href="/help/contact">contact us</a> if we've
- missed one.
+ responses to requests. Please <a href="<%= help_contact_path %>">contact us</a> if we've
+ missed one.
For technical reasons we don't always remove them from attachments, such as certain PDFs.</p>
<p>If you need to know what an address was that we've removed, please <a
- href="/help/contact">get in touch with us</a>. Occasionally, an email address
+ href="<%= help_contact_path %>">get in touch with us</a>. Occasionally, an email address
forms an important part of a response and we will post it up in an obscured
form in an annotation.
</dd>
@@ -225,10 +225,10 @@
<dt id="copyright"><a name="commercial"></a>What is your policy on copyright of documents?<a href="#copyright">#</a> </dt>
<dd>Our Freedom of Information law is "applicant blind", so anyone in the
- world can request the same document and get a copy of it.
+ world can request the same document and get a copy of it.
If you think our making a document available on the internet infringes your
- copyright, you may <a href="/help/contact">contact us</a> and ask us
+ copyright, you may <a href="<%= help_contact_path %>">contact us</a> and ask us
to take it down. However, to save tax payers' money by preventing duplicate
requests, and for good public relations, we'd advise you not to do that.
</dd>
@@ -238,8 +238,8 @@
</dl>
- <p><strong>If you haven't already</strong>, read <a href="/help/about">the introduction</a> --&gt;
- <br><strong>Otherwise</strong>, the <a href="/help/credits">credits</a> or the <a href="/help/api">programmers API</a> --&gt;
+ <p><strong>If you haven't already</strong>, read <a href="<%= help_about_path %>">the introduction</a> --&gt;
+ <br><strong>Otherwise</strong>, the <a href="<%= help_credits_path %>">credits</a> or the <a href="<%= help_api_path %>">programmers API</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/privacy.rhtml b/app/views/help/privacy.rhtml
index bec0c8c23..8e5293892 100644
--- a/app/views/help/privacy.rhtml
+++ b/app/views/help/privacy.rhtml
@@ -10,7 +10,7 @@
<dd><p>We will not disclose your email address to anyone unless we are obliged to by law,
or you ask us to. This includes the public authority that you are sending a
- request to. They only get to see an email address
+ request to. They only get to see an email address
@whatdotheyknow.com which is specific to that request. </p>
<p>If you send a message to another user on the site, then it will reveal your
email address to them. You will be told that this is going to happen.</p>
@@ -35,7 +35,7 @@
Your name is tangled up with your request, so has to be published as well.
It is only fair, as we're going to publish the name of the civil servant who
writes the response to your request. Using your real name also helps people
- get in touch with you to assist you with your research or to campaign with you.
+ get in touch with you to assist you with your research or to campaign with you.
</p>
<p>By law, you must use your real name for the request to be a valid Freedom of
Information request. See the next question for alternatives if you do not want
@@ -66,16 +66,16 @@ Information Commissioner later about the handling of your request.
<ul>
<li>Use a different form of your name. The guidance says
that "Mr Arthur Thomas Roberts" can make a valid request as "Arthur Roberts",
-"A. T. Roberts", or "Mr Roberts", but <strong>not</strong> as "Arthur" or "A.T.R.".
+"A. T. Roberts", or "Mr Roberts", but <strong>not</strong> as "Arthur" or "A.T.R.".
</li>
<li>Women may use their maiden name.</li>
<li>In most cases, you may use any name by which you are "widely known and/or
is regularly used".
<li>Use the name of an organisation, the name of a company, the trading name of
a company, or the trading name of a sole trader.
-<li>Ask someone else to make the request on your behalf.
+<li>Ask someone else to make the request on your behalf.
<li>You may, if you are really stuck, ask us to make the request on
-your behalf. Please <a href="/help/contact">contact us</a> with
+your behalf. Please <a href="<%= help_contact_path %>">contact us</a> with
a good reason why you cannot make the request yourself and cannot
ask a friend to. We don't have the resources to do this for everyone.
</ul>
@@ -88,19 +88,19 @@ ask a friend to. We don't have the resources to do this for everyone.
<dd>
<p>If a public authority asks you for your full, physical address, reply to them saying
-that section 8.1.b of the FOI Act asks for an "address for correspondence",
-and that the email address you are using is sufficient.
+that section 8.1.b of the FOI Act asks for an "address for correspondence",
+and that the email address you are using is sufficient.
</p>
<p>
The Ministry of Justice has <a href="http://www.justice.gov.uk/guidance/foi-procedural-what.htm">guidance
on this</a> &ndash;
<em>"As well as hard copy written correspondence, requests that are
-transmitted electronically (for example, in emails) are acceptable
+transmitted electronically (for example, in emails) are acceptable
... If a request is received by email and no postal address is given, the email
address should be treated as the return address."
</em>
</p>
-<p>As if that isn't enough, the Information Commissioner's
+<p>As if that isn't enough, the Information Commissioner's
<a href="http://www.ico.gov.uk/upload/documents/library/freedom_of_information/practical_application/foi_hints_for_practitioners_handing_foi_and_eir_requests_2008_final.pdf">Hints for Practitioners</a> say
<em>"Any correspondence could include a request for information. If it is written (this includes e-mail), legible, gives the name of the applicant, an address for reply (which could be electronic), and includes a description of the information required, then it will fall within the scope of the legislation."</em>
</p>
@@ -141,10 +141,10 @@ see the section on <a href="#real_name">pseudonyms</a>.</p>
<dt id="takedown">Can you take down personal information about me? <a href="#takedown">#</a> </dt>
-<dd>
+<dd>
<p>If you see any personal information about you on the site which you'd like
-us to remove or hide, then please <a href="/help/contact">let us know</a>.
+us to remove or hide, then please <a href="<%= help_contact_path %>">let us know</a>.
Specify exactly what information you believe to be problematic and why, and
where it appears on the site.</p>
@@ -165,8 +165,8 @@ which outweighs the public interest, and must demonstrate that efforts have
been made to conceal the name on the organisation's own website.</p>
<p>For all other requests we apply a public interest test to decide
-whether information should be removed.
-<a href="http://www.statutelaw.gov.uk/content.aspx?ActiveTextDocId=3190650"> Section 32</a>
+whether information should be removed.
+<a href="http://www.statutelaw.gov.uk/content.aspx?ActiveTextDocId=3190650"> Section 32</a>
of the Data Protection Act 1998 permits us to do this, as the material we
publish is journalistic. We cannot easily edit many types of attachments (such
as PDFs, or Microsoft Word or Excel files), so we will usually ask
@@ -177,7 +177,7 @@ that authorities resend these with the personal information removed.</p>
</dl>
-<p><strong>Learn more</strong> from the help for <a href="/help/officers">FOI officers</a> --&gt;
+<p><strong>Learn more</strong> from the help for <a href="<%= help_officers_path %>">FOI officers</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/requesting.rhtml b/app/views/help/requesting.rhtml
index af8f2e45d..e7cfdd199 100644
--- a/app/views/help/requesting.rhtml
+++ b/app/views/help/requesting.rhtml
@@ -20,7 +20,7 @@
<li>Don't worry excessively about getting the right authority. If you get it
wrong, they ought to advise you who to make the request to instead.
</li>
- <li>If you've got a thorny case, please <a href="/help/contact">contact us</a> for help.</li>
+ <li>If you've got a thorny case, please <a href="<%= help_contact_path %>">contact us</a> for help.</li>
</ul>
</dd>
@@ -30,7 +30,7 @@
<dt id="missing_body">You're missing the public authority that I want to request from! <a href="#missing_body">#</a> </dt>
<dd>
- <p>Please <a href="/help/contact">contact us</a> with the name of the public authority and,
+ <p>Please <a href="<%= help_contact_path %>">contact us</a> with the name of the public authority and,
if you can find it, their contact email address for Freedom of Information requests.
</p>
<p>If you'd like to help add a whole category of public authority to the site, we'd love
@@ -47,11 +47,11 @@
<ul>
<li> Those formally subject to the FOI Act</li>
- <li> Those formally subject to the Environmental Regulations (a less well
+ <li> Those formally subject to the Environmental Regulations (a less well
defined group)</li>
<li> Those which voluntarily comply with the FOI Act</li>
<li> Those which aren't subject to the Act but we think should be, on grounds
- such as them having significant public responsibilities.
+ such as them having significant public responsibilities.
</li>
</ul>
@@ -132,7 +132,7 @@
<p>Even if they are not prompt, in nearly all cases they must respond within
20 working days. If you had to clarify your request, or they are a school,
or one or two other cases, then they may have more time
- (<a href="/help/officers#days">full details</a>).
+ (<a href="<%= help_officers_path(:anchor => 'days') %>">full details</a>).
<p>WhatDoTheyKnow will email you if you don't get a timely response. You can
then send the public authority a message to remind them, and tell them if they
@@ -158,24 +158,24 @@
checking that they received the request. It was sent to them by email.
</li>
<li>If they have not received it, the problem is most likely due to
- "spam filters". Refer the authority to the measures in the answer
- '<a href="/help/officers#spam_problems">I can see a request on WhatDoTheyKnow, but we never got it by email!</a>'
- in the FOI officers section of this help.
+ "spam filters". Refer the authority to the measures in the answer
+ '<a href="<%= help_officers_path(:anchor => 'spam_problems') %>">I can see a request on WhatDoTheyKnow, but we never got it by email!</a>'
+ in the FOI officers section of this help.
</li>
<li>If you're still having no luck, then you can ask for an internal review,
and then complain to the Information Commissioner about the authority.
- Read our page '<a href="/help/unhappy">Unhappy about the response you got?</a>'.
+ Read our page '<a href="<%= help_general_path(:action => 'unhappy') %>">Unhappy about the response you got?</a>'.
</ul>
</dd>
<dt id="not_satifised">What if I'm not satisfied with the response? <a href="#not_satifised">#</a> </dt>
<dd>If you didn't get the information you asked for, or you didn't get it in time,
- then read our page '<a href="/help/unhappy">Unhappy about the response you got?</a>'.
+ then read our page '<a href="<%= help_general_path(:action => 'unhappy') %>">Unhappy about the response you got?</a>'.
</dd>
<dt id="reuse">It says I can't re-use the information I got!<a href="#reuse">#</a> </dt>
<dd>
- <p>Authorities often add legal boilerplate about the
+ <p>Authorities often add legal boilerplate about the
"<a href="http://www.opsi.gov.uk/si/si2005/20051515">Re-Use of Public Sector
Information Regulations 2005</a>", which at first glance implies you may not
be able do anything with the information.
@@ -184,7 +184,7 @@
<p>You can, of course, write articles about the information or summarise it, or
quote parts of it. We also think you should feel free to republish the
information in full, just as we do, even though in theory you might not be
- allowed to do so. See <a href="/help/officers#copyright">our policy on copyright</a>.</p>
+ allowed to do so. See <a href="<%= help_officers_path(:anchor => 'copyright') %>">our policy on copyright</a>.</p>
</dd>
@@ -199,7 +199,7 @@
the process is very similar. There are differences around time
limits for compliance.
See the <a href="http://www.itspublicknowledge.info/nmsruntime/saveasdialog.asp?lID=1858&amp;sID=321">Scottish
- Information Commissioner's guidance</a> for details.</p>
+ Information Commissioner's guidance</a> for details.</p>
</dd>
<dt id="data_protection">Can I request information about myself? <a href="#data_protection">#</a> </dt>
@@ -216,7 +216,7 @@
website) explains how to do this.</p>
<p>If you see that somebody has included personal information, perhaps
- unwittingly, in a request, please <a href="/help/contact">contact us</a>
+ unwittingly, in a request, please <a href="<%= help_contact_path %>">contact us</a>
immediately so we can remove it.</p>
</dd>
@@ -228,7 +228,7 @@
to read. </p>
<p>You should contact the public authority directly if you would like to
make a request in private. If you're interested in buying a system which helps
- you manage FOI requests in secret, then <a href="/help/contact">contact us</a>.
+ you manage FOI requests in secret, then <a href="<%= help_contact_path %>">contact us</a>.
</p>
</dd>
@@ -237,7 +237,7 @@
<dd>
<p>Some public authorities, such as <a href="http://www.whatdotheyknow.com/body/south_east_water">South East Water</a>,
don't come under the Freedom of Information Act, but do come under another law called
- the Environmental Information Regulations (EIR).
+ the Environmental Information Regulations (EIR).
</p>
<p>It's a very similar law, so you make a request
@@ -250,7 +250,7 @@
<p>You can, of course, request environmental information from other
authorities. Just make a Freedom of Information (FOI) request as normal. The
authority has a duty to work out if the Environmental Information Regulations
- (EIR) is the more appropriate legislation to reply under.
+ (EIR) is the more appropriate legislation to reply under.
</p>
</dd>
@@ -275,7 +275,7 @@
<dt id="moderation">How do you moderate request annotations? <a href="#moderation">#</a> </dt>
- <dd>
+ <dd>
<p>Annotations on WhatDoTheyKnow are to help
people get the information they want, or to give them pointers to places they
can go to help them act on it. We reserve the right to remove anything else.
@@ -286,7 +286,7 @@
</dl>
- <p><strong>Next</strong>, read about <a href="/help/privacy">your privacy</a> --&gt;
+ <p><strong>Next</strong>, read about <a href="<%= help_privacy_path %>">your privacy</a> --&gt;
<div id="hash_link_padding"></div>
</div>
diff --git a/app/views/help/unhappy.rhtml b/app/views/help/unhappy.rhtml
index 4f3c67b9e..2b00341c2 100644
--- a/app/views/help/unhappy.rhtml
+++ b/app/views/help/unhappy.rhtml
@@ -2,7 +2,7 @@
<% if !@info_request.nil? %>
-<h1>Unhappy about the response you got
+<h1>Unhappy about the response you got
to your request '<%=request_link(@info_request) %>'?
</h1>
<% else %>
@@ -58,10 +58,10 @@ authority, then <a href="http://www.itspublicknowledge.info/YourRights/HowToApp
to the Scottish Information Commissioner</a>.
</p>
-<p>To make it easier to send the relevant information to the
+<p>To make it easier to send the relevant information to the
Information Commissioner, either
<% if !@info_request.nil? %>
- include a link to your request
+ include a link to your request
<strong><%=h main_url(request_url(@info_request)) %></strong>
<% else %>
include a link to your request on <%= site_name %>
@@ -86,8 +86,8 @@ get the information by <strong>other means...</strong></p>
<ul>
<li>Make a <strong>new FOI request</strong> for summary information, or for
-documentation relating indirectly to matters in your refused request.
-<a href="/help/contact">Ask us for ideas</a> if you're stuck.</li>
+documentation relating indirectly to matters in your refused request.
+<a href="<%= help_contact_path %>">Ask us for ideas</a> if you're stuck.</li>
<li>If any <strong>other public authorities</strong> or publicly owned companies are involved,
then make FOI requests to them.</li>
<li>Write to <strong>your MP</strong> or other politician using <a
@@ -95,13 +95,13 @@ href="http://www.writetothem.com">WriteToThem</a> and ask for their help
finding the answer. MPs can write directly to ministers or departments, and
can ask written questions in the House of Commons. Councillors in local authorities
can talk directly to council officers.</li>
-<li>Ask <strong>other researchers</strong> who are interested in a similar
+<li>Ask <strong>other researchers</strong> who are interested in a similar
issue to yours for ideas. You can sometimes find them by browsing this site;
contact any registered user from their page. There may be an Internet
forum or group that they hang out in. If it is a local matter, use <a
-href="http://www.groupsnearyou.com">GroupsNearYou</a> to find such a
+href="http://www.groupsnearyou.com">GroupsNearYou</a> to find such a
forum.</li>
-<li><strong>Start a pledge</strong> on <a href="http://www.pledgebank.com">PledgeBank</a> to get
+<li><strong>Start a pledge</strong> on <a href="http://www.pledgebank.com">PledgeBank</a> to get
others to act together with you. For example, you could arrange a meeting with
staff from the authority. Or you could form a small local campaigns group.
</ul>
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index 8c4ae588b..6ac7064a7 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -69,9 +69,9 @@
<% if !@popup_banner.blank? %>
<div id="everypage" class="jshide">
- <p style="float:right"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
+ <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
<%= @popup_banner %>
- <p style="text-align: right"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
+ <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p>
</div>
<% end %>
@@ -100,7 +100,7 @@
<% end %>
<div id="navigation_search">
- <form id="navigation_search_form" method="post" action="/search">
+ <form id="navigation_search_form" method="post" action="<%= search_redirect_path %>">
<p>
<%= text_field_tag 'query', params[:query], { :size => 40, :id => "navigation_search_query" } %>
<input id="navigation_search_button" type="submit" value="search">
diff --git a/commonlib b/commonlib
-Subproject 3bf8c34eed89a946f981363eb862f25ac843769
+Subproject 4e84e9ed8fc75764da5139a51722729f2ba96ce
diff --git a/config/environment.rb b/config/environment.rb
index e79efdcfa..5670aed0b 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -9,6 +9,8 @@ if RUBY_VERSION.to_f >= 1.9
require 'active_support/inflector'
# Activate warning messages again.
$VERBOSE = original_verbose
+ require 'yaml'
+ YAML::ENGINE.yamler = "syck"
end
# Uncomment below to force Rails into production mode when
diff --git a/config/general.yml-example b/config/general.yml-example
index fd134b0c2..bfe289541 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -32,6 +32,10 @@ AVAILABLE_LOCALES: 'en es'
DEFAULT_LOCALE: 'en'
USE_DEFAULT_BROWSER_LANGUAGE: true
+# If you don't want the default locale to be included in URLs generated
+# by the application, set this to false
+INCLUDE_DEFAULT_LOCALE_IN_URLS: true
+
# How many days should have passed before an answer to a request is officially late?
REPLY_LATE_AFTER_DAYS: 20
REPLY_VERY_LATE_AFTER_DAYS: 40
diff --git a/config/initializers/fast_gettext.rb b/config/initializers/fast_gettext.rb
index 2ecf5cb5d..1cd6440e4 100644
--- a/config/initializers/fast_gettext.rb
+++ b/config/initializers/fast_gettext.rb
@@ -3,4 +3,4 @@ FastGettext.default_text_domain = 'app'
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
-
+RoutingFilter::Locale.include_default_locale = Configuration::include_default_locale_in_urls \ No newline at end of file
diff --git a/config/packages b/config/packages
index ded8dc9a5..775a55e38 100644
--- a/config/packages
+++ b/config/packages
@@ -33,7 +33,7 @@ libpq-dev
uuid-dev
ruby1.8-dev
rubygems
-rake
+rake (>= 0.9.2.2)
build-essential
bundler
sqlite3
diff --git a/lib/alaveteli_external_command.rb b/lib/alaveteli_external_command.rb
index 3bfc34e3a..24b4b1aa8 100644
--- a/lib/alaveteli_external_command.rb
+++ b/lib/alaveteli_external_command.rb
@@ -2,6 +2,12 @@ require 'external_command'
module AlaveteliExternalCommand
class << self
+ # Final argument can be a hash of options.
+ # Valid options are:
+ # :append_to - string to append the output of the process to
+ # :stdin_string - stdin string to pass to the process
+ # :binary_output - boolean flag for treating the output as binary or text (only significant
+ # ruby 1.9 and above)
def run(program_name, *args)
# Run an external program, and return its output.
# Standard error is suppressed unless the program
@@ -10,7 +16,7 @@ module AlaveteliExternalCommand
if !args.empty? && args[-1].is_a?(Hash)
opts = args.pop
end
-
+
if program_name =~ %r(^/)
program_path = program_name
else
@@ -24,12 +30,16 @@ module AlaveteliExternalCommand
end
raise "Could not find #{program_name} in any of #{Configuration::utility_search_path.join(', ')}" if !found
end
-
+
xc = ExternalCommand.new(program_path, *args)
if opts.has_key? :append_to
xc.out = opts[:append_to]
end
+ if opts.has_key? :binary_output
+ xc.binary_mode = opts[:binary_output]
+ end
xc.run(opts[:stdin_string] || "", opts[:env] || {})
+
if xc.status != 0
# Error
$stderr.puts("Error from #{program_name} #{args.join(' ')}:")
diff --git a/lib/configuration.rb b/lib/configuration.rb
index abd0f5cdc..11fe1c56e 100644
--- a/lib/configuration.rb
+++ b/lib/configuration.rb
@@ -25,6 +25,7 @@ module Configuration
:GA_CODE => '',
:GAZE_URL => '',
:HTML_TO_PDF_COMMAND => '',
+ :INCLUDE_DEFAULT_LOCALE_IN_URLS => true,
:INCOMING_EMAIL_DOMAIN => 'localhost',
:INCOMING_EMAIL_PREFIX => '',
:INCOMING_EMAIL_SECRET => 'dummysecret',
diff --git a/lib/mail_handler/backends/mail_backend.rb b/lib/mail_handler/backends/mail_backend.rb
index 0e198adf0..b75e6ed63 100644
--- a/lib/mail_handler/backends/mail_backend.rb
+++ b/lib/mail_handler/backends/mail_backend.rb
@@ -23,19 +23,291 @@ module MailHandler
main
end
+ # Returns an outlook message as a Mail object
+ def mail_from_outlook(content)
+ msg = Mapi::Msg.open(StringIO.new(content))
+ mail = mail_from_raw_email(msg.to_mime.to_s)
+ mail.ready_to_send!
+ mail
+ end
+
# Return a copy of the file name for the mail part
- def get_part_file_name(mail_part)
- part_file_name = mail_part.filename
+ def get_part_file_name(part)
+ part_file_name = part.filename
part_file_name.nil? ? nil : part_file_name.dup
end
+ # Get the body of a mail part
+ def get_part_body(part)
+ part.body.decoded
+ end
+
+ # Return the first from field if any
+ def first_from(mail)
+ if mail[:from]
+ begin
+ mail[:from].addrs[0]
+ mail[:from].decoded
+ return mail[:from].addrs[0]
+ rescue
+ return mail[:from].value
+ end
+ else
+ nil
+ end
+ end
+
+ # Return the first from address if any
+ def get_from_address(mail)
+ first_from = first_from(mail)
+ if first_from
+ if first_from.is_a?(String)
+ return nil
+ else
+ return first_from.address
+ end
+ else
+ return nil
+ end
+ end
+
+ # Return the first from name if any
+ def get_from_name(mail)
+ first_from = first_from(mail)
+ if first_from
+ if first_from.is_a?(String)
+ return nil
+ else
+ return first_from.display_name ? eval(%Q{"#{first_from.display_name}"}) : nil
+ end
+ else
+ return nil
+ end
+ end
+
+ def get_all_addresses(mail)
+ envelope_to = mail['envelope-to'] ? [mail['envelope-to'].value] : []
+ ((mail.to || []) +
+ (mail.cc || []) +
+ (envelope_to || [])).uniq
+ end
+
+ def empty_return_path?(mail)
+ return false if mail['return-path'].nil?
+ return true if mail['return-path'].value.blank?
+ return false
+ end
+
+ def get_auto_submitted(mail)
+ mail['auto-submitted'] ? mail['auto-submitted'].value : nil
+ end
+
+ def get_content_type(part)
+ part.content_type ? part.content_type.split(';')[0] : nil
+ end
+
+ def get_header_string(header, mail)
+ mail.header[header] ? mail.header[header].to_s : nil
+ end
+
+ # Detects whether a mail part is an Outlook email
+ def is_outlook?(part)
+ filename = get_part_file_name(part)
+ return true if get_content_type(part) == 'application/vnd.ms-outlook'
+ if filename && AlaveteliFileTypes.filename_to_mimetype(filename) == 'application/vnd.ms-outlook'
+ return true
+ end
+ return false
+ end
+
+ # Convert a mail part which is an attached mail in one of
+ # several formats into a mail object and set it as the
+ # rfc822_attachment on the part. If the mail part can't be
+ # converted, the content type on the part is updated to
+ # 'text/plain' for an RFC822 attachment, and 'application/octet-stream'
+ # for other types
+ def decode_attached_part(part, parent_mail)
+ if get_content_type(part) == 'message/rfc822'
+ # An email attached as text
+ part.rfc822_attachment = mail_from_raw_email(part.body)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as text
+ part.content_type = 'text/plain'
+ end
+ elsif is_outlook?(part)
+ part.rfc822_attachment = mail_from_outlook(part.body.decoded)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ part.content_type = 'application/octet-stream'
+ end
+ elsif get_content_type(part) == 'application/ms-tnef'
+ # A set of attachments in a TNEF file
+ part.rfc822_attachment = mail_from_tnef(part.body.decoded)
+ if part.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ part.content_type = 'application/octet-stream'
+ end
+ end
+ if part.rfc822_attachment
+ expand_and_normalize_parts(part.rfc822_attachment, parent_mail)
+ end
+ end
+
+ # Expand and normalize a mail part recursively. Decodes attached messages into
+ # Mail objects wherever possible. Sets a default content type if none is
+ # set. Tries to set a more specific content type for binary content types.
+ def expand_and_normalize_parts(part, parent_mail)
+ if part.multipart?
+ part.parts.each{ |sub_part| expand_and_normalize_parts(sub_part, parent_mail) }
+ else
+ part_filename = get_part_file_name(part)
+ charset = part.charset # save this, because overwriting content_type also resets charset
+
+ # Don't allow nil content_types
+ if get_content_type(part).nil?
+ part.content_type = 'application/octet-stream'
+ end
+
+ # PDFs often come with this mime type, fix it up for view code
+ if get_content_type(part) == 'application/octet-stream'
+ part_body = get_part_body(part)
+ calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_filename,
+ part_body)
+ if calc_mime
+ part.content_type = calc_mime
+ end
+ end
+
+ # Use standard content types for Word documents etc.
+ part.content_type = normalise_content_type(get_content_type(part))
+ decode_attached_part(part, parent_mail)
+ part.charset = charset
+ end
+ end
+
+ # Count the parts in a mail part recursively, including any attached messages.
+ # Set the count on the parent mail, and set a url_part_number on the part itself.
+ # Set the count for the first uudecoded part on the parent mail also.
+ def count_parts(part, parent_mail)
+ if part.multipart?
+ part.parts.each { |p| count_parts(p, parent_mail) }
+ else
+ if part.rfc822_attachment
+ count_parts(part.rfc822_attachment, parent_mail)
+ else
+ parent_mail.count_parts_count += 1
+ part.url_part_number = parent_mail.count_parts_count
+ end
+ end
+ parent_mail.count_first_uudecode_count = parent_mail.count_parts_count
+ end
+
+ # Choose the best part from alternatives
+ def choose_best_alternative(mail)
+ if mail.html_part
+ return mail.html_part
+ elsif mail.text_part
+ return mail.text_part
+ else
+ return mail.parts.first
+ end
+ end
+
+ # Expand and normalize the parts of a mail, select the best part
+ # wherever there is an alternative, and then count the returned
+ # leaves and assign url_part values to them
+ def get_attachment_leaves(mail)
+ expand_and_normalize_parts(mail, mail)
+ leaves = _get_attachment_leaves_recursive(mail, nil, mail)
+ mail.count_parts_count = 0
+ count_parts(mail, mail)
+ return leaves
+ end
+
+ # Recurse through a mail part, selecting the best part wherever there is
+ # an alternative
+ def _get_attachment_leaves_recursive(part, within_rfc822_attachment, parent_mail)
+ leaves_found = []
+ if part.multipart?
+ raise "no parts on multipart mail" if part.parts.size == 0
+ if part.sub_type == 'alternative'
+ best_part = choose_best_alternative(part)
+ leaves_found += _get_attachment_leaves_recursive(best_part,
+ within_rfc822_attachment,
+ parent_mail)
+ else
+ # Add all parts
+ part.parts.each do |sub_part|
+ leaves_found += _get_attachment_leaves_recursive(sub_part,
+ within_rfc822_attachment,
+ parent_mail)
+ end
+ end
+ else
+ # Add all the parts of a decoded attached message
+ if part.rfc822_attachment
+ leaves_found += _get_attachment_leaves_recursive(part.rfc822_attachment,
+ part.rfc822_attachment,
+ parent_mail)
+ else
+ # Store leaf
+ part.within_rfc822_attachment = within_rfc822_attachment
+ leaves_found += [part]
+ end
+ end
+ return leaves_found
+ end
+
+ # Add selected useful headers from an attached message to its body
+ def extract_attached_message_headers(leaf)
+ body = get_part_body(leaf)
+ # Test to see if we are in the first part of the attached
+ # RFC822 message and it is text, if so add headers.
+ if leaf.within_rfc822_attachment == leaf && get_content_type(leaf) == 'text/plain'
+ headers = ""
+ [ 'Date', 'Subject', 'From', 'To', 'Cc' ].each do |header|
+ if header_value = get_header_string(header, leaf.within_rfc822_attachment)
+ if !header_value.blank?
+ headers = headers + header + ": " + header_value.to_s + "\n"
+ end
+ end
+ end
+ # XXX call _convert_part_body_to_text here, but need to get charset somehow
+ # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
+ body = headers + "\n" + body
+ end
+ body
+ end
+
+ # Generate a hash of the attributes associated with each significant part of a Mail object
+ def get_attachment_attributes(mail)
+ leaves = get_attachment_leaves(mail)
+ attachments = []
+ for leaf in leaves
+ body = get_part_body(leaf)
+ if leaf.within_rfc822_attachment
+ within_rfc822_subject = leaf.within_rfc822_attachment.subject
+ body = extract_attached_message_headers(leaf)
+ end
+ leaf_attributes = { :url_part_number => leaf.url_part_number,
+ :content_type => get_content_type(leaf),
+ :filename => get_part_file_name(leaf),
+ :charset => leaf.charset,
+ :within_rfc822_subject => within_rfc822_subject,
+ :body => body,
+ :hexdigest => Digest::MD5.hexdigest(body) }
+ attachments << leaf_attributes
+ end
+ return attachments
+ end
+
# Format
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 Mail::Address.new(email)
+ return Mail::Address.new(email).to_s
end
address = Mail::Address.new
address.display_name = name
diff --git a/lib/mail_handler/backends/mail_extensions.rb b/lib/mail_handler/backends/mail_extensions.rb
index cbe0491ed..f756abd1a 100644
--- a/lib/mail_handler/backends/mail_extensions.rb
+++ b/lib/mail_handler/backends/mail_extensions.rb
@@ -1,7 +1,67 @@
+require 'mail/message'
+require 'mail/fields/common/parameter_hash'
module Mail
class Message
attr_accessor :url_part_number
attr_accessor :rfc822_attachment # when a whole email message is attached as text
attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+ attr_accessor :count_parts_count
+ attr_accessor :count_first_uudecode_count
+
+ # A patched version of the message initializer to work around a bug where stripping the original
+ # input removes meaningful spaces - e.g. in the case of uuencoded bodies.
+ def initialize(*args, &block)
+ @body = nil
+ @body_raw = nil
+ @separate_parts = false
+ @text_part = nil
+ @html_part = nil
+ @errors = nil
+ @header = nil
+ @charset = 'UTF-8'
+ @defaulted_charset = true
+
+ @perform_deliveries = true
+ @raise_delivery_errors = true
+
+ @delivery_handler = nil
+
+ @delivery_method = Mail.delivery_method.dup
+
+ @transport_encoding = Mail::Encodings.get_encoding('7bit')
+
+ @mark_for_delete = false
+
+ if args.flatten.first.respond_to?(:each_pair)
+ init_with_hash(args.flatten.first)
+ else
+ # The replacement of this commented out line is the change.
+ # init_with_string(args.flatten[0].to_s.strip)
+ init_with_string(args.flatten[0].to_s)
+ end
+
+ if block_given?
+ instance_eval(&block)
+ end
+
+ self
+ end
+ end
+
+ # A patched version of the parameter hash that handles nil values without throwing
+ # an error.
+ class ParameterHash < IndifferentHash
+
+ def encoded
+ map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
+ # The replacement of this commented out line is the change
+ # unless value.ascii_only?
+ unless value.nil? || value.ascii_only?
+ value = Mail::Encodings.param_encode(value)
+ key_name = "#{key_name}*"
+ end
+ %Q{#{key_name}=#{quote_token(value)}}
+ end.join(";\r\n\s")
+ end
end
end \ No newline at end of file
diff --git a/lib/mail_handler/backends/tmail_backend.rb b/lib/mail_handler/backends/tmail_backend.rb
index 87aba73d7..02124cdb1 100644
--- a/lib/mail_handler/backends/tmail_backend.rb
+++ b/lib/mail_handler/backends/tmail_backend.rb
@@ -41,6 +41,234 @@ module MailHandler
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 = 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 = 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 = 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 = 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 get_attachment_attributes(mail)
+ leaves = get_attachment_leaves(mail)
+ # XXX we have to call ensure_parts_counted after get_attachment_leaves
+ # which is really messy.
+ ensure_parts_counted(mail)
+ attachment_attributes = []
+ for leaf in leaves
+ body = get_part_body(leaf)
+ # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
+ # to prevent excess memory use. XXX not really sure if this helps reduce
+ # peak RAM use overall. Anyway, maybe there is something better to do than this.
+ GC.start
+ if leaf.within_rfc822_attachment
+ within_rfc822_subject = leaf.within_rfc822_attachment.subject
+ # Test to see if we are in the first part of the attached
+ # RFC822 message and it is text, if so add headers.
+ # XXX should probably use hunting algorithm to find main text part, rather than
+ # just expect it to be first. This will do for now though.
+ if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
+ headers = ""
+ for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
+ if leaf.within_rfc822_attachment.header.include?(header.downcase)
+ header_value = leaf.within_rfc822_attachment.header[header.downcase]
+ if !header_value.blank?
+ headers = headers + header + ": " + header_value.to_s + "\n"
+ end
+ end
+ end
+ # XXX call _convert_part_body_to_text here, but need to get charset somehow
+ # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
+ body = headers + "\n" + body
+
+ # This is quick way of getting all headers, but instead we only add some a) to
+ # make it more usable, b) as at least one authority accidentally leaked security
+ # information into a header.
+ #attachment.body = leaf.within_rfc822_attachment.port.to_s
+ end
+ end
+ attachment_attributes << {:url_part_number => leaf.url_part_number,
+ :content_type => get_content_type(leaf),
+ :filename => get_part_file_name(leaf),
+ :charset => leaf.charset,
+ :within_rfc822_subject => within_rfc822_subject,
+ :body => body,
+ :hexdigest => Digest::MD5.hexdigest(body) }
+ end
+ attachment_attributes
+ end
+
+ # (This risks losing info if the unchosen alternative is the only one to contain
+ # useful info, but let's worry about that another time)
+ def get_attachment_leaves(mail)
+ return _get_attachment_leaves_recursive(mail, mail)
+ end
+ def _get_attachment_leaves_recursive(curr_mail, parent_mail, within_rfc822_attachment = nil)
+ leaves_found = []
+ if curr_mail.multipart?
+ if curr_mail.parts.size == 0
+ raise "no parts on multipart mail"
+ end
+
+ if curr_mail.sub_type == 'alternative'
+ # Choose best part from alternatives
+ best_part = nil
+ # Take the last text/plain one, or else the first one
+ curr_mail.parts.each do |m|
+ if not best_part
+ best_part = m
+ elsif m.content_type == 'text/plain'
+ best_part = m
+ end
+ end
+ # Take an HTML one as even higher priority. (They tend
+ # to render better than text/plain, e.g. don't wrap links here:
+ # http://www.whatdotheyknow.com/request/amount_and_cost_of_freedom_of_in#incoming-72238 )
+ curr_mail.parts.each do |m|
+ if m.content_type == 'text/html'
+ best_part = m
+ end
+ end
+ leaves_found += _get_attachment_leaves_recursive(best_part, parent_mail, within_rfc822_attachment)
+ else
+ # Add all parts
+ curr_mail.parts.each do |m|
+ leaves_found += _get_attachment_leaves_recursive(m, parent_mail, within_rfc822_attachment)
+ end
+ end
+ else
+ # XXX Yuck. this section alters various content_types. That puts
+ # it into conflict with ensure_parts_counted which it has to be
+ # called both before and after. It will fail with cases of
+ # attachments of attachments etc.
+ charset = curr_mail.charset # save this, because overwriting content_type also resets charset
+ # Don't allow nil content_types
+ if curr_mail.content_type.nil?
+ curr_mail.content_type = 'application/octet-stream'
+ end
+ # PDFs often come with this mime type, fix it up for view code
+ if curr_mail.content_type == 'application/octet-stream'
+ part_file_name = get_part_file_name(curr_mail)
+ part_body = get_part_body(curr_mail)
+ calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, part_body)
+ if calc_mime
+ curr_mail.content_type = calc_mime
+ end
+ end
+
+ # Use standard content types for Word documents etc.
+ curr_mail.content_type = normalise_content_type(curr_mail.content_type)
+ if curr_mail.content_type == 'message/rfc822'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ if curr_mail.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as text
+ curr_mail.content_type = 'text/plain'
+ end
+ end
+ if curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ if curr_mail.rfc822_attachment.nil?
+ # Attached mail didn't parse, so treat as binary
+ curr_mail.content_type = 'application/octet-stream'
+ end
+ end
+ # If the part is an attachment of email
+ if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
+ ensure_parts_counted(parent_mail) # fills in rfc822_attachment variable
+ leaves_found += _get_attachment_leaves_recursive(curr_mail.rfc822_attachment, parent_mail, curr_mail.rfc822_attachment)
+ else
+ # Store leaf
+ curr_mail.within_rfc822_attachment = within_rfc822_attachment
+ leaves_found += [curr_mail]
+ end
+ # restore original charset
+ curr_mail.charset = charset
+ end
+ return leaves_found
+ 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"
diff --git a/lib/mail_handler/backends/tmail_extensions.rb b/lib/mail_handler/backends/tmail_extensions.rb
index 9359dfeea..3576a8eca 100644
--- a/lib/mail_handler/backends/tmail_extensions.rb
+++ b/lib/mail_handler/backends/tmail_extensions.rb
@@ -20,6 +20,8 @@ module TMail
attr_accessor :url_part_number
attr_accessor :rfc822_attachment # when a whole email message is attached as text
attr_accessor :within_rfc822_attachment # for parts within a message attached as text (for getting subject mainly)
+ attr_accessor :count_parts_count
+ attr_accessor :count_first_uudecode_count
# Monkeypatch! (check to see if this becomes a standard function in
# TMail::Mail, then use that, whatever it is called)
diff --git a/lib/mail_handler/mail_handler.rb b/lib/mail_handler/mail_handler.rb
index 24d14b5c8..8b227b9ca 100644
--- a/lib/mail_handler/mail_handler.rb
+++ b/lib/mail_handler/mail_handler.rb
@@ -4,10 +4,12 @@ require 'tmpdir'
module MailHandler
if RUBY_VERSION.to_f >= 1.9
+ require 'mail'
require 'backends/mail_extensions'
require 'backends/mail_backend'
include Backends::MailBackend
else
+ require 'action_mailer'
require 'backends/tmail_extensions'
require 'backends/tmail_backend'
include Backends::TmailBackend
@@ -19,7 +21,7 @@ module MailHandler
def tnef_attachments(content)
attachments = []
Dir.mktmpdir do |dir|
- IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "w") do |f|
+ IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "wb") do |f|
f.write(content)
f.close
if $?.signaled?
@@ -32,7 +34,7 @@ module MailHandler
found = 0
Dir.new(dir).sort.each do |file| # sort for deterministic behaviour
if file != "." && file != ".."
- file_content = File.open("#{dir}/#{file}", "r").read
+ file_content = File.open("#{dir}/#{file}", "rb").read
attachments << { :content => file_content,
:filename => file }
found += 1
@@ -45,6 +47,131 @@ module MailHandler
attachments
end
+ def normalise_content_type(content_type)
+ # e.g. http://www.whatdotheyknow.com/request/93/response/250
+ if content_type == 'application/excel' or content_type == 'application/msexcel' or content_type == 'application/x-ms-excel'
+ content_type = 'application/vnd.ms-excel'
+ end
+ if content_type == 'application/mspowerpoint' or content_type == 'application/x-ms-powerpoint'
+ content_type = 'application/vnd.ms-powerpoint'
+ end
+ if content_type == 'application/msword' or content_type == 'application/x-ms-word'
+ content_type = 'application/vnd.ms-word'
+ end
+ if content_type == 'application/x-zip-compressed'
+ content_type = 'application/zip'
+ end
+
+ # e.g. http://www.whatdotheyknow.com/request/copy_of_current_swessex_scr_opt#incoming-9928
+ if content_type == 'application/acrobat'
+ content_type = 'application/pdf'
+ end
+
+ return content_type
+ end
+
+ def get_attachment_text_one_file(content_type, body, charset = 'utf-8')
+ # note re. charset: TMail always tries to convert email bodies
+ # to UTF8 by default, so normally it should already be that.
+ text = ''
+ # XXX - tell all these command line tools to return utf-8
+ if content_type == 'text/plain'
+ text += body + "\n\n"
+ else
+ tempfile = Tempfile.new('foiextract')
+ tempfile.binmode
+ tempfile.print body
+ tempfile.flush
+ default_params = { :append_to => text, :binary_output => false }
+ if content_type == 'application/vnd.ms-word'
+ AlaveteliExternalCommand.run("wvText", tempfile.path, tempfile.path + ".txt")
+ # Try catdoc if we get into trouble (e.g. for InfoRequestEvent 2701)
+ if not File.exists?(tempfile.path + ".txt")
+ AlaveteliExternalCommand.run("catdoc", tempfile.path, default_params)
+ else
+ text += File.read(tempfile.path + ".txt") + "\n\n"
+ File.unlink(tempfile.path + ".txt")
+ end
+ elsif content_type == 'application/rtf'
+ # catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf
+ AlaveteliExternalCommand.run("catdoc", tempfile.path, default_params)
+ elsif content_type == 'text/html'
+ # lynx wordwraps links in its output, which then don't
+ # get formatted properly by Alaveteli. We use elinks
+ # instead, which doesn't do that.
+ AlaveteliExternalCommand.run("elinks", "-eval", "set document.codepage.assume = \"#{charset}\"",
+ "-eval", "set document.codepage.force_assumed = 1",
+ "-dump-charset", "utf-8",
+ "-force-html", "-dump",
+ tempfile.path,
+ default_params.merge(:env => {"LANG" => "C"}))
+ elsif content_type == 'application/vnd.ms-excel'
+ # Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
+ # py_xls2txt only extract text from cells, not from floating
+ # notes. catdoc may be fooled by weird character sets, but will
+ # probably do for UK FOI requests.
+ AlaveteliExternalCommand.run("/usr/bin/strings", tempfile.path, default_params)
+ elsif content_type == 'application/vnd.ms-powerpoint'
+ # ppthtml seems to catch more text, but only outputs HTML when
+ # we want text, so just use catppt for now
+ AlaveteliExternalCommand.run("catppt", tempfile.path, default_params)
+ elsif content_type == 'application/pdf'
+ AlaveteliExternalCommand.run("pdftotext", tempfile.path, "-", default_params)
+ elsif content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
+ # This is Microsoft's XML office document format.
+ # Just pull out the main XML file, and strip it of text.
+ xml = AlaveteliExternalCommand.run("/usr/bin/unzip", "-qq",
+ "-c",
+ tempfile.path,
+ "word/document.xml",
+ {:binary_output => false})
+ if !xml.nil?
+ doc = REXML::Document.new(xml)
+ text += doc.each_element( './/text()' ){}.join(" ")
+ end
+ elsif content_type == 'application/zip'
+ # recurse into zip files
+ begin
+ zip_file = Zip::ZipFile.open(tempfile.path)
+ text += get_attachment_text_from_zip_file(zip_file)
+ zip_file.close()
+ rescue
+ $stderr.puts("Error processing zip file: #{$!.inspect}")
+ end
+ end
+ tempfile.close
+ end
+
+ return text
+ end
+ def get_attachment_text_from_zip_file(zip_file)
+
+ text = ""
+ for entry in zip_file
+ if entry.file?
+ filename = entry.to_s
+ begin
+ body = entry.get_input_stream.read
+ rescue
+ # move to next attachment silently if there were problems
+ # XXX really should reduce this to specific exceptions?
+ # e.g. password protected
+ next
+ end
+ calc_mime = AlaveteliFileTypes.filename_to_mimetype(filename)
+ if calc_mime
+ content_type = calc_mime
+ else
+ content_type = 'application/octet-stream'
+ end
+
+ text += get_attachment_text_one_file(content_type, body)
+
+ end
+ end
+ return text
+ end
+
# Turn instance methods into class methods
extend self
diff --git a/locale/cs/app.po b/locale/cs/app.po
index ae0e761cb..078ecd3c9 100644
--- a/locale/cs/app.po
+++ b/locale/cs/app.po
@@ -14,8 +14,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-07 09:52+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-29 14:58+0000\n"
+"Last-Translator: janakneschke <jana.kneschke@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -299,7 +299,7 @@ msgid ""
"<p>Your request contains a <strong>postcode</strong>. Unless it directly "
"relates to the subject of your request, please remove any address as it will"
" <strong>appear publicly on the Internet</strong>.</p>"
-msgstr "<p>Váš dotaz obsahuje <strong>PSČ</strong>. Pokud to není údaj nutný k zodpovězení dotazu, prosíme odstraňte adresu či jiný identifikátor. Váš dotaz bude v celém znění<strong>uveřejněn na internetu</strong>.</p>"
+msgstr "<p>Váš dotaz obsahuje <strong>PSČ</strong>. Pokud to není údaj nutný k zodpovězení dotazu, prosíme odstraňte adresu či jiný identifikátor. Váš dotaz bude v celém znění a bez osobních údajů<strong>uveřejněn na internetu</strong>.</p>"
msgid ""
"<p>Your {{law_used_full}} request has been <strong>sent on its "
@@ -309,7 +309,7 @@ msgid ""
"<p>If you write about this request (for example in a forum or a blog) please"
" link to this page, and add an\\n annotation below telling people"
" about your writing.</p>"
-msgstr "<p><strong>Odesláno!</strong></p>\n<p><strong>Pošleme vám e-mail </strong> až vám instituce odpoví nebo po {{late_number_of_days}} dní po vypršení lhůty na odpověď.</p>\\n <p>Pokud o tomto dotazu budete dále psát (například na nějakém fóru či blogu) prosím přiložte odkaz k vašemu dotazu na těchto stránkách a přidejte k němu komentář.</p>"
+msgstr "<p><strong>Odesláno!</strong></p>\n<p><strong>Pošleme vám e-mail </strong> až vám instituce odpoví nebo po {{late_number_of_days}} dní po vypršení lhůty na odpověď.</p>\\n <p>Pokud o svém dotazu budete dále psát (například na nějakém fóru či blogu), přiložte odkaz na stránky IPV a k dotazu přidejte komentář.</p>"
msgid ""
"<p>{{site_name}} is currently in maintenance. You can only view existing "
@@ -353,12 +353,12 @@ msgstr "<strong><code>dotaz:</code></strong> k omezení na konkrétní dotaz vep
msgid ""
"<strong><code>requested_by:julian_todd</code></strong> to search requests "
"made by Julian Todd, typing the name as in the URL."
-msgstr "<strong><code>requested_by:julian_todd</code></strong> pokud chcete nalézt více poznámek od Tonyho Bowdena, napište jeho jméno jako URL."
+msgstr "<strong><code>rpožadováno od uživatele: julian_todd</code></strong> pokud chcete nalézt více poznámek od Tonyho Bowdena, napište jeho jméno jako URL."
msgid ""
"<strong><code>requested_from:home_office</code></strong> to search requests "
"from the Home Office, typing the name as in the URL."
-msgstr "<strong><code>vyžádáno_od:ministerstvo_vnitra</code></strong> pokud chcete nalézt více dotazů vznesených na Ministerstva vnitra, napište název instituce jako URL."
+msgstr "<strong><code>vyžádáno_od:ministerstvo_vnitra</code></strong> pokud chcete nalézt více dotazů vznesených na Ministerstvo vnitra, napište název instituce jako URL."
msgid ""
"<strong><code>status:</code></strong> to select based on the status or "
@@ -373,7 +373,7 @@ msgid ""
"tag:financial_transaction:335633</code>. Note that by default any of the "
"tags\\n can be present, you have to put <code>AND</code> explicitly if "
"you only want results them all present."
-msgstr "<strong><code>Tagujte:charity</code></strong> umožní vám najít instituce nebo žádosti s daným tagem. Můžete použít více tagů, ⏎ a hodnot tagů, např. <code>tag:openlylocal AND tag:financial_transaction:335633</code>. Jakýkoliv tag může být zahrnut⏎ je třeba vložit <code>AND</code> pokud chcete výsledek, který zahrnuje všechny tagy."
+msgstr "<strong><code>Tagujte:charity</code></strong> umožní vám najít instituce nebo dotazy s daným klíčových slovem. Můžete použít více tagů, ⏎ a hodnot tagů, např. <code>tag:openlylocal AND tag:financial_transaction:335633</code>. Jakýkoliv tag může být zahrnut⏎ je třeba vložit <code>AND</code> pokud chcete výsledek, který zahrnuje všechny tagy."
msgid ""
"<strong><code>variety:</code></strong> to select type of thing to search "
@@ -400,7 +400,7 @@ msgid ""
"FOI law, and the\\nway authorities use it. Plus you'll need to be an elite "
"statistician. Please\\n<a href=\"{{contact_path}}\">contact us</a> with "
"questions."
-msgstr "<strong>Varování!</strong>Aby datay na stránkách {{site_name}} byla správně seřazena, je vhodné chovat se podle zásad slušného jednání a netikety. Třídění vznesených dotazů není zas tak jednoduché a mohou \nvzniknout chyby a dojít k různým nejednoznačnostem. Musíte\ntaké rozumět zákonu o svobodném přístupu k informacím\na způsobu, jakým jej instituce používají. A ke všemu být vynikající statistik. Prosíme\n<a href=\"{{contact_path}}\">kontaktujte nás</a>, pokud máte otázky."
+msgstr "<strong>Doporučení</strong>Veškerá konverzace na stránkách {{site_name}} podléhá zásadám slušného jednání a netikety. Třídění vznesených dotazů není zas tak jednoduché a mohou \nvzniknout chyby a dojít k různým nejednoznačnostem. Je nutné\ntaké rozumět zákonu o svobodném přístupu k informacím\na způsobu, jak tento instituce interpretují. A ke všemu být vynikající statistik. Prosíme\n<a href=\"{{contact_path}}\">kontaktujte nás</a>, pokud máte otázky."
msgid "<strong>Clarification</strong> has been requested"
msgstr "Bylo vyžádáno <strong>upřesnění</strong> "
@@ -413,7 +413,7 @@ msgstr "<strong>Bez odpovědi</strong> \n <small>(možná je to j
msgid ""
"<strong>Note:</strong> Because we're testing, requests are being sent to "
"{{email}} rather than to the actual authority."
-msgstr "<strong>Note:</strong> Protože testujeme, žádosti jsou posílány na {{email}} a ne příslušné autoritě."
+msgstr "<strong>Note:</strong> Protože testujeme, dotazy jsou posílány na {{email}} a ne příslušné autoritě."
msgid ""
"<strong>Note:</strong> You're sending a message to yourself, presumably\\n"
@@ -438,7 +438,7 @@ msgstr "<strong>Důležité upozornění:</strong> Vaše fotografie bude zveřej
msgid ""
"<strong>Privacy warning:</strong> Your message, and any response\\n "
"to it, will be displayed publicly on this website."
-msgstr "<strong>Ochrana soukromí:</strong> Vámi vznesený dotaz, a všechny \n odpovědi na něj budou uveřejněny na těchto stránkách."
+msgstr "<strong>Ochrana soukromí:</strong> vámi vznesený dotaz, a všechny \n odpovědi na něj budou uveřejněny na stránkách IPV."
msgid "<strong>Some of the information</strong> has been sent "
msgstr "<strong>Některé informace</strong> byly odeslány"
@@ -555,7 +555,7 @@ msgstr "Upravit sledování dotazů"
msgid ""
"Although all responses are automatically published, we depend on\\nyou, the "
"original requester, to evaluate them."
-msgstr "I když jsou všechny odpovědi automaticky uveřejňovány, hodnocení dotazu závisí na vás, tedy tazateli."
+msgstr "I když jsou všechny odpovědi automaticky uveřejňovány, hodnocení dotazu závisí na vás, na tazateli."
msgid ""
"An <a href=\"{{request_url}}\">annotation</a> to <em>{{request_title}}</em> "
@@ -613,7 +613,7 @@ msgid "Awaiting classification."
msgstr "Čeká se zařazení."
msgid "Awaiting internal review."
-msgstr "Čeká se na přezkoumání dotazu. "
+msgstr "Čeká se na doplnění dotazu."
msgid "Awaiting response."
msgstr "Čeká se na odpověď."
@@ -637,12 +637,12 @@ msgstr "Procházejte všechny instituce"
msgid ""
"By law, under all circumstances, {{public_body_link}} should have responded "
"by now"
-msgstr "Podle zákona by {{public_body_link}} měla veřejná instituce v každém případě odpovědět."
+msgstr "Podle zákona by instituce {{public_body_link}} měla v každém případě odpovědět."
msgid ""
"By law, {{public_body_link}} should normally have responded "
"<strong>promptly</strong> and"
-msgstr "Podle zákona, by měla instituce {{public_body_link}} odpovědět okamžitě"
+msgstr "Podle zákona, by měla instituce {{public_body_link}} odpovědět okamžitě a "
msgid "Cancel a {{site_name}} alert"
msgstr "Zrušte tato upozornění stránek {{site_name}}"
@@ -818,7 +818,7 @@ msgstr "Kulturní památky (které mohou být ovlivěny tak, \n jak j
msgid ""
"Currently <strong>waiting for a response</strong> from {{public_body_link}},"
" they must respond promptly and"
-msgstr "V současnosti <strong> se čeká na odpověď</strong> od {{public_body_link}}, musí odpovědět co nejdříve a"
+msgstr "V současnosti <strong> se čeká na odpověď</strong> od instituce {{public_body_link}}, musí odpovědět co nejdříve a "
msgid "Date:"
msgstr "Datum:"
@@ -847,12 +847,12 @@ msgid ""
msgstr "Vyloučení odpovědnosti: Tato zpráva a jakékoliv odpovědi na ni budou uveřejněny na stránkách Informace pro všechny. Naše pravidla o ochraně osobních údajů a autorských právech si přečtěte:"
msgid "Disclosure log"
-msgstr ""
+msgstr "Disclosure log"
msgid ""
"Don't want to address your message to {{person_or_body}}? You can also "
"write to:"
-msgstr "Nechcete svou zprávu adresovat pro {{person_or_body}}? Můžete také napsat:"
+msgstr "Nechcete svou zprávu adresovat na {{person_or_body}}? Můžete také napsat:"
msgid "Done"
msgstr "Hotovo"
@@ -900,12 +900,12 @@ msgstr "Pošlete mi budoucí aktualizace tohoto dotazu. "
msgid ""
"Enter words that you want to find separated by spaces, e.g. <strong>climbing"
" lane</strong>"
-msgstr "Slova, která chcete najít musí být oddělena mezerou, například <strong>přechod pro chodce</strong>"
+msgstr "Slova, která chcete najít, musí být oddělena mezerou, například <strong>přechod pro chodce</strong>"
msgid ""
"Enter your response below. You may attach one file (use email, or\\n <a "
"href=\"%s\">contact us</a> if you need more)."
-msgstr "Níže můžete vložit svou odpověď. Pokud je to nutné, přiložte jeden dokument, (s použitím e-mailu, nebo⏎ <a href=\"%s\">nás kontaktujte</a> pokud potřebujete přiložit více dokumentů). ??"
+msgstr "Níže můžete vložit svou odpověď. Pokud je to nutné, přiložte jeden dokument, (s použitím e-mailu, nebo⏎ <a href=\"%s\">nás kontaktujte</a> pokud potřebujete přiložit více dokumentů)."
msgid "Environmental Information Regulations"
msgstr "Informace o životním prostředí"
@@ -1001,7 +1001,7 @@ msgid "Follow all new requests"
msgstr "Sledovat všechny nově vznesené dotazy"
msgid "Follow new successful responses"
-msgstr "Sledovat všechny zodpovězené dotazy ??"
+msgstr "Sledovat všechny zodpovězené dotazy"
msgid "Follow requests to {{public_body_name}}"
msgstr "Sledovat dotaz vznesený na {{public_body_name}}"
@@ -1144,7 +1144,7 @@ msgid ""
"hasn't told us whether or not it was successful. Would you mind taking\\n"
" a moment to read it and help us keep the place tidy for everyone?\\n "
"Thanks."
-msgstr "Haló! Potřebujeme vaši pomoc. Osoba, která vznesla tento dotaz nám nesdělila, jestli byla zodpovězena úspěšně. Můžete si ji přečíst a pomoci nám udržovat stránky přehledné? Děkujeme."
+msgstr "Haló! Potřebujeme vaši pomoc. Osoba, která vznesla tento dotaz nám nesdělila, jestli byla zodpovězena úspěšně. Můžete si dotaz i odpověď přečíst a pomoci nám tak udržovat stránky přehledné? Děkujeme."
msgid "Holiday"
msgstr "Státní svátek"
@@ -1173,7 +1173,7 @@ msgid "I am asking for <strong>new information</strong>"
msgstr "Žádám <strong>novou informaci</strong>"
msgid "I am requesting an <strong>internal review</strong>"
-msgstr "Žádám o <strong>přezkoumání</strong>"
+msgstr "Žádám <strong>o doplnění dotazu</strong>"
msgid "I don't like these ones &mdash; give me some more!"
msgstr "To se mi nelíbí &mdash; nabídněte nějaké další!"
@@ -1193,10 +1193,10 @@ msgid ""
msgstr "Stále <strong>čekám</strong> na svou informaci\n <small>(možná máte potvrzení)</small>"
msgid "I'm still <strong>waiting</strong> for the internal review"
-msgstr "Stále <strong>čekám</strong> na přezkoumání"
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu"
msgid "I'm waiting for an <strong>internal review</strong> response"
-msgstr "Čekám na vyjádření týkající se <strong>přezkoumání</strong> "
+msgstr "Čekám na <strong>doplnění</strong> dotazu"
msgid "I've been asked to <strong>clarify</strong> my request"
msgstr "Byl/a jsem požádán/a o <strong>vyjasnění</strong> mého dotazu"
@@ -1246,7 +1246,7 @@ msgstr "Pokud jste {{user_link}}, prosíme"
msgid ""
"If you believe this request is not suitable, you can report it for attention"
" by the site administrators"
-msgstr "Pokud se domníváte, že je tato žádost nevhodná, můžete ji nahlásit administrátorům stránky. "
+msgstr "Pokud se domníváte, že je tento dotaz nevhodný, můžete jej nahlásit administrátorům stránky. "
msgid ""
"If you can't click on it in the email, you'll have to <strong>select and "
@@ -1334,13 +1334,13 @@ msgid "IncomingMessage|Valid to reply to"
msgstr "IncomingMessage | Platná zpráva pro"
msgid "Individual requests"
-msgstr "Jednotlivé žádosti"
+msgstr "Jednotlivé dotazy"
msgid "Info request"
-msgstr "Žádost o informaci"
+msgstr "Dotaz na informaci"
msgid "Info request event"
-msgstr "Žádost o informaci – akce ??"
+msgstr "Dotaz na informaci – akce ??"
msgid "InfoRequestEvent|Calculated state"
msgstr "InfoRequestEvent| V procesu"
@@ -1361,16 +1361,16 @@ msgid "InfoRequestEvent|Prominence"
msgstr "InfoRequestEvent | Prominence ??"
msgid "InfoRequest|Allow new responses from"
-msgstr "InfoRequestEvent | Povolit nové odpovědi od"
+msgstr "InfoRequestEvent | Povolit nové odpovědi od "
msgid "InfoRequest|Attention requested"
-msgstr "InfoRequestEvent | Podle zákona ??"
+msgstr "InfoRequestEvent|Podle zákona ??"
msgid "InfoRequest|Awaiting description"
msgstr "InfoRequestEvent | Očekává se popis"
msgid "InfoRequest|Comments allowed"
-msgstr "Info o žádosti|Komentáře dovoleny"
+msgstr "Info o dotazu|Komentáře dovoleny"
msgid "InfoRequest|Described state"
msgstr "InfoRequestEvent | Popsaný status"
@@ -1382,10 +1382,10 @@ msgid "InfoRequest|External user name"
msgstr "InfoRequest|External user name ??"
msgid "InfoRequest|Handle rejected responses"
-msgstr "InfoRequestEvent | Řešit odmítnuté odpovědi"
+msgstr "InfoRequestEvent | Řešit odmítnuté odpovědi "
msgid "InfoRequest|Idhash"
-msgstr "InfoRequestEvent I dhash ??"
+msgstr "InfoRequestEventIdhash ??"
msgid "InfoRequest|Law used"
msgstr "InfoRequestEvent | Podle zákona"
@@ -1394,10 +1394,10 @@ msgid "InfoRequest|Prominence"
msgstr "InfoRequestEvent| Prominence ??"
msgid "InfoRequest|Title"
-msgstr "InfoRequestEvent | Název"
+msgstr "InfoRequestEvent|Název"
msgid "InfoRequest|Url title"
-msgstr "InfoRequestEvent| Název URL"
+msgstr "InfoRequestEvent|Název URL"
msgid "Information not held."
msgstr "Informace není k dispozici."
@@ -1408,12 +1408,12 @@ msgid ""
msgstr "Informace o vypouštění imisí a emisí (např. energie, hluk, radiace, odpady)"
msgid "Internal review request"
-msgstr "Přezkoumání dotazu"
+msgstr "Doplnění dotazu"
msgid ""
"Is {{email_address}} the wrong address for {{type_of_request}} requests to "
"{{public_body_name}}? If so, please contact us using this form:"
-msgstr "Je {{email_address}} chybná adresa pro {{type_of_request}} dotazy vznesené na {{public_body_name}}? Pokud ano, prosíme kontaktujte nás vyplněním tohoto formuláře:"
+msgstr "Je {{email_address}} chybná adresa pro {{type_of_request}} dotazy vznesené na instituci {{public_body_name}}? Pokud ano, prosíme kontaktujte nás vyplněním tohoto formuláře:"
msgid ""
"It may be that your browser is not set to accept a thing called "
@@ -1470,22 +1470,22 @@ msgid "Made between"
msgstr "Vloženo mezi"
msgid "Mail server log"
-msgstr ""
+msgstr "Mail server log"
msgid "Mail server log done"
-msgstr ""
+msgstr "Mail server log done"
msgid "MailServerLogDone|Filename"
-msgstr ""
+msgstr "MailServerLogDone|Jméno souboru"
msgid "MailServerLogDone|Last stat"
-msgstr ""
+msgstr "MailServerLogDone|Nejnovější statistiky"
msgid "MailServerLog|Line"
-msgstr ""
+msgstr "MailServerLog|Řádek"
msgid "MailServerLog|Order"
-msgstr ""
+msgstr "MailServerLog|Příkaz"
msgid "Make a new <strong>Environmental Information</strong> request"
msgstr "Vzneste dotaz týkající se <strong>životního prostředí</strong> "
@@ -1513,7 +1513,7 @@ msgid "Make your own request"
msgstr "Vzneste vlastní dotaz"
msgid "Many requests"
-msgstr "Mnoho žádostí"
+msgstr "Mnoho dotazů"
msgid "Message"
msgstr "Zpráva"
@@ -1528,7 +1528,7 @@ msgid "More about this authority"
msgstr "Více o této instituci"
msgid "More requests..."
-msgstr "Více žádostí..."
+msgstr "Více dotazů..."
msgid "More similar requests"
msgstr "Více podobných dotazů"
@@ -1540,7 +1540,7 @@ msgid "My profile"
msgstr "Můj profil"
msgid "My request has been <strong>refused</strong>"
-msgstr "Můj dotaz byla <strong>odmítnut</strong>"
+msgstr "Zopodvězení mého dotazu bylo <strong>zadmítnuto</strong>"
msgid "My requests"
msgstr "Moje dotazy"
@@ -1616,7 +1616,7 @@ msgstr "Nic tu není"
msgid ""
"Note that the requester will not be notified about your annotation, because "
"the request was published by {{public_body_name}} on their behalf."
-msgstr "Rádi bychom vás uzporonili, že tazatel nebude na váš komentář upozorněn, jelikož tento dotaz byl zveřejněn na žádost instituce {{public_body_name}}."
+msgstr "Rádi bychom vás upozornili, že tazatel nebude o vašem komentáři informován, jelikož tento dotaz byl zveřejněn na žádost instituce {{public_body_name}}."
msgid "Now check your email!"
msgstr "Nyní zkontrolujte svou e-mailovou schránku!"
@@ -1638,7 +1638,7 @@ msgstr "Nevhodný obsah?"
msgid ""
"Oh no! Sorry to hear that your request was refused. Here is what to do now."
-msgstr "Ale ne! Je nám líto, že váš dotaz byla zamítnut. Pokud chcete, můžete učinit následující kroky."
+msgstr "Mrzí nás, že zodpovězení vašeho dotazu bylo zamítnuto. Pokud na poskytnutí informací trváte, máte následující možnosti."
msgid "Old e-mail:"
msgstr "Původní e-mail"
@@ -1848,7 +1848,7 @@ msgid "Please enter your password"
msgstr "Vložte heslo. "
msgid "Please give details explaining why you want a review"
-msgstr "Prosíme vysvětlete, proč potřebujete přezkoumání. "
+msgstr "Prosíme uveďte důvody, kvůli kterým žádáte o doplnění"
msgid "Please keep it shorter than 500 characters"
msgstr "Váš text musí být kratší než 500 znaků."
@@ -1954,7 +1954,7 @@ msgid "Preview follow up to '"
msgstr "Náhled odpovědi pro"
msgid "Preview new annotation on '{{info_request_title}}'"
-msgstr "Náhled nové anotace o '{{info_request_title}}'"
+msgstr "Náhled nové anotace týkající se '{{info_request_title}}'"
msgid "Preview your annotation"
msgstr "Náhled poznámky"
@@ -1990,7 +1990,7 @@ msgid "Public body/translation"
msgstr "PublicBody | Verze ??"
msgid "PublicBody::Translation|Disclosure log"
-msgstr ""
+msgstr "PublicBody::Translation|Disclosure log"
msgid "PublicBody::Translation|First letter"
msgstr "PublicBody | Začáteční písmeno"
@@ -2020,7 +2020,7 @@ msgid "PublicBody|Api key"
msgstr "PublicBody | Název ??"
msgid "PublicBody|Disclosure log"
-msgstr ""
+msgstr "PublicBody|Disclosure log"
msgid "PublicBody|First letter"
msgstr "PublicBody | První dopis"
@@ -2029,7 +2029,7 @@ msgid "PublicBody|Home page"
msgstr "PublicBody | Domovská stránka"
msgid "PublicBody|Info requests count"
-msgstr "Veřejný orgán | Info o počtu žádostí"
+msgstr "Veřejný orgán | Info o počtu dotazů"
msgid "PublicBody|Last edit comment"
msgstr "PublicBody | Naposled aktualizovaný komentář"
@@ -2065,7 +2065,7 @@ msgid "Purge request"
msgstr "Vyčistit formulář dotazu"
msgid "PurgeRequest|Model"
-msgstr "PurgeRequest|Verze ??"
+msgstr "PurgeRequest|Verze"
msgid "PurgeRequest|Url"
msgstr "PurgeRequest|URL"
@@ -2117,26 +2117,26 @@ msgid "Reported for administrator attention."
msgstr "Administrátor byl již upozorněn."
msgid "Request an internal review"
-msgstr "Požádat o přezkoumání dotazu"
+msgstr "Požádat o doplnění dotazu"
msgid "Request an internal review from {{person_or_body}}"
-msgstr "Požádat o přezkoumání dotazu u {{person_or_body}}"
+msgstr "Požádat o doplnění dotazu od instituce či jmenovitě jejím pracovníkem {{person_or_body}}"
msgid "Request has been removed"
msgstr "Dotaz byl odstraněn"
msgid ""
"Request sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
-msgstr "Dotaz byl vznesen na {{public_body_name}} od {{info_request_user}} dne {{date}}."
+msgstr "Dotaz byl vznesen na instituci {{public_body_name}} uživatelem {{info_request_user}} dne {{date}}."
msgid ""
"Request to {{public_body_name}} by {{info_request_user}}. Annotated by "
"{{event_comment_user}} on {{date}}."
-msgstr "Dotaz byl vznesen na {{public_body_name}} od {{info_request_user}}. Poznámka od {{event_comment_user}} dne {{date}}."
+msgstr "Dotaz byl vznesen na instituci {{public_body_name}} uživatelem {{info_request_user}}. Poznámka od uživatele {{event_comment_user}} dne {{date}}."
msgid ""
"Requested from {{public_body_name}} by {{info_request_user}} on {{date}}"
-msgstr "Vyžádáno {{public_body_name}} od {{info_request_user}} dne {{date}}"
+msgstr "Vyžádáno institucí {{public_body_name}} od {{info_request_user}} dne {{date}}"
msgid "Requested on {{date}}"
msgstr "Dotaz byl vznesen dne {{date}}"
@@ -2162,10 +2162,10 @@ msgid "Respond using the web"
msgstr "Odpovězte na internetových stránkách"
msgid "Response"
-msgstr "Odpověď"
+msgstr "Odpověď "
msgid "Response from a public authority"
-msgstr "Odpověď od instituce"
+msgstr "Odpověď od instituce "
msgid "Response to '{{title}}'"
msgstr "Odpověď na '{{title}}'"
@@ -2180,7 +2180,7 @@ msgid "Response to your request"
msgstr "Odpověď na váš dotaz"
msgid "Response:"
-msgstr "Odpověď:"
+msgstr "Odpověď: "
msgid "Restrict to"
msgstr "Omezit na"
@@ -2210,10 +2210,10 @@ msgid ""
"Search over<br/>\\n <strong>{{number_of_requests}} requests</strong> "
"<span>and</span><br/>\\n <strong>{{number_of_authorities}} "
"authorities</strong>"
-msgstr "Prohledávejte více než<br/>\n <strong>{{number_of_requests}} dotazů</strong> <span>a</span><br/>\n <strong>{{number_of_authorities}} institucí</strong>"
+msgstr "Prohledávejte více než<br/>\n <strong>{{number_of_requests}} dotazů</strong> <span>a</span><br/>\n <strong>{{number_of_authorities}} institucí v adresáři</strong>"
msgid "Search queries"
-msgstr "Prohledávejte žádosti"
+msgstr "Prohledávejte dotazy"
msgid "Search results"
msgstr "Výsledky vyhledávání"
@@ -2288,7 +2288,7 @@ msgid "Sign out"
msgstr "Odhlásit"
msgid "Sign up"
-msgstr "Registovat"
+msgstr "Registrovat"
msgid "Similar requests"
msgstr "Podobné dotazy"
@@ -2314,7 +2314,7 @@ msgid "Somebody added a note to your FOI request - "
msgstr "Někdo přidal poznámku k vašemu dotazu – "
msgid "Someone has updated the status of your request"
-msgstr "Někdo aktualizovat status vaší žádosti"
+msgstr "Někdo aktualizoval status vašeho dotazu"
msgid ""
"Someone, perhaps you, just tried to change their email address "
@@ -2351,7 +2351,7 @@ msgid "Stay up to date"
msgstr "Buďte informováni"
msgid "Still awaiting an <strong>internal review</strong>"
-msgstr "Stále čekám na <strong>přezkoumání dotazu</strong>"
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu"
msgid "Subject"
msgstr "Předmět"
@@ -2377,7 +2377,7 @@ msgstr "Úspěch."
msgid ""
"Suggest how the requester can find the <strong>rest of the "
"information</strong>."
-msgstr "Doporučte, jak tazatel může najít<strong>úplné informace</strong>."
+msgstr "Doporučte, jak tazatel může najít <strong>úplné informace</strong>."
msgid "Summary:"
msgstr "Shrnutí:"
@@ -2406,13 +2406,13 @@ msgstr "Děkujeme vám za vaši anotaci!"
msgid ""
"Thank you for responding to this FOI request! Your response has been "
"published below, and a link to your response has been emailed to "
-msgstr "Děkujeme vám za odpověď na tento dotaz! Vaše odpověď byla publikována níže a odkaz vaší odpovědi byl odeslán "
+msgstr "Děkujeme vám za odpověď na tento dotaz! Vaše odpověď byla publikována níže a odkaz s vaší odpovědí byl odeslán "
msgid ""
"Thank you for updating the status of the request '<a "
"href=\"{{url}}\">{{info_request_title}}</a>'. There are some more requests "
"below for you to classify."
-msgstr "Děkujeme za aktualizaci statusu dotazu '<a href=\"{{url}}\">{{info_request_title}}</a>'. Zde jsou další odkazy, které je třeba zatřídit. "
+msgstr "Děkujeme za aktualizaci statusu dotazu '<a href=\"{{url}}\">{{info_request_title}}</a>'. Zde jsou další odkazy, které čekají na zařazenít. "
msgid "Thank you for updating this request!"
msgstr "Děkujeme vám za aktualizaci tohoto dotazu!"
@@ -2523,7 +2523,7 @@ msgid ""
"The response to your request has been <strong>delayed</strong>. You can say"
" that, \\n by law, the authority should normally have "
"responded\\n <strong>promptly</strong> and"
-msgstr "Odpověď na váš dotaz má <strong>zpoždění</strong>. Můžete napsat, že podle zákona by instituce měla odpovědět nejpozději\n <strong>do 15 dnů</strong> a"
+msgstr "Odpověď na váš dotaz má <strong>zpoždění</strong>. Můžete napsat, že podle zákona by instituce měla odpovědět nejpozději\n <strong>do 15 dnů</strong> a "
msgid ""
"The response to your request is <strong>long overdue</strong>. You can say"
@@ -2575,7 +2575,7 @@ msgid "Then you can sign in to {{site_name}}"
msgstr "Poté se můžete přihlásit na stránkách {{site_name}}"
msgid "Then you can update the status of your request to "
-msgstr "Poté můžete aktualizovat status svého dotazu na"
+msgstr "Poté můžete aktualizovat status svého dotazu na "
msgid "Then you can upload an FOI response. "
msgstr "Poté můžete nahrát odpověď na váš dotaz."
@@ -2610,7 +2610,7 @@ msgstr "Od nynějška budete upozorněni jakmile bude na '{{public_body_name}}'
msgid ""
"Then you will be updated whenever the request '{{request_title}}' is "
"updated."
-msgstr "Budete upozorněni na zmeěu stavu u dotazu '{{request_title}}'."
+msgstr "Budete upozorněni na změnu stavu u dotazu '{{request_title}}'."
msgid "Then you'll be allowed to send FOI requests."
msgstr "Pak můžete vznést dotaz."
@@ -2701,7 +2701,7 @@ msgid ""
msgstr "Toto zahrnuje široké spektrum informací o stavu \n <strong>přírody a památek</strong>, například:"
msgid "This external request has been hidden"
-msgstr "This external request has been hidden ??"
+msgstr "Tento dotaz byl skryt."
msgid ""
"This is a plain-text version of the Freedom of Information request "
@@ -2781,7 +2781,7 @@ msgid ""
"This request has been <strong>withdrawn</strong> by the person who made "
"it.\\n There may be an explanation in the correspondence "
"below."
-msgstr "Dotaz byla <strong>stažen</strong> tazatelem. \nBližší vysvětlení můžete najít v níže uvedené korespondenci."
+msgstr "Dotaz byl <strong>stažen</strong> tazatelem. \nBližší vysvětlení můžete najít v níže uvedené korespondenci."
msgid ""
"This request has been marked for review by the site administrators, who have"
@@ -2795,7 +2795,7 @@ msgstr "Odesláno administrátorovi"
msgid ""
"This request has been set by an administrator to \"allow new responses from "
"nobody\""
-msgstr "Tento dotaz byl poslán administrátorovi, aby \"povolil nové odpovědi od nikoho\""
+msgstr "Tento dotaz byl poslán administrátorovi, aby \"povolil nové odpovědi od nikoho\"."
msgid ""
"This request has had an unusual response, and <strong>requires "
@@ -2854,7 +2854,7 @@ msgid "To change your email address used on {{site_name}}"
msgstr "Změnit e-mailovou adresu, kterou používáte na stránkách {{site_name}}"
msgid "To classify the response to this FOI request"
-msgstr "Pro zatřídění dotazů"
+msgstr "Pro utřídění dotazů"
msgid "To do that please send a private email to "
msgstr "Aby se tak stalo, prosíme pošlete e-mail soukromou cestou"
@@ -2894,7 +2894,7 @@ msgstr "Abychom udržovali stránky přehledné, někdo jiný upravil status va
msgid ""
"To let everyone know, follow this link and then select the appropriate box."
-msgstr "To let everyone know, follow this link and then select the appropriate box. ??"
+msgstr "Sdílejte tento link a vyberte nejvhodnější variantu. ??"
msgid "To log into the administrative interface"
msgstr "Přihlásit se do administrace"
@@ -2925,7 +2925,7 @@ msgstr "Aktualizovat stav dotazu"
msgid ""
"To upload a response, you must be logged in using an email address from "
-msgstr "Abyste mohli nahrát odpověď, musíte být přihlášeni pod e-mailovou adresou z"
+msgstr "Abyste mohli nahrát odpověď, musíte být přihlášeni pod e-mailovou adresou "
msgid ""
"To use the advanced search, combine phrases and labels as described in the "
@@ -3029,7 +3029,7 @@ msgid "Update the status of your request to "
msgstr "Aktualizujte stav svého dotazu vzneseného na "
msgid "Upload FOI response"
-msgstr "Nahrajte odpověď na žádost"
+msgstr "Nahrajte odpověď na dotaz "
msgid ""
"Use OR (in capital letters) where you don't mind which word, e.g. "
@@ -3131,12 +3131,12 @@ msgstr "Čeká se na vysvětlení. "
msgid ""
"Waiting for an <strong>internal review</strong> by {{public_body_link}} of "
"their handling of this request."
-msgstr "Čeká se na <strong>přezkoumání dotazu</strong> u {{public_body_link}}."
+msgstr "Stále <strong>čekám</strong> na doplnění dotazu institucí či jmenovitě jejím pracovníkem {{person_or_body}}."
msgid ""
"Waiting for the public authority to complete an internal review of their "
"handling of the request"
-msgstr "Čeká se na vnitřní přezkoumání u instituce, jak na dotaz odpoví"
+msgstr "Čekám, až instituce doplní požadované informace k mému dotazu"
msgid "Waiting for the public authority to reply"
msgstr "Čeká se na odpověď instituce"
@@ -3181,7 +3181,7 @@ msgstr "Čekáme, až si to někdo přečte"
msgid ""
"We've sent an email to your new email address. You'll need to click the link"
" in\\nit before your email address will be changed."
-msgstr "Poslali jsme e-mail na vaši novou adresu. Je třeba kliknout na zaslaný odkaz,\naby byla vaše nová e-mailová adresa aktivována."
+msgstr "Poslali jsme e-mail na vaši novou adresu. Je třeba kliknout na zaslaný odkaz, aby vaše nová e-mailová adresa byla správně přiřazena."
msgid ""
"We've sent you an email, and you'll need to click the link in it before you "
@@ -3208,17 +3208,17 @@ msgstr "Jaká informace byla požadována?"
msgid ""
"When you get there, please update the status to say if the response "
"\\ncontains any useful information."
-msgstr "Až se přihlásíte, aktualizujte status s ohledem na to,\nzda odpověď je dostačující či nikoliv."
+msgstr "Přihlašte se na stránky IPV, přečtěte si odpověď a aktualizujte status tohoto dotazu s ohledem na poskytnutou odpověď. Status aktualizujete jednoduše zaškrtnutím jedné z možností.\n\nStav svého dotazu či odpověď instituce včetně svého dotazu můžete také sdílet na sociálních sítích nebo na svých stránkách či blogu. Pokud tak uděláte, vždy sdílejte i link na svůj dotaz na stránkách IPV. Děkujeme Vám."
msgid ""
"When you receive the paper response, please help\\n others find "
"out what it says:"
-msgstr "Pokud obdržíte písemnou odpověď, prosíme shrňte\n pro ostatní co se v odpovědi říká:"
+msgstr "Pokud obdržíte písemnou odpověď, prosíme shrňte\n pro ostatní, co se v odpovědi říká:"
msgid ""
"When you're done, <strong>come back here</strong>, <a href=\"%s\">reload "
"this page</a> and file your new request."
-msgstr "Až budete hotovi, <strong>vraťte se sem</strong>, <a href=\"%s\">obnovte stránku</a> a vzneste svůj nový dotaz."
+msgstr "Až budete hotovi, <strong>vraťte se sem</strong>, <a href=\"%s\">obnovte stránku</a> a vzneste nový dotaz."
msgid "Which of these is happening?"
msgstr "Vyberte nejvhodnější popis z následujících možností."
@@ -3245,7 +3245,7 @@ msgid "Write your FOI follow up message to "
msgstr "Napište svou odpověď pro "
msgid "Write your request in <strong>simple, precise language</strong>."
-msgstr "Napište svůj dotaz <strong>jednoduše a jasně</strong>.\nSvůj dotaz formulujte v jasných bodech. \nPokud žádáte o více nesouvisejících informací, rozdělte je do více žádostí. "
+msgstr "Napište svůj dotaz <strong>jednoduše a jasně</strong>.\nSvůj dotaz formulujte v jasných bodech. \nPokud žádáte o více nesouvisejících informací, rozdělte je do více dotazů. "
msgid "You"
msgstr "Vy"
@@ -3303,7 +3303,7 @@ msgid ""
msgstr "Od této instituce můžete požadovat pouze informace podle zákona o životním prostředí. "
msgid "You have a new response to the {{law_used_full}} request "
-msgstr "Máte novou odpověďna {{law_used_full}} "
+msgstr "Máte novou odpověď na {{law_used_full}} "
msgid ""
"You have found a bug. Please <a href=\"{{contact_url}}\">contact us</a> to "
@@ -3400,10 +3400,10 @@ msgid ""
msgstr "Odpověď na váš dotaz obdržíte, pouze pokud odpovíte \nbližším vysvětlením."
msgid "You're in. <a href=\"#\" id=\"send-request\">Continue sending your request</a>"
-msgstr ""
+msgstr "Odesláno. <a href=\"#\" id=\"send-request\"> Můžete vznést další dotaz.</a>"
msgid "You're long overdue a response to your FOI request - "
-msgstr "Dlouho jste nereagovali na odpověď k vašemu dotazu –"
+msgstr "Dlouho jste nereagovali na odpověď ke svému dotazu –"
msgid "You're not following anything."
msgstr "Nesledujete žádný vznesený dotaz."
@@ -3428,7 +3428,7 @@ msgid ""
"href=\"%s\">why?</a>)\\n on this website and in search engines. If "
"you\\n are thinking of using a pseudonym, please \\n <a "
"href=\"%s\">read this first</a>."
-msgstr "Vaše<strong> jméno bude uveřejněno</strong> \n (<a href=\"%s\">proč?</a>)\n na těchto stránkách a ve vyhledavačích. Pokud\n chcete použít přezdívku, prosíme, \n <a href=\"%s\">přečtěte nejdříve toto</a>."
+msgstr "Vaše<strong> jméno bude uveřejněno</strong> \n (<a href=\"%s\">proč?</a>)\n na těchto stránkách a ve vyhledávačích. Pokud\n chcete použít přezdívku, prosíme, \n <a href=\"%s\">přečtěte si nejdříve toto</a>."
msgid "Your annotations"
msgstr "Vaše poznámky"
@@ -3444,13 +3444,13 @@ msgid ""
"Your follow up has not been sent because this request has been stopped to "
"prevent spam. Please <a href=\"%s\">contact us</a> if you really want to "
"send a follow up message."
-msgstr "Vaše odpověď nebyla odeslána, protože tento dotaz byla identifikován jako nevyžádaná zpráva. Prosíme <a href=\"%s\">kontaktujte nás</a> pokud svou zprávu chcete odeslat. "
+msgstr "Vaše odpověď nebyla odeslána, protože tento dotaz byl identifikován jako nevyžádaná zpráva. Prosíme <a href=\"%s\">kontaktujte nás</a> pokud svou zprávu chcete odeslat. "
msgid "Your follow up message has been sent on its way."
msgstr "Zpráva s vaší odpovědí byla odeslána. "
msgid "Your internal review request has been sent on its way."
-msgstr "Vaše žádost o přezkoumání byla odeslána."
+msgstr "Dotaz týkající se doplnění byla odeslána."
msgid ""
"Your message has been sent. Thank you for getting in touch! We'll get back "
@@ -3495,18 +3495,18 @@ msgstr "<strong>Důležité upozornění:</strong> Vaše fotografie bude zveřej
msgid ""
"Your request was called {{info_request}}. Letting everyone know whether you "
"got the information will help us keep tabs on"
-msgstr "Informace se týká vašeho dotazu {{info_request}}. Můžete všechny informovat, zda jste požadovanou informaci obdrželi a budeme mít přehled o odpovědích instituce"
+msgstr "Informace se týká vašeho dotazu {{info_request}}. Můžete všechny informovat, zda jste požadovanou informaci obdrželi a bude tak přehled o odpovědích této instituce"
msgid "Your request:"
msgstr "Váš dotaz:"
msgid "Your response to an FOI request was not delivered"
-msgstr "Vaše odpověď na žádost nebyla doručena"
+msgstr "Vaše odpověď na dotaz nebyla doručena"
msgid ""
"Your response will <strong>appear on the Internet</strong>, <a "
"href=\"%s\">read why</a> and answers to other questions."
-msgstr "Vaše odpověď <strong>bude uveřejněna na internetu</strong>, <a href=\"%s\">čtěte proč</a>odpovědi na další otázky."
+msgstr "Vaše odpověď <strong>bude uveřejněna na internetu</strong>, <a href=\"%s\">čtěte proč</a> spolu s historií konverzace."
msgid ""
"Your thoughts on what the {{site_name}} <strong>administrators</strong> "
@@ -3581,7 +3581,7 @@ msgid "authorities"
msgstr "instituce"
msgid "awaiting a response"
-msgstr "očekává se odpověď"
+msgstr "očekává se odpověď "
msgid "beginning with ‘{{first_letter}}’"
msgstr "Začínající na ‘{{first_letter}}’"
@@ -3596,7 +3596,7 @@ msgid "by <strong>{{date}}</strong>"
msgstr "od <strong>{{date}}</strong>"
msgid "by {{public_body_name}} to {{info_request_user}} on {{date}}."
-msgstr "od {{public_body_name}} pro {{info_request_user}} dne {{date}}."
+msgstr "od instituce {{public_body_name}} pro uživatele {{info_request_user}} dne {{date}}."
msgid "by {{user_link_absolute}}"
msgstr "by {{user_link_absolute}}"
@@ -3689,7 +3689,7 @@ msgid "please sign in as "
msgstr "prosíme přihlašte se jako"
msgid "requesting an internal review"
-msgstr "požadavek na přezkoumání dotazu"
+msgstr "požádat o doplnění dotazu "
msgid "requests"
msgstr "dotazy"
@@ -3700,13 +3700,13 @@ msgstr "dotazy které jsou {{list_of_statuses}}"
msgid ""
"response as needing administrator attention. Take a look, and reply to "
"this\\nemail to let them know what you are going to do about it."
-msgstr "jako odpověď, která vyžaduje pozornost administrátora. Podívejte se na ni, odpovězte\nna tento e-mail a oznamte, co budete dělat. ??"
+msgstr "adminsitrátorovi. Podívejte se prosím na vznesený dotaz i odpověď. Až uděláte nezbytné kroky, odpovězte na tento e-mail a stručně je popište."
msgid "send a follow up message"
msgstr "poslat odpověď"
msgid "sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
-msgstr "posláno {{public_body_name}} od {{info_request_user}} v {{date}}."
+msgstr "posláno instituci {{public_body_name}} uživatelem {{info_request_user}} dne {{date}}."
msgid "show quoted sections"
msgstr "ukázat citované pasáže"
@@ -3727,7 +3727,7 @@ msgid "that you made to"
msgstr "kterou jste vznesli na"
msgid "the main FOI contact address for {{public_body}}"
-msgstr "hlavní kontaktní adresa pro poskytování informací institucí {{public_body}}"
+msgstr "hlavní kontaktní adresa instituce {{public_body}} pro poskytování informací "
msgid "the main FOI contact at {{public_body}}"
msgstr "hlavní kontakt pro vznesení dotazu na instituci {{public_body}}"
@@ -3791,7 +3791,7 @@ msgid "{{info_request_user_name}} only:"
msgstr "Pouze {{info_request_user_name}}:"
msgid "{{law_used_full}} request - {{title}}"
-msgstr "Žádost o informace podle {{law_used_full}} - {{title}}"
+msgstr "Dotaz o informace podle {{law_used_full}} - {{title}}"
msgid "{{law_used_full}} request GQ - {{title}}"
msgstr "{{law_used_full}} žádejte GQ - {{title}} ??"
@@ -3809,10 +3809,10 @@ msgid "{{number_of_comments}} comments"
msgstr "{{number_of_comments}} komentářů"
msgid "{{public_body_link}} answered a request about"
-msgstr "{{public_body_link}} zodpověděl dotaz týkající se"
+msgstr "Instituce {{public_body_link}} zodpověděla dotaz týkající se "
msgid "{{public_body_link}} was sent a request about"
-msgstr "Na {{public_body_link}} byl odeslán dotaz"
+msgstr "Na instituci {{public_body_link}} byl vznesen dotaz "
msgid "{{public_body_name}} only:"
msgstr "Pouze {{public_body_name}}:"
@@ -3838,7 +3838,7 @@ msgstr "Stránky {{site_name}} zahrnují dotazy vznesené na {{number_of_authori
msgid ""
"{{site_name}} sends new requests to <strong>{{request_email}}</strong> for "
"this authority."
-msgstr "Stránky {{site_name}} vznáší nové dotazy na <strong>{{request_email}}</strong> tuto instituci."
+msgstr "Pomocí stránek {{site_name}} vznesete nové dotazy na <strong>{{request_email}}</strong> tuto instituci."
msgid ""
"{{site_name}} users have made {{number_of_requests}} requests, including:"
@@ -3880,7 +3880,7 @@ msgid ""
"{{user}} ({{user_admin_link}}) made this {{law_used_full}} request (<a "
"href=\"{{request_admin_url}}\">admin</a>) to {{public_body_link}} (<a "
"href=\"{{public_body_admin_url}}\">admin</a>)"
-msgstr "{{user}} ({{user_admin_link}}) podal tuto {{law_used_full}} (<a href=\"{{request_admin_url}}\">admin</a>) pro {{public_body_link}} (<a href=\"{{public_body_admin_url}}\">admin</a>)"
+msgstr "{{user}} ({{user_admin_link}}) vznesl tento {{law_used_full}} dotaz (<a href=\"{{request_admin_url}}\">admin</a>) na instuci {{public_body_link}} (<a href=\"{{public_body_admin_url}}\">admin</a>)"
msgid "{{user}} made this {{law_used_full}} request"
msgstr "{{user}} vložil tuto {{law_used_full}} "
diff --git a/locale/cy/app.po b/locale/cy/app.po
index 346bb698e..b55a61b5d 100644
--- a/locale/cy/app.po
+++ b/locale/cy/app.po
@@ -9,8 +9,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-06 18:09+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-20 22:54+0000\n"
+"Last-Translator: skenaja <alex@alexskene.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -45,7 +45,7 @@ msgstr ""
msgid ""
" <strong>Note:</strong>\\n We will send you an email. Follow the "
"instructions in it to change\\n your password."
-msgstr ""
+msgstr " <strong>Sylwer:</strong>\\n Byddwn yn anfon e-bost atoch. Dilynwch y cyfarwyddiadau ynddo i newid eich cyfrinair."
msgid " <strong>Privacy note:</strong> Your email address will be given to"
msgstr ""
@@ -139,7 +139,7 @@ msgstr[2] ""
msgstr[3] ""
msgid "'Crime statistics by ward level for Wales'"
-msgstr ""
+msgstr "'Ystadegau troseddu yn ôl lefel ward ar gyfer Cymru'"
msgid "'Pollution levels over time for the River Tyne'"
msgstr ""
@@ -332,7 +332,7 @@ msgstr[3] ""
msgid ""
"<strong> Can I request information about myself?</strong>\\n\t\t\t<a "
"href=\"%s\">No! (Click here for details)</a>"
-msgstr ""
+msgstr "<strong> A gaf i wneud cais am wybodaeth amdanaf fi fy hun?</strong>\\n\t\t\t<a href=\"%s\">Na chewch! (Cliciwch yma am fanylion)</a>"
msgid ""
"<strong><code>commented_by:tony_bowden</code></strong> to search annotations"
@@ -595,7 +595,7 @@ msgstr ""
msgid ""
"Ask for <strong>specific</strong> documents or information, this site is not"
" suitable for general enquiries."
-msgstr ""
+msgstr "Gofynnwch am ddogfennau neu wybodaeth <strong>benodol</strong>. Nid yw'r wefan hon yn addas ar gyfer ymholiadau cyffredinol."
msgid ""
"At the bottom of this page, write a reply to them trying to persuade them to"
@@ -631,7 +631,7 @@ msgid ""
msgstr ""
msgid "Browse all authorities..."
-msgstr ""
+msgstr "Mwy o awdurdodau..."
msgid ""
"By law, under all circumstances, {{public_body_link}} should have responded "
@@ -689,13 +689,13 @@ msgid "Change your email address used on {{site_name}}"
msgstr ""
msgid "Change your password"
-msgstr ""
+msgstr "Newid eich cyfrinair"
msgid "Change your password on {{site_name}}"
msgstr "Newid eich cyfrinair ar {{site_name}}"
msgid "Change your password {{site_name}}"
-msgstr ""
+msgstr "Newid eich cyfrinair {{site_name}}"
msgid "Charity registration"
msgstr "Elusen gofrestredig"
@@ -823,7 +823,7 @@ msgid "Date:"
msgstr "Dyddiad"
msgid "Dear {{public_body_name}},"
-msgstr ""
+msgstr "Annwyl {{public_body_name}},"
msgid "Delayed response to your FOI request - "
msgstr ""
@@ -967,7 +967,7 @@ msgid ""
"First, type in the <strong>name of the UK public authority</strong> you'd "
"\\n like information from. <strong>By law, they have to "
"respond</strong>\\n (<a href=\"%s#%s\">why?</a>)."
-msgstr ""
+msgstr "Yn gyntaf, teipiwch <strong>enw awdurdod cyhoeddus yn y DU</strong> yr hoffech gael gwybodaeth ganddo. <strong>Yn ôl y gyfraith, mae'n rhaid iddynt ymateb</strong> (<a href=\"%s#%s\">pam?</a>)."
msgid "Foi attachment"
msgstr ""
@@ -1052,7 +1052,7 @@ msgid ""
msgstr ""
msgid "Forgotten your password?"
-msgstr ""
+msgstr "Wedi anghofio eich cyfrinair?"
msgid "Found %d public authority %s"
msgid_plural "Found %d public authorities %s"
@@ -1238,7 +1238,7 @@ msgstr ""
msgid ""
"If you are thinking of using a pseudonym,\\n please <a "
"href=\"%s\">read this first</a>."
-msgstr ""
+msgstr "Os ydych yn ystyried defnyddio ffugenw, <a href=\"%s\">darllenwch hwn yn gyntaf</a> os gwelwch yn dda."
msgid "If you are {{user_link}}, please"
msgstr ""
@@ -1293,10 +1293,10 @@ msgid ""
msgstr ""
msgid "If you're new to {{site_name}}"
-msgstr ""
+msgstr "Os ydych yn newydd i {{site_name}}"
msgid "If you've used {{site_name}} before"
-msgstr ""
+msgstr "Os ydych chi wedi defnyddio {{site_name}} o'r blaen "
msgid ""
"If your browser is set to accept cookies and you are seeing this "
@@ -1435,7 +1435,7 @@ msgstr ""
msgid ""
"Keep it <strong>focused</strong>, you'll be more likely to get what you want"
" (<a href=\"%s\">why?</a>)."
-msgstr ""
+msgstr "Cadwch at <strong>y prif bwynt</strong>. Byddwch yn fwy tebygol o gael beth rydych yn chwilio amdano (<a href=\"%s\">pam?</a>)."
msgid "Keywords"
msgstr ""
@@ -1713,7 +1713,7 @@ msgid "Password:"
msgstr "Cyfrinair"
msgid "Password: (again)"
-msgstr ""
+msgstr "Cyfrinair: (eto)"
msgid "Paste this link into emails, tweets, and anywhere else:"
msgstr ""
@@ -1963,7 +1963,7 @@ msgid "Preview your message"
msgstr ""
msgid "Preview your public request"
-msgstr ""
+msgstr "Rhagolwg o'ch cais cyhoeddus"
msgid "Profile photo"
msgstr ""
@@ -2102,7 +2102,7 @@ msgstr ""
msgid ""
"Remember me</label> (keeps you signed in longer;\\n do not use on a "
"public computer) "
-msgstr ""
+msgstr "Cofiwch fi</label> (yn eich cadw wedi eich mewngofnodi yn hwy; peidiwch â defnyddio hwn ar gyfrifiadur cyhoeddus)"
msgid "Report abuse"
msgstr ""
@@ -2277,7 +2277,7 @@ msgid "Showing"
msgstr ""
msgid "Sign in"
-msgstr ""
+msgstr "Mewngofnodi"
msgid "Sign in or make a new account"
msgstr "Mewngofnodi neu gofrestru"
@@ -2289,7 +2289,7 @@ msgid "Sign out"
msgstr ""
msgid "Sign up"
-msgstr ""
+msgstr "Cofrestrwch"
msgid "Similar requests"
msgstr ""
@@ -2381,7 +2381,7 @@ msgid ""
msgstr ""
msgid "Summary:"
-msgstr ""
+msgstr "Crynodeb:"
msgid "Table of statuses"
msgstr ""
@@ -2948,7 +2948,7 @@ msgid "To {{public_body_link_absolute}}"
msgstr ""
msgid "To:"
-msgstr ""
+msgstr "I:"
msgid "Today"
msgstr ""
@@ -3164,17 +3164,17 @@ msgstr ""
msgid ""
"We will not reveal your email address to anybody unless you or\\n the"
" law tell us to (<a href=\"%s\">details</a>). "
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud (<a href=\"%s\">manylion</a>)."
msgid ""
"We will not reveal your email address to anybody unless you\\nor the law "
"tell us to."
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud."
msgid ""
"We will not reveal your email addresses to anybody unless you\\nor the law "
"tell us to."
-msgstr ""
+msgstr "Ni fyddwn yn datgelu eich cyfeiriad e-bost i neb oni bai eich bod chi neu'r gyfraith yn dweud wrthym i wneud."
msgid "We're waiting for"
msgstr ""
@@ -3249,7 +3249,7 @@ msgid "Write your FOI follow up message to "
msgstr ""
msgid "Write your request in <strong>simple, precise language</strong>."
-msgstr ""
+msgstr "Ysgrifennwch eich cais mewn <strong>iaith syml, gryno</strong>."
msgid "You"
msgstr ""
@@ -3434,7 +3434,7 @@ msgid ""
"href=\"%s\">why?</a>)\\n on this website and in search engines. If "
"you\\n are thinking of using a pseudonym, please \\n <a "
"href=\"%s\">read this first</a>."
-msgstr ""
+msgstr "Bydd <strong>eich enw yn ymddangos yn gyhoeddus</strong> ( <a href=\"%s\">pam?</a>) ar y wefan hon ac mewn peiriannau chwilio.\nOs ydych yn ystyried defnyddio ffugenw, os gwelwch yn dda <a href=\"%s\">darllenwch hwn yn gyntaf</a>."
msgid "Your annotations"
msgstr ""
@@ -3444,7 +3444,7 @@ msgid ""
msgstr ""
msgid "Your e-mail:"
-msgstr ""
+msgstr "Eich cyfeiriad e-bost:"
msgid ""
"Your follow up has not been sent because this request has been stopped to "
@@ -3482,7 +3482,7 @@ msgid ""
msgstr ""
msgid "Your name:"
-msgstr ""
+msgstr "Eich enw:"
msgid "Your original message is attached."
msgstr ""
@@ -3523,7 +3523,7 @@ msgid "Your {{site_name}} email alert"
msgstr ""
msgid "Yours faithfully,"
-msgstr "Yn gywir"
+msgstr "Yr eiddoch yn gywir,"
msgid "Yours sincerely,"
msgstr "Yn gywir"
@@ -3543,7 +3543,7 @@ msgid ""
msgstr ""
msgid "a one line summary of the information you are requesting, \\n\t\t\te.g."
-msgstr ""
+msgstr "crynodeb un llinell o'r wybodaeth rydych yn gofyn amdano, \n\\n \t\t\tee "
msgid "admin"
msgstr ""
@@ -3718,7 +3718,7 @@ msgid "show quoted sections"
msgstr ""
msgid "sign in"
-msgstr "Mewngofnodi neu gofrestru"
+msgstr "Mewngofnodi"
msgid "simple_date_format"
msgstr ""
diff --git a/locale/es/app.po b/locale/es/app.po
index c211b65a0..720bab544 100644
--- a/locale/es/app.po
+++ b/locale/es/app.po
@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: alaveteli\n"
"Report-Msgid-Bugs-To: http://github.com/sebbacon/alaveteli/issues\n"
"POT-Creation-Date: 2012-11-06 16:35+0000\n"
-"PO-Revision-Date: 2012-11-06 18:20+0000\n"
-"Last-Translator: louisecrow <louise@mysociety.org>\n"
+"PO-Revision-Date: 2012-11-29 00:22+0000\n"
+"Last-Translator: David Cabo <david.cabo@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -2580,7 +2580,7 @@ msgid "Then you can write your reply to "
msgstr "Entonces podrás escribir tu respuesta a "
msgid "Then you will be following all new FOI requests."
-msgstr ""
+msgstr "Entonces recibirás actualizaciones por correo de todas las nuevas solicitudes."
msgid ""
"Then you will be notified whenever '{{user_name}}' requests something or "
@@ -2603,7 +2603,7 @@ msgstr "Entonces vas a ser avisado cuando alguien haga un pedido o "
msgid ""
"Then you will be updated whenever the request '{{request_title}}' is "
"updated."
-msgstr ""
+msgstr "Entonces recibirás correos siempre que la solicitud '{{request_title}}' se actualice."
msgid "Then you'll be allowed to send FOI requests."
msgstr "Entonces podrá enviar solicitudes de información."
@@ -2777,7 +2777,7 @@ msgid ""
"This request has been marked for review by the site administrators, who have"
" not hidden it at this time. If you believe it should be hidden, please <a "
"href=\"%s\">contact us</a>."
-msgstr ""
+msgstr "Esta solicitud va a ser revisada por los administradores de la web, que no la han ocultado de momento. Si crees que debe ser ocultada, por favor <a href=\"%s\">contáctanos</a>."
msgid "This request has been reported for administrator attention"
msgstr "Este pedido ha sido reportado al administrador del sitio."
@@ -2859,7 +2859,7 @@ msgid "To follow all successful requests"
msgstr "Sigue todos los pedidos exitosos"
msgid "To follow new requests"
-msgstr ""
+msgstr "Seguir nuevas solicitudes"
msgid "To follow requests and responses matching your search"
msgstr "Para seguir solicitudes y respuestas que encajen con tu búsqueda"
@@ -3247,16 +3247,16 @@ msgid "You are already following requests to {{public_body_name}}"
msgstr "Tu ya estas siguiendo pedidos a {{public_body_name}}"
msgid "You are already following things matching this search"
-msgstr ""
+msgstr "Ya estás siguiendo esta búsqueda por correo"
msgid "You are already following this person"
-msgstr ""
+msgstr "Ya estás siguiendo a esta persona por correo"
msgid "You are already following this request"
-msgstr ""
+msgstr "Ya estás siguiendo esta solicitud por correo"
msgid "You are already following updates about {{track_description}}"
-msgstr ""
+msgstr "Ya estás recibiendo actualizaciones por correo sobre la solicitud."
msgid ""
"You are currently receiving notification of new activity on your wall by "
@@ -3264,7 +3264,7 @@ msgid ""
msgstr "Actualmente estas recibiendo notificaciones de nueva actividad en tu muro por correo electronico."
msgid "You are following all new successful responses"
-msgstr ""
+msgstr "Estás recibiendo correos sobre cualquier nueva respuesta exitosa"
msgid "You are no longer following {{track_description}}."
msgstr ""
@@ -3272,7 +3272,7 @@ msgstr ""
msgid ""
"You are now <a href=\"{{wall_url_user}}\">following</a> updates about "
"{{track_description}}"
-msgstr ""
+msgstr "Ahora estás recibiendo <a href=\"{{wall_url_user}}\">actualizaciones</a> sobre {{track_description}}"
msgid "You can <strong>complain</strong> by"
msgstr "Puede <strong>apelar</strong>"
@@ -3382,7 +3382,7 @@ msgstr "Ya no recibirá correos para esas alertas"
msgid ""
"You will now be emailed updates about {{track_description}}. <a "
"href=\"{{change_email_alerts_url}}\">Prefer not to receive emails?</a>"
-msgstr ""
+msgstr "Ahora recibirás actualizaciones por correo sobre {{track_description}}. <a href=\"{{change_email_alerts_url}}\">¿Prefieres no recibir correos?</a>"
msgid ""
"You will only get an answer to your request if you follow up\\nwith the "
@@ -3396,7 +3396,7 @@ msgid "You're long overdue a response to your FOI request - "
msgstr "La respuesta a tu solicitud de información está muy retrasada - "
msgid "You're not following anything."
-msgstr ""
+msgstr "No estás recibiendo actualizaciones por correo."
msgid "You've now cleared your profile photo"
msgstr "Has borrado la foto de tu perfil"
@@ -3511,13 +3511,13 @@ msgid "Yours sincerely,"
msgstr "Un saludo,"
msgid "[FOI #{{request}} email]"
-msgstr ""
+msgstr "[Dirección de correo de la solicitud #{{request}}]"
msgid "[{{public_body}} request email]"
-msgstr ""
+msgstr "[Dirección de correo del organismo {{public_body}}]"
msgid "[{{site_name}} contact email]"
-msgstr ""
+msgstr "[Correo de contacto de {{site_name}}]"
msgid ""
"\\n\\n[ {{site_name}} note: The above text was badly encoded, and has had "
@@ -3598,7 +3598,7 @@ msgid ""
msgstr "incluyendo tu dirección postal, y pidiéndoles que contesten a tu solicitud.\n O prueba a llamarles por teléfono."
msgid "details"
-msgstr ""
+msgstr "detalles"
msgid "display_status only works for incoming and outgoing messages right now"
msgstr "display_status sólo funciona para mensajes de entrada y salida ahora mismo"
@@ -3625,7 +3625,7 @@ msgid "have delayed."
msgstr "han retrasado."
msgid "hide quoted sections"
-msgstr ""
+msgstr "ocultar partes citadas"
msgid "in term time"
msgstr "durante el periodo escolar"
@@ -3697,13 +3697,13 @@ msgid "sent to {{public_body_name}} by {{info_request_user}} on {{date}}."
msgstr "enviada a {{public_body_name}} por {{info_request_user}} el {{date}}."
msgid "show quoted sections"
-msgstr ""
+msgstr "mostrar partes citadas"
msgid "sign in"
msgstr "abrir sesión"
msgid "simple_date_format"
-msgstr ""
+msgstr "simple_date_format"
msgid "successful"
msgstr "exitosas"
@@ -3785,7 +3785,7 @@ msgid "{{law_used_full}} request GQ - {{title}}"
msgstr "solicitud {{law_used_full}} - {{title}}"
msgid "{{law_used}} requests at {{public_body}}"
-msgstr ""
+msgstr "Solicitudes de información a {{public_body}}"
msgid "{{length_of_time}} ago"
msgstr "hace {{length_of_time}}"
@@ -3833,7 +3833,7 @@ msgid ""
msgstr "Los usuarios de {{site_name}} han hecho {{number_of_requests}} solicitudes, incluyendo:"
msgid "{{title}} - a Freedom of Information request to {{public_body}}"
-msgstr ""
+msgstr "{{title}} - una solicitud de información a {{public_body}}"
msgid "{{user_name}} (Account suspended)"
msgstr "{{user_name}} (Expulsado)"
diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css
index 23c4b32ae..0bf88b0f2 100644
--- a/public/stylesheets/main.css
+++ b/public/stylesheets/main.css
@@ -744,6 +744,10 @@ padding:4px;
margin:0.5em 0;
}
+#everypage .popup-close {
+ text-align: right;
+}
+
#game_sidebar {
float:right;
clear:none;
diff --git a/script/handle-mail-replies.rb b/script/handle-mail-replies.rb
index c05dca453..73fca33c3 100755
--- a/script/handle-mail-replies.rb
+++ b/script/handle-mail-replies.rb
@@ -20,9 +20,11 @@ $:.push(File.join($alaveteli_dir, "lib", "mail_handler"))
require "configuration"
MySociety::Config.set_file(File.join($alaveteli_dir, 'config', 'general'), true)
MySociety::Config.load_default
-
-require 'action_mailer'
require 'mail_handler'
+if RUBY_VERSION.to_f >= 1.9
+ # the default encoding for IO is utf-8, and we use utf-8 internally
+ Encoding.default_external = Encoding.default_internal = Encoding::UTF_8
+end
def main(in_test_mode)
Dir.chdir($alaveteli_dir) do
@@ -47,16 +49,17 @@ def main(in_test_mode)
return 1
end
+ content_type = MailHandler.get_content_type(message)
# If we are still here, there are no permanent failures,
# so if the message is a multipart/report then it must be
# reporting a temporary failure. In this case we discard it
- if message.content_type == "multipart/report"
+ if content_type == "multipart/report"
return 1
end
# Another style of temporary failure message
- subject = message.header_string("Subject")
- if message.content_type == "multipart/mixed" && subject == "Delivery Status Notification (Delay)"
+ subject = MailHandler.get_header_string("Subject", message)
+ if content_type == "multipart/mixed" && subject == "Delivery Status Notification (Delay)"
return 1
end
@@ -72,26 +75,26 @@ def main(in_test_mode)
end
def permanently_failed_addresses(message)
- if message.header_string("Return-Path") == "<>"
+ if MailHandler.empty_return_path?(message)
# Some sort of auto-response
# Check for Exim’s X-Failed-Recipients header
- failed_recipients = message.header_string("X-Failed-Recipients")
+ failed_recipients = MailHandler.get_header_string("X-Failed-Recipients", message)
if !failed_recipients.nil?
# The X-Failed-Recipients header contains the email address that failed
# Check for the words "This is a permanent error." in the body, to indicate
# a permanent failure
- if message.body =~ /This is a permanent error./
+ if MailHandler.get_part_body(message) =~ /This is a permanent error./
return failed_recipients.split(/,\s*/)
end
end
# Next, look for multipart/report
- if message.content_type == "multipart/report"
+ if MailHandler.get_content_type(message) == "multipart/report"
permanently_failed_recipients = []
message.parts.each do |part|
- if part.content_type == "message/delivery-status"
- sections = part.body.split(/\r?\n\r?\n/)
+ if MailHandler.get_content_type(part) == "message/delivery-status"
+ sections = MailHandler.get_part_body(part).split(/\r?\n\r?\n/)
# The first section is a generic header; subsequent sections
# represent a particular recipient. Since we
sections[1..-1].each do |section|
@@ -111,11 +114,11 @@ def permanently_failed_addresses(message)
end
end
- subject = message.header_string("Subject")
+ subject = MailHandler.get_header_string("Subject", message)
# Then look for the style we’ve seen in WebShield bounces
# (These do not have a return path of <> in the cases I have seen.)
if subject == "Returned Mail: Error During Delivery"
- if message.body =~ /^\s*---- Failed Recipients ----\s*((?:<[^>]+>\n)+)/
+ if MailHandler.get_part_body(message) =~ /^\s*---- Failed Recipients ----\s*((?:<[^>]+>\n)+)/
return $1.scan(/<([^>]+)>/).flatten
end
end
@@ -126,12 +129,12 @@ end
def is_oof?(message)
# Check for out-of-office
- if message.header_string("X-POST-MessageClass") == "9; Autoresponder"
+ if MailHandler.get_header_string("X-POST-MessageClass", message) == "9; Autoresponder"
return true
end
- subject = message.header_string("Subject").downcase
- if message.header_string("Return-Path") == "<>"
+ subject = MailHandler.get_header_string("Subject", message).downcase
+ if MailHandler.empty_return_path?(message)
if subject.start_with? "out of office: "
return true
end
@@ -140,7 +143,7 @@ def is_oof?(message)
end
end
- if message.header_string("Auto-Submitted") == "auto-generated"
+ if MailHandler.get_header_string("Auto-Submitted", message) == "auto-generated"
if subject =~ /out of( the)? office/
return true
end
@@ -169,8 +172,8 @@ def forward_on(raw_message)
end
def load_rails
- require File.join('config', 'boot')
- require File.join('config', 'environment')
+ require File.join($alaveteli_dir, 'config', 'boot')
+ require File.join($alaveteli_dir, 'config', 'environment')
end
def record_bounce(email_address, bounce_message)
diff --git a/spec/controllers/api_controller_spec.rb b/spec/controllers/api_controller_spec.rb
index 8d8a39950..1c320f85c 100644
--- a/spec/controllers/api_controller_spec.rb
+++ b/spec/controllers/api_controller_spec.rb
@@ -2,7 +2,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
def normalise_whitespace(s)
- s = s.gsub(/^\s+|\s+$/, "")
+ s = s.gsub(/\A\s+|\s+\Z/, "")
s = s.gsub(/\s+/, " ")
return s
end
@@ -14,23 +14,36 @@ Spec::Matchers.define :be_equal_modulo_whitespace_to do |expected|
end
describe ApiController, "when using the API" do
- it "should check the API key" do
- request_data = {
+
+ describe 'checking API keys' do
+ before do
+ @number_of_requests = InfoRequest.count
+ @request_data = {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}
-
- number_of_requests = InfoRequest.count
+ end
+
+ it 'should check that an API key is given as a param' do
expect {
- post :create_request, :k => "This is not really an API key", :request_json => request_data.to_json
+ post :create_request, :request_json => @request_data.to_json
}.to raise_error ApplicationController::PermissionDenied
-
- InfoRequest.count.should == number_of_requests
+ InfoRequest.count.should == @number_of_requests
+ end
+
+ it "should check the API key" do
+ expect {
+ post :create_request,
+ :k => "This is not really an API key",
+ :request_json => @request_data.to_json
+ }.to raise_error ApplicationController::PermissionDenied
+ InfoRequest.count.should == @number_of_requests
+ end
end
-
+
it "should create a new request from a POST" do
number_of_requests = InfoRequest.count(
:conditions => [
@@ -38,61 +51,61 @@ describe ApiController, "when using the API" do
public_bodies(:geraldine_public_body).id
]
)
-
+
request_data = {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}
-
+
post :create_request, :k => public_bodies(:geraldine_public_body).api_key, :request_json => request_data.to_json
response.should be_success
response.content_type.should == "application/json"
-
+
response_body = ActiveSupport::JSON.decode(response.body)
response_body["errors"].should be_nil
response_body["url"].should =~ /^http/
-
+
InfoRequest.count(:conditions => [
"public_body_id = ?",
public_bodies(:geraldine_public_body).id]
).should == number_of_requests + 1
-
+
new_request = InfoRequest.find(response_body["id"])
new_request.user_id.should be_nil
new_request.external_user_name.should == request_data["external_user_name"]
new_request.external_url.should == request_data["external_url"]
-
+
new_request.title.should == request_data["title"]
new_request.last_event_forming_initial_request.outgoing_message.body.should == request_data["body"].strip
-
+
new_request.public_body_id.should == public_bodies(:geraldine_public_body).id
end
-
+
def _create_request
post :create_request,
:k => public_bodies(:geraldine_public_body).api_key,
:request_json => {
"title" => "Tell me about your chickens",
"body" => "Dear Sir,\n\nI should like to know about your chickens.\n\nYours in faith,\nBob\n",
-
+
"external_url" => "http://www.example.gov.uk/foi/chickens_23",
"external_user_name" => "Bob Smith",
}.to_json
response.content_type.should == "application/json"
return ActiveSupport::JSON.decode(response.body)["id"]
end
-
+
it "should add a response to a request" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has no incoming messages
IncomingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 0
-
+
# Now add one
sent_at = "2012-05-28T12:35:39+01:00"
response_body = "Thank you for your request for information, which we are handling in accordance with the Freedom of Information Act 2000. You will receive a response within 20 working days or before the next full moon, whichever is sooner.\n\nYours sincerely,\nJohn Gandermulch,\nExample Council FOI Officer\n"
@@ -104,13 +117,13 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => response_body
}.to_json
-
+
# And make sure it worked
response.should be_success
incoming_messages = IncomingMessage.all(:conditions => ["info_request_id = ?", request_id])
incoming_messages.count.should == 1
incoming_message = incoming_messages[0]
-
+
incoming_message.sent_at.should == Time.iso8601(sent_at)
incoming_message.get_main_body_text_folded.should be_equal_modulo_whitespace_to(response_body)
end
@@ -118,10 +131,10 @@ describe ApiController, "when using the API" do
it "should add a followup to a request" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has one outgoing message
OutgoingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 1
-
+
# Add another, as a followup
sent_at = "2012-05-29T12:35:39+01:00"
followup_body = "Pls answer ASAP.\nkthxbye\n"
@@ -133,7 +146,7 @@ describe ApiController, "when using the API" do
"sent_at" => sent_at,
"body" => followup_body
}.to_json
-
+
# Make sure it worked
response.should be_success
followup_messages = OutgoingMessage.all(
@@ -141,15 +154,15 @@ describe ApiController, "when using the API" do
)
followup_messages.size.should == 1
followup_message = followup_messages[0]
-
+
followup_message.last_sent_at.should == Time.iso8601(sent_at)
followup_message.body.should == followup_body.strip
end
-
+
it "should not allow internal requests to be updated" do
n_incoming_messages = IncomingMessage.count
n_outgoing_messages = OutgoingMessage.count
-
+
request_id = info_requests(:naughty_chicken_request).id
post :add_correspondence,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -159,20 +172,20 @@ describe ApiController, "when using the API" do
"sent_at" => Time.now.iso8601,
"body" => "xxx"
}.to_json
-
+
response.status.should == "500 Internal Server Error"
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"Request #{request_id} cannot be updated using the API"]
-
+
IncomingMessage.count.should == n_incoming_messages
OutgoingMessage.count.should == n_outgoing_messages
end
-
+
it "should not allow other people's requests to be updated" do
request_id = _create_request
n_incoming_messages = IncomingMessage.count
n_outgoing_messages = OutgoingMessage.count
-
+
post :add_correspondence,
:k => public_bodies(:humpadink_public_body).api_key,
:id => request_id,
@@ -181,15 +194,15 @@ describe ApiController, "when using the API" do
"sent_at" => Time.now.iso8601,
"body" => "xxx"
}.to_json
-
+
response.status.should == "500 Internal Server Error"
ActiveSupport::JSON.decode(response.body)["errors"].should == [
"You do not own request #{request_id}"]
-
+
IncomingMessage.count.should == n_incoming_messages
OutgoingMessage.count.should == n_outgoing_messages
end
-
+
it "should not allow files to be attached to a followup" do
post :add_correspondence,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -202,21 +215,21 @@ describe ApiController, "when using the API" do
:attachments => [
fixture_file_upload("files/tfl.pdf")
]
-
-
+
+
# Make sure it worked
response.status.to_i.should == 500
errors = ActiveSupport::JSON.decode(response.body)["errors"]
errors.should == ["You cannot attach files to messages in the 'request' direction"]
end
-
+
it "should allow files to be attached to a response" do
# First we need an external request
request_id = info_requests(:external_request).id
-
+
# Initially it has no incoming messages
IncomingMessage.count(:conditions => ["info_request_id = ?", request_id]).should == 0
-
+
# Now add one
sent_at = "2012-05-28T12:35:39+01:00"
response_body = "Thank you for your request for information, which we are handling in accordance with the Freedom of Information Act 2000. You will receive a response within 20 working days or before the next full moon, whichever is sooner.\n\nYours sincerely,\nJohn Gandermulch,\nExample Council FOI Officer\n"
@@ -231,34 +244,33 @@ describe ApiController, "when using the API" do
:attachments => [
fixture_file_upload("files/tfl.pdf")
]
-
+
# And make sure it worked
response.should be_success
incoming_messages = IncomingMessage.all(:conditions => ["info_request_id = ?", request_id])
incoming_messages.count.should == 1
incoming_message = incoming_messages[0]
-
+
incoming_message.sent_at.should == Time.iso8601(sent_at)
incoming_message.get_main_body_text_folded.should be_equal_modulo_whitespace_to(response_body)
-
+
# Get the attachment
attachments = incoming_message.get_attachments_for_display
attachments.size.should == 1
attachment = attachments[0]
-
attachment.filename.should == "tfl.pdf"
- attachment.body.should == load_file_fixture("tfl.pdf")
+ attachment.body.should == load_file_fixture("tfl.pdf", as_binary=true)
end
-
+
it "should show information about a request" do
info_request = info_requests(:naughty_chicken_request)
get :show_request,
:k => public_bodies(:geraldine_public_body).api_key,
:id => info_request.id
-
+
response.should be_success
assigns[:request].id.should == info_request.id
-
+
r = ActiveSupport::JSON.decode(response.body)
r["title"].should == info_request.title
# Let’s not test all the fields here, because it would
@@ -266,13 +278,13 @@ describe ApiController, "when using the API" do
# assigns them and changing assignment to an equality
# check, which does not really test anything at all.
end
-
+
it "should show an Atom feed of new request events" do
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
:feed_type => "atom"
-
+
response.should be_success
response.should render_template("api/request_events.atom")
assigns[:events].size.should > 0
@@ -288,7 +300,7 @@ describe ApiController, "when using the API" do
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
:feed_type => "json"
-
+
response.should be_success
assigns[:events].size.should > 0
assigns[:events].each do |event|
@@ -296,13 +308,13 @@ describe ApiController, "when using the API" do
event.outgoing_message.should_not be_nil
event.event_type.should satisfy {|x| ['sent', 'followup_sent', 'resent', 'followup_resent'].include?(x)}
end
-
+
assigns[:event_data].size.should == assigns[:events].size
assigns[:event_data].each do |event_record|
event_record[:event_type].should satisfy {|x| ['sent', 'followup_sent', 'resent', 'followup_resent'].include?(x)}
end
end
-
+
it "should honour the since_event_id parameter" do
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
@@ -311,7 +323,7 @@ describe ApiController, "when using the API" do
response.should be_success
first_event = assigns[:event_data][0]
second_event_id = assigns[:event_data][1][:event_id]
-
+
get :body_request_events,
:id => public_bodies(:geraldine_public_body).id,
:k => public_bodies(:geraldine_public_body).api_key,
@@ -320,14 +332,14 @@ describe ApiController, "when using the API" do
response.should be_success
assigns[:event_data].should == [first_event]
end
-
+
it "should honour the since_date parameter for the Atom feed" do
get :body_request_events,
:id => public_bodies(:humpadink_public_body).id,
:k => public_bodies(:humpadink_public_body).api_key,
:since_date => "2010-01-01",
:feed_type => "atom"
-
+
response.should be_success
response.should render_template("api/request_events.atom")
assigns[:events].size.should > 0
@@ -335,7 +347,7 @@ describe ApiController, "when using the API" do
event.created_at.should >= Date.new(2010, 1, 1)
end
end
-
+
it "should return a JSON 404 error for non-existent requests" do
request_id = 123459876 # Let's hope this doesn't exist!
sent_at = "2012-05-28T12:35:39+01:00"
@@ -351,7 +363,7 @@ describe ApiController, "when using the API" do
response.status.should == "404 Not Found"
ActiveSupport::JSON.decode(response.body)["errors"].should == ["Could not find request 123459876"]
end
-
+
it "should return a JSON 500 error if we try to add correspondence to a request we don't own" do
request_id = info_requests(:naughty_chicken_request).id
sent_at = "2012-05-28T12:35:39+01:00"
diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb
index 830486493..642ed0e05 100644
--- a/spec/controllers/general_controller_spec.rb
+++ b/spec/controllers/general_controller_spec.rb
@@ -97,8 +97,57 @@ describe GeneralController, "when showing the frontpage" do
response.should be_success
end
+ describe 'when there is more than one locale' do
+
+ describe 'when using the default locale' do
+
+ before do
+ @default_lang_home_link = /href=".*\/en\//
+ @other_lang_home_link = /href=".*\/es\//
+ @old_include_default_locale_in_urls = Configuration::include_default_locale_in_urls
+ end
+
+ def set_default_locale_in_urls(value)
+ Configuration.stub!(:include_default_locale_in_urls).and_return(value)
+ load Rails.root.join("config/initializers/fast_gettext.rb")
+ end
+
+ describe 'when the config value INCLUDE_DEFAULT_LOCALE_IN_URLS is false' do
+
+ before do
+ set_default_locale_in_urls(false)
+ end
+
+ it 'should generate URLs without a locale prepended' do
+ get :frontpage
+ response.should_not have_text(@default_lang_home_link)
+ end
+
+ it 'should render the front page in the default language when no locale param
+ is present and the session locale is not the default' do
+ get(:frontpage, {}, {:locale => 'es'})
+ response.should_not have_text(@other_lang_home_link)
+ end
+ end
+
+ it 'should generate URLs with a locale prepended when the config value
+ INCLUDE_DEFAULT_LOCALE_IN_URLS is true' do
+ set_default_locale_in_urls(true)
+ get :frontpage
+ response.should have_text(@default_lang_home_link)
+ end
+
+ after do
+ set_default_locale_in_urls(@old_include_default_locale_in_urls)
+ end
+
+ end
+ end
+
+
describe "when using different locale settings" do
home_link_regex = /href=".*\/en\//
+
it "should generate URLs with a locale prepended when there's more than one locale set" do
get :frontpage
response.should have_text(home_link_regex)
@@ -137,6 +186,7 @@ describe GeneralController, "when showing the frontpage" do
FastGettext.default_available_locales = old_fgt_available_locales
I18n.available_locales = old_i18n_available_locales
end
+
end
end
describe GeneralController, "when showing the front page with fixture data" do
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index e898fb91b..f40eecfff 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -757,7 +757,7 @@ describe RequestController, "when showing one request" do
assigns[:url_path].should_not == old_path
response.location.should have_text(/#{assigns[:url_path]}/)
zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile|
- zipfile.count.should == 5 # the message, two hello.txt, the unknown attachment, and its empty message
+ zipfile.count.should == 4 # the message, two hello.txt plus the unknown attachment
}
end
diff --git a/spec/fixtures/files/dos-linebreaks.email b/spec/fixtures/files/dos-linebreaks.email
new file mode 100644
index 000000000..1f5f1473f
--- /dev/null
+++ b/spec/fixtures/files/dos-linebreaks.email
@@ -0,0 +1,31 @@
+From email@example.com Wed Mar 12 14:58:26 2008
+Return-path: email@example.com>
+Envelope-to: request-xxx-xxxxxx@whatdotheyknow.com
+Delivery-date: Wed, 12 Mar 2008 14:58:26 +0000
+Received: from example.com ([0.0.0.0]:1368 helo=example.com)
+ by tea.ukcod.org.uk with esmtp (Exim 4.50)
+ id 1JZSPS-0002yK-Rq
+ for request-60-3548031c@whatdotheyknow.com; Wed, 12 Mar 2008 14:58:26 +0000
+X-MimeOLE: Produced By Microsoft Exchange V0.0.0.0
+Content-class: urn:content-classes:message
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+Disposition-Notification-To: "A Person" email@example.com>
+Subject: RE: Freedom of Information request - Plans for the East Oxford Community Centre
+Date: Wed, 12 Mar 2008 14:59:04 -0000
+Message-ID: <3D8BEC617D49EF45A9E6D103A83FD30331BF84@local>
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+Thread-Topic: Freedom of Information request
+Thread-Index: AciDziuIcYirFQ7GT36VyP2ABE14qgAg1c0w
+From: "A Person" email@example.com>
+To: FOI Person <EMAIL_TO>
+X-OriginalArrivalTime: 12 Mar 2008 14:59:04.0368 (UTC) FILETIME=[9D245300:01C88451]
+X-SEF-7853D99-ADF1-478E-8894-213D316B8FFA: 1
+X-SEF-Processed: 6_0_1_111__2008_03_12_14_59_05
+
+Thank you for your Freedom of Information request. I have forwarded it=0D=0A=
+to the relevant department for their reply.=0D=0A=0D=0A
+
diff --git a/spec/fixtures/files/humberside-police-odd-mime-type.email b/spec/fixtures/files/humberside-police-odd-mime-type.email
index 5514b29da..ae4ceeffe 100644
--- a/spec/fixtures/files/humberside-police-odd-mime-type.email
+++ b/spec/fixtures/files/humberside-police-odd-mime-type.email
@@ -3,19 +3,20 @@ Return-path: <>
Envelope-to: request-5335-xxxxxxxx@whatdotheyknow.com
Delivery-date: Thu, 01 Jan 2009 15:56:20 +0000
Received: from earth.karoo.kcom.com ([212.50.160.55]:62894)
- by sandwich.ukcod.org.uk with esmtp (Exim 4.63)
- id 1LIPuG-0004AJ-B3
- for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:20 +0000
+ by sandwich.ukcod.org.uk with esmtp (Exim 4.63)
+ id 1LIPuG-0004AJ-B3
+ for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:20 +0000
Received: from unknown (HELO smtp-in.karoo.kcom.com) ([10.102.8.11])
by earth.karoo.kcom.com with ESMTP; 01 Jan 2009 15:44:42 +0000
Received: from exim by smtp-in.karoo.kcom.comwith local (Exim 4.30)
- id 1LIPu0-0004fg-G6 server-id smtp-in4
- for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:04 +0000
+ id 1LIPu0-0004fg-G6 server-id smtp-in4
+ for request-5335-xxxxxxxx@whatdotheyknow.com; Thu, 01 Jan 2009 15:56:04 +0000
X-Failed-Recipients: clerk@humberside-pa.karoo.co.uk
Reply-To: Postmaster <postmaster@karoo.kcom.com>
Auto-Submitted: auto-generated
From: Mail Delivery System <Mailer-Daemon@karoo.co.uk>
To: request-5335-xxxxxxxx@whatdotheyknow.com
+Cc: request-5335-xxxxxxxx@whatdotheyknow.com
Subject: Mail delivery failed : returning message to sender
X-Mailer: Karoo Mailcore [version 2.0-IB]
MIME-Version: 1.0
@@ -52,19 +53,19 @@ Content-Transfer-Encoding: 8bit
Return-path: <request-5335-xxxxxxxx@whatdotheyknow.com>
Received: from [212.50.160.60] (helo=venus.karoo.kcom.com)
- by smtp-in.karoo.kcom.comwith esmtp (Exim 4.30)
- id 1LIPu0-0004fc-FM server-id smtp-in4
- for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:04 +0000
+ by smtp-in.karoo.kcom.comwith esmtp (Exim 4.30)
+ id 1LIPu0-0004fc-FM server-id smtp-in4
+ for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:04 +0000
X-IronPort-Anti-Spam-Filtered: true
X-IronPort-Anti-Spam-Result: AnECAF9nXElSb+bUmWdsb2JhbACMZQGHFQEBAQEBCAsKBxG2eIVy
-X-IronPort-AV: E=Sophos;i="4.36,313,1228089600";
+X-IronPort-AV: E=Sophos;i="4.36,313,1228089600";
d="scan'208";a="465483300"
Received: from sandwich.ukcod.org.uk ([82.111.230.212])
by venus.karoo.kcom.com with ESMTP; 01 Jan 2009 15:46:44 +0000
Received: from foi by sandwich.ukcod.org.uk with local (Exim 4.63)
- (envelope-from <request-5335-xxxxxxxx@whatdotheyknow.com>)
- id 1LIPtz-0004AG-OC
- for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:03 +0000
+ (envelope-from <request-5335-xxxxxxxx@whatdotheyknow.com>)
+ id 1LIPtz-0004AG-OC
+ for clerk@humberside-pa.karoo.co.uk; Thu, 01 Jan 2009 15:56:03 +0000
From: John Jarman <request-5335-xxxxxxxx@whatdotheyknow.com>
To: FOI requests at Humberside Police Authority <clerk@humberside-pa.karoo.co.uk>
Subject: Freedom of Information request - Police Injury Award Pensions
diff --git a/spec/fixtures/files/incoming-request-attachment-headers.email b/spec/fixtures/files/incoming-request-attachment-headers.email
new file mode 100644
index 000000000..80e71556d
--- /dev/null
+++ b/spec/fixtures/files/incoming-request-attachment-headers.email
@@ -0,0 +1,50 @@
+From foi@example.com Mon Oct 06 13:45:38 2008
+Return-path: <foi@example.com>
+Envelope-to: foi@sandwich.ukcod.org.uk
+Delivery-date: Mon, 06 Oct 2008 13:45:38 +0100
+Message-Id: <s8ea1156.098@example.com>
+X-Mailer: Novell GroupWise Internet Agent 6.5.4
+Date: Mon, 06 Oct 2008 13:23:00 +0100
+From: "Steve Knight" <foi@example.com>
+To: <request-xxxx-xxxxx@whatdotheyknow.com>
+Subject: FOI request
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="=__PartFDD45234.0__="
+X-Proofpoint-Virus-Version: vendor=nai engine=5.2.00 definitions=5398 signatures=473327
+X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 classifier= adjust=0 reason=limit engine=3.1.0-0805090000 definitions=main-0810060054
+
+This is a MIME message. If you are reading this text, you may want to
+consider changing to a mail reader or gateway that understands how to
+properly handle MIME multipart messages.
+--=__PartFDD45234.0__=
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Dear Mr Requester,
+
+Here is some information.
+
+--=__PartFDD45234.0__=
+Content-Type: message/rfc822
+
+Date: Fri, 23 May 2008 17:02:11 +0100
+From: "David Bishop" <foi2@example.com>
+To: "foi" <foi3@example.com>
+Subject: Foi request
+Mime-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Extra info.
+
+--=__PartFDD45234.0__=
+Content-Type: image/jpeg; name="info.jpg"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="info.jpg"
+
+xxxx
+
+--=__PartFDD45234.0__=--
+
diff --git a/spec/fixtures/files/incoming-request-attachment-unknown-extension.email b/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
index b3485ec2d..aecd9a52c 100644
--- a/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
+++ b/spec/fixtures/files/incoming-request-attachment-unknown-extension.email
@@ -4,17 +4,16 @@ Subject: Same attachment twice
Content-Type: multipart/mixed; boundary="Q68bSM7Ycu6FN28Q"
Content-Disposition: inline
-
--Q68bSM7Ycu6FN28Q
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
-
--Q68bSM7Ycu6FN28Q
Content-Type: application/x-nonsense
Content-Disposition: attachment; filename="hello.qwglhm"
This is an unusual sort of file.
---Q68bSM7Ycu6FN28Q
+--Q68bSM7Ycu6FN28Q--
+
diff --git a/spec/fixtures/files/many-attachments-date-header.email b/spec/fixtures/files/many-attachments-date-header.email
new file mode 100644
index 000000000..a241e2456
--- /dev/null
+++ b/spec/fixtures/files/many-attachments-date-header.email
@@ -0,0 +1,451 @@
+From email@example.com Wed Apr 14 11:23:08 2010
+Return-path: <email@example.com>
+Envelope-to: email@example.com
+Delivery-date: Wed, 14 Apr 2010 11:23:08 +0100
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from example.com ([0.0.0.0]) by example.com ([0.0.0.0]) with ESMTP (TREND IMSS SMTP Service 7.0) id 1ec0f7ac0002a77f ; Wed, 14 Apr 2010 11:22:52 +0100
+Received: from GWGATE-MTA by example.com
+ with Novell_GroupWise; Wed, 14 Apr 2010 11:22:53 +0100
+Message-Id: <email@example.com>
+X-Mailer: Novell GroupWise Internet Agent 8.0.1
+Date: Wed, 14 Apr 2010 11:22:47 +0100
+From: "A Person" <email@example.com>
+To: <email@example.com>
+Cc: "FOI FOI" <email@example.com>,
+ "A Person" <email@example.com>
+Subject: Fwd: Re: Freedom of Information request
+References: <email@example.com>
+ <email@example.com>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="=__Part163C9567.0__="
+
+This is a MIME message. If you are reading this text, you may want to
+consider changing to a mail reader or gateway that understands how to
+properly handle MIME multipart messages.
+
+--=__Part163C9567.0__=
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+Some information
+
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Date: Wed, 10 Mar 2010 14:17:52 +0000
+From: "A Person" <email@example.com>
+To: "A Person" <email@example.com>
+Subject: Re: xxx
+Mime-Version: 1.0
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+2
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Tue, 24 Nov 2009 10:45:58 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from example.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 00660acd00000f42 ; Tue, 24 Nov 2009 10:45:55 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob115.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Tue, 24 Nov 2009 10:45:56 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Tue, 24 Nov 2009
+ 10:45:53 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+Date: Tue, 24 Nov 2009 10:45:52 +0000
+Subject: example
+Thread-Topic: example
+Thread-Index: AcpnbI2i+XAmfHFVTFy0eGDpVJhXoQFhVeZw
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/mixed;
+ boundary="_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17028.005
+X-TM-AS-Result: No--19.329-5.0-31-1
+X-imss-scan-details: No--19.329-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: multipart/related;
+ boundary="_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_";
+ type="multipart/alternative"
+
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: text/plain; charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+
+3
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: text/html; charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+4
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Tue, 17 Nov 2009 09:58:46 GMT";
+ modification-date="Tue, 17 Nov 2009 09:58:46 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+5
+--_005_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_
+Content-Type: application/vnd.ms-excel;
+ name="particpant list.xls"
+Content-Description: particpant list.xls
+Content-Disposition: attachment;
+ filename="particpant list.xls"; size=21504;
+ creation-date="Mon, 02 Nov 2009 09:42:37 GMT";
+ modification-date="Tue, 24 Nov 2009 10:45:52 GMT"
+Content-Transfer-Encoding: base64
+
+6
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48FD3549evs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Thu, 03 Dec 2009 09:29:07 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog116.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 0ac1bf1b0001116e ; Thu, 3 Dec 2009 09:29:04 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob116.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Thu, 03 Dec 2009 09:29:06 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Thu, 3 Dec 2009 09:29:03
+ +0000
+From: A Person <email@example.com>
+To: 'A Person' <email@example.com>
+Date: Thu, 3 Dec 2009 09:29:03 +0000
+Subject: RE: example
+Thread-Topic: example
+Thread-Index: AcpuoEyRvzM8fXw+THuj/617pjnvCgFWqZdQ
+Message-ID: <email@example.com>
+References: <email@example.com>
+ <email@example.com>
+In-Reply-To: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17046.004
+X-TM-AS-Result: No--16.791-5.0-31-1
+X-imss-scan-details: No--16.791-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+7
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Wed, 25 Nov 2009 22:26:23 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog105.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 034354c900007016 ; Wed, 25 Nov 2009 22:26:19 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob105.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Wed, 25 Nov 2009 22:26:21 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Wed, 25 Nov 2009
+ 22:26:15 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+CC: A Person <email@example.com>
+Date: Wed, 25 Nov 2009 22:26:12 +0000
+Subject: As promised - Masterclass info (example)
+Thread-Topic: As promised - Masterclass info (example)
+Thread-Index: AcpuHcJ4yrR8PBHZTVCU/RLGzwqsDAAACGwQ
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/mixed;
+ boundary="_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17032.000
+X-TM-AS-Result: No--26.167-5.0-31-1
+X-imss-scan-details: No--26.167-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: multipart/related;
+ boundary="_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_";
+ type="multipart/alternative"
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+8
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: text/html; charset="utf-8"
+Content-Transfer-Encoding: base64
+
+9
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Wed, 25 Nov 2009 22:26:14 GMT";
+ modification-date="Wed, 25 Nov 2009 22:26:14 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+
+10
+
+--_006_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: application/msword;
+ name= "Participant List.doc"
+Content-Description: Participant List.doc
+Content-Disposition: attachment;
+ filename="Participant List.doc"; size=112640;
+ creation-date="Wed, 25 Nov 2009 22:17:24 GMT";
+ modification-date="Wed, 25 Nov 2009 11:43:48 GMT"
+Content-Transfer-Encoding: base64
+
+11
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_
+Content-Type: application/msword; name="Information & Booking Form.doc"
+Content-Description: Information & Booking Form.doc
+Content-Disposition: attachment; filename="Information & Booking Form.doc"; size=84480;
+ creation-date="Wed, 25 Nov 2009 22:17:40 GMT";
+ modification-date="Wed, 04 Nov 2009 14:42:54 GMT"
+Content-Transfer-Encoding: base64
+
+12
+
+--_007_B3BDF1D06801114FA040D0F1BEE7CF9C48DC6D82evs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Fri, 04 Dec 2009 10:00:05 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog109.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 100473260001a476 ; Fri, 4 Dec 2009 10:00:01 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob109.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Fri, 04 Dec 2009 10:00:04 UTC
+Received: from example.com ([::1]) by
+ example.com ([::1]) with mapi; Fri, 4 Dec 2009 10:00:01
+ +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+Date: Fri, 4 Dec 2009 10:00:01 +0000
+Subject: Re: As promised - info (example)
+Thread-Topic: As promised - info (example)
+Thread-Index: AcpzhLeBjBId8eZATYudOfBgN6CPXQBQ9Pok
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: base64
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17048.005
+X-TM-AS-Result: No--24.171-5.0-31-1
+X-imss-scan-details: No--24.171-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+13
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Sun, 21 Mar 2010 21:53:38 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog117.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 1e3611c1000d37df ; Sun, 21 Mar 2010 21:53:32 +0000
+Received: from source ([0.0.0.0]) (using TLSv1) by eu1sys200aob117.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Sun, 21 Mar 2010 21:53:37 UTC
+Received: from example.com ([::1]) by exchhub01
+ ([0.0.0.0]) with mapi; Sun, 21 Mar 2010 21:53:34 +0000
+From: A Person <email@example.com>
+To: email@example.com <email@example.com>
+CC: A Person <email@example.com>
+Date: Sun, 21 Mar 2010 21:53:32 +0000
+Subject: Thank you from example
+Thread-Topic: Thank you from example
+Thread-Index: AcrJQPL4xb9zjXMHRJGTjAxo3X/kfA==
+Message-ID: <email@example.com>
+Accept-Language: en-US, en-GB
+Content-Language: en-US
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+acceptlanguage: en-US, en-GB
+Content-Type: multipart/related;
+ boundary="_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_";
+ type="multipart/alternative"
+MIME-Version: 1.0
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17266.002
+X-TM-AS-Result: No--26.373-5.0-31-1
+X-imss-scan-details: No--26.373-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: multipart/alternative;
+ boundary="_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_"
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+14
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: text/html; charset="us-ascii"
+Content-Transfer-Encoding: quoted-printable
+
+15
+
+--_000_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_--
+
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_
+Content-Type: image/gif; name="image001.gif"
+Content-Description: image001.gif
+Content-Disposition: inline; filename="image001.gif"; size=5445;
+ creation-date="Sun, 21 Mar 2010 21:53:33 GMT";
+ modification-date="Sun, 21 Mar 2010 21:53:33 GMT"
+Content-ID: <email@example.com>
+Content-Transfer-Encoding: base64
+
+16
+--_004_B3BDF1D06801114FA040D0F1BEE7CF9C5E14635Bevs02ukcommonpu_--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Tue, 23 Feb 2010 15:33:48 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog112.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id 96f54043000f2e72 ; Tue, 23 Feb 2010 15:33:48 +0000
+Received: from source ([0.0.0.0]) by eu1sys200aob112.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Tue, 23 Feb 2010 15:33:47 UTC
+Received: from gla-002561-lap ([0.0.0.0]) by example.com with Microsoft SMTPSVC(0.0.0.0);
+ Tue, 23 Feb 2010 15:33:46 +0000
+Reply-To: email@example.com
+From: email@example.com
+To: email@example.com
+Subject: example - Meeting - Tuesday 2nd March
+Date: 23 February 2010 15:33
+X-Mailer: Internet Professional v1.15
+Return-Path: email@example.com
+Message-ID: <email@example.com>
+X-OriginalArrivalTime: 23 Feb 2010 15:33:46.0648 (UTC) FILETIME=[96CEC980:01CAB49D]
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17212.000
+X-TM-AS-Result: No--16.146-5.0-31-1
+X-imss-scan-details: No--16.146-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+17
+
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Return-path: <email@example.com>
+Received: from example.com ([0.0.0.0])
+ by example.com with ESMTP; Mon, 08 Mar 2010 09:21:42 +0000
+X-TM-IMSS-Message-ID:<email@example.com>
+Received: from eu1sys200aog117.obsmtp.com ([0.0.0.0]) by example.com ([0.0.0.0]) with SMTP (TREND IMSS SMTP Service 7.0) id d8931aff001580d6 ; Mon, 8 Mar 2010 09:21:40 +0000
+Received: from source ([0.0.0.0]) by eu1sys200aob117.postini.com ([0.0.0.0]) with SMTP
+ ID email@example.com Mon, 08 Mar 2010 09:21:39 UTC
+Received: from gla-002561-lap ([0.0.0.0]) by example.com with Microsoft SMTPSVC(0.0.0.0);
+ Mon, 8 Mar 2010 09:21:36 +0000
+Reply-To: email@example.com
+From: email@example.com
+To: email@example.com
+Subject: example - Help needed
+Date: 08 March 2010 09:21
+X-Mailer: Internet Professional v1.15
+MIME-Version: 1.0
+Content-Type: multipart/mixed;boundary="_NextPart_00009D35-00000F3C-00271781-26DF"
+Return-Path: email@example.com
+Message-ID: <email@example.com>
+X-OriginalArrivalTime: 08 Mar 2010 09:21:36.0283 (UTC) FILETIME=[C03E3EB0:01CABEA0]
+X-TM-AS-Product-Ver: IMSS-0.0.0.0-0.0.0.0-17236.006
+X-TM-AS-Result: No--32.111-5.0-31-1
+X-imss-scan-details: No--32.111-5.0-31-1
+X-TM-AS-User-Approved-Sender: No
+X-TM-AS-User-Blocked-Sender: No
+
+This message is in MIME format. Since your mail reader does not
+understand this format, some or all of this message may not be legible.
+--_NextPart_00009D35-00000F3C-00271781-26DF
+Content-Type: text/plain
+Content-Transfer-Encoding: 7bit
+
+18
+
+--_NextPart_00009D35-00000F3C-00271781-26DF
+Content-Type: application/octet-stream;name="Information Pack.pdf"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;filename="Information Pack.pdf";size=106688
+
+19
+--_NextPart_00009D35-00000F3C-00271781-26DF--
+--=__Part163C9567.0__=
+Content-Type: message/rfc822
+
+Date: Wed, 02 Dec 2009 19:21:27 +0000
+From: "A Person" <email@example.com>
+To: "A Person" <email@example.com>
+Subject: Re: As promised - info (example)
+Mime-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline
+
+20
+--=__Part163C9567.0__=--
+
diff --git a/spec/fixtures/files/rfc822-attachment.email b/spec/fixtures/files/rfc822-attachment.email
new file mode 100644
index 000000000..ae58a06af
--- /dev/null
+++ b/spec/fixtures/files/rfc822-attachment.email
@@ -0,0 +1,147 @@
+From foi.officer@example.com Fri Mar 14 08:39:57 2008
+Return-path: <foi.officer@example.com>
+Envelope-to: request-bounce-xx-xxxxx@whatdotheyknow.com
+Delivery-date: Fri, 14 Mar 2008 08:39:57 +0000
+Received: from service27.mimecast.com ([213.235.63.79]:55305)
+ by tea.ukcod.org.uk with smtp (Exim 4.50)
+ id 1Ja5SH-0005iP-Jm
+ for xxx@whatdotheyknow.com; Fri, 14 Mar 2008 08:39:57 +0000
+Received: from mailscan.ad.example.com (mailgate.example.com [194.70.143.2])
+ by service27.mimecast.com;
+ Fri, 14 Mar 2008 08:38:47 +0000
+Received: from exch2serv.ad.example.com ([201.234.62.4]) by mailscan.ad.example.com with InterScan Message Security Suite; Fri, 14 Mar 2008 08:38:47 -0000
+X-MIMEOLE: Produced By Microsoft Exchange V6.0.6603.0
+content-class: urn:content-classes:message
+MIME-Version: 1.0
+Subject:
+Date: Fri, 14 Mar 2008 08:38:46 -0000
+Message-ID: <0F3951EA9DCFB246827E1F6513F6C79D096DDADB@exch2serv.ad.example.com>
+X-MS-Has-Attach: yes
+X-MS-TNEF-Correlator:
+Thread-Index: AciFrtIeS9pyMOhuQdyxkm5305zs9g==
+From: "An FOI Officer" <foi.officer@example.com>
+To: "On" <request-bounce-xx-xxxxx@whatdotheyknow.com>
+X-MC-Unique: 108031408384702301
+Content-Type: multipart/mixed;
+ boundary="----_=_NextPart_001_01C885AE.D1BF23AC"
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_001_01C885AE.D1BF23AC
+Content-Type: multipart/alternative;
+ boundary="MCBoundary=_108031408384800401"
+
+--MCBoundary=_108031408384800401
+Content-Type: text/plain; charset=WINDOWS-1252
+Content-Transfer-Encoding: quoted-printable
+
+
+ <<Freedom of Information request >>=20
+
+
+e-mail: foi.officer@example.com=20
+
+
+
+
+***************************************************************************=
+********
+The information in this Email and any attachments is personal to the
+sender and the views of the author may not necessarily reflect those
+of Borough Council. The information is strictly confidential
+and is intended only for the named person or organisation to whom it is
+addressed as it may contain privileged and confidential information. If
+you are not the intended recipient do not copy, distribute or use this
+Email, and please notify the sender. Please note that we cannot
+guarantee that this message or any attachment is virus free or has not
+been intercepted and amended.
+***************************************************************************=
+********
+
+Disclaimer=20
+---------------------------------------------------------------------------=
+----------------
+This email message has been scanned for viruses by Mimecast.
+Mimecast delivers a complete managed email solution from a single web based=
+ platform.
+For more information please visit www.mimecast.com
+---------------------------------------------------------------------------=
+----------------
+--MCBoundary=_108031408384800401
+Content-Type: text/html; charset=WINDOWS-1252
+Content-Transfer-Encoding: quoted-printable
+
+<HTML><BODY> =20
+ <BR>
+ &lt;&lt;Freedom of Information request &gt;&gt; <BR>
+<BR>
+e-mail: <a href=3D"mailto:foi.officer@example.com">foi.officer@example.com</a> <BR>
+<BR>
+<BR>
+<BR>
+<BR>
+***************************************************************************=
+********<BR>
+The information in this Email and any attachments is personal to the<B=
+R>
+sender and the views of the author may not necessarily reflect those<BR=
+>
+of Borough Council. The information is strictly confidential<B=
+R>
+and is intended only for the named person or organisation to whom it is<BR>
+addressed as it may contain privileged and confidential information. If<B=
+R>
+you are not the intended recipient do not copy, distribute or use this=
+<BR>
+Email, and please notify the sender. Please note that we cannot<B=
+R>
+guarantee that this message or any attachment is virus free or has not<BR>
+been intercepted and amended.<BR>
+***************************************************************************=
+********<BR>
+
+ <BR>
+ <BR>
+ <span style=3D"font-family:Arial; Font-size:10.0pt">
+ Disclaimer <p>
+ <hr width=3D"100%">
+ This email message has been scanned for viruses by Mimecast.<BR>
+ Mimecast delivers a complete managed email solution from a single we=
+b based platform.<BR>
+ For more information please visit <a href=3D"http://www.mimecast.com=
+">http://www.mimecast.com</a>
+ <hr width=3D"100%">
+ </span>
+ </BODY></HTML>
+
+
+--MCBoundary=_108031408384800401--
+------_=_NextPart_001_01C885AE.D1BF23AC
+Content-Type: message/rfc822
+Content-Transfer-Encoding: 7bit
+
+X-MIMEOLE: Produced By Microsoft Exchange V6.0.6603.0
+content-class: urn:content-classes:message
+MIME-Version: 1.0
+Subject: Freedom of Information request
+Date: Thu, 13 Mar 2008 16:57:33 -0000
+Message-ID: <0F3951EA9DCFB246827E1F6513F6C79D098AC8C5@exch2serv.ad.example.com>
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+Thread-Topic: Freedom of Information request
+Thread-Index: AciE9w0L7QnQlahDQS+Zjrz40mr8KAAD5KuQAADg+1AABjC9wAAAXtJgAAEHJvAAAHTxwA==
+X-Priority: 1
+Priority: Urgent
+Importance: high
+From: "An FOI Officer" <foi.officer@example.com>
+To: <request-bounce-xx-xxxxx@whatdotheyno.com>
+Content-Type: text/plain;
+ charset="utf-8"
+Content-Transfer-Encoding: base64
+
+c29tZSBleGFtcGxlIHRleHQ=
+
+------_=_NextPart_001_01C885AE.D1BF23AC--
+
+
+
diff --git a/spec/lib/i18n_interpolation.rb b/spec/lib/i18n_interpolation.rb
index 8c86413ad..6b745059c 100644
--- a/spec/lib/i18n_interpolation.rb
+++ b/spec/lib/i18n_interpolation.rb
@@ -1,6 +1,3 @@
-# This is a test of the set_content_type monkey patch in
-# lib/tmail_extensions.rb
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "when using i18n" do
diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb
index a3fba0698..ae65210f2 100644
--- a/spec/lib/mail_handler/mail_handler_spec.rb
+++ b/spec/lib/mail_handler/mail_handler_spec.rb
@@ -1,6 +1,12 @@
# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '../../../spec_helper')
+def create_message_from(from_field)
+ mail_data = load_file_fixture('incoming-request-plain.email')
+ mail_data.gsub!('EMAIL_FROM', from_field)
+ mail = MailHandler.mail_from_raw_email(mail_data)
+end
+
describe 'when creating a mail object from raw data' do
it 'should correctly parse a multipart email with a linebreak in the boundary' do
@@ -17,7 +23,361 @@ describe 'when creating a mail object from raw data' do
it 'should convert an iso8859 email to utf8' do
mail = get_fixture_mail('iso8859_2_raw_email.email')
mail.subject.should have_text(/gjatë/u)
- mail.body.is_utf8?.should == true
+ MailHandler.get_part_body(mail).is_utf8?.should == true
+ end
+
+end
+
+describe 'when asked for the from name' do
+
+ it 'should return nil if there is a blank "From" field' do
+ mail = create_message_from('')
+ MailHandler.get_from_name(mail).should == nil
+ end
+
+ it 'should correctly return an encoded name from the from field' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ MailHandler.get_from_name(mail).should == 'Coordenação de Relacionamento, Pesquisa e Informação/CEDI'
+ end
+
+ it 'should get a name from a "From" field with a name and address' do
+ mail = get_fixture_mail('incoming-request-oft-attachments.email')
+ MailHandler.get_from_name(mail).should == 'Public Authority'
+ end
+
+ it 'should return nil from a "From" field that is just a name'do
+ mail = get_fixture_mail('track-response-webshield-bounce.email')
+ MailHandler.get_from_name(mail).should == nil
+ end
+
+end
+
+describe 'when asked for the from address' do
+
+ it 'should return nil if there is a blank "From" field' do
+ mail = create_message_from('')
+ MailHandler.get_from_address(mail).should == nil
+ end
+
+ it 'should correctly return an address from a mail that has an encoded name in the from field' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ MailHandler.get_from_address(mail).should == 'geraldinequango@localhost'
+ end
+
+ it 'should return nil if there is no address in the "From" field' do
+ mail = get_fixture_mail('track-response-webshield-bounce.email')
+ MailHandler.get_from_address(mail).should == nil
+ end
+
+ it 'should return the "From" email address if there is one' do
+ mail = get_fixture_mail('track-response-abcmail-oof.email')
+ MailHandler.get_from_address(mail).should == 'Name.Removed@example.gov.uk'
+ end
+
+ it 'should get an address from a "From" field with a name and address' do
+ mail = get_fixture_mail('incoming-request-oft-attachments.email')
+ MailHandler.get_from_address(mail).should == 'public@authority.gov.uk'
+ end
+end
+
+describe 'when asked for all the addresses a mail has been sent to' do
+
+ it 'should return an array containing the envelope-to address and the to address, and the cc address if there is one' do
+ mail_data = load_file_fixture('humberside-police-odd-mime-type.email')
+ mail_data.gsub!('Envelope-to: request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'Envelope-to: request-5555-xxxxxxxx@whatdotheyknow.com')
+ mail_data.gsub!('Cc: request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'Cc: request-3333-xxxxxxxx@whatdotheyknow.com')
+ mail = MailHandler.mail_from_raw_email(mail_data)
+ MailHandler.get_all_addresses(mail).should == ['request-5335-xxxxxxxx@whatdotheyknow.com',
+ 'request-3333-xxxxxxxx@whatdotheyknow.com',
+ 'request-5555-xxxxxxxx@whatdotheyknow.com']
+ end
+
+ it 'should only return unique values' do
+ # envelope-to and to fields are the same
+ mail = get_fixture_mail('humberside-police-odd-mime-type.email')
+ MailHandler.get_all_addresses(mail).should == ['request-5335-xxxxxxxx@whatdotheyknow.com']
+ end
+
+ it 'should handle the absence of an envelope-to or cc field' do
+ mail_data = load_file_fixture('autoresponse-header.email')
+ mail_data.gsub!('To: FOI Person <EMAIL_TO>',
+ 'To: FOI Person <request-5555-xxxxxxxx@whatdotheyknow.com>')
+ mail = MailHandler.mail_from_raw_email(mail_data)
+ MailHandler.get_all_addresses(mail).should == ["request-5555-xxxxxxxx@whatdotheyknow.com"]
+ end
+end
+
+describe 'when asked for auto_submitted' do
+
+ it 'should return a string value for an email with an auto-submitted header' do
+ mail = get_fixture_mail('autoresponse-header.email')
+ MailHandler.get_auto_submitted(mail).should == 'auto-replied'
+ end
+
+ it 'should return a nil value for an email with no auto-submitted header' do
+ mail = get_fixture_mail('incoming-request-plain.email')
+ MailHandler.get_auto_submitted(mail).should == nil
+ end
+
+end
+
+describe 'when asked if there is an empty return path' do
+
+ it 'should return true if there is an empty return-path specified' do
+ mail = get_fixture_mail('empty-return-path.email')
+ MailHandler.empty_return_path?(mail).should == true
+ end
+
+ it 'should return false if there is no return-path header' do
+ mail = get_fixture_mail('incoming-request-attach-attachments.email')
+ MailHandler.empty_return_path?(mail).should == false
+ end
+
+ it 'should return false if there is a return path address' do
+ mail = get_fixture_mail('autoresponse-header.email')
+ MailHandler.empty_return_path?(mail).should == false
+ end
+end
+
+describe 'when deriving a name, email and formatted address from a message from a line' do
+
+ def should_render_from_address(from_line, expected_result)
+ mail = create_message_from(from_line)
+ name = MailHandler.get_from_name(mail)
+ email = MailHandler.get_from_address(mail)
+ address = MailHandler.address_from_name_and_email(name, email).to_s
+ [name, email, address].should == expected_result
+ end
+
+ it 'should correctly render a name with quoted commas' do
+ should_render_from_address('"Clare College, Cambridge" <test@test.test>',
+ ['Clare College, Cambridge',
+ 'test@test.test',
+ '"Clare College, Cambridge" <test@test.test>'])
end
+ it 'should correctly reproduce a simple name and email that does not need quotes' do
+ should_render_from_address('"FOI Person" <foiperson@localhost>',
+ ['FOI Person',
+ 'foiperson@localhost',
+ 'FOI Person <foiperson@localhost>'])
+ end
+
+ it 'should render an address with no name' do
+ should_render_from_address("foiperson@localhost",
+ [nil,
+ "foiperson@localhost",
+ "foiperson@localhost"])
+ end
+
+ it 'should quote a name with a square bracked in it' do
+ should_render_from_address('"FOI [ Person" <foiperson@localhost>',
+ ['FOI [ Person',
+ 'foiperson@localhost',
+ '"FOI [ Person" <foiperson@localhost>'])
+ end
+
+ it 'should quote a name with an @ in it' do
+ should_render_from_address('"FOI @ Person" <foiperson@localhost>',
+ ['FOI @ Person',
+ 'foiperson@localhost',
+ '"FOI @ Person" <foiperson@localhost>'])
+ end
+
+
+ it 'should quote a name with quotes in it' do
+ should_render_from_address('"FOI \" Person" <foiperson@localhost>',
+ ['FOI " Person',
+ 'foiperson@localhost',
+ '"FOI \" Person" <foiperson@localhost>'])
+ end
+
+end
+
+describe 'when getting the content type of a mail part' do
+
+ def expect_content_type(fixture_file, content_type)
+ mail = get_fixture_mail(fixture_file)
+ MailHandler.get_content_type(mail).should == content_type
+ end
+
+ it 'should correctly return a type of "multipart/report"' do
+ expect_content_type('track-response-multipart-report.email', 'multipart/report')
+ end
+
+ it 'should correctly return a type of "text/plain"' do
+ expect_content_type('track-response-abcmail-oof.email', 'text/plain')
+ end
+
+ it 'should correctly return a type of "multipart/mixed"' do
+ expect_content_type('track-response-messageclass-oof.email', 'multipart/mixed')
+ end
+
+ it 'should correctly return the types in an example bounce report' do
+ mail = get_fixture_mail('track-response-ms-bounce.email')
+ report = mail.parts.detect{ |part| MailHandler.get_content_type(part) == 'multipart/report'}
+ MailHandler.get_content_type(report.parts[0]).should == 'text/plain'
+ MailHandler.get_content_type(report.parts[1]).should == 'message/delivery-status'
+ MailHandler.get_content_type(report.parts[2]).should == 'message/rfc822'
+ end
+
+end
+
+describe 'when getting header strings' do
+
+ def expect_header_string(fixture_file, header, header_string)
+ mail = get_fixture_mail(fixture_file)
+ MailHandler.get_header_string(header, mail).should == header_string
+ end
+
+ it 'should return the contents of a "Subject" header' do
+ expect_header_string('track-response-ms-bounce.email',
+ 'Subject',
+ 'Delivery Status Notification (Delay)')
+ end
+
+ it 'should return the contents of an "X-Failed-Recipients" header' do
+ expect_header_string('autoresponse-header.email',
+ 'X-Failed-Recipients',
+ 'enquiries@cheese.com')
+ end
+
+ it 'should return the contents of an example "" header' do
+ expect_header_string('track-response-messageclass-oof.email',
+ 'X-POST-MessageClass',
+ '9; Autoresponder')
+ end
+
+end
+
+describe "when parsing HTML mail" do
+ it "should display UTF-8 characters in the plain text version correctly" do
+ html = "<html><b>foo</b> është"
+ plain_text = MailHandler.get_attachment_text_one_file('text/html', html)
+ plain_text.should match(/është/)
+ end
+
+end
+
+describe "when getting the attachment text" do
+ it "should not raise an error if the expansion of a zip file raises an error" do
+ mock_entry = mock('ZipFile entry', :file? => true)
+ mock_entries = [mock_entry]
+ mock_entries.stub!(:close)
+ mock_entry.stub!(:get_input_stream).and_raise("invalid distance too far back")
+ Zip::ZipFile.stub!(:open).and_return(mock_entries)
+ MailHandler.get_attachment_text_one_file('application/zip', "some string")
+ end
+
+end
+
+describe 'when getting attachment attributes' do
+
+ it 'should get two attachment parts from a multipart mail with text and html alternatives
+ and an image' do
+ mail = get_fixture_mail('quoted-subject-iso8859-1.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.size.should == 2
+ end
+
+ it 'should expand a mail attached as text' do
+ mail = get_fixture_mail('rfc822-attachment.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ attributes.size.should == 2
+ rfc_attachment = attributes[1]
+ rfc_attachment[:within_rfc822_subject].should == 'Freedom of Information request'
+ headers = ['Date: Thu, 13 Mar 2008 16:57:33 +0000',
+ 'Subject: Freedom of Information request',
+ 'From: An FOI Officer <foi.officer@example.com>',
+ 'To: request-bounce-xx-xxxxx@whatdotheyno.com']
+ rfc_attachment[:body].should == "#{headers.join("\n")}\n\nsome example text"
+ end
+
+ it 'should handle a mail which causes Tmail to generate a blank header value' do
+ mail = get_fixture_mail('many-attachments-date-header.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+ end
+
+ it 'should produce a consistent set of url_part_numbers, content_types, within_rfc822_subjects
+ and filenames from an example mail with lots of attachments' do
+ mail = get_fixture_mail('many-attachments-date-header.email')
+ attributes = MailHandler.get_attachment_attributes(mail)
+
+ expected_attributes = [ { :content_type=>"text/plain",
+ :url_part_number=>1,
+ :within_rfc822_subject=>nil,
+ :filename=>nil},
+ { :content_type=>"text/plain",
+ :url_part_number=>2,
+ :within_rfc822_subject=>"Re: xxx",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>4,
+ :within_rfc822_subject=>"example",
+ :filename=>nil},
+ { :content_type=>"image/gif", :url_part_number=>5,
+ :within_rfc822_subject=>"example",
+ :filename=>"image001.gif"},
+ { :content_type=>"application/vnd.ms-excel",
+ :url_part_number=>6,
+ :within_rfc822_subject=>"example",
+ :filename=>"particpant list.xls"},
+ { :content_type=>"text/plain",
+ :url_part_number=>7,
+ :within_rfc822_subject=>"RE: example",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>9,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>nil},
+ { :content_type=>"image/gif",
+ :url_part_number=>10,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"image001.gif"},
+ { :content_type=>"application/vnd.ms-word",
+ :url_part_number=>11,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"Participant List.doc"},
+ { :content_type=>"application/vnd.ms-word",
+ :url_part_number=>12,
+ :within_rfc822_subject=>"As promised - Masterclass info (example)",
+ :filename=>"Information & Booking Form.doc"},
+ { :content_type=>"text/plain",
+ :url_part_number=>13,
+ :within_rfc822_subject=>"Re: As promised - info (example)",
+ :filename=>nil},
+ { :content_type=>"text/html",
+ :url_part_number=>15,
+ :within_rfc822_subject=>"Thank you from example",
+ :filename=>nil},
+ { :content_type=>"image/gif",
+ :url_part_number=>16,
+ :within_rfc822_subject=>"Thank you from example",
+ :filename=>"image001.gif"},
+ { :content_type=>"text/plain",
+ :url_part_number=>17,
+ :within_rfc822_subject=>"example - Meeting - Tuesday 2nd March",
+ :filename=>nil},
+ { :content_type=>"text/plain",
+ :url_part_number=>18,
+ :within_rfc822_subject=>"example - Help needed",
+ :filename=>nil},
+ { :content_type=>"application/pdf",
+ :url_part_number=>19,
+ :within_rfc822_subject=>"example - Help needed",
+ :filename=>"Information Pack.pdf"},
+ { :content_type=>"text/plain",
+ :url_part_number=>20,
+ :within_rfc822_subject=>"Re: As promised - info (example)",
+ :filename=>nil} ]
+
+ attributes.each_with_index do |attr, index|
+ attr.delete(:charset)
+ attr.delete(:body)
+ attr.delete(:hexdigest)
+ attr.should == expected_attributes[index]
+ end
+ end
end
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index fdbcd1e23..70b323e9f 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -26,11 +26,6 @@ describe IncomingMessage, " when dealing with incoming mail" do
@im.sent_at.should == @im.mail.date
end
- it "should be able to parse emails with quoted commas in" do
- em = "\"Clare College, Cambridge\" <test@test.test>"
- TMail::Address.parse(em)
- end
-
it "should correctly fold various types of footer" do
Dir.glob(File.join(Spec::Runner.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
message = File.read(file)
@@ -73,6 +68,14 @@ describe IncomingMessage, " when dealing with incoming mail" do
message.get_main_body_text_internal.should include("The above text was badly encoded")
end
+ it 'should convert DOS-style linebreaks to Unix style' do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('dos-linebreaks.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.parse_raw_email!
+ message.get_main_body_text_internal.should_not match(/\r\n/)
+ end
+
it "should fold multiline sections" do
{
"foo\n--------\nconfidential" => "foo\nFOLDED_QUOTED_SECTION\n", # basic test
@@ -107,27 +110,6 @@ describe IncomingMessage, " when dealing with incoming mail" do
end
-describe IncomingMessage, "when parsing HTML mail" do
- it "should display UTF-8 characters in the plain text version correctly" do
- html = "<html><b>foo</b> është"
- plain_text = IncomingMessage._get_attachment_text_internal_one_file('text/html', html)
- plain_text.should match(/është/)
- end
-
-end
-
-describe IncomingMessage, "when getting the attachment text" do
-
- it "should not raise an error if the expansion of a zip file raises an error" do
- mock_entry = mock('ZipFile entry', :file? => true)
- mock_entry.stub!(:get_input_stream).and_raise("invalid distance too far back")
- Zip::ZipFile.stub!(:open).and_return([mock_entry])
- IncomingMessage._get_attachment_text_internal_one_file('application/zip', "some string")
- end
-
-end
-
-
describe IncomingMessage, " display attachments" do
it "should not show slashes in filenames" do
@@ -143,7 +125,7 @@ describe IncomingMessage, " display attachments" do
# http://www.whatdotheyknow.com/request/post_commercial_manager_librarie#incoming-17233
foi_attachment.within_rfc822_subject = "FOI/09/066 RESPONSE TO FOI REQUEST RECEIVED 21st JANUARY 2009"
foi_attachment.content_type = 'text/plain'
- foi_attachment.ensure_filename!
+ foi_attachment.ensure_filename!
expected_display_filename = foi_attachment.within_rfc822_subject.gsub(/\//, " ") + ".txt"
foi_attachment.display_filename.should == expected_display_filename
end
@@ -194,59 +176,50 @@ describe IncomingMessage, " folding quoted parts of emails" do
end
describe IncomingMessage, " checking validity to reply to" do
- def test_email(result, email, return_path, autosubmitted = nil)
- @address = mock(TMail::Address)
- @address.stub!(:spec).and_return(email)
-
- @return_path = mock(TMail::ReturnPathHeader)
- @return_path.stub!(:addr).and_return(return_path)
- if !autosubmitted.nil?
- @autosubmitted = TMail::UnstructuredHeader.new("auto-submitted", autosubmitted)
- end
- @mail = mock(TMail::Mail)
- @mail.stub!(:from_addrs).and_return( [ @address ] )
- @mail.stub!(:[]).with("return-path").and_return(@return_path)
- @mail.stub!(:[]).with("auto-submitted").and_return(@autosubmitted)
-
+ def test_email(result, email, empty_return_path, autosubmitted = nil)
+ @mail = mock('mail')
+ MailHandler.stub!(:get_from_address).and_return(email)
+ MailHandler.stub!(:empty_return_path?).with(@mail).and_return(empty_return_path)
+ MailHandler.stub!(:get_auto_submitted).with(@mail).and_return(autosubmitted)
@incoming_message = IncomingMessage.new()
@incoming_message.stub!(:mail).and_return(@mail)
@incoming_message._calculate_valid_to_reply_to.should == result
end
it "says a valid email is fine" do
- test_email(true, "team@mysociety.org", nil)
+ test_email(true, "team@mysociety.org", false)
end
it "says postmaster email is bad" do
- test_email(false, "postmaster@mysociety.org", nil)
+ test_email(false, "postmaster@mysociety.org", false)
end
it "says Mailer-Daemon email is bad" do
- test_email(false, "Mailer-Daemon@mysociety.org", nil)
+ test_email(false, "Mailer-Daemon@mysociety.org", false)
end
it "says case mangled MaIler-DaemOn email is bad" do
- test_email(false, "MaIler-DaemOn@mysociety.org", nil)
+ test_email(false, "MaIler-DaemOn@mysociety.org", false)
end
it "says Auto_Reply email is bad" do
- test_email(false, "Auto_Reply@mysociety.org", nil)
+ test_email(false, "Auto_Reply@mysociety.org", false)
end
it "says DoNotReply email is bad" do
- test_email(false, "DoNotReply@tube.tfl.gov.uk", nil)
+ test_email(false, "DoNotReply@tube.tfl.gov.uk", false)
end
it "says a filled-out return-path is fine" do
- test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>")
+ test_email(true, "team@mysociety.org", false)
end
it "says an empty return-path is bad" do
- test_email(false, "team@mysociety.org", "<>")
+ test_email(false, "team@mysociety.org", true)
end
it "says an auto-submitted keyword is bad" do
- test_email(false, "team@mysociety.org", nil, "auto-replied")
+ test_email(false, "team@mysociety.org", false, "auto-replied")
end
end
@@ -340,12 +313,12 @@ describe IncomingMessage, " when censoring data" do
orig_pdf = load_file_fixture('tfl.pdf')
pdf = orig_pdf.dup
- orig_text = IncomingMessage._get_attachment_text_internal_one_file('application/pdf', pdf)
+ orig_text = MailHandler.get_attachment_text_one_file('application/pdf', pdf)
orig_text.should match(/foi@tfl.gov.uk/)
@im.binary_mask_stuff!(pdf, "application/pdf")
- masked_text = IncomingMessage._get_attachment_text_internal_one_file('application/pdf', pdf)
+ masked_text = MailHandler.get_attachment_text_one_file('application/pdf', pdf)
masked_text.should_not match(/foi@tfl.gov.uk/)
masked_text.should match(/xxx@xxx.xxx.xx/)
config['USE_GHOSTSCRIPT_COMPRESSION'] = previous
@@ -455,6 +428,24 @@ end
describe IncomingMessage, "when messages are attached to messages" do
+ it 'should expand an RFC822 attachment' do
+ mail_body = load_file_fixture('rfc822-attachment.email')
+ mail = MailHandler.mail_from_raw_email(mail_body)
+
+ im = incoming_messages(:useless_incoming_message)
+ im.stub!(:mail).and_return(mail)
+
+ attachments = im.get_attachments_for_display
+ attachments.size.should == 1
+ attachment = attachments.first
+
+ attachment.content_type.should == 'text/plain'
+ attachment.filename.should == "Freedom of Information request.txt"
+ attachment.charset.should == "utf-8"
+ attachment.within_rfc822_subject.should == "Freedom of Information request"
+ attachment.hexdigest.should == 'f10fe56e4f2287685a58b71329f09639'
+ end
+
it "should flatten all the attachments out" do
mail = get_fixture_mail('incoming-request-attach-attachments.email')
@@ -470,6 +461,19 @@ describe IncomingMessage, "when messages are attached to messages" do
'hello.txt',
]
end
+
+ it 'should add headers to attached plain text message bodies' do
+ mail_body = load_file_fixture('incoming-request-attachment-headers.email')
+ mail = MailHandler.mail_from_raw_email(mail_body)
+
+ im = incoming_messages(:useless_incoming_message)
+ im.stub!(:mail).and_return(mail)
+
+ attachments = im.get_attachments_for_display
+ attachments.size.should == 2
+ attachments[0].body.should match('Date: Fri, 23 May 2008')
+ end
+
end
describe IncomingMessage, "when Outlook messages are attached to messages" do
diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb
index 2aeac2fec..544852f91 100644
--- a/spec/models/info_request_spec.rb
+++ b/spec/models/info_request_spec.rb
@@ -2,6 +2,22 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe InfoRequest do
+ describe 'when generating a user name slug' do
+
+ before do
+ @public_body = mock_model(PublicBody, :url_name => 'example_body',
+ :eir_only? => false)
+ @info_request = InfoRequest.new(:external_url => 'http://www.example.com',
+ :external_user_name => 'Example User',
+ :public_body => @public_body)
+ end
+
+ it 'should generate a slug for an example user name' do
+ @info_request.user_name_slug.should == 'example_body_example_user'
+ end
+
+ end
+
describe "guessing a request from an email" do
before(:each) do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d4dad591d..9621211f5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -93,9 +93,14 @@ def file_fixture_name(file_name)
return File.join(Spec::Runner.configuration.fixture_path, "files", file_name)
end
-def load_file_fixture(file_name)
+def load_file_fixture(file_name, as_binary=false)
file_name = file_fixture_name(file_name)
- content = File.read(file_name)
+ content = File.open(file_name, 'r') do |file|
+ if as_binary
+ file.set_encoding(Encoding::BINARY) if file.respond_to?(:set_encoding)
+ end
+ file.read
+ end
return content
end