class RIMS::Protocol::UserMailboxDecoder::Engine

Attributes

mail_store[R]
unique_user_id[R]

Public Class Methods

new(unique_user_id, mail_store, logger, bulk_response_count: 100, bulk_response_size: 1024**2 * 10, read_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS, write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS, cleanup_write_lock_timeout_seconds: 1, charset_aliases: RFC822::DEFAULT_CHARSET_ALIASES, charset_convert_options: nil) click to toggle source
# File lib/rims/protocol/decoder.rb, line 756
def initialize(unique_user_id, mail_store, logger,
               bulk_response_count: 100,
               bulk_response_size: 1024**2 * 10,
               read_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
               write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
               cleanup_write_lock_timeout_seconds: 1,
               charset_aliases: RFC822::DEFAULT_CHARSET_ALIASES,
               charset_convert_options: nil)
  @unique_user_id = unique_user_id
  @mail_store = mail_store
  @logger = logger
  @bulk_response_count = bulk_response_count
  @bulk_response_size = bulk_response_size
  @read_lock_timeout_seconds = read_lock_timeout_seconds
  @write_lock_timeout_seconds = write_lock_timeout_seconds
  @cleanup_write_lock_timeout_seconds = cleanup_write_lock_timeout_seconds
  @charset_aliases = charset_aliases
  @charset_convert_options = charset_convert_options
  @folders = {}
  @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#initialize at #{self}") if @logger.debug?
end

Private Class Methods

imap_command_authenticated(name, **guard_optional) click to toggle source
# File lib/rims/protocol/decoder.rb, line 905
          def imap_command_authenticated(name, **guard_optional)
            name = name.to_sym
            orig_name = "_#{name}".to_sym
            alias_method orig_name, name

            guard_options_name = "_#{name}_guard_options".to_sym
            define_method guard_options_name, lambda{ guard_optional }
            private guard_options_name

            class_eval(<<-EOF, __FILE__, __LINE__ + 1)
              def #{name}(token, tag, *args, **kw_args, &block)
                guard_authenticated(:#{orig_name}, token, tag, *args, **kw_args, **#{guard_options_name}, &block)
              end
            EOF

            name
          end
imap_command_selected(name, **guard_optional) click to toggle source
# File lib/rims/protocol/decoder.rb, line 924
          def imap_command_selected(name, **guard_optional)
            name = name.to_sym
            orig_name = "_#{name}".to_sym
            alias_method orig_name, name

            guard_options_name = "_#{name}_guard_options".to_sym
            define_method guard_options_name, lambda{ guard_optional }
            private guard_options_name

            class_eval(<<-EOF, __FILE__, __LINE__ + 1)
              def #{name}(token, tag, *args, **kw_args, &block)
                guard_selected(:#{orig_name}, token, tag, *args, **kw_args, **#{guard_options_name}, &block)
              end
            EOF

            name
          end

Public Instance Methods

append(token, tag, mbox_name, *opt_args, msg_text) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1270
def append(token, tag, mbox_name, *opt_args, msg_text)
  res = new_bulk_response
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (mbox_id = @mail_store.mbox_id(mbox_name_utf8)) then
    msg_flags = []
    msg_date = Time.now

    if ((! opt_args.empty?) && (opt_args[0].is_a? Array)) then
      opt_flags = opt_args.shift
      if (opt_flags[0] != :group) then
        raise SyntaxError, 'bad flag list.'
      end
      for flag_atom in opt_flags[1..-1]
        case (flag_atom.upcase)
        when '\ANSWERED'
          msg_flags << 'answered'
        when '\FLAGGED'
          msg_flags << 'flagged'
        when '\DELETED'
          msg_flags << 'deleted'
        when '\SEEN'
          msg_flags << 'seen'
        when '\DRAFT'
          msg_flags << 'draft'
        else
          raise SyntaxError, "invalid flag: #{flag_atom}"
        end
      end
    end

    if ((! opt_args.empty?) && (opt_args[0].is_a? String)) then
      begin
        msg_date = Time.parse(opt_args.shift)
      rescue ArgumentError
        raise SyntaxError, $!.message
      end
    end

    unless (opt_args.empty?) then
      raise SyntaxError, "unknown option: #{opt_args.inspect}"
    end

    uid = @mail_store.add_msg(mbox_id, msg_text, msg_date)
    for flag_name in msg_flags
      @mail_store.set_msg_flag(mbox_id, uid, flag_name, true)
    end

    mailbox_size_server_response_multicast_push(mbox_id)
    if (token) then
      folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
      folder.server_response_fetch{|untagged_response|
        res << untagged_response
        yield(res.flush) if res.full?
      }
    end

    res << "#{tag} OK [APPENDUID #{mbox_id} #{uid}] APPEND completed\r\n"
  else
    if (token) then
      folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
      folder.server_response_fetch{|untagged_response|
        res << untagged_response
        yield(res.flush) if res.full?
      }
    end
    res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
  end
  yield(res.flush)
end
check(token, tag) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1341
def check(token, tag)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  @mail_store.sync
  res << "#{tag} OK CHECK completed\r\n"
  yield(res.flush)
end
cleanup(token) click to toggle source
# File lib/rims/protocol/decoder.rb, line 834
def cleanup(token)
  if (token) then
    begin
      @mail_store.write_synchronize(@cleanup_write_lock_timeout_seconds) {
        folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
        close_folder(token) do |untagged_response|
          folder.server_response_multicast_push(untagged_response)
        end
      }
    rescue WriteLockTimeoutError
      @logger.warn("give up to close folder becaue of write-lock timeout over #{@write_lock_timeout_seconds} seconds")
      @folders.delete(token)
    end
  end

  nil
end
close(token, tag) { |"#{tag} OK CLOSE completed\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1368
def close(token, tag)
  close_no_response(token)
  yield([ "#{tag} OK CLOSE completed\r\n" ])
end
copy(token, tag, msg_set, mbox_name, uid: false) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1630
def copy(token, tag, msg_set, mbox_name, uid: false)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.should_be_alive
  folder.reload if folder.updated?

  res = new_bulk_response
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  msg_set = folder.parse_msg_set(msg_set, uid: uid)

  if (mbox_id = @mail_store.mbox_id(mbox_name_utf8)) then
    msg_list = folder.msg_find_all(msg_set, uid: uid)

    src_uids = []
    dst_uids = []
    for msg in msg_list
      src_uids << msg.uid
      dst_uids << @mail_store.copy_msg(msg.uid, folder.mbox_id, mbox_id)
    end

    if (msg_list.size > 0) then
      mailbox_size_server_response_multicast_push(mbox_id)
      folder.server_response_fetch{|untagged_response|
        res << untagged_response
        yield(res.flush) if res.full?
      }
      res << "#{tag} OK [COPYUID #{mbox_id} #{src_uids.join(',')} #{dst_uids.join(',')}] COPY completed\r\n"
    else
      folder.server_response_fetch{|untagged_response|
        res << untagged_response
        yield(res.flush) if res.full?
      }
      res << "#{tag} OK COPY completed\r\n"
    end
  else
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
    res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
  end
  yield(res.flush)
end
create(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1032
def create(token, tag, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (@mail_store.mbox_id(mbox_name_utf8)) then
    res << "#{tag} NO duplicated mailbox\r\n"
  else
    @mail_store.add_mbox(mbox_name_utf8)
    res << "#{tag} OK CREATE completed\r\n"
  end
  yield(res.flush)
end
delete(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1052
def delete(token, tag, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
    if (id != @mail_store.mbox_id('INBOX')) then
      @mail_store.del_mbox(id)
      res << "#{tag} OK DELETE completed\r\n"
    else
      res << "#{tag} NO not delete inbox\r\n"
    end
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)
end
destroy() click to toggle source
# File lib/rims/protocol/decoder.rb, line 852
def destroy
  @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#destroy at #{self}") if @logger.debug?
  tmp_mail_store = @mail_store
  ReadWriteLock.write_lock_timeout_detach(@cleanup_write_lock_timeout_seconds, @write_lock_timeout_seconds, logger: @logger) {|timeout_seconds|
    tmp_mail_store.write_synchronize(timeout_seconds) {
      @logger.info("close mail store: #{@unique_user_id}")
      tmp_mail_store.close
    }
  }
  @mail_store = nil

  nil
end
examine(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1007
def examine(token, tag, mbox_name)
  if (token) then
    close_no_response(token)
  end

  res = new_bulk_response
  new_token = nil
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)

  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
    new_token = open_folder(id, read_only: true)
    folder_open_msgs(new_token) {|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
    res << "#{tag} OK [READ-ONLY] EXAMINE completed\r\n"
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)

  new_token
end
expunge(token, tag) { |"#{tag} NO cannot expunge in read-only mode\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1374
def expunge(token, tag)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.should_be_alive
  return yield([ "#{tag} NO cannot expunge in read-only mode\r\n" ]) if folder.read_only?
  folder.reload if folder.updated?

  res = new_bulk_response
  folder.server_response_fetch{|untagged_response|
    res << untagged_response
    yield(res.flush) if res.full?
 }

  folder.expunge_mbox do |msg_num|
    untagged_response = "* #{msg_num} EXPUNGE\r\n"
    res << untagged_response
    yield(res.flush) if res.full?
    folder.server_response_multicast_push(untagged_response)
  end

  res << "#{tag} OK EXPUNGE completed\r\n"
  yield(res.flush)
end
fetch(token, tag, msg_set, data_item_group, uid: false) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1478
def fetch(token, tag, msg_set, data_item_group, uid: false)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.should_be_alive
  folder.reload if folder.updated?

  msg_set = folder.parse_msg_set(msg_set, uid: uid)
  msg_list = folder.msg_find_all(msg_set, uid: uid)

  unless ((data_item_group.is_a? Array) && data_item_group[0] == :group) then
    data_item_group = [ :group, data_item_group ]
  end
  if (uid) then
    unless (data_item_group.find{|i| (i.is_a? String) && (i.upcase == 'UID') }) then
      data_item_group = [ :group, 'UID' ] + data_item_group[1..-1]
    end
  end

  parser = FetchParser.new(@mail_store, folder, charset_aliases: @charset_aliases)
  fetch = parser.parse(data_item_group)

  res = new_bulk_response
  folder.server_response_fetch{|untagged_response|
    res << untagged_response
    yield(res.flush) if res.full?
  }

  for msg in msg_list
    res << ('* '.b << msg.num.to_s.b << ' FETCH '.b << fetch.call(msg) << "\r\n".b)
    yield(res.flush) if res.full?
  end

  res << "#{tag} OK FETCH completed\r\n"
  yield(res.flush)
end
idle(token, tag, client_input_gets, server_output_write, connection_timer) { |last_res| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1674
def idle(token, tag, client_input_gets, server_output_write, connection_timer)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.should_be_alive

  @logger.info('idle start...')
  server_output_write.call([ "+ continue\r\n" ])

  server_response_thread = Thread.new{
    res = new_bulk_response
    @logger.info('idle server response thread start...')
    folder.server_response_idle_wait{|server_response_list|
      for untagged_response in server_response_list
        @logger.debug("idle server response: #{untagged_response}") if @logger.debug?
        res << untagged_response
        server_output_write.call(res.flush) if res.full?
      end
      server_output_write.call(res.flush) unless res.empty?
    }
    @logger.info('idle server response thread terminated.')
  }

  begin
    connection_timer.command_wait or return
    line = client_input_gets.call
  ensure
    folder.server_response_idle_interrupt
    server_response_thread.join
  end

  last_res = []
  if (line) then
    line.chomp!("\n")
    line.chomp!("\r")
    if (line.upcase == "DONE") then
      @logger.info('idle terminated.')
      last_res << "#{tag} OK IDLE terminated\r\n"
    else
      @logger.warn('unexpected client response and idle terminated.')
      @logger.debug("unexpected client response data: #{line}") if @logger.debug?
      last_res << "#{tag} BAD unexpected client response\r\n"
    end
  else
    @logger.warn('unexpected client connection close and idle terminated.')
    last_res << "#{tag} BAD unexpected client connection close\r\n"
  end
  yield(last_res)
end
list(token, tag, ref_name, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1167
def list(token, tag, ref_name, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  if (mbox_name.empty?) then
    res << "* LIST (\\Noselect) NIL \"\"\r\n"
  else
    list_mbox(ref_name, mbox_name) {|mbox_entry|
      res << "* LIST #{mbox_entry}\r\n"
      yield(res.flush) if res.full?
    }
  end
  res << "#{tag} OK LIST completed\r\n"
  yield(res.flush)
end
lsub(token, tag, ref_name, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1189
def lsub(token, tag, ref_name, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  if (mbox_name.empty?) then
    res << "* LSUB (\\Noselect) NIL \"\"\r\n"
  else
    list_mbox(ref_name, mbox_name) {|mbox_entry|
      res << "* LSUB #{mbox_entry}\r\n"
      yield(res.flush) if res.full?
    }
  end
  res << "#{tag} OK LSUB completed\r\n"
  yield(res.flush)
end
noop(token, tag) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 949
def noop(token, tag)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    begin
      @mail_store.read_synchronize(@read_lock_timeout_seconds) {
        folder.server_response_fetch{|untagged_response|
          res << untagged_response
          yield(res.flush) if res.full?
        }
      }
    rescue ReadLockTimeoutError
      @logger.warn("give up to get folder status because of read-lock timeout over #{@read_lock_timeout_seconds} seconds")
    end
  end
  res << "#{tag} OK NOOP completed\r\n"
  yield(res.flush)
end
recovery_if_needed(username) { |"* OK [ALERT] start user data recovery.\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 781
def recovery_if_needed(username)
  @mail_store.write_synchronize(@write_lock_timeout_seconds) {
    if (@mail_store.abort_transaction?) then
      @logger.warn("user data recovery start: #{username}")
      yield("* OK [ALERT] start user data recovery.\r\n")
      @mail_store.recovery_data(logger: @logger).sync
      @logger.warn("user data recovery end: #{username}")
      yield("* OK completed user data recovery.\r\n")

      self
    end
  }
end
rename(token, tag, src_name, dst_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1076
def rename(token, tag, src_name, dst_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  src_name_utf8 = Net::IMAP.decode_utf7(src_name)
  dst_name_utf8 = Net::IMAP.decode_utf7(dst_name)
  unless (id = @mail_store.mbox_id(src_name_utf8)) then
    res << "#{tag} NO not found a mailbox\r\n"
    return yield(res.flush)
  end
  if (id == @mail_store.mbox_id('INBOX')) then
    res << "#{tag} NO not rename inbox\r\n"
    return yield(res.flush)
  end
  if (@mail_store.mbox_id(dst_name_utf8)) then
    res << "#{tag} NO duplicated mailbox\r\n"
    return yield(res.flush)
  end
  @mail_store.rename_mbox(id, dst_name_utf8)
  res << "#{tag} OK RENAME completed\r\n"
  yield(res.flush)
end
select(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 982
def select(token, tag, mbox_name)
  if (token) then
    close_no_response(token)
  end

  res = new_bulk_response
  new_token = nil
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)

  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
    new_token = open_folder(id)
    folder_open_msgs(new_token) {|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
    res << "#{tag} OK [READ-WRITE] SELECT completed\r\n"
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)

  new_token
end
status(token, tag, mbox_name, data_item_group) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1211
def status(token, tag, mbox_name, data_item_group)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
    unless ((data_item_group.is_a? Array) && (data_item_group[0] == :group)) then
      raise SyntaxError, 'second arugment is not a group list.'
    end

    values = []
    for item in data_item_group[1..-1]
      case (item.upcase)
      when 'MESSAGES'
        values << 'MESSAGES' << @mail_store.mbox_msg_num(id)
      when 'RECENT'
        values << 'RECENT' << @mail_store.mbox_flag_num(id, 'recent')
      when 'UIDNEXT'
        values << 'UIDNEXT' << @mail_store.uid(id)
      when 'UIDVALIDITY'
        values << 'UIDVALIDITY' << id
      when 'UNSEEN'
        unseen_flags = @mail_store.mbox_msg_num(id) - @mail_store.mbox_flag_num(id, 'seen')
        values << 'UNSEEN' << unseen_flags
      else
        raise SyntaxError, "unknown status data: #{item}"
      end
    end

    res << "* STATUS #{Protocol.quote(mbox_name)} (#{values.join(' ')})\r\n"
    res << "#{tag} OK STATUS completed\r\n"
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)
end
store(token, tag, msg_set, data_item_name, data_item_value, uid: false) { |"#{tag} NO cannot store in read-only mode\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1514
def store(token, tag, msg_set, data_item_name, data_item_value, uid: false)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.should_be_alive
  return yield([ "#{tag} NO cannot store in read-only mode\r\n" ]) if folder.read_only?
  folder.reload if folder.updated?

  msg_set = folder.parse_msg_set(msg_set, uid: uid)
  name, option = data_item_name.split(/\./, 2)

  case (name.upcase)
  when 'FLAGS'
    action = :flags_replace
  when '+FLAGS'
    action = :flags_add
  when '-FLAGS'
    action = :flags_del
  else
    raise SyntaxError, "unknown store action: #{name}"
  end

  case (option && option.upcase)
  when 'SILENT'
    is_silent = true
  when nil
    is_silent = false
  else
    raise SyntaxError, "unknown store option: #{option.inspect}"
  end

  if ((data_item_value.is_a? Array) && data_item_value[0] == :group) then
    flag_list = []
    for flag_atom in data_item_value[1..-1]
      case (flag_atom.upcase)
      when '\ANSWERED'
        flag_list << 'answered'
      when '\FLAGGED'
        flag_list << 'flagged'
      when '\DELETED'
        flag_list << 'deleted'
      when '\SEEN'
        flag_list << 'seen'
      when '\DRAFT'
        flag_list << 'draft'
      else
        raise SyntaxError, "invalid flag: #{flag_atom}"
      end
    end
    rest_flag_list = (MailStore::MSG_FLAG_NAMES - %w[ recent ]) - flag_list
  else
    raise SyntaxError, 'third arugment is not a group list.'
  end

  msg_list = folder.msg_find_all(msg_set, uid: uid)

  for msg in msg_list
    case (action)
    when :flags_replace
      for name in flag_list
        @mail_store.set_msg_flag(folder.mbox_id, msg.uid, name, true)
      end
      for name in rest_flag_list
        @mail_store.set_msg_flag(folder.mbox_id, msg.uid, name, false)
      end
    when :flags_add
      for name in flag_list
        @mail_store.set_msg_flag(folder.mbox_id, msg.uid, name, true)
      end
    when :flags_del
      for name in flag_list
        @mail_store.set_msg_flag(folder.mbox_id, msg.uid, name, false)
      end
    else
      raise "internal error: unknown action: #{action}"
    end
  end

  res = new_bulk_response
  folder.server_response_fetch{|untagged_response|
    res << untagged_response
    yield(res.flush) if res.full?
  }

  if (is_silent) then
    res << "#{tag} OK STORE completed\r\n"
    yield(res.flush)
  else
    for msg in msg_list
      flag_atom_list = nil

      if (@mail_store.msg_exist? folder.mbox_id, msg.uid) then
        flag_atom_list = []
        for name in MailStore::MSG_FLAG_NAMES
          if (@mail_store.msg_flag(folder.mbox_id, msg.uid, name)) then
            flag_atom_list << "\\#{name.capitalize}"
          end
        end
      end

      if (flag_atom_list) then
        if (uid) then
          res << "* #{msg.num} FETCH (UID #{msg.uid} FLAGS (#{flag_atom_list.join(' ')}))\r\n"
        else
          res << "* #{msg.num} FETCH (FLAGS (#{flag_atom_list.join(' ')}))\r\n"
        end
        yield(res.flush) if res.full?
      else
        @logger.warn("not found a message and skipped: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
      end
    end

    res << "#{tag} OK STORE completed\r\n"
    yield(res.flush)
  end
end
subscribe(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1105
def subscribe(token, tag, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (@mail_store.mbox_id(mbox_name_utf8)) then
    res << "#{tag} OK SUBSCRIBE completed\r\n"
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)
end
unsubscribe(token, tag, mbox_name) { |flush| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1124
def unsubscribe(token, tag, mbox_name)
  res = new_bulk_response
  if (token) then
    folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
    folder.server_response_fetch{|untagged_response|
      res << untagged_response
      yield(res.flush) if res.full?
    }
  end
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
  if (@mail_store.mbox_id(mbox_name_utf8)) then
    res << "#{tag} NO not implemented subscribe/unsbscribe command\r\n"
  else
    res << "#{tag} NO not found a mailbox\r\n"
  end
  yield(res.flush)
end

Private Instance Methods

close_folder(token) { |"* #{msg_num} EXPUNGE\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 808
def close_folder(token)
  @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#close_folder: #{token}") if @logger.debug?
  folder = @folders.delete(token) or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  folder.reload if folder.updated?
  begin
    if (block_given?) then
      saved_recent_msgs = @mail_store.mbox_flag_num(folder.mbox_id, 'recent')
      folder.close do |msg_num|
        yield("* #{msg_num} EXPUNGE\r\n")
      end
      last_recent_msgs = @mail_store.mbox_flag_num(folder.mbox_id, 'recent')
      if (last_recent_msgs != saved_recent_msgs) then
        yield("* #{last_recent_msgs} RECENT\r\n")
      end
    else
      folder.close
    end
  ensure
    folder.detach
  end
  @mail_store.sync

  nil
end
close_no_response(token) click to toggle source
# File lib/rims/protocol/decoder.rb, line 1356
def close_no_response(token)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  close_folder(token) do |untagged_response|
    # IMAP CLOSE command may not send untagged EXPUNGE
    # responses, but notifies other connections of them.
    folder.server_response_multicast_push(untagged_response)
  end

  nil
end
folder_open_msgs(token) { |"* #{all_msgs} EXISTS\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 968
def folder_open_msgs(token)
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
  all_msgs = @mail_store.mbox_msg_num(folder.mbox_id)
  recent_msgs = @mail_store.mbox_flag_num(folder.mbox_id, 'recent')
  unseen_msgs = all_msgs - @mail_store.mbox_flag_num(folder.mbox_id, 'seen')
  yield("* #{all_msgs} EXISTS\r\n")
  yield("* #{recent_msgs} RECENT\r\n")
  yield("* OK [UNSEEN #{unseen_msgs}]\r\n")
  yield("* OK [UIDVALIDITY #{folder.mbox_id}]\r\n")
  yield("* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n")
  nil
end
guard_authenticated(imap_command, token, tag, *args, exclusive: false, **kw_args) { |"#{tag} BAD write-lock timeout over #{write_lock_timeout_seconds} seconds"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 866
def guard_authenticated(imap_command, token, tag, *args, exclusive: false, **kw_args, &block)
  if (exclusive.nil?) then
    if (kw_args.empty?) then
      __send__(imap_command, token, tag, *args, &block)
    else
      __send__(imap_command, token, tag, *args, **kw_args, &block)
    end
  else
    begin
      if (exclusive) then
        @mail_store.write_synchronize(@write_lock_timeout_seconds) {
          guard_authenticated(imap_command, token, tag, *args, exclusive: nil, **kw_args, &block)
        }
      else
        @mail_store.read_synchronize(@read_lock_timeout_seconds){
          guard_authenticated(imap_command, token, tag, *args, exclusive: nil, **kw_args, &block)
        }
      end
    rescue ReadLockTimeoutError
      @logger.error("write-lock timeout over #{@write_lock_timeout_seconds} seconds")
      yield([ "#{tag} BAD write-lock timeout over #{@write_lock_timeout_seconds} seconds" ])
    rescue WriteLockTimeoutError
      @logger.error("read-lock timeout over #{@read_lock_timeout_seconds} seconds")
      yield([ "#{tag} BAD read-lock timeout over #{@read_lock_timeout_seconds} seconds" ])
    end
  end
end
guard_selected(imap_command, token, tag, *args, **kw_args) { |"#{tag} NO not selected\r\n"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 895
def guard_selected(imap_command, token, tag, *args, **kw_args, &block)
  if (token) then
    guard_authenticated(imap_command, token, tag, *args, **kw_args, &block)
  else
    yield([ "#{tag} NO not selected\r\n" ])
  end
end
list_mbox(ref_name, mbox_name) { |"(#{attrs}) NIL #{quote}"| ... } click to toggle source
# File lib/rims/protocol/decoder.rb, line 1143
def list_mbox(ref_name, mbox_name)
  ref_name_utf8 = Net::IMAP.decode_utf7(ref_name)
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)

  mbox_filter = Protocol.compile_wildcard(mbox_name_utf8)
  mbox_list = @mail_store.each_mbox_id.map{|id| [ id, @mail_store.mbox_name(id) ] }
  mbox_list.keep_if{|id, name| name.start_with? ref_name_utf8 }
  mbox_list.keep_if{|id, name| name[(ref_name_utf8.length)..-1] =~ mbox_filter }

  for id, name_utf8 in mbox_list
    name = Net::IMAP.encode_utf7(name_utf8)
    attrs = '\Noinferiors'
    if (@mail_store.mbox_flag_num(id, 'recent') > 0) then
      attrs << ' \Marked'
    else
      attrs << ' \Unmarked'
    end
    yield("(#{attrs}) NIL #{Protocol.quote(name)}")
  end

  nil
end
mailbox_size_server_response_multicast_push(mbox_id) click to toggle source
# File lib/rims/protocol/decoder.rb, line 1254
def mailbox_size_server_response_multicast_push(mbox_id)
  all_msgs = @mail_store.mbox_msg_num(mbox_id)
  recent_msgs = @mail_store.mbox_flag_num(mbox_id, 'recent')

  f = @mail_store.open_folder(mbox_id, read_only: true)
  begin
    f.server_response_multicast_push("* #{all_msgs} EXISTS\r\n")
    f.server_response_multicast_push("* #{recent_msgs} RECENT\r\n")
  ensure
    f.close
  end

  nil
end
new_bulk_response() click to toggle source
# File lib/rims/protocol/decoder.rb, line 944
def new_bulk_response
  BulkResponse.new(@bulk_response_count, @bulk_response_size)
end
open_folder(mbox_id, read_only: false) click to toggle source
# File lib/rims/protocol/decoder.rb, line 795
def open_folder(mbox_id, read_only: false)
  folder = @mail_store.open_folder(mbox_id, read_only: read_only)
  token = folder.object_id
  if (@folders.key? token) then
    raise "internal error: duplicated folder token: #{token}"
  end
  @folders[token] = folder

  @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#open_folder: #{token}") if @logger.debug?
  token
end