bitlbee_config 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 23649e6dfedc017442195984c024a994f56918f6
4
+ data.tar.gz: 58bbf7b3f91899ab5e64ee9973a0c9be493f8254
5
+ SHA512:
6
+ metadata.gz: b351506aa977419dd3b1a971fea6c537af1f0b0459174f4f9c6b58871fdd7539814d5ae91c26fc3d7e452a6b64a7b592ea7b4811917c9822cd8955ae6109f950
7
+ data.tar.gz: 04a3e809d005689cce48b060b271e11b424c98d89c6cb003b0e419d2f51021d935cf1769fb7df51d111ec53b2ec94c93fe429ffb7a7c3ac4984c24a3cc60653b
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/cache
data/.rubocop.yml ADDED
@@ -0,0 +1,35 @@
1
+ AllCops:
2
+ Includes:
3
+ - Guardfile
4
+ Excludes:
5
+ - tmp/**
6
+
7
+ CollectionMethods:
8
+ PreferredMethods:
9
+ Enabled: false
10
+ #collect: 'collect'
11
+ #collect!: 'collect!'
12
+ #inject: 'inject'
13
+ #detect: 'detect'
14
+ #find_all: 'select'
15
+
16
+ CommentAnnotation:
17
+ Enabled: false
18
+
19
+ Documentation:
20
+ Enabled: false
21
+
22
+ Encoding:
23
+ Enabled: false
24
+
25
+ LineLength:
26
+ Enabled: false
27
+
28
+ MethodLength:
29
+ Max: 20
30
+
31
+ RegexpLiteral:
32
+ Enabled: false
33
+
34
+ StringLiterals:
35
+ EnforcedStyle: double_quotes
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ script:
7
+ - bundle exec rake travis
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard :minitest do
2
+ watch(%r{^test/(.*)_test\.rb})
3
+ watch("lib/bitlbee_config.rb") { "test" }
4
+ watch(%r{^lib/bitlbee_config/(.*/)?([^/]+)\.rb}) { |m| "test/#{ m[1] }#{ m[2] }_test.rb" }
5
+ watch(%r{^test/helper\.rb}) { "test" }
6
+ watch(%r{^test/fixtures/.*\.xml}) { "test" }
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Nils Landt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ Currently only for internal use in the [bitlbee chef cookbook](https://github.com/promisedlandt/cookbook-bitlbee).
2
+
3
+ Interfaces are bound to change / be removed, you do **not** want to use this yet.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "rubocop/rake_task"
4
+ require "yard"
5
+
6
+
7
+ desc "Start guard for minitest"
8
+ task :guard do
9
+ sh "guard --no-interactions --notify false --clear"
10
+ end
11
+
12
+ desc "Run rubocop linter"
13
+ Rubocop::RakeTask.new(:rubocop) do |task|
14
+ task.formatters = %w(simple)
15
+ end
16
+
17
+ YARD::Rake::YardocTask.new do |t|
18
+ t.options = ["--no-private"]
19
+ end
20
+
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << "lib" << "test"
23
+ test.pattern = "test/**/*_test.rb"
24
+ test.verbose = true
25
+ end
26
+
27
+ desc "for travis-ci run"
28
+ task travis: :default
29
+
30
+ task default: [:test, :rubocop]
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bitlbee_config/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bitlbee_config"
8
+ spec.version = BitlbeeConfig::VERSION
9
+ spec.authors = ["Nils Landt"]
10
+ spec.email = ["nils@promisedlandt.de"]
11
+ spec.description = %q{Create, read and modify configuration files for bitlbee}
12
+ spec.summary = spec.description
13
+ spec.homepage = "https://github.com/promisedlandt/bitlbee_config"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri", "~> 1.6"
22
+ spec.add_dependency "mixlib-shellout", "~> 1"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake", "~> 10.1"
26
+ spec.add_development_dependency "debugger", "~> 1.6"
27
+ spec.add_development_dependency "rubocop", "~> 0.1"
28
+ spec.add_development_dependency "minitest", "~> 5"
29
+ spec.add_development_dependency "guard", "~> 2"
30
+ spec.add_development_dependency "guard-minitest", "~> 2"
31
+ spec.add_development_dependency "yard", "~> 0.8"
32
+ spec.add_development_dependency "mocha", "~> 1"
33
+ spec.add_development_dependency "coveralls"
34
+ end
@@ -0,0 +1,94 @@
1
+ module BitlbeeConfig
2
+ # An account with an IM service, e.g. an ICQ or Skype account
3
+ class Account
4
+ include BitlbeeConfig::XmlBuildable
5
+
6
+ attr_accessor :protocol, :handle, :password, :autoconnect, :tag, :server, :cleartext_password, :user, :settings
7
+
8
+ class << self
9
+ # @param [Nokogiri::XML::Element] xml XML element to create account from
10
+ # @return [BitlbeeConfig::Account|BitlbeeConfig::Accounts::Icq] The newly created account
11
+ def from_xml(xml)
12
+ new_account = {}
13
+
14
+ # get setting attributes
15
+ xml.attributes.each do |att_name, att_value|
16
+ new_account[att_name.to_sym] = att_value.text
17
+ end
18
+
19
+ # get setting children
20
+ xml.children.select { |node| node.is_a?(Nokogiri::XML::Element) }.each do |setting_element|
21
+ new_account[setting_element.values.first.to_sym] = setting_element.text
22
+ end
23
+
24
+ create_new_account(new_account)
25
+ end
26
+
27
+ # Creates a new account. The class of the account varies by the attributes.
28
+ #
29
+ # @param [Hash] account Attributes for the account the be created
30
+ # @return [BitlbeeConfig::Account|BitlbeeConfig::Accounts::Icq] The newly created account
31
+ def create_new_account(account = {})
32
+ account_class = case
33
+ when account[:handle] =~ /#{ BitlbeeConfig::Accounts::Facebook::USERNAME_SUFFIX }$/
34
+ BitlbeeConfig::Accounts::Facebook
35
+ when account[:server] == BitlbeeConfig::Accounts::Gtalk::DEFAULT_GTALK_SERVER
36
+ BitlbeeConfig::Accounts::Gtalk
37
+ when account[:protocol].to_s == "jabber"
38
+ BitlbeeConfig::Accounts::Jabber
39
+ when account[:protocol].to_s == "steam"
40
+ BitlbeeConfig::Accounts::Steam
41
+ when account[:protocol].to_s == "oscar"
42
+ BitlbeeConfig::Accounts::Icq
43
+ else
44
+ BitlbeeConfig::Account
45
+ end
46
+
47
+ account_class.new(account)
48
+ end
49
+ end
50
+
51
+ # @param [Hash] options
52
+ # @option options [String] :protocol Protocol used for the account
53
+ # @option options [String] :handle The handle / login for the account, e.g. your ICQ number
54
+ # @option options [String] :tag A label for easy recognition of your account, e.g. "jabber-work"
55
+ # @option options [String] :autoconnect Autoconnect to account on identify?
56
+ # @option options [String] :server Overwrite server to connect to - for example if you use a proxy, or stunnel
57
+ # @option options [String] :cleartext_password Cleartext password for this account. Will be encrypted before written
58
+ # @option options [String] :password The encrypted password of the account, as it appears in the XML document (encrypted_password with the users password
59
+ # @option options [String] :user User this account belongs to. Needed for it's cleartext password
60
+ def initialize(options = {})
61
+ @protocol ||= options.delete(:protocol)
62
+ @handle ||= options.delete(:handle)
63
+ @tag ||= options.delete(:tag)
64
+ @autoconnect ||= options.delete(:autoconnect)
65
+ @server ||= options.delete(:server)
66
+ @password ||= options.delete(:password)
67
+ @cleartext_password ||= options.delete(:cleartext_password)
68
+ @user ||= options.delete(:user)
69
+ @settings = options || {}
70
+ end
71
+
72
+ # Uniquely identify this account - currently by protocol and handle
73
+ def id
74
+ "#{ @protocol }##{ @handle }"
75
+ end
76
+
77
+ # When a cleartext password and the user's cleartext password are given, encrypt the cleartext_password with the user's cleartext password
78
+ def regenerate_password_if_needed
79
+ @password = @cleartext_password.encrypt_bitlbee_password(@user.cleartext_password) if @user && @user.cleartext_password && @cleartext_password
80
+ end
81
+
82
+ # @param [Nokogiri::XML::Builder] xml_builder All XML will be added to this builder
83
+ def build_xml(xml_builder)
84
+ regenerate_password_if_needed
85
+
86
+ account_options = [:password, :protocol, :handle, :autoconnect, :tag, :server].each_with_object({}) do |option, options_hash|
87
+ value = instance_variable_get("@#{ option }")
88
+ options_hash[option] = value unless value.nil? || value.empty?
89
+ end
90
+
91
+ to_xml_with_options(xml_builder, account_options)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,44 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Facebook < Accounts::Jabber
4
+ attr_accessor :auth_strategy
5
+
6
+ USERNAME_SUFFIX = "@chat.facebook.com"
7
+
8
+ def initialize(options = {})
9
+ # username needs to be downcased when not using OAuth. Just always downcase it, always works
10
+ options[:handle] &&= options[:handle].downcase
11
+
12
+ # unless otherwise specified, use oauth authentication
13
+ @auth_strategy = options.delete(:auth_strategy) || :oauth
14
+
15
+ options = add_auth_strategy_options(options)
16
+
17
+ super({ tag: "fb",
18
+ nick_format: "%full_name"
19
+ }.merge(options))
20
+
21
+ ensure_handle_is_suffixed if @handle
22
+ end
23
+
24
+ # We don't want the user to have to enter "@chat.facebook.com" with their handle, so we do it for them
25
+ def ensure_handle_is_suffixed
26
+ @handle += USERNAME_SUFFIX unless @handle =~ /#{ USERNAME_SUFFIX }$/
27
+ end
28
+
29
+ # Depending on the authentication strategy, we set a few things
30
+ #
31
+ # @param [Hash] init_options The options passed to initialize
32
+ # @return [Hash] The original options, which may be modified now
33
+ def add_auth_strategy_options(init_options)
34
+ case @auth_strategy
35
+ when :oauth
36
+ init_options[:oauth] = "on"
37
+ init_options[:cleartext_password] ||= ""
38
+ end
39
+
40
+ init_options
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Gtalk < Accounts::Jabber
4
+ DEFAULT_GTALK_SERVER = "talk.google.com"
5
+
6
+ def initialize(options = {})
7
+ # unlike facebook, we don't give support password authentication here, only oauth
8
+ super({ tag: "gtalk",
9
+ nick_format: "%full_name",
10
+ server: DEFAULT_GTALK_SERVER,
11
+ oauth: "on",
12
+ cleartext_password: ""
13
+ }.merge(options))
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Hipchat < Accounts::Jabber
4
+ USERNAME_SUFFIX = "@chat.hipchat.com"
5
+
6
+ def initialize(options = {})
7
+ super({ tag: "hipchat",
8
+ nick_format: "%full_name"
9
+ }.merge(options))
10
+
11
+ ensure_handle_is_suffixed if @handle
12
+ end
13
+
14
+ # We don't want the user to have to enter "@chat.facebook.com" with their handle, so we do it for them
15
+ def ensure_handle_is_suffixed
16
+ @handle += USERNAME_SUFFIX unless @handle =~ /#{ USERNAME_SUFFIX }$/
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Icq < Account
4
+ def initialize(options = {})
5
+ @protocol = :oscar
6
+ super
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Jabber < Account
4
+ def initialize(options = {})
5
+ @protocol = :jabber
6
+ super
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module BitlbeeConfig
2
+ module Accounts
3
+ class Steam < Account
4
+ def initialize(options = {})
5
+ @protocol = :steam
6
+ @tag = "steam"
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ require "bitlbee_config/accounts/icq"
2
+ require "bitlbee_config/accounts/jabber"
3
+ require "bitlbee_config/accounts/steam"
4
+
5
+ require "bitlbee_config/accounts/facebook"
6
+ require "bitlbee_config/accounts/gtalk"
7
+ require "bitlbee_config/accounts/hipchat"
@@ -0,0 +1,43 @@
1
+ module BitlbeeConfig
2
+ # A channel on the bitlbee server. Can either be a control channel, in which you issue commands to the BitlBee root user, or a chat channel, which is a multi contact conversation
3
+ class Channel
4
+ include BitlbeeConfig::XmlBuildable
5
+
6
+ attr_accessor :name, :type, :settings
7
+
8
+ class << self
9
+ # @param [Nokogiri::XML::Element] xml XML element to create channel from
10
+ # @return [BitlbeeConfig::Channel] The newly created channel
11
+ def from_xml(xml)
12
+ new_channel = {}
13
+
14
+ # get setting attributes
15
+ xml.attributes.each do |att_name, att_value|
16
+ new_channel[att_name.to_sym] = att_value.text
17
+ end
18
+
19
+ # get setting children
20
+ xml.children.select { |node| node.is_a?(Nokogiri::XML::Element) }.each do |setting_element|
21
+ new_channel[setting_element.values.first.to_sym] = setting_element.text
22
+ end
23
+
24
+ BitlbeeConfig::Channel.new(new_channel)
25
+ end
26
+ end
27
+
28
+ # @param [Hash] options
29
+ # @option options [String] :name Channel name. Don't forget # or &
30
+ # @option options ["control"|"chat"] :type Type of channel
31
+ # @option options [String] All other entries will be converted to settings
32
+ def initialize(options = {})
33
+ @name = options.delete(:name)
34
+ @type = options.delete(:type)
35
+ @settings = options || {}
36
+ end
37
+
38
+ # @param [Nokogiri::XML::Builder] xml_builder All XML will be added to this builder
39
+ def build_xml(xml_builder)
40
+ to_xml_with_options(xml_builder, name: @name, type: @type)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,81 @@
1
+ module BitlbeeConfig
2
+ # A configuration file for BitlBee. Has exactly one user
3
+ class Config
4
+ attr_accessor :user
5
+
6
+ class << self
7
+ # Create a config for every user XML in the given directory
8
+ #
9
+ # @param [String] directory Directory to load from
10
+ # @return [Array<BitlbeeConfig::User>]
11
+ def from_directory(directory)
12
+ Dir.glob(File.join(directory, "*.xml")).each_with_object([]) do |path_to_file, config_collection|
13
+ config_collection << BitlbeeConfig::Config.from_file(path_to_file)
14
+ end
15
+ end
16
+
17
+ # Create a config for one specific user, reading the XML from a directory
18
+ #
19
+ # @param [String] directory Directory to load from
20
+ # @param [String] username User to load config for
21
+ # @return [BitlbeeConfig::User|nil]
22
+ def from_directory_for_user(directory, username)
23
+ from_file(File.join(directory, BitlbeeConfig::User.username_to_filename(username)))
24
+ end
25
+
26
+ # Create a config from an XML file
27
+ #
28
+ # @param [String] file_path Path to the XML configuration file
29
+ # @return [BitlbeeConfig::Config] The created configuration object
30
+ def from_file(file_path)
31
+ from_xml(File.read(file_path))
32
+ end
33
+
34
+ # @param [String] xml The XML to parse
35
+ # @return [BitlbeeConfig::Config] The created configuration object
36
+ def from_xml(xml)
37
+ doc = Nokogiri::XML(xml)
38
+
39
+ # Bitlbee config says there can be only one user per XML file
40
+ user_xml = doc.xpath("//user").first
41
+
42
+ BitlbeeConfig::Config.new(user: BitlbeeConfig::User.from_xml(user_xml))
43
+ end
44
+
45
+ # Deletes file for a specified user name from the given directory
46
+ #
47
+ # @param [String] directory Directory to check for user XML files
48
+ # @param [String] username User to delete config for
49
+ def delete_from_directory_for_user(directory, username)
50
+ file_to_delete = File.join(directory, BitlbeeConfig::User.username_to_filename(username))
51
+ File.delete(file_to_delete) if File.exists?(file_to_delete)
52
+ end
53
+ end
54
+
55
+ # @param [Hash] options
56
+ # @option options [Array<BitlbeeConfig::User>] :user User this configuration belongs to
57
+ def initialize(options = {})
58
+ @user = options.delete(:user)
59
+ end
60
+
61
+ def to_xml
62
+ builder = Nokogiri::XML::Builder.new do |xml_builder|
63
+ @user.build_xml(xml_builder)
64
+ end
65
+
66
+ builder.doc.root.to_xml
67
+ end
68
+
69
+ # Saves the configuration to a specified directory
70
+ # User files are named <username.downcase>.xml
71
+ #
72
+ # @param [String] path_to_dir Directory to save user xml to
73
+ def save_to_directory(path_to_dir)
74
+ user_file_path = File.join(path_to_dir, "#{ @user.nick.downcase }.xml")
75
+
76
+ File.open(user_file_path, "w") do |file|
77
+ file.write(to_xml)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,46 @@
1
+ class String
2
+ # @see http://wiki.bitlbee.org/DecodingPasswords
3
+
4
+ # Get the bitlbee password hash for this string
5
+ #
6
+ # @return [String] The bitlbee password hash for this string
7
+ def to_bitlbee_password_hash
8
+ cmd = Mixlib::ShellOut.new(" bitlbee -x hash '#{ self }'")
9
+ cmd.run_command
10
+ cmd.error!
11
+ cmd.stdout.chomp
12
+ end
13
+
14
+ # Check whether this string matches a given bitlbee password hash
15
+ #
16
+ # @return [Boolean]
17
+ def matches_bitlbee_password_hash?(hash)
18
+ cmd = Mixlib::ShellOut.new(" bitlbee -x chkhash '#{ hash }' '#{ self }'")
19
+ cmd.run_command
20
+ cmd.exitstatus == 0
21
+ end
22
+
23
+ # Encrypt a bitlbee account password
24
+ # Used to encrypt passwords for individual IM accounts with the password of the bitlbee user
25
+ #
26
+ # @param [String] key Key to encrypt with
27
+ # @return [String] The encrypted version of this string
28
+ def encrypt_bitlbee_password(key)
29
+ cmd = Mixlib::ShellOut.new(" bitlbee -x enc '#{ key }' '#{ self }'")
30
+ cmd.run_command
31
+ cmd.error!
32
+ cmd.stdout.chomp
33
+ end
34
+
35
+ # Decrypt a bitlbee account password
36
+ # Used to decrypt passwords for individual IM accounts with the password of the bitlbee user
37
+ #
38
+ # @param [String] key Key to decrypt with
39
+ # @return [String] The cleartext version of this string
40
+ def decrypt_bitlbee_password(key)
41
+ cmd = Mixlib::ShellOut.new(" bitlbee -x dec '#{ key }' '#{ self }'")
42
+ cmd.run_command
43
+ cmd.error!
44
+ cmd.stdout.chomp
45
+ end
46
+ end
@@ -0,0 +1 @@
1
+ require "bitlbee_config/core_extensions/string.rb"
@@ -0,0 +1,25 @@
1
+ module BitlbeeConfig
2
+ module XmlBuildable
3
+ def to_xml_with_options(xml_builder, options = {}, &block)
4
+ # Accounts can have many different classes, but the element is always named "account"
5
+ element_name = case
6
+ when self.is_a?(BitlbeeConfig::Account)
7
+ "account"
8
+ else
9
+ self.class.name.split("::").last.downcase
10
+ end
11
+
12
+ xml_builder.send(element_name, options) do |xml|
13
+ if @settings
14
+ @settings.each do |setting_name, setting_value|
15
+ xml.setting(name: setting_name) do |setting_xml|
16
+ setting_xml.text setting_value
17
+ end
18
+ end
19
+ end
20
+
21
+ block.call(xml) if block
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require "bitlbee_config/mixins/xml_buildable"