summaryrefslogtreecommitdiff
path: root/app/models/sip_account.rb
blob: cdb609dbd637a31fc1d6f74939bd23687bfc0124 (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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# encoding: UTF-8

class SipAccount < ActiveRecord::Base
  include ActionView::Helpers::TextHelper

  attr_accessible :auth_name, :caller_name, :password, :voicemail_pin, 
                  :tenant_id, :call_waiting, :clir, :clip_no_screening,
                  :clip, :description, :callforward_rules_act_per_sip_account,
                  :hotdeskable, :gs_node_id, :language_code

  # Associations:
  #
  belongs_to :sip_accountable, :polymorphic => true, :touch => true
  
  has_many :phone_sip_accounts, :uniq => true
  has_many :phones, :through => :phone_sip_accounts
  
  has_many :phone_numbers, :as => :phone_numberable, :dependent => :destroy
  has_many :call_forwards, :as => :call_forwardable, :dependent => :destroy

  belongs_to :tenant
  belongs_to :sip_domain

  has_many :softkeys, :dependent => :destroy, :order => :position

  has_many :voicemail_messages, :foreign_key => 'username', :primary_key => 'auth_name'

  has_many :call_histories, :as => :call_historyable, :dependent => :destroy

  has_one :voicemail_setting, :class_name => "VoicemailSetting", :primary_key => 'auth_name', :foreign_key => 'username', :dependent => :destroy

  belongs_to :gs_node

  belongs_to :language, :foreign_key => 'language_code', :primary_key => 'code'

  has_many :group_memberships, :as => :item, :dependent => :destroy, :uniq => true
  has_many :groups, :through => :group_memberships

  has_many :ringtones, :as => :ringtoneable, :dependent => :destroy

  has_many :call_legs, :class_name => 'Call'
  has_many :b_call_legs, :class_name => 'Call', :foreign_key => 'b_sip_account_id'

  # Delegations:
  #
  delegate :host, :to => :sip_domain, :allow_nil => true
  delegate :realm, :to => :sip_domain, :allow_nil => true

  # Validations:
  #
  validates_presence_of :caller_name
  validates_presence_of :sip_accountable
  validates_presence_of :tenant
  validates_presence_of :sip_domain
  
  validate_sip_password :password
  
  validates_format_of :voicemail_pin, :with => /[0-9]+/,
    :allow_nil => true, :allow_blank => true
  
  validates_uniqueness_of :auth_name, :scope => :sip_domain_id

  # Before and after hooks:
  # 
  before_save :save_value_of_to_s
  after_save :create_voicemail_setting, :if => :'voicemail_setting == nil'
  before_validation :find_and_set_tenant_id
  before_validation :set_sip_domain_id
  before_validation :convert_umlauts_in_caller_name
  before_destroy :remove_sip_accounts_or_logout_phones

  # Sync other nodes when this is a cluster.
  #
  validates_presence_of :uuid
  validates_uniqueness_of :uuid

  after_create { self.create_on_other_gs_nodes('sip_accountable', self.sip_accountable.try(:uuid)) }
  after_create :create_default_group_memberships
  after_destroy :destroy_on_other_gs_nodes
  after_update { self.update_on_other_gs_nodes('sip_accountable', self.sip_accountable.try(:uuid)) }

  after_update :log_out_phone_if_not_local

  def to_s
    truncate((self.caller_name || "SipAccount ID #{self.id}"), :length => GsParameter.get('TO_S_MAX_CALLER_NAME_LENGTH')) + " (#{truncate(self.auth_name, :length => GsParameter.get('TO_S_MAX_LENGTH_OF_AUTH_NAME'))}@...#{self.host.split(/\./)[2,3].to_a.join('.') if self.host })"
  end

  def calls
    self.call_legs + self.b_call_legs
  end
  
  def call_forwarding_toggle( call_forwarding_service, to_voicemail = nil )
    if ! self.phone_numbers.first
      errors.add(:base, "You must provide at least one phone number")
    end

    service_id = CallForwardCase.where(:value => call_forwarding_service).first.id

    call_forwarding_master = self.phone_numbers.first.call_forwards.where(:call_forward_case_id => service_id).order(:active).all(:conditions => 'source IS NULL OR source = ""').first
    if ! call_forwarding_master
      errors.add(:base, "No call forwarding entries found that could be toggled")
      return false
    end

    if call_forwarding_master.active
      call_forwarding_master.active = false
    else
      if call_forwarding_service = 'assistant' && call_forwarding_master.destinationable_type == 'HuntGroup' && call_forwarding_master.destinationable
        if call_forwarding_master.destinationable.hunt_group_members.where(:active => true).count > 0
          call_forwarding_master.active = true
        else
          call_forwarding_master.active = false
        end
      end
    end

    self.phone_numbers.each do |phone_number|
      call_forwarding = phone_number.call_forwards.where(:call_forward_case_id => service_id).order(:active).all(:conditions => 'source IS NULL OR source = ""').first
      if ! call_forwarding
        call_forwarding = CallForward.new()
        call_forwarding.call_forwardable = phone_number
      end

      if to_voicemail == nil 
        to_voicemail = call_forwarding_master.to_voicemail
      end

      call_forwarding.call_forward_case_id = call_forwarding_master.call_forward_case_id
      call_forwarding.timeout = call_forwarding_master.timeout
      call_forwarding.destination = call_forwarding_master.destination
      call_forwarding.source = call_forwarding_master.source
      call_forwarding.depth = call_forwarding_master.depth
      call_forwarding.active = call_forwarding_master.active
      call_forwarding.to_voicemail = to_voicemail

      if ! call_forwarding.save
        call_forwarding.errors.messages.each_with_index do |(error_key, error_message), index|
          errors.add(error_key, "number: #{phone_number}: #{error_message}")
        end
      end
    end

    if errors.empty? 
      return call_forwarding_master
    end

    return false
  end

  def registration
    return SipRegistration.where(:sip_user => self.auth_name).first
  end

  def call( phone_number )
    require 'freeswitch_event'
    return FreeswitchAPI.execute(
      'originate', 
      "{origination_uuid=#{UUID.new.generate},origination_caller_id_number='#{phone_number}',origination_caller_id_name='Call'}user/#{self.auth_name} #{phone_number}", 
      true
    );
  end


  def target_sip_accounts_by_permission(permission)
    target_groups = Group.union(self.groups.collect{|g| g.permission_targets(permission)})
    target_groups = target_groups + Group.union(self.sip_accountable.groups.collect{|g| g.permission_targets(permission)})
    sip_accounts = []
    GroupMembership.where(:group_id => target_groups).each do |group_membership|
      if group_membership.item.class == User || group_membership.item.class == Tenant
        sip_accounts = sip_accounts + group_membership.item.sip_accounts
      elsif group_membership.item.class == SipAccount
        sip_accounts << group_membership.item
      end
        
      sip_accounts = sip_accounts.uniq
    end

    return sip_accounts
  end


  private
      
  def save_value_of_to_s
    self.value_of_to_s = self.to_s
  end
  
  def find_and_set_tenant_id
      if self.new_record? and self.tenant_id != nil
        return        
      else
        tenant = case self.sip_accountable_type
          when 'Tenant'    ; sip_accountable
          when 'UserGroup' ; sip_accountable.tenant
          when 'User'      ; sip_accountable.try(:current_tenant) || sip_accountable.try(:tenants).try(:last)
          else nil
        end
        self.tenant_id = tenant.id if tenant != nil
      end
  end
  
  def set_sip_domain_id
    self.sip_domain_id = self.tenant.try(:sip_domain_id)
  end
  
  def convert_umlauts_in_caller_name
    if !self.caller_name.blank?
      self.caller_name = self.caller_name.sub(/ä/,'ae').
                              sub(/Ä/,'Ae').
                              sub(/ü/,'ue').
                              sub(/Ü/,'Ue').
                              sub(/ö/,'oe').
                              sub(/Ö/,'Oe').
                              sub(/ß/,'ss')

      self.caller_name = self.caller_name.gsub(/[^a-zA-Z0-9\-\,\:\. ]/,'_')
    end
  end

  # Make sure that a tenant phone goes back to the tenant and doesn't
  # get deleted with this user.
  #
  def remove_sip_accounts_or_logout_phones
    self.phones.each do |phone|
      if phone.sip_accounts.where(:sip_accountable_type => 'Tenant').count > 0
        phone.user_logout
      else
        PhoneSipAccount.delete_all(:sip_account_id => self.id)
      end
    end
    self.reload
  end

  # log out phone if sip_account is not on this node
  def log_out_phone_if_not_local
    if self.gs_node_id && GsNode.count > 1 && ! GsNode.where(:ip_address => GsParameter.get('HOMEBASE_IP_ADDRESS'), :id => self.gs_node_id).first
      self.phones.each do |phone|
        phone.user_logout;
      end
    end
  end

  def create_voicemail_setting
    voicemail_setting = VoicemailSetting.new()
    voicemail_setting.username = self.auth_name
    voicemail_setting.domain = self.sip_domain.try(:host)
    voicemail_setting.password = self.voicemail_pin
    voicemail_setting.notify = true
    voicemail_setting.attachment = true
    voicemail_setting.mark_read = true
    voicemail_setting.purge = false
    voicemail_setting.save
  end

  def create_default_group_memberships
    default_groups = Hash.new()
    templates = GsParameter.get('SipAccount', 'group', 'default')
    if templates.class == Array
      templates.each do |group_name|
        default_groups[group_name] = true
      end
    end

    templates = GsParameter.get("SipAccount.#{self.sip_accountable_type}", 'group', 'default')
    if templates.class == Array
      templates.each do |group_name|
        default_groups[group_name] = true
      end
    end

    default_groups.each do |group_name, value|
      group = Group.where(:name => group_name).first
      if group
        self.group_memberships.create(:group_id => group.id)
      end
    end
  end

end