From cddc787b697d3be4c92dccbb3761aa2df87588a1 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Wed, 9 Apr 2014 21:19:04 +0200 Subject: [PATCH] Hetzner support and version bump --- pythonwhois/parse.py | 5 ++ setup.py | 2 +- test/data/ismtgoxdeadyet.com | 99 +++++++++++++++++++++++ test/target_default/ismtgoxdeadyet.com | 1 + test/target_normalized/ismtgoxdeadyet.com | 1 + 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 test/data/ismtgoxdeadyet.com create mode 100644 test/target_default/ismtgoxdeadyet.com create mode 100644 test/target_normalized/ismtgoxdeadyet.com diff --git a/pythonwhois/parse.py b/pythonwhois/parse.py index f50a4d3..fd84d1f 100644 --- a/pythonwhois/parse.py +++ b/pythonwhois/parse.py @@ -48,6 +48,7 @@ grammar = { 'Expired\s?[.]*:?\s*?(?P.+)', 'Domain Expiration Date\s?[.]*:?\s*?(?P.+)', 'paid-till:\s*(?P.+)', + 'expiration_date:\s*(?P.+)', 'renewal:\s*(?P.+)', 'expire:\s*(?P.+)'], 'updated_date': ['\[Last Updated\]\s*(?P.+)', @@ -66,6 +67,7 @@ grammar = { 'Record last updated\s?[.]*:\s?(?P.+)', 'Modified\s?[.]*:\s?(?P.+)', '(C|c)hanged:\s*(?P.+)', + 'last_update:\s*(?P.+)', 'Last Update\s?[.]*:\s?(?P.+)', 'Last updated on (?P.+) [a-z]{3,4}', 'Last updated:\s*(?P.+)', @@ -480,6 +482,7 @@ def parse_registrants(data): " Registrant:\n (?P.+)\n\n Registrant type:\n .*\n\n Registrant's address:\n The registrant .* opted to have", # Nominet (.uk) with hidden address " Registrant:\n (?P.+)\n\n Registrant type:\n .*\n\n Registrant's address:\n (?P.+)\n(?: (?P.+)\n)?(?: (?P.+)\n)? (?P.+)\n (?P.+)\n (?P.+)\n (?P.+)", # Nominet (.uk) with visible address "Registrant contact:\n (?P.+)\n (?P.*)\n (?P.+), (?P.+) (?P.+) (?P.+)\n\n", # Fabulous.com + "registrant-name:\s*(?P.+)\nregistrant-type:\s*(?P.+)\nregistrant-address:\s*(?P.+)\nregistrant-postcode:\s*(?P.+)\nregistrant-city:\s*(?P.+)\nregistrant-country:\s*(?P.+)\n(?:registrant-phone:\s*(?P.+)\n)?(?:registrant-email:\s*(?P.+)\n)?", # Hetzner "Registrant Contact Information :[ ]*\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n\n", # GAL Communication "Contact Information : For Customer # [0-9]+[ ]*\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n\n", # GAL Communication alternative (private WHOIS) format? " Registrant:\n (?P.+)\n (?P.+)\n (?P.+) (?P\S+),[ ]+(?P.+)\n (?P.+)", # .am @@ -505,6 +508,7 @@ def parse_registrants(data): "Technical Contact ID:[ ]*(?P.*)\nTechnical Contact Name:[ ]*(?P.*)\n(?:Technical Contact Organization:[ ]*(?P.*)\n)?Technical Contact Address1:[ ]*(?P.*)\n(?:Technical Contact Address2:[ ]*(?P.*)\n)?(?:Technical Contact Address3:[ ]*(?P.*)\n)?Technical Contact City:[ ]*(?P.*)\nTechnical Contact State/Province:[ ]*(?P.*)\nTechnical Contact Postal Code:[ ]*(?P.*)\nTechnical Contact Country:[ ]*(?P.*)\nTechnical Contact Country Code:[ ]*.*\nTechnical Contact Phone Number:[ ]*(?P.*)\n(?:Technical Contact Facsimile Number:[ ]*(?P.*)\n)?Technical Contact Email:[ ]*(?P.*)", # .US (NeuStar) "Tech Name[.]* (?P.*)\n Tech Address[.]* (?P.*)\n Tech Address[.]* (?P.*)\n(?: Tech Address[.]* (?P.*)\n)? Tech Address[.]* (?P.*)\n Tech Address[.]* (?P.*)\n Tech Address[.]* (?P.*)\n Tech Address[.]* (?P.*)\n Tech Email[.]* (?P.*)\n Tech Phone[.]* (?P.*)\n Tech Fax[.]* (?P.*)", # Melbourne IT "Technical contact:\n(?: (?P.+)\n)? (?P.+)\n (?P.+)\n (?P.+)\n (?P.+), (?P.+) (?P.+) (?P.+)\n Phone: (?P.*)\n Fax: (?P.*)\n", # Fabulous.com + "tech-c-name:\s*(?P.+)\ntech-c-type:\s*(?P.+)\ntech-c-address:\s*(?P.+)\ntech-c-postcode:\s*(?P.+)\ntech-c-city:\s*(?P.+)\ntech-c-country:\s*(?P.+)\n(?:tech-c-phone:\s*(?P.+)\n)?(?:tech-c-email:\s*(?P.+)\n)?", # Hetzner "Admin Contact Information :[ ]*\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n\n", # GAL Communication " Technical contact:\n (?P.+)\n (?P.*)\n (?P.+)\n (?P.+) (?P\S+),[ ]+(?P.+)\n (?P.+)\n (?P.+)\n (?P.*)\n (?P.*)", # .am "\[Zone-C\]\nType: (?P.+)\nName: (?P.+)\n(Organisation: (?P.+)\n){0,1}(Address: (?P.+)\n){1}(Address: (?P.+)\n){0,1}(Address: (?P.+)\n){0,1}(Address: (?P.+)\n){0,1}PostalCode: (?P.+)\nCity: (?P.+)\nCountryCode: (?P[A-Za-z]{2})\nPhone: (?P.+)\nFax: (?P.+)\nEmail: (?P.+)\n(Remarks: (?P.+)\n){0,1}Changed: (?P.+)", # DeNIC @@ -525,6 +529,7 @@ def parse_registrants(data): "Administrative Contact ID:[ ]*(?P.*)\nAdministrative Contact Name:[ ]*(?P.*)\n(?:Administrative Contact Organization:[ ]*(?P.*)\n)?Administrative Contact Address1:[ ]*(?P.*)\n(?:Administrative Contact Address2:[ ]*(?P.*)\n)?(?:Administrative Contact Address3:[ ]*(?P.*)\n)?Administrative Contact City:[ ]*(?P.*)\nAdministrative Contact State/Province:[ ]*(?P.*)\nAdministrative Contact Postal Code:[ ]*(?P.*)\nAdministrative Contact Country:[ ]*(?P.*)\nAdministrative Contact Country Code:[ ]*.*\nAdministrative Contact Phone Number:[ ]*(?P.*)\n(?:Administrative Contact Facsimile Number:[ ]*(?P.*)\n)?Administrative Contact Email:[ ]*(?P.*)", # .US (NeuStar) "Admin Name[.]* (?P.*)\n Admin Address[.]* (?P.*)\n Admin Address[.]* (?P.*)\n(?: Admin Address[.]* (?P.*)\n)? Admin Address[.]* (?P.*)\n Admin Address[.]* (?P.*)\n Admin Address[.]* (?P.*)\n Admin Address[.]* (?P.*)\n Admin Email[.]* (?P.*)\n Admin Phone[.]* (?P.*)\n Admin Fax[.]* (?P.*)", # Melbourne IT "Administrative contact:\n(?: (?P.+)\n)? (?P.+)\n (?P.+)\n (?P.+)\n (?P.+), (?P.+) (?P.+) (?P.+)\n Phone: (?P.*)\n Fax: (?P.*)\n", # Fabulous.com + "admin-c-name:\s*(?P.+)\nadmin-c-type:\s*(?P.+)\nadmin-c-address:\s*(?P.+)\nadmin-c-postcode:\s*(?P.+)\nadmin-c-city:\s*(?P.+)\nadmin-c-country:\s*(?P.+)\n(?:admin-c-phone:\s*(?P.+)\n)?(?:admin-c-email:\s*(?P.+)\n)?", # Hetzner "Tech Contact Information :[ ]*\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n[ ]+(?P.*)\n\n", # GAL Communication " Administrative contact:\n (?P.+)\n (?P.*)\n (?P.+)\n (?P.+) (?P\S+),[ ]+(?P.+)\n (?P.+)\n (?P.+)\n (?P.*)\n (?P.*)", # .am "\[Tech-C\]\nType: (?P.+)\nName: (?P.+)\n(Organisation: (?P.+)\n){0,1}(Address: (?P.+)\n){1}(Address: (?P.+)\n){0,1}(Address: (?P.+)\n){0,1}(Address: (?P.+)\n){0,1}PostalCode: (?P.+)\nCity: (?P.+)\nCountryCode: (?P[A-Za-z]{2})\nPhone: (?P.+)\nFax: (?P.+)\nEmail: (?P.+)\n(Remarks: (?P.+)\n){0,1}Changed: (?P.+)", # DeNIC diff --git a/setup.py b/setup.py index 1c478fb..42f2e29 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name='pythonwhois', - version='2.1.1', + version='2.1.2', description='Module for retrieving and parsing the WHOIS data for a domain. Supports most domains. No dependencies.', author='Sven Slootweg', author_email='pythonwhois@cryto.net', diff --git a/test/data/ismtgoxdeadyet.com b/test/data/ismtgoxdeadyet.com new file mode 100644 index 0000000..0ad85c9 --- /dev/null +++ b/test/data/ismtgoxdeadyet.com @@ -0,0 +1,99 @@ + + +domain: ismtgoxdeadyet.com +expiration_date: 2015-02-11 00:12:48 +last_update: 2014-02-15 00:59:45 + +nameserver: ns1.cyon.ch +nameserver: ns2.cyon.ch + +registrant-name: Christian Bock +registrant-type: PERSON +registrant-address: Schopenhauerstr. 43 +registrant-postcode: 80807 +registrant-city: Muenchen +registrant-country: DE + +admin-c-name: Christian Bock +admin-c-type: PERSON +admin-c-address: Schopenhauerstr. 43 +admin-c-postcode: 80807 +admin-c-city: Muenchen +admin-c-country: DE +admin-c-phone: +498935061638 +admin-c-email: bock@imap.ch + +tech-c-name: Christian Bock +tech-c-type: PERSON +tech-c-address: Schopenhauerstr. 43 +tech-c-postcode: 80807 +tech-c-city: Muenchen +tech-c-country: DE +tech-c-phone: +498935061638 +tech-c-email: bock@imap.ch + +zone-c-name: Christian Bock +zone-c-type: PERSON +zone-c-address: Schopenhauerstr. 43 +zone-c-postcode: 80807 +zone-c-city: Muenchen +zone-c-country: DE +zone-c-phone: +498935061638 +zone-c-email: bock@imap.ch + + + +-- + +Whois Server Version 2.0 + +Domain names in the .com and .net domains can now be registered +with many different competing registrars. Go to http://www.internic.net +for detailed information. + + Domain Name: ISMTGOXDEADYET.COM + Registrar: HETZNER ONLINE AG + Whois Server: whois.your-server.de + Referral URL: http://www.hetzner.de + Name Server: NS1.CYON.CH + Name Server: NS2.CYON.CH + Status: ok + Updated Date: 10-feb-2014 + Creation Date: 10-feb-2014 + Expiration Date: 10-feb-2015 + +>>> Last update of whois database: Sun, 02 Mar 2014 00:05:42 UTC <<< + +NOTICE: The expiration date displayed in this record is the date the +registrar's sponsorship of the domain name registration in the registry is +currently set to expire. This date does not necessarily reflect the expiration +date of the domain name registrant's agreement with the sponsoring +registrar. Users may consult the sponsoring registrar's Whois database to +view the registrar's reported date of expiration for this registration. + +TERMS OF USE: You are not authorized to access or query our Whois +database through the use of electronic processes that are high-volume and +automated except as reasonably necessary to register domain names or +modify existing registrations; the Data in VeriSign Global Registry +Services' ("VeriSign") Whois database is provided by VeriSign for +information purposes only, and to assist persons in obtaining information +about or related to a domain name registration record. VeriSign does not +guarantee its accuracy. By submitting a Whois query, you agree to abide +by the following terms of use: You agree that you may use this Data only +for lawful purposes and that under no circumstances will you use this Data +to: (1) allow, enable, or otherwise support the transmission of mass +unsolicited, commercial advertising or solicitations via e-mail, telephone, +or facsimile; or (2) enable high volume, automated, electronic processes +that apply to VeriSign (or its computer systems). The compilation, +repackaging, dissemination or other use of this Data is expressly +prohibited without the prior written consent of VeriSign. You agree not to +use electronic processes that are automated and high-volume to access or +query the Whois database except as reasonably necessary to register +domain names or modify existing registrations. VeriSign reserves the right +to restrict your access to the Whois database in its sole discretion to ensure +operational stability. VeriSign may restrict or terminate your access to the +Whois database for failure to abide by these terms of use. VeriSign +reserves the right to modify these terms at any time. + +The Registry database contains ONLY .COM, .NET, .EDU domains and +Registrars. diff --git a/test/target_default/ismtgoxdeadyet.com b/test/target_default/ismtgoxdeadyet.com new file mode 100644 index 0000000..d9ca925 --- /dev/null +++ b/test/target_default/ismtgoxdeadyet.com @@ -0,0 +1 @@ +{"updated_date": ["2014-02-15T00:59:45"], "status": ["ok"], "contacts": {"admin": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "phone": "+498935061638", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON", "email": "bock@imap.ch"}, "tech": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "phone": "+498935061638", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON", "email": "bock@imap.ch"}, "registrant": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON"}, "billing": null}, "nameservers": ["ns1.cyon.ch", "ns2.cyon.ch"], "expiration_date": ["2015-02-11T00:12:48"], "creation_date": ["2014-02-10T00:00:00"], "raw": ["\n\ndomain: ismtgoxdeadyet.com\nexpiration_date: 2015-02-11 00:12:48\nlast_update: 2014-02-15 00:59:45\n\nnameserver: ns1.cyon.ch\nnameserver: ns2.cyon.ch\n\nregistrant-name: Christian Bock\nregistrant-type: PERSON\nregistrant-address: Schopenhauerstr. 43\nregistrant-postcode: 80807\nregistrant-city: Muenchen\nregistrant-country: DE\n\nadmin-c-name: Christian Bock\nadmin-c-type: PERSON\nadmin-c-address: Schopenhauerstr. 43\nadmin-c-postcode: 80807\nadmin-c-city: Muenchen\nadmin-c-country: DE\nadmin-c-phone: +498935061638\nadmin-c-email: bock@imap.ch\n\ntech-c-name: Christian Bock\ntech-c-type: PERSON\ntech-c-address: Schopenhauerstr. 43\ntech-c-postcode: 80807\ntech-c-city: Muenchen\ntech-c-country: DE\ntech-c-phone: +498935061638\ntech-c-email: bock@imap.ch\n\nzone-c-name: Christian Bock\nzone-c-type: PERSON\nzone-c-address: Schopenhauerstr. 43\nzone-c-postcode: 80807\nzone-c-city: Muenchen\nzone-c-country: DE\nzone-c-phone: +498935061638\nzone-c-email: bock@imap.ch\n\n\n", "\nWhois Server Version 2.0\n\nDomain names in the .com and .net domains can now be registered\nwith many different competing registrars. Go to http://www.internic.net\nfor detailed information.\n\n Domain Name: ISMTGOXDEADYET.COM\n Registrar: HETZNER ONLINE AG\n Whois Server: whois.your-server.de\n Referral URL: http://www.hetzner.de\n Name Server: NS1.CYON.CH\n Name Server: NS2.CYON.CH\n Status: ok\n Updated Date: 10-feb-2014\n Creation Date: 10-feb-2014\n Expiration Date: 10-feb-2015\n\n>>> Last update of whois database: Sun, 02 Mar 2014 00:05:42 UTC <<<\n\nNOTICE: The expiration date displayed in this record is the date the \nregistrar's sponsorship of the domain name registration in the registry is \ncurrently set to expire. This date does not necessarily reflect the expiration \ndate of the domain name registrant's agreement with the sponsoring \nregistrar. Users may consult the sponsoring registrar's Whois database to \nview the registrar's reported date of expiration for this registration.\n\nTERMS OF USE: You are not authorized to access or query our Whois \ndatabase through the use of electronic processes that are high-volume and \nautomated except as reasonably necessary to register domain names or \nmodify existing registrations; the Data in VeriSign Global Registry \nServices' (\"VeriSign\") Whois database is provided by VeriSign for \ninformation purposes only, and to assist persons in obtaining information \nabout or related to a domain name registration record. VeriSign does not \nguarantee its accuracy. By submitting a Whois query, you agree to abide \nby the following terms of use: You agree that you may use this Data only \nfor lawful purposes and that under no circumstances will you use this Data \nto: (1) allow, enable, or otherwise support the transmission of mass \nunsolicited, commercial advertising or solicitations via e-mail, telephone, \nor facsimile; or (2) enable high volume, automated, electronic processes \nthat apply to VeriSign (or its computer systems). The compilation, \nrepackaging, dissemination or other use of this Data is expressly \nprohibited without the prior written consent of VeriSign. You agree not to \nuse electronic processes that are automated and high-volume to access or \nquery the Whois database except as reasonably necessary to register \ndomain names or modify existing registrations. VeriSign reserves the right \nto restrict your access to the Whois database in its sole discretion to ensure \noperational stability. VeriSign may restrict or terminate your access to the \nWhois database for failure to abide by these terms of use. VeriSign \nreserves the right to modify these terms at any time. \n\nThe Registry database contains ONLY .COM, .NET, .EDU domains and\nRegistrars.\n"], "whois_server": ["whois.your-server.de"], "registrar": ["HETZNER ONLINE AG"]} \ No newline at end of file diff --git a/test/target_normalized/ismtgoxdeadyet.com b/test/target_normalized/ismtgoxdeadyet.com new file mode 100644 index 0000000..ff5199c --- /dev/null +++ b/test/target_normalized/ismtgoxdeadyet.com @@ -0,0 +1 @@ +{"updated_date": ["2014-02-15T00:59:45"], "status": ["ok"], "contacts": {"admin": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "phone": "+498935061638", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON", "email": "bock@imap.ch"}, "tech": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "phone": "+498935061638", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON", "email": "bock@imap.ch"}, "registrant": {"city": "Muenchen", "name": "Christian Bock", "country": "DE", "street": "Schopenhauerstr. 43", "postalcode": "80807", "type": "PERSON"}, "billing": null}, "nameservers": ["ns1.cyon.ch", "ns2.cyon.ch"], "expiration_date": ["2015-02-11T00:12:48"], "creation_date": ["2014-02-10T00:00:00"], "raw": ["\n\ndomain: ismtgoxdeadyet.com\nexpiration_date: 2015-02-11 00:12:48\nlast_update: 2014-02-15 00:59:45\n\nnameserver: ns1.cyon.ch\nnameserver: ns2.cyon.ch\n\nregistrant-name: Christian Bock\nregistrant-type: PERSON\nregistrant-address: Schopenhauerstr. 43\nregistrant-postcode: 80807\nregistrant-city: Muenchen\nregistrant-country: DE\n\nadmin-c-name: Christian Bock\nadmin-c-type: PERSON\nadmin-c-address: Schopenhauerstr. 43\nadmin-c-postcode: 80807\nadmin-c-city: Muenchen\nadmin-c-country: DE\nadmin-c-phone: +498935061638\nadmin-c-email: bock@imap.ch\n\ntech-c-name: Christian Bock\ntech-c-type: PERSON\ntech-c-address: Schopenhauerstr. 43\ntech-c-postcode: 80807\ntech-c-city: Muenchen\ntech-c-country: DE\ntech-c-phone: +498935061638\ntech-c-email: bock@imap.ch\n\nzone-c-name: Christian Bock\nzone-c-type: PERSON\nzone-c-address: Schopenhauerstr. 43\nzone-c-postcode: 80807\nzone-c-city: Muenchen\nzone-c-country: DE\nzone-c-phone: +498935061638\nzone-c-email: bock@imap.ch\n\n\n", "\nWhois Server Version 2.0\n\nDomain names in the .com and .net domains can now be registered\nwith many different competing registrars. Go to http://www.internic.net\nfor detailed information.\n\n Domain Name: ISMTGOXDEADYET.COM\n Registrar: HETZNER ONLINE AG\n Whois Server: whois.your-server.de\n Referral URL: http://www.hetzner.de\n Name Server: NS1.CYON.CH\n Name Server: NS2.CYON.CH\n Status: ok\n Updated Date: 10-feb-2014\n Creation Date: 10-feb-2014\n Expiration Date: 10-feb-2015\n\n>>> Last update of whois database: Sun, 02 Mar 2014 00:05:42 UTC <<<\n\nNOTICE: The expiration date displayed in this record is the date the \nregistrar's sponsorship of the domain name registration in the registry is \ncurrently set to expire. This date does not necessarily reflect the expiration \ndate of the domain name registrant's agreement with the sponsoring \nregistrar. Users may consult the sponsoring registrar's Whois database to \nview the registrar's reported date of expiration for this registration.\n\nTERMS OF USE: You are not authorized to access or query our Whois \ndatabase through the use of electronic processes that are high-volume and \nautomated except as reasonably necessary to register domain names or \nmodify existing registrations; the Data in VeriSign Global Registry \nServices' (\"VeriSign\") Whois database is provided by VeriSign for \ninformation purposes only, and to assist persons in obtaining information \nabout or related to a domain name registration record. VeriSign does not \nguarantee its accuracy. By submitting a Whois query, you agree to abide \nby the following terms of use: You agree that you may use this Data only \nfor lawful purposes and that under no circumstances will you use this Data \nto: (1) allow, enable, or otherwise support the transmission of mass \nunsolicited, commercial advertising or solicitations via e-mail, telephone, \nor facsimile; or (2) enable high volume, automated, electronic processes \nthat apply to VeriSign (or its computer systems). The compilation, \nrepackaging, dissemination or other use of this Data is expressly \nprohibited without the prior written consent of VeriSign. You agree not to \nuse electronic processes that are automated and high-volume to access or \nquery the Whois database except as reasonably necessary to register \ndomain names or modify existing registrations. VeriSign reserves the right \nto restrict your access to the Whois database in its sole discretion to ensure \noperational stability. VeriSign may restrict or terminate your access to the \nWhois database for failure to abide by these terms of use. VeriSign \nreserves the right to modify these terms at any time. \n\nThe Registry database contains ONLY .COM, .NET, .EDU domains and\nRegistrars.\n"], "whois_server": ["whois.your-server.de"], "registrar": ["Hetzner Online AG"]} \ No newline at end of file