Merge branch 'MDL-68292-admin-sesskey' of https://github.com/brendanheywood/moodle
authorJake Dallimore <jake@moodle.com>
Mon, 12 Oct 2020 06:36:41 +0000 (14:36 +0800)
committerJake Dallimore <jake@moodle.com>
Mon, 12 Oct 2020 06:36:41 +0000 (14:36 +0800)
27 files changed:
admin/classes/local/externalpage/accesscallback.php [new file with mode: 0644]
admin/tool/mobile/lang/en/tool_mobile.php
admin/tool/mobile/lib.php
admin/tool/mobile/logout.php [new file with mode: 0644]
backup/util/ui/renderer.php
calendar/export_execute.php
files/classes/external/delete/draft.php [new file with mode: 0644]
files/tests/externallib_test.php
lang/en/moodle.php
lib/classes/component.php
lib/classes/session/redis.php
lib/db/services.php
lib/form/templates/element-defaultcustom.mustache
lib/jabber/XMPP/BOSH.php
lib/jabber/XMPP/Exception.php
lib/jabber/XMPP/Log.php
lib/jabber/XMPP/README.txt [deleted file]
lib/jabber/XMPP/Roster.php
lib/jabber/XMPP/XMLObj.php
lib/jabber/XMPP/XMLStream.php
lib/jabber/XMPP/XMPP.php
lib/jabber/XMPP/XMPP_Old.php [deleted file]
lib/jabber/readme_moodle.txt
lib/thirdpartylibs.xml
lib/upgrade.txt
message/output/jabber/message_output_jabber.php
version.php

diff --git a/admin/classes/local/externalpage/accesscallback.php b/admin/classes/local/externalpage/accesscallback.php
new file mode 100644 (file)
index 0000000..05fb07f
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * External admin page class that allows a callback to be provided to determine whether page can be accessed
+ *
+ * @package     core_admin
+ * @copyright   2019 Marina Glancy
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_admin\local\externalpage;
+
+use admin_externalpage;
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("{$CFG->libdir}/adminlib.php");
+
+/**
+ * Admin externalpage class
+ *
+ * @package     core_admin
+ * @copyright   2019 Marina Glancy
+ * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class accesscallback extends admin_externalpage {
+
+    /** @var callable $accesscheckcallback */
+    protected $accesscheckcallback;
+
+    /**
+     * Class constructor
+     *
+     * @param string $name
+     * @param string $visiblename
+     * @param string $url
+     * @param callable $accesscheckcallback The callback method that will be executed to check whether user has access to
+     *     this page. The setting instance ($this) is passed as an argument to the callback. Should return boolean value
+     * @param bool $hidden
+     */
+    public function __construct(string $name, string $visiblename, string $url, callable $accesscheckcallback,
+            bool $hidden = false) {
+
+        $this->accesscheckcallback = $accesscheckcallback;
+
+        parent::__construct($name, $visiblename, $url, [], $hidden);
+    }
+
+    /**
+     * Determines if the current user has access to this external page based on access callback
+     *
+     * @return bool
+     */
+    public function check_access() {
+        return ($this->accesscheckcallback)($this);
+    }
+}
index 6fafc5f..f3e6d7d 100644 (file)
@@ -83,6 +83,7 @@ $string['iosappid_desc'] = 'This setting may be left as default unless you have
 $string['loginintheapp'] = 'Via the app';
 $string['logininthebrowser'] = 'Via a browser window (for SSO plugins)';
 $string['loginintheembeddedbrowser'] = 'Via an embedded browser (for SSO plugins)';
+$string['logoutconfirmation'] = 'Are you sure you want to log out from the mobile app on your mobile devices? By logging out, you will then need to re-enter your username and password in the mobile app on all devices where you have the app installed.';
 $string['mainmenu'] = 'Main menu';
 $string['managefiletypes'] = 'Manage file types';
 $string['minimumversion'] = 'If an app version is specified (3.8.0 or higher), any users using an older app version will be prompted to upgrade their app before being allowed access to the site.';
index 567af8b..6b9dbf3 100644 (file)
@@ -134,15 +134,11 @@ function tool_mobile_myprofile_navigation(\core_user\output\myprofile\tree $tree
         return;
     }
 
-    if (!$iscurrentuser) {
-        return;
-    }
-
     $newnodes = [];
     $mobilesettings = get_config('tool_mobile');
 
     // Check if we should display a QR code.
-    if (!empty($mobilesettings->qrcodetype)) {
+    if ($iscurrentuser && !empty($mobilesettings->qrcodetype)) {
         $mobileqr = null;
         $qrcodeforappstr = get_string('qrcodeformobileappaccess', 'tool_mobile');
 
@@ -182,6 +178,13 @@ function tool_mobile_myprofile_navigation(\core_user\output\myprofile\tree $tree
         $mobilestrconnected = get_string('lastsiteaccess');
         if ($usertoken->lastaccess) {
             $mobilelastaccess = userdate($usertoken->lastaccess) . "&nbsp; (" . format_time(time() - $usertoken->lastaccess) . ")";
+            // Logout link.
+            $validtoken = empty($usertoken->validuntil) || time() < $usertoken->validuntil;
+            if ($iscurrentuser && $validtoken) {
+                $url = new moodle_url('/'.$CFG->admin.'/tool/mobile/logout.php', ['sesskey' => sesskey()]);
+                $logoutlink = html_writer::link($url, get_string('logout'));
+                $mobilelastaccess .= "&nbsp; ($logoutlink)";
+            }
         } else {
             // We should not reach this point.
             $mobilelastaccess = get_string("never");
diff --git a/admin/tool/mobile/logout.php b/admin/tool/mobile/logout.php
new file mode 100644 (file)
index 0000000..17a768a
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Log out a user from his external mobile devices (phones, tables, Moodle Desktop app, etc..)
+ *
+ * @package tool_mobile
+ * @copyright 2020 Juan Leyva <juan@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../config.php');
+require_once($CFG->dirroot . '/admin/tool/mobile/lib.php');
+require_once($CFG->dirroot . '/webservice/lib.php');
+
+if (!$CFG->enablemobilewebservice) {
+    print_error('enablewsdescription', 'webservice');
+}
+
+require_login(null, false);
+
+// Require an active user: not guest, not suspended.
+core_user::require_active_user($USER);
+
+$redirecturl = new \moodle_url('/user/profile.php');
+
+if (optional_param('confirm', 0, PARAM_INT) && data_submitted()) {
+    require_sesskey();
+
+    // Get the mobile service token to be deleted.
+    $token = tool_mobile_get_token($USER->id);
+
+    if ($token) {
+        $webservicemanager = new webservice();
+        $webservicemanager->delete_user_ws_token($token->id);
+    }
+    redirect($redirecturl);
+}
+
+// Page settings.
+$title = get_string('logout');
+$context = context_system::instance();
+$PAGE->set_url(new \moodle_url('/'.$CFG->admin.'/tool/mobile/logout.php'));
+$PAGE->navbar->add($title);
+$PAGE->set_context($context);
+$PAGE->set_title($SITE->fullname. ': ' . $title);
+
+// Display the page.
+echo $OUTPUT->header();
+
+$message = get_string('logoutconfirmation', 'tool_mobile');
+$confirmurl = new \moodle_url('logout.php', ['confirm' => 1]);
+$yesbutton = new single_button($confirmurl, get_string('yes'), 'post');
+$nobutton = new single_button($redirecturl, get_string('no'));
+echo $OUTPUT->confirm($message, $yesbutton, $nobutton);
+
+echo $OUTPUT->footer();
index 4a1f187..d404126 100644 (file)
@@ -126,8 +126,9 @@ class core_backup_renderer extends plugin_renderer_base {
 
         $html  = html_writer::start_tag('div', array('class' => 'backup-restore'));
 
-        $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
-        $html .= $this->output->heading(get_string('backupdetails', 'backup'), 2, array('class' => 'header'));
+        $html .= html_writer::start_tag('div', ['class' => 'backup-section',
+            'role' => 'table', 'aria-labelledby' => 'backupdetailsheader']);
+        $html .= $this->output->heading(get_string('backupdetails', 'backup'), 2, 'header', 'backupdetailsheader');
         $html .= $this->backup_detail_pair(get_string('backuptype', 'backup'), get_string('backuptype'.$details->type, 'backup'));
         $html .= $this->backup_detail_pair(get_string('backupformat', 'backup'), get_string('backupformat'.$details->format, 'backup'));
         $html .= $this->backup_detail_pair(get_string('backupmode', 'backup'), get_string('backupmode'.$details->mode, 'backup'));
@@ -153,8 +154,9 @@ class core_backup_renderer extends plugin_renderer_base {
 
         $html .= html_writer::end_tag('div');
 
-        $html .= html_writer::start_tag('div', array('class' => 'backup-section settings-section'));
-        $html .= $this->output->heading(get_string('backupsettings', 'backup'), 2, array('class' => 'header'));
+        $html .= html_writer::start_tag('div', ['class' => 'backup-section settings-section',
+            'role' => 'table', 'aria-labelledby' => 'backupsettingsheader']);
+        $html .= $this->output->heading(get_string('backupsettings', 'backup'), 2, 'header', 'backupsettingsheader');
         foreach ($details->root_settings as $label => $value) {
             if ($label == 'filename' or $label == 'user_files') {
                 continue;
@@ -164,8 +166,9 @@ class core_backup_renderer extends plugin_renderer_base {
         $html .= html_writer::end_tag('div');
 
         if ($details->type === 'course') {
-            $html .= html_writer::start_tag('div', array('class' => 'backup-section'));
-            $html .= $this->output->heading(get_string('backupcoursedetails', 'backup'), 2, array('class' => 'header'));
+            $html .= html_writer::start_tag('div', ['class' => 'backup-section',
+                    'role' => 'table', 'aria-labelledby' => 'backupcoursedetailsheader']);
+            $html .= $this->output->heading(get_string('backupcoursedetails', 'backup'), 2, 'header', 'backupcoursedetailsheader');
             $html .= $this->backup_detail_pair(get_string('coursetitle', 'backup'), $details->course->title);
             $html .= $this->backup_detail_pair(get_string('courseid', 'backup'), $details->course->courseid);
 
@@ -200,7 +203,7 @@ class core_backup_renderer extends plugin_renderer_base {
                         $table->data = array();
                     }
                     $name = get_string('pluginname', $activity->modulename);
-                    $icon = new image_icon('icon', $name, $activity->modulename, array('class' => 'iconlarge icon-pre'));
+                    $icon = new image_icon('icon', '', $activity->modulename, ['class' => 'iconlarge icon-pre']);
                     $table->data[] = array(
                         $this->output->render($icon).$name,
                         $activity->title,
@@ -424,13 +427,25 @@ class core_backup_renderer extends plugin_renderer_base {
     protected function backup_detail_pair($label, $value) {
         static $count = 0;
         $count ++;
-        $html  = html_writer::start_tag('div', array('class' => 'detail-pair'));
-        $html .= html_writer::tag('label', $label, array('class' => 'detail-pair-label', 'for' => 'detail-pair-value-'.$count));
-        $html .= html_writer::tag('div', $value, array('class' => 'detail-pair-value pl-2', 'name' => 'detail-pair-value-'.$count));
+        $html  = html_writer::start_tag('div', ['class' => 'detail-pair', 'role' => 'row']);
+        $html .= html_writer::tag('div', $label, ['class' => 'detail-pair-label mb-2', 'role' => 'cell']);
+        $html .= html_writer::tag('div', $value, ['class' => 'detail-pair-value pl-2', 'role' => 'cell']);
         $html .= html_writer::end_tag('div');
         return $html;
     }
 
+    /**
+     * Creates a unique id string by appending an incremental number to the prefix.
+     *
+     * @param string $prefix To be used as the left part of the id string.
+     * @return string
+     */
+    protected function make_unique_id(string $prefix): string {
+        static $count = 0;
+
+        return $prefix . '-' . $count++;
+    }
+
     /**
      * Created a detailed pairing with an input
      *
@@ -448,9 +463,11 @@ class core_backup_renderer extends plugin_renderer_base {
         } else {
             $description = '';
         }
+        $id = $this->make_unique_id('detail-pair-value');
         return $this->backup_detail_pair(
-            $label,
-            html_writer::empty_tag('input', $attributes + array('name' => $name, 'type' => $type, 'value' => $value)) . $description
+            html_writer::label($label, $id),
+            html_writer::empty_tag('input', $attributes + ['id' => $id, 'name' => $name, 'type' => $type, 'value' => $value]) .
+                $description
         );
     }
 
@@ -718,8 +735,6 @@ class core_backup_renderer extends plugin_renderer_base {
      * @return string
      */
     public function render_restore_course_search(restore_course_search $component) {
-        $url = $component->get_url();
-
         $output = html_writer::start_tag('div', array('class' => 'restore-course-search mb-1'));
         $output .= html_writer::start_tag('div', array('class' => 'rcs-results table-sm w-75'));
 
@@ -733,11 +748,18 @@ class core_backup_renderer extends plugin_renderer_base {
                 if (!$course->visible) {
                     $row->attributes['class'] .= ' dimmed';
                 }
-                $row->cells = array(
-                    html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $course->id)),
-                    format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
-                    format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
-                );
+                $id = $this->make_unique_id('restore-course');
+                $row->cells = [
+                    html_writer::empty_tag('input', ['type' => 'radio', 'name' => 'targetid', 'value' => $course->id,
+                        'id' => $id]),
+                    html_writer::label(
+                        format_string($course->shortname, true, ['context' => context_course::instance($course->id)]),
+                        $id,
+                        true,
+                        ['class' => 'd-block']
+                    ),
+                    format_string($course->fullname, true, ['context' => context_course::instance($course->id)])
+                ];
                 $table->data[] = $row;
             }
             if ($component->has_more_results()) {
@@ -779,8 +801,6 @@ class core_backup_renderer extends plugin_renderer_base {
      * @return string
      */
     public function render_import_course_search(import_course_search $component) {
-        $url = $component->get_url();
-
         $output = html_writer::start_tag('div', array('class' => 'import-course-search'));
         if ($component->get_count() === 0) {
             $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
@@ -790,6 +810,8 @@ class core_backup_renderer extends plugin_renderer_base {
                 'type' => 'text',
                 'name' => restore_course_search::$VAR_SEARCH,
                 'value' => $component->get_search(),
+                'aria-label' => get_string('searchcourses'),
+                'placeholder' => get_string('searchcourses'),
                 'class' => 'form-control'
             );
             $output .= html_writer::empty_tag('input', $attrs);
@@ -825,11 +847,18 @@ class core_backup_renderer extends plugin_renderer_base {
             if (!$course->visible) {
                 $row->attributes['class'] .= ' dimmed';
             }
-            $row->cells = array(
-                html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'importid', 'value' => $course->id)),
-                format_string($course->shortname, true, array('context' => context_course::instance($course->id))),
-                format_string($course->fullname, true, array('context' => context_course::instance($course->id)))
-            );
+            $id = $this->make_unique_id('import-course');
+            $row->cells = [
+                html_writer::empty_tag('input', ['type' => 'radio', 'name' => 'importid', 'value' => $course->id,
+                    'id' => $id]),
+                html_writer::label(
+                    format_string($course->shortname, true, ['context' => context_course::instance($course->id)]),
+                    $id,
+                    true,
+                    ['class' => 'd-block']
+                ),
+                format_string($course->fullname, true, ['context' => context_course::instance($course->id)])
+            ];
             $table->data[] = $row;
         }
         if ($component->has_more_results()) {
@@ -848,6 +877,8 @@ class core_backup_renderer extends plugin_renderer_base {
             'type' => 'text',
             'name' => restore_course_search::$VAR_SEARCH,
             'value' => $component->get_search(),
+            'aria-label' => get_string('searchcourses'),
+            'placeholder' => get_string('searchcourses'),
             'class' => 'form-control');
         $output .= html_writer::empty_tag('input', $attrs);
         $attrs = array(
@@ -870,8 +901,6 @@ class core_backup_renderer extends plugin_renderer_base {
      * @return string
      */
     public function render_restore_category_search(restore_category_search $component) {
-        $url = $component->get_url();
-
         $output = html_writer::start_tag('div', array('class' => 'restore-course-search mb-1'));
         $output .= html_writer::start_tag('div', array('class' => 'rcs-results table-sm w-75'));
 
@@ -887,12 +916,19 @@ class core_backup_renderer extends plugin_renderer_base {
                     $row->attributes['class'] .= ' dimmed';
                 }
                 $context = context_coursecat::instance($category->id);
-                $row->cells = array(
-                    html_writer::empty_tag('input', array('type' => 'radio', 'name' => 'targetid', 'value' => $category->id)),
-                    format_string($category->name, true, array('context' => context_coursecat::instance($category->id))),
+                $id = $this->make_unique_id('restore-category');
+                $row->cells = [
+                    html_writer::empty_tag('input', ['type' => 'radio', 'name' => 'targetid', 'value' => $category->id,
+                        'id' => $id]),
+                    html_writer::label(
+                        format_string($category->name, true, ['context' => context_coursecat::instance($category->id)]),
+                        $id,
+                        true,
+                        ['class' => 'd-block']
+                    ),
                     format_text(file_rewrite_pluginfile_urls($category->description, 'pluginfile.php', $context->id,
-                        'coursecat', 'description', null), $category->descriptionformat, array('overflowdiv' => true))
-                );
+                        'coursecat', 'description', null), $category->descriptionformat, ['overflowdiv' => true])
+                ];
                 $table->data[] = $row;
             }
             if ($component->has_more_results()) {
@@ -918,7 +954,7 @@ class core_backup_renderer extends plugin_renderer_base {
             'inform' => true,
             'extraclasses' => 'rcs-search mb-3 w-25',
             'inputname' => restore_category_search::$VAR_SEARCH,
-            'searchstring' => get_string('search'),
+            'searchstring' => get_string('searchcoursecategories'),
             'query' => $component->get_search(),
         ];
         $output .= $this->output->render_from_template('core/search_input', $data);
index 66c348e..5a53e7e 100644 (file)
@@ -18,7 +18,7 @@ if (empty($CFG->enablecalendarexport)) {
 $checkuserid = !empty($userid) && $user = $DB->get_record('user', array('id' => $userid), 'id,password');
 //allowing for fallback check of old url - MDL-27542
 $checkusername = !empty($username) && $user = $DB->get_record('user', array('username' => $username), 'id,password');
-if (!$checkuserid && !$checkusername) {
+if ((!$checkuserid && !$checkusername) || !$user) {
     //No such user
     die('Invalid authentication');
 }
diff --git a/files/classes/external/delete/draft.php b/files/classes/external/delete/draft.php
new file mode 100644 (file)
index 0000000..3ad88a8
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This is the external method for deleting draft files.
+ *
+ * @package    core_files
+ * @since      Moodle 3.10
+ * @copyright  2020 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_files\external\delete;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/externallib.php');
+require_once($CFG->libdir . '/filelib.php');
+
+use external_api;
+use external_function_parameters;
+use external_multiple_structure;
+use external_single_structure;
+use external_value;
+use external_warnings;
+use context_user;
+
+/**
+ * This is the external method for deleting draft files.
+ *
+ * @copyright  2020 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class draft extends external_api {
+
+    /**
+     * Describes the parameters for execute.
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.10
+     */
+    public static function execute_parameters() : external_function_parameters {
+        return new external_function_parameters (
+            [
+                'draftitemid' => new external_value(PARAM_INT, 'Item id of the draft file area'),
+                'files' => new external_multiple_structure(
+                    new external_single_structure(
+                        [
+                            'filepath'  => new external_value(PARAM_PATH, 'Path to the file or directory to delete.'),
+                            'filename'  => new external_value(PARAM_FILE, 'Name of the file to delete.'),
+                        ]
+                    ), 'Files or directories to be deleted.'
+                ),
+            ]
+        );
+    }
+
+    /**
+     * Delete the indicated files (or directories) from a user draft file area.
+     *
+     * @param  int    $draftitemid item id of the draft file area
+     * @param  array  $files       files to be deleted
+     * @return array of warnings and parent paths of the files deleted
+     * @since Moodle 3.10
+     */
+    public static function execute(int $draftitemid, array $files) : array {
+        global $CFG, $USER;
+        require_once($CFG->dirroot . '/repository/lib.php');
+
+        $params = self::validate_parameters(self::execute_parameters(), compact('draftitemid', 'files'));
+        [$draftitemid, $files] = array_values($params);
+
+        $usercontext = context_user::instance($USER->id);
+        self::validate_context($usercontext);
+
+        $files = array_map(function($file) {
+            return (object) $file;
+        }, $files);
+        $parentpaths = repository_delete_selected_files($usercontext, 'user', 'draft', $draftitemid, $files);
+
+        return [
+            'parentpaths' => array_keys($parentpaths),
+            'warnings' => [],
+        ];
+    }
+
+    /**
+     * Describes the execute return value.
+     *
+     * @return external_single_structure
+     * @since Moodle 3.10
+     */
+    public static function execute_returns() : external_single_structure {
+        return new external_single_structure(
+            [
+                'parentpaths' => new external_multiple_structure(
+                    new external_value(PARAM_PATH, 'Path to parent directory of the deleted files.')
+                ),
+                'warnings' => new external_warnings(),
+            ]
+        );
+    }
+}
index b652e6d..91a9e13 100644 (file)
@@ -293,4 +293,57 @@ class core_files_externallib_testcase extends advanced_testcase {
 
         $this->assertEquals($testfilelisting, $testdata);
     }
+
+    /**
+     * Test delete draft files
+     */
+    public function test_delete_draft_files() {
+        global $USER;
+
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        // Add files to user draft area.
+        $draftitemid = file_get_unused_draft_itemid();
+        $context = context_user::instance($USER->id);
+        $filerecordinline = array(
+            'contextid' => $context->id,
+            'component' => 'user',
+            'filearea'  => 'draft',
+            'itemid'    => $draftitemid,
+            'filepath'  => '/',
+            'filename'  => 'faketxt.txt',
+        );
+        $fs = get_file_storage();
+        $fs->create_file_from_string($filerecordinline, 'fake txt contents 1.');
+
+        // Now create a folder with a file inside.
+        $fs->create_directory($context->id, 'user', 'draft', $draftitemid, '/fakefolder/');
+        $filerecordinline['filepath'] = '/fakefolder/';
+        $filerecordinline['filename'] = 'fakeimage.png';
+        $fs->create_file_from_string($filerecordinline, 'img...');
+
+        // Check two files were created (one file and one directory).
+        $files = core_files_external::get_files($context->id, 'user', 'draft', $draftitemid, '/', '');
+        $files = external_api::clean_returnvalue(core_files_external::get_files_returns(), $files);
+        $this->assertCount(2, $files['files']);
+
+        // Check the folder has one file.
+        $files = core_files_external::get_files($context->id, 'user', 'draft', $draftitemid, '/fakefolder/', '');
+        $files = external_api::clean_returnvalue(core_files_external::get_files_returns(), $files);
+        $this->assertCount(1, $files['files']);
+
+        // Delete a file and a folder.
+        $filestodelete = [
+            ['filepath' => '/', 'filename' => 'faketxt.txt'],
+            ['filepath' => '/fakefolder/', 'filename' => ''],
+        ];
+        $paths = core_files\external\delete\draft::execute($draftitemid, $filestodelete);
+        $paths = external_api::clean_returnvalue(core_files\external\delete\draft::execute_returns(), $paths);
+
+        // Check everything was deleted.
+        $files = core_files_external::get_files($context->id, 'user', 'draft', $draftitemid, '/', '');
+        $files = external_api::clean_returnvalue(core_files_external::get_files_returns(), $files);
+        $this->assertCount(0, $files['files']);
+    }
 }
index 41b2cee..3db9de3 100644 (file)
@@ -1819,6 +1819,7 @@ $string['searchagain'] = 'Search again';
 $string['searchactivities'] = 'Search for activities by name or description';
 $string['searchbyemail'] = 'Search by email address';
 $string['searchbyusername'] = 'Search by username';
+$string['searchcoursecategories'] = 'Search categories';
 $string['searchcourses'] = 'Search courses';
 $string['searchoptions'] = 'Search options';
 $string['searchresults'] = 'Search results';
index fa25ae4..e3366b6 100644 (file)
@@ -96,6 +96,7 @@ class core_component {
         'MoodleHQ\\RTLCSS' => 'lib/rtlcss',
         'ScssPhp\\ScssPhp' => 'lib/scssphp',
         'Box\\Spout' => 'lib/spout/src/Spout',
+        'BirknerAlex\\XMPPHP' => 'lib/jabber/XMPP',
         'MatthiasMullie\\Minify' => 'lib/minify/matthiasmullie-minify/src/',
         'MatthiasMullie\\PathConverter' => 'lib/minify/matthiasmullie-pathconverter/src/',
         'IMSGlobal\LTI' => 'lib/ltiprovider/src',
index 40639c2..61ee51e 100644 (file)
@@ -56,6 +56,9 @@ class redis extends handler {
     protected $lockretry = 100;
     /** @var int $serializer The serializer to use */
     protected $serializer = \Redis::SERIALIZER_PHP;
+    /** @var string $lasthash hash of the session data content */
+    protected $lasthash = null;
+
     /**
      * @var int $lockexpire how long to wait in seconds before expiring the lock automatically
      * so that other requests may continue execution, ignored if PECL redis is below version 2.2.0.
@@ -237,6 +240,7 @@ class redis extends handler {
      * @return bool true on success.  false on unable to unlock sessions.
      */
     public function handler_close() {
+        $this->lasthash = null;
         try {
             foreach ($this->locks as $id => $expirytime) {
                 if ($expirytime > $this->time()) {
@@ -269,6 +273,7 @@ class redis extends handler {
                 if ($this->requires_write_lock()) {
                     $this->unlock_session($id);
                 }
+                $this->lasthash = sha1('');
                 return '';
             }
             $this->connection->expire($id, $this->timeout);
@@ -276,6 +281,7 @@ class redis extends handler {
             error_log('Failed talking to redis: '.$e->getMessage());
             throw $e;
         }
+        $this->lasthash = sha1(base64_encode($sessiondata));
         return $sessiondata;
     }
 
@@ -287,6 +293,14 @@ class redis extends handler {
      * @return bool true on write success, false on failure
      */
     public function handler_write($id, $data) {
+
+        $hash = sha1(base64_encode($data));
+
+        // If the content has not changed don't bother writing.
+        if ($hash === $this->lasthash) {
+            return true;
+        }
+
         if (is_null($this->connection)) {
             // The session has already been closed, don't attempt another write.
             error_log('Tried to write session: '.$id.' before open or after close.');
@@ -313,6 +327,7 @@ class redis extends handler {
      * @return bool true if the session was deleted, false otherwise.
      */
     public function handler_destroy($id) {
+        $this->lasthash = null;
         try {
             $this->connection->del($id);
             $this->unlock_session($id);
index c589221..9f21e40 100644 (file)
@@ -814,6 +814,13 @@ $functions = array(
         'type'        => 'write',
         'classpath'   => 'files/externallib.php',
     ),
+    'core_files_delete_draft_files' => array(
+        'classname' => 'core_files\external\delete\draft',
+        'methodname' => 'execute',
+        'description' => 'Delete the indicated files (or directories) from a user draft file area.',
+        'type'        => 'write',
+        'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+    ),
     'core_form_get_filetypes_browser_data' => array(
         'classname' => 'core_form\external',
         'methodname' => 'get_filetypes_browser_data',
index 3c6fbda..af33ffc 100644 (file)
         }
     }
 }}
-{{< core_form/element-template }}
+{{< core_form/element-group }}
     {{$element}}
-        <span class="fdefaultcustom">
-        {{#element.elements}}
-            {{{separator}}}
-            {{{html}}}
-        {{/element.elements}}
-        </span>
+        <fieldset class="w-100 m-0 p-0 border-0" id="{{element.id}}">
+            <legend class="sr-only">{{label}}</legend>
+            <div class="d-flex flex-wrap align-items-center">
+                <span class="fdefaultcustom">
+                    {{#element.elements}}
+                        {{{separator}}}
+                        {{{html}}}
+                    {{/element.elements}}
+                </span>
+            </div>
+        </fieldset>
     {{/element}}
-{{/ core_form/element-template }}
+{{/ core_form/element-group }}
index befaf60..51b7077 100644 (file)
 <?php
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
 
-/** XMPPHP_XMLStream */
-require_once dirname(__FILE__) . "/XMPP.php";
+namespace BirknerAlex\XMPPHP;
+
+    /**
+     * XMPPHP: The PHP XMPP Library
+     * Copyright (C) 2008  Nathanael C. Fritz
+     * This file is part of SleekXMPP.
+     *
+     * XMPPHP is free software; you can redistribute it and/or modify
+     * it under the terms of the GNU General Public License as published by
+     * the Free Software Foundation; either version 2 of the License, or
+     * (at your option) any later version.
+     *
+     * XMPPHP 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 General Public License for more details.
+     *
+     * You should have received a copy of the GNU General Public License
+     * along with XMPPHP; if not, write to the Free Software
+     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+     *
+     * @category   xmpphp
+     * @package    XMPPHP
+     * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+     * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+     * @author     Michael Garvin <JID: gar@netflint.net>
+     * @author     Alexander Birkner (https://github.com/BirknerAlex)
+     * @copyright  2008 Nathanael C. Fritz
+     */
 
 /**
  * XMPPHP Main Class
- * 
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-class XMPPHP_BOSH extends XMPPHP_XMPP {
-
-               protected $rid;
-               protected $sid;
-               protected $http_server;
-               protected $http_buffer = Array();
-               protected $session = false;
+class BOSH extends XMPP
+{
+    protected $rid;
+    protected $sid;
+    protected $http_server;
+    protected $http_buffer = Array();
+    protected $session = false;
 
-               public function connect($server, $wait='1', $session=false) {
-                       $this->http_server = $server;
-                       $this->use_encryption = false;
-                       $this->session = $session;
+    public function connect($server, $wait = '1', $session = false)
+    {
+        $this->http_server = $server;
+        $this->use_encryption = false;
+        $this->session = $session;
 
-                       $this->rid = 3001;
-                       $this->sid = null;
-                       if($session)
-                       {
-                               $this->loadSession();
-                       }
-                       if(!$this->sid) {
-                               $body = $this->__buildBody();
-                               $body->addAttribute('hold','1');
-                               $body->addAttribute('to', $this->host);
-                               $body->addAttribute('route', "xmpp:{$this->host}:{$this->port}");
-                               $body->addAttribute('secure','true');
-                               $body->addAttribute('xmpp:version','1.6', 'urn:xmpp:xbosh');
-                               $body->addAttribute('wait', strval($wait));
-                               $body->addAttribute('ack','1');
-                               $body->addAttribute('xmlns:xmpp','urn:xmpp:xbosh');
-                               $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
-                               xml_parse($this->parser, $buff, false);
-                               $response = $this->__sendBody($body);
-                               $rxml = new SimpleXMLElement($response);
-                               $this->sid = $rxml['sid'];
+        $this->rid = 3001;
+        $this->sid = null;
+        if ($session) {
+            $this->loadSession();
+        }
+        if (!$this->sid) {
+            $body = $this->__buildBody();
+            $body->addAttribute('hold', '1');
+            $body->addAttribute('to', $this->host);
+            $body->addAttribute('route', "xmpp:{$this->host}:{$this->port}");
+            $body->addAttribute('secure', 'true');
+            $body->addAttribute('xmpp:version', '1.6', 'urn:xmpp:xbosh');
+            $body->addAttribute('wait', strval($wait));
+            $body->addAttribute('ack', '1');
+            $body->addAttribute('xmlns:xmpp', 'urn:xmpp:xbosh');
+            $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
+            xml_parse($this->parser, $buff, false);
+            $response = $this->__sendBody($body);
+            $rxml = new \SimpleXMLElement($response);
+            $this->sid = $rxml['sid'];
 
-                       } else {
-                               $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
-                               xml_parse($this->parser, $buff, false);
-                       }
-               }
+        } else {
+            $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
+            xml_parse($this->parser, $buff, false);
+        }
+    }
 
-               public function __sendBody($body=null, $recv=true) {
-                       if(!$body) {
-                               $body = $this->__buildBody();
-                       }
-                       $ch = curl_init($this->http_server);
-                       curl_setopt($ch, CURLOPT_HEADER, 0);
-                       curl_setopt($ch, CURLOPT_POST, 1);
-                       curl_setopt($ch, CURLOPT_POSTFIELDS, $body->asXML());
-                       curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
-                       $header = array('Accept-Encoding: gzip, deflate','Content-Type: text/xml; charset=utf-8');
-                       curl_setopt($ch, CURLOPT_HTTPHEADER, $header );
-                       curl_setopt($ch, CURLOPT_VERBOSE, 0);
-                       $output = '';
-                       if($recv) {
-                               curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-                               $output = curl_exec($ch);
-                               $this->http_buffer[] = $output;
-                       }
-                       curl_close($ch);
-                       return $output;
-               }
+    public function __sendBody($body = null, $recv = true)
+    {
+        if (!$body) {
+            $body = $this->__buildBody();
+        }
+        $ch = curl_init($this->http_server);
+        curl_setopt($ch, CURLOPT_HEADER, 0);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $body->asXML());
+        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+        $header = array('Accept-Encoding: gzip, deflate', 'Content-Type: text/xml; charset=utf-8');
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        curl_setopt($ch, CURLOPT_VERBOSE, 0);
+        $output = '';
+        if ($recv) {
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+            $output = curl_exec($ch);
+            $this->http_buffer[] = $output;
+        }
+        curl_close($ch);
+        return $output;
+    }
 
-               public function __buildBody($sub=null) {
-                       $xml = new SimpleXMLElement("<body xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh' />");
-                       $xml->addAttribute('content', 'text/xml; charset=utf-8');
-                       $xml->addAttribute('rid', $this->rid);
-                       $this->rid += 1;
-                       if($this->sid) $xml->addAttribute('sid', $this->sid);
-                       #if($this->sid) $xml->addAttribute('xmlns', 'http://jabber.org/protocol/httpbind');
-                       $xml->addAttribute('xml:lang', 'en');
-                       if($sub) { // ok, so simplexml is lame
-                               $p = dom_import_simplexml($xml);
-                               $c = dom_import_simplexml($sub);
-                               $cn = $p->ownerDocument->importNode($c, true);
-                               $p->appendChild($cn);
-                               $xml = simplexml_import_dom($p);
-                       }
-                       return $xml;
-               }
+    public function __buildBody($sub = null)
+    {
+        $xml = new \SimpleXMLElement("<body xmlns='http://jabber.org/protocol/httpbind' xmlns:xmpp='urn:xmpp:xbosh' />");
+        $xml->addAttribute('content', 'text/xml; charset=utf-8');
+        $xml->addAttribute('rid', $this->rid);
+        $this->rid += 1;
+        if ($this->sid) $xml->addAttribute('sid', $this->sid);
+        #if($this->sid) $xml->addAttribute('xmlns', 'http://jabber.org/protocol/httpbind');
+        $xml->addAttribute('xml:lang', 'en');
+        if ($sub) { // ok, so simplexml is lame
+            $p = dom_import_simplexml($xml);
+            $c = dom_import_simplexml($sub);
+            $cn = $p->ownerDocument->importNode($c, true);
+            $p->appendChild($cn);
+            $xml = simplexml_import_dom($p);
+        }
+        return $xml;
+    }
 
-               public function __process() {
-                       if($this->http_buffer) {
-                               $this->__parseBuffer();
-                       } else {
-                               $this->__sendBody();
-                               $this->__parseBuffer();
-                       }
-               }
+    public function __process()
+    {
+        if ($this->http_buffer) {
+            $this->__parseBuffer();
+        } else {
+            $this->__sendBody();
+            $this->__parseBuffer();
+        }
+    }
 
-               public function __parseBuffer() {
-                       while ($this->http_buffer) {
-                               $idx = key($this->http_buffer);
-                               $buffer = $this->http_buffer[$idx];
-                               unset($this->http_buffer[$idx]);
-                               if($buffer) {
-                                       $xml = new SimpleXMLElement($buffer);
-                                       $children = $xml->xpath('child::node()');
-                                       foreach ($children as $child) {
-                                               $buff = $child->asXML();
-                                               $this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
-                                               xml_parse($this->parser, $buff, false);
-                                       }
-                               }
-                       }
-               }
+    public function __parseBuffer()
+    {
+        while ($this->http_buffer) {
+            $idx = key($this->http_buffer);
+            $buffer = $this->http_buffer[$idx];
+            unset($this->http_buffer[$idx]);
+            if ($buffer) {
+                $xml = new \SimpleXMLElement($buffer);
+                $children = $xml->xpath('child::node()');
+                foreach ($children as $child) {
+                    $buff = $child->asXML();
+                    $this->log->log("RECV: $buff", Log::LEVEL_VERBOSE);
+                    xml_parse($this->parser, $buff, false);
+                }
+            }
+        }
+    }
 
-               public function send($msg) {
-                       $this->log->log("SEND: $msg",  XMPPHP_Log::LEVEL_VERBOSE);
-                       $msg = new SimpleXMLElement($msg);
-                       #$msg->addAttribute('xmlns', 'jabber:client');
-                       $this->__sendBody($this->__buildBody($msg), true);
-                       #$this->__parseBuffer();
-               }
+    public function send($msg)
+    {
+        $this->log->log("SEND: $msg", Log::LEVEL_VERBOSE);
+        $msg = new \SimpleXMLElement($msg);
+        #$msg->addAttribute('xmlns', 'jabber:client');
+        $this->__sendBody($this->__buildBody($msg), true);
+        #$this->__parseBuffer();
+    }
 
-               public function reset() {
-                       $this->xml_depth = 0;
-                       unset($this->xmlobj);
-                       $this->xmlobj = array();
-                       $this->setupParser();
-                       #$this->send($this->stream_start);
-                       $body = $this->__buildBody();
-                       $body->addAttribute('to', $this->host);
-                       $body->addAttribute('xmpp:restart', 'true', 'urn:xmpp:xbosh');
-                       $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
-                       $response = $this->__sendBody($body);
-                       $this->been_reset = true;
-                       xml_parse($this->parser, $buff, false);
-               }
+    public function reset()
+    {
+        $this->xml_depth = 0;
+        unset($this->xmlobj);
+        $this->xmlobj = array();
+        $this->setupParser();
+        #$this->send($this->stream_start);
+        $body = $this->__buildBody();
+        $body->addAttribute('to', $this->host);
+        $body->addAttribute('xmpp:restart', 'true', 'urn:xmpp:xbosh');
+        $buff = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
+        $response = $this->__sendBody($body);
+        $this->been_reset = true;
+        xml_parse($this->parser, $buff, false);
+    }
 
-               public function loadSession() {
-                       if(isset($_SESSION['XMPPHP_BOSH_RID'])) $this->rid = $_SESSION['XMPPHP_BOSH_RID'];
-                       if(isset($_SESSION['XMPPHP_BOSH_SID'])) $this->sid = $_SESSION['XMPPHP_BOSH_SID'];
-                       if(isset($_SESSION['XMPPHP_BOSH_authed'])) $this->authed = $_SESSION['XMPPHP_BOSH_authed'];
-                       if(isset($_SESSION['XMPPHP_BOSH_jid'])) $this->jid = $_SESSION['XMPPHP_BOSH_jid'];
-                       if(isset($_SESSION['XMPPHP_BOSH_fulljid'])) $this->fulljid = $_SESSION['XMPPHP_BOSH_fulljid'];
-               }
+    public function loadSession()
+    {
+        if (isset($_SESSION['XMPPHP_BOSH_RID'])) $this->rid = $_SESSION['XMPPHP_BOSH_RID'];
+        if (isset($_SESSION['XMPPHP_BOSH_SID'])) $this->sid = $_SESSION['XMPPHP_BOSH_SID'];
+        if (isset($_SESSION['XMPPHP_BOSH_authed'])) $this->authed = $_SESSION['XMPPHP_BOSH_authed'];
+        if (isset($_SESSION['XMPPHP_BOSH_jid'])) $this->jid = $_SESSION['XMPPHP_BOSH_jid'];
+        if (isset($_SESSION['XMPPHP_BOSH_fulljid'])) $this->fulljid = $_SESSION['XMPPHP_BOSH_fulljid'];
+    }
 
-               public function saveSession() {
-                       $_SESSION['XMPPHP_BOSH_RID'] = (string) $this->rid;
-                       $_SESSION['XMPPHP_BOSH_SID'] = (string) $this->sid;
-                       $_SESSION['XMPPHP_BOSH_authed'] = (boolean) $this->authed;
-                       $_SESSION['XMPPHP_BOSH_jid'] = (string) $this->jid;
-                       $_SESSION['XMPPHP_BOSH_fulljid'] = (string) $this->fulljid;
-               }
-}
+    public function saveSession()
+    {
+        $_SESSION['XMPPHP_BOSH_RID'] = (string)$this->rid;
+        $_SESSION['XMPPHP_BOSH_SID'] = (string)$this->sid;
+        $_SESSION['XMPPHP_BOSH_authed'] = (boolean)$this->authed;
+        $_SESSION['XMPPHP_BOSH_jid'] = (string)$this->jid;
+        $_SESSION['XMPPHP_BOSH_fulljid'] = (string)$this->fulljid;
+    }
+}
\ No newline at end of file
index da59bc7..c8129ab 100644 (file)
@@ -1,41 +1,47 @@
 <?php
+
+namespace BirknerAlex\XMPPHP;
+
+use \Exception as ObjectException;
+
 /**
  * XMPPHP: The PHP XMPP Library
  * Copyright (C) 2008  Nathanael C. Fritz
  * This file is part of SleekXMPP.
- * 
+ *
  * XMPPHP is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * XMPPHP 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 General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with XMPPHP; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
- * @category   xmpphp 
+ * @category   xmpphp
  * @package    XMPPHP
  * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
  * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * @author     Michael Garvin <JID: gar@netflint.net>
+ * @author     Alexander Birkner (https://github.com/BirknerAlex)
  * @copyright  2008 Nathanael C. Fritz
  */
 
 /**
- * XMPPHP Exception
+ * XMPPHP Main Class
  *
- * @category   xmpphp 
+ * @category   xmpphp
  * @package    XMPPHP
  * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
  * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
  * @version    $Id$
  */
-class XMPPHP_Exception extends Exception {
+class Exception extends ObjectException {
 }
index a9bce3d..0ceaeac 100644 (file)
@@ -1,42 +1,47 @@
 <?php
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
+
+namespace BirknerAlex\XMPPHP;
+
+       /**
+        * XMPPHP: The PHP XMPP Library
+        * Copyright (C) 2008  Nathanael C. Fritz
+        * This file is part of SleekXMPP.
+        *
+        * XMPPHP is free software; you can redistribute it and/or modify
+        * it under the terms of the GNU General Public License as published by
+        * the Free Software Foundation; either version 2 of the License, or
+        * (at your option) any later version.
+        *
+        * XMPPHP 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 General Public License for more details.
+        *
+        * You should have received a copy of the GNU General Public License
+        * along with XMPPHP; if not, write to the Free Software
+        * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+        *
+        * @category   xmpphp
+        * @package    XMPPHP
+        * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+        * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+        * @author     Michael Garvin <JID: gar@netflint.net>
+        * @author     Alexander Birkner (https://github.com/BirknerAlex)
+        * @copyright  2008 Nathanael C. Fritz
+        */
 
 /**
- * XMPPHP Log
- * 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * XMPPHP Main Class
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-class XMPPHP_Log {
+class Log {
        
        const LEVEL_ERROR   = 0;
        const LEVEL_WARNING = 1;
diff --git a/lib/jabber/XMPP/README.txt b/lib/jabber/XMPP/README.txt
deleted file mode 100644 (file)
index 0c2f53d..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-About
-================================================================================
-XMPPHP is an elegant PHP library for XMPP (aka Jabber, Google Talk, etc).
-
-Homepage: http://code.google.com/p/xmpphp
-Author: Nathan Fritz, jabber id: fritzy [at] netflint.net
-Co-Author: Stephan Wentz, jabber id: stephan [at] jabber.wentz.it
-
-If you have any questions (no matter how dumb), please send me an IM.  I enjoy
-helping people with my code.
-
-
-Requirements
-================================================================================
-* PHP 5.x
-* SSL Support Compiled
-
-History
-================================================================================
-Carlo Zottmann handed me maintenance of Class.Jabber.PHP years and years ago
-(2003?).  While I did fix some bugs, I never did much with it.  I promised many
-people that it would return as a PHP5 rewrite.  That day has finally come.
-
-This code is based on my experience with Class.Jabber.PHP, but more closely
-related to my Python library, SleekXMPP (http://code.google.com/p/sleekxmpp).
-
-Documentation
-================================================================================
-For now, look at the examples.  In the near future, I'll have better
-documentation on the website.
-
-TODO
-================================================================================
-* Documentation
-* MUC Support
-
-License Exception
-===============================================================================
-Please contact Nathan Fritz for library exceptions if you would like to
-distribute XMPPHP with a non-GPL compatible license.
-
-Also, if you would like to distribute XMPPHP as part of a commercial package,
-I sell commercial licenses.
index 69457b2..99ec8c1 100644 (file)
@@ -1,43 +1,46 @@
 <?php
+
+namespace BirknerAlex\XMPPHP;
+
 /**
  * XMPPHP: The PHP XMPP Library
  * Copyright (C) 2008  Nathanael C. Fritz
  * This file is part of SleekXMPP.
- * 
+ *
  * XMPPHP is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * XMPPHP 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 General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with XMPPHP; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
+ * @author     Alexander Birkner (https://github.com/BirknerAlex)
  * @copyright  2008 Nathanael C. Fritz
  */
 
 /**
- * XMPPHP Roster Object
- * 
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * XMPPHP Main Class
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-
 class Roster {
        /**
         * Roster array, handles contacts and presence.  Indexed by jid.
@@ -118,15 +121,17 @@ class Roster {
         * @param string $status
        */
        public function setPresence($presence, $priority, $show, $status) {
-               list($jid, $resource) = explode("/", $presence);
+               $presence = explode('/', $presence, 2);
+               $jid = $presence[0];
+               $resource = isset($presence[1]) ? $presence[1] : '';
                if ($show != 'unavailable') {
                        if (!$this->isContact($jid)) {
                                $this->addContact($jid, 'not-in-roster');
                        }
-                       $resource = $resource ? $resource : '';
                        $this->roster_array[$jid]['presence'][$resource] = array('priority' => $priority, 'show' => $show, 'status' => $status);
                } else { //Nuke unavailable resources to save memory
                        unset($this->roster_array[$jid]['resource'][$resource]);
+                       unset($this->roster_array[$jid]['presence'][$resource]);
                }
        }
 
@@ -137,7 +142,7 @@ class Roster {
         * @param string $jid
         */
        public function getPresence($jid) {
-               $split = explode("/", $jid);
+               $split = explode('/', $jid, 2);
                $jid = $split[0];
                if($this->isContact($jid)) {
                        $current = array('resource' => '', 'active' => '', 'priority' => -129, 'show' => '', 'status' => ''); //Priorities can only be -128 = 127
index 0d3e219..0bb314d 100644 (file)
@@ -1,43 +1,47 @@
-<?php 
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
+<?php
+
+namespace BirknerAlex\XMPPHP;
+
+       /**
+        * XMPPHP: The PHP XMPP Library
+        * Copyright (C) 2008  Nathanael C. Fritz
+        * This file is part of SleekXMPP.
+        *
+        * XMPPHP is free software; you can redistribute it and/or modify
+        * it under the terms of the GNU General Public License as published by
+        * the Free Software Foundation; either version 2 of the License, or
+        * (at your option) any later version.
+        *
+        * XMPPHP 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 General Public License for more details.
+        *
+        * You should have received a copy of the GNU General Public License
+        * along with XMPPHP; if not, write to the Free Software
+        * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+        *
+        * @category   xmpphp
+        * @package    XMPPHP
+        * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+        * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+        * @author     Michael Garvin <JID: gar@netflint.net>
+        * @author     Alexander Birkner (https://github.com/BirknerAlex)
+        * @copyright  2008 Nathanael C. Fritz
+        */
 
 /**
- * XMPPHP XML Object
- * 
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * XMPPHP Main Class
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-class XMPPHP_XMLObj {
+class XMLObj {
        /**
         * Tag name
         *
index 41c7a78..badc805 100644 (file)
@@ -1,52 +1,47 @@
 <?php
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
 
-/** XMPPHP_Exception */
-require_once dirname(__FILE__) . '/Exception.php';
+namespace BirknerAlex\XMPPHP;
 
-/** XMPPHP_XMLObj */
-require_once dirname(__FILE__) . '/XMLObj.php';
-
-/** XMPPHP_Log */
-require_once dirname(__FILE__) . '/Log.php';
+       /**
+        * XMPPHP: The PHP XMPP Library
+        * Copyright (C) 2008  Nathanael C. Fritz
+        * This file is part of SleekXMPP.
+        *
+        * XMPPHP is free software; you can redistribute it and/or modify
+        * it under the terms of the GNU General Public License as published by
+        * the Free Software Foundation; either version 2 of the License, or
+        * (at your option) any later version.
+        *
+        * XMPPHP 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 General Public License for more details.
+        *
+        * You should have received a copy of the GNU General Public License
+        * along with XMPPHP; if not, write to the Free Software
+        * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+        *
+        * @category   xmpphp
+        * @package    XMPPHP
+        * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+        * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+        * @author     Michael Garvin <JID: gar@netflint.net>
+        * @author     Alexander Birkner (https://github.com/BirknerAlex)
+        * @copyright  2008 Nathanael C. Fritz
+        */
 
 /**
- * XMPPHP XML Stream
- * 
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ * XMPPHP Main Class
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-class XMPPHP_XMLStream {
+class XMLStream {
        /**
         * @var resource
         */
@@ -82,7 +77,7 @@ class XMPPHP_XMLStream {
        /**
         * @var boolean
         */
-       protected $disconnected = false;
+       protected $disconnected = true;
        /**
         * @var boolean
         */
@@ -124,13 +119,13 @@ class XMPPHP_XMLStream {
         */
        protected $default_ns;
        /**
-        * @var string
+        * @var string[]
         */
-       protected $until = [];
+       protected $until = array();
        /**
-        * @var string
+        * @var int[]
         */
-       protected $until_count = [];
+       protected $until_count = array();
        /**
         * @var array
         */
@@ -140,7 +135,7 @@ class XMPPHP_XMLStream {
         */
        protected $until_payload = array();
        /**
-        * @var XMPPHP_Log
+        * @var Log
         */
        protected $log;
        /**
@@ -183,7 +178,7 @@ class XMPPHP_XMLStream {
                $this->host = $host;
                $this->port = $port;
                $this->setupParser();
-               $this->log = new XMPPHP_Log($printlog, $loglevel);
+               $this->log = new Log($printlog, $loglevel);
        }
 
        /**
@@ -199,7 +194,7 @@ class XMPPHP_XMLStream {
        /**
         * Return the log instance
         *
-        * @return XMPPHP_Log
+        * @return Log
         */
        public function getLog() {
                return $this->log;
@@ -263,7 +258,7 @@ class XMPPHP_XMLStream {
                        $ns_tags = array($xpath);
                }
                foreach($ns_tags as $ns_tag) {
-                       list($l, $r) = explode("}", $ns_tag);
+                       list($l, $r) = explode('}', $ns_tag);
                        if ($r != null) {
                                $xpart = array(substr($l, 1), $r);
                        } else {
@@ -288,9 +283,12 @@ class XMPPHP_XMLStream {
        /**
         * Connect to XMPP Host
         *
-        * @param integer $timeout
+        * @param integer $timeout    Timeout in seconds
         * @param boolean $persistent
-        * @param boolean $sendinit
+        * @param boolean $sendinit   Send XMPP starting sequence after connect
+        *                            automatically
+        *
+        * @throws Exception When the connection fails
         */
        public function connect($timeout = 30, $persistent = false, $sendinit = true) {
                $this->sent_disconnect = false;
@@ -310,10 +308,10 @@ class XMPPHP_XMLStream {
                        try {
                                $this->socket = @stream_socket_client("$conntype://{$this->host}:{$this->port}", $errno, $errstr, $timeout, $conflag);
                        } catch (Exception $e) {
-                               throw new XMPPHP_Exception($e->getMessage());
+                               throw new Exception($e->getMessage());
                        }
                        if(!$this->socket) {
-                               $this->log->log("Could not connect.",  XMPPHP_Log::LEVEL_ERROR);
+                               $this->log->log("Could not connect.",  Log::LEVEL_ERROR);
                                $this->disconnected = true;
                                # Take it easy for a few seconds
                                sleep(min($timeout, 5));
@@ -324,16 +322,20 @@ class XMPPHP_XMLStream {
                        stream_set_blocking($this->socket, 1);
                        if($sendinit) $this->send($this->stream_start);
                } else {
-                       throw new XMPPHP_Exception("Could not connect before timeout.");
+                       throw new Exception("Could not connect before timeout.");
                }
        }
 
        /**
         * Reconnect XMPP Host
+        *
+        * @throws Exception When the connection fails
+        * @uses   $reconnectTimeout
+        * @see    setReconnectTimeout()
         */
        public function doReconnect() {
                if(!$this->is_server) {
-                       $this->log->log("Reconnecting ($this->reconnectTimeout)...",  XMPPHP_Log::LEVEL_WARNING);
+                       $this->log->log("Reconnecting ($this->reconnectTimeout)...",  Log::LEVEL_WARNING);
                        $this->connect($this->reconnectTimeout, false, false);
                        $this->reset();
                        $this->event('reconnect');
@@ -348,7 +350,7 @@ class XMPPHP_XMLStream {
         * Disconnect from XMPP Host
         */
        public function disconnect() {
-               $this->log->log("Disconnecting...",  XMPPHP_Log::LEVEL_VERBOSE);
+               $this->log->log("Disconnecting...",  Log::LEVEL_VERBOSE);
                if(false == (bool) $this->socket) {
                        return;
                }
@@ -368,15 +370,58 @@ class XMPPHP_XMLStream {
                return $this->disconnected;
        }
 
+       /**
+        * Checks if the given string is closed with the same tag as it is
+        * opened. We try to be as fast as possible here.
+        *
+        * @param string $buff Read buffer of __process()
+        *
+        * @return boolean true if the buffer seems to be complete
+        */
+       protected function bufferComplete($buff)
+       {
+               if (substr($buff, -1) != '>') {
+                       return false;
+               }
+               //we always have a space since the namespace needs to be
+               //declared. could be a tab, though
+               $start = substr(
+                       $buff, 1,
+                       min(strpos($buff, '>', 2), strpos($buff, ' ', 2)) - 1
+               );
+               $stop  = substr($buff, -strlen($start) - 3);
+
+               if ($start == '?xml') {
+                       //starting with an xml tag. this means a stream is being
+                       // opened, which is not much of data, so no fear it's
+                       // not complete
+                       return true;
+               }
+               if (substr($stop, -2) == '/>') {
+                       //one tag, i.e. <success />
+                       return true;
+               }
+               if ('</' . $start . '>' == $stop) {
+                       return true;
+               }
+
+               return false;
+       }
+
        /**
         * Core reading tool
-        * 0 -> only read if data is immediately ready
-        * NULL -> wait forever and ever
-        * integer -> process for this amount of time 
+        *
+        * @param mixed   $maximum Limit when to return
+        *                         - 0: only read if data is immediately ready
+        *                         - NULL: wait forever and ever
+        *                         - integer: process for this amount of microseconds
+        * @param boolean $return_when_received Immediately return when data have been
+        *                                      received
+        *
+        * @return boolean True when all goes well, false when something fails
         */
-       
-       private function __process($maximum=5) {
-               
+       private function __process($maximum = 5, $return_when_received = false)
+       {
                $remaining = $maximum;
                
                do {
@@ -396,7 +441,7 @@ class XMPPHP_XMLStream {
                        }
                        $updated = @stream_select($read, $write, $except, $secs, $usecs);
                        if ($updated === false) {
-                               $this->log->log("Error on stream_select()",  XMPPHP_Log::LEVEL_VERBOSE);                                
+                               $this->log->log("Error on stream_select()",  Log::LEVEL_VERBOSE);
                                if ($this->reconnect) {
                                        $this->doReconnect();
                                } else {
@@ -405,19 +450,34 @@ class XMPPHP_XMLStream {
                                        return false;
                                }
                        } else if ($updated > 0) {
-                               # XXX: Is this big enough?
-                               $buff = @fread($this->socket, 4096);
-                               if(!$buff) { 
-                                       if($this->reconnect) {
-                                               $this->doReconnect();
-                                       } else {
-                                               fclose($this->socket);
-                                               $this->socket = NULL;
-                                               return false;
+                               $buff = '';
+                               do {
+                                       if ($buff != '') {
+                                               //disable blocking for now because fread() will
+                                               // block until the 4k are full if we already
+                                               // read a part of the packet
+                                               stream_set_blocking($this->socket, 0);
                                        }
-                               }
-                               $this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
+                                       $part = fread($this->socket, 4096);
+                                       stream_set_blocking($this->socket, 1);
+
+                                       if (!$part && feof($this->socket)) {
+                                               if($this->reconnect) {
+                                                       $this->doReconnect();
+                                               } else {
+                                                       fclose($this->socket);
+                                                       $this->socket = NULL;
+                                                       return false;
+                                               }
+                                       }
+                                       $this->log->log("RECV: $part",  Log::LEVEL_VERBOSE);
+                                       $buff .= $part;
+                               } while (!$this->bufferComplete($buff));
+
                                xml_parse($this->parser, $buff, false);
+                               if ($return_when_received) {
+                                       return true;
+                               }
                        } else {
                                # $updated == 0 means no changes during timeout.
                        }
@@ -440,8 +500,11 @@ class XMPPHP_XMLStream {
        /**
         * Process until a timeout occurs
         *
-        * @param integer $timeout
+        * @param integer $timeout Time in seconds
+        *
         * @return string
+        *
+        * @see __process()
         */
        public function processTime($timeout=NULL) {
                if (is_null($timeout)) {
@@ -454,23 +517,43 @@ class XMPPHP_XMLStream {
        /**
         * Process until a specified event or a timeout occurs
         *
-        * @param string|array $event
-        * @param integer $timeout
-        * @return string
+        * @param string|array $event   Event name or array of event names
+        * @param integer      $timeout Timeout in seconds
+        *
+        * @return array Payload
         */
-       public function processUntil($event, $timeout=-1) {
+       public function processUntil($event, $timeout = -1)
+       {
+               if ($this->disconnected) {
+                       throw new Exception('You need to connect first');
+               }
+
                $start = time();
-               if(!is_array($event)) $event = array($event);
+               if (!is_array($event)) {
+                       $event = array($event);
+               }
+
                $this->until[] = $event;
                end($this->until);
                $event_key = key($this->until);
                reset($this->until);
+
                $this->until_count[$event_key] = 0;
                $updated = '';
-               while(!$this->disconnected and $this->until_count[$event_key] < 1 and (time() - $start < $timeout or $timeout == -1)) {
-                       $this->__process();
+               while (!$this->disconnected
+                       && $this->until_count[$event_key] < 1
+                       && ($timeout == -1 || time() - $start < $timeout)
+               ) {
+                       $maximum = $timeout == -1
+                               ? NULL
+                               : ($timeout - (time() - $start)) * 1000000;
+                       $ret = $this->__process($maximum, true);
+                       if (!$ret) {
+                               break;
+                       }
                }
-               if(array_key_exists($event_key, $this->until_payload)) {
+
+               if (array_key_exists($event_key, $this->until_payload)) {
                        $payload = $this->until_payload[$event_key];
                        unset($this->until_payload[$event_key]);
                        unset($this->until_count[$event_key]);
@@ -478,6 +561,7 @@ class XMPPHP_XMLStream {
                } else {
                        $payload = array();
                }
+
                return $payload;
        }
 
@@ -522,7 +606,7 @@ class XMPPHP_XMLStream {
                        $ns = $this->ns_map[$name[0]];
                        $name = $name[1];
                }
-               $obj = new XMPPHP_XMLObj($name, $ns, $attr);
+               $obj = new XMLObj($name, $ns, $attr);
                if($this->xml_depth > 1) {
                        $this->xmlobj[$this->xml_depth - 1]->subs[] = $obj;
                }
@@ -538,7 +622,7 @@ class XMPPHP_XMLStream {
         * @param string   $name
         */
        public function endXML($parser, $name) {
-               #$this->log->log("Ending $name",  XMPPHP_Log::LEVEL_DEBUG);
+               #$this->log->log("Ending $name",  Log::LEVEL_DEBUG);
                #print "$name\n";
                if($this->been_reset) {
                        $this->been_reset = false;
@@ -563,7 +647,7 @@ class XMPPHP_XMLStream {
                                                }
                                                if ($searchxml !== null) {
                                                        if($handler[2] === null) $handler[2] = $this;
-                                                       $this->log->log("Calling {$handler[1]}",  XMPPHP_Log::LEVEL_DEBUG);
+                                                       $this->log->log("Calling {$handler[1]}",  Log::LEVEL_DEBUG);
                                                        $handler[2]->{$handler[1]}($this->xmlobj[2]);
                                                }
                                        }
@@ -577,7 +661,7 @@ class XMPPHP_XMLStream {
                                }
                                if($searchxml !== null and $searchxml->name == $handler[0] and ($searchxml->ns == $handler[1] or (!$handler[1] and $searchxml->ns == $this->default_ns))) {
                                        if($handler[3] === null) $handler[3] = $this;
-                                       $this->log->log("Calling {$handler[2]}",  XMPPHP_Log::LEVEL_DEBUG);
+                                       $this->log->log("Calling {$handler[2]}",  Log::LEVEL_DEBUG);
                                        $handler[3]->{$handler[2]}($this->xmlobj[2]);
                                }
                        }
@@ -592,7 +676,7 @@ class XMPPHP_XMLStream {
                        }
                        if(is_array($this->xmlobj)) {
                                $this->xmlobj = array_slice($this->xmlobj, 0, 1);
-                               if(isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMPPHP_XMLObj) {
+                               if(isset($this->xmlobj[0]) && $this->xmlobj[0] instanceof XMLObj) {
                                        $this->xmlobj[0]->subs = null;
                                }
                        }
@@ -634,7 +718,7 @@ class XMPPHP_XMLStream {
         * @param string $payload
         */
        public function event($name, $payload = null) {
-               $this->log->log("EVENT: $name",  XMPPHP_Log::LEVEL_DEBUG);
+               $this->log->log("EVENT: $name",  Log::LEVEL_DEBUG);
                foreach($this->eventhandlers as $handler) {
                        if($name == $handler[0]) {
                                if($handler[2] === null) {
@@ -643,6 +727,7 @@ class XMPPHP_XMLStream {
                                $handler[2]->{$handler[1]}($payload);
                        }
                }
+
                foreach($this->until as $key => $until) {
                        if(is_array($until)) {
                                if(in_array($name, $until)) {
@@ -670,7 +755,7 @@ class XMPPHP_XMLStream {
                                return false;
                        }
                }
-               $this->log->log("RECV: $buff",  XMPPHP_Log::LEVEL_VERBOSE);
+               $this->log->log("RECV: $buff",  Log::LEVEL_VERBOSE);
                xml_parse($this->parser, $buff, false);
        }
 
@@ -705,20 +790,20 @@ class XMPPHP_XMLStream {
                        # TODO: retry send here
                        return false;
                } elseif ($select > 0) {
-                       $this->log->log("Socket is ready; send it.", XMPPHP_Log::LEVEL_VERBOSE);
+                       $this->log->log("Socket is ready; send it.", Log::LEVEL_VERBOSE);
                } else {
-                       $this->log->log("Socket is not ready; break.", XMPPHP_Log::LEVEL_ERROR);
+                       $this->log->log("Socket is not ready; break.", Log::LEVEL_ERROR);
                        return false;
                }
                
                $sentbytes = @fwrite($this->socket, $msg);
-               $this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), XMPPHP_Log::LEVEL_VERBOSE);
+               $this->log->log("SENT: " . mb_substr($msg, 0, $sentbytes, '8bit'), Log::LEVEL_VERBOSE);
                if($sentbytes === FALSE) {
-                       $this->log->log("ERROR sending message; reconnecting.", XMPPHP_Log::LEVEL_ERROR);
+                       $this->log->log("ERROR sending message; reconnecting.", Log::LEVEL_ERROR);
                        $this->doReconnect();
                        return false;
                }
-               $this->log->log("Successfully sent $sentbytes bytes.", XMPPHP_Log::LEVEL_VERBOSE);
+               $this->log->log("Successfully sent $sentbytes bytes.", Log::LEVEL_VERBOSE);
                return $sentbytes;
        }
 
index c0f8963..9714529 100644 (file)
@@ -1,47 +1,47 @@
 <?php
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
 
-/** XMPPHP_XMLStream */
-require_once dirname(__FILE__) . "/XMLStream.php";
-require_once dirname(__FILE__) . "/Roster.php";
+namespace BirknerAlex\XMPPHP;
+
+       /**
+        * XMPPHP: The PHP XMPP Library
+        * Copyright (C) 2008  Nathanael C. Fritz
+        * This file is part of SleekXMPP.
+        *
+        * XMPPHP is free software; you can redistribute it and/or modify
+        * it under the terms of the GNU General Public License as published by
+        * the Free Software Foundation; either version 2 of the License, or
+        * (at your option) any later version.
+        *
+        * XMPPHP 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 General Public License for more details.
+        *
+        * You should have received a copy of the GNU General Public License
+        * along with XMPPHP; if not, write to the Free Software
+        * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+        *
+        * @category   xmpphp
+        * @package    XMPPHP
+        * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+        * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+        * @author     Michael Garvin <JID: gar@netflint.net>
+        * @author     Alexander Birkner (https://github.com/BirknerAlex)
+        * @copyright  2008 Nathanael C. Fritz
+        */
 
 /**
  * XMPPHP Main Class
- * 
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
+ *
+ * @category   xmpphp
+ * @package    XMPPHP
+ * @author     Nathanael C. Fritz <JID: fritzy@netflint.net>
+ * @author     Stephan Wentz <JID: stephan@jabber.wentz.it>
+ * @author     Michael Garvin <JID: gar@netflint.net>
  * @copyright  2008 Nathanael C. Fritz
- * @version    $Id$
+ * @version    $Id$
  */
-class XMPPHP_XMPP extends XMPPHP_XMLStream {
+class XMPP extends XMLStream {
        /**
         * @var string
         */
@@ -117,6 +117,7 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
                $this->password = $password;
                $this->resource = $resource;
                if(!$server) $server = $host;
+               $this->server = $server;
                $this->basejid = $this->user . '@' . $this->host;
 
                $this->roster = new Roster();
@@ -162,11 +163,14 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
         * @param string $subject
         */
        public function message($to, $body, $type = 'chat', $subject = null, $payload = null) {
-           if(is_null($type))
-           {
+               if ($this->disconnected) {
+                       throw new Exception('You need to connect first');
+               }
+
+           if(empty($type)) {
                $type = 'chat';
            }
-           
+
                $to       = htmlspecialchars($to);
                $body   = htmlspecialchars($body);
                $subject = htmlspecialchars($subject);
@@ -187,7 +191,11 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
         * @param string $show
         * @param string $to
         */
-       public function presence($status = null, $show = 'available', $to = null, $type='available', $priority=0) {
+       public function presence($status = null, $show = 'available', $to = null, $type='available', $priority=null) {
+               if ($this->disconnected) {
+                       throw new Exception('You need to connect first');
+               }
+
                if($type == 'available') $type = '';
                $to      = htmlspecialchars($to);
                $status = htmlspecialchars($status);
@@ -196,13 +204,13 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
                $out = "<presence";
                if($to) $out .= " to=\"$to\"";
                if($type) $out .= " type='$type'";
-               if($show == 'available' and !$status) {
+               if($show == 'available' and !$status and $priority !== null) {
                        $out .= "/>";
                } else {
                        $out .= ">";
                        if($show != 'available') $out .= "<show>$show</show>";
                        if($status) $out .= "<status>$status</status>";
-                       if($priority) $out .= "<priority>$priority</priority>";
+                       if($priority !== null) $out .= "<priority>$priority</priority>";
                        $out .= "</presence>";
                }
                
@@ -229,10 +237,11 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
                } else {
                        $payload['type'] = 'chat';
                }
+               $body = $xml->sub('body');
                $payload['from'] = $xml->attrs['from'];
-               $payload['body'] = $xml->sub('body')->data;
+               $payload['body'] = is_object($body) ? $body->data : FALSE; // $xml->sub('body')->data;
                $payload['xml'] = $xml;
-               $this->log->log("Message: {$xml->sub('body')->data}", XMPPHP_Log::LEVEL_DEBUG);
+               $this->log->log("Message: {$payload['body']}", Log::LEVEL_DEBUG);
                $this->event('message', $payload);
        }
 
@@ -251,7 +260,7 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
                if($this->track_presence) {
                        $this->roster->setPresence($payload['from'], $payload['priority'], $payload['show'], $payload['status']);
                }
-               $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}",  XMPPHP_Log::LEVEL_DEBUG);
+               $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}",  Log::LEVEL_DEBUG);
                if(array_key_exists('type', $xml->attrs) and $xml->attrs['type'] == 'subscribe') {
                        if($this->auto_subscribe) {
                                $this->send("<presence type='subscribed' to='{$xml->attrs['from']}' from='{$this->fulljid}' />");
@@ -304,10 +313,10 @@ class XMPPHP_XMPP extends XMPPHP_XMLStream {
         * @param string $xml
         */
        protected function sasl_failure_handler($xml) {
-               $this->log->log("Auth failed!",  XMPPHP_Log::LEVEL_ERROR);
+               $this->log->log("Auth failed!",  Log::LEVEL_ERROR);
                $this->disconnect();
                
-               throw new XMPPHP_Exception('Auth failed!');
+               throw new Exception('Auth failed!');
        }
 
        /**
diff --git a/lib/jabber/XMPP/XMPP_Old.php b/lib/jabber/XMPP/XMPP_Old.php
deleted file mode 100644 (file)
index 43f56b1..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-/**
- * XMPPHP: The PHP XMPP Library
- * Copyright (C) 2008  Nathanael C. Fritz
- * This file is part of SleekXMPP.
- * 
- * XMPPHP is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * XMPPHP 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 General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with XMPPHP; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- * @category   xmpphp 
- * @package    XMPPHP
- * @author      Nathanael C. Fritz <JID: fritzy@netflint.net>
- * @author      Stephan Wentz <JID: stephan@jabber.wentz.it>
- * @author      Michael Garvin <JID: gar@netflint.net>
- * @copyright  2008 Nathanael C. Fritz
- */
-
-/** XMPPHP_XMPP 
- *
- * This file is unnecessary unless you need to connect to older, non-XMPP-compliant servers like Dreamhost's.
- * In this case, use instead of XMPPHP_XMPP, otherwise feel free to delete it.
- * The old Jabber protocol wasn't standardized, so use at your own risk.
- *
- */
-require_once "XMPP.php";
-
-       class XMPPHP_XMPPOld extends XMPPHP_XMPP {
-               /**
-                *
-                * @var string
-                */
-               protected $session_id;
-
-               public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null) {
-                       parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
-                       if(!$server) $server = $host;
-                       $this->stream_start = '<stream:stream to="' . $server . '" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">';
-                       $this->fulljid = "{$user}@{$server}/{$resource}";
-               }
-       
-               /**
-                * Override XMLStream's startXML
-                *
-                * @param parser $parser
-                * @param string $name
-                * @param array $attr
-                */
-               public function startXML($parser, $name, $attr) {
-                       if($this->xml_depth == 0) {
-                               $this->session_id = $attr['ID'];
-                               $this->authenticate();
-                       }
-                       parent::startXML($parser, $name, $attr);
-               }
-
-               /**
-                * Send Authenticate Info Request
-                *
-                */
-               public function authenticate() {
-                       $id = $this->getId();
-                       $this->addidhandler($id, 'authfieldshandler');
-                       $this->send("<iq type='get' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username></query></iq>");
-               }
-
-               /**
-                * Retrieve auth fields and send auth attempt
-                *
-                * @param XMLObj $xml
-                */
-               public function authFieldsHandler($xml) {
-                       $id = $this->getId();
-                       $this->addidhandler($id, 'oldAuthResultHandler');
-                       if($xml->sub('query')->hasSub('digest')) {
-                               $hash = sha1($this->session_id . $this->password);
-                               print "{$this->session_id} {$this->password}\n";
-                               $out = "<iq type='set' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username><digest>{$hash}</digest><resource>{$this->resource}</resource></query></iq>";
-                       } else {
-                               $out = "<iq type='set' id='$id'><query xmlns='jabber:iq:auth'><username>{$this->user}</username><password>{$this->password}</password><resource>{$this->resource}</resource></query></iq>";
-                       }
-                       $this->send($out);
-
-               }
-               
-               /**
-                * Determine authenticated or failure
-                *
-                * @param XMLObj $xml
-                */
-               public function oldAuthResultHandler($xml) {
-                       if($xml->attrs['type'] != 'result') {
-                               $this->log->log("Auth failed!",  XMPPHP_Log::LEVEL_ERROR);
-                               $this->disconnect();
-                               throw new XMPPHP_Exception('Auth failed!');
-                       } else {
-                               $this->log->log("Session started");
-                               $this->event('session_start');
-                       }
-               }
-       }
-
-
-?>
index 38e0bbd..fd91dc6 100644 (file)
@@ -1,5 +1,6 @@
-Description of XMPPHP (aka jabber) version 0.1rc2-r77 library import into Moodle
+Description of XMPPHP (aka jabber) version 2.2 library import into Moodle
 
-MDL-20876 - replaced deprecated split() with explode()
-MDL-52335 - PHP7 variable syntax changes
-MDL-64223 - PHP7.1 compatibility changes
+To upgrade:
+1. Go to https://github.com/BirknerAlex/XMPPHP/releases
+2. Download the latest archive file for the release.
+3. Extract the contents of src/BirknerAlex/XMPPHP to lib/jabber/XMPP
\ No newline at end of file
index 2f2733e..356cc82 100644 (file)
@@ -53,7 +53,7 @@
     <location>jabber</location>
     <name>XMPPHP</name>
     <license>GPL</license>
-    <version>0.1rc2-r77</version>
+    <version>2.2</version>
     <licenseversion>2.0+</licenseversion>
   </library>
   <library>
index c5fe41f..c5b16dd 100644 (file)
@@ -52,6 +52,8 @@ information provided here is intended especially for developers.
   renamed to `is_listed()` and `get_not_listed()` respectively.
 * Method `mustache_helper_collection::strip_blacklisted_helpers()` has been deprecated and renamed to
   `strip_disallowed_helpers()`.
+* A new admin externalpage type `\core_admin\local\externalpage\accesscallback` for use in plugin settings is available that allows
+  a callback to be provided to determine whether page can be accessed.
 
 === 3.9 ===
 * Following function has been deprecated, please use \core\task\manager::run_from_cli().
index 7556cd7..f2c2a6b 100644 (file)
@@ -23,8 +23,6 @@
  */
 
 require_once($CFG->dirroot.'/message/output/lib.php');
-require_once($CFG->libdir.'/jabber/XMPP/XMPP.php');
-
 /**
  * The jabber message processor
  *
@@ -76,7 +74,7 @@ class message_output_jabber extends message_output {
 
         $jabbermessage .= "\n(".get_string('noreply','message').')';
 
-        $conn = new XMPPHP_XMPP($CFG->jabberhost,$CFG->jabberport,$CFG->jabberusername,$CFG->jabberpassword,'moodle',$CFG->jabberserver);
+        $conn = new \BirknerAlex\XMPPHP\XMPP($CFG->jabberhost,$CFG->jabberport,$CFG->jabberusername,$CFG->jabberpassword,'moodle',$CFG->jabberserver);
 
         // No need to track the presence during the sending message process.
         $conn->track_presence = false;
@@ -88,7 +86,7 @@ class message_output_jabber extends message_output {
             $conn->presence();
             $conn->message($jabberaddress, $jabbermessage);
             $conn->disconnect();
-        } catch(XMPPHP_Exception $e) {
+        } catch(\BirknerAlex\XMPPHP\Exception $e) {
             debugging($e->getMessage());
             return false;
         }
index 397c3c5..403cafc 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2021052500.21;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2021052500.22;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 $release  = '4.0dev (Build: 20201009)'; // Human-friendly version name