class RIMS::DB::Meta

Constants

LOST_FOUND_MBOX_NAME

Attributes

lost_found_mbox_set[R]
lost_found_msg_set[R]

Public Instance Methods

add_mbox(name, mbox_id: nil) click to toggle source
# File lib/rims/db.rb, line 178
def add_mbox(name, mbox_id: nil)
  if (@kvs.key? "mbox_name2id-#{name}") then
    raise "duplicated mailbox name: #{name}."
  end

  if (mbox_id) then
    if (@kvs.key? "mbox_id2name-#{mbox_id}") then
      raise "duplicated mailbox id: #{mbox_id}"
    end
    if (uidvalidity <= mbox_id) then
      put_num('uidvalidity', mbox_id + 1)
    end
  else
    mbox_id = uidvalidity_succ!
  end

  mbox_set = get_num_set('mbox_set')
  if (mbox_set.include? mbox_id) then
    raise "internal error: duplicated mailbox id: #{mbox_id}"
  end
  mbox_set << mbox_id
  put_num_set('mbox_set', mbox_set)

  put_str("mbox_id2name-#{mbox_id}", name)
  put_num("mbox_name2id-#{name}", mbox_id)

  mbox_id
end
add_msg_mbox_uid(msg_id, mbox_id) click to toggle source
# File lib/rims/db.rb, line 360
def add_msg_mbox_uid(msg_id, mbox_id)
  uid = mbox_uid_succ!(mbox_id)
  mbox_uid_map = msg_mbox_uid_mapping(msg_id)
  if (mbox_uid_map.key? mbox_id) then
    msg_uid_set = mbox_uid_map[mbox_id]
  else
    msg_uid_set = mbox_uid_map[mbox_id] = [].to_set
  end
  if (msg_uid_set.include? uid) then
    raise "duplicated uid(#{uid}) in mailbox id(#{mbox_id} on message id(#{msg_id}))"
  end
  mbox_uid_map[mbox_id] << uid
  put_obj("msg_id2mbox-#{msg_id}", mbox_uid_map)

  mbox_msg_num_increment(mbox_id)
  flag_set = get_str_set("msg_id2flag-#{msg_id}")
  for name in flag_set
    mbox_flag_num_increment(mbox_id, name)
  end

  uid
end
clear_mbox_flag_num(mbox_id, name) click to toggle source
# File lib/rims/db.rb, line 296
def clear_mbox_flag_num(mbox_id, name)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  if (@kvs.delete("mbox_id2flagnum-#{mbox_id}-#{name}")) then
    self
  end
end
clear_msg_date(msg_id) click to toggle source
# File lib/rims/db.rb, line 312
def clear_msg_date(msg_id)
  if (@kvs.delete("msg_id2date-#{msg_id}")) then
    self
  end
end
clear_msg_flag(msg_id) click to toggle source
# File lib/rims/db.rb, line 350
def clear_msg_flag(msg_id)
  if (@kvs.delete("msg_id2flag-#{msg_id}")) then
    self
  end
end
clear_msg_mbox_uid_mapping(msg_id) click to toggle source
# File lib/rims/db.rb, line 402
def clear_msg_mbox_uid_mapping(msg_id)
  if (@kvs.delete("msg_id2mbox-#{msg_id}")) then
    self
  end
end
cnum() click to toggle source
# File lib/rims/db.rb, line 154
def cnum
  get_num('cnum')
end
cnum_succ!() click to toggle source
# File lib/rims/db.rb, line 158
def cnum_succ!
  num_succ!('cnum')
end
del_mbox(mbox_id) click to toggle source
# File lib/rims/db.rb, line 207
def del_mbox(mbox_id)
  mbox_set = get_num_set('mbox_set')
  if (mbox_set.include? mbox_id) then
    mbox_set.delete(mbox_id)
    put_num_set('mbox_set', mbox_set)
    name = mbox_name(mbox_id)
    @kvs.delete("mbox_id2name-#{mbox_id}") or raise "not found a mailbox name for id: #{mbox_id}"
    @kvs.delete("mbox_name2id-#{name}") or raise "not found a mailbox id for name: #{name}"
    @kvs.delete("mbox_id2uid-#{mbox_id}")
    @kvs.delete("mbox_id2msgnum-#{mbox_id}")
    self
  end
end
del_msg_mbox_uid(msg_id, mbox_id, uid) click to toggle source
# File lib/rims/db.rb, line 383
def del_msg_mbox_uid(msg_id, mbox_id, uid)
  mbox_uid_map = msg_mbox_uid_mapping(msg_id)
  if (uid_set = mbox_uid_map[mbox_id]) then
    if (uid_set.include? uid) then
      uid_set.delete(uid)
      mbox_uid_map.delete(mbox_id) if uid_set.empty?
      put_obj("msg_id2mbox-#{msg_id}", mbox_uid_map)

      mbox_msg_num_decrement(mbox_id)
      flag_set = get_str_set("msg_id2flag-#{msg_id}")
      for name in flag_set
        mbox_flag_num_decrement(mbox_id, name)
      end

      mbox_uid_map
    end
  end
end
dirty=(dirty_flag) click to toggle source
# File lib/rims/db.rb, line 143
def dirty=(dirty_flag)
  if (dirty_flag) then
    put_str('dirty', '')
  else
    @kvs.delete('dirty')
  end
  @kvs.sync

  dirty_flag
end
dirty?() click to toggle source
# File lib/rims/db.rb, line 139
def dirty?
  @kvs.key? 'dirty'
end
each_mbox_id() { |mbox_id| ... } click to toggle source
# File lib/rims/db.rb, line 235
def each_mbox_id
  return enum_for(:each_mbox_id) unless block_given?
  mbox_set = get_num_set('mbox_set')
  for mbox_id in mbox_set
    yield(mbox_id)
  end
  self
end
mbox_flag_num(mbox_id, name) click to toggle source
# File lib/rims/db.rb, line 279
def mbox_flag_num(mbox_id, name)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  get_num("mbox_id2flagnum-#{mbox_id}-#{name}")
end
mbox_flag_num_decrement(mbox_id, name) click to toggle source
# File lib/rims/db.rb, line 290
def mbox_flag_num_decrement(mbox_id, name)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  num_decrement("mbox_id2flagnum-#{mbox_id}-#{name}")
  self
end
mbox_flag_num_increment(mbox_id, name) click to toggle source
# File lib/rims/db.rb, line 284
def mbox_flag_num_increment(mbox_id, name)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  num_increment("mbox_id2flagnum-#{mbox_id}-#{name}")
  self
end
mbox_id(name) click to toggle source
# File lib/rims/db.rb, line 248
def mbox_id(name)
  get_num("mbox_name2id-#{name}", default_value: nil)
end
mbox_msg_num(mbox_id) click to toggle source
# File lib/rims/db.rb, line 262
def mbox_msg_num(mbox_id)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  get_num("mbox_id2msgnum-#{mbox_id}")
end
mbox_msg_num_decrement(mbox_id) click to toggle source
# File lib/rims/db.rb, line 273
def mbox_msg_num_decrement(mbox_id)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  num_decrement("mbox_id2msgnum-#{mbox_id}")
  self
end
mbox_msg_num_increment(mbox_id) click to toggle source
# File lib/rims/db.rb, line 267
def mbox_msg_num_increment(mbox_id)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  num_increment("mbox_id2msgnum-#{mbox_id}")
  self
end
mbox_name(mbox_id) click to toggle source
# File lib/rims/db.rb, line 244
def mbox_name(mbox_id)
  get_str("mbox_id2name-#{mbox_id}", default_value: nil)
end
mbox_uid(mbox_id) click to toggle source
# File lib/rims/db.rb, line 252
def mbox_uid(mbox_id)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  get_num("mbox_id2uid-#{mbox_id}", default_value: 1)
end
mbox_uid_succ!(mbox_id) click to toggle source
# File lib/rims/db.rb, line 257
def mbox_uid_succ!(mbox_id)
  mbox_name(mbox_id) or raise "not found a mailbox for id: #{mbox_id}"
  num_succ!("mbox_id2uid-#{mbox_id}", default_value: 1)
end
msg_date(msg_id) click to toggle source
# File lib/rims/db.rb, line 303
def msg_date(msg_id)
  get_obj("msg_id2date-#{msg_id}") or raise "not found a message date for internal id: #{msg_id}"
end
msg_flag(msg_id, name) click to toggle source
# File lib/rims/db.rb, line 318
def msg_flag(msg_id, name)
  flag_set = get_str_set("msg_id2flag-#{msg_id}")
  flag_set.include? name
end
msg_id() click to toggle source
# File lib/rims/db.rb, line 162
def msg_id
  get_num('msg_id')
end
msg_id_succ!() click to toggle source
# File lib/rims/db.rb, line 166
def msg_id_succ!
  num_succ!('msg_id')
end
msg_mbox_uid_mapping(msg_id) click to toggle source
# File lib/rims/db.rb, line 356
def msg_mbox_uid_mapping(msg_id)
  get_obj("msg_id2mbox-#{msg_id}", default_value: {})
end
recovery_end() click to toggle source
# File lib/rims/db.rb, line 413
def recovery_end
  @lost_found_msg_set = nil
  @lost_found_mbox_set = nil
end
recovery_phase1_msg_scan(msg_db, logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 430
def recovery_phase1_msg_scan(msg_db, logger: Logger.new(STDOUT))
  logger.info('recovery phase 1: start.')

  max_msg_id = -1
  msg_db.each_msg_id do |msg_id|
    max_msg_id = msg_id if (max_msg_id < msg_id)
    unless (@kvs.key? "msg_id2mbox-#{msg_id}") then
      logger.warn("lost+found message: #{msg_id}")
      @lost_found_msg_set << msg_id
    end
    unless (@kvs.key? "msg_id2date-#{msg_id}") then
      logger.warn("repair internal date: #{msg_id}")
      set_msg_date(msg_id, Time.now)
    end
  end

  if (msg_id <= max_msg_id) then
    next_msg_id = max_msg_id + 1
    logger.warn("repair msg_id: #{next_msg_id}")
    put_num('msg_id', next_msg_id)
  end

  logger.info('recovery phase 1: end.')

  self
end
recovery_phase2_msg_scan(msg_db, logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 457
def recovery_phase2_msg_scan(msg_db, logger: Logger.new(STDOUT))
  logger.info('recovery phase 2: start.')

  lost_msg_set = [].to_set
  mbox_set = get_num_set('mbox_set')

  @kvs.each_key do |key|
    if (msg_id = get_recover_entry(key, 'msg_id2mbox-') {|s| s.to_i }) then
      if (msg_db.msg_exist? msg_id) then
        msg_mbox_uid_mapping(msg_id).each_key do |mbox_id|
          unless (mbox_set.include? mbox_id) then
            logger.warn("lost+found mailbox: #{mbox_id}")
            @lost_found_mbox_set << mbox_id
          end
        end
      else
        lost_msg_set << msg_id
      end
    end
  end

  for msg_id in lost_msg_set
    logger.warn("clear lost message: #{msg_id}")
    clear_msg_date(msg_id)
    clear_msg_flag(msg_id)
    clear_msg_mbox_uid_mapping(msg_id)
  end

  logger.info('recovery phase 2: end.')

  self
end
recovery_phase3_mbox_scan(logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 503
def recovery_phase3_mbox_scan(logger: Logger.new(STDOUT))
  logger.info('recovery phase 3: start.')

  mbox_set = get_num_set('mbox_set')

  max_mbox_id = 0
  for mbox_id in mbox_set
    max_mbox_id = mbox_id if (mbox_id > max_mbox_id)
    if (name = mbox_name(mbox_id)) then
      mbox_id2 = mbox_id(name)
      unless (mbox_id2 && (mbox_id2 == mbox_id)) then
        logger.warn("repair mailbox name -> id: #{name.inspect} -> #{mbox_id}")
        put_num("mbox_name2id-#{name}", mbox_id)
      end
    else
      new_name = make_mbox_repair_name(mbox_id)
      logger.warn("repair mailbox id name pair: #{mbox_id}, #{new_name.inspect}")
      put_str("mbox_id2name-#{mbox_id}", new_name)
      put_num("mbox_name2id-#{new_name}", mbox_id)
    end
  end

  if (uidvalidity <= max_mbox_id) then
    next_uidvalidity = max_mbox_id + 1
    logger.warn("repair uidvalidity: #{next_uidvalidity}")
    put_num('uidvalidity', next_uidvalidity)
  end

  logger.info('recovery phase 3: end.')

  self
end
recovery_phase4_mbox_scan(logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 536
def recovery_phase4_mbox_scan(logger: Logger.new(STDOUT))
  logger.info('recovery phase 4: start.')

  mbox_set = get_num_set('mbox_set')

  del_key_list = []
  @kvs.each_key do |key|
    if (mbox_id = get_recover_entry(key, 'mbox_id2name-') {|s| s.to_i }) then
      unless (mbox_set.include? mbox_id) then
        del_key_list << key
      end
    elsif (name = get_recover_entry(key, 'mbox_name2id-')) then
      unless ((mbox_id = mbox_id(name)) && (mbox_set.include? mbox_id) && (mbox_name(mbox_id) == name)) then
        del_key_list << key
      end
    end
  end

  for key in del_key_list
    logger.warn("unlinked mailbox entry: #{key}")
    @kvs.delete(key)
  end

  logger.info('recovery phase 4: end.')

  self
end
recovery_phase5_mbox_repair(logger: Logger.new(STDOUT)) { |mbox_id| ... } click to toggle source
# File lib/rims/db.rb, line 566
def recovery_phase5_mbox_repair(logger: Logger.new(STDOUT))
  logger.info('recovery phase 5: start.')

  for mbox_id in @lost_found_mbox_set
    logger.warn("repair lost mailbox: #{mbox_id}")
    add_mbox(make_mbox_repair_name(mbox_id), mbox_id: mbox_id)
    yield(mbox_id)
  end
  unless (mbox_id(LOST_FOUND_MBOX_NAME)) then
    logger.warn('create lost+found mailbox.')
    mbox_id = add_mbox(LOST_FOUND_MBOX_NAME)
    yield(mbox_id)
  end

  logger.info('recovery phase 5: end.')

  self
end
recovery_phase6_msg_scan(mbox_db, logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 585
def recovery_phase6_msg_scan(mbox_db, logger: Logger.new(STDOUT))
  logger.info('recovery phase 6: start.')

  @kvs.each_key do |key|
    if (msg_id = get_recover_entry(key, 'msg_id2mbox-') {|s| s.to_i }) then
      is_modified = false
      mbox_uid_map = msg_mbox_uid_mapping(msg_id)
      mbox_uid_map.each_pair.to_a.each do |mbox_id, uid_set|
        uid_set.to_a.each do |uid|
          if (msg_id2 = mbox_db[mbox_id].msg_id(uid)) then
            if (msg_id != msg_id2) then
              logger.warn("lost+found message -> mailbox: #{msg_id} -> #{mbox_id},#{uid}")
              is_modified = true
              uid_set.delete(uid)
              mbox_uid_map.delete(mbox_id) if uid_set.empty?
              @lost_found_msg_set << msg_id
            end
          else
            logger.warn("repair mailbox -> message: #{mbox_id},#{uid} -> #{msg_id}")
            mbox_db[mbox_id].add_msg(uid, msg_id)
          end
        end
      end

      if (is_modified) then
        logger.warn("save repaired message: #{msg_id}")
        put_obj("msg_id2mbox-#{msg_id}", mbox_uid_map)
      end
    end
  end

  logger.info('recovery phase 6: end.')

  self
end
recovery_phase7_mbox_msg_scan(mbox_db, flag_name_list, logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 621
def recovery_phase7_mbox_msg_scan(mbox_db, flag_name_list, logger: Logger.new(STDOUT))
  logger.info('recovery phase 7: start.')

  mbox_db.each_key do |mbox_id|
    logger.info("scan mailbox: #{mbox_id}")

    max_uid = 0
    msg_num = 0
    flag_num = Hash.new(0)
    del_uid_list = []

    mbox_db[mbox_id].each_msg_uid do |uid|
      msg_id = mbox_db[mbox_id].msg_id(uid)
      if ((mbox_uid_map = msg_mbox_uid_mapping(msg_id)) && (uid_set = mbox_uid_map[mbox_id]) && (uid_set.include? uid)) then
        max_uid = uid if (max_uid < uid)
        msg_num += 1
        for name in flag_name_list
          if (name == 'deleted') then
            if (mbox_db[mbox_id].msg_flag_deleted(uid)) then
              flag_num['deleted'] += 1
            end
          else
            if (msg_flag(msg_id, name)) then
              flag_num[name] += 1
            end
          end
        end
      else
        del_uid_list << uid
      end
    end

    for uid in del_uid_list
      logger.warn("unlinked message uid: #{mbox_id},#{uid}")
      mbox_db[mbox_id].set_msg_flag_deleted(uid, true)
      mbox_db[mbox_id].expunge_msg(uid)
    end

    if (mbox_uid(mbox_id) <= max_uid) then
      next_uid = max_uid + 1
      logger.warn("repair mailbox uid: #{next_uid}")
      put_num("mbox_id2uid-#{mbox_id}", next_uid)
    end

    if (mbox_msg_num(mbox_id) != msg_num) then
      logger.warn("repair mailbox message number: #{msg_num}")
      put_num("mbox_id2msgnum-#{mbox_id}", msg_num)
    end

    for name in flag_name_list
      if (mbox_flag_num(mbox_id, name) != flag_num[name]) then
        logger.warn("repair mailbox #{name} flag number: #{flag_num[name]}")
        put_num("mbox_id2flagnum-#{mbox_id}-#{name}", flag_num[name])
      end
    end
  end

  logger.info('recovery phase 7: end.')

  self
end
recovery_phase8_lost_found(mbox_db, logger: Logger.new(STDOUT)) click to toggle source
# File lib/rims/db.rb, line 683
def recovery_phase8_lost_found(mbox_db, logger: Logger.new(STDOUT))
  logger.info('recovery phase 8: start.')

  mbox_id = mbox_id(LOST_FOUND_MBOX_NAME) or raise "not found a #{LOST_FOUND_MBOX_NAME} mailbox."
  for msg_id in @lost_found_msg_set
    logger.warn("repair lost+found message: #{msg_id}")
    uid = add_msg_mbox_uid(msg_id, mbox_id)
    mbox_db[mbox_id].add_msg(uid, msg_id)
  end

  logger.info('recovery phase 8: end.')

  self
end
recovery_start() click to toggle source
# File lib/rims/db.rb, line 408
def recovery_start
  @lost_found_msg_set = [].to_set
  @lost_found_mbox_set = [].to_set
end
rename_mbox(mbox_id, new_name) click to toggle source
# File lib/rims/db.rb, line 221
def rename_mbox(mbox_id, new_name)
  old_name = get_str("mbox_id2name-#{mbox_id}") or raise "not found a mailbox name for id: #{mbox_id}"
  if (new_name == old_name) then
    return
  end
  if (@kvs.key? "mbox_name2id-#{new_name}") then
    raise "duplicated mailbox name: #{new_name}"
  end
  @kvs.delete("mbox_name2id-#{old_name}") or raise "not found a mailbox old name for id: #{mbox_id}"
  put_str("mbox_id2name-#{mbox_id}", new_name)
  put_num("mbox_name2id-#{new_name}", mbox_id)
  self
end
set_msg_date(msg_id, date) click to toggle source
# File lib/rims/db.rb, line 307
def set_msg_date(msg_id, date)
  put_obj("msg_id2date-#{msg_id}", date)
  self
end
set_msg_flag(msg_id, name, value) click to toggle source
# File lib/rims/db.rb, line 323
def set_msg_flag(msg_id, name, value)
  flag_set = get_str_set("msg_id2flag-#{msg_id}")
  if (value) then
    unless (flag_set.include? name) then
      mbox_uid_map = msg_mbox_uid_mapping(msg_id)
      for mbox_id, uid_set in mbox_uid_map
        uid_set.length.times do
          mbox_flag_num_increment(mbox_id, name)
        end
      end
    end
    flag_set.add(name)
  else
    if (flag_set.include? name) then
      mbox_uid_map = msg_mbox_uid_mapping(msg_id)
      for mbox_id, uid_set in mbox_uid_map
        uid_set.length.times do
          mbox_flag_num_decrement(mbox_id, name)
        end
      end
    end
    flag_set.delete(name)
  end
  put_str_set("msg_id2flag-#{msg_id}", flag_set)
  self
end
uidvalidity() click to toggle source
# File lib/rims/db.rb, line 170
def uidvalidity
  get_num('uidvalidity', default_value: 1)
end
uidvalidity_succ!() click to toggle source
# File lib/rims/db.rb, line 174
def uidvalidity_succ!
  num_succ!('uidvalidity', default_value: 1)
end

Private Instance Methods

get_recover_entry(key, prefix) { |entry_key| ... } click to toggle source
# File lib/rims/db.rb, line 421
def get_recover_entry(key, prefix)
  if (key.start_with? prefix) then
    entry_key = key[(prefix.length)..-1]
    entry_key = yield(entry_key) if block_given?
    entry_key
  end
end
make_mbox_repair_name(mbox_id) click to toggle source
# File lib/rims/db.rb, line 490
def make_mbox_repair_name(mbox_id)
  new_name = "MAILBOX##{mbox_id}"
  if (mbox_id(new_name)) then
    new_name << ' (1)'
    while (mbox_id(new_name))
      new_name.succ!
    end
  end

  new_name
end