Class: ICFS::Email::Core

Inherits:
Object
  • Object
show all
Defined in:
lib/icfs/email/core.rb

Overview

Core email processing engine.

Constant Summary collapse

ValFile =

A file to attach

{
  method: :hash,
  required: {
    content: Validate::IsString,     # content of the file
    name: Items::FieldFilename,      # name of the file
  }.freeze
}.freeze
ValReceive =

Results of processing a valid message

{
  method: :hash,
  required: {
    orig: Validate::IsString,        # the raw message
    caseid: Items::FieldCaseid,      # case to write
    user: Items::FieldUsergrp,       # user as author
    files: {                         # files to attach
      method: :array,
      check: ValFile
    }.freeze,
  }.freeze,
  optional: {
    entry: Validate::IsIntPos,       # the entry number
    time: Validate::IsIntPos,        # the time of the entry
    title: Items::FieldTitle,        # title of the entry
    content: Items::FieldContent,    # content of the entry
    tags: {                          # tags to apply
      method: :array,
      check: Items::FieldTag
    }.freeze,
    perms: {                         # perms to apply
      method: :array,
      check: Items::FieldPermAny,
    }.freeze,
    stats: {                         # stats to apply
      method: :array,
      min: 1,
      check: {
        method: :hash,
        required: {
          "name" => Items::FieldStat,
          "value" => Validate::IsFloat,
          "credit" => {
            method: :array,
            min: 1,
            max: 32,
            check: Items::FieldUsergrp
          }.freeze
        }.freeze
      }.freeze
    }.freeze,
    save_raw: Validate::IsBoolean,   # save the raw email as a File
    save_msg: Validate::IsBoolean,   # save the processed message w/o attach
  }.freeze,
  others: true,
}.freeze
DefaultTitle =

Default title

'Email gateway default title'
DefaultContent =

Default content

'Entry generated via email gateway with no content.'
DefaultOrig =

Filename for original content

'email_received.eml'
DefaultEmail =

Filename for processed content without attachments

'email.eml'
CopyFields =

Basic header fields to copy

[
  "to",
  "cc",
  "message-id",
  "in-reply-to",
  "references",
  "subject",
  "comments",
  "keywords",
  "date",
  "from",
  "sender",
  "reply-to",
].freeze
ContentFields =

Content related fields

[
  "content-transfer-encoding",
  "content-description",
  "content-disposition",
  "content-type",
  "content-id",
  "content-location",
].freeze
FieldsSet =

Set of header fields to copy set

Set.new(CopyFields).merge(ContentFields).freeze

Instance Method Summary collapse

Constructor Details

#initialize(api, log, st = nil) ⇒ Core

New instance

Parameters:

  • api (ICFS::Api)

    the ICFS API

  • log (Logger)

    The log

  • st (Array) (defaults to: nil)

    the middleware



119
120
121
122
123
# File 'lib/icfs/email/core.rb', line 119

def initialize(api, log, st=nil)
  @api = api
  @log = log
  self.stack_set(st) if st
end

Instance Method Details

#receive(msg) ⇒ Array

Process a received email using the middleware stack

Parameters:

  • msg (::Mail::Message)

    the email message

Returns:

  • (Array)

    results, first field is a Symbol, second field is error or the recorded message



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/icfs/email/core.rb', line 146

def receive(msg)
  @log.debug('Email: Processing %s' % msg.message_id)

  # setup the environment
  env = {
    orig: msg.raw_source.dup, # the original text email
    msg: msg,                 # the email message being worked on
    files: [],                # files to attach to the entry
    api: @api,                # the ICFS API
  }

  # process all middleware
  @stack.each do |mid|
    resp, err = mid.receive(env)
    case resp
    when :continue
      next
    when :stop
      break
    when :failure
      return [:failure, err]
    else
      raise ScriptError
    end
  end

  # check that all required fields were completed
  err = Validate.check(env, ValReceive)
  if err
    @log.info('Email: Invalid: %s' % err.inspect)
    return [:invalid, err]
  end

  # API set to active user
  @api.user = env[:user]

  # if an entry was specified
  if env[:entry] && env[:entry] != 0
    ent = @api.entry_read(env[:caseid], env[:entry])
    ent.delete('icfs')
    ent.delete('log')
    ent.delete('user')
    ent.delete('tags') if ent['tags'][0] == ICFS::TagNone
  else
    ent = {}
    ent['caseid'] = env[:caseid]
  end

  # build entry
  ent['time'] = env[:time] if env[:time]
  ent['title'] = env[:title] if env[:title]
  ent['title'] ||= DefaultTitle
  ent['content'] = env[:content] if env[:content]
  ent['content'] ||= DefaultContent
  if env[:tags]
    ent['tags'] ||= []
    ent['tags'] = (ent['tags'] + env[:tags]).uniq
  end
  ent['perms'] = env[:perms].uniq if env[:perms]
  ent['stats'] = env[:stats] if env[:stats]

  # files
  files = env[:files].map do |fd|
    tmp = @api.tempfile
    tmp.write(fd[:content])
    { 'name' => fd[:name], 'temp' => tmp }
  end
  if env[:save_original]
    tmp = @api.tempfile
    tmp.write(env[:orig])
    files << { 'name' => DefaultOrig, 'temp' => tmp }
  end
  if env[:save_email]
    tmp = @api.tempfile
    env[:msg].header.fields.delete_if do |fi|
      !FieldsSet.include?(fi.name.downcase)
    end
    tmp.write(env[:msg].encoded)
    files << { 'name' => DefaultEmail, 'temp' => tmp }
  end
  unless files.empty?
    ent['files'] ||= []
    ent['files'] = ent['files'] + files
  end

  # try to record it
  @api.record(ent, nil, nil, nil)

  @log.info('Email: Success: %s %d-%d' %
      [ent['caseid'], ent['entry'], ent['log']])
  return [:success, ent]

rescue ICFS::Error::Conflict => ex
  @log.warn('Email: Conflict: %s' % ex.message)
  return [:conflict, ex.message]
rescue ICFS::Error::NotFound => ex
  @log.warn('Email: Not Found: %s' % ex.message)
  return [:notfound, ex.message]
rescue ICFS::Error::Perms => ex
  @log.warn('Email: Permissions: %s' % ex.message)
  return [:perms, ex.message]
rescue ICFS::Error::Value => ex
  @log.warn('Email: Value: %s' % ex.message)
  return [:value, ex.message]
end

#stack_set(st) ⇒ Object

Set the middleware stack

Each middleware object must respond to #receive and return one of:

* :continue - process more middleware
* :success - stops further middleare, and records the entry
* :failure - stops further middlware and does not record


134
135
136
# File 'lib/icfs/email/core.rb', line 134

def stack_set(st)
  @stack = st
end