summaryrefslogtreecommitdiff
path: root/app/models/phone.rb
blob: 8b41b59d427ae0d23846b6348617329c96c5a6da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
require 'scanf'

class Phone < ActiveRecord::Base
  
  attr_accessible :mac_address, :ip_address, :http_user, :http_password, 
                  :phone_model_id, :hot_deskable, :nightly_reboot,
                  :provisioning_key, :provisioning_key_active, :fallback_sip_account_id, :tenant
  
  # Associations
  #
  belongs_to :phone_model
  belongs_to :phoneable, :polymorphic => true, :touch => true

  has_many :phone_sip_accounts, :dependent => :destroy, :uniq => true, :order => :position
  has_many :sip_accounts, :through => :phone_sip_accounts
  
  belongs_to :tenant
  belongs_to :fallback_sip_account, :class_name => "SipAccount"

  # Validations
  #
  before_validation :sanitize_mac_address
  before_validation :destroy_fallback_sip_account_if_not_hot_deskable
  
  validates_presence_of     :mac_address
  validate_mac_address      :mac_address
  validates_uniqueness_of   :mac_address
  
  validates_uniqueness_of   :ip_address,
    :if => Proc.new { |me| ! me.ip_address.blank? }
  validate_ip_address       :ip_address,
    :if => Proc.new { |me| ! me.ip_address.blank? }
  
  validates_presence_of     :phone_model
  validates_presence_of     :phoneable

  validates_uniqueness_of   :fallback_sip_account_id, :allow_nil => true
  
  before_save :save_last_ip_address
  before_save :destroy_phones_sip_accounts_if_phoneable_changed
  before_save :remove_ip_address_when_mac_address_was_changed
  
  # State machine:
  #
  default_scope where(:state => 'active')
  state_machine :initial => :active do
    
    event :deactivate do
      transition [:active] => :deactivated
    end
    
    event :activate do
      transition [:deactivated] => :active
    end
  end
  
  def to_s
    "%s %s %s" % [
      pretty_mac_address,
      "(#{self.phone_model})",
      self.ip_address ? "(#{self.ip_address})" : "",
    ]
  end
  
  def pretty_mac_address
    return [].fill('%02X', 0, 6).join(':') % self.mac_address.scanf( '%2X' * 6 )
  end


  def resync(reboot = false, sip_account = nil)
    if ! self.phone_model || ! self.phone_model.manufacturer
      return false
    end

    if self.phone_model.manufacturer.ieee_name == 'SNOM Technology AG'
      if !sip_account
        self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).each do |sip_account_associated|
          if sip_account_associated.registration
            sip_account = sip_account_associated
            break
          end
        end
      end

      if ! sip_account or ! sip_account.registration
        require 'open-uri'
        begin
          if open("http://#{self.ip_address}/advanced_update.htm?reboot=Reboot", :http_basic_authentication=>[self.http_user, self.http_password], :proxy => nil)
            return true
          end
        rescue
          return false
        end
      end

      require 'freeswitch_event'
      event = FreeswitchEvent.new("NOTIFY")
      event.add_header("profile", "gemeinschaft")
      event.add_header("event-string", "check-sync;reboot=#{reboot.to_s}")
      event.add_header("user", sip_account.auth_name)
      event.add_header("host", sip_account.sip_domain.host)
      event.add_header("content-type", "application/simple-message-summary")   
      return event.fire()

    elsif self.phone_model.manufacturer.ieee_name == 'Siemens Enterprise CommunicationsGmbH & Co. KG'
      require 'open-uri'
      begin
        if open("http://#{self.ip_address}:8085/contact_dls.html/ContactDLS", :http_basic_authentication=>[self.http_user, self.http_password], :proxy => nil)
          return true
        end
      rescue
        return false
      end
    end
      
    return false
  end


  # OPTIMIZE i18n translations
  def user_login(user, sip_account = nil)
    if ! self.hot_deskable
      errors.add(:hot_deskable, "Phone not hot-deskable")
      return false
    end

    phones_affected = Hash.new()
    sip_accounts = Array.new(1, sip_account)
    
    if !sip_account
      sip_accounts = user.sip_accounts.where(:hotdeskable => true).all
    end

    if sip_accounts.blank?
      errors.add(:sip_accounts, "No hot-deskable Sip Accounts available")
      return false
    end

    sip_account_resync = self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).first

    phones_affected.each_pair do |id,phone|
      if phone.id != self.id
        phone.user_logout()
      end
    end

    PhoneSipAccount.where(:phone_id => self.id).destroy_all

    self.phoneable = user
    sip_accounts.each do |sip_account|
      if ! self.sip_accounts.where(:id => sip_account.id).first
        self.sip_accounts.push(sip_account)
      end
    end
    
    @not_destroy_phones_sip_accounts = true
    if ! self.save
      return false
    end
    
    sleep(0.5)

    if ! self.resync(true, sip_account_resync)
      errors.add(:resync, "Resync failed")
    end

    return true
  end


  # OPTIMIZE i18n translations
  def user_logout
    if ! self.hot_deskable
      errors.add(:hot_deskable, "Phone not hot-deskable")
      return false
    end

    sip_account = self.sip_accounts.where(:sip_accountable_type => self.phoneable_type).first

    if self.tenant
      self.phoneable = self.tenant
      if ! self.save
        errors.add(:phoneable, "Could not change owner")
        return false
      end
    end

    sleep(0.5)

    if ! self.resync(true, sip_account)
      errors.add(:resync, "Resync failed")
      return false
    end

    return true
  end

  private
  
  # Sanitize MAC address.
  #
  def sanitize_mac_address
    if self.mac_address.split(/:/).count == 6 && self.mac_address.length < 17
      splitted_mac_address = self.mac_address.split(/:/)
      self.mac_address = splitted_mac_address.map{|part| (part.size == 1 ? "0#{part}" : part)}.join('')
    end
    self.mac_address = self.mac_address.to_s.upcase.gsub( /[^A-F0-9]/, '' )
  end
  
  # Saves the last IP address.
  #
  def save_last_ip_address
    if self.ip_address_changed? \
    && self.ip_address != self.ip_address_was
      self.last_ip_address = self.ip_address_was
    end
  end
  
  # When ever the parent of a phone changes all the SIP accounts associations
  # are destroyed unless this is a user logout operation
  #
  def destroy_phones_sip_accounts_if_phoneable_changed
    if (self.phoneable_type_changed? || self.phoneable_id_changed?) && ! @not_destroy_phones_sip_accounts
      self.phone_sip_accounts.destroy_all
    end
  end

  def remove_ip_address_when_mac_address_was_changed
    if self.mac_address_changed?
      self.ip_address = nil
      self.last_ip_address = nil
    end
  end

  def destroy_fallback_sip_account_if_not_hot_deskable
    if !self.hot_deskable?
      self.fallback_sip_account_id = nil
    end
  end
  
end