Class: Akaer::Application

Inherits:
Object
  • Object
show all
Defined in:
lib/akaer/application.rb

Instance Attribute Summary collapse

Instance Attribute Details

#commandMamertes::Command

Returns The Mamertes command.

Returns:

  • (Mamertes::Command)

    The Mamertes command.



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/akaer/application.rb', line 270

class Application
  attr_reader :config
  attr_reader :command
  attr_accessor :logger

  include Lazier::I18n
  include Akaer::ApplicationMethods::General
  include Akaer::ApplicationMethods::System

  # Creates a new application.
  #
  # @param command [Mamertes::Command] The current Mamertes command.
  # @param locale [Symbol] The locale to use for the application.
  def initialize(command, locale)
    i18n_setup(:akaer, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
    self.i18n = locale

    @command = command
    options = @command.application.get_options.reject {|_, v| v.nil? }

    # Setup logger
    Bovem::Logger.start_time = Time.now
    @logger = Bovem::Logger.create(Bovem::Logger.get_real_file(options["log_file"]) || Bovem::Logger.default_file, Logger::INFO)

    # Open configuration
    read_configuration(options)

    self
  end

  # Checks if and address is a valid IPv4 address.
  #
  # @param address [String] The address to check.
  # @return [Boolean] `true` if the address is a valid IPv4 address, `false` otherwise.
  def is_ipv4?(address)
    address = address.ensure_string

    mo = /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/.match(address)
    (mo && mo.captures.all? {|i| i.to_i < 256}) ? true : false
  end

  # Checks if and address is a valid IPv6 address.
  #
  # @param address [String] The address to check.
  # @return [Boolean] `true` if the address is a valid IPv6 address, `false` otherwise.
  def is_ipv6?(address)
    address = address.ensure_string

    catch(:valid) do
      # IPv6 (normal)
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ address
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
      throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
      # IPv6 (IPv4 compat)
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ address && is_ipv4?($')
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && is_ipv4?($')
      throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && is_ipv4?($')

      false
    end
  end


  # Pads a number to make it print friendly.
  #
  # @param num [Fixnum] The number to pad.
  # @param len [Fixnum] The minimum length of the padded string.
  # @return [String] The padded number.
  def pad_number(num, len = nil)
    num.to_integer.to_s.rjust([len.to_integer, 2].max, "0")
  end

  # Gets the current logger of the application.
  #
  # @return [Logger] The current logger of the application.
  def get_logger
    @logger ||= Bovem::Logger.create(@config.log_file, @config.log_level, @log_formatter)
  end

  # Gets the path for the launch agent file.
  #
  # @param name [String] The base name for the agent.
  # @return [String] The path for the launch agent file.
  def launch_agent_path(name = "it.cowtech.akaer")
    ENV["HOME"] + "/Library/LaunchAgents/#{name}.plist"
  end

  # Executes a shell command.
  #
  # @param command [String] The command to execute.
  # @return [Boolean] `true` if command succeeded, `false` otherwise.
  def execute_command(command)
    Kernel.system(command)
  end

  # Computes the list of address to manage.
  #
  # @param type [Symbol] The type of addresses to consider. Valid values are `:ipv4`, `:ipv6`, otherwise all addresses are considered.
  # @return [Array] The list of addresses to add or remove from the interface.
  def compute_addresses(type = :all)
    config = self.config
    config.addresses.present? ? filter_addresses(config, type) : generate_addresses(config, type)
  end

  # Returns a unique (singleton) instance of the application.
  #
  # @param command [Mamertes::Command] The current Mamertes command.
  # @param locale [Symbol] The locale to use for the application.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Application] The unique (singleton) instance of the application.
  def self.instance(command, locale = nil, force = false)
    @instance = nil if force
    @instance ||= Akaer::Application.new(command, locale)
  end

  private
    # Reads the configuration.
    #
    # @param options [Hash] The options to read.
    def read_configuration(options)
      begin
        @config = Akaer::Configuration.new(options["configuration"], options, @logger)
        @logger = nil
        @logger = get_logger
      rescue Bovem::Errors::InvalidConfiguration => e
        @logger ? @logger.fatal(e.message) : Bovem::Logger.create("STDERR").fatal(i18n.logging_failed(log_file))
        raise ::SystemExit
      end
    end

    # Filters a list of addresses to return just certain type(s).
    #
    # @param config [Configuration] The current configuration.
    # @param type [Symbol] The type of addresses to return.
    # @return [Array] A list of IPs.
    def filter_addresses(config, type)
      filters =  [:ipv4, :ipv6].select {|i| type == i || type == :all }.compact

      config.addresses.select { |address|
        filters.any? {|filter| send("is_#{filter}?", address) }
      }.compact.uniq
    end

    # Generates a list of addresses which are immediate successors of a start address.
    #
    # @param config [Configuration] The current configuration.
    # @param type [Symbol] The type of addresses to return.
    # @return [Array] A list of IPs.
    def generate_addresses(config, type)
      begin
        ip = IPAddr.new(config.start_address.ensure_string)
        raise ArgumentError if type != :all && !ip.send("#{type}?")

        (config.aliases > 0 ? config.aliases : 5).times.collect {|_|
          current = ip
          ip = ip.succ
          current
        }
      rescue ArgumentError
        []
      end
    end
end

#configConfiguration

Returns The Configuration of this application.

Returns:



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/akaer/application.rb', line 270

class Application
  attr_reader :config
  attr_reader :command
  attr_accessor :logger

  include Lazier::I18n
  include Akaer::ApplicationMethods::General
  include Akaer::ApplicationMethods::System

  # Creates a new application.
  #
  # @param command [Mamertes::Command] The current Mamertes command.
  # @param locale [Symbol] The locale to use for the application.
  def initialize(command, locale)
    i18n_setup(:akaer, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
    self.i18n = locale

    @command = command
    options = @command.application.get_options.reject {|_, v| v.nil? }

    # Setup logger
    Bovem::Logger.start_time = Time.now
    @logger = Bovem::Logger.create(Bovem::Logger.get_real_file(options["log_file"]) || Bovem::Logger.default_file, Logger::INFO)

    # Open configuration
    read_configuration(options)

    self
  end

  # Checks if and address is a valid IPv4 address.
  #
  # @param address [String] The address to check.
  # @return [Boolean] `true` if the address is a valid IPv4 address, `false` otherwise.
  def is_ipv4?(address)
    address = address.ensure_string

    mo = /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/.match(address)
    (mo && mo.captures.all? {|i| i.to_i < 256}) ? true : false
  end

  # Checks if and address is a valid IPv6 address.
  #
  # @param address [String] The address to check.
  # @return [Boolean] `true` if the address is a valid IPv6 address, `false` otherwise.
  def is_ipv6?(address)
    address = address.ensure_string

    catch(:valid) do
      # IPv6 (normal)
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ address
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
      throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ address
      # IPv6 (IPv4 compat)
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ address && is_ipv4?($')
      throw(:valid, true) if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && is_ipv4?($')
      throw(:valid, true) if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ address && is_ipv4?($')

      false
    end
  end


  # Pads a number to make it print friendly.
  #
  # @param num [Fixnum] The number to pad.
  # @param len [Fixnum] The minimum length of the padded string.
  # @return [String] The padded number.
  def pad_number(num, len = nil)
    num.to_integer.to_s.rjust([len.to_integer, 2].max, "0")
  end

  # Gets the current logger of the application.
  #
  # @return [Logger] The current logger of the application.
  def get_logger
    @logger ||= Bovem::Logger.create(@config.log_file, @config.log_level, @log_formatter)
  end

  # Gets the path for the launch agent file.
  #
  # @param name [String] The base name for the agent.
  # @return [String] The path for the launch agent file.
  def launch_agent_path(name = "it.cowtech.akaer")
    ENV["HOME"] + "/Library/LaunchAgents/#{name}.plist"
  end

  # Executes a shell command.
  #
  # @param command [String] The command to execute.
  # @return [Boolean] `true` if command succeeded, `false` otherwise.
  def execute_command(command)
    Kernel.system(command)
  end

  # Computes the list of address to manage.
  #
  # @param type [Symbol] The type of addresses to consider. Valid values are `:ipv4`, `:ipv6`, otherwise all addresses are considered.
  # @return [Array] The list of addresses to add or remove from the interface.
  def compute_addresses(type = :all)
    config = self.config
    config.addresses.present? ? filter_addresses(config, type) : generate_addresses(config, type)
  end

  # Returns a unique (singleton) instance of the application.
  #
  # @param command [Mamertes::Command] The current Mamertes command.
  # @param locale [Symbol] The locale to use for the application.
  # @param force [Boolean] If to force recreation of the instance.
  # @return [Application] The unique (singleton) instance of the application.
  def self.instance(command, locale = nil, force = false)
    @instance = nil if force
    @instance ||= Akaer::Application.new(command, locale)
  end

  private
    # Reads the configuration.
    #
    # @param options [Hash] The options to read.
    def read_configuration(options)
      begin
        @config = Akaer::Configuration.new(options["configuration"], options, @logger)
        @logger = nil
        @logger = get_logger
      rescue Bovem::Errors::InvalidConfiguration => e
        @logger ? @logger.fatal(e.message) : Bovem::Logger.create("STDERR").fatal(i18n.logging_failed(log_file))
        raise ::SystemExit
      end
    end

    # Filters a list of addresses to return just certain type(s).
    #
    # @param config [Configuration] The current configuration.
    # @param type [Symbol] The type of addresses to return.
    # @return [Array] A list of IPs.
    def filter_addresses(config, type)
      filters =  [:ipv4, :ipv6].select {|i| type == i || type == :all }.compact

      config.addresses.select { |address|
        filters.any? {|filter| send("is_#{filter}?", address) }
      }.compact.uniq
    end

    # Generates a list of addresses which are immediate successors of a start address.
    #
    # @param config [Configuration] The current configuration.
    # @param type [Symbol] The type of addresses to return.
    # @return [Array] A list of IPs.
    def generate_addresses(config, type)
      begin
        ip = IPAddr.new(config.start_address.ensure_string)
        raise ArgumentError if type != :all && !ip.send("#{type}?")

        (config.aliases > 0 ? config.aliases : 5).times.collect {|_|
          current = ip
          ip = ip.succ
          current
        }
      rescue ArgumentError
        []
      end
    end
end