bitlbee_config 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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"