rims 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +19 -17
- data/ChangeLog +36 -0
- data/Rakefile +70 -1
- data/lib/rims.rb +3 -5
- data/lib/rims/auth.rb +5 -7
- data/lib/rims/cmd.rb +758 -188
- data/lib/rims/error.rb +8 -10
- data/lib/rims/kvs.rb +4 -0
- data/lib/rims/lock.rb +4 -1
- data/lib/rims/protocol.rb +7 -1
- data/lib/rims/protocol/decoder.rb +24 -7
- data/lib/rims/protocol/parser.rb +1 -1
- data/lib/rims/service.rb +953 -0
- data/lib/rims/version.rb +1 -1
- data/rims.gemspec +2 -0
- data/test/cmd/test_command.rb +999 -0
- data/test/test_auth.rb +3 -1
- data/test/test_cmd.rb +36 -0
- data/test/test_error.rb +22 -78
- data/test/test_protocol_auth.rb +3 -1
- data/test/test_protocol_decoder.rb +6 -3
- data/test/test_service.rb +1120 -0
- metadata +38 -11
- data/lib/rims/daemon.rb +0 -338
- data/lib/rims/server.rb +0 -567
- data/test/test_config.rb +0 -533
- data/test/test_daemon_status_file.rb +0 -169
- data/test/test_daemon_waitpid.rb +0 -72
    
        data/lib/rims/error.rb
    CHANGED
    
    | @@ -2,17 +2,15 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module RIMS
         | 
| 4 4 | 
             
              class Error < StandardError
         | 
| 5 | 
            -
                def self. | 
| 6 | 
            -
                   | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
                     | 
| 10 | 
            -
             | 
| 11 | 
            -
                      nil
         | 
| 12 | 
            -
                    end
         | 
| 13 | 
            -
                  else
         | 
| 14 | 
            -
                    yield
         | 
| 5 | 
            +
                def self.trace_error_chain(exception)
         | 
| 6 | 
            +
                  return enum_for(:trace_error_chain, exception) unless block_given?
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  while (exception)
         | 
| 9 | 
            +
                    yield(exception)
         | 
| 10 | 
            +
                    exception = exception.cause
         | 
| 15 11 | 
             
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  nil
         | 
| 16 14 | 
             
                end
         | 
| 17 15 | 
             
              end
         | 
| 18 16 | 
             
            end
         | 
    
        data/lib/rims/kvs.rb
    CHANGED
    
    
    
        data/lib/rims/lock.rb
    CHANGED
    
    | @@ -136,7 +136,10 @@ module RIMS | |
| 136 136 | 
             
                        retry
         | 
| 137 137 | 
             
                      rescue
         | 
| 138 138 | 
             
                        logger.error('unexpected error at a detached thread and give up to retry write-lock timeout error.')
         | 
| 139 | 
            -
                         | 
| 139 | 
            +
                        Error.trace_error_chain($!) do |exception|
         | 
| 140 | 
            +
                          logger.error(exception)
         | 
| 141 | 
            +
                        end
         | 
| 142 | 
            +
             | 
| 140 143 | 
             
                        $!
         | 
| 141 144 | 
             
                      end
         | 
| 142 145 | 
             
                    }
         | 
    
        data/lib/rims/protocol.rb
    CHANGED
    
    | @@ -31,13 +31,19 @@ module RIMS | |
| 31 31 | 
             
                end
         | 
| 32 32 | 
             
                module_function :compile_wildcard
         | 
| 33 33 |  | 
| 34 | 
            +
                IO_DATA_DUMP = false        # true for debug
         | 
| 35 | 
            +
             | 
| 34 36 | 
             
                def io_data_log(str)
         | 
| 35 37 | 
             
                  s = '<'
         | 
| 36 38 | 
             
                  s << str.encoding.to_s
         | 
| 37 39 | 
             
                  if (str.ascii_only?) then
         | 
| 38 40 | 
             
                    s << ':ascii_only'
         | 
| 39 41 | 
             
                  end
         | 
| 40 | 
            -
                   | 
| 42 | 
            +
                  if (IO_DATA_DUMP) then
         | 
| 43 | 
            +
                    s << '> ' << str.inspect
         | 
| 44 | 
            +
                  else
         | 
| 45 | 
            +
                    s << '> ' << str.bytesize.to_s << ' octets'
         | 
| 46 | 
            +
                  end
         | 
| 41 47 | 
             
                end
         | 
| 42 48 | 
             
                module_function :io_data_log
         | 
| 43 49 |  | 
| @@ -37,7 +37,9 @@ module RIMS | |
| 37 37 | 
             
                        atom_list = request_reader.read_command
         | 
| 38 38 | 
             
                      rescue
         | 
| 39 39 | 
             
                        logger.error('invalid client command.')
         | 
| 40 | 
            -
                         | 
| 40 | 
            +
                        Error.trace_error_chain($!) do |exception|
         | 
| 41 | 
            +
                          logger.error(exception)
         | 
| 42 | 
            +
                        end
         | 
| 41 43 | 
             
                        response_write.call([ "* BAD client command syntax error\r\n" ])
         | 
| 42 44 | 
             
                        next
         | 
| 43 45 | 
             
                      end
         | 
| @@ -46,7 +48,16 @@ module RIMS | |
| 46 48 |  | 
| 47 49 | 
             
                      tag, command, *opt_args = atom_list
         | 
| 48 50 | 
             
                      logger.info("client command: #{tag} #{command}")
         | 
| 49 | 
            -
                       | 
| 51 | 
            +
                      if (logger.debug?) then
         | 
| 52 | 
            +
                        case (command.upcase)
         | 
| 53 | 
            +
                        when 'LOGIN'
         | 
| 54 | 
            +
                          log_opt_args = opt_args.dup
         | 
| 55 | 
            +
                          log_opt_args[-1] = '********'
         | 
| 56 | 
            +
                        else
         | 
| 57 | 
            +
                          log_opt_args = opt_args
         | 
| 58 | 
            +
                        end
         | 
| 59 | 
            +
                        logger.debug("client command parameter: #{log_opt_args.inspect}")
         | 
| 60 | 
            +
                      end
         | 
| 50 61 |  | 
| 51 62 | 
             
                      begin
         | 
| 52 63 | 
             
                        case (command.upcase)
         | 
| @@ -126,7 +137,9 @@ module RIMS | |
| 126 137 | 
             
                        end
         | 
| 127 138 | 
             
                      rescue
         | 
| 128 139 | 
             
                        logger.error('unexpected error.')
         | 
| 129 | 
            -
                         | 
| 140 | 
            +
                        Error.trace_error_chain($!) do |exception|
         | 
| 141 | 
            +
                          logger.error(exception)
         | 
| 142 | 
            +
                        end
         | 
| 130 143 | 
             
                        response_write.call([ "#{tag} BAD unexpected error\r\n" ])
         | 
| 131 144 | 
             
                      end
         | 
| 132 145 |  | 
| @@ -139,7 +152,7 @@ module RIMS | |
| 139 152 |  | 
| 140 153 | 
             
                    nil
         | 
| 141 154 | 
             
                  ensure
         | 
| 142 | 
            -
                     | 
| 155 | 
            +
                    decoder.cleanup
         | 
| 143 156 | 
             
                  end
         | 
| 144 157 |  | 
| 145 158 | 
             
                  def initialize(auth, logger)
         | 
| @@ -158,7 +171,9 @@ module RIMS | |
| 158 171 | 
             
                      rescue
         | 
| 159 172 | 
             
                        raise if ($!.class.name =~ /AssertionFailedError/)
         | 
| 160 173 | 
             
                        @logger.error('internal server error.')
         | 
| 161 | 
            -
                         | 
| 174 | 
            +
                        Error.trace_error_chain($!) do |exception|
         | 
| 175 | 
            +
                          @logger.error(exception)
         | 
| 176 | 
            +
                        end
         | 
| 162 177 | 
             
                        res << "#{tag} BAD internal server error\r\n"
         | 
| 163 178 | 
             
                      end
         | 
| 164 179 | 
             
                    }
         | 
| @@ -183,7 +198,9 @@ module RIMS | |
| 183 198 | 
             
                    rescue
         | 
| 184 199 | 
             
                      raise if ($!.class.name =~ /AssertionFailedError/)
         | 
| 185 200 | 
             
                      @logger.error('internal server error.')
         | 
| 186 | 
            -
                       | 
| 201 | 
            +
                      Error.trace_error_chain($!) do |exception|
         | 
| 202 | 
            +
                        @logger.error(exception)
         | 
| 203 | 
            +
                      end
         | 
| 187 204 | 
             
                      yield([ "#{tag} BAD internal server error\r\n" ])
         | 
| 188 205 | 
             
                    end
         | 
| 189 206 | 
             
                  end
         | 
| @@ -245,7 +262,7 @@ module RIMS | |
| 245 262 |  | 
| 246 263 | 
             
                class InitialDecoder < Decoder
         | 
| 247 264 | 
             
                  def initialize(mail_store_pool, auth, logger,
         | 
| 248 | 
            -
                                 mail_delivery_user:  | 
| 265 | 
            +
                                 mail_delivery_user: Service::DEFAULT_CONFIG.mail_delivery_user,
         | 
| 249 266 | 
             
                                 write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
         | 
| 250 267 | 
             
                                 **next_decoder_optional)
         | 
| 251 268 | 
             
                    super(auth, logger)
         | 
    
        data/lib/rims/protocol/parser.rb
    CHANGED
    
    | @@ -190,7 +190,7 @@ module RIMS | |
| 190 190 |  | 
| 191 191 | 
             
                  def read_client_response_data_plain(inline_client_response_data_base64)
         | 
| 192 192 | 
             
                    if (inline_client_response_data_base64) then
         | 
| 193 | 
            -
                      @logger.debug("authenticate command: inline client response data: #{inline_client_response_data_base64}") if @logger.debug?
         | 
| 193 | 
            +
                      @logger.debug("authenticate command: inline client response data: #{Protocol.io_data_log(inline_client_response_data_base64)}") if @logger.debug?
         | 
| 194 194 | 
             
                      Protocol.decode_base64(inline_client_response_data_base64)
         | 
| 195 195 | 
             
                    else
         | 
| 196 196 | 
             
                      read_client_response_data
         | 
    
        data/lib/rims/service.rb
    ADDED
    
    | @@ -0,0 +1,953 @@ | |
| 1 | 
            +
            # -*- coding: utf-8 -*-
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'etc'
         | 
| 4 | 
            +
            require 'json'
         | 
| 5 | 
            +
            require 'logger'
         | 
| 6 | 
            +
            require 'logger/joint'
         | 
| 7 | 
            +
            require 'pathname'
         | 
| 8 | 
            +
            require 'riser'
         | 
| 9 | 
            +
            require 'socket'
         | 
| 10 | 
            +
            require 'yaml'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            module RIMS
         | 
| 13 | 
            +
              class Service
         | 
| 14 | 
            +
                class Configuration
         | 
| 15 | 
            +
                  class << self
         | 
| 16 | 
            +
                    def stringify_symbol(collection)
         | 
| 17 | 
            +
                      case (collection)
         | 
| 18 | 
            +
                      when Hash
         | 
| 19 | 
            +
                        Hash[collection.map{|key, value| [ stringify_symbol(key), stringify_symbol(value) ] }]
         | 
| 20 | 
            +
                      when Array
         | 
| 21 | 
            +
                        collection.map{|i| stringify_symbol(i) }
         | 
| 22 | 
            +
                      else
         | 
| 23 | 
            +
                        case (value = collection)
         | 
| 24 | 
            +
                        when Symbol
         | 
| 25 | 
            +
                          value.to_s
         | 
| 26 | 
            +
                        else
         | 
| 27 | 
            +
                          value
         | 
| 28 | 
            +
                        end
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def update(dest, other)
         | 
| 33 | 
            +
                      case (dest)
         | 
| 34 | 
            +
                      when Hash
         | 
| 35 | 
            +
                        unless (other.is_a? Hash) then
         | 
| 36 | 
            +
                          raise ArgumentError, 'hash can only be updated with hash.'
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
                        for key, value in other
         | 
| 39 | 
            +
                          dest[key] = update(dest[key], value)
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
                        dest
         | 
| 42 | 
            +
                      when Array
         | 
| 43 | 
            +
                        if (other.is_a? Array) then
         | 
| 44 | 
            +
                          dest.concat(other)
         | 
| 45 | 
            +
                        else
         | 
| 46 | 
            +
                          other
         | 
| 47 | 
            +
                        end
         | 
| 48 | 
            +
                      else
         | 
| 49 | 
            +
                        other
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    def get_configuration(collection, base_dir=nil)
         | 
| 54 | 
            +
                      if ((collection.key? 'configuration') && (collection.key? 'configuration_file')) then
         | 
| 55 | 
            +
                        raise KeyError, 'configuration conflict: configuration, configuraion_file'
         | 
| 56 | 
            +
                      end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      if (collection.key? 'configuration') then
         | 
| 59 | 
            +
                        collection['configuration']
         | 
| 60 | 
            +
                      elsif (collection.key? 'configuration_file') then
         | 
| 61 | 
            +
                        configuration_file_path = Pathname(collection['configuration_file'])
         | 
| 62 | 
            +
                        if (configuration_file_path.relative?) then
         | 
| 63 | 
            +
                          base_dir or raise ArgumentError, 'need for base_dir.'
         | 
| 64 | 
            +
                          configuration_file_path = base_dir + configuration_file_path # expect base_dir to be Pathname
         | 
| 65 | 
            +
                        end
         | 
| 66 | 
            +
                        YAML.load_file(configuration_file_path.to_s)
         | 
| 67 | 
            +
                      else
         | 
| 68 | 
            +
                        {}
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  def initialize
         | 
| 74 | 
            +
                    @config = {}
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  def load(config, load_path=nil)
         | 
| 78 | 
            +
                    stringified_config = self.class.stringify_symbol(config)
         | 
| 79 | 
            +
                    if (stringified_config.key? 'base_dir') then
         | 
| 80 | 
            +
                      base_dir = Pathname(stringified_config['base_dir'])
         | 
| 81 | 
            +
                      if (load_path && base_dir.relative?) then
         | 
| 82 | 
            +
                        stringified_config['base_dir'] = Pathname(load_path) + base_dir
         | 
| 83 | 
            +
                      else
         | 
| 84 | 
            +
                        stringified_config['base_dir'] = base_dir
         | 
| 85 | 
            +
                      end
         | 
| 86 | 
            +
                    elsif (load_path) then
         | 
| 87 | 
            +
                      stringified_config['base_dir'] = Pathname(load_path)
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                    self.class.update(@config, stringified_config)
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    self
         | 
| 92 | 
            +
                  end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  # configuration example.
         | 
| 95 | 
            +
                  #   required_features:
         | 
| 96 | 
            +
                  #     - rims/qdbm
         | 
| 97 | 
            +
                  #     - rims/passwd/ldap
         | 
| 98 | 
            +
                  #   logging:
         | 
| 99 | 
            +
                  #     file:
         | 
| 100 | 
            +
                  #       path: rims.log
         | 
| 101 | 
            +
                  #       shift_age: 10
         | 
| 102 | 
            +
                  #       shift_size: 1048576
         | 
| 103 | 
            +
                  #       level: debug
         | 
| 104 | 
            +
                  #       datetime_format: %Y-%m-%d %H:%M:%S
         | 
| 105 | 
            +
                  #       shift_period_suffix: %Y%m%d
         | 
| 106 | 
            +
                  #     stdout:
         | 
| 107 | 
            +
                  #       level: info
         | 
| 108 | 
            +
                  #       datetime_format: %Y-%m-%d %H:%M:%S
         | 
| 109 | 
            +
                  #     protocol:
         | 
| 110 | 
            +
                  #       # default is not output.
         | 
| 111 | 
            +
                  #       # to output, set the log level to info or less.
         | 
| 112 | 
            +
                  #       path: protocol.log
         | 
| 113 | 
            +
                  #       shift_age: 10
         | 
| 114 | 
            +
                  #       shift_size: 1048576
         | 
| 115 | 
            +
                  #       level: info
         | 
| 116 | 
            +
                  #       datetime_format: %Y-%m-%d %H:%M:%S
         | 
| 117 | 
            +
                  #       shift_period_suffix: %Y%m%d
         | 
| 118 | 
            +
                  #   daemon:
         | 
| 119 | 
            +
                  #     daemonize: true
         | 
| 120 | 
            +
                  #     debug: false
         | 
| 121 | 
            +
                  #     status_file: rims.pid
         | 
| 122 | 
            +
                  #     server_polling_interval_seconds: 3
         | 
| 123 | 
            +
                  #     server_privileged_user: nobody
         | 
| 124 | 
            +
                  #     server_privileged_group: nogroup
         | 
| 125 | 
            +
                  #   server:
         | 
| 126 | 
            +
                  #     listen_address:
         | 
| 127 | 
            +
                  #       # see `Riser::SocketAddress.parse' for address format
         | 
| 128 | 
            +
                  #       type: tcp
         | 
| 129 | 
            +
                  #       host: 0.0.0.0
         | 
| 130 | 
            +
                  #       port: 143
         | 
| 131 | 
            +
                  #       backlog: 64
         | 
| 132 | 
            +
                  #     accept_polling_timeout_seconds: 0.1
         | 
| 133 | 
            +
                  #     thread_num: 20
         | 
| 134 | 
            +
                  #     thread_queue_size: 20
         | 
| 135 | 
            +
                  #     thread_queue_polling_timeout_seconds: 0.1
         | 
| 136 | 
            +
                  #     send_buffer_limit_size: 16384
         | 
| 137 | 
            +
                  #   openssl:
         | 
| 138 | 
            +
                  #     use_ssl: true
         | 
| 139 | 
            +
                  #     ssl_context: |
         | 
| 140 | 
            +
                  #       # this entry is evaluated in an anonymous ruby module
         | 
| 141 | 
            +
                  #       # including OpenSSL to initialize the SSLContext used
         | 
| 142 | 
            +
                  #       # for TLS connection.
         | 
| 143 | 
            +
                  #       # SSLContext object is stored at `_'.
         | 
| 144 | 
            +
                  #       # Pathname object is stored at `base_dir'.
         | 
| 145 | 
            +
                  #       _.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_default.cert").read)
         | 
| 146 | 
            +
                  #       _.key = PKey.read((base_dir / "tls_secret" / "server.priv_key").read)
         | 
| 147 | 
            +
                  #       sni_tbl = {
         | 
| 148 | 
            +
                  #         "imap.example.com"  => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap.cert").read) },
         | 
| 149 | 
            +
                  #         "imap2.example.com" => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap2.cert").read) },
         | 
| 150 | 
            +
                  #         "imap3.example.com" => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap3.cert").read) }
         | 
| 151 | 
            +
                  #       }
         | 
| 152 | 
            +
                  #       _.servername_cb = lambda{|ssl_socket, hostname| sni_tbl[hostname.downcase] }
         | 
| 153 | 
            +
                  #   lock:
         | 
| 154 | 
            +
                  #     read_lock_timeout_seconds: 30
         | 
| 155 | 
            +
                  #     write_lock_timeout_seconds: 30
         | 
| 156 | 
            +
                  #     cleanup_write_lock_timeout_seconds: 1
         | 
| 157 | 
            +
                  #   storage:
         | 
| 158 | 
            +
                  #     meta_key_value_store:
         | 
| 159 | 
            +
                  #       type: qdbm_depot
         | 
| 160 | 
            +
                  #       configuration:
         | 
| 161 | 
            +
                  #         bnum 1200000
         | 
| 162 | 
            +
                  #       use_checksum: true
         | 
| 163 | 
            +
                  #     text_key_value_store:
         | 
| 164 | 
            +
                  #       type: qdbm_curia
         | 
| 165 | 
            +
                  #       configuration_file: text_kvs_config.yml
         | 
| 166 | 
            +
                  #       use_checksum: true
         | 
| 167 | 
            +
                  #   authentication:
         | 
| 168 | 
            +
                  #     hostname: imap.example.com
         | 
| 169 | 
            +
                  #     password_sources:
         | 
| 170 | 
            +
                  #       - type: plain
         | 
| 171 | 
            +
                  #         configuration:
         | 
| 172 | 
            +
                  #           - user: alice
         | 
| 173 | 
            +
                  #             pass: open sesame
         | 
| 174 | 
            +
                  #           - user: bob
         | 
| 175 | 
            +
                  #             pass: Z1ON0101
         | 
| 176 | 
            +
                  #       - type: hash
         | 
| 177 | 
            +
                  #         configuration_file: passwd_hash.yml
         | 
| 178 | 
            +
                  #       - type: ldap
         | 
| 179 | 
            +
                  #         configuration:
         | 
| 180 | 
            +
                  #           ldap_uri: ldap://ldap.example.com/ou=user,o=example,dc=nodomain?uid?one?(memberOf=cn=imap,ou=group,o=example,dc=nodomain)
         | 
| 181 | 
            +
                  #   authorization:
         | 
| 182 | 
            +
                  #     mail_delivery_user: "#postman"
         | 
| 183 | 
            +
                  #
         | 
| 184 | 
            +
                  # backward compatibility for required_features.
         | 
| 185 | 
            +
                  #   load_libraries:
         | 
| 186 | 
            +
                  #     - rims/qdbm
         | 
| 187 | 
            +
                  #     - rims/passwd/ldap
         | 
| 188 | 
            +
                  #
         | 
| 189 | 
            +
                  # backward compatibility for logging.
         | 
| 190 | 
            +
                  #   log_file: rims.log
         | 
| 191 | 
            +
                  #   log_level: debug
         | 
| 192 | 
            +
                  #   log_shift_age: 10
         | 
| 193 | 
            +
                  #   log_shift_size: 1048576
         | 
| 194 | 
            +
                  #   log_stdout: info
         | 
| 195 | 
            +
                  #
         | 
| 196 | 
            +
                  # backward compatibility for daemon.
         | 
| 197 | 
            +
                  #   process_privilege_user: nobody
         | 
| 198 | 
            +
                  #   process_privilege_group: nogroup
         | 
| 199 | 
            +
                  #
         | 
| 200 | 
            +
                  # backward compatibility for server.
         | 
| 201 | 
            +
                  #   imap_host: 0.0.0.0
         | 
| 202 | 
            +
                  #   imap_port: 143
         | 
| 203 | 
            +
                  #   ip_addr: 0.0.0.0
         | 
| 204 | 
            +
                  #   ip_port: 143
         | 
| 205 | 
            +
                  #   send_buffer_limit: 16384
         | 
| 206 | 
            +
                  #
         | 
| 207 | 
            +
                  # backward compatibility for lock.
         | 
| 208 | 
            +
                  #   read_lock_timeout_seconds: 30
         | 
| 209 | 
            +
                  #   write_lock_timeout_seconds: 30
         | 
| 210 | 
            +
                  #   cleanup_write_lock_timeout_seconds: 1
         | 
| 211 | 
            +
                  #
         | 
| 212 | 
            +
                  # backward compatibility for storage.
         | 
| 213 | 
            +
                  #   key_value_store_type: gdbm
         | 
| 214 | 
            +
                  #   use_key_value_store_checksum: true
         | 
| 215 | 
            +
                  #   meta_key_value_store:
         | 
| 216 | 
            +
                  #     plug_in: qdbm_depot
         | 
| 217 | 
            +
                  #     configuration:
         | 
| 218 | 
            +
                  #       bnum 1200000
         | 
| 219 | 
            +
                  #     use_checksum: true
         | 
| 220 | 
            +
                  #   text_key_value_store:
         | 
| 221 | 
            +
                  #     plug_in: qdbm_curia
         | 
| 222 | 
            +
                  #     configuration_file: text_kvs_config.yml
         | 
| 223 | 
            +
                  #     use_checksum: true
         | 
| 224 | 
            +
                  #
         | 
| 225 | 
            +
                  # backward compatibility for authentication.
         | 
| 226 | 
            +
                  #   hostname: imap.example.com
         | 
| 227 | 
            +
                  #   username: alice
         | 
| 228 | 
            +
                  #   password: open sesame
         | 
| 229 | 
            +
                  #   user_list:
         | 
| 230 | 
            +
                  #     - user: alice
         | 
| 231 | 
            +
                  #       pass: open sesame
         | 
| 232 | 
            +
                  #     - user: bob
         | 
| 233 | 
            +
                  #       pass: Z1ON0101
         | 
| 234 | 
            +
                  #   authentication:
         | 
| 235 | 
            +
                  #     - plug_in: plain
         | 
| 236 | 
            +
                  #       configuration:
         | 
| 237 | 
            +
                  #         - user: alice
         | 
| 238 | 
            +
                  #           pass: open sesame
         | 
| 239 | 
            +
                  #         - user: bob
         | 
| 240 | 
            +
                  #           pass: Z1ON0101
         | 
| 241 | 
            +
                  #     - plug_in: hash
         | 
| 242 | 
            +
                  #       configuration_file: passwd_hash.yml
         | 
| 243 | 
            +
                  #     - plug_in: ldap
         | 
| 244 | 
            +
                  #       configuration:
         | 
| 245 | 
            +
                  #         ldap_uri: ldap://ldap.example.com/ou=user,o=example,dc=nodomain?uid?one?(memberOf=cn=imap,ou=group,o=example,dc=nodomain)
         | 
| 246 | 
            +
                  #
         | 
| 247 | 
            +
                  # backward compatibility for authorization.
         | 
| 248 | 
            +
                  #   mail_delivery_user: "#postman"
         | 
| 249 | 
            +
                  #
         | 
| 250 | 
            +
                  def load_yaml(path)
         | 
| 251 | 
            +
                    load(YAML.load_file(path), File.dirname(path))
         | 
| 252 | 
            +
                    self
         | 
| 253 | 
            +
                  end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                  def require_features
         | 
| 256 | 
            +
                    # built-in plug-in
         | 
| 257 | 
            +
                    require 'rims/gdbm_kvs'
         | 
| 258 | 
            +
                    require 'rims/passwd'
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                    if (feature_list = get_required_features) then
         | 
| 261 | 
            +
                      for feature in feature_list
         | 
| 262 | 
            +
                        require(feature)
         | 
| 263 | 
            +
                      end
         | 
| 264 | 
            +
                    end
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                    nil
         | 
| 267 | 
            +
                  end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                  def get_required_features
         | 
| 270 | 
            +
                    @config.dig('required_features') ||
         | 
| 271 | 
            +
                      @config.dig('load_libraries') || # for backward compatibility
         | 
| 272 | 
            +
                      []
         | 
| 273 | 
            +
                  end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                  def base_dir
         | 
| 276 | 
            +
                    @config['base_dir'] or raise KeyError, 'not defined base_dir.'
         | 
| 277 | 
            +
                  end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                  def get_configuration(collection)
         | 
| 280 | 
            +
                    if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
         | 
| 281 | 
            +
                      self.class.get_configuration(collection, base_dir)
         | 
| 282 | 
            +
                    else
         | 
| 283 | 
            +
                      self.class.get_configuration(collection)
         | 
| 284 | 
            +
                    end
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
                  private :get_configuration
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                  # return parameters for Logger.new
         | 
| 289 | 
            +
                  def make_file_logger_params
         | 
| 290 | 
            +
                    log_path = Pathname(@config.dig('logging', 'file', 'path') ||
         | 
| 291 | 
            +
                                        @config.dig('log_file') || # for backward compatibility
         | 
| 292 | 
            +
                                        'rims.log')
         | 
| 293 | 
            +
                    if (log_path.relative?) then
         | 
| 294 | 
            +
                      if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
         | 
| 295 | 
            +
                        log_path = base_dir + log_path
         | 
| 296 | 
            +
                      end
         | 
| 297 | 
            +
                    end
         | 
| 298 | 
            +
                    logger_params = [ log_path.to_s ]
         | 
| 299 | 
            +
             | 
| 300 | 
            +
                    shift_age = @config.dig('logging', 'file', 'shift_age') ||
         | 
| 301 | 
            +
                                @config.dig('log_shift_age') # for backward compatibility
         | 
| 302 | 
            +
                    shift_size = @config.dig('logging', 'file', 'shift_size') ||
         | 
| 303 | 
            +
                                 @config.dig('log_shift_size') # for backward compatibility
         | 
| 304 | 
            +
                    if (shift_size) then
         | 
| 305 | 
            +
                      logger_params << (shift_age || 0)
         | 
| 306 | 
            +
                      logger_params << shift_size
         | 
| 307 | 
            +
                    elsif (shift_age) then
         | 
| 308 | 
            +
                      logger_params << shift_age
         | 
| 309 | 
            +
                    end
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                    kw_args = {}
         | 
| 312 | 
            +
                    kw_args[:level] = @config.dig('logging', 'file', 'level') ||
         | 
| 313 | 
            +
                                      @config.dig('log_level') || # for backward compatibility
         | 
| 314 | 
            +
                                      'info'
         | 
| 315 | 
            +
                    kw_args[:progname] = 'rims'
         | 
| 316 | 
            +
                    if (datetime_format = @config.dig('logging', 'file', 'datetime_format')) then
         | 
| 317 | 
            +
                      kw_args[:datetime_format] = datetime_format
         | 
| 318 | 
            +
                    end
         | 
| 319 | 
            +
                    if (shift_period_suffix = @config.dig('logging', 'file', 'shift_period_suffix')) then
         | 
| 320 | 
            +
                      kw_args[:shift_period_suffix] = shift_period_suffix
         | 
| 321 | 
            +
                    end
         | 
| 322 | 
            +
                    logger_params << kw_args
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                    logger_params
         | 
| 325 | 
            +
                  end
         | 
| 326 | 
            +
             | 
| 327 | 
            +
                  # return parameters for Logger.new
         | 
| 328 | 
            +
                  def make_stdout_logger_params
         | 
| 329 | 
            +
                    logger_params = [ STDOUT ]
         | 
| 330 | 
            +
             | 
| 331 | 
            +
                    kw_args = {}
         | 
| 332 | 
            +
                    kw_args[:level] = @config.dig('logging', 'stdout', 'level') ||
         | 
| 333 | 
            +
                                      @config.dig('log_stdout') || # for backward compatibility
         | 
| 334 | 
            +
                                      'info'
         | 
| 335 | 
            +
                    kw_args[:progname] = 'rims'
         | 
| 336 | 
            +
                    if (datetime_format = @config.dig('logging', 'stdout', 'datetime_format')) then
         | 
| 337 | 
            +
                      kw_args[:datetime_format] = datetime_format
         | 
| 338 | 
            +
                    end
         | 
| 339 | 
            +
                    logger_params << kw_args
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                    logger_params
         | 
| 342 | 
            +
                  end
         | 
| 343 | 
            +
             | 
| 344 | 
            +
                  # return parameters for Logger.new
         | 
| 345 | 
            +
                  def make_protocol_logger_params
         | 
| 346 | 
            +
                    log_path = Pathname(@config.dig('logging', 'protocol', 'path') || 'protocol.log')
         | 
| 347 | 
            +
                    if (log_path.relative?) then
         | 
| 348 | 
            +
                      if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
         | 
| 349 | 
            +
                        log_path = base_dir + log_path
         | 
| 350 | 
            +
                      end
         | 
| 351 | 
            +
                    end
         | 
| 352 | 
            +
                    logger_params = [ log_path.to_s ]
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    shift_age = @config.dig('logging', 'protocol', 'shift_age')
         | 
| 355 | 
            +
                    shift_size = @config.dig('logging', 'protocol', 'shift_size')
         | 
| 356 | 
            +
                    if (shift_size) then
         | 
| 357 | 
            +
                      logger_params << (shift_age || 0)
         | 
| 358 | 
            +
                      logger_params << shift_size
         | 
| 359 | 
            +
                    elsif (shift_age) then
         | 
| 360 | 
            +
                      logger_params << shift_age
         | 
| 361 | 
            +
                    end
         | 
| 362 | 
            +
             | 
| 363 | 
            +
                    kw_args = {}
         | 
| 364 | 
            +
                    kw_args[:level] = @config.dig('logging', 'protocol', 'level') || 'unknown'
         | 
| 365 | 
            +
                    kw_args[:progname] = 'rims'
         | 
| 366 | 
            +
                    if (datetime_format = @config.dig('logging', 'protocol', 'datetime_format')) then
         | 
| 367 | 
            +
                      kw_args[:datetime_format] = datetime_format
         | 
| 368 | 
            +
                    end
         | 
| 369 | 
            +
                    if (shift_period_suffix = @config.dig('logging', 'protocol', 'shift_period_suffix')) then
         | 
| 370 | 
            +
                      kw_args[:shift_period_suffix] = shift_period_suffix
         | 
| 371 | 
            +
                    end
         | 
| 372 | 
            +
                    logger_params << kw_args
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    logger_params
         | 
| 375 | 
            +
                  end
         | 
| 376 | 
            +
             | 
| 377 | 
            +
                  def daemonize?
         | 
| 378 | 
            +
                    daemon_config = @config['daemon'] || {}
         | 
| 379 | 
            +
                    if (daemon_config.key? 'daemonize') then
         | 
| 380 | 
            +
                      daemon_config['daemonize']
         | 
| 381 | 
            +
                    else
         | 
| 382 | 
            +
                      true
         | 
| 383 | 
            +
                    end
         | 
| 384 | 
            +
                  end
         | 
| 385 | 
            +
             | 
| 386 | 
            +
                  def daemon_name
         | 
| 387 | 
            +
                    'rims'
         | 
| 388 | 
            +
                  end
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                  def daemon_debug?
         | 
| 391 | 
            +
                    daemon_config = @config['daemon'] || {}
         | 
| 392 | 
            +
                    if (daemon_config.key? 'debug') then
         | 
| 393 | 
            +
                      daemon_config['debug']
         | 
| 394 | 
            +
                    else
         | 
| 395 | 
            +
                      false
         | 
| 396 | 
            +
                    end
         | 
| 397 | 
            +
                  end
         | 
| 398 | 
            +
             | 
| 399 | 
            +
                  def status_file
         | 
| 400 | 
            +
                    file_path = @config.dig('daemon', 'status_file') || 'rims.pid'
         | 
| 401 | 
            +
                    file_path = Pathname(file_path)
         | 
| 402 | 
            +
                    if (file_path.relative?) then
         | 
| 403 | 
            +
                      if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
         | 
| 404 | 
            +
                        file_path = base_dir + file_path
         | 
| 405 | 
            +
                      end
         | 
| 406 | 
            +
                    end
         | 
| 407 | 
            +
                    file_path.to_s
         | 
| 408 | 
            +
                  end
         | 
| 409 | 
            +
             | 
| 410 | 
            +
                  def server_polling_interval_seconds
         | 
| 411 | 
            +
                    @config.dig('daemon', 'server_polling_interval_seconds') || 3
         | 
| 412 | 
            +
                  end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                  def server_restart_overlap_seconds
         | 
| 415 | 
            +
                    # to avoid resource conflict between the new server and the old server.
         | 
| 416 | 
            +
                    0
         | 
| 417 | 
            +
                  end
         | 
| 418 | 
            +
             | 
| 419 | 
            +
                  def server_privileged_user
         | 
| 420 | 
            +
                    @config.dig('daemon', 'server_privileged_user') ||
         | 
| 421 | 
            +
                      @config.dig('process_privilege_user') # for backward compatibility
         | 
| 422 | 
            +
                  end
         | 
| 423 | 
            +
             | 
| 424 | 
            +
                  def server_privileged_group
         | 
| 425 | 
            +
                    @config.dig('daemon', 'server_privileged_group') ||
         | 
| 426 | 
            +
                      @config.dig('process_privilege_group') # for backward compatibility
         | 
| 427 | 
            +
                  end
         | 
| 428 | 
            +
             | 
| 429 | 
            +
                  def listen_address
         | 
| 430 | 
            +
                    if (socket_address = @config.dig('server', 'listen_address')) then
         | 
| 431 | 
            +
                      return socket_address
         | 
| 432 | 
            +
                    end
         | 
| 433 | 
            +
             | 
| 434 | 
            +
                    # for backward compatibility
         | 
| 435 | 
            +
                    host = @config.dig('imap_host') || @config.dig('ip_addr')
         | 
| 436 | 
            +
                    port = @config.dig('imap_port') || @config.dig('ip_port')
         | 
| 437 | 
            +
                    if (host || port) then
         | 
| 438 | 
            +
                      socket_adress = {
         | 
| 439 | 
            +
                        'type' => 'tcp',
         | 
| 440 | 
            +
                        'host' => host || '0.0.0.0',
         | 
| 441 | 
            +
                        'port' => port || 1430
         | 
| 442 | 
            +
                      }
         | 
| 443 | 
            +
                      return socket_adress
         | 
| 444 | 
            +
                    end
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                    # default
         | 
| 447 | 
            +
                    '0.0.0.0:1430'
         | 
| 448 | 
            +
                  end
         | 
| 449 | 
            +
             | 
| 450 | 
            +
                  def accept_polling_timeout_seconds
         | 
| 451 | 
            +
                    @config.dig('server', 'accept_polling_timeout_seconds') || 0.1
         | 
| 452 | 
            +
                  end
         | 
| 453 | 
            +
             | 
| 454 | 
            +
                  def process_num
         | 
| 455 | 
            +
                    # not yet supported multi-process server configuration.
         | 
| 456 | 
            +
                    0
         | 
| 457 | 
            +
                  end
         | 
| 458 | 
            +
             | 
| 459 | 
            +
                  def process_queue_size
         | 
| 460 | 
            +
                    20
         | 
| 461 | 
            +
                  end
         | 
| 462 | 
            +
             | 
| 463 | 
            +
                  def process_queue_polling_timeout_seconds
         | 
| 464 | 
            +
                    0.1
         | 
| 465 | 
            +
                  end
         | 
| 466 | 
            +
             | 
| 467 | 
            +
                  def process_send_io_polling_timeout_seconds
         | 
| 468 | 
            +
                    0.1
         | 
| 469 | 
            +
                  end
         | 
| 470 | 
            +
             | 
| 471 | 
            +
                  def thread_num
         | 
| 472 | 
            +
                    @config.dig('server', 'thread_num') || 20
         | 
| 473 | 
            +
                  end
         | 
| 474 | 
            +
             | 
| 475 | 
            +
                  def thread_queue_size
         | 
| 476 | 
            +
                    @config.dig('server', 'thread_queue_size') || 20
         | 
| 477 | 
            +
                  end
         | 
| 478 | 
            +
             | 
| 479 | 
            +
                  def thread_queue_polling_timeout_seconds
         | 
| 480 | 
            +
                    @config.dig('server', 'thread_queue_polling_timeout_seconds') || 0.1
         | 
| 481 | 
            +
                  end
         | 
| 482 | 
            +
             | 
| 483 | 
            +
                  def send_buffer_limit_size
         | 
| 484 | 
            +
                    @config.dig('server', 'send_buffer_limit_size') ||
         | 
| 485 | 
            +
                      @config.dig('send_buffer_limit') || # for backward compatibility
         | 
| 486 | 
            +
                      1024 * 16
         | 
| 487 | 
            +
                  end
         | 
| 488 | 
            +
             | 
| 489 | 
            +
                  module SSLContextConfigAttribute
         | 
| 490 | 
            +
                    def ssl_context
         | 
| 491 | 
            +
                      @__ssl_context__
         | 
| 492 | 
            +
                    end
         | 
| 493 | 
            +
             | 
| 494 | 
            +
                    def base_dir
         | 
| 495 | 
            +
                      @__base_dir__
         | 
| 496 | 
            +
                    end
         | 
| 497 | 
            +
             | 
| 498 | 
            +
                    alias _ ssl_context
         | 
| 499 | 
            +
             | 
| 500 | 
            +
                    class << self
         | 
| 501 | 
            +
                      def new_module(ssl_context, base_dir)
         | 
| 502 | 
            +
                        _module = Module.new
         | 
| 503 | 
            +
                        _module.instance_variable_set(:@__ssl_context__, ssl_context)
         | 
| 504 | 
            +
                        _module.instance_variable_set(:@__base_dir__, base_dir)
         | 
| 505 | 
            +
                        _module.module_eval{
         | 
| 506 | 
            +
                          include OpenSSL
         | 
| 507 | 
            +
                          include OpenSSL::SSL
         | 
| 508 | 
            +
                          extend SSLContextConfigAttribute
         | 
| 509 | 
            +
                        }
         | 
| 510 | 
            +
                        _module
         | 
| 511 | 
            +
                      end
         | 
| 512 | 
            +
             | 
| 513 | 
            +
                      # methodized to isolate local variable scope.
         | 
| 514 | 
            +
                      def eval_config(_module, expr, filename='(eval_config)')
         | 
| 515 | 
            +
                        _module.module_eval(expr, filename)
         | 
| 516 | 
            +
                      end
         | 
| 517 | 
            +
                    end
         | 
| 518 | 
            +
                  end
         | 
| 519 | 
            +
             | 
| 520 | 
            +
                  def ssl_context
         | 
| 521 | 
            +
                    if (openssl_config = @config['openssl']) then
         | 
| 522 | 
            +
                      if (openssl_config.key? 'use_ssl') then
         | 
| 523 | 
            +
                        use_ssl = openssl_config['use_ssl']
         | 
| 524 | 
            +
                      else
         | 
| 525 | 
            +
                        use_ssl = openssl_config.key? 'ssl_context'
         | 
| 526 | 
            +
                      end
         | 
| 527 | 
            +
             | 
| 528 | 
            +
                      if (use_ssl) then
         | 
| 529 | 
            +
                        ssl_context = OpenSSL::SSL::SSLContext.new
         | 
| 530 | 
            +
                        if (ssl_config_expr = openssl_config['ssl_context']) then
         | 
| 531 | 
            +
                          anon_mod = SSLContextConfigAttribute.new_module(ssl_context, base_dir)
         | 
| 532 | 
            +
                          SSLContextConfigAttribute.eval_config(anon_mod, ssl_config_expr, 'ssl_context')
         | 
| 533 | 
            +
                        end
         | 
| 534 | 
            +
             | 
| 535 | 
            +
                        ssl_context
         | 
| 536 | 
            +
                      end
         | 
| 537 | 
            +
                    end
         | 
| 538 | 
            +
                  end
         | 
| 539 | 
            +
             | 
| 540 | 
            +
                  def read_lock_timeout_seconds
         | 
| 541 | 
            +
                    @config.dig('lock', 'read_lock_timeout_seconds') ||
         | 
| 542 | 
            +
                      @config.dig('read_lock_timeout_seconds') || # for backward compatibility
         | 
| 543 | 
            +
                      30
         | 
| 544 | 
            +
                  end
         | 
| 545 | 
            +
             | 
| 546 | 
            +
                  def write_lock_timeout_seconds
         | 
| 547 | 
            +
                    @config.dig('lock', 'write_lock_timeout_seconds') ||
         | 
| 548 | 
            +
                      @config.dig('write_lock_timeout_seconds') || # for backward compatibility
         | 
| 549 | 
            +
                      30
         | 
| 550 | 
            +
                  end
         | 
| 551 | 
            +
             | 
| 552 | 
            +
                  def cleanup_write_lock_timeout_seconds
         | 
| 553 | 
            +
                    @config.dig('lock', 'cleanup_write_lock_timeout_seconds') ||
         | 
| 554 | 
            +
                      @config.dig('cleanup_write_lock_timeout_seconds') || # for backward compatibility
         | 
| 555 | 
            +
                      1
         | 
| 556 | 
            +
                  end
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                  KeyValueStoreFactoryBuilderParams = Struct.new(:origin_type, :origin_config, :middleware_list)
         | 
| 559 | 
            +
                  class KeyValueStoreFactoryBuilderParams
         | 
| 560 | 
            +
                    def build_factory
         | 
| 561 | 
            +
                      builder = KeyValueStore::FactoryBuilder.new
         | 
| 562 | 
            +
                      builder.open{|name| origin_type.open_with_conf(name, origin_config) }
         | 
| 563 | 
            +
                      for middleware in middleware_list
         | 
| 564 | 
            +
                        builder.use(middleware)
         | 
| 565 | 
            +
                      end
         | 
| 566 | 
            +
                      builder.factory
         | 
| 567 | 
            +
                    end
         | 
| 568 | 
            +
                  end
         | 
| 569 | 
            +
             | 
| 570 | 
            +
                  def make_key_value_store_params(collection)
         | 
| 571 | 
            +
                    kvs_params = KeyValueStoreFactoryBuilderParams.new
         | 
| 572 | 
            +
                    kvs_params.origin_type = KeyValueStore::FactoryBuilder.get_plug_in(collection['type'] ||
         | 
| 573 | 
            +
                                                                                       collection['plug_in'] ||           # for backward compatibility
         | 
| 574 | 
            +
                                                                                       @config['key_value_store_type'] || # for backward compatibility
         | 
| 575 | 
            +
                                                                                       'gdbm')
         | 
| 576 | 
            +
                    kvs_params.origin_config = get_configuration(collection)
         | 
| 577 | 
            +
             | 
| 578 | 
            +
                    if (collection.key? 'use_checksum') then
         | 
| 579 | 
            +
                      use_checksum = collection['use_checksum']
         | 
| 580 | 
            +
                    elsif (@config.key? 'use_key_value_store_checksum') then
         | 
| 581 | 
            +
                      use_checksum = @config['use_key_value_store_checksum'] # for backward compatibility
         | 
| 582 | 
            +
                    else
         | 
| 583 | 
            +
                      use_checksum = true   # default
         | 
| 584 | 
            +
                    end
         | 
| 585 | 
            +
             | 
| 586 | 
            +
                    kvs_params.middleware_list = []
         | 
| 587 | 
            +
                    kvs_params.middleware_list << Checksum_KeyValueStore if use_checksum
         | 
| 588 | 
            +
             | 
| 589 | 
            +
                    kvs_params
         | 
| 590 | 
            +
                  end
         | 
| 591 | 
            +
                  private :make_key_value_store_params
         | 
| 592 | 
            +
             | 
| 593 | 
            +
                  def make_meta_key_value_store_params
         | 
| 594 | 
            +
                    make_key_value_store_params(@config.dig('storage', 'meta_key_value_store') ||
         | 
| 595 | 
            +
                                                @config.dig('meta_key_value_store') || # for backward compatibility
         | 
| 596 | 
            +
                                                {})
         | 
| 597 | 
            +
                  end
         | 
| 598 | 
            +
             | 
| 599 | 
            +
                  def make_text_key_value_store_params
         | 
| 600 | 
            +
                    make_key_value_store_params(@config.dig('storage', 'text_key_value_store') ||
         | 
| 601 | 
            +
                                                @config.dig('text_key_value_store') || # for backward compatibility
         | 
| 602 | 
            +
                                                {})
         | 
| 603 | 
            +
                  end
         | 
| 604 | 
            +
             | 
| 605 | 
            +
                  def make_key_value_store_path(mailbox_data_structure_version, unique_user_id)
         | 
| 606 | 
            +
                    if (mailbox_data_structure_version.empty?) then
         | 
| 607 | 
            +
                      raise ArgumentError, 'too short mailbox data structure version.'
         | 
| 608 | 
            +
                    end
         | 
| 609 | 
            +
                    if (unique_user_id.length <= 2) then
         | 
| 610 | 
            +
                      raise ArgumentError, 'too short unique user ID.'
         | 
| 611 | 
            +
                    end
         | 
| 612 | 
            +
             | 
| 613 | 
            +
                    bucket_dir_name = unique_user_id[0..1]
         | 
| 614 | 
            +
                    store_dir_name = unique_user_id[2..-1]
         | 
| 615 | 
            +
             | 
| 616 | 
            +
                    base_dir + mailbox_data_structure_version + bucket_dir_name + store_dir_name
         | 
| 617 | 
            +
                  end
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                  def make_authentication
         | 
| 620 | 
            +
                    if ((@config.key? 'authentication') && (@config['authentication'].is_a? Hash)) then
         | 
| 621 | 
            +
                      auth_conf = @config['authentication']
         | 
| 622 | 
            +
                    else
         | 
| 623 | 
            +
                      auth_conf = {}
         | 
| 624 | 
            +
                    end
         | 
| 625 | 
            +
             | 
| 626 | 
            +
                    hostname = auth_conf['hostname'] ||
         | 
| 627 | 
            +
                               @config['hostname'] || # for backward compatibility
         | 
| 628 | 
            +
                               Socket.gethostname
         | 
| 629 | 
            +
                    auth = Authentication.new(hostname: hostname)
         | 
| 630 | 
            +
             | 
| 631 | 
            +
                    if (passwd_src_list = auth_conf['password_sources']) then
         | 
| 632 | 
            +
                      for passwd_src_conf in passwd_src_list
         | 
| 633 | 
            +
                        plug_in_name = passwd_src_conf['type'] or raise KeyError, 'not found a password source type.'
         | 
| 634 | 
            +
                        plug_in_config = get_configuration(passwd_src_conf)
         | 
| 635 | 
            +
                        passwd_src = Authentication.get_plug_in(plug_in_name, plug_in_config)
         | 
| 636 | 
            +
                        auth.add_plug_in(passwd_src)
         | 
| 637 | 
            +
                      end
         | 
| 638 | 
            +
                    end
         | 
| 639 | 
            +
             | 
| 640 | 
            +
                    # for backward compatibility
         | 
| 641 | 
            +
                    if (user_list = @config['user_list']) then
         | 
| 642 | 
            +
                      plain_src = Password::PlainSource.new
         | 
| 643 | 
            +
                      for pw in user_list
         | 
| 644 | 
            +
                        user = pw['user'] or raise KeyError, 'not found a user_list user.'
         | 
| 645 | 
            +
                        pass = pw['pass'] or raise KeyError, 'not found a user_list pass.'
         | 
| 646 | 
            +
                        plain_src.entry(user, pass)
         | 
| 647 | 
            +
                      end
         | 
| 648 | 
            +
                      auth.add_plug_in(plain_src)
         | 
| 649 | 
            +
                    end
         | 
| 650 | 
            +
             | 
| 651 | 
            +
                    # for backward compatibility
         | 
| 652 | 
            +
                    if (username = @config['username']) then
         | 
| 653 | 
            +
                      password = @config['password'] or raise KeyError, 'not found a password.'
         | 
| 654 | 
            +
                      plain_src = Password::PlainSource.new
         | 
| 655 | 
            +
                      plain_src.entry(username, password)
         | 
| 656 | 
            +
                      auth.add_plug_in(plain_src)
         | 
| 657 | 
            +
                    end
         | 
| 658 | 
            +
             | 
| 659 | 
            +
                    # for backward compatibility
         | 
| 660 | 
            +
                    if ((@config.key? 'authentication') && (@config['authentication'].is_a? Array)) then
         | 
| 661 | 
            +
                      plug_in_list = @config['authentication']
         | 
| 662 | 
            +
                      for plug_in_conf in plug_in_list
         | 
| 663 | 
            +
                        plug_in_name = plug_in_conf['plug_in'] or raise KeyError, 'not found an authentication plug_in.'
         | 
| 664 | 
            +
                        plug_in_config = get_configuration(plug_in_conf)
         | 
| 665 | 
            +
                        passwd_src = Authentication.get_plug_in(plug_in_name, plug_in_config)
         | 
| 666 | 
            +
                        auth.add_plug_in(passwd_src)
         | 
| 667 | 
            +
                      end
         | 
| 668 | 
            +
                    end
         | 
| 669 | 
            +
             | 
| 670 | 
            +
                    auth
         | 
| 671 | 
            +
                  end
         | 
| 672 | 
            +
             | 
| 673 | 
            +
                  def mail_delivery_user
         | 
| 674 | 
            +
                    @config.dig('authorization', 'mail_delivery_user') ||
         | 
| 675 | 
            +
                      @config.dig('mail_delivery_user') || # for backward compatibility
         | 
| 676 | 
            +
                      '#postman'
         | 
| 677 | 
            +
                  end
         | 
| 678 | 
            +
                end
         | 
| 679 | 
            +
             | 
| 680 | 
            +
                DEFAULT_CONFIG = Configuration.new
         | 
| 681 | 
            +
             | 
| 682 | 
            +
                # default features
         | 
| 683 | 
            +
                DEFAULT_CONFIG.require_features
         | 
| 684 | 
            +
             | 
| 685 | 
            +
                # for read-only
         | 
| 686 | 
            +
                class << DEFAULT_CONFIG
         | 
| 687 | 
            +
                  undef load
         | 
| 688 | 
            +
                  undef load_yaml
         | 
| 689 | 
            +
                end
         | 
| 690 | 
            +
             | 
| 691 | 
            +
                def initialize(config)
         | 
| 692 | 
            +
                  @config = config
         | 
| 693 | 
            +
                end
         | 
| 694 | 
            +
             | 
| 695 | 
            +
                using Logger::JointPlus
         | 
| 696 | 
            +
             | 
| 697 | 
            +
                def setup(server, daemon: false)
         | 
| 698 | 
            +
                  file_logger_params = @config.make_file_logger_params
         | 
| 699 | 
            +
                  logger = Logger.new(*file_logger_params)
         | 
| 700 | 
            +
             | 
| 701 | 
            +
                  stdout_logger_params = @config.make_stdout_logger_params
         | 
| 702 | 
            +
                  unless (daemon && @config.daemonize?) then
         | 
| 703 | 
            +
                    logger += Logger.new(*stdout_logger_params)
         | 
| 704 | 
            +
                  end
         | 
| 705 | 
            +
             | 
| 706 | 
            +
                  protocol_logger_params = @config.make_protocol_logger_params
         | 
| 707 | 
            +
                  protocol_logger = Logger.new(*protocol_logger_params)
         | 
| 708 | 
            +
             | 
| 709 | 
            +
                  logger.info('preload libraries.')
         | 
| 710 | 
            +
                  Riser.preload
         | 
| 711 | 
            +
                  Riser.preload(RIMS)
         | 
| 712 | 
            +
                  Riser.preload(RIMS::Protocol)
         | 
| 713 | 
            +
                  @config.require_features
         | 
| 714 | 
            +
             | 
| 715 | 
            +
                  logger.info('setup server.')
         | 
| 716 | 
            +
                  server.accept_polling_timeout_seconds          = @config.accept_polling_timeout_seconds
         | 
| 717 | 
            +
                  server.process_num                             = @config.process_num
         | 
| 718 | 
            +
                  server.process_queue_size                      = @config.process_queue_size
         | 
| 719 | 
            +
                  server.process_queue_polling_timeout_seconds   = @config.process_queue_polling_timeout_seconds
         | 
| 720 | 
            +
                  server.process_send_io_polling_timeout_seconds = @config.process_send_io_polling_timeout_seconds
         | 
| 721 | 
            +
                  server.thread_num                              = @config.thread_num
         | 
| 722 | 
            +
                  server.thread_queue_size                       = @config.thread_queue_size
         | 
| 723 | 
            +
                  server.thread_queue_polling_timeout_seconds    = @config.thread_queue_polling_timeout_seconds
         | 
| 724 | 
            +
             | 
| 725 | 
            +
                  ssl_context = @config.ssl_context
         | 
| 726 | 
            +
             | 
| 727 | 
            +
                  make_kvs_factory = lambda{|kvs_params, kvs_type|
         | 
| 728 | 
            +
                    kvs_factory = kvs_params.build_factory
         | 
| 729 | 
            +
                    return lambda{|mailbox_data_structure_version, unique_user_id, db_name|
         | 
| 730 | 
            +
                      kvs_path = @config.make_key_value_store_path(mailbox_data_structure_version, unique_user_id)
         | 
| 731 | 
            +
                      unless (kvs_path.directory?) then
         | 
| 732 | 
            +
                        logger.debug("make a directory: #{kvs_path}") if logger.debug?
         | 
| 733 | 
            +
                        kvs_path.mkpath
         | 
| 734 | 
            +
                      end
         | 
| 735 | 
            +
                      db_path = kvs_path + db_name
         | 
| 736 | 
            +
                      logger.debug("#{kvs_type} data key-value sotre path: #{db_path}") if logger.debug?
         | 
| 737 | 
            +
                      kvs_factory.call(db_path.to_s)
         | 
| 738 | 
            +
                    }, lambda{
         | 
| 739 | 
            +
                      logger.info("#{kvs_type} key-value store parameter: type=#{kvs_params.origin_type}")
         | 
| 740 | 
            +
                      logger.info("#{kvs_type} key-value store parameter: config=#{kvs_params.origin_config.to_json}")
         | 
| 741 | 
            +
                      kvs_params.middleware_list.each_with_index do |middleware, i|
         | 
| 742 | 
            +
                        logger.info("#{kvs_type} key-value store parameter: middleware[#{i}]=#{middleware}")
         | 
| 743 | 
            +
                      end
         | 
| 744 | 
            +
                    }
         | 
| 745 | 
            +
                  }
         | 
| 746 | 
            +
             | 
| 747 | 
            +
                  kvs_meta_open, kvs_meta_log = make_kvs_factory.call(@config.make_meta_key_value_store_params, 'meta')
         | 
| 748 | 
            +
                  kvs_text_open, kvs_text_log = make_kvs_factory.call(@config.make_text_key_value_store_params, 'text')
         | 
| 749 | 
            +
                  auth = @config.make_authentication
         | 
| 750 | 
            +
                  mail_store_pool = MailStore.build_pool(kvs_meta_open, kvs_text_open)
         | 
| 751 | 
            +
             | 
| 752 | 
            +
                  server.before_start{|server_socket|
         | 
| 753 | 
            +
                    logger.info('start server.')
         | 
| 754 | 
            +
                    for feature in @config.get_required_features
         | 
| 755 | 
            +
                      logger.info("required feature: #{feature}")
         | 
| 756 | 
            +
                    end
         | 
| 757 | 
            +
                    logger.info("file logging parameter: path=#{file_logger_params[0]}")
         | 
| 758 | 
            +
                    file_logger_params[1..-2].each_with_index do |value, i|
         | 
| 759 | 
            +
                      logger.info("file logging parameter: shift_args[#{i}]=#{value}")
         | 
| 760 | 
            +
                    end
         | 
| 761 | 
            +
                    for name, value in file_logger_params[-1]
         | 
| 762 | 
            +
                      logger.info("file logging parameter: #{name}=#{value}")
         | 
| 763 | 
            +
                    end
         | 
| 764 | 
            +
                    for name, value in stdout_logger_params[-1]
         | 
| 765 | 
            +
                      logger.info("stdout logging parameter: #{name}=#{value}")
         | 
| 766 | 
            +
                    end
         | 
| 767 | 
            +
                    logger.info("protocol logging parameter: path=#{protocol_logger_params[0]}")
         | 
| 768 | 
            +
                    protocol_logger_params[1..-2].each_with_index do |value, i|
         | 
| 769 | 
            +
                      logger.info("protocol logging parameter: shift_args[#{i}]=#{value}")
         | 
| 770 | 
            +
                    end
         | 
| 771 | 
            +
                    for name, value in protocol_logger_params[-1]
         | 
| 772 | 
            +
                      logger.info("protocol logging parameter: #{name}=#{value}")
         | 
| 773 | 
            +
                    end
         | 
| 774 | 
            +
                    logger.info("listen address: #{server_socket.local_address.inspect_sockaddr}")
         | 
| 775 | 
            +
                    privileged_user = Etc.getpwuid(Process.euid).name rescue ''
         | 
| 776 | 
            +
                    logger.info("server privileged user: #{privileged_user}(#{Process.euid})")
         | 
| 777 | 
            +
                    privileged_group = Etc.getgrgid(Process.egid).name rescue ''
         | 
| 778 | 
            +
                    logger.info("server privileged group: #{privileged_group}(#{Process.egid})")
         | 
| 779 | 
            +
                    logger.info("server parameter: accept_polling_timeout_seconds=#{server.accept_polling_timeout_seconds}")
         | 
| 780 | 
            +
                    logger.info("server parameter: process_num=#{server.process_num}")
         | 
| 781 | 
            +
                    logger.info("server parameter: process_queue_size=#{server.process_queue_size}")
         | 
| 782 | 
            +
                    logger.info("server parameter: process_queue_polling_timeout_seconds=#{server.process_queue_polling_timeout_seconds}")
         | 
| 783 | 
            +
                    logger.info("server parameter: process_send_io_polling_timeout_seconds=#{server.process_send_io_polling_timeout_seconds}")
         | 
| 784 | 
            +
                    logger.info("server parameter: thread_num=#{server.thread_num}")
         | 
| 785 | 
            +
                    logger.info("server parameter: thread_queue_size=#{server.thread_queue_size}")
         | 
| 786 | 
            +
                    logger.info("server parameter: thread_queue_polling_timeout_seconds=#{server.thread_queue_polling_timeout_seconds}")
         | 
| 787 | 
            +
                    logger.info("server parameter: send_buffer_limit_size=#{@config.send_buffer_limit_size}")
         | 
| 788 | 
            +
                    if (ssl_context) then
         | 
| 789 | 
            +
                      Array(ssl_context.alpn_protocols).each_with_index do |protocol, i|
         | 
| 790 | 
            +
                        logger.info("openssl parameter: alpn_protocols[#{i}]=#{protocol}")
         | 
| 791 | 
            +
                      end
         | 
| 792 | 
            +
                      logger.info("openssl parameter: alpn_select_cb=#{ssl_context.alpn_select_cb.inspect}") if ssl_context.alpn_select_cb
         | 
| 793 | 
            +
                      logger.info("openssl parameter: ca_file=#{ssl_context.ca_file}") if ssl_context.ca_file
         | 
| 794 | 
            +
                      logger.info("openssl parameter: ca_path=#{ssl_context.ca_path}") if ssl_context.ca_path
         | 
| 795 | 
            +
                      if (ssl_context.cert) then
         | 
| 796 | 
            +
                        ssl_context.cert.to_text.each_line do |line|
         | 
| 797 | 
            +
                          logger.info("openssl parameter: [cert] #{line.chomp}")
         | 
| 798 | 
            +
                        end
         | 
| 799 | 
            +
                      else
         | 
| 800 | 
            +
                        logger.warn('openssl parameter: not defined cert attribute.')
         | 
| 801 | 
            +
                      end
         | 
| 802 | 
            +
                      logger.info("openssl parameter: cert_store=#{ssl_context.cert_store.inspect}") if ssl_context.cert_store
         | 
| 803 | 
            +
                      Array(ssl_context.ciphers).each_with_index do |cipher, i|
         | 
| 804 | 
            +
                        logger.info("openssl parameter: ciphers[#{i}]=#{cipher.join(',')}")
         | 
| 805 | 
            +
                      end
         | 
| 806 | 
            +
                      Array(ssl_context.client_ca).each_with_index do |cert, i|
         | 
| 807 | 
            +
                        cert.to_text.each_line do |line|
         | 
| 808 | 
            +
                          logger.info("openssl parameter: client_ca[#{i}]: #{line.chomp}")
         | 
| 809 | 
            +
                        end
         | 
| 810 | 
            +
                      end
         | 
| 811 | 
            +
                      logger.info("openssl parameter: client_cert_cb=#{ssl_context.client_cert_cb.inspect}") if ssl_context.client_cert_cb
         | 
| 812 | 
            +
                      Array(ssl_context.extra_chain_cert).each_with_index do |cert, i|
         | 
| 813 | 
            +
                        cert.to_text.each_line do |line|
         | 
| 814 | 
            +
                          logger.info("openssl parameter: extra_chain_cert[#{i}]: #{line.chomp}")
         | 
| 815 | 
            +
                        end
         | 
| 816 | 
            +
                      end
         | 
| 817 | 
            +
                      if (ssl_context.key) then
         | 
| 818 | 
            +
                        logger.info("openssl parameter: key=#{ssl_context.key.inspect}")
         | 
| 819 | 
            +
                        if (logger.debug?) then
         | 
| 820 | 
            +
                          ssl_context.key.to_text.each_line do |line|
         | 
| 821 | 
            +
                            logger.debug("openssl parameter: [key] #{line.chomp}")
         | 
| 822 | 
            +
                          end
         | 
| 823 | 
            +
                        end
         | 
| 824 | 
            +
                      else
         | 
| 825 | 
            +
                        logger.warn('openssl parameter: not defined key attribute.')
         | 
| 826 | 
            +
                      end
         | 
| 827 | 
            +
                      Array(ssl_context.npn_protocols).each_with_index do |protocol, i|
         | 
| 828 | 
            +
                        logger.info("openssl parameter: npn_protocols[#{i}]=#{protocol}")
         | 
| 829 | 
            +
                      end
         | 
| 830 | 
            +
                      logger.info("openssl parameter: npn_select_cb=#{ssl_context.npn_select_cb.inspect}") if ssl_context.npn_select_cb
         | 
| 831 | 
            +
                      logger.info("openssl parameter: options=0x#{'%08x' % ssl_context.options}") if ssl_context.options
         | 
| 832 | 
            +
                      logger.info("openssl parameter: renegotiation_cb=#{ssl_context.renegotiation_cb.inspect}") if ssl_context.renegotiation_cb
         | 
| 833 | 
            +
                      logger.info("openssl parameter: security_level=#{ssl_context.security_level}")
         | 
| 834 | 
            +
                      logger.info("openssl parameter: servername_cb=#{ssl_context.servername_cb.inspect}") if ssl_context.servername_cb
         | 
| 835 | 
            +
                      logger.info("openssl parameter: session_cache_mode=0x#{'%08x' % ssl_context.session_cache_mode}")
         | 
| 836 | 
            +
                      logger.info("openssl parameter: session_cache_size=#{ssl_context.session_cache_size }")
         | 
| 837 | 
            +
                      logger.info("openssl parameter: session_get_cb=#{ssl_context.session_get_cb.inspect}") if ssl_context.session_get_cb
         | 
| 838 | 
            +
                      logger.info("openssl parameter: session_id_context=#{ssl_context.session_id_context}") if ssl_context.session_id_context
         | 
| 839 | 
            +
                      logger.info("openssl parameter: session_new_cb=#{ssl_context.session_new_cb.inspect}") if ssl_context.session_new_cb
         | 
| 840 | 
            +
                      logger.info("openssl parameter: session_remove_cb=#{ssl_context.session_remove_cb}") if ssl_context.session_remove_cb
         | 
| 841 | 
            +
                      logger.info("openssl parameter: ssl_timeout=#{ssl_context.ssl_timeout}") if ssl_context.ssl_timeout
         | 
| 842 | 
            +
                      logger.info("openssl parameter: tmp_dh_callback=#{ssl_context.tmp_dh_callback}") if ssl_context.tmp_dh_callback
         | 
| 843 | 
            +
                      logger.info("openssl parameter: verify_callback=#{ssl_context.verify_callback}") if ssl_context.verify_callback
         | 
| 844 | 
            +
                      logger.info("openssl parameter: verify_depth=#{ssl_context.verify_depth}") if ssl_context.verify_depth
         | 
| 845 | 
            +
                      logger.info("openssl parameter: verify_hostname=#{ssl_context.verify_hostname}") if ssl_context.verify_hostname
         | 
| 846 | 
            +
                      logger.info("openssl parameter: verify_mode=0x#{'%08x' % ssl_context.verify_mode}") if ssl_context.verify_mode
         | 
| 847 | 
            +
                    end
         | 
| 848 | 
            +
                    logger.info("lock parameter: read_lock_timeout_seconds=#{@config.read_lock_timeout_seconds}")
         | 
| 849 | 
            +
                    logger.info("lock parameter: write_lock_timeout_seconds=#{@config.write_lock_timeout_seconds}")
         | 
| 850 | 
            +
                    logger.info("lock parameter: cleanup_write_lock_timeout_seconds=#{@config.cleanup_write_lock_timeout_seconds}")
         | 
| 851 | 
            +
                    kvs_meta_log.call
         | 
| 852 | 
            +
                    kvs_text_log.call
         | 
| 853 | 
            +
                    logger.info("authentication parameter: hostname=#{auth.hostname}")
         | 
| 854 | 
            +
                    logger.info("authorization parameter: mail_delivery_user=#{@config.mail_delivery_user}")
         | 
| 855 | 
            +
                  }
         | 
| 856 | 
            +
                  # server.at_fork{}
         | 
| 857 | 
            +
                  # server.at_stop{}
         | 
| 858 | 
            +
                  server.at_stat{|info|
         | 
| 859 | 
            +
                    logger.info("stat: #{info.to_json}")
         | 
| 860 | 
            +
                  }
         | 
| 861 | 
            +
                  server.preprocess{
         | 
| 862 | 
            +
                    auth.start_plug_in(logger)
         | 
| 863 | 
            +
                  }
         | 
| 864 | 
            +
                  server.dispatch{|socket|
         | 
| 865 | 
            +
                    begin
         | 
| 866 | 
            +
                      begin
         | 
| 867 | 
            +
                        begin
         | 
| 868 | 
            +
                          begin
         | 
| 869 | 
            +
                            remote_address = socket.remote_address # the place where the remote socket is most likely not closed is here
         | 
| 870 | 
            +
                            logger.info("accept connection: #{remote_address.inspect_sockaddr}")
         | 
| 871 | 
            +
                            if (ssl_context) then
         | 
| 872 | 
            +
                              ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
         | 
| 873 | 
            +
                              logger.info("start tls: #{ssl_socket.state}")
         | 
| 874 | 
            +
                              ssl_socket.accept
         | 
| 875 | 
            +
                              logger.info("accept tls: #{ssl_socket.state}")
         | 
| 876 | 
            +
                              ssl_socket.sync = true
         | 
| 877 | 
            +
                              stream = Riser::WriteBufferStream.new(ssl_socket, @config.send_buffer_limit_size)
         | 
| 878 | 
            +
                            else
         | 
| 879 | 
            +
                              stream = Riser::WriteBufferStream.new(socket, @config.send_buffer_limit_size)
         | 
| 880 | 
            +
                            end
         | 
| 881 | 
            +
                            stream = Riser::LoggingStream.new(stream, protocol_logger)
         | 
| 882 | 
            +
                            decoder = Protocol::Decoder.new_decoder(mail_store_pool, auth, logger,
         | 
| 883 | 
            +
                                                                    mail_delivery_user: @config.mail_delivery_user,
         | 
| 884 | 
            +
                                                                    read_lock_timeout_seconds: @config.read_lock_timeout_seconds,
         | 
| 885 | 
            +
                                                                    write_lock_timeout_seconds: @config.write_lock_timeout_seconds,
         | 
| 886 | 
            +
                                                                    cleanup_write_lock_timeout_seconds: @config.cleanup_write_lock_timeout_seconds)
         | 
| 887 | 
            +
                            Protocol::Decoder.repl(decoder, stream, stream, logger)
         | 
| 888 | 
            +
                          ensure
         | 
| 889 | 
            +
                            if (stream) then
         | 
| 890 | 
            +
                              stream.flush
         | 
| 891 | 
            +
                            end
         | 
| 892 | 
            +
                          end
         | 
| 893 | 
            +
                        ensure
         | 
| 894 | 
            +
                          if (ssl_socket) then
         | 
| 895 | 
            +
                            ssl_socket.close
         | 
| 896 | 
            +
                            logger.info("close tls: #{ssl_socket.state}")
         | 
| 897 | 
            +
                          end
         | 
| 898 | 
            +
                        end
         | 
| 899 | 
            +
                      ensure
         | 
| 900 | 
            +
                        socket.close
         | 
| 901 | 
            +
                        if (remote_address) then
         | 
| 902 | 
            +
                          logger.info("close connection: #{remote_address.inspect_sockaddr}")
         | 
| 903 | 
            +
                        else
         | 
| 904 | 
            +
                          logger.info('close connection.')
         | 
| 905 | 
            +
                        end
         | 
| 906 | 
            +
                      end
         | 
| 907 | 
            +
                    rescue
         | 
| 908 | 
            +
                      logger.error('interrupt connection with unexpected error.')
         | 
| 909 | 
            +
                      Error.trace_error_chain($!) do |exception|
         | 
| 910 | 
            +
                        logger.error(exception)
         | 
| 911 | 
            +
                      end
         | 
| 912 | 
            +
                    end
         | 
| 913 | 
            +
                  }
         | 
| 914 | 
            +
                  server.postprocess{
         | 
| 915 | 
            +
                    auth.stop_plug_in(logger)
         | 
| 916 | 
            +
                  }
         | 
| 917 | 
            +
                  server.after_stop{
         | 
| 918 | 
            +
                    logger.info('stop server.')
         | 
| 919 | 
            +
                  }
         | 
| 920 | 
            +
             | 
| 921 | 
            +
                  nil
         | 
| 922 | 
            +
                end
         | 
| 923 | 
            +
              end
         | 
| 924 | 
            +
            end
         | 
| 925 | 
            +
             | 
| 926 | 
            +
            if ($0 == __FILE__) then
         | 
| 927 | 
            +
              require 'pp' if $DEBUG
         | 
| 928 | 
            +
              require 'rims'
         | 
| 929 | 
            +
             | 
| 930 | 
            +
              if (ARGV.length != 1) then
         | 
| 931 | 
            +
                STDERR.puts "usage: #{$0} config.yml"
         | 
| 932 | 
            +
                exit(1)
         | 
| 933 | 
            +
              end
         | 
| 934 | 
            +
             | 
| 935 | 
            +
              config = RIMS::Service::Configuration.new
         | 
| 936 | 
            +
              config.load_yaml(ARGV[0])
         | 
| 937 | 
            +
              pp config if $DEBUG
         | 
| 938 | 
            +
             | 
| 939 | 
            +
              server = Riser::SocketServer.new
         | 
| 940 | 
            +
              service = RIMS::Service.new(config)
         | 
| 941 | 
            +
              service.setup(server)
         | 
| 942 | 
            +
             | 
| 943 | 
            +
              Signal.trap(:INT) { server.signal_stop_forced }
         | 
| 944 | 
            +
              Signal.trap(:TERM) { server.signal_stop_graceful }
         | 
| 945 | 
            +
             | 
| 946 | 
            +
              listen_address = Riser::SocketAddress.parse(config.listen_address)
         | 
| 947 | 
            +
              server.start(listen_address.open_server)
         | 
| 948 | 
            +
            end
         | 
| 949 | 
            +
             | 
| 950 | 
            +
            # Local Variables:
         | 
| 951 | 
            +
            # mode: Ruby
         | 
| 952 | 
            +
            # indent-tabs-mode: nil
         | 
| 953 | 
            +
            # End:
         |