Class: EPPClient::AFNIC

Inherits:
Base
  • Object
show all
Includes:
RGP, SecDNS
Defined in:
lib/epp-client/afnic.rb

Overview

This handles the AFNIC specificities.

See www.afnic.fr/doc/interface/epp

Constant Summary collapse

SCHEMAS_AFNIC =
%w(
  frnic-1.4
).freeze

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ AFNIC

Sets the default for AFNIC, that is, server and port, according to AFNIC’s documentation. www.afnic.fr/doc/interface/epp

Optional Attributes

:test

sets the server to be the test server.



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/epp-client/afnic.rb', line 26

def initialize(args)
  args[:server] ||= if args.delete(:test) == true
                      'epp.sandbox.nic.fr'
                    else
                      'epp.nic.fr'
                    end
  @services = EPPClient::SCHEMAS_URL.values_at('domain', 'contact')
  args[:port] ||= 700
  super(args)
  @extensions << EPPClient::SCHEMAS_URL['frnic']
end

Instance Method Details

#contact_afnic_qualification(xml) ⇒ Object

:nodoc:



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/epp-client/afnic.rb', line 468

def contact_afnic_qualification(xml) #:nodoc:
  contact = xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:quaData/frnic:contact', EPPClient::SCHEMAS_URL)
  ret = { :id => contact.xpath('frnic:id', EPPClient::SCHEMAS_URL).text }
  qP = contact.xpath('frnic:qualificationProcess', EPPClient::SCHEMAS_URL)
  ret[:qualificationProcess] = { :s => qP.attr('s').value }
  ret[:qualificationProcess][:lang] = qP.attr('lang').value if qP.attr('lang')
  unless (leI = contact.xpath('frnic:legalEntityInfos', EPPClient::SCHEMAS_URL)).empty?
    ret[:legalEntityInfos] = legalEntityInfos(leI)
  end
  reach = contact.xpath('frnic:reachability', EPPClient::SCHEMAS_URL)
  ret[:reachability] = { :reStatus => reach.xpath('frnic:reStatus', EPPClient::SCHEMAS_URL).text }
  unless (voice = reach.xpath('frnic:voice', EPPClient::SCHEMAS_URL)).empty?
    ret[:reachability][:voice] = voice.text
  end
  unless (email = reach.xpath('frnic:email', EPPClient::SCHEMAS_URL)).empty?
    ret[:reachability][:email] = email.text
  end
  ret
end

#contact_create(contact) ⇒ Object

Extends the EPPClient::Contact#contact_create so that the specific AFNIC create informations can be sent, the additionnal informations are :

either :

:legalEntityInfos

indicating that the contact is an organisation with the following informations :

:idStatus

indicates the identification process status.

:legalStatus

should be either company, association or other.

:siren

contains the SIREN number of the organisation.

:VAT

is optional and contains the VAT number of the organisation.

:trademark

is optional and contains the trademark number of the organisation.

:DUNS

is optional and contains the Data Universal Numbering System number of the organisation.

:local

is optional and contains an identifier local to the eligible country.

:asso

indicates the organisation is an association and contains either a waldec or a decl and a publ :

:waldec

contains the waldec id of the association.

:decl

optionally indicate the date of the association was declared at the prefecture.

:publ

contains informations regarding the publication in the “Journal Officiel” :

:date

the date of publication.

:page

the page the announce is on.

:announce

the announce number on the page (optional).

:individualInfos

indicating that the contact is a person with the following informations :

:idStatus

indicates the identification process status.

:birthDate

the date of birth of the contact.

:birthCity

the city of birth of the contact.

:birthPc

the postal code of the city of birth.

:birthCc

the country code of the place of birth.

Additionnaly, when the contact is a person, there can be the following informations :

:firstName

the first name of the person. (The last name being stored in the name field in the postalInfo.)

:list

with the value of restrictedPublication mean that the element diffusion should be restricted.

Optionnaly, there can be :

:reachable

the contact is reachable through the optional :media.

The returned information contains new keys :

:idStatus

indicates the identification process status. It’s only present when the created contact was created with the :individualInfos or :legalEntityInfos extensions.

:nhStatus

is a boolean indicating wether the contact is really new, or if there was already a contact with the exact same informations in the database, in which case, it has been returned.



359
360
361
# File 'lib/epp-client/afnic.rb', line 359

def contact_create(contact)
  super # placeholder so that I can add some doc
end

#contact_create_process(xml) ⇒ Object

:nodoc:



363
364
365
366
367
368
369
370
# File 'lib/epp-client/afnic.rb', line 363

def contact_create_process(xml) #:nodoc:
  ret = super
  unless (creData = xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:creData', EPPClient::SCHEMAS_URL)).empty?
    ret[:nhStatus] = creData.xpath('frnic:nhStatus', EPPClient::SCHEMAS_URL).attr('new').value == '1'
    ret[:idStatus] = creData.xpath('frnic:idStatus', EPPClient::SCHEMAS_URL).text
  end
  ret
end

#contact_create_xml(contact) ⇒ Object

:nodoc:



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
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/epp-client/afnic.rb', line 235

def contact_create_xml(contact) #:nodoc:
  ret = super

  ext = extension do |xml|
    xml.ext(:xmlns => EPPClient::SCHEMAS_URL['frnic']) do
      xml.create do
        xml.contact do
          if contact.key?(:legalEntityInfos)
            lEI = contact[:legalEntityInfos]
            xml.legalEntityInfos do
              xml.idStatus(lEI[:idStatus]) if lEI.key?(:idStatus)
              xml.legalStatus(:s => lEI[:legalStatus])
              [:siren, :VAT, :trademark, :DUNS, :local].each do |val|
                xml.__send__(val, lEI[val]) if lEI.key?(val)
              end
              if lEI.key?(:asso)
                asso = lEI[:asso]
                xml.asso do
                  if asso.key?(:waldec)
                    xml.waldec(asso[:waldec])
                  else
                    xml.decl(asso[:decl]) if asso.key?(:decl)
                    attrs = { :page => asso[:publ][:page] }
                    attrs[:announce] = asso[:publ][:announce] if asso[:publ].key?(:announce)
                    xml.publ(attrs, asso[:publ][:date])
                  end
                end
              end
            end
          else
            xml.list(contact[:list]) if contact.key?(:list)
            if contact.key?(:individualInfos)
              iI = contact[:individualInfos]
              xml.individualInfos do
                xml.idStatus(iI[:idStatus]) if iI.key?(:idStatus)
                xml.birthDate(iI[:birthDate])
                xml.birthCity(iI[:birthCity]) if iI.key?(:birthCity)
                xml.birthPc(iI[:birthPc]) if iI.key?(:birthPc)
                xml.birthCc(iI[:birthCc])
              end
            end
            xml.firstName(contact[:firstName]) if contact.key?(:firstName)
          end
          if contact.key?(:reachable)
            reachable = contact[:reachable]

            raise ArgumentError, 'reachable has to be a Hash' unless reachable.is_a?(Hash)

            xml.reachable(reachable, 1)
          end
        end
      end
    end
  end

  insert_extension(ret, ext)
end

#contact_delete(_args) ⇒ Object

Raises an exception, as contacts are deleted with a garbage collector.

Raises:

  • (NotImplementedError)


382
383
384
# File 'lib/epp-client/afnic.rb', line 382

def contact_delete(_args)
  raise NotImplementedError, 'Contacts are deleted with a garbage collector'
end

#contact_info(contact) ⇒ Object

Extends the EPPClient::Contact#contact_info so that the specific AFNIC check informations are processed, the additionnal informations are :

either :

:legalEntityInfos

indicating that the contact is an organisation with the following informations :

:legalStatus

should be either company, association or other.

:idStatus

indicates the identification process status. Has optional :when and :source attributes.

:siren

contains the SIREN number of the organisation.

:VAT

is optional and contains the VAT number of the organisation.

:trademark

is optional and contains the trademark number of the organisation.

:DUNS

is optional and contains the Data Universal Numbering System number of the organisation.

:local

is optional and contains an identifier local to the eligible country.

:asso

indicates the organisation is an association and contains either a waldec or a decl and a publ :

:waldec

contains the waldec id of the association.

:decl

optionally indicate the date of the association was declared at the prefecture.

:publ

contains informations regarding the publication in the “Journal Officiel” :

:date

the date of publication.

:page

the page the announce is on.

:announce

the announce number on the page (optional).

:individualInfos

indicating that the contact is a person with the following informations :

:idStatus

indicates the identification process status. Has optional :when and :source attributes.

:birthDate

the date of birth of the contact.

:birthCity

the city of birth of the contact.

:birthPc

the postal code of the city of birth.

:birthCc

the country code of the place of birth.

Additionnaly, when the contact is a person, there can be the following informations :

:firstName

the first name of the person. (The last name being stored in the name field in the postalInfo.)

:list

with the value of restrictedPublication mean that the element diffusion should be restricted.

Optionnaly, there can be :

:obsoleted

the contact info is obsolete since/from the optional date :when.

:reachable

the contact is reachable through the optional :media since/from the optional date :when. The info having been specified by the :source.



179
180
181
# File 'lib/epp-client/afnic.rb', line 179

def contact_info(contact)
  super # placeholder so that I can add some doc
end

#contact_info_process(xml) ⇒ Object

:nodoc:



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
# File 'lib/epp-client/afnic.rb', line 183

def contact_info_process(xml) #:nodoc:
  ret = super
  unless (contact = xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:infData/frnic:contact', EPPClient::SCHEMAS_URL)).empty?
    unless (list = contact.xpath('frnic:list', EPPClient::SCHEMAS_URL)).empty?
      ret[:list] = list.map(&:text)
    end
    unless (firstName = contact.xpath('frnic:firstName', EPPClient::SCHEMAS_URL)).empty?
      ret[:firstName] = firstName.text
    end
    unless (iI = contact.xpath('frnic:individualInfos', EPPClient::SCHEMAS_URL)).empty?
      ret[:individualInfos] = {}
      ret[:individualInfos][:birthDate] = Date.parse(iI.xpath('frnic:birthDate', EPPClient::SCHEMAS_URL).text)
      unless (r = iI.xpath('frnic:idStatus', EPPClient::SCHEMAS_URL)).empty?
        ret[:individualInfos][:idStatus] = { :value => r.text }
        ret[:individualInfos][:idStatus][:when] = r.attr('when').value if r.attr('when')
        ret[:individualInfos][:idStatus][:source] = r.attr('source').value if r.attr('source')
      end
      %w(birthCity birthPc birthCc).each do |val|
        unless (r = iI.xpath("frnic:#{val}", EPPClient::SCHEMAS_URL)).empty?
          ret[:individualInfos][val.to_sym] = r.text
        end
      end
    end
    unless (leI = contact.xpath('frnic:legalEntityInfos', EPPClient::SCHEMAS_URL)).empty?
      ret[:legalEntityInfos] = legalEntityInfos(leI)
    end
    unless (obsoleted = contact.xpath('frnic:obsoleted', EPPClient::SCHEMAS_URL)).empty?
      if obsoleted.text != '0'
        ret[:obsoleted] = {}
        if (v_when = obsoleted.attr('when'))
          ret[:obsoleted][:when] = DateTime.parse(v_when.value)
        end
      end
    end
    unless (reachable = contact.xpath('frnic:reachable', EPPClient::SCHEMAS_URL)).empty?
      if reachable.text != '0'
        ret[:reachable] = {}
        if (v_when = reachable.attr('when'))
          ret[:reachable][:when] = DateTime.parse(v_when.value)
        end
        if (media = reachable.attr('media'))
          ret[:reachable][:media] = media.value
        end
        if (source = reachable.attr('source'))
          ret[:reachable][:source] = source.value
        end
      end
    end
  end
  ret
end

#contact_update(args) ⇒ Object

Extends the EPPClient::Contact#contact_update so that the specific AFNIC update informations can be sent, the additionnal informations are :

:add/:rem

adds or removes the following datas :

:list

with the value of restrictedPublication mean that the element diffusion should/should not be restricted.

:idStatus

indicates the identification process status.

:reachable

the contact is reachable through the optional :media.



429
430
431
# File 'lib/epp-client/afnic.rb', line 429

def contact_update(args)
  super # placeholder so that I can add some doc
end

#contact_update_xml(args) ⇒ Object

:nodoc:



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/epp-client/afnic.rb', line 386

def contact_update_xml(args) #:nodoc:
  ret = super

  return ret unless [:add, :rem].any? { |c| args.key?(c) && [:list, :reachable, :idStatus].any? { |k| args[c].key?(k) } }

  ext = extension do |xml|
    xml.ext(:xmlns => EPPClient::SCHEMAS_URL['frnic']) do
      xml.update do
        xml.contact do
          [:add, :rem].each do |c|
            next unless args.key?(c) && [:list, :reachable, :idStatus].any? { |k| args[c].key?(k) }
            xml.__send__(c) do
              xml.list(args[c][:list]) if args[c].key?(:list)
              xml.idStatus(args[c][:idStatus]) if args[c].key?(:idStatus)
              if args[c].key?(:reachable)
                reachable = args[c][:reachable]

                raise ArgumentError, 'reachable has to be a Hash' unless reachable.is_a?(Hash)

                xml.reachable(reachable, 1)
              end
            end
          end
        end
      end
    end
  end

  insert_extension(ret, ext)
end

#domain_afnic_trade_response(xml) ⇒ Object

:nodoc:



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/epp-client/afnic.rb', line 490

def domain_afnic_trade_response(xml) #:nodoc:
  dom = xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:trdData/frnic:domain', EPPClient::SCHEMAS_URL)
  ret = {
    :name     => dom.xpath('frnic:name', EPPClient::SCHEMAS_URL).text,
    :trStatus => dom.xpath('frnic:trStatus', EPPClient::SCHEMAS_URL).text,
    :reID     => dom.xpath('frnic:reID', EPPClient::SCHEMAS_URL).text,
    :reDate   => DateTime.parse(dom.xpath('frnic:reDate', EPPClient::SCHEMAS_URL).text),
    :acID     => dom.xpath('frnic:acID', EPPClient::SCHEMAS_URL).text,
  }

  # FIXME: there are discrepencies between the 1.2 xmlschema, the documentation and the reality, I'm trying to stick to reality here.
  %w(reHldID acHldID).each do |f|
    unless (field = dom.xpath("frnic:#{f}", EPPClient::SCHEMAS_URL)).empty?
      ret[f.to_sym] = field.text
    end
  end
  %w(rhDate ahDate).each do |f|
    unless (field = dom.xpath("frnic:#{f}", EPPClient::SCHEMAS_URL)).empty?
      ret[f.to_sym] = DateTime.parse(field.text)
    end
  end
  ret
end

#domain_check(*domains) ⇒ Object

Extends the EPPClient::Domain#domain_check so that the specific AFNIC check informations are processed, the additionnal informations are :

:reserved

the domain is a reserved name.

:rsvReason

the optional reason why the domain is reserved.

:forbidden

the domain is a forbidden name.

:fbdReason

the optional reason why the domain is forbidden.



45
46
47
# File 'lib/epp-client/afnic.rb', line 45

def domain_check(*domains)
  super # placeholder so that I can add some doc
end

#domain_check_process(xml) ⇒ Object

:nodoc:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/epp-client/afnic.rb', line 49

def domain_check_process(xml) # :nodoc:
  ret = super
  xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:chkData/frnic:domain/frnic:cd', EPPClient::SCHEMAS_URL).each do |dom|
    name = dom.xpath('frnic:name', EPPClient::SCHEMAS_URL)
    hash = ret.find { |d| d[:name] == name.text }
    hash[:reserved] = name.attr('reserved').value == '1'
    unless (reason = dom.xpath('frnic:rsvReason', EPPClient::SCHEMAS_URL).text).empty?
      hash[:rsvReason] = reason
    end
    hash[:forbidden] = name.attr('forbidden').value == '1'
    unless (reason = dom.xpath('frnic:fbdReason', EPPClient::SCHEMAS_URL).text).empty?
      hash[:fbdReason] = reason
    end
  end
  ret
end

#domain_create(args) ⇒ Object

Extends the EPPClient::Domain#domain_create to make sure there’s no :ns, :dsData or :keyData records, AFNIC’s servers sends quite a strange error when there is.

Raises:

  • (ArgumentError)


375
376
377
378
379
# File 'lib/epp-client/afnic.rb', line 375

def domain_create(args)
  raise ArgumentError, "You can't create a domain with ns records, you must do an update afterwards" if args.key?(:ns)
  raise ArgumentError, "You can't create a domain with ds or key records, you must do an update afterwards" if args.key?(:dsData) || args.key?(:keyData)
  super
end

#domain_info(domain) ⇒ Object

Extends the EPPClient::Domain#domain_info so that the specific AFNIC :status can be added.



68
69
70
# File 'lib/epp-client/afnic.rb', line 68

def domain_info(domain)
  super # placeholder so that I can add some doc
end

#domain_info_process(xml) ⇒ Object

:nodoc:



72
73
74
75
76
77
78
79
# File 'lib/epp-client/afnic.rb', line 72

def domain_info_process(xml) #:nodoc:
  ret = super
  unless (frnic_status = xml.xpath('epp:extension/frnic:ext/frnic:resData/frnic:infData/frnic:domain/frnic:status', EPPClient::SCHEMAS_URL)).empty?
    ret[:status] ||= [] # The status is optional, there may be none at this point.
    ret[:status] += frnic_status.map { |s| s.attr('s') }
  end
  ret
end

#domain_update(args) ⇒ Object

Extends the EPPClient::Domain#domain_update so that AFNIC’s weirdnesses can be taken into account.

AFNIC does not support ns/hostObj, only ns/hostAttr/Host*, so, take care of this here. Also, you can only do one of the following at a time :

  • update contacts

  • update name servers

  • update status & authInfo



442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/epp-client/afnic.rb', line 442

def domain_update(args)
  if args.key?(:chg) && args[:chg].key?(:registrant)
    raise ArgumentError, 'You need to do a trade or recover operation to change the registrant'
  end
  has_contacts = args.key?(:add) && args[:add].key?(:contacts) || args.key?(:add) && args[:add].key?(:contacts)
  has_ns = args.key?(:add) && args[:add].key?(:ns) || args.key?(:add) && args[:add].key?(:ns)
  has_other = args.key?(:add) && args[:add].key?(:status) || args.key?(:add) && args[:add].key?(:status) || args.key?(:chg) && args[:chg].key?(:authInfo)
  if [has_contacts, has_ns, has_other].count { |v| v } > 1
    raise ArgumentError, "You can't update all that at one time"
  end
  [:add, :rem].each do |ar|
    if args.key?(ar) && args[ar].key?(:ns) && args[ar][:ns].first.is_a?(String)
      args[ar][:ns] = args[ar][:ns].map { |ns| { :hostName => ns } }
    end
  end
  super
end

#poll_reqObject

Extends the EPPClient::Poll#poll_req to be able to parse quallification response extension.



462
463
464
# File 'lib/epp-client/afnic.rb', line 462

def poll_req
  super # placeholder so that I can add some doc
end