module RIMS::Cmd

Constants

CMDs

Public Class Methods

command_function(method_name, description) click to toggle source
# File lib/rims/cmd.rb, line 21
def self.command_function(method_name, description)
  module_function(method_name)
  method_name = method_name.to_s
  unless (method_name =~ /\A cmd_/x) then
    raise "invalid command function name: #{method_name}"
  end
  cmd_name = $'.gsub(/_/, '-')
  CMDs[cmd_name] = { function: method_name.to_sym, description: description }
end
each_message(args, verbose: false) { |msg_txt, nil| ... } click to toggle source
# File lib/rims/cmd.rb, line 1278
def each_message(args, verbose: false)
  if (args.empty?) then
    msg_txt = STDIN.read
    yield(msg_txt, nil)
    return 0
  else
    error_count = 0
    args.each_with_index do |filename, i|
      puts "progress: #{i + 1}/#{args.length}" if verbose
      begin
        msg_txt = IO.read(filename, mode: 'rb', encoding: 'ascii-8bit')
        yield(msg_txt, filename)
      rescue
        error_count += 1
        puts "failed to append message: #{filename}"
        Error.trace_error_chain($!) do |exception|
          puts "error: #{exception}"
          if ($DEBUG) then
            for frame in exception.backtrace
              puts frame
            end
          end
        end
      end
    end

    if (error_count > 0) then
      puts "#{error_count} errors!"
      return 1
    else
      return 0
    end
  end
end
imap_append(imap, mailbox, message, store_flags: [], date_time: nil, verbose: false) click to toggle source
# File lib/rims/cmd.rb, line 1269
def imap_append(imap, mailbox, message, store_flags: [], date_time: nil, verbose: false)
  puts "message date: #{date_time}" if (verbose && date_time)
  store_flags = nil if store_flags.empty?
  res = imap.append(mailbox, message, store_flags, date_time)
  puts "append: #{imap_res2str(res)}" if verbose
  nil
end
imap_res2str(imap_response) click to toggle source
# File lib/rims/cmd.rb, line 841
def imap_res2str(imap_response)
  "#{imap_response.name} #{imap_response.data.text}"
end
make_service_config(options) click to toggle source
# File lib/rims/cmd.rb, line 96
def make_service_config(options)
  build = ServiceConfigChainBuilder.new
  build.chain{|c| c.load(base_dir: Dir.getwd) }

  options.summary_width = 37
  log_level_list = %w[ debug info warn error fatal unknown ]

  options.on('-h', '--help', 'Show this message.') do
    puts options
    exit
  end
  options.on('-f', '--config-yaml=CONFIG_FILE',
             String,
             "Load optional parameters from CONFIG_FILE."
            ) do |path|
    build.chain{|c| c.load_yaml(path) }
  end
  options.on('-r', '--required-feature=FEATURE',
             String,
             "Add required feature."
            ) do |feature|
    require(feature)
    build.chain{|c| c.load(required_features: [ feature ]) }
  end
  options.on('-d', '--base-dir=DIR',
             String,
             "Directory that places log file, mailbox database, etc. default is current directory."
            ) do |path|
    build.chain{|c| c.load(base_dir: path) }
  end
  options.on('--log-file=FILE',
             String,
             "Name of log file. default is `#{Service::DEFAULT_CONFIG.make_file_logger_params[0]}'."
            ) do |path|
    build.chain{|c|
      c.load(logger: {
               file: {
                 path: path
               }
             })
    }
  end
  options.on('-l', '--log-level=LEVEL',
             log_level_list,
             "Logging level (#{log_level_list.join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_file_logger_params[-1][:level] +
             "'."
            ) do |level|
    build.chain{|c|
      c.load(logging: {
               file: {
                 level: level
               }
             })
    }
  end
  options.on('--log-shift-age=NUMBER',
             Integer,
             'Number of old log files to keep.'
            ) do |num|
    build.chain{|c|
      c.load(logging: {
               file: {
                 shift_age: num
               }
             })
    }
  end
  options.on('--log-shift-age-daily',
             'Frequency of daily log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'daily'
               }
             })
    }
  end
  options.on('--log-shift-age-weekly',
             'Frequency of weekly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'weekly'
               }
             })
    }
  end
  options.on('--log-shift-age-monthly',
             'Frequency of monthly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'monthly'
               }
             })
    }
  end
  options.on('--log-shift-size=SIZE',
             Integer,
             'Maximum logfile size.'
            ) do |size|
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_size: size
               }
             })
    }
  end
  options.on('-v', '--log-stdout=LEVEL',
             log_level_list + %w[  quiet ],
             "Stdout logging level (#{(log_level_list + %w[ quiet ]).join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_stdout_logger_params[-1][:level] +
             "'."
            ) do |level|
    if (level == 'quiet') then
      level = 'unknown'
    end
    build.chain{|c|
      c.load(logging: {
               stdout: {
                 level: level
               }
             })
    }
  end
  options.on('--protocol-log-file=FILE',
             String,
             "Name of log file. default is `#{Service::DEFAULT_CONFIG.make_protocol_logger_params[0]}'."
            ) do |path|
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 path: path
               }
             })
    }
  end
  options.on('-p', '--protocol-log-level=LEVEL',
             log_level_list,
             "Logging level (#{log_level_list.join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_protocol_logger_params[-1][:level] +
             "'."
            ) do |level|
    build.chain{|c|
      c.load(logging: {
               protocol: {
                 level: level
               }
             })
    }
  end
  options.on('--protocol-log-shift-age=NUMBER',
             Integer,
             'Number of old log files to keep.'
            ) do |num|
    build.chain{|c|
      c.load(logging: {
               protocol: {
                 shift_age: num
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-daily',
             'Frequency of daily log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'daily'
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-weekly',
             'Frequency of weekly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'weekly'
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-monthly',
             'Frequency of monthly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'monthly'
               }
             })
    }
  end
  options.on('--protocol-log-shift-size=SIZE',
             Integer,
             'Maximum logfile size.'
            ) do |size|
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_size: size
               }
             })
    }
  end
  options.on('--[no-]daemonize',
             "Daemonize server process. effective only with daemon command."
            ) do |daemonize|
    build.chain{|c|
      c.load(daemon: {
               daemonize: daemonize
             })
    }
  end
  options.on('--[no-]daemon-debug',
             "Debug daemon. effective only with daemon command."
            ) do |debug|
    build.chain{|c|
      c.load(daemon: {
               debug: debug
             })
    }
  end
  options.on('--daemon-umask=UMASK',
             Integer,
             "Umask(2). effective only with daemon command. default is `#{'%04o' % Service::DEFAULT_CONFIG.daemon_umask}'."
            ) do |umask|
    build.chain{|c|
      c.load(daemon: {
               umask: umask
             })
    }
  end
  options.on('--status-file=FILE',
             String,
             "Name of status file. effective only with daemon command. default is `#{Service::DEFAULT_CONFIG.status_file}'."
            ) do |path|
    build.chain{|c|
      c.load(daemon: {
               status_file: path
             })
    }
  end
  options.on('--privilege-user=USER',
             String,
             "Privilege user name or ID for server process. effective only with daemon command."
            ) do |user|
    build.chain{|c|
      c.load(daemon: {
               server_privileged_user: user
             })
    }
  end
  options.on('--privilege-group=GROUP',
             String,
             "Privilege group name or ID for server process. effective only with daemon command."
            ) do |group|
    build.chain{|c|
      c.load(daemon: {
               server_privileged_group: group
             })
    }
  end
  options.on('-s', '--listen=HOST_PORT',
             String,
             "Listen socket address. default is `#{Service::DEFAULT_CONFIG.listen_address}'"
            ) do |host_port|
    build.chain{|c|
      c.load(server: {
               listen_address: host_port
             })
    }
  end
  options.on('--accept-polling-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(server: {
               accept_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--process-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(server: {
               process_num: num
             })
    }
  end
  options.on('--process-queue-size=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(server: {
               process_queue_size: size
             })
    }
  end
  options.on('--process-queue-polling-timeout=SECONDS',
             Float) do |seconds|
    build.chain{|c|
      c.load(server: {
               process_queue_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--process-send-io-polling-timeout=SECONDS',
             Float) do |seconds|
    build.chain{|c|
      c.load(server: {
               process_send_io_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--thread-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(server: {
               thread_num: num
             })
    }
  end
  options.on('--thread-queue-size=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(server: {
               thread_queue_size: size
             })
    }
  end
  options.on('--thread-queue-polling-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(server: {
               thread_queue_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--send-buffer-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(connection: {
               send_buffer_limit_size: size
             })
    }
  end
  options.on('--read-polling-interval=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(connection: {
               read_polling_interval_seconds: seconds
             })
    }
  end
  options.on('--command-wait-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(connection: {
               command_wait_timeout_seconds: seconds
             })
    }
  end
  options.on('--line-length-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               line_length_limit: size
             })
    }
  end
  options.on('--literal-size-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               literal_size_limit: size
             })
    }
  end
  options.on('--command-size-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               command_size_limit: size
             })
    }
  end
  options.on('--[no-]use-default-charset-aliases'
            ) do |use_default_aliases|
    build.chain{|c|
      c.load(charset: {
               use_default_aliases: use_default_aliases
             })
    }
  end
  options.on('--add-charset-alias=NAME_TO_ENCODING',
             /\A \S+,\S+ \z/x,
             "Set the alias name and encoding separated with comma (,)."
            ) do |name_to_encoding|
    name, encoding = name_to_encoding.split(',', 2)
    build.chain{|c|
      c.load(charset: {
               aliases: [
                 { name: name, encoding: encoding }
               ]
             })
    }
  end
  options.on('--[no-]replace-charset-invalid'
            ) do |replace|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replace_invalid_byte_sequence: replace
               }
             })
    }
  end
  options.on('--[no-]replace-charset-undef'
            ) do |replace|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replace_undefined_character: replace
               }
             })
    }
  end
  options.on('--charset-replaced-mark=MARK',
             String
            ) do |mark|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replaced_mark: mark
               }
             })
    }
  end
  options.on('--drb-process-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(drb_services: {
               process_num: num
             })
    }
  end
  options.on('--drb-load-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(drb_services: {
               load_limit: size
             })
    }
  end
  options.on('--bulk-response-count=COUNT',
             Integer) do |count|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 bulk_response_count: count
               }
             })
    }
  end
  options.on('--bulk-response-size=SIZE',
             Integer) do |size|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 bulk_response_size: size
               }
             })
    }
  end
  options.on('--read-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 read_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--write-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 write_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--cleanup-write-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 cleanup_write_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--meta-kvs-type=TYPE',
             "Choose key-value store type of mailbox meta-data database. default is `" +
             KeyValueStore::FactoryBuilder.plug_in_names[0] +
             "'."
            ) do |kvs_type|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 type: kvs_type
               }
             })
    }
  end
  options.on('--meta-kvs-config=JSON_DATA',
             JSON,
             "Configuration for key-value store of mailbox meta-data database."
            ) do |json_data|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 configuration: json_data
               }
             })
    }
  end
  options.on('--[no-]use-meta-kvs-checksum',
             "Enable/disable data checksum at key-value store of mailbox meta-data database. default is " +
             if (Service::DEFAULT_CONFIG.make_meta_key_value_store_params.middleware_list.include? Checksum_KeyValueStore) then
               'enabled'
             else
               'disbled'
             end +
             "."
            ) do |use_checksum|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 use_checksum: use_checksum
               }
             })
    }
  end
  options.on('--text-kvs-type=TYPE',
             "Choose key-value store type of mailbox text-data database. default is `" +
             KeyValueStore::FactoryBuilder.plug_in_names[0] +
             "'."
            ) do |kvs_type|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 type: kvs_type
               }
             })
    }
  end
  options.on('--text-kvs-config=JSON_DATA',
             JSON,
             "Configuration for key-value store of mailbox text-data database."
            ) do |json_data|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 configuration: json_data
               }
             })
    }
  end
  options.on('--[no-]use-text-kvs-checksum',
             "Enable/disable data checksum at key-value store of mailbox text-data database. default is " +
             if (Service::DEFAULT_CONFIG.make_text_key_value_store_params.middleware_list.include? Checksum_KeyValueStore) then
               'enabled'
             else
               'disbled'
             end +
             "."
            ) do |use_checksum|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 use_checksum: use_checksum
               }
             })
    }
  end
  options.on('--auth-hostname=HOSTNAME',
             String,
             "Hostname to authenticate with cram-md5. default is `#{Service::DEFAULT_CONFIG.make_authentication.hostname}'."
            ) do |hostname|
    build.chain{|c|
      c.load(authentication: {
               hostname: hostname
             })
    }
  end
  options.on('--passwd-config=TYPE_JSONDATA',
             /([^:]+)(?::(.*))?/,
             "Password source type and configuration. format is `[type]:[json_data]'."
            ) do |_, type, json_data|
    build.chain{|c|
      c.load(authentication: {
               password_sources: [
                 { type: type,
                   configuration: JSON.load(json_data)
                 }
               ]
             })
    }
  end
  options.on('--passwd-file=TYPE_FILE',
             /([^:]+):(.+)/,
             "Password source type and configuration file. format is `[type]:[file]'."
            ) do |_, type, path|
    build.chain{|c|
      c.load(authentication: {
               password_sources: [
                 { type: type,
                   configuration_file: path
                 }
               ]
             })
    }
  end
  options.on('--mail-delivery-user=USERNAME',
             String,
             "Username authorized to deliver messages to any mailbox. default is `#{Service::DEFAULT_CONFIG.mail_delivery_user}'"
            ) do |username|
    build.chain{|c|
      c.load(authorization: {
               mail_delivery_user: username
             })
    }
  end

  options.on('--imap-host=HOSTNAME',
             String,
             'Deplicated.'
            ) do |host|
    warn("warning: `--imap-host=HOSTNAME' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(imap_host: host) }
  end
  options.on('--imap-port=PORT',
             String,
             'Deplicated.'
            ) do |value|
    warn("warning: `--imap-port=PORT' is deplicated option and should use `--listen=HOST_PORT'.")
    if (value =~ /\A \d+ \z/x) then
      port_number = value.to_i
      build.chain{|c| c.load(imap_port: port_number) }
    else
      service_name = value
      build.chain{|c| c.load(imap_port: service_name) }
    end
  end
  options.on('--ip-addr=IP_ADDR',
             String,
             'Deplicated.'
            ) do |ip_addr|
    warn("warning: `--ip-addr=IP_ADDR' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(ip_addr: ip_addr) }
  end
  options.on('--ip-port=PORT',
             Integer,
             'Deplicated.'
            ) do |port|
    warn("warning: `--ip-port=PORT' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(ip_port: port) }
  end
  options.on('--kvs-type=TYPE',
             'Deplicated.'
            ) do |kvs_type|
    warn("warning: `--kvs-type=TYPE' is deplicated option and should use `--meta-kvs-type=TYPE' or `--text-kvs-type=TYPE'.")
    build.chain{|c| c.load(key_value_store_type: kvs_type) }
  end
  options.on('--[no-]use-kvs-cksum',
             'Deplicated.'
            ) do |use_checksum|
    warn("warning: `--[no-]use-kvs-cksum' is deplicated option and should use `--[no-]use-meta-kvs-checksum' or `--[no-]use-text-kvs-checksum'.")
    build.chain{|c| c.load(use_key_value_store_checksum: use_checksum) }
  end
  options.on('-u', '--username=NAME',
             String,
             'Deplicated.'
            ) do |name|
    warn("warning: `--username=NAME' is deplicated option and should use `--passwd-config=TYPE_JSONDATA' or `--passwd-file=TYPE_FILE'.")
    build.chain{|c| c.load(username: name) }
  end
  options.on('-w', '--password=PASS',
             String,
             'Deplicated.'
            ) do |pass|
    warn("warning: `--password=PASS' is deplicated option and should use `--passwd-config=TYPE_JSONDATA' or `--passwd-file=TYPE_FILE'.")
    build.chain{|c| c.load(password: pass) }
  end

  build
end
run_cmd(args) click to toggle source
# File lib/rims/cmd.rb, line 31
def run_cmd(args)
  options = OptionParser.new
  if (args.empty?) then
    cmd_help(options, args)
    return 1
  end

  cmd_name = args.shift
  pp cmd_name if $DEBUG
  pp args if $DEBUG

  cmd_entry = CMDs[cmd_name] or raise "unknown command: #{cmd_name}. Run `#{options.program_name} help'."
  options.program_name += " #{cmd_name}"
  send(cmd_entry[:function], options, args)
end

Public Instance Methods

cmd_daemon(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1128
def cmd_daemon(options, args)
  conf = Config.new(options,
                    [ [ :use_status_code,
                        true,
                        '--[no-]status-code',
                        "Return the result of `status' operation as an exit code."
                      ],
                      [ :is_daemon,
                        nil,
                        '--[no-]daemon',
                        'Obsoleted.'
                      ],
                      [ :is_syslog,
                        nil,
                        '--[no-]syslog',
                        'Obsoleted.'
                      ]
                    ])
  conf.help_option(add_banner: ' start/stop/restart/status [server options]')
  conf.quiet_option
  conf.setup_option_list
  conf.parse_options!(args, order: true)
  pp args if $DEBUG

  operation = args.shift or raise 'need for daemon operation.'
  server_options = OptionParser.new
  build = make_service_config(server_options)
  server_options.parse!(args)

  unless (conf[:is_daemon].nil?) then
    warn("warning: `--[no-]daemon' is obsoleted option and no effect. use server option `--[no-]daemonize'.")
  end
  unless (conf[:is_syslog].nil?) then
    warn("warning: `--[no-]syslog' is obsoleted option and no effect.")
  end

  svc_conf = build.call
  pp svc_conf if $DEBUG

  status_file_locked = lambda{
    begin
      File.open(svc_conf.status_file, File::WRONLY) {|lock_file|
        ! lock_file.flock(File::LOCK_EX | File::LOCK_NB)
      }
    rescue Errno::ENOENT
      false
    end
  }

  start_daemon = lambda{
    Riser::Daemon.start_daemon(daemonize: svc_conf.daemonize?,
                               daemon_name: svc_conf.daemon_name,
                               daemon_debug: svc_conf.daemon_debug?,
                               daemon_umask: svc_conf.daemon_umask,
                               status_file: svc_conf.status_file,
                               listen_address: proc{
                                 # to reload on server restart
                                 build.call.listen_address
                               },
                               server_polling_interval_seconds: svc_conf.server_polling_interval_seconds,
                               server_restart_overlap_seconds: svc_conf.server_restart_overlap_seconds,
                               server_privileged_user: svc_conf.server_privileged_user,
                               server_privileged_group: svc_conf.server_privileged_group
                              ) {|server|
      c = build.call        # to reload on server restart
      service = RIMS::Service.new(c)
      service.setup(server, daemon: true)
    }
  }

  case (operation)
  when 'start'
    start_daemon.call
  when 'stop'
    if (status_file_locked.call) then
      pid = YAML.load(IO.read(svc_conf.status_file))['pid']
      Process.kill(Riser::Daemon::SIGNAL_STOP_GRACEFUL, pid)
    else
      abort('No daemon.')
    end
  when 'restart'
    if (status_file_locked.call) then
      pid = YAML.load(IO.read(svc_conf.status_file))['pid']
      Process.kill(Riser::Daemon::SIGNAL_RESTART_GRACEFUL, pid)
    else
      start_daemon.call
    end
  when 'status'
    if (status_file_locked.call) then
      puts 'daemon is running.' if conf[:verbose]
      return 0 if conf[:use_status_code]
    else
      puts 'daemon is stopped.' if conf[:verbose]
      return 1 if conf[:use_status_code]
    end
  else
    raise "unknown daemon operation: #{operation}"
  end

  0
end
cmd_debug_dump_kvs(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1516
def cmd_debug_dump_kvs(options, args)
  option_list = [
    [ :match_key, nil, '--match-key=REGEXP', Regexp, 'Show keys matching regular expression.' ],
    [ :dump_size, true, '--[no-]dump-size', 'Dump size of value with key.' ],
    [ :dump_value, true, '--[no-]dump-value', 'Dump value with key.' ],
    [ :marshal_restore, true, '--[no-]marshal-restore', 'Restore serialized object.' ]
  ]

  conf = Config.new(options, option_list)
  conf.required_feature_option
  conf.key_value_store_option
  conf.help_option(add_banner: ' [DB_NAME]')
  conf.setup_option_list
  conf.parse_options!(args)
  pp conf if $DEBUG

  name = args.shift or raise 'need for DB name.'
  unless (conf[:key_value_store_type].exist? name) then
    raise "not found a key-value store: #{name}"
  end

  factory = conf.make_kvs_factory
  db = factory.call(name)
  begin
    db.each_key do |key|
      if (conf[:match_key] && (key !~ conf[:match_key])) then
        next
      end

      entry = key.inspect
      if (conf[:dump_size]) then
        size = db[key].bytesize
        entry += ": #{size} bytes"
      end
      if (conf[:dump_value]) then
        v = db[key]
        if (conf[:marshal_restore]) then
          begin
            v = Marshal.restore(v)
          rescue
            # not marshal object!
          end
        end
        entry += ": #{v.inspect}"
      end

      puts entry
    end
  ensure
    db.close
  end

  0
end
cmd_environment(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1231
def cmd_environment(options, args)
  format = {
    yaml: lambda{|env|
      YAML.dump(env)
    },
    json: lambda{|env|
      JSON.pretty_generate(env)
    }
  }

  conf = Config.new(options,
                    [ [ :format_type,
                        format.keys.first,
                        '--format=FORMAT',
                        format.keys,
                        "Choose display format (#{format.keys.join(' ')})."
                      ]
                    ])
  conf.required_feature_option
  conf.setup_option_list
  conf.parse_options!(args)

  env = {
    'RIMS Environment' => [
      { 'RUBY VERSION' => RUBY_DESCRIPTION },
      { 'RIMS VERSION' => RIMS::VERSION },
      { 'AUTHENTICATION PLUG-IN' => Authentication.plug_in_names },
      { 'KEY-VALUE STORE PLUG-IN' => KeyValueStore::FactoryBuilder.plug_in_names }
    ]
  }

  formatter = format[conf[:format_type]]
  puts formatter.call(env)

  0
end
cmd_help(options, args) click to toggle source
# File lib/rims/cmd.rb, line 48
def cmd_help(options, args)
  show_debug_command = false
  options.on('--show-debug-command', 'Show command for debug in help message. At default, debug command is hidden.') do
    show_debug_command = true
  end
  options.parse!(args)

  puts "usage: #{File.basename($0)} command options"
  puts ""
  puts "commands:"
  w = CMDs.keys.map{|k| k.length }.max + 4
  fmt = "    %- #{w}s%s"
  CMDs.sort_by{|cmd_name, _| cmd_name }.each do |cmd_name, cmd_entry|
    if ((! show_debug_command) && (cmd_name =~ /\A debug/x)) then
      next
    end
    puts format(fmt, cmd_name, cmd_entry[:description])
  end
  puts ""
  puts "command help options:"
  puts "    -h, --help"
  0
end
cmd_imap_append(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1347
def cmd_imap_append(options, args)
  STDIN.set_encoding(Encoding::ASCII_8BIT)

  option_list =
    Config::VERBOSE_OPTION_LIST +
    Config::IMAP_CONNECT_OPTION_LIST +
    Config::IMAP_MAILBOX_OPTION_LIST +
    Config::IMAP_STORE_FLAG_OPTION_LIST +
    Config::MAIL_DATE_OPTION_LIST

  conf = Config.new(options, option_list)
  conf.help_option(add_banner: ' [MESSAGE_FILEs]')
  conf.load_config_option
  conf.setup_option_list
  conf.imap_debug_option
  conf.parse_options!(args)

  store_flags = conf.make_imap_store_flags
  conf.imap_connect{|imap|
    each_message(args) do |msg_txt, filename|
      t = conf.look_for_date(msg_txt, filename)
      imap_append(imap, conf[:mailbox], msg_txt, store_flags: store_flags, date_time: t, verbose: conf[:verbose])
    end
  }
end
cmd_mbox_dirty_flag(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1374
def cmd_mbox_dirty_flag(options, args)
  option_list = [
    [ :return_flag_exit_code, true, '--[no-]return-flag-exit-code', 'Dirty flag value is returned to exit code. default is true.' ]
  ]

  conf = Config.new(options, option_list)
  conf.required_feature_option
  conf.key_value_store_option
  conf.help_option(add_banner: ' [mailbox directory]')
  conf.quiet_option
  conf.setup_option_list

  write_dirty_flag = nil
  options.on('--enable-dirty-flag', 'Enable mailbox dirty flag.') { write_dirty_flag = true }
  options.on('--disable-dirty-flag', 'Disable mailbox dirty flag.') { write_dirty_flag = false }

  conf.parse_options!(args)
  pp conf if $DEBUG

  mbox_dir = args.shift or raise 'need for mailbox directory.'
  meta_db_path = File.join(mbox_dir, 'meta')
  unless (conf[:key_value_store_type].exist? meta_db_path) then
    raise "not found a mailbox meta DB: #{meta_db_path}"
  end

  kvs_factory = conf.make_kvs_factory
  meta_db = DB::Meta.new(kvs_factory.call(File.join(mbox_dir, 'meta')))
  begin
    unless (write_dirty_flag.nil?) then
      meta_db.dirty = write_dirty_flag
    end

    if (conf[:verbose]) then
      puts "dirty flag is #{meta_db.dirty?}."
    end

    if (conf[:return_flag_exit_code]) then
      if (meta_db.dirty?) then
        1
      else
        0
      end
    else
      0
    end
  ensure
    meta_db.close
  end
end
cmd_pass_hash(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1466
    def cmd_pass_hash(options, args)
      option_list = [
        [ :hash_type, 'SHA256', '--hash-type=DIGEST', String, 'Password hash type (ex SHA256, MD5, etc). default is SHA256.' ],
        [ :stretch_count, 10000, '--stretch-count=COUNT', Integer, 'Count to stretch password hash. default is 10000.' ],
        [ :salt_size, 16, '--salt-size=OCTETS', Integer, 'Size of salt string. default is 16 octets.' ]
      ]

      conf = Config.new(options, option_list)
      conf.help_option(add_banner: <<-'EOF'.chomp)
 passwd_plain.yml
Example
  $ cat passwd_plain.yml
  - { user: foo, pass: open_sesame }
  - { user: "#postman", pass: "#postman" }
  $ rims pass-hash passwd_plain.yml >passwd_hash.yml
  $ cat passwd_hash.yml
  ---
  - user: foo
    hash: SHA256:10000:YkslZucwN2QJ7LOft59Pgw==:d5dca9109cc787220eba65810e40165079ce3292407e74e8fbd5c6a8a9b12204
  - user: "#postman"
    hash: SHA256:10000:6Qj/wAYmb7NUGdOy0N35qg==:e967e46b8e0d9df6324e66c7e42da64911a8715e06a123fe5abf7af4ca45a386
Options:
      EOF
      conf.setup_option_list
      conf.parse_options!(args)
      pp conf if $DEBUG

      case (args.length)
      when 0
        passwd, *_optional = YAML.load_stream(STDIN)
      when 1
        passwd, *_optional = File.open(args[0]) {|f| YAML.load_stream(f) }
      else
        raise ArgumentError, 'too many input files.'
      end

      digest_factory = Password::HashSource.search_digest_factory(conf[:hash_type])
      salt_generator = Password::HashSource.make_salt_generator(conf[:salt_size])

      for entry in passwd
        pass = entry.delete('pass') or raise "not found a `pass' entry."
        entry['hash'] = Password::HashSource.make_entry(digest_factory, conf[:stretch_count], salt_generator.call, pass).to_s
      end

      puts passwd.to_yaml

      0
    end
cmd_post_mail(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1314
def cmd_post_mail(options, args)
  STDIN.set_encoding(Encoding::ASCII_8BIT)

  option_list =
    Config::VERBOSE_OPTION_LIST +
    Config::POST_MAIL_CONNECT_OPTION_LIST +
    Config::IMAP_MAILBOX_OPTION_LIST +
    Config::IMAP_STORE_FLAG_OPTION_LIST +
    Config::MAIL_DATE_OPTION_LIST

  conf = Config.new(options, option_list)
  conf.help_option(add_banner: ' [POST USER] [MESSAGE_FILEs]')
  conf.load_config_option
  conf.setup_option_list
  conf.imap_debug_option
  conf.parse_options!(args)

  post_user = args.shift or raise 'need for post user.'

  store_flags = conf.make_imap_store_flags
  conf.imap_connect{|imap|
    unless (imap.capability.find{|c| c == 'X-RIMS-MAIL-DELIVERY-USER' }) then
      warn('warning: This IMAP server might not support RIMS mail delivery protocol.')
    end
    each_message(args) do |msg_txt, filename|
      t = conf.look_for_date(msg_txt, filename)
      encoded_mbox_name = Protocol::Decoder.encode_delivery_target_mailbox(post_user, conf[:mailbox])
      imap_append(imap, encoded_mbox_name, msg_txt, store_flags: store_flags, date_time: t, verbose: conf[:verbose])
    end
  }
end
cmd_server(options, args) click to toggle source
# File lib/rims/cmd.rb, line 822
def cmd_server(options, args)
  build = make_service_config(options)
  options.parse!(args)

  config = build.call
  server = Riser::SocketServer.new
  service = RIMS::Service.new(config)
  service.setup(server)

  Signal.trap(:INT) { server.signal_stop_forced }
  Signal.trap(:TERM) { server.signal_stop_graceful }

  listen_address = Riser::SocketAddress.parse(config.listen_address)
  server.start(listen_address.open_server)

  0
end
cmd_show_user_mbox(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1440
def cmd_show_user_mbox(options, args)
  svc_conf = RIMS::Service::Configuration.new
  load_service_config = false

  options.banner += ' [base directory] [username] OR -f [config.yml path] [username]'
  options.on('-f', '--config-yaml=CONFIG_FILE',
             String,
             'Load optional parameters from CONFIG_FILE.') do |path|
    svc_conf.load_yaml(path)
    load_service_config = true
  end
  options.parse!(args)

  unless (load_service_config) then
    base_dir = args.shift or raise 'need for base directory.'
    svc_conf.load(base_dir: base_dir)
  end

  username = args.shift or raise 'need for a username.'
  unique_user_id = Authentication.unique_user_id(username)
  puts svc_conf.make_key_value_store_path(MAILBOX_DATA_STRUCTURE_VERSION, unique_user_id)

  0
end
cmd_unique_user_id(options, args) click to toggle source
# File lib/rims/cmd.rb, line 1425
def cmd_unique_user_id(options, args)
  options.banner += ' [username]'
  options.parse!(args)

  if (args.length != 1) then
    raise 'need for a username.'
  end
  username = args.shift

  puts Authentication.unique_user_id(username)

  0
end
cmd_version(options, args) click to toggle source
# File lib/rims/cmd.rb, line 73
def cmd_version(options, args)
  options.parse!(args)
  puts RIMS::VERSION
  0
end

Private Instance Methods

each_message(args, verbose: false) { |msg_txt, nil| ... } click to toggle source
# File lib/rims/cmd.rb, line 1278
def each_message(args, verbose: false)
  if (args.empty?) then
    msg_txt = STDIN.read
    yield(msg_txt, nil)
    return 0
  else
    error_count = 0
    args.each_with_index do |filename, i|
      puts "progress: #{i + 1}/#{args.length}" if verbose
      begin
        msg_txt = IO.read(filename, mode: 'rb', encoding: 'ascii-8bit')
        yield(msg_txt, filename)
      rescue
        error_count += 1
        puts "failed to append message: #{filename}"
        Error.trace_error_chain($!) do |exception|
          puts "error: #{exception}"
          if ($DEBUG) then
            for frame in exception.backtrace
              puts frame
            end
          end
        end
      end
    end

    if (error_count > 0) then
      puts "#{error_count} errors!"
      return 1
    else
      return 0
    end
  end
end
imap_append(imap, mailbox, message, store_flags: [], date_time: nil, verbose: false) click to toggle source
# File lib/rims/cmd.rb, line 1269
def imap_append(imap, mailbox, message, store_flags: [], date_time: nil, verbose: false)
  puts "message date: #{date_time}" if (verbose && date_time)
  store_flags = nil if store_flags.empty?
  res = imap.append(mailbox, message, store_flags, date_time)
  puts "append: #{imap_res2str(res)}" if verbose
  nil
end
imap_res2str(imap_response) click to toggle source
# File lib/rims/cmd.rb, line 841
def imap_res2str(imap_response)
  "#{imap_response.name} #{imap_response.data.text}"
end
make_service_config(options) click to toggle source
# File lib/rims/cmd.rb, line 96
def make_service_config(options)
  build = ServiceConfigChainBuilder.new
  build.chain{|c| c.load(base_dir: Dir.getwd) }

  options.summary_width = 37
  log_level_list = %w[ debug info warn error fatal unknown ]

  options.on('-h', '--help', 'Show this message.') do
    puts options
    exit
  end
  options.on('-f', '--config-yaml=CONFIG_FILE',
             String,
             "Load optional parameters from CONFIG_FILE."
            ) do |path|
    build.chain{|c| c.load_yaml(path) }
  end
  options.on('-r', '--required-feature=FEATURE',
             String,
             "Add required feature."
            ) do |feature|
    require(feature)
    build.chain{|c| c.load(required_features: [ feature ]) }
  end
  options.on('-d', '--base-dir=DIR',
             String,
             "Directory that places log file, mailbox database, etc. default is current directory."
            ) do |path|
    build.chain{|c| c.load(base_dir: path) }
  end
  options.on('--log-file=FILE',
             String,
             "Name of log file. default is `#{Service::DEFAULT_CONFIG.make_file_logger_params[0]}'."
            ) do |path|
    build.chain{|c|
      c.load(logger: {
               file: {
                 path: path
               }
             })
    }
  end
  options.on('-l', '--log-level=LEVEL',
             log_level_list,
             "Logging level (#{log_level_list.join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_file_logger_params[-1][:level] +
             "'."
            ) do |level|
    build.chain{|c|
      c.load(logging: {
               file: {
                 level: level
               }
             })
    }
  end
  options.on('--log-shift-age=NUMBER',
             Integer,
             'Number of old log files to keep.'
            ) do |num|
    build.chain{|c|
      c.load(logging: {
               file: {
                 shift_age: num
               }
             })
    }
  end
  options.on('--log-shift-age-daily',
             'Frequency of daily log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'daily'
               }
             })
    }
  end
  options.on('--log-shift-age-weekly',
             'Frequency of weekly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'weekly'
               }
             })
    }
  end
  options.on('--log-shift-age-monthly',
             'Frequency of monthly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_age: 'monthly'
               }
             })
    }
  end
  options.on('--log-shift-size=SIZE',
             Integer,
             'Maximum logfile size.'
            ) do |size|
    build.chain{|c|
      c.load(logger: {
               file: {
                 shift_size: size
               }
             })
    }
  end
  options.on('-v', '--log-stdout=LEVEL',
             log_level_list + %w[  quiet ],
             "Stdout logging level (#{(log_level_list + %w[ quiet ]).join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_stdout_logger_params[-1][:level] +
             "'."
            ) do |level|
    if (level == 'quiet') then
      level = 'unknown'
    end
    build.chain{|c|
      c.load(logging: {
               stdout: {
                 level: level
               }
             })
    }
  end
  options.on('--protocol-log-file=FILE',
             String,
             "Name of log file. default is `#{Service::DEFAULT_CONFIG.make_protocol_logger_params[0]}'."
            ) do |path|
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 path: path
               }
             })
    }
  end
  options.on('-p', '--protocol-log-level=LEVEL',
             log_level_list,
             "Logging level (#{log_level_list.join(' ')}). default is `" +
             Service::DEFAULT_CONFIG.make_protocol_logger_params[-1][:level] +
             "'."
            ) do |level|
    build.chain{|c|
      c.load(logging: {
               protocol: {
                 level: level
               }
             })
    }
  end
  options.on('--protocol-log-shift-age=NUMBER',
             Integer,
             'Number of old log files to keep.'
            ) do |num|
    build.chain{|c|
      c.load(logging: {
               protocol: {
                 shift_age: num
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-daily',
             'Frequency of daily log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'daily'
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-weekly',
             'Frequency of weekly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'weekly'
               }
             })
    }
  end
  options.on('--protocol-log-shift-age-monthly',
             'Frequency of monthly log rotation.'
            ) do
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_age: 'monthly'
               }
             })
    }
  end
  options.on('--protocol-log-shift-size=SIZE',
             Integer,
             'Maximum logfile size.'
            ) do |size|
    build.chain{|c|
      c.load(logger: {
               protocol: {
                 shift_size: size
               }
             })
    }
  end
  options.on('--[no-]daemonize',
             "Daemonize server process. effective only with daemon command."
            ) do |daemonize|
    build.chain{|c|
      c.load(daemon: {
               daemonize: daemonize
             })
    }
  end
  options.on('--[no-]daemon-debug',
             "Debug daemon. effective only with daemon command."
            ) do |debug|
    build.chain{|c|
      c.load(daemon: {
               debug: debug
             })
    }
  end
  options.on('--daemon-umask=UMASK',
             Integer,
             "Umask(2). effective only with daemon command. default is `#{'%04o' % Service::DEFAULT_CONFIG.daemon_umask}'."
            ) do |umask|
    build.chain{|c|
      c.load(daemon: {
               umask: umask
             })
    }
  end
  options.on('--status-file=FILE',
             String,
             "Name of status file. effective only with daemon command. default is `#{Service::DEFAULT_CONFIG.status_file}'."
            ) do |path|
    build.chain{|c|
      c.load(daemon: {
               status_file: path
             })
    }
  end
  options.on('--privilege-user=USER',
             String,
             "Privilege user name or ID for server process. effective only with daemon command."
            ) do |user|
    build.chain{|c|
      c.load(daemon: {
               server_privileged_user: user
             })
    }
  end
  options.on('--privilege-group=GROUP',
             String,
             "Privilege group name or ID for server process. effective only with daemon command."
            ) do |group|
    build.chain{|c|
      c.load(daemon: {
               server_privileged_group: group
             })
    }
  end
  options.on('-s', '--listen=HOST_PORT',
             String,
             "Listen socket address. default is `#{Service::DEFAULT_CONFIG.listen_address}'"
            ) do |host_port|
    build.chain{|c|
      c.load(server: {
               listen_address: host_port
             })
    }
  end
  options.on('--accept-polling-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(server: {
               accept_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--process-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(server: {
               process_num: num
             })
    }
  end
  options.on('--process-queue-size=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(server: {
               process_queue_size: size
             })
    }
  end
  options.on('--process-queue-polling-timeout=SECONDS',
             Float) do |seconds|
    build.chain{|c|
      c.load(server: {
               process_queue_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--process-send-io-polling-timeout=SECONDS',
             Float) do |seconds|
    build.chain{|c|
      c.load(server: {
               process_send_io_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--thread-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(server: {
               thread_num: num
             })
    }
  end
  options.on('--thread-queue-size=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(server: {
               thread_queue_size: size
             })
    }
  end
  options.on('--thread-queue-polling-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(server: {
               thread_queue_polling_timeout_seconds: seconds
             })
    }
  end
  options.on('--send-buffer-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(connection: {
               send_buffer_limit_size: size
             })
    }
  end
  options.on('--read-polling-interval=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(connection: {
               read_polling_interval_seconds: seconds
             })
    }
  end
  options.on('--command-wait-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(connection: {
               command_wait_timeout_seconds: seconds
             })
    }
  end
  options.on('--line-length-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               line_length_limit: size
             })
    }
  end
  options.on('--literal-size-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               literal_size_limit: size
             })
    }
  end
  options.on('--command-size-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(protocol: {
               command_size_limit: size
             })
    }
  end
  options.on('--[no-]use-default-charset-aliases'
            ) do |use_default_aliases|
    build.chain{|c|
      c.load(charset: {
               use_default_aliases: use_default_aliases
             })
    }
  end
  options.on('--add-charset-alias=NAME_TO_ENCODING',
             /\A \S+,\S+ \z/x,
             "Set the alias name and encoding separated with comma (,)."
            ) do |name_to_encoding|
    name, encoding = name_to_encoding.split(',', 2)
    build.chain{|c|
      c.load(charset: {
               aliases: [
                 { name: name, encoding: encoding }
               ]
             })
    }
  end
  options.on('--[no-]replace-charset-invalid'
            ) do |replace|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replace_invalid_byte_sequence: replace
               }
             })
    }
  end
  options.on('--[no-]replace-charset-undef'
            ) do |replace|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replace_undefined_character: replace
               }
             })
    }
  end
  options.on('--charset-replaced-mark=MARK',
             String
            ) do |mark|
    build.chain{|c|
      c.load(charset: {
               convert_options: {
                 replaced_mark: mark
               }
             })
    }
  end
  options.on('--drb-process-num=NUMBER',
             Integer
            ) do |num|
    build.chain{|c|
      c.load(drb_services: {
               process_num: num
             })
    }
  end
  options.on('--drb-load-limit=SIZE',
             Integer
            ) do |size|
    build.chain{|c|
      c.load(drb_services: {
               load_limit: size
             })
    }
  end
  options.on('--bulk-response-count=COUNT',
             Integer) do |count|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 bulk_response_count: count
               }
             })
    }
  end
  options.on('--bulk-response-size=SIZE',
             Integer) do |size|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 bulk_response_size: size
               }
             })
    }
  end
  options.on('--read-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 read_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--write-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 write_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--cleanup-write-lock-timeout=SECONDS',
             Float
            ) do |seconds|
    build.chain{|c|
      c.load(drb_services: {
               engine: {
                 cleanup_write_lock_timeout_seconds: seconds
               }
             })
    }
  end
  options.on('--meta-kvs-type=TYPE',
             "Choose key-value store type of mailbox meta-data database. default is `" +
             KeyValueStore::FactoryBuilder.plug_in_names[0] +
             "'."
            ) do |kvs_type|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 type: kvs_type
               }
             })
    }
  end
  options.on('--meta-kvs-config=JSON_DATA',
             JSON,
             "Configuration for key-value store of mailbox meta-data database."
            ) do |json_data|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 configuration: json_data
               }
             })
    }
  end
  options.on('--[no-]use-meta-kvs-checksum',
             "Enable/disable data checksum at key-value store of mailbox meta-data database. default is " +
             if (Service::DEFAULT_CONFIG.make_meta_key_value_store_params.middleware_list.include? Checksum_KeyValueStore) then
               'enabled'
             else
               'disbled'
             end +
             "."
            ) do |use_checksum|
    build.chain{|c|
      c.load(storage: {
               meta_key_value_store: {
                 use_checksum: use_checksum
               }
             })
    }
  end
  options.on('--text-kvs-type=TYPE',
             "Choose key-value store type of mailbox text-data database. default is `" +
             KeyValueStore::FactoryBuilder.plug_in_names[0] +
             "'."
            ) do |kvs_type|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 type: kvs_type
               }
             })
    }
  end
  options.on('--text-kvs-config=JSON_DATA',
             JSON,
             "Configuration for key-value store of mailbox text-data database."
            ) do |json_data|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 configuration: json_data
               }
             })
    }
  end
  options.on('--[no-]use-text-kvs-checksum',
             "Enable/disable data checksum at key-value store of mailbox text-data database. default is " +
             if (Service::DEFAULT_CONFIG.make_text_key_value_store_params.middleware_list.include? Checksum_KeyValueStore) then
               'enabled'
             else
               'disbled'
             end +
             "."
            ) do |use_checksum|
    build.chain{|c|
      c.load(storage: {
               text_key_value_store: {
                 use_checksum: use_checksum
               }
             })
    }
  end
  options.on('--auth-hostname=HOSTNAME',
             String,
             "Hostname to authenticate with cram-md5. default is `#{Service::DEFAULT_CONFIG.make_authentication.hostname}'."
            ) do |hostname|
    build.chain{|c|
      c.load(authentication: {
               hostname: hostname
             })
    }
  end
  options.on('--passwd-config=TYPE_JSONDATA',
             /([^:]+)(?::(.*))?/,
             "Password source type and configuration. format is `[type]:[json_data]'."
            ) do |_, type, json_data|
    build.chain{|c|
      c.load(authentication: {
               password_sources: [
                 { type: type,
                   configuration: JSON.load(json_data)
                 }
               ]
             })
    }
  end
  options.on('--passwd-file=TYPE_FILE',
             /([^:]+):(.+)/,
             "Password source type and configuration file. format is `[type]:[file]'."
            ) do |_, type, path|
    build.chain{|c|
      c.load(authentication: {
               password_sources: [
                 { type: type,
                   configuration_file: path
                 }
               ]
             })
    }
  end
  options.on('--mail-delivery-user=USERNAME',
             String,
             "Username authorized to deliver messages to any mailbox. default is `#{Service::DEFAULT_CONFIG.mail_delivery_user}'"
            ) do |username|
    build.chain{|c|
      c.load(authorization: {
               mail_delivery_user: username
             })
    }
  end

  options.on('--imap-host=HOSTNAME',
             String,
             'Deplicated.'
            ) do |host|
    warn("warning: `--imap-host=HOSTNAME' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(imap_host: host) }
  end
  options.on('--imap-port=PORT',
             String,
             'Deplicated.'
            ) do |value|
    warn("warning: `--imap-port=PORT' is deplicated option and should use `--listen=HOST_PORT'.")
    if (value =~ /\A \d+ \z/x) then
      port_number = value.to_i
      build.chain{|c| c.load(imap_port: port_number) }
    else
      service_name = value
      build.chain{|c| c.load(imap_port: service_name) }
    end
  end
  options.on('--ip-addr=IP_ADDR',
             String,
             'Deplicated.'
            ) do |ip_addr|
    warn("warning: `--ip-addr=IP_ADDR' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(ip_addr: ip_addr) }
  end
  options.on('--ip-port=PORT',
             Integer,
             'Deplicated.'
            ) do |port|
    warn("warning: `--ip-port=PORT' is deplicated option and should use `--listen=HOST_PORT'.")
    build.chain{|c| c.load(ip_port: port) }
  end
  options.on('--kvs-type=TYPE',
             'Deplicated.'
            ) do |kvs_type|
    warn("warning: `--kvs-type=TYPE' is deplicated option and should use `--meta-kvs-type=TYPE' or `--text-kvs-type=TYPE'.")
    build.chain{|c| c.load(key_value_store_type: kvs_type) }
  end
  options.on('--[no-]use-kvs-cksum',
             'Deplicated.'
            ) do |use_checksum|
    warn("warning: `--[no-]use-kvs-cksum' is deplicated option and should use `--[no-]use-meta-kvs-checksum' or `--[no-]use-text-kvs-checksum'.")
    build.chain{|c| c.load(use_key_value_store_checksum: use_checksum) }
  end
  options.on('-u', '--username=NAME',
             String,
             'Deplicated.'
            ) do |name|
    warn("warning: `--username=NAME' is deplicated option and should use `--passwd-config=TYPE_JSONDATA' or `--passwd-file=TYPE_FILE'.")
    build.chain{|c| c.load(username: name) }
  end
  options.on('-w', '--password=PASS',
             String,
             'Deplicated.'
            ) do |pass|
    warn("warning: `--password=PASS' is deplicated option and should use `--passwd-config=TYPE_JSONDATA' or `--passwd-file=TYPE_FILE'.")
    build.chain{|c| c.load(password: pass) }
  end

  build
end
run_cmd(args) click to toggle source
# File lib/rims/cmd.rb, line 31
def run_cmd(args)
  options = OptionParser.new
  if (args.empty?) then
    cmd_help(options, args)
    return 1
  end

  cmd_name = args.shift
  pp cmd_name if $DEBUG
  pp args if $DEBUG

  cmd_entry = CMDs[cmd_name] or raise "unknown command: #{cmd_name}. Run `#{options.program_name} help'."
  options.program_name += " #{cmd_name}"
  send(cmd_entry[:function], options, args)
end