class RIMS::MailStore

Constants

MSG_FLAG_NAMES

Public Class Methods

build(unique_user_id, kvs_meta_open, kvs_text_open) click to toggle source
# File lib/rims/mail_store.rb, line 34
def self.build(unique_user_id, kvs_meta_open, kvs_text_open)
  kvs_build = proc{|kvs_open, db_name|
    kvs_open.call(MAILBOX_DATA_STRUCTURE_VERSION, unique_user_id, db_name)
  }

  mail_store = MailStore.new(DB::Meta.new(kvs_build.call(kvs_meta_open, 'meta')),
                             DB::Message.new(kvs_build.call(kvs_text_open, 'message'))) {|mbox_id|
    DB::Mailbox.new(kvs_build.call(kvs_meta_open, "mailbox_#{mbox_id}"))
  }
  mail_store.add_mbox('INBOX') unless mail_store.mbox_id('INBOX')

  mail_store
end
new(meta_db, msg_db) { |mbox_id| ... } click to toggle source
# File lib/rims/mail_store.rb, line 12
def initialize(meta_db, msg_db, &mbox_db_factory) # :yields: mbox_id
  @rw_lock = ReadWriteLock.new

  @meta_db = meta_db
  @msg_db = msg_db
  @mbox_db_factory = mbox_db_factory

  @mbox_db = {}
  @meta_db.each_mbox_id do |mbox_id|
    @mbox_db[mbox_id] = nil
  end

  if (@meta_db.dirty?) then
    @abort_transaction = true
  else
    @abort_transaction = false
    @meta_db.dirty = true
  end

  @channel = ServerResponseChannel.new
end

Public Instance Methods

abort_transaction?() click to toggle source
# File lib/rims/mail_store.rb, line 57
def abort_transaction?
  @abort_transaction
end
add_mbox(name) click to toggle source
# File lib/rims/mail_store.rb, line 152
def add_mbox(name)
  transaction{
    name = 'INBOX' if (name =~ /\A INBOX \z/xi)
    name = name.b

    mbox_id = @meta_db.add_mbox(name)
    @mbox_db[mbox_id] = nil

    @meta_db.cnum_succ!

    mbox_id
  }
end
add_msg(mbox_id, msg_text, msg_date=Time.now) click to toggle source
# File lib/rims/mail_store.rb, line 235
def add_msg(mbox_id, msg_text, msg_date=Time.now)
  transaction{
    mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."

    msg_id = @meta_db.msg_id_succ!
    @msg_db.add_msg(msg_id, msg_text)
    @meta_db.set_msg_date(msg_id, msg_date)
    @meta_db.set_msg_flag(msg_id, 'recent', true)

    uid = @meta_db.add_msg_mbox_uid(msg_id, mbox_id)
    mbox_db.add_msg(uid, msg_id)

    @meta_db.cnum_succ!

    uid
  }
end
close() click to toggle source
# File lib/rims/mail_store.rb, line 119
def close
  @mbox_db.each_value do |db|
    db.close if db
  end
  @msg_db.close
  @meta_db.dirty = false unless @abort_transaction
  @meta_db.close
  self
end
cnum() click to toggle source
# File lib/rims/mail_store.rb, line 140
def cnum
  @meta_db.cnum
end
copy_msg(src_uid, src_mbox_id, dst_mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 266
def copy_msg(src_uid, src_mbox_id, dst_mbox_id)
  transaction{
    src_mbox_db = get_mbox_db(src_mbox_id) or raise "not found a source mailbox: #{src_mbox_id}"
    dst_mbox_db = get_mbox_db(dst_mbox_id) or raise "not found a destination mailbox: #{dst_mbox_id}"

    msg_id = src_mbox_db.msg_id(src_uid) or raise "not found a message: #{src_mbox_id},#{src_uid}"
    dst_uid = @meta_db.add_msg_mbox_uid(msg_id, dst_mbox_id)
    dst_mbox_db.add_msg(dst_uid, msg_id)

    @meta_db.cnum_succ!

    dst_uid
  }
end
del_mbox(mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 166
def del_mbox(mbox_id)
  transaction{
    mbox_name = @meta_db.mbox_name(mbox_id) or raise "not found a mailbox: #{mbox_id}."

    get_mbox_db(mbox_id)
    mbox_db = @mbox_db.delete(mbox_id)
    mbox_db.each_msg_uid do |uid|
      msg_id = mbox_db.msg_id(uid)
      del_msg(msg_id, mbox_id, uid)
    end
    mbox_db.close
    mbox_db.destroy

    for name in MSG_FLAG_NAMES
      @meta_db.clear_mbox_flag_num(mbox_id, name)
    end
    @meta_db.del_mbox(mbox_id) or raise 'internal error.'

    @meta_db.cnum_succ!

    mbox_name
  }
end
each_mbox_id() { |mbox_id| ... } click to toggle source
# File lib/rims/mail_store.rb, line 215
def each_mbox_id
  return enum_for(:each_mbox_id) unless block_given?
  @meta_db.each_mbox_id do |mbox_id|
    yield(mbox_id)
  end
  self
end
each_msg_uid(mbox_id) { |uid| ... } click to toggle source
# File lib/rims/mail_store.rb, line 336
def each_msg_uid(mbox_id)
  mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
  return enum_for(:each_msg_uid, mbox_id) unless block_given?
  mbox_db.each_msg_uid do |uid|
    yield(uid)
  end
  self
end
expunge_mbox(mbox_id) { |uid| ... } click to toggle source
# File lib/rims/mail_store.rb, line 345
def expunge_mbox(mbox_id)
  transaction{
    mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."

    uid_list = mbox_db.each_msg_uid.find_all{|uid| mbox_db.msg_flag_deleted(uid) }
    msg_id_list = uid_list.map{|uid| mbox_db.msg_id(uid) }

    uid_list.zip(msg_id_list) do |uid, msg_id|
      mbox_db.expunge_msg(uid)
      del_msg(msg_id, mbox_id, uid)
      yield(uid) if block_given?
    end

    @meta_db.cnum_succ!

    self
  }
end
mbox_flag_num(mbox_id, flag_name) click to toggle source
# File lib/rims/mail_store.rb, line 227
def mbox_flag_num(mbox_id, flag_name)
  if (MSG_FLAG_NAMES.include? flag_name) then
    @meta_db.mbox_flag_num(mbox_id, flag_name)
  else
    raise "unknown flag name: #{name}"
  end
end
mbox_id(mbox_name) click to toggle source
# File lib/rims/mail_store.rb, line 210
def mbox_id(mbox_name)
  mbox_name = 'INBOX' if (mbox_name =~ /\A INBOX \z/xi)
  @meta_db.mbox_id(mbox_name.b)
end
mbox_msg_num(mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 223
def mbox_msg_num(mbox_id)
  @meta_db.mbox_msg_num(mbox_id)
end
mbox_name(mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 204
def mbox_name(mbox_id)
  if (name = @meta_db.mbox_name(mbox_id)) then
    name.dup.force_encoding('utf-8')
  end
end
msg_date(mbox_id, uid) click to toggle source
# File lib/rims/mail_store.rb, line 292
def msg_date(mbox_id, uid)
  mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
  msg_id = mbox_db.msg_id(uid) or raise "not found a message: #{mbox_id},#{uid}"
  @meta_db.msg_date(msg_id)
end
msg_exist?(mbox_id, uid) click to toggle source
# File lib/rims/mail_store.rb, line 281
def msg_exist?(mbox_id, uid)
  mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
  mbox_db.msg_exist? uid
end
msg_flag(mbox_id, uid, flag_name) click to toggle source
# File lib/rims/mail_store.rb, line 298
def msg_flag(mbox_id, uid, flag_name)
  mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."

  if ((MSG_FLAG_NAMES - %w[ deleted ]).include? flag_name) then
    msg_id = mbox_db.msg_id(uid) or raise "not found a message: #{mbox_id},#{uid}"
    @meta_db.msg_flag(msg_id, flag_name)
  elsif (flag_name == 'deleted') then
    mbox_db.msg_flag_deleted(uid)
  else
    raise "unknown flag name: #{flag_name}"
  end
end
msg_text(mbox_id, uid) click to toggle source
# File lib/rims/mail_store.rb, line 286
def msg_text(mbox_id, uid)
  mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
  msg_id = mbox_db.msg_id(uid) or raise "not found a message: #{mbox_id},#{uid}"
  @msg_db.msg_text(msg_id)
end
open_folder(mbox_id, read_only: false) click to toggle source
# File lib/rims/mail_store.rb, line 364
def open_folder(mbox_id, read_only: false)
  @meta_db.mbox_name(mbox_id) or raise "not found a mailbox: #{mbox_id}."
  MailFolder.new(mbox_id, self, read_only: read_only).attach(@channel)
end
recovery_data(logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/mail_store.rb, line 77
def recovery_data(logger: Logger.new(STDOUT))
  begin
    logger.info('test read all: meta DB')
    @meta_db.test_read_all do |error|
      logger.error("read fail: #{error}")
    end
    logger.info('test read all: msg DB')
    @msg_db.test_read_all do |error|
      logger.error("read fail: #{error}")
    end
    @mbox_db.each_key do |mbox_id|
      logger.info("test_read_all: mailbox DB #{mbox_id}")
      get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
      @mbox_db[mbox_id].test_read_all do |error|
        logger.error("read fail: #{error}")
      end
    end

    @meta_db.recovery_start
    @meta_db.recovery_phase1_msg_scan(@msg_db, logger: logger)
    @meta_db.recovery_phase2_msg_scan(@msg_db, logger: logger)
    @meta_db.recovery_phase3_mbox_scan(logger: logger)
    @meta_db.recovery_phase4_mbox_scan(logger: logger)
    @meta_db.recovery_phase5_mbox_repair(logger: logger) {|mbox_id|
      if (@mbox_db.key? mbox_id) then
        raise "not a lost mailbox: #{mbox_id}"
      else
        @mbox_db[mbox_id] = nil
        get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."
      end
    }
    @meta_db.recovery_phase6_msg_scan(@mbox_db, logger: logger)
    @meta_db.recovery_phase7_mbox_msg_scan(@mbox_db, MSG_FLAG_NAMES, logger: logger)
    @meta_db.recovery_phase8_lost_found(@mbox_db, logger: logger)
    @meta_db.recovery_end
  ensure
    @abort_transaction = ! $!.nil?
  end

  self
end
rename_mbox(mbox_id, new_name) click to toggle source
# File lib/rims/mail_store.rb, line 190
def rename_mbox(mbox_id, new_name)
  transaction{
    old_name = @meta_db.mbox_name(mbox_id) or raise "not found a mailbox: #{mbox_id}."
    old_name = old_name.dup.force_encoding('utf-8')

    new_name = 'INBOX' if (new_name =~ /\A INBOX \z/xi)
    @meta_db.rename_mbox(mbox_id, new_name.b)

    @meta_db.cnum_succ!

    old_name
  }
end
set_msg_flag(mbox_id, uid, flag_name, flag_value) click to toggle source
# File lib/rims/mail_store.rb, line 311
def set_msg_flag(mbox_id, uid, flag_name, flag_value)
  transaction{
    mbox_db = get_mbox_db(mbox_id) or raise "not found a mailbox: #{mbox_id}."

    if ((MSG_FLAG_NAMES - %w[ deleted ]).include? flag_name) then
      msg_id = mbox_db.msg_id(uid) or raise "not found a message: #{mbox_id},#{uid}"
      @meta_db.set_msg_flag(msg_id, flag_name, flag_value)
    elsif (flag_name == 'deleted') then
      prev_deleted = mbox_db.msg_flag_deleted(uid)
      mbox_db.set_msg_flag_deleted(uid, flag_value)
      if (! prev_deleted && flag_value) then
        @meta_db.mbox_flag_num_increment(mbox_id, 'deleted')
      elsif (prev_deleted && ! flag_value) then
        @meta_db.mbox_flag_num_decrement(mbox_id, 'deleted')
      end
    else
      raise "unknown flag name: #{flag_name}"
    end

    @meta_db.cnum_succ!

    self
  }
end
sync() click to toggle source
# File lib/rims/mail_store.rb, line 129
def sync
  transaction{
    @msg_db.sync
    @mbox_db.each_value do |db|
      db.sync if db
    end
    @meta_db.sync
    self
  }
end
transaction() { || ... } click to toggle source
# File lib/rims/mail_store.rb, line 61
def transaction
  if (@abort_transaction) then
    raise 'abort transaction.'
  end

  transaction_completed = false
  begin
    return_value = yield
    transaction_completed = true
  ensure
    @abort_transaction = true unless transaction_completed
  end

  return_value
end
uid(mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 144
def uid(mbox_id)
  @meta_db.mbox_uid(mbox_id)
end
uidvalidity() click to toggle source
# File lib/rims/mail_store.rb, line 148
def uidvalidity
  @meta_db.uidvalidity
end

Private Instance Methods

del_msg(msg_id, mbox_id, uid) click to toggle source
# File lib/rims/mail_store.rb, line 253
def del_msg(msg_id, mbox_id, uid)
  mbox_uid_map = @meta_db.del_msg_mbox_uid(msg_id, mbox_id, uid)
  if (mbox_uid_map.empty?) then
    @meta_db.clear_msg_date(msg_id)
    @meta_db.clear_msg_flag(msg_id)
    @meta_db.clear_msg_mbox_uid_mapping(msg_id)
    @msg_db.del_msg(msg_id)
  end
  @meta_db.mbox_flag_num_decrement(mbox_id, 'deleted')
  nil
end
get_mbox_db(mbox_id) click to toggle source
# File lib/rims/mail_store.rb, line 50
def get_mbox_db(mbox_id)
  if (@mbox_db.key? mbox_id) then
    @mbox_db[mbox_id] ||= @mbox_db_factory.call(mbox_id)
  end
end