code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Polish (http://www.transifex.com/gnu-social/gnu-social/language/pl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pl\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:43+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Portuguese (http://www.transifex.com/gnu-social/gnu-social/language/pt/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr "Cache de processo adicional para plug-ins."

repo_name: spookie/gnu

license: mit


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/gnu-social/gnu-social/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Romanian (Romania) (http://www.transifex.com/gnu-social/gnu-social/language/ro_RO/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ro_RO\n"
"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:43+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Russian (http://www.transifex.com/gnu-social/gnu-social/language/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ru\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr "Дополнительный внутрипроцессный кэш для плагинов."


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Slovenian (http://www.transifex.com/gnu-social/gnu-social/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Serbian (http://www.transifex.com/gnu-social/gnu-social/language/sr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sr\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Swedish (http://www.transifex.com/gnu-social/gnu-social/language/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:48+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Tamil (http://www.transifex.com/gnu-social/gnu-social/language/ta/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ta\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Telugu (http://www.transifex.com/gnu-social/gnu-social/language/te/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: te\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:43+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Tagalog (http://www.transifex.com/gnu-social/gnu-social/language/tl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tl\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr "Karagdagang taguang isinisagawa para sa mga pampasak."


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Turkish (http://www.transifex.com/gnu-social/gnu-social/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:43+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Ukrainian (http://www.transifex.com/gnu-social/gnu-social/language/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: uk\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr "Додатковий «кеш-в-процесі» для додатків."


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Urdu (Pakistan) (http://www.transifex.com/gnu-social/gnu-social/language/ur_PK/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ur_PK\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Vietnamese (http://www.transifex.com/gnu-social/gnu-social/language/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (http://www.transifex.com/gnu-social/gnu-social/language/zh/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:43+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/gnu-social/gnu-social/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr "其他过程中缓存的插件。"


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-06 16:30+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/gnu-social/gnu-social/language/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Plugin dscription.
#: InProcessCachePlugin.php:183
msgid "Additional in-process cache for plugins."
msgstr ""


code:
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Plugin to enable Infinite Scrolling
 *
 * @package   GNUsocial
 * @author    Craig Andrews <candrews@integralblue.com>
 * @copyright 2009-2019 Free Software Foundation, Inc http://www.fsf.org
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */

defined('GNUSOCIAL') || die();

class InfiniteScrollPlugin extends Plugin
{
    const PLUGIN_VERSION = '2.0.0';

    public $on_next_only = false;

    function onEndShowScripts(Action $action)
    {
        $action->inlineScript('var infinite_scroll_on_next_only = ' . ($this->on_next_only?'true':'false') . ';');
        $action->inlineScript('var ajax_loader_url = "' . ($this->path('ajax-loader.gif')) . '";');
        $action->script($this->path('jquery.infinitescroll.js'));
        $action->script($this->path('infinitescroll.js'));
    }

    public function onPluginVersion(array &$versions): bool
    {
        $versions[] = [
            'name' => 'InfiniteScroll',
            'version' => self::PLUGIN_VERSION,
            'author' => 'Craig Andrews',
            'homepage' => GNUSOCIAL_ENGINE_REPO_URL . 'tree/master/plugins/InfiniteScroll',
            'rawdescription' =>
            // TRANS: Plugin dscription.
            _m('Infinite Scroll adds the following functionality to your StatusNet installation: When a user scrolls towards the bottom of the page, the next page of notices is automatically retrieved and appended. This means they never need to click "Next Page", which dramatically increases stickiness.')
        ];
        return true;
    }
}


code:
Infinite Scroll adds the following functionality to your StatusNet installation:
When a user scrolls towards the bottom of the page, the next page of notices is
automatically retrieved and appended. This means they never need to click "Next
Page", which dramatically increases stickiness.

Note that there is a configuration option to make it so that the plugin doesn't
trigger when the user approaches the bottom of the page, but instead triggers
when the "next" button is clicked.

Installation
============
Add "addPlugin('InfiniteScroll',
    array('setting'=>'value', 'setting2'=>'value2', ...);"
to the bottom of your config.php

Settings
========
on_next_only (false): trigger infinite scroll when the next button is clicked.
Note that this means you really don't have "infinite scrolling" as the trigger
is clicking, not scrolling.

Example
=======
addPlugin('InfiniteScroll', array(
    'on_next_only'=>false
));

path: plugins/InfiniteScroll/README


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-14 15:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#. TRANS: Plugin dscription.
#: InfiniteScrollPlugin.php:51
msgid ""
"Infinite Scroll adds the following functionality to your GNU social "
"installation: When a user scrolls towards the bottom of the page, the next "
"page of notices is automatically retrieved and appended. This means they "
"never need to click \"Next Page\", which dramatically increases stickiness."
msgstr ""


code:
StartDiscoveryMethodRegistration
- $disco:   Discovery object that accepts the registrations

EndDiscoveryMethodRegistration: Register remote URI discovery methods
- $disco:   Discovery object that accepts the registrations


code:
<?php
/*
 * GNU Social - a federating social network
 * Copyright (C) 2013, Free Software Foundation, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Implements Link-based Resource Descriptor Discovery based on RFC6415,
 * Web Host Metadata, i.e. the predecessor to WebFinger resource discovery.
 *
 * @package GNUsocial
 * @author  Mikael Nordfeldth <mmn@hethane.se>
 */

if (!defined('GNUSOCIAL')) { exit(1); }

set_include_path(get_include_path() . PATH_SEPARATOR . __DIR__ . '/extlib/');

class LRDDPlugin extends Plugin
{
    const PLUGIN_VERSION = '2.0.0';

    public function onAutoload($cls)
    {
        switch ($cls) {
        case 'XML_XRD':
            require_once __DIR__ . '/extlib/XML/XRD.php';
            return false;
        }

        return parent::onAutoload($cls);
    }

    public function onStartDiscoveryMethodRegistration(Discovery $disco) {
        $disco->registerMethod('LRDDMethod_WebFinger');
    }

    public function onEndDiscoveryMethodRegistration(Discovery $disco) {
        $disco->registerMethod('LRDDMethod_HostMeta');
        $disco->registerMethod('LRDDMethod_LinkHeader');
        $disco->registerMethod('LRDDMethod_LinkHTML');
    }

    public function onPluginVersion(array &$versions): bool
    {
        $versions[] = array('name' => 'LRDD',
                            'version' => self::PLUGIN_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => GNUSOCIAL_ENGINE_URL,
                            // TRANS: Plugin description.
                            'rawdescription' => _m('Implements LRDD support for GNU Social.'));

        return true;
    }

    /**
     * Fetch all the aliases of some remote profile
     *
     * @param string $uri profile's URI
     * @return array|null aliases
     * @throws Exception (If the Discovery's HTTP requests fail)
     * @author Bruno Casteleiro <brunoccast@fc.up.pt>
     */
    public static function grab_profile_aliases(string $uri): ?array
    {
        $disco = new Discovery();
        $xrd = $disco->lookup($uri);

        $all_ids = array_merge([$xrd->subject], $xrd->aliases);

        if (!in_array($uri, $all_ids)) {
            $this->log(LOG_INFO, 'The original URI was not listed itself when doing discovery on it!');
            return null;
        }

        return $all_ids;
    }
}


code:
The LRDD plugin implements Link-based Resource Descriptor Discovery
based on RFC6415, Web Host Metadata, i.e. the predecessor to WebFinger resource
discovery.

See: http://tools.ietf.org/html/rfc6415

Installation
============
This plugin is enabled by default

Settings
========
none

path: plugins/LRDD/README


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/PropertyAccess.php';
require_once 'XML/XRD/Element/Link.php';
require_once 'XML/XRD/Loader.php';
require_once 'XML/XRD/Serializer.php';

/**
 * Main class used to load XRD documents from string or file.
 *
 * After loading the file, access to links is possible with get() and getAll(),
 * as well as foreach-iterating over the XML_XRD object.
 *
 * Property access is possible with getProperties() and array access (foreach)
 * on the XML_XRD object.
 *
 * Verification that the subject/aliases match the requested URL can be done with
 * describes().
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD extends XML_XRD_PropertyAccess implements IteratorAggregate
{
    /**
     * XRD file/string loading dispatcher
     *
     * @var XML_XRD_Loader
     */
    public $loader;

    /**
     * XRD serializing dispatcher
     *
     * @var XML_XRD_Serializer
     */
    public $serializer;

    /**
     * XRD subject
     *
     * @var string
     */
    public $subject;

    /**
     * Array of subject alias strings
     *
     * @var array
     */
    public $aliases = array();

    /**
     * Array of link objects
     *
     * @var array
     */
    public $links = array();

    /**
     * Unix timestamp when the document expires.
     * NULL when no expiry date set.
     *
     * @var integer|null
     */
    public $expires;

    /**
     * xml:id of the XRD document
     *
     * @var string|null
     */
    public $id;

    /**
     * Loads the contents of the given file.
     *
     * Note: Only use file type auto-detection for local files.
     * Do not use it on remote files as the file gets requested several times.
     *
     * @param string $file Path to an XRD file
     * @param string $type File type: xml or json, NULL for auto-detection
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the file is invalid or cannot be
     *                                   loaded
     */
    public function loadFile($file, $type = null)
    {
        if (!isset($this->loader)) {
            $this->loader = new XML_XRD_Loader($this);
        }
        return $this->loader->loadFile($file, $type);
    }

    /**
     * Loads the contents of the given string
     *
     * @param string $str  XRD string
     * @param string $type File type: xml or json, NULL for auto-detection
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the string is invalid or cannot be
     *                                   loaded
     */
    public function loadString($str, $type = null)
    {
        if (!isset($this->loader)) {
            $this->loader = new XML_XRD_Loader($this);
        }
        return $this->loader->loadString($str, $type);
    }

    /**
     * Checks if the XRD document describes the given URI.
     *
     * This should always be used to make sure the XRD file
     * is the correct one for e.g. the given host, and not a copycat.
     *
     * Checks against the subject and aliases
     *
     * @param string $uri An URI that the document is expected to describe
     *
     * @return boolean True or false
     */
    public function describes($uri)
    {
        if ($this->subject == $uri) {
            return true;
        }
        foreach ($this->aliases as $alias) {
            if ($alias == $uri) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get the link with highest priority for the given relation and type.
     *
     * @param string  $rel          Relation name
     * @param string  $type         MIME Type
     * @param boolean $typeFallback When true and no link with the given type
     *                              could be found, the best link without a
     *                              type will be returned
     *
     * @return XML_XRD_Element_Link Link object or NULL if none found
     */
    public function get($rel, $type = null, $typeFallback = true)
    {
        $links = $this->getAll($rel, $type, $typeFallback);
        if (count($links) == 0) {
            return null;
        }

        return $links[0];
    }

    /**
     * Get all links with the given relation and type, highest priority first.
     *
     * @param string  $rel          Relation name
     * @param string  $type         MIME Type
     * @param boolean $typeFallback When true and no link with the given type
     *                              could be found, the best link without a
     *                              type will be returned
     *
     * @return array Array of XML_XRD_Element_Link objects
     */
    public function getAll($rel, $type = null, $typeFallback = true)
    {
        $links = array();
        $exactType = false;
        foreach ($this->links as $link) {
            if ($link->rel == $rel
                && ($type === null || $link->type == $type
                || $typeFallback && $link->type === null)
            ) {
                $links[]    = $link;
                $exactType |= $typeFallback && $type !== null
                    && $link->type == $type;
            }
        }
        if ($exactType) {
            //remove all links without type
            $exactlinks = array();
            foreach ($links as $link) {
                if ($link->type !== null) {
                    $exactlinks[] = $link;
                }
            }
            $links = $exactlinks;
        }
        return $links;
    }

    /**
     * Return the iterator object to loop over the links
     *
     * Part of the IteratorAggregate interface
     *
     * @return Traversable Iterator for the links
     */
    public function getIterator()
    {
        return new ArrayIterator($this->links);
    }

    /**
     * Converts this XRD object to XML or JSON.
     *
     * @param string $type Serialization type: xml or json
     *
     * @return string Generated content
     */
    public function to($type)
    {
        if (!isset($this->serializer)) {
            $this->serializer = new XML_XRD_Serializer($this);
        }
        return $this->serializer->to($type);
    }

    /**
     * Converts this XRD object to XML.
     *
     * @return string Generated XML
     *
     * @deprecated use to('xml')
     */
    public function toXML()
    {
        return $this->to('xml');
    }
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/PropertyAccess.php';

/**
 * Link element in a XRD file. Attribute access via object properties.
 *
 * Retrieving the title of a link is possible with the getTitle() convenience
 * method.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Element_Link extends XML_XRD_PropertyAccess
{
    /**
     * Link relation
     *
     * @var string
     */
    public $rel;

    /**
     * Link type (MIME type)
     *
     * @var string
     */
    public $type;

    /**
     * Link URL
     *
     * @var string
     */
    public $href;

    /**
     * Link URL template
     *
     * @var string
     */
    public $template;

    /**
     * Array of key-value pairs: Key is the language, value the title
     *
     * @var array
     */
    public $titles = array();

    /**
     * Create a new instance and load data from the XML element
     *
     * @param string  $relOrXml   string with the relation name/URL
     * @param string  $href       HREF value
     * @param string  $type       Type value
     * @param boolean $isTemplate When set to true, the $href is
     *                            used as template
     */
    public function __construct(
        $rel = null, $href = null, $type = null, $isTemplate = false
    ) {
        $this->rel = $rel;
        if ($isTemplate) {
            $this->template = $href;
        } else {
            $this->href = $href;
        }
        $this->type = $type;
    }

    /**
     * Returns the title of the link in the given language.
     * If the language is not available, the first title without the language
     * is returned. If no such one exists, the first title is returned.
     *
     * @param string $lang 2-letter language name
     *
     * @return string|null Link title
     */
    public function getTitle($lang = null)
    {
        if (count($this->titles) == 0) {
            return null;
        }

        if ($lang == null) {
            return reset($this->titles);
        }

        if (isset($this->titles[$lang])) {
            return $this->titles[$lang];
        }
        if (isset($this->titles[''])) {
            return $this->titles[''];
        }

        //return first
        return reset($this->titles);
    }
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Property element in a XRD document.
 *
 * The <XRD> root element as well as <Link> tags may have <Property> children.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Element_Property
{
    /**
     * Value of the property.
     *
     * @var string|null
     */
    public $value;

    /**
     * Type of the propery.
     *
     * @var string
     */
    public $type;

    /**
     * Create a new instance
     *
     * @param string $type  String representing the property type
     * @param string $value Value of the property, may be NULL
     */
    public function __construct($type = null, $value = null)
    {
        $this->type  = $type;
        $this->value = $value;
    }
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Base exception interface for all XML_XRD related exceptions.
 * With that interface, it is possible to catch all XML_XRD exceptions
 * with a single catch statement.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
interface XML_XRD_Exception
{
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/Loader/Exception.php';

/**
 * File/string loading dispatcher.
 * Loads the correct loader for the type of XRD file (XML or JSON).
 * Also provides type auto-detection.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Loader
{
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Loads the contents of the given file.
     *
     * Note: Only use file type auto-detection for local files.
     * Do not use it on remote files as the file gets requested several times.
     *
     * @param string $file Path to an XRD file
     * @param string $type File type: xml or json, NULL for auto-detection
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the file is invalid or cannot be
     *                                   loaded
     */
    public function loadFile($file, $type = null)
    {
        if ($type === null) {
            $type = $this->detectTypeFromFile($file);
        }
        $loader = $this->getLoader($type);
        $loader->loadFile($file);
    }

    /**
     * Loads the contents of the given string
     *
     * @param string $str  XRD string
     * @param string $type File type: xml or json, NULL for auto-detection
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the string is invalid or cannot be
     *                                   loaded
     */
    public function loadString($str, $type = null)
    {
        if ($type === null) {
            $type = $this->detectTypeFromString($str);
        }
        $loader = $this->getLoader($type);
        $loader->loadString($str);
    }

    /**
     * Creates a XRD loader object for the given type
     *
     * @param string $type File type: xml or json
     *
     * @return XML_XRD_Loader
     */
    protected function getLoader($type)
    {
        $class = 'XML_XRD_Loader_' . strtoupper($type);
        $file = str_replace('_', '/', $class) . '.php';
        include_once $file;
        if (class_exists($class)) {
            return new $class($this->xrd);
        }

        throw new XML_XRD_Loader_Exception(
            'No loader for XRD type "' . $type . '"',
            XML_XRD_Loader_Exception::NO_LOADER
        );
    }

    /**
     * Tries to detect the file type (xml or json) from the file content
     *
     * @param string $file File name to check
     *
     * @return string File type ('xml' or 'json')
     *
     * @throws XML_XRD_Loader_Exception When opening the file fails.
     */
    public function detectTypeFromFile($file)
    {
        if (!file_exists($file)) {
            throw new XML_XRD_Loader_Exception(
                'Error loading XRD file: File does not exist',
                XML_XRD_Loader_Exception::OPEN_FILE
            );
        }
        $handle = fopen($file, 'r');
        if (!$handle) {
            throw new XML_XRD_Loader_Exception(
                'Cannot open file to determine type',
                XML_XRD_Loader_Exception::OPEN_FILE
            );
        }

        $str = (string)fgets($handle, 10);
        fclose($handle);
        return $this->detectTypeFromString($str);
    }

    /**
     * Tries to detect the file type from the content of the file
     *
     * @param string $str Content of XRD file
     *
     * @return string File type ('xml' or 'json')
     *
     * @throws XML_XRD_Loader_Exception When the type cannot be detected
     */
    public function detectTypeFromString($str)
    {
        if (substr($str, 0, 1) == '{') {
            return 'json';
        } else if (substr($str, 0, 5) == '<?xml') {
            return 'xml';
        }

        throw new XML_XRD_Loader_Exception(
            'Detecting file type failed',
            XML_XRD_Loader_Exception::DETECT_TYPE
        );
    }

}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/Exception.php';

/**
 * XML_XRD exception that's thrown when loading the XRD fails.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Loader_Exception extends Exception implements XML_XRD_Exception
{
    /**
     * The document namespace is not the XRD 1.0 namespace
     */
    const DOC_NS = 10;

    /**
     * The document root element is not XRD
     */
    const DOC_ROOT = 11;

    /**
     * Error loading the XML|JSON file|string
     */
    const LOAD = 12;

    /**
     * Unsupported XRD file/string type (no loader)
     */
    const NO_LOADER = 13;

    /**
     * Error opening file
     */
    const OPEN_FILE = 14;

    /**
     * Detecting the file type failed
     */
    const DETECT_TYPE = 20;
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Loads XRD data from a JSON file
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Loader_JSON
{
    /**
     * Data storage the XML data get loaded into
     *
     * @var XML_XRD
     */
    protected $xrd;

    /**
     * Init object with xrd object
     *
     * @param XML_XRD $xrd Data storage the JSON data get loaded into
     */
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Loads the contents of the given file
     *
     * @param string $file Path to an JRD file
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the JSON is invalid or cannot be
     *                                   loaded
     */
    public function loadFile($file)
    {
        $json = file_get_contents($file);
        if ($json === false) {
            throw new XML_XRD_Loader_Exception(
                'Error loading JRD file: ' . $file,
                XML_XRD_Loader_Exception::LOAD
            );
        }
        return $this->loadString($json);
    }

    /**
     * Loads the contents of the given string
     *
     * @param string $json JSON string
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the JSON is invalid or cannot be
     *                                   loaded
     */
    public function loadString($json)
    {
        if ($json == '') {
            throw new XML_XRD_Loader_Exception(
                'Error loading JRD: string empty',
                XML_XRD_Loader_Exception::LOAD
            );
        }

        $obj = json_decode($json);
        if ($obj !== null) {
            return $this->load($obj);
        }

        $constants = get_defined_constants(true);
        $json_errors = array();
        foreach ($constants['json'] as $name => $value) {
            if (!strncmp($name, 'JSON_ERROR_', 11)) {
                $json_errors[$value] = $name;
            }
        }
        throw new XML_XRD_Loader_Exception(
            'Error loading JRD: ' . $json_errors[json_last_error()],
            XML_XRD_Loader_Exception::LOAD
        );
    }

    /**
     * Loads the JSON object into the classes' data structures
     *
     * @param object $j JSON object containing the whole JSON document
     *
     * @return void
     */
    public function load(stdClass $j)
    {
        if (isset($j->subject)) {
            $this->xrd->subject = (string)$j->subject;
        }
        if (isset($j->aliases)) {
            foreach ($j->aliases as $jAlias) {
                $this->xrd->aliases[] = (string)$jAlias;
            }
        }

        if (isset($j->links)) {
            foreach ($j->links as $jLink) {
                $this->xrd->links[] = $this->loadLink($jLink);
            }
        }

        $this->loadProperties($this->xrd, $j);

        if (isset($j->expires)) {
            $this->xrd->expires = strtotime($j->expires);
        }
    }

    /**
     * Loads the Property elements from XML
     *
     * @param object $store Data store where the properties get stored
     * @param object $j     JSON element with "properties" variable
     *
     * @return boolean True when all went well
     */
    protected function loadProperties(
        XML_XRD_PropertyAccess $store, stdClass $j
    ) {
        if (!isset($j->properties)) {
            return true;
        }

        foreach ($j->properties as $type => $jProp) {
            $store->properties[] = new XML_XRD_Element_Property(
                $type, (string)$jProp
            );
        }

        return true;
    }

    /**
     * Create a link element object from XML element
     *
     * @param object $j JSON link object
     *
     * @return XML_XRD_Element_Link Created link object
     */
    protected function loadLink(stdClass $j)
    {
        $link = new XML_XRD_Element_Link();
        foreach (array('rel', 'type', 'href', 'template') as $var) {
            if (isset($j->$var)) {
                $link->$var = (string)$j->$var;
            }
        }

        if (isset($j->titles)) {
            foreach ($j->titles as $lang => $jTitle) {
                if (!isset($link->titles[$lang])) {
                    $link->titles[$lang] = (string)$jTitle;
                }
            }
        }
        $this->loadProperties($link, $j);

        return $link;
    }
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Loads XRD data from an XML file
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Loader_XML
{
    /**
     * Data storage the XML data get loaded into
     *
     * @var XML_XRD
     */
    protected $xrd;

    /**
     * XRD 1.0 namespace
     */
    const NS_XRD = 'http://docs.oasis-open.org/ns/xri/xrd-1.0';

    /**
     * Init object with xrd object
     *
     * @param XML_XRD $xrd Data storage the XML data get loaded into
     */
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Loads the contents of the given file
     *
     * @param string $file Path to an XRD file
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the XML is invalid or cannot be
     *                                   loaded
     */
    public function loadFile($file)
    {
        $old = libxml_use_internal_errors(true);
        $x = simplexml_load_file($file);
        libxml_use_internal_errors($old);
        if ($x === false) {
            throw new XML_XRD_Loader_Exception(
                'Error loading XML file: ' . libxml_get_last_error()->message,
                XML_XRD_Loader_Exception::LOAD
            );
        }
        return $this->load($x);
    }

    /**
     * Loads the contents of the given string
     *
     * @param string $xml XML string
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the XML is invalid or cannot be
     *                                   loaded
     */
    public function loadString($xml)
    {
        if ($xml == '') {
            throw new XML_XRD_Loader_Exception(
                'Error loading XML string: string empty',
                XML_XRD_Loader_Exception::LOAD
            );
        }
        $old = libxml_use_internal_errors(true);
        $x = simplexml_load_string($xml);
        libxml_use_internal_errors($old);
        if ($x === false) {
            throw new XML_XRD_Loader_Exception(
                'Error loading XML string: ' . libxml_get_last_error()->message,
                XML_XRD_Loader_Exception::LOAD
            );
        }
        return $this->load($x);
    }

    /**
     * Loads the XML element into the classes' data structures
     *
     * @param object $x XML element containing the whole XRD document
     *
     * @return void
     *
     * @throws XML_XRD_Loader_Exception When the XML is invalid
     */
    public function load(SimpleXMLElement $x)
    {
        $ns = $x->getDocNamespaces();
        if ($ns[''] !== self::NS_XRD) {
            throw new XML_XRD_Loader_Exception(
                'Wrong document namespace', XML_XRD_Loader_Exception::DOC_NS
            );
        }
        if ($x->getName() != 'XRD') {
            throw new XML_XRD_Loader_Exception(
                'XML root element is not "XRD"', XML_XRD_Loader_Exception::DOC_ROOT
            );
        }

        if (isset($x->Subject)) {
            $this->xrd->subject = (string)$x->Subject;
        }
        foreach ($x->Alias as $xAlias) {
            $this->xrd->aliases[] = (string)$xAlias;
        }

        foreach ($x->Link as $xLink) {
            $this->xrd->links[] = $this->loadLink($xLink);
        }

        $this->loadProperties($this->xrd, $x);

        if (isset($x->Expires)) {
            $this->xrd->expires = strtotime($x->Expires);
        }

        $xmlAttrs = $x->attributes('http://www.w3.org/XML/1998/namespace');
        if (isset($xmlAttrs['id'])) {
            $this->xrd->id = (string)$xmlAttrs['id'];
        }
    }

    /**
     * Loads the Property elements from XML
     *
     * @param object $store Data store where the properties get stored
     * @param object $x     XML element
     *
     * @return boolean True when all went well
     */
    protected function loadProperties(
        XML_XRD_PropertyAccess $store, SimpleXMLElement $x
    ) {
        foreach ($x->Property as $xProp) {
            $store->properties[] = $this->loadProperty($xProp);
        }
    }

    /**
     * Create a link element object from XML element
     *
     * @param object $x XML link element
     *
     * @return XML_XRD_Element_Link Created link object
     */
    protected function loadLink(SimpleXMLElement $x)
    {
        $link = new XML_XRD_Element_Link();
        foreach (array('rel', 'type', 'href', 'template') as $var) {
            if (isset($x[$var])) {
                $link->$var = (string)$x[$var];
            }
        }

        foreach ($x->Title as $xTitle) {
            $xmlAttrs = $xTitle->attributes('http://www.w3.org/XML/1998/namespace');
            $lang = '';
            if (isset($xmlAttrs['lang'])) {
                $lang = (string)$xmlAttrs['lang'];
            }
            if (!isset($link->titles[$lang])) {
                $link->titles[$lang] = (string)$xTitle;
            }
        }
        $this->loadProperties($link, $x);

        return $link;
    }

    /**
     * Create a property element object from XML element
     *
     * @param object $x XML property element
     *
     * @return XML_XRD_Element_Property Created link object
     */
    protected function loadProperty(SimpleXMLElement $x)
    {
        $prop = new XML_XRD_Element_Property();
        if (isset($x['type'])) {
            $prop->type = (string)$x['type'];
        }
        $s = (string)$x;
        if ($s != '') {
            $prop->value = $s;
        }

        return $prop;
    }
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/Exception.php';

/**
 * XML_XRD exception that's thrown when something is not supported
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_LogicException extends LogicException implements XML_XRD_Exception
{
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/LogicException.php';
require_once 'XML/XRD/Element/Property.php';

/**
 * Provides ArrayAccess to extending classes (XML_XRD and XML_XRD_Element_Link).
 *
 * By extending PropertyAccess, access to properties is possible with
 * "$object['propertyType']" array access notation.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
abstract class XML_XRD_PropertyAccess implements ArrayAccess
{

    /**
     * Array of property objects
     *
     * @var array
     */
    public $properties = array();

    /**
     * Check if the property with the given type exists
     *
     * Part of the ArrayAccess interface
     *
     * @param string $type Property type to check for
     *
     * @return boolean True if it exists
     */
    public function offsetExists($type)
    {
        foreach ($this->properties as $prop) {
            if ($prop->type == $type) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return the highest ranked property with the given type
     *
     * Part of the ArrayAccess interface
     *
     * @param string $type Property type to check for
     *
     * @return string Property value or NULL if empty
     */
    public function offsetGet($type)
    {
        foreach ($this->properties as $prop) {
            if ($prop->type == $type) {
                return $prop->value;
            }
        }
        return null;
    }

    /**
     * Not implemented.
     *
     * Part of the ArrayAccess interface
     *
     * @param string $type  Property type to check for
     * @param string $value New property value
     *
     * @return void
     *
     * @throws XML_XRD_LogicException Always
     */
    public function offsetSet($type, $value)
    {
        throw new XML_XRD_LogicException('Changing properties not implemented');
    }

    /**
     * Not implemented.
     *
     * Part of the ArrayAccess interface
     *
     * @param string $type Property type to check for
     *
     * @return void
     *
     * @throws XML_XRD_LogicException Always
     */
    public function offsetUnset($type)
    {
        throw new XML_XRD_LogicException('Changing properties not implemented');
    }

    /**
     * Get all properties with the given type
     *
     * @param string $type Property type to filter by
     *
     * @return array Array of XML_XRD_Element_Property objects
     */
    public function getProperties($type = null)
    {
        if ($type === null) {
            return $this->properties;
        }
        $properties = array();
        foreach ($this->properties as $prop) {
            if ($prop->type == $type) {
                $properties[] = $prop;
            }
        }
        return $properties;
    }
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/Serializer/Exception.php';

/**
 * Serialization dispatcher - loads the correct serializer for saving XRD data.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Serializer
{
    /**
     * XRD data storage
     *
     * @var XML_XRD
     */
    protected $xrd;

    /**
     * Init object with xrd object
     *
     * @param XML_XRD $xrd Data storage the data are fetched from
     */
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Convert the XRD data into a string of the given type
     *
     * @param string $type File type: xml or json
     *
     * @return string Serialized data
     */
    public function to($type)
    {
        return (string)$this->getSerializer($type);
    }

    /**
     * Creates a XRD loader object for the given type
     *
     * @param string $type File type: xml or json
     *
     * @return XML_XRD_Loader
     */
    protected function getSerializer($type)
    {
        $class = 'XML_XRD_Serializer_' . strtoupper($type);
        $file = str_replace('_', '/', $class) . '.php';
        include_once $file;
        if (class_exists($class)) {
            return new $class($this->xrd);
        }

        throw new XML_XRD_Serializer_Exception(
            'No serializer for type "' . $type . '"',
            XML_XRD_Loader_Exception::NO_LOADER
        );
    }
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

require_once 'XML/XRD/Exception.php';

/**
 * XML_XRD exception that's thrown when saving an XRD file fails.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Serializer_Exception extends Exception implements XML_XRD_Exception
{
}
?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Generate JSON from a XML_XRD object (for JRD files).
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 * @link     http://tools.ietf.org/html/rfc6415#appendix-A
 */
class XML_XRD_Serializer_JSON
{
    protected $xrd;

    /**
     * Create new instance
     *
     * @param XML_XRD $xrd XRD instance to convert to JSON
     */
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Generate JSON.
     *
     * @return string JSON code
     */
    public function __toString()
    {
        $o = new stdClass();
        if ($this->xrd->expires !== null) {
            $o->expires = gmdate('Y-m-d\TH:i:s\Z', $this->xrd->expires);
        }
        if ($this->xrd->subject !== null) {
            $o->subject = $this->xrd->subject;
        }
        foreach ($this->xrd->aliases as $alias) {
            $o->aliases[] = $alias;
        }
        foreach ($this->xrd->properties as $property) {
            $o->properties[$property->type] = $property->value;
        }
        $o->links = array();
        foreach ($this->xrd->links as $link) {
            $lid = count($o->links);
            $o->links[$lid] = new stdClass();
            if ($link->rel) {
                $o->links[$lid]->rel = $link->rel;
            }
            if ($link->type) {
                $o->links[$lid]->type = $link->type;
            }
            if ($link->href) {
                $o->links[$lid]->href = $link->href;
            }
            if ($link->template !== null && $link->href === null) {
                $o->links[$lid]->template = $link->template;
            }

            foreach ($link->titles as $lang => $value) {
                if ($lang == null) {
                    $lang = 'default';
                }
                $o->links[$lid]->titles[$lang] = $value;
            }
            foreach ($link->properties as $property) {
                $o->links[$lid]->properties[$property->type] = $property->value;
            }
        }
        if (count($o->links) == 0) {
            unset($o->links);
        }

        return json_encode($o);
    }
}

?>


code:
<?php
/**
 * Part of XML_XRD
 *
 * PHP version 5
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @link     http://pear.php.net/package/XML_XRD
 */

/**
 * Generate XML from a XML_XRD object.
 *
 * @category XML
 * @package  XML_XRD
 * @author   Christian Weiske <cweiske@php.net>
 * @license  http://www.gnu.org/copyleft/lesser.html LGPL
 * @version  Release: @package_version@
 * @link     http://pear.php.net/package/XML_XRD
 */
class XML_XRD_Serializer_XML
{
    protected $xrd;

    /**
     * Create new instance
     *
     * @param XML_XRD $xrd XRD instance to convert to XML
     */
    public function __construct(XML_XRD $xrd)
    {
        $this->xrd = $xrd;
    }

    /**
     * Generate XML.
     *
     * @return string Full XML code
     */
    public function __toString()
    {
        $hasXsi = false;
        $x = new XMLWriter();
        $x->openMemory();
        //no encoding means UTF-8
        //http://www.w3.org/TR/2008/REC-xml-20081126/#sec-guessing-no-ext-info
        $x->startDocument('1.0', 'UTF-8');
        $x->setIndent(true);
        $x->startElement('XRD');
        $x->writeAttribute('xmlns', 'http://docs.oasis-open.org/ns/xri/xrd-1.0');
        $x->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
        if ($this->xrd->id) {
            $x->writeAttribute('xml:id', $this->xrd->id);
        }

        if ($this->xrd->expires !== null) {
            $x->writeElement(
                'Expires', gmdate('Y-m-d\TH:i:s\Z', $this->xrd->expires)
            );
        }
        if ($this->xrd->subject !== null) {
            $x->writeElement('Subject', $this->xrd->subject);
        }
        foreach ($this->xrd->aliases as $alias) {
            $x->writeElement('Alias', $alias);
        }
        foreach ($this->xrd->properties as $property) {
            $this->writeProperty($x, $property, $hasXsi);
        }

        foreach ($this->xrd->links as $link) {
            $x->startElement('Link');
            $x->writeAttribute('rel', $link->rel);
            if ($link->type !== null) {
                $x->writeAttribute('type', $link->type);
            }
            if ($link->href !== null) {
                $x->writeAttribute('href', $link->href);
            }
            //template only when no href
            if ($link->template !== null && $link->href === null) {
                $x->writeAttribute('template', $link->template);
            }

            foreach ($link->titles as $lang => $value) {
                $x->startElement('Title');
                if ($lang) {
                    $x->writeAttribute('xml:lang', $lang);
                }
                $x->text($value);
                $x->endElement();
            }
            foreach ($link->properties as $property) {
                $this->writeProperty($x, $property, $hasXsi);
            }
            $x->endElement();
        }

        $x->endElement();
        $x->endDocument();
        $s = $x->flush();
        if (!$hasXsi) {
            $s = str_replace(
                ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"', '', $s
            );
        }
        return $s;
    }

    /**
     * Write a property in the XMLWriter stream output
     *
     * @param XMLWriter                $x        Writer object to write to
     * @param XML_XRD_Element_Property $property Property to write
     * @param boolean                  &$hasXsi  If an xsi: attribute is used
     *
     * @return void
     */
    protected function writeProperty(
        XMLWriter $x, XML_XRD_Element_Property $property, &$hasXsi
    ) {
        $x->startElement('Property');
        $x->writeAttribute('type', $property->type);
        if ($property->value === null) {
            $x->writeAttribute('xsi:nil', 'true');
            $hasXsi = true;
        } else {
            $x->text($property->value);
        }
        $x->endElement();
    }
}

?>


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, StatusNet, Inc.
 *
 * This class performs lookups based on methods implemented in separate
 * classes, where a resource uri is given. Examples are WebFinger (RFC7033)
 * and the LRDD (Link-based Resource Descriptor Discovery) in RFC6415.
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Discovery
 * @package   GNUsocial
 * @author    James Walker <james@status.net>
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2010 StatusNet, Inc.
 * @copyright 2013 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://www.gnu.org/software/social/
 */

if (!defined('GNUSOCIAL')) { exit(1); }

class Discovery
{
    const LRDD_REL    = 'lrdd';
    const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from';
    const HCARD       = 'http://microformats.org/profile/hcard';
    const MF2_HCARD   = 'http://microformats.org/profile/h-card';   // microformats2 h-card

    const JRD_MIMETYPE_OLD = 'application/json';    // RFC6415 uses this
    const JRD_MIMETYPE = 'application/jrd+json';
    const XRD_MIMETYPE = 'application/xrd+xml';

    public $methods = array();

    /**
     * Constructor for a discovery object
     *
     * Registers different discovery methods.
     *
     * @return Discovery this
     */

    public function __construct()
    {
        if (Event::handle('StartDiscoveryMethodRegistration', array($this))) {
            Event::handle('EndDiscoveryMethodRegistration', array($this));
        }
    }

    public static function supportedMimeTypes()
    {
        return array('json'=>self::JRD_MIMETYPE,
                     'jsonold'=>self::JRD_MIMETYPE_OLD,
                     'xml'=>self::XRD_MIMETYPE);
    }

    /**
     * Register a discovery class
     *
     * @param string $class Class name
     *
     * @return void
     */
    public function registerMethod($class)
    {
        $this->methods[] = $class;
    }

    /**
     * Given a user ID, return the first available resource descriptor
     *
     * @param string $id User ID URI
     *
     * @return XML_XRD object for the resource descriptor of the id
     */
    public function lookup($id)
    {
        // Normalize the incoming $id to make sure we have a uri
        $uri = self::normalize($id);

        common_debug(sprintf('Performing discovery for "%s" (normalized "%s")', $id, $uri));

        foreach ($this->methods as $class) {
            try {
                $xrd = new XML_XRD();

                common_debug("LRDD discovery method for '$uri': {$class}");
                $lrdd = new $class;
                $links = $lrdd->discover($uri);
                $link = Discovery::getService($links, Discovery::LRDD_REL);

                // Load the LRDD XRD
                if (!empty($link->template)) {
                    $xrd_uri = Discovery::applyTemplate($link->template, $uri);
                } elseif (!empty($link->href)) {
                    $xrd_uri = $link->href;
                } else {
                    throw new Exception('No resource descriptor URI in link.');
                }

                $client  = new HTTPClient();
                $headers = array();
                if (!is_null($link->type)) {
                    $headers[] = "Accept: {$link->type}";
                }

                $response = $client->get($xrd_uri, $headers);
                if ($response->getStatus() != 200) {
                    throw new Exception('Unexpected HTTP status code.');
                }

                switch (common_bare_mime($response->getHeader('content-type'))) {
                case self::JRD_MIMETYPE_OLD:
                case self::JRD_MIMETYPE:
                    $type = 'json';
                    break;
                case self::XRD_MIMETYPE:
                    $type = 'xml';
                    break;
                default:
                    // fall back to letting XML_XRD auto-detect
                    common_debug('No recognized content-type header for resource descriptor body on '._ve($xrd_uri));
                    $type = null;
                }
                $xrd->loadString($response->getBody(), $type);
                return $xrd;

            } catch (ClientException $e) {
                if ($e->getCode() === 403) {
                    common_log(LOG_INFO, sprintf('%s: Aborting discovery on URL %s: %s', _ve($class), _ve($uri), _ve($e->getMessage())));
                    break;
                }
            } catch (Exception $e) {
                common_log(LOG_INFO, sprintf('%s: Failed for %s: %s', _ve($class), _ve($uri), _ve($e->getMessage())));
                continue;
            }
        }

        // TRANS: Exception. %s is an ID.
        throw new Exception(sprintf(_('Unable to find services for %s.'), $id));
    }

    /**
     * Given an array of links, returns the matching service
     *
     * @param array  $links   Links to check (as instances of XML_XRD_Element_Link)
     * @param string $service Service to find
     *
     * @return array $link assoc array representing the link
     */
    public static function getService(array $links, $service)
    {
        foreach ($links as $link) {
            if ($link->rel === $service) {
                return $link;
            }
            common_debug('LINK: rel '.$link->rel.' !== '.$service);
        }

        throw new Exception('No service link found');
    }

    /**
     * Given a "user id" make sure it's normalized to an acct: uri
     *
     * @param string $user_id User ID to normalize
     *
     * @return string normalized acct: URI
     */
    public static function normalize($uri)
    {
        if (is_null($uri) || $uri==='') {
            throw new Exception(_('No resource given.'));
        }

        $parts = parse_url($uri);
        // If we don't have a scheme, but the path implies user@host,
        // though this is far from a perfect matching procedure...
        if (!isset($parts['scheme']) && isset($parts['path'])
                && preg_match('/[\w@\w]/u', $parts['path'])) {
            return 'acct:' . $uri;
        }

        return $uri;
    }

    public static function isAcct($uri)
    {
        return (mb_strtolower(mb_substr($uri, 0, 5)) == 'acct:');
    }

    /**
     * Apply a template using an ID
     *
     * Replaces {uri} in template string with the ID given.
     *
     * @param string $template Template to match
     * @param string $uri      URI to replace with
     *
     * @return string replaced values
     */
    public static function applyTemplate($template, $uri)
    {
        $template = str_replace('{uri}', urlencode($uri), $template);

        return $template;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, StatusNet, Inc.
 *
 * Parse HTTP response for interesting Link: headers
 *
 * PHP version 5
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/**
 * Class to represent Link: headers in an HTTP response
 *
 * Since these are a fairly important part of Hammer-stack discovery, they're
 * reified and implemented here.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 *
 * @see       Discovery
 */
class LinkHeader
{
    var $href;
    var $rel;
    var $type;

    /**
     * Initialize from a string
     *
     * @param string $str Link: header value
     *
     * @return LinkHeader self
     */
    function __construct($str)
    {
        preg_match('/^<[^>]+>/', $str, $uri_reference);
        //if (empty($uri_reference)) return;

        $this->href = trim($uri_reference[0], '<>');
        $this->rel  = array();
        $this->type = null;

        // remove uri-reference from header
        $str = substr($str, strlen($uri_reference[0]));

        // parse link-params
        $params = explode(';', $str);

        foreach ($params as $param) {
            if (empty($param)) {
                continue;
            }
            list($param_name, $param_value) = explode('=', $param, 2);

            $param_name  = trim($param_name);
            $param_value = preg_replace('(^"|"$)', '', trim($param_value));

            // for now we only care about 'rel' and 'type' link params
            // TODO do something with the other links-params
            switch ($param_name) {
            case 'rel':
                $this->rel = trim($param_value);
                break;

            case 'type':
                $this->type = trim($param_value);
            }
        }
    }

    /**
     * Given an HTTP response, return the requested Link: header
     *
     * @param HTTP_Request2_Response $response response to check
     * @param string                 $rel      relationship to look for
     * @param string                 $type     media type to look for
     *
     * @return LinkHeader discovered header, or null on failure
     */
    static function getLink($response, $rel=null, $type=null)
    {
        $headers = $response->getHeader('Link');
        if ($headers) {
            // Can get an array or string, so try to simplify the path
            if (!is_array($headers)) {
                $headers = array($headers);
            }

            foreach ($headers as $header) {
                $lh = new LinkHeader($header);

                if ((is_null($rel) || $lh->rel == $rel) &&
                    (is_null($type) || $lh->type == $type)) {
                    return $lh->href;
                }
            }
        }
        return null;
    }
}


code:
<?php
/**
 * Abstract class for LRDD discovery methods
 *
 * Objects that extend this class can retrieve an array of
 * resource descriptor links for the URI. The array consists
 * of XML_XRD_Element_Link elements.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
abstract class LRDDMethod
{
    protected $xrd = null;

    public function __construct() {
        $this->xrd = new XML_XRD();
    }

    /**
     * Discover interesting info about the URI
     *
     * @param string $uri URI to inquire about
     *
     * @return array of XML_XRD_Element_Link elements to discovered resource descriptors
     */
    abstract public function discover($uri);

    protected function fetchUrl($url, $method=HTTPClient::METHOD_GET)
    {
        // If we have a blacklist enabled, let's check against it
        Event::handle('UrlBlacklistTest', array($url));

        $client  = new HTTPClient();

        // GAAHHH, this method sucks! How about we make a better HTTPClient interface?
        switch ($method) {
        case HTTPClient::METHOD_GET:
            $response = $client->get($url);
            break;
        case HTTPClient::METHOD_HEAD:
            $response = $client->head($url);
            break;
        default:
            throw new Exception('Bad HTTP method.');
        }

        if ($response->getStatus() != 200) {
            throw new Exception('Unexpected HTTP status code.');
        }

        return $response;
    }
}


code:
<?php
/**
 * Implementation of discovery using host-meta file
 *
 * Discovers resource descriptor file for a user by going to the
 * organization's host-meta file and trying to find a template for LRDD.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class LRDDMethod_HostMeta extends LRDDMethod
{
    /**
     * For RFC6415 and HTTP URIs, fetch the host-meta file
     * and look for LRDD templates
     */
    public function discover($uri)
    {
        // This is allowed for RFC6415 but not the 'WebFinger' RFC7033.
        $try_schemes = array('https', 'http');

        $scheme = mb_strtolower(parse_url($uri, PHP_URL_SCHEME));
        switch ($scheme) {
        case 'acct':
            if (!Discovery::isAcct($uri)) {
                throw new Exception('Bad resource URI: '.$uri);
            }
            // We can't use parse_url data for this, since the 'host'
            // entry is only set if the scheme has '://' after it.
            list($user, $domain) = explode('@', parse_url($uri, PHP_URL_PATH));
            break;
        case 'http':
        case 'https':
            $domain = mb_strtolower(parse_url($uri, PHP_URL_HOST));
            $try_schemes = array($scheme);
            break;
        default:
            throw new Exception('Unable to discover resource descriptor endpoint.');
        }

        foreach ($try_schemes as $scheme) {
            $url = $scheme . '://' . $domain . '/.well-known/host-meta';

            try {
                $response = self::fetchUrl($url);
                $this->xrd->loadString($response->getBody());
            } catch (Exception $e) {
                common_debug('LRDD could not load resource descriptor: '.$url.' ('.$e->getMessage().')');
                continue;
            }
            return $this->xrd->links;
        }

        throw new Exception('Unable to retrieve resource descriptor links.');
    }
}


code:
<?php
/**
 * Implementation of discovery using HTTP Link header
 *
 * Discovers XRD file for a user by fetching the URL and reading any
 * Link: headers in the HTTP response.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class LRDDMethod_LinkHeader extends LRDDMethod
{
    /**
     * For HTTP IDs fetch the URL and look for Link headers.
     *
     * @todo fail out of WebFinger URIs faster
     */
    public function discover($uri)
    {
        $response = self::fetchUrl($uri, HTTPClient::METHOD_HEAD);

        $link_header = $response->getHeader('Link');
        if (empty($link_header)) {
            throw new Exception('No Link header found');
        }
        common_debug('LRDD LinkHeader found: '.var_export($link_header,true));

        return self::parseHeader($link_header);
    }

    /**
     * Given a string or array of headers, returns JRD-like assoc array
     *
     * @param string|array $header string or array of strings for headers
     *
     * @return array of associative arrays in JRD-like array format
     */
    protected static function parseHeader($header)
    {
        $lh = new LinkHeader($header);

        $link = new XML_XRD_Element_Link($lh->rel, $lh->href, $lh->type);

        return array($link);
    }
}


code:
<?php
/**
 * Implementation of discovery using HTML <link> element
 *
 * Discovers XRD file for a user by fetching the URL and reading any
 * <link> elements in the HTML response.
 *
 * @category  Discovery
 * @package   StatusNet
 * @author    James Walker <james@status.net>
 * @copyright 2010 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class LRDDMethod_LinkHTML extends LRDDMethod
{
    /**
     * For HTTP IDs, fetch the URL and look for <link> elements
     * in the HTML response.
     *
     * @todo fail out of WebFinger URIs faster
     */
    public function discover($uri)
    {
        $response = self::fetchUrl($uri);

        return self::parse($response->getBody());
    }

    /**
     * Parse HTML and return <link> elements
     *
     * Given an HTML string, scans the string for <link> elements
     *
     * @param string $html HTML to scan
     *
     * @return array array of associative arrays in JRD-ish array format
     */
    public function parse($html)
    {
        $links = array();

        preg_match('/<head(\s[^>]*)?>(.*?)<\/head>/is', $html, $head_matches);
        $head_html = $head_matches[2];

        preg_match_all('/<link\s[^>]*>/i', $head_html, $link_matches);

        foreach ($link_matches[0] as $link_html) {
            $link_url  = null;
            $link_rel  = null;
            $link_type = null;

            preg_match('/\srel=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $rel_matches);
            if ( isset($rel_matches[3]) ) {
                $link_rel = $rel_matches[3];
            } else if ( isset($rel_matches[1]) ) {
                $link_rel = $rel_matches[1];
            }

            preg_match('/\shref=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $href_matches);
            if ( isset($href_matches[3]) ) {
                $link_uri = $href_matches[3];
            } else if ( isset($href_matches[1]) ) {
                $link_uri = $href_matches[1];
            }

            preg_match('/\stype=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $type_matches);
            if ( isset($type_matches[3]) ) {
                $link_type = $type_matches[3];
            } else if ( isset($type_matches[1]) ) {
                $link_type = $type_matches[1];
            }

            $links[] = new XML_XRD_Element_Link($link_rel, $link_uri, $link_type);
        }

        return $links;
    }
}


code:
<?php
/**
 * Implementation of WebFinger resource discovery (RFC7033)
 *
 * @category  Discovery
 * @package   GNUsocial
 * @author    Mikael Nordfeldth <mmn@hethane.se>
 * @copyright 2013 Free Software Foundation, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
class LRDDMethod_WebFinger extends LRDDMethod
{
    /**
     * Simply returns the WebFinger URL over HTTPS at the uri's domain:
     * https://{domain}/.well-known/webfinger?resource={uri}
     */
    public function discover($uri)
    {
        if (!Discovery::isAcct($uri)) {
            throw new Exception('Bad resource URI: '.$uri);
        }
        list($user, $domain) = explode('@', parse_url($uri, PHP_URL_PATH));
        if (!filter_var($domain, FILTER_VALIDATE_IP)
                && !filter_var(gethostbyname($domain), FILTER_VALIDATE_IP)) {
            throw new Exception('Bad resource host.');
        }

        $link = new XML_XRD_Element_Link(
                        Discovery::LRDD_REL,
                        'https://' . $domain . '/.well-known/webfinger?resource={uri}',
                        Discovery::JRD_MIMETYPE,
                        true);  //isTemplate

        return array($link);
    }
}


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-08-14 14:51+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:64
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Afrikaans (http://www.transifex.com/gnu-social/gnu-social/language/af/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: af\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic (http://www.transifex.com/gnu-social/gnu-social/language/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic (Egypt) (http://www.transifex.com/gnu-social/gnu-social/language/ar_EG/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar_EG\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Asturian (http://www.transifex.com/gnu-social/gnu-social/language/ast/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ast\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Belarusian (Tarask) (http://www.transifex.com/gnu-social/gnu-social/language/be@tarask/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: be@tarask\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bulgarian (http://www.transifex.com/gnu-social/gnu-social/language/bg/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bg\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bengali (India) (http://www.transifex.com/gnu-social/gnu-social/language/bn_IN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: bn_IN\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Breton (http://www.transifex.com/gnu-social/gnu-social/language/br/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: br\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Catalan (http://www.transifex.com/gnu-social/gnu-social/language/ca/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ca\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Czech (http://www.transifex.com/gnu-social/gnu-social/language/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Danish (http://www.transifex.com/gnu-social/gnu-social/language/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: German (http://www.transifex.com/gnu-social/gnu-social/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Greek (http://www.transifex.com/gnu-social/gnu-social/language/el/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: el\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
# Luke Hollins <luke@farcry.ca>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-03-07 15:41+0000\n"
"Last-Translator: Luke Hollins <luke@farcry.ca>\n"
"Language-Team: English (United Kingdom) (http://www.transifex.com/gnu-social/gnu-social/language/en_GB/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: en_GB\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr "Implements LRDD support for GNU Social."


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 08:45+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Esperanto (http://www.transifex.com/gnu-social/gnu-social/language/eo/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: eo\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
# Ismael Moral <jastertdc@gmail.com>, 2015
# Juan Riquelme González <soulchainer@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-27 15:36+0000\n"
"Last-Translator: Juan Riquelme González <soulchainer@gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/gnu-social/gnu-social/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Plugin description.
#: LRDDPlugin.php:61
msgid "Implements LRDD support for GNU Social."
msgstr "Implementa soporte de <abbr title=\"Link-based Resource Descriptor Discovery\">LRDD</abbr> para GNU Social. "