Class: Msn::NotificationServer

Inherits:
EventMachine::Connection
  • Object
show all
Includes:
Protocol
Defined in:
lib/msn/notification_server.rb

Overview

:nodoc:

Constant Summary

ContactsNamespace =
{'ns' => 'http://www.msn.com/webservices/AddressBook'}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Protocol

#answer_challenge, #clear_event, #handle_event, #handle_normal_command, #handle_payload_command, #method_missing, #on_event, #receive_binary_data, #receive_line, #send_command, #send_command_internal, #send_payload_command, #send_payload_command_and_wait

Constructor Details

#initialize(messenger) ⇒ NotificationServer

Returns a new instance of NotificationServer



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/msn/notification_server.rb', line 10

def initialize(messenger)
  @messenger = messenger
  @guid = Guid.new.to_s
  @switchboards = {}

  @message_id = 1
  @message_ids = {}

  on_event 'ADL' do |header, data|
    data = Nokogiri::XML(data)
    domain = data.xpath('//ml/d').first['n']
    c = data.xpath('//ml/d/c').first
    username = c['n']
    display_name = c['f']
    messenger.contact_request "#{username}@#{domain}", display_name
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Msn::Protocol

Instance Attribute Details

#guidObject (readonly)

Returns the value of attribute guid



8
9
10
# File 'lib/msn/notification_server.rb', line 8

def guid
  @guid
end

#messengerObject (readonly)

Returns the value of attribute messenger



7
8
9
# File 'lib/msn/notification_server.rb', line 7

def messenger
  @messenger
end

Instance Method Details

#cache_key_xmlObject



109
110
111
# File 'lib/msn/notification_server.rb', line 109

def cache_key_xml
  @cache_key ? "<CacheKey>#{REXML::Text.normalize @cache_key}</CacheKey>" : ""
end

#create_switchboard(email, host_and_port) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/msn/notification_server.rb', line 161

def create_switchboard(email, host_and_port)
  host, port = host_and_port.split(':')
  switchboard = EM.connect host, port, Msn::Switchboard, messenger
  switchboard.on_event 'ACK' do |header|
    notify_ack header[1].to_i, :ack
  end
  switchboard.on_event 'NAK' do |header|
    notify_ack header[1].to_i, :nak
  end
  switchboard.on_event 'BYE' do |header|
    destroy_switchboard email if header[1] =~ /#{email}/
  end
  @switchboards[email] = switchboard
end

#destroy_switchboard(email) ⇒ Object



176
177
178
179
# File 'lib/msn/notification_server.rb', line 176

def destroy_switchboard(email)
  switchboard = @switchboards.delete email
  switchboard.close_connection if switchboard
end

#get_contactsObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/msn/notification_server.rb', line 68

def get_contacts
  contacts = {}

  xml = Nokogiri::XML(get_soap_contacts.body)
  xml.xpath('//ns:Membership', ContactsNamespace).each do |membership|
    role = membership.xpath('ns:MemberRole', ContactsNamespace).text
    membership.xpath('ns:Members/ns:Member', ContactsNamespace).each do |member|
      cid = member.xpath('ns:CID', ContactsNamespace).text
      email = member.xpath('ns:PassportName', ContactsNamespace).text
      display_name = member.xpath('ns:DisplayName', ContactsNamespace)

      contact = contacts[cid] ||= Msn::Contact.new(email, display_name ? display_name.text : nil)
      case role
      when 'Allow' then contact.allow = true
      when 'Block' then contact.block = true
      when 'Reverse' then contact.reverse = true
      when 'Pending' then contact.pending = true
      end
    end
  end

  contacts.values
end

#get_soap_contacts(url = "https://local-bay.contacts.msn.com/abservice/SharingService.asmx") ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/msn/notification_server.rb', line 92

def get_soap_contacts(url = "https://local-bay.contacts.msn.com/abservice/SharingService.asmx")
  msn_get_contacts_template_file = File.expand_path('../soap/msn_get_contacts_template.xml', __FILE__)
  msn_get_contacts_template = ERB.new File.read(msn_get_contacts_template_file)
  soap = msn_get_contacts_template.result(binding)

  RestClient.post url, soap, {
    'SOAPAction' => 'http://www.msn.com/webservices/AddressBook/FindMembership',
    'Content-Type' => 'text/xml',
  }
rescue RestClient::ExceptionWithResponse => ex
  # Assume the CacheKey changed and we need to request to another URL
  xml = Nokogiri::XML(ex.response.body)
  cache_key = xml.xpath("//ns:CacheKey", ContactsNamespace).first
  @cache_key = cache_key.text if cache_key
  get_soap_contacts(ex.response.headers[:location])
end

#loginObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/msn/notification_server.rb', line 119

def 
  Fiber.new do
    begin
      ver "MSNP18", "CVR0"
      cvr "0x0409", "winnt", "5.1", "i386", "MSNMSGR", "8.5.1302", "BC01", username
      response = usr "SSO", "I", username
      if response[0] == "XFR" && response[2] == "NS"
        host, port = response[3].split ':'
        @reconnect_host, @reconnect_port = response[3].split ':'
        close_connection
      else
        (response[4], response[5])
      end
    rescue Msn::AuthenticationError => ex
      messenger.(ex.message)
      close_connection
    end
  end.resume
end

#login_to_nexus(policy, nonce) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/msn/notification_server.rb', line 139

def (policy, nonce)
  @nexus = Msn::Nexus.new self, policy, nonce

  first_msg = true
  on_event('MSG') do
    if first_msg
      first_msg = false
      messenger.ready
    end
  end

  on_event('RNG') do |header|
    switchboard = create_switchboard header[5], header[2]
    switchboard.ans username_guid, header[4], header[1]
  end

  response = usr "SSO", "S", @nexus.sso_token, @nexus.secret, guid
  if response[2] != "OK"
    raise Msn::AuthenticationError.new("Didn't receive OK from SSO")
  end
end

#notify_ack(trid, status) ⇒ Object



181
182
183
184
# File 'lib/msn/notification_server.rb', line 181

def notify_ack(trid, status)
  id = @message_ids.delete(trid)
  messenger.accept_message_ack id, status if id
end

#passwordObject



64
65
66
# File 'lib/msn/notification_server.rb', line 64

def password
  messenger.password
end

#post_initObject



113
114
115
116
117
# File 'lib/msn/notification_server.rb', line 113

def post_init
  super

  
end

#send_message(email, text) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/msn/notification_server.rb', line 32

def send_message(email, text)
  message_id = @message_id
  @message_id += 1

  switchboard = @switchboards[email]
  if switchboard
    trid = switchboard.send_message text
    @message_ids[trid] = message_id
  else
    Fiber.new do
      response = xfr "SB"
      switchboard = create_switchboard email, response[3]
      switchboard.on_event 'JOI' do
        switchboard.clear_event 'JOI'
        trid = switchboard.send_message text
        @message_ids[trid] = message_id
      end
      switchboard.usr username_guid, response[5]
      cal_response = switchboard.cal email
      if cal_response[0] == '217'
        messenger.accept_message_ack message_id, :offline
      end
    end.resume
  end

  message_id
end

#unbindObject



186
187
188
189
190
191
192
193
194
# File 'lib/msn/notification_server.rb', line 186

def unbind
  if @reconnect_host
    reconnect @reconnect_host, @reconnect_port.to_i
    @reconnect_host = @reconnect_port = nil
    
  else
    messenger.disconnected
  end
end

#usernameObject



60
61
62
# File 'lib/msn/notification_server.rb', line 60

def username
  messenger.username
end

#username_guidObject



28
29
30
# File 'lib/msn/notification_server.rb', line 28

def username_guid
  @username_guid ||= "#{messenger.username};{#{guid}}"
end