The TLD list in the valid domain extensions regex is not up to date; a number of Top-Level-Domains (TLDs) have since been created which are not represented here. Similarly, the contry code validator is too lax, which prevents the use of the unassigned TLDs while testing on local servers. Luckily the IANA maintains a list of valid TLDs that we can poll. This patch polls that list once a day and defaults to the current list if for whichever reason (intranet, etc.) it cannot reach the data source.

This way we won't have to update the module just because the TLD list is changing (and with the recent changes towards openness, the changes will be numerous)

Comments

pvmchau’s picture

StatusFileSize
new3.43 KB

New patch for this issue

thehong’s picture

Status: Needs review » Reviewed & tested by the community

Look good for me.

jcfiala’s picture

Status: Reviewed & tested by the community » Needs work

I like the idea of this, and wouldn't mind it with a couple of changes:

1) I've already added code that allows users to update the list of TLDs with a variable get, and created a function called _link_domains() that does a similar job to _link_get_TLD_regex. Personally, I prefer the function name _link_domains(), especially since:

2) I don't like the idea that a call to validate a value will trigger an attempt to load data from offsite - I'd rather we restricted that entirely to hook_cron, and just checked the current value when validating.

If you can fix those two up, I think we might have a winner!

Anonymous’s picture

Title: Updated TLD list » Periodically Update TLD list from Current IANA List
Version: 7.x-1.0 » 7.x-1.x-dev
Assigned: MisterSpeed » Unassigned
Category: Bug report » Feature request
Issue summary: View changes
Status: Needs work » Needs review
StatusFileSize
new3.27 KB

I am attaching a patch taking ideas from the one uploaded by MisterSpeed, but taking into consideration the comments by jcfiala.

What the patch does:

1. Uses hook_cron() & hook_cron_queue_info() to poll the IANA list once a day, & upon failure, every six hours. Polling is done with drupal_http_request() rather than file_get_contents() (the Drupal Way, doesn't require allow_url_fopen to be set on the server php.ini (better security)). This sets the IANA list values to a variable, link_iana_domains. In the case of an error retrieving the list from the remote URL, an error message is written to the logs with watchdog().

2. On form validation, the function _link_domains() is used, but now it checks whether the variable link_iana_domains has data. If it does, it uses those TLD strings as the return value (or if $link_extra_domains is also set, both concatenated). Since the IANA list is likely to include any extra TLDs anybody might set, this concatenation may not be of too much value, but I can imagine some extreme edge cases where someone is using this field to manage bleeding edge TLDs, such as ones proposed but not yet accepted, so it might be useful to keep this. If link_iana_domains has no data, then the present functionality of _link_domains() is performed.

No calls to remote URLs are done on validation, & there is fallback to the present functionality if the list cannot be updated for any reason. Additionally, since 'local' is already included in the current 'LINK_DOMAINS' definition, I just added 'localhost' in there as well, rather than preserve the additional definitions used by the initial patch.

Please review it & let me know what you think!

Anonymous’s picture

StatusFileSize
new3.22 KB

Just a small fix to the last patch (there was a missing newline at the end of the patch file).

chx’s picture

StatusFileSize
new12.37 KB

This is an alternative solution adding a drush command instead writing a file. It's feels a bit too much for a variable.

Also, the patch above doesn't deal with Punycode. My version considers both the Punycode encoded and decoded versions valid. That's the only reason for the size of the patch, otherwise it's really small. The userspace Punycode library used is https://github.com/true/php-punycode/ MIT so GPL compatible.

Status: Needs review » Needs work

The last submitted patch, 6: 1846202_6.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new11.39 KB
Anonymous’s picture

Status: Needs review » Needs work

It seems the patch in #8 doesn't have the drush part from the patch at #6.

Also, what about sites not using drush?

rossb89’s picture

Patch #5 has worked great for me.

Patch applied cleanly, ran cron, tested 'drush vget link_iana_domains' and had a whole load of lovely domains.

Tested on a field, the validation that was previously causing a '.global' domain to fail now allowed the new domain.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new12.3 KB

Added back the drush command -- sorry for leaving it out. And I guess the important part of this, the Punycode part could be merged with #5. #5 IMO could just do DrupalQueue::get('link_iana')->createItem(NULL) and leave the loop out of cron and the argument out in the worker callback. And perhaps it could use the file approach? Not sure but I do not like having such a huge variable around loaded for each page.

Status: Needs review » Needs work

The last submitted patch, 11: 1846202_8.patch, failed testing.

chx’s picture

Status: Needs work » Needs review
StatusFileSize
new12.3 KB

Oh. Yeah. Ancient PHP.

Status: Needs review » Needs work

The last submitted patch, 13: 1846202_12.patch, failed testing.

chx’s picture

Status: Needs work » Needs review

Nevermind. #5 is the one to review. It's impossible to make this 5.2 compatible since it's namespace and it's not worth the bothering. Feel free to use it if you have at least 5.4.

The last submitted patch, link.module.patch, failed testing.

The last submitted patch, 1: link.module.patch, failed testing.

chx’s picture

StatusFileSize
new12.13 KB

OK, one last try.

Status: Needs review » Needs work

The last submitted patch, 18: 1846202_17.patch, failed testing.

chx’s picture

Status: Needs work » Needs review

OK, withdrawn. I tried but I can't put in more time to get it working with such an ancient PHP. #5 is the one to review.

jcnventura’s picture

StatusFileSize
new12.14 KB

Re-rolling.

fonant’s picture

#5 works nicely here, updating the list of LTDs via cron.

ibonelli’s picture

I tested it too, works for me. I run drush manually though, and don't see anything that would pull it automatically on cron runs? I guess we need to manually add the "drush link-extra-domains" task to our crons... Right?
In any case, working for me too. Thanks!

fonant’s picture

If you use patch #5 then the normal Drupal cron task will do the work for you, as the updating is done via hook_cron() once per day. All Drupal sites need cron, if only the built-in fake cron, to do things like checking for updates, update the search index, etc.

If you use patch #21 then you'd need to explicitly call the drush command separately from cron at whatever frequency you wish. Gives a bit more flexibility, perhaps, but I prefer patch #5 as being more "plug and play".

ibonelli’s picture

Thanks for the feedback :)

I understood we were testing/using #5. I tested that patch.
cron: Did not see in the patch the cron_hook so I assumed it wasn't being called.
Any way we can test the pull is being done on the cron run?

MisterSpeed’s picture

The original idea of my first offered patch was to adapt to the rapidly changing TLD landscape; it could easily be adapted to doing a new update if the check fails for example (within safe calling deltas so as not to run incessantly to the data source) instead of waiting for the next cron and add error check flags there. Let's keep this in mind while updating: the new TLDs are already coming fast and are expected to accelerate.

That being said, I am wildly happy that so many have taken an interest in this. The PunyCode work is especially awesome, and so is the drush command. Thanks everyone !

chx’s picture

marcingy’s picture

StatusFileSize
new11.56 KB

This is new version doing a variable set rather than dumping to a file, otherwise the same as #21.

shevgeny’s picture

#28 I have not tested Cyrillic addresses (PunyCode)

shevgeny’s picture

#21 I have not tested Cyrillic addresses (PunyCode)
(The value http://www.сайт.рф provided for Link is not a valid URL)

quicksketch’s picture

Coming over here from #2299657: Allow any TLD because site admins can never keep up with ICANN.

Although neat, this seems like an overly complicated bit of code to maintain. Perhaps we'd be better off if the logic in Link module were reversed. Instead of including a white-listing by default, it instead allowed all domain names (using a regex instead). To keep the functionality of strictly allowing certain (or valid) domain names, we could change Link's current "link_additional_domains" to "link_allowed_domains". If empty (the default) then all domains (even invalid ones) would be allowed. This would be good enough for almost all Link module users. If you needed strict checking, you could use a contrib module to maintain the list via cron jobs and update this variable.

Or, better yet, you'd probably want real link checking and just validate that the server can actually hit the URL directly (see #2262307: Add Server Validation Option).

But in general, I think both concepts are a too heavy for Link module. I'd prefer to see these solutions implemented as add-on modules.

quicksketch’s picture

Also, if this were to move forward. I think you could avoid using any custom Punycode and just using the PHP built-in functions for domain conversion: idn_to_ascii and its opposite idn_to_utf8, available in PHP 5.3.0 and higher.

chx’s picture

oh! I had no idea that has been reimplemented in PHP core without intl.

basvredeling’s picture

If this functionality is added to a add-on module, we should adopt blacklisting instead of whitelisting as #31 proposes. Link should accept every url regardless, and the link submodule could be called link_validate or something similar.

quicksketch’s picture

oh! I had no idea that has been reimplemented in PHP core without intl.

Well, I just found out that indeed it's *not* in PHP core all the time. If you have PHP 5.3 and have not compiled with --enable-intl, it's not present. PHP 5.4 appears to have it all the time.

This output can be reproduced easily enough at https://3v4l.org/UuOYG, though on my localhost with MAMP, intl is enabled so the function exists in PHP 5.3.

In any case, at this point I think that's a suitable level of acceptance. Perhaps just check for the function if it exists before escaping.

basvredeling’s picture

So something like this?

function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, &$optional_field_found, &$errors) {
  if ($item['url'] && !(isset($instance['default_value'][$delta]['url']) && $item['url'] === $instance['default_value'][$delta]['url'] && !$instance['required'])) {
    // Validate the link.
    if (version_compare(PHP_VERSION, '5.4.0') >= 0 || extension_loaded('intl')) {
      // validate domain
      $domain = idn_to_ascii($item['url']);

      // ..... continue
    }
  }
}

quicksketch’s picture

Just a straight check on the function is a lot simpler and likely to be more accurate:

    // Validate the link.
    if (function_exists('idn_to_ascii') {
      // validate domain
      $domain = idn_to_ascii($item['url'])
   }

However, you need to split out the domain portion of the URL, you can't run that function on the entire thing, otherwise it will convert other parts of the URL like the path or query string as well.

christophweber’s picture

I agree with quicksketch's general ideas in #31 and later to simplify link core code, and make full validation optional in a submodule. The perfunctory validation in the main module can be a lot like email address validation, just a regex to catch obvious issues and prevent script embedding and such.

drdam’s picture

that's are right for me,

drdam’s picture

StatusFileSize
new13.25 KB

Hi,

I've just add a hook_cron every month and call on install or update

Thanks

Status: Needs review » Needs work

The last submitted patch, 40: issue-10474308-40.patch, failed testing.

drdam’s picture

Status: Needs work » Active
StatusFileSize
new13.24 KB

Without the name-error on hook_cron

marcingy’s picture

Please don't include code for other contrib projects in the patches you provide as they won't apply. Which you just did!!!

drdam’s picture

StatusFileSize
new13.04 KB

Ohh.. sorry

That's better ?

danny englander’s picture

I have a .photo domain and confirm I can use that now with Link after applying the patch from #44. Many thanks!

leducdubleuet’s picture

Status: Active » Reviewed & tested by the community

Patch in #44 is working great, thank you very much!

The last submitted patch, 42: link-large-TLD-issue-10474308-40.patch, failed testing.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 44: link-large-TLD-issue-10474308-43.patch, failed testing.

leducdubleuet’s picture

Status: Needs work » Needs review
StatusFileSize
new22.6 KB

I re-rolled the patch in #44 against current HEAD since the last one failed testing.

cycas’s picture

I tested patch #49 and once applied, was able to create a link to a url using a .farm extension domain.

duaelfr’s picture

Status: Needs review » Needs work

It seems to be working but I don't like the way you did it. I mean, the module code should not call the drush command but declare its own helper that drush is going to use as well as the cron, the install and update scripts.
One should not have to open the drush.inc file to understand how the module works.

I also dislike the dependency on Punycode. Don't we have enough tools in our API to deal with that file properly?
If the answer is "no" I suggest to put that Punycode declaration in a separate include file.

duaelfr’s picture

One more thought is that we should provide a button somewhere in the UI to allow the site admin to force the refresh of the list. Sadly, not everyone know how to use drush or delete a variable.

msti’s picture

This is a great solution but there are still some cases that are not supported:
1. Tor, the anonymity network, defines its own TLD (.onion) .
2. Custom TLDs, internal to private networks are not supported

For 1. we can hardcode the .onion domain in the variable
For 2. we can add a drupal_alter.

What do you think?

duaelfr’s picture

I support the idea of an alter hook.
We could use it to properly provide the .onion extension instead of hardcoding it.

klausi’s picture

Instead of updating the list in hopes of ever keeping up with ICANN I'm going with a much simpler approach of just allowing any letters as top level domain: #2747561: Allow any TLD because site admins can never keep up with ICANN

jwilson3’s picture

Agree 100% with Klausi. There is no way for module maintainers to keep up with ever changing TLDs now that we have "Branded top-level domains" I now see that this issue is supposed to get around the issue of manually "keeping up" by an automated sync with a list, but then it also ties the codebase to a text file produced by the third party IANA website, which could break or stop working at any point in the future.

I like the idea of allowing any TLD and if someone wants to specifically prohibit some TLDs that should be doable in a submit validation hook or via a variable exposed in admin UI to let you blacklist domains.

artusamak’s picture

Status: Needs work » Closed (duplicate)

A fix has been committed in #2299657: Allow any TLD because site admins can never keep up with ICANN this issue should be closed now. The fix in the linked issue is working.