ahoy 0.0.1

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.
@@ -0,0 +1,18 @@
1
+ base = File.expand_path("#{File.dirname(__FILE__)}/ahoy")
2
+ libs = %W{chat contact contact_list errors user}
3
+
4
+ libs.each do |lib|
5
+ require "#{base}/#{lib}"
6
+ end
7
+
8
+ module Ahoy
9
+ SERVICE_TYPE = "_presence._tcp"
10
+
11
+ def self.more_coming?(reply)
12
+ reply.flags.to_i & DNSSD::Flags::MoreComing > 0
13
+ end
14
+
15
+ def self.add?(reply)
16
+ reply.flags.to_i & DNSSD::Flags::Add > 0
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../ahoy")
2
+
3
+ module Ahoy
4
+ class Broadcast
5
+ def initialize(name, location="nowhere", domain="local")
6
+ user = Ahoy::User.new(name, location, domain)
7
+ user.sign_in
8
+ sleep 1
9
+ @chats = user.contacts.map {|contact| user.chat(contact)}
10
+ end
11
+
12
+ def send(message)
13
+ @chats.each do |chat|
14
+ begin
15
+ chat.send(message)
16
+ rescue
17
+ @chats.delete(chat)
18
+ end
19
+ end
20
+ end
21
+
22
+ def close
23
+ @chats.each {|chat| chat.close}
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'xmpp4r'
3
+ require File.expand_path("#{File.dirname(__FILE__)}/xmpp4r_hack")
4
+
5
+ module Ahoy
6
+ class Chat
7
+ attr_reader :user, :contact, :client
8
+ private :client
9
+
10
+ def initialize(user, contact)
11
+ @user = user
12
+ @contact = contact
13
+ @client = nil
14
+ end
15
+
16
+ # May raise Ahoy::ContactOfflineError
17
+ #
18
+ def start
19
+ user.contact.resolve.getaddrinfo
20
+ connect
21
+ end
22
+
23
+ # May raise Ahoy::ContactOfflineError
24
+ #
25
+ def send(message)
26
+ start unless client
27
+
28
+ message = Jabber::Message.new(contact.name, message)
29
+ message.type = :chat
30
+ begin
31
+ @client.send(message)
32
+ rescue IOError
33
+ connect
34
+ retry
35
+ end
36
+ message
37
+ end
38
+
39
+ def close
40
+ @client.close
41
+ @client = nil
42
+ end
43
+
44
+ private
45
+ def connect
46
+ contact.resolve
47
+
48
+ @client = Jabber::Client.new(Jabber::JID.new(user.name))
49
+ sleep 0.5
50
+ begin
51
+ @client.connect(contact.target, contact.port, user.contact.ip)
52
+ rescue Errno::ECONNREFUSED
53
+ raise Ahoy::ContactOfflineError.new("Contact Offline")
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'dnssd'
3
+
4
+ module Ahoy
5
+ class Contact
6
+ attr_reader :name, :domain, :target, :ip, :port, :interface
7
+ attr_accessor :online
8
+
9
+ def initialize(name, domain="local")
10
+ @name = name
11
+ @domain = domain
12
+ @target = nil
13
+ @ip = nil
14
+ @port = nil
15
+ @interface = nil
16
+ @online = true
17
+ end
18
+
19
+ def fullname
20
+ [name, Ahoy::SERVICE_TYPE, domain].join(".")
21
+ end
22
+
23
+ def ==(other)
24
+ other.is_a?(self.class) && other.fullname == fullname
25
+ end
26
+
27
+ def online?
28
+ online
29
+ end
30
+
31
+ def resolve
32
+ service = DNSSD::Service.new
33
+ main = Thread.current
34
+ service.resolve(name, Ahoy::SERVICE_TYPE, domain) do |resolved|
35
+ next if Ahoy::more_coming?(resolved)
36
+ service.stop unless service.stopped?
37
+ @target = resolved.target
38
+ @port = resolved.port
39
+ @interface = resolved.interface
40
+ main.run
41
+ end
42
+ Thread.stop unless service.stopped?
43
+ self
44
+ end
45
+
46
+ def getaddrinfo
47
+ service = DNSSD::Service.new
48
+ main = Thread.current
49
+ service.getaddrinfo(target, DNSSD::Service::IPv4, 0, interface) do |addressed|
50
+ service.stop unless service.stopped?
51
+ @ip = addressed.address
52
+ main.run
53
+ end
54
+ Thread.stop unless service.stopped?
55
+ self
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,76 @@
1
+ require 'thread'
2
+ require 'weakref'
3
+
4
+ module Ahoy
5
+ class ContactList
6
+ include Enumerable
7
+
8
+ attr_reader :list, :weak_list, :lock, :user
9
+ private :list, :weak_list, :lock
10
+
11
+ def initialize(user)
12
+ @user = user
13
+ @list = []
14
+ @weak_list = []
15
+ @lock = Mutex.new
16
+
17
+ start_browse
18
+ end
19
+
20
+ def each(&block)
21
+ lock.synchronize do
22
+ list.each(&block)
23
+ end
24
+ end
25
+
26
+ private
27
+ def start_browse
28
+ DNSSD.browse(Ahoy::SERVICE_TYPE) do |browsed|
29
+ # next if Ahoy::more_coming?(browsed)
30
+ if Ahoy::add?(browsed) && browsed.name != user.name
31
+ add(Ahoy::Contact.new(browsed.name, browsed.domain))
32
+ elsif Ahoy::add?(browsed)
33
+ user.contact = Ahoy::Contact.new(browsed.name, browsed.domain)
34
+ else
35
+ remove(browsed.fullname)
36
+ end
37
+ end
38
+ end
39
+
40
+ def add(contact)
41
+ lock.synchronize do
42
+ unless list.find {|in_list| contact == in_list}
43
+ contact ||= find_in_weak_list(contact)
44
+ contact.online = true
45
+ list.push(contact)
46
+ end
47
+ end
48
+ end
49
+
50
+ def remove(fullname)
51
+ fullname = fullname.fullname if fullname.respond_to?(:fullname)
52
+ lock.synchronize do
53
+ contact = list.find {|c| c.fullname == fullname}
54
+ if contact
55
+ list.delete(contact)
56
+ contact.online = false
57
+ weak_list.push(WeakRef.new(contact))
58
+ contact
59
+ end
60
+ end
61
+ end
62
+
63
+ def find_in_weak_list(contact)
64
+ existing_contact = nil
65
+ Thread.exclusive do
66
+ GC.disable
67
+ weak_list.select! {|ref| ref.weakref_alive?}
68
+ contact_ref = weak_list.find {|ref| contact == ref}
69
+ existing_contact = contact_ref.__getobj__
70
+ GC.enable
71
+ end
72
+ existing_contact
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module Ahoy
2
+ ContactOfflineError = Class.new(StandardError)
3
+ end
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'dnssd'
3
+
4
+ module Ahoy
5
+ class User
6
+ attr_reader :short_name, :location, :domain, :contacts
7
+ attr_accessor :port, :flags, :interface, :contact
8
+
9
+ def initialize(name, location="nowhere", domain="local")
10
+ @short_name = name
11
+ @location = location
12
+ @domain = domain
13
+
14
+ @contacts = Ahoy::ContactList.new(self)
15
+ @contact = nil
16
+
17
+ @port = 5562
18
+ @flags = 0
19
+ @interface = "en0"
20
+ end
21
+
22
+ def name
23
+ "#{short_name}@#{location}"
24
+ end
25
+
26
+ def sign_in(status="avail", msg=nil)
27
+ @registrar = DNSSD.register(
28
+ name,
29
+ Ahoy::SERVICE_TYPE,
30
+ domain,
31
+ port,
32
+ txt_record(status, msg),
33
+ flags.to_i,
34
+ interface)
35
+ end
36
+
37
+ def contact
38
+ sleep 0.01 until @contact
39
+ @contact
40
+ end
41
+
42
+ def chat(contact)
43
+ Ahoy::Chat.new(self, contact)
44
+ end
45
+
46
+ private
47
+ def txt_record(status, msg)
48
+ DNSSD::TextRecord.new(
49
+ "txtvers" => 1,
50
+ "port.p2pj" => port,
51
+ "status" => status,
52
+ "msg" => msg,
53
+ "1st" => short_name)
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'xmpp4r'
3
+
4
+ module Jabber
5
+ class Connection
6
+ def connect(host, port, local_host=nil, local_port=nil)
7
+ @host = host
8
+ @port = port
9
+ # Reset is_tls?, so that it works when reconnecting
10
+ @tls = false
11
+
12
+ Jabber::debuglog("CONNECTING:\n#{@host}:#{@port}, local #{local_host}:#{local_port}")
13
+ @socket = TCPSocket.new(@host, @port, local_host, local_port)
14
+
15
+ # We want to use the old and deprecated SSL protocol (usually on port 5223)
16
+ if @use_ssl
17
+ ssl = OpenSSL::SSL::SSLSocket.new(@socket)
18
+ ssl.connect # start SSL session
19
+ ssl.sync_close = true
20
+ Jabber::debuglog("SSL connection established.")
21
+ @socket = ssl
22
+ end
23
+
24
+ start
25
+
26
+ accept_features
27
+
28
+ @keepaliveThread = Thread.new do
29
+ Thread.current.abort_on_exception = true
30
+ keepalive_loop
31
+ end
32
+ end
33
+ end
34
+
35
+ class Client
36
+ def connect(host, port, local_host=nil, local_port=nil)
37
+ super
38
+ self
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ahoy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mat Sadler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-27 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dnssd
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.3.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: xmpp4r
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: "0.5"
34
+ version:
35
+ description: Serverless Messaging using DNSDS/mDNS, XMPP, and Ruby
36
+ email: mat@sourcetagsandcodes.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/ahoy/broadcast.rb
45
+ - lib/ahoy/chat.rb
46
+ - lib/ahoy/contact.rb
47
+ - lib/ahoy/contact_list.rb
48
+ - lib/ahoy/errors.rb
49
+ - lib/ahoy/user.rb
50
+ - lib/ahoy/xmpp4r_hack.rb
51
+ - lib/ahoy.rb
52
+ has_rdoc: true
53
+ homepage: http://sourcetagsandcodes.com
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --inline-source
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Bonjour Chat for Ruby
81
+ test_files: []
82
+