[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mnet/ -> lib.php (source)

   1  <?php
   2  /**
   3   * Library functions for mnet
   4   *
   5   * @author  Donal McMullan  donal@catalyst.net.nz
   6   * @version 0.0.1
   7   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
   8   * @package mnet
   9   */
  10  require_once $CFG->dirroot.'/mnet/xmlrpc/xmlparser.php';
  11  require_once $CFG->dirroot.'/mnet/peer.php';
  12  require_once $CFG->dirroot.'/mnet/environment.php';
  13  
  14  /// CONSTANTS ///////////////////////////////////////////////////////////
  15  
  16  define('RPC_OK',                0);
  17  define('RPC_NOSUCHFILE',        1);
  18  define('RPC_NOSUCHCLASS',       2);
  19  define('RPC_NOSUCHFUNCTION',    3);
  20  define('RPC_FORBIDDENFUNCTION', 4);
  21  define('RPC_NOSUCHMETHOD',      5);
  22  define('RPC_FORBIDDENMETHOD',   6);
  23  
  24  /**
  25   * Strip extraneous detail from a URL or URI and return the hostname
  26   *
  27   * @param  string  $uri  The URI of a file on the remote computer, optionally
  28   *                       including its http:// prefix like
  29   *                       http://www.example.com/index.html
  30   * @return string        Just the hostname
  31   */
  32  function mnet_get_hostname_from_uri($uri = null) {
  33      $count = preg_match("@^(?:http[s]?://)?([A-Z0-9\-\.]+).*@i", $uri, $matches);
  34      if ($count > 0) return $matches[1];
  35      return false;
  36  }
  37  
  38  /**
  39   * Get the remote machine's SSL Cert
  40   *
  41   * @param  string  $uri     The URI of a file on the remote computer, including
  42   *                          its http:// or https:// prefix
  43   * @return string           A PEM formatted SSL Certificate.
  44   */
  45  function mnet_get_public_key($uri, $application=null) {
  46      global $CFG, $DB;
  47      $mnet = get_mnet_environment();
  48      // The key may be cached in the mnet_set_public_key function...
  49      // check this first
  50      $key = mnet_set_public_key($uri);
  51      if ($key != false) {
  52          return $key;
  53      }
  54  
  55      if (empty($application)) {
  56          $application = $DB->get_record('mnet_application', array('name'=>'moodle'));
  57      }
  58  
  59      $rq = xmlrpc_encode_request('system/keyswap', array($CFG->wwwroot, $mnet->public_key, $application->name), array("encoding" => "utf-8"));
  60      $ch = curl_init($uri . $application->xmlrpc_server_url);
  61  
  62      curl_setopt($ch, CURLOPT_TIMEOUT, 60);
  63      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  64      curl_setopt($ch, CURLOPT_POST, true);
  65      curl_setopt($ch, CURLOPT_USERAGENT, 'Moodle');
  66      curl_setopt($ch, CURLOPT_POSTFIELDS, $rq);
  67      curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml charset=UTF-8"));
  68      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  69      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  70  
  71      // check for proxy
  72      if (!empty($CFG->proxyhost) and !is_proxybypass($uri)) {
  73          // SOCKS supported in PHP5 only
  74          if (!empty($CFG->proxytype) and ($CFG->proxytype == 'SOCKS5')) {
  75              if (defined('CURLPROXY_SOCKS5')) {
  76                  curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  77              } else {
  78                  curl_close($ch);
  79                  print_error( 'socksnotsupported','mnet' );
  80              }
  81          }
  82  
  83          curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
  84  
  85          if (empty($CFG->proxyport)) {
  86              curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost);
  87          } else {
  88              curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost.':'.$CFG->proxyport);
  89          }
  90  
  91          if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
  92              curl_setopt($ch, CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
  93              if (defined('CURLOPT_PROXYAUTH')) {
  94                  // any proxy authentication if PHP 5.1
  95                  curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
  96              }
  97          }
  98      }
  99  
 100      $res = xmlrpc_decode(curl_exec($ch));
 101  
 102      // check for curl errors
 103      $curlerrno = curl_errno($ch);
 104      if ($curlerrno!=0) {
 105          debugging("Request for $uri failed with curl error $curlerrno");
 106      }
 107  
 108      // check HTTP error code
 109      $info =  curl_getinfo($ch);
 110      if (!empty($info['http_code']) and ($info['http_code'] != 200)) {
 111          debugging("Request for $uri failed with HTTP code ".$info['http_code']);
 112      }
 113  
 114      curl_close($ch);
 115  
 116      if (!is_array($res)) { // ! error
 117          $public_certificate = $res;
 118          $credentials=array();
 119          if (strlen(trim($public_certificate))) {
 120              $credentials = openssl_x509_parse($public_certificate);
 121              $host = $credentials['subject']['CN'];
 122              if (array_key_exists( 'subjectAltName', $credentials['subject'])) {
 123                  $host = $credentials['subject']['subjectAltName'];
 124              }
 125              if (strpos($uri, $host) !== false) {
 126                  mnet_set_public_key($uri, $public_certificate);
 127                  return $public_certificate;
 128              }
 129              else {
 130                  debugging("Request for $uri returned public key for different URI - $host");
 131              }
 132          }
 133          else {
 134              debugging("Request for $uri returned empty response");
 135          }
 136      }
 137      else {
 138          debugging( "Request for $uri returned unexpected result");
 139      }
 140      return false;
 141  }
 142  
 143  /**
 144   * Store a URI's public key in a static variable, or retrieve the key for a URI
 145   *
 146   * @param  string  $uri  The URI of a file on the remote computer, including its
 147   *                       https:// prefix
 148   * @param  mixed   $key  A public key to store in the array OR null. If the key
 149   *                       is null, the function will return the previously stored
 150   *                       key for the supplied URI, should it exist.
 151   * @return mixed         A public key OR true/false.
 152   */
 153  function mnet_set_public_key($uri, $key = null) {
 154      static $keyarray = array();
 155      if (isset($keyarray[$uri]) && empty($key)) {
 156          return $keyarray[$uri];
 157      } elseif (!empty($key)) {
 158          $keyarray[$uri] = $key;
 159          return true;
 160      }
 161      return false;
 162  }
 163  
 164  /**
 165   * Sign a message and return it in an XML-Signature document
 166   *
 167   * This function can sign any content, but it was written to provide a system of
 168   * signing XML-RPC request and response messages. The message will be base64
 169   * encoded, so it does not need to be text.
 170   *
 171   * We compute the SHA1 digest of the message.
 172   * We compute a signature on that digest with our private key.
 173   * We link to the public key that can be used to verify our signature.
 174   * We base64 the message data.
 175   * We identify our wwwroot - this must match our certificate's CN
 176   *
 177   * The XML-RPC document will be parceled inside an XML-SIG document, which holds
 178   * the base64_encoded XML as an object, the SHA1 digest of that document, and a
 179   * signature of that document using the local private key. This signature will
 180   * uniquely identify the RPC document as having come from this server.
 181   *
 182   * See the {@Link http://www.w3.org/TR/xmldsig-core/ XML-DSig spec} at the W3c
 183   * site
 184   *
 185   * @param  string   $message              The data you want to sign
 186   * @param  resource $privatekey           The private key to sign the response with
 187   * @return string                         An XML-DSig document
 188   */
 189  function mnet_sign_message($message, $privatekey = null) {
 190      global $CFG;
 191      $digest = sha1($message);
 192  
 193      $mnet = get_mnet_environment();
 194      // If the user hasn't supplied a private key (for example, one of our older,
 195      //  expired private keys, we get the current default private key and use that.
 196      if ($privatekey == null) {
 197          $privatekey = $mnet->get_private_key();
 198      }
 199  
 200      // The '$sig' value below is returned by reference.
 201      // We initialize it first to stop my IDE from complaining.
 202      $sig  = '';
 203      $bool = openssl_sign($message, $sig, $privatekey); // TODO: On failure?
 204  
 205      $message = '<?xml version="1.0" encoding="iso-8859-1"?>
 206      <signedMessage>
 207          <Signature Id="MoodleSignature" xmlns="http://www.w3.org/2000/09/xmldsig#">
 208              <SignedInfo>
 209                  <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
 210                  <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
 211                  <Reference URI="#XMLRPC-MSG">
 212                      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
 213                      <DigestValue>'.$digest.'</DigestValue>
 214                  </Reference>
 215              </SignedInfo>
 216              <SignatureValue>'.base64_encode($sig).'</SignatureValue>
 217              <KeyInfo>
 218                  <RetrievalMethod URI="'.$CFG->wwwroot.'/mnet/publickey.php"/>
 219              </KeyInfo>
 220          </Signature>
 221          <object ID="XMLRPC-MSG">'.base64_encode($message).'</object>
 222          <wwwroot>'.$mnet->wwwroot.'</wwwroot>
 223          <timestamp>'.time().'</timestamp>
 224      </signedMessage>';
 225      return $message;
 226  }
 227  
 228  /**
 229   * Encrypt a message and return it in an XML-Encrypted document
 230   *
 231   * This function can encrypt any content, but it was written to provide a system
 232   * of encrypting XML-RPC request and response messages. The message will be
 233   * base64 encoded, so it does not need to be text - binary data should work.
 234   *
 235   * We compute the SHA1 digest of the message.
 236   * We compute a signature on that digest with our private key.
 237   * We link to the public key that can be used to verify our signature.
 238   * We base64 the message data.
 239   * We identify our wwwroot - this must match our certificate's CN
 240   *
 241   * The XML-RPC document will be parceled inside an XML-SIG document, which holds
 242   * the base64_encoded XML as an object, the SHA1 digest of that document, and a
 243   * signature of that document using the local private key. This signature will
 244   * uniquely identify the RPC document as having come from this server.
 245   *
 246   * See the {@Link http://www.w3.org/TR/xmlenc-core/ XML-ENC spec} at the W3c
 247   * site
 248   *
 249   * @param  string   $message              The data you want to sign
 250   * @param  string   $remote_certificate   Peer's certificate in PEM format
 251   * @return string                         An XML-ENC document
 252   */
 253  function mnet_encrypt_message($message, $remote_certificate) {
 254      $mnet = get_mnet_environment();
 255  
 256      // Generate a key resource from the remote_certificate text string
 257      $publickey = openssl_get_publickey($remote_certificate);
 258  
 259      if ( gettype($publickey) != 'resource' ) {
 260          // Remote certificate is faulty.
 261          return false;
 262      }
 263  
 264      // Initialize vars
 265      $encryptedstring = '';
 266      $symmetric_keys = array();
 267  
 268      //        passed by ref ->     &$encryptedstring &$symmetric_keys
 269      $bool = openssl_seal($message, $encryptedstring, $symmetric_keys, array($publickey));
 270      $message = $encryptedstring;
 271      $symmetrickey = array_pop($symmetric_keys);
 272  
 273      $message = '<?xml version="1.0" encoding="iso-8859-1"?>
 274      <encryptedMessage>
 275          <EncryptedData Id="ED" xmlns="http://www.w3.org/2001/04/xmlenc#">
 276              <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#arcfour"/>
 277              <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
 278                  <ds:RetrievalMethod URI="#EK" Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"/>
 279                  <ds:KeyName>XMLENC</ds:KeyName>
 280              </ds:KeyInfo>
 281              <CipherData>
 282                  <CipherValue>'.base64_encode($message).'</CipherValue>
 283              </CipherData>
 284          </EncryptedData>
 285          <EncryptedKey Id="EK" xmlns="http://www.w3.org/2001/04/xmlenc#">
 286              <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
 287              <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
 288                  <ds:KeyName>SSLKEY</ds:KeyName>
 289              </ds:KeyInfo>
 290              <CipherData>
 291                  <CipherValue>'.base64_encode($symmetrickey).'</CipherValue>
 292              </CipherData>
 293              <ReferenceList>
 294                  <DataReference URI="#ED"/>
 295              </ReferenceList>
 296              <CarriedKeyName>XMLENC</CarriedKeyName>
 297          </EncryptedKey>
 298          <wwwroot>'.$mnet->wwwroot.'</wwwroot>
 299      </encryptedMessage>';
 300      return $message;
 301  }
 302  
 303  /**
 304   * Get your SSL keys from the database, or create them (if they don't exist yet)
 305   *
 306   * Get your SSL keys from the database, or (if they don't exist yet) call
 307   * mnet_generate_keypair to create them
 308   *
 309   * @param   string  $string     The text you want to sign
 310   * @return  string              The signature over that text
 311   */
 312  function mnet_get_keypair() {
 313      global $CFG, $DB;
 314      static $keypair = null;
 315      if (!is_null($keypair)) return $keypair;
 316      if ($result = get_config('mnet', 'openssl')) {
 317          list($keypair['certificate'], $keypair['keypair_PEM']) = explode('@@@@@@@@', $result);
 318          $keypair['privatekey'] = openssl_pkey_get_private($keypair['keypair_PEM']);
 319          $keypair['publickey']  = openssl_pkey_get_public($keypair['certificate']);
 320          return $keypair;
 321      } else {
 322          $keypair = mnet_generate_keypair();
 323          return $keypair;
 324      }
 325  }
 326  
 327  /**
 328   * Generate public/private keys and store in the config table
 329   *
 330   * Use the distinguished name provided to create a CSR, and then sign that CSR
 331   * with the same credentials. Store the keypair you create in the config table.
 332   * If a distinguished name is not provided, create one using the fullname of
 333   * 'the course with ID 1' as your organization name, and your hostname (as
 334   * detailed in $CFG->wwwroot).
 335   *
 336   * @param   array  $dn  The distinguished name of the server
 337   * @return  string      The signature over that text
 338   */
 339  function mnet_generate_keypair($dn = null, $days=28) {
 340      global $CFG, $USER, $DB;
 341  
 342      // check if lifetime has been overriden
 343      if (!empty($CFG->mnetkeylifetime)) {
 344          $days = $CFG->mnetkeylifetime;
 345      }
 346  
 347      $host = strtolower($CFG->wwwroot);
 348      $host = preg_replace("~^http(s)?://~",'',$host);
 349      $break = strpos($host.'/' , '/');
 350      $host   = substr($host, 0, $break);
 351  
 352      $site = get_site();
 353      $organization = $site->fullname;
 354  
 355      $keypair = array();
 356  
 357      $country  = 'NZ';
 358      $province = 'Wellington';
 359      $locality = 'Wellington';
 360      $email    = !empty($CFG->noreplyaddress) ? $CFG->noreplyaddress : 'noreply@'.$_SERVER['HTTP_HOST'];
 361  
 362      if(!empty($USER->country)) {
 363          $country  = $USER->country;
 364      }
 365      if(!empty($USER->city)) {
 366          $province = $USER->city;
 367          $locality = $USER->city;
 368      }
 369      if(!empty($USER->email)) {
 370          $email    = $USER->email;
 371      }
 372  
 373      if (is_null($dn)) {
 374          $dn = array(
 375             "countryName" => $country,
 376             "stateOrProvinceName" => $province,
 377             "localityName" => $locality,
 378             "organizationName" => $organization,
 379             "organizationalUnitName" => 'Moodle',
 380             "commonName" => substr($CFG->wwwroot, 0, 64),
 381             "subjectAltName" => $CFG->wwwroot,
 382             "emailAddress" => $email
 383          );
 384      }
 385  
 386      $dnlimits = array(
 387             'countryName'            => 2,
 388             'stateOrProvinceName'    => 128,
 389             'localityName'           => 128,
 390             'organizationName'       => 64,
 391             'organizationalUnitName' => 64,
 392             'commonName'             => 64,
 393             'emailAddress'           => 128
 394      );
 395  
 396      foreach ($dnlimits as $key => $length) {
 397          $dn[$key] = core_text::substr($dn[$key], 0, $length);
 398      }
 399  
 400      // ensure we remove trailing slashes
 401      $dn["commonName"] = preg_replace(':/$:', '', $dn["commonName"]);
 402      if (!empty($CFG->opensslcnf)) { //allow specification of openssl.cnf especially for Windows installs
 403          $new_key = openssl_pkey_new(array("config" => $CFG->opensslcnf));
 404      } else {
 405          $new_key = openssl_pkey_new();
 406      }
 407      if ($new_key === false) {
 408          // can not generate keys - missing openssl.cnf??
 409          return null;
 410      }
 411      if (!empty($CFG->opensslcnf)) { //allow specification of openssl.cnf especially for Windows installs
 412          $csr_rsc = openssl_csr_new($dn, $new_key, array("config" => $CFG->opensslcnf));
 413          $selfSignedCert = openssl_csr_sign($csr_rsc, null, $new_key, $days, array("config" => $CFG->opensslcnf));
 414      } else {
 415          $csr_rsc = openssl_csr_new($dn, $new_key, array('private_key_bits',2048));
 416          $selfSignedCert = openssl_csr_sign($csr_rsc, null, $new_key, $days);
 417      }
 418      unset($csr_rsc); // Free up the resource
 419  
 420      // We export our self-signed certificate to a string.
 421      openssl_x509_export($selfSignedCert, $keypair['certificate']);
 422      openssl_x509_free($selfSignedCert);
 423  
 424      // Export your public/private key pair as a PEM encoded string. You
 425      // can protect it with an optional passphrase if you wish.
 426      if (!empty($CFG->opensslcnf)) { //allow specification of openssl.cnf especially for Windows installs
 427          $export = openssl_pkey_export($new_key, $keypair['keypair_PEM'], null, array("config" => $CFG->opensslcnf));
 428      } else {
 429          $export = openssl_pkey_export($new_key, $keypair['keypair_PEM'] /* , $passphrase */);
 430      }
 431      openssl_pkey_free($new_key);
 432      unset($new_key); // Free up the resource
 433  
 434      return $keypair;
 435  }
 436  
 437  
 438  function mnet_update_sso_access_control($username, $mnet_host_id, $accessctrl) {
 439      global $DB;
 440  
 441      $mnethost = $DB->get_record('mnet_host', array('id'=>$mnet_host_id));
 442      if ($aclrecord = $DB->get_record('mnet_sso_access_control', array('username'=>$username, 'mnet_host_id'=>$mnet_host_id))) {
 443          // Update.
 444          $aclrecord->accessctrl = $accessctrl;
 445          $DB->update_record('mnet_sso_access_control', $aclrecord);
 446  
 447          // Trigger access control updated event.
 448          $params = array(
 449              'objectid' => $aclrecord->id,
 450              'context' => context_system::instance(),
 451              'other' => array(
 452                  'username' => $username,
 453                  'hostname' => $mnethost->name,
 454                  'accessctrl' => $accessctrl
 455              )
 456          );
 457          $event = \core\event\mnet_access_control_updated::create($params);
 458          $event->add_record_snapshot('mnet_host', $mnethost);
 459          $event->trigger();
 460      } else {
 461          // Insert.
 462          $aclrecord = new stdClass();
 463          $aclrecord->username = $username;
 464          $aclrecord->accessctrl = $accessctrl;
 465          $aclrecord->mnet_host_id = $mnet_host_id;
 466          $aclrecord->id = $DB->insert_record('mnet_sso_access_control', $aclrecord);
 467  
 468          // Trigger access control created event.
 469          $params = array(
 470              'objectid' => $aclrecord->id,
 471              'context' => context_system::instance(),
 472              'other' => array(
 473                  'username' => $username,
 474                  'hostname' => $mnethost->name,
 475                  'accessctrl' => $accessctrl
 476              )
 477          );
 478          $event = \core\event\mnet_access_control_created::create($params);
 479          $event->add_record_snapshot('mnet_host', $mnethost);
 480          $event->trigger();
 481      }
 482      return true;
 483  }
 484  
 485  function mnet_get_peer_host ($mnethostid) {
 486      global $DB;
 487      static $hosts;
 488      if (!isset($hosts[$mnethostid])) {
 489          $host = $DB->get_record('mnet_host', array('id' => $mnethostid));
 490          $hosts[$mnethostid] = $host;
 491      }
 492      return $hosts[$mnethostid];
 493  }
 494  
 495  /**
 496   * Inline function to modify a url string so that mnet users are requested to
 497   * log in at their mnet identity provider (if they are not already logged in)
 498   * before ultimately being directed to the original url.
 499   *
 500   * @param string $jumpurl the url which user should initially be directed to.
 501   *     This is a URL associated with a moodle networking peer when it
 502   *     is fulfiling a role as an identity provider (IDP). Different urls for
 503   *     different peers, the jumpurl is formed partly from the IDP's webroot, and
 504   *     partly from a predefined local path within that webwroot.
 505   *     The result of the user hitting this jump url is that they will be asked
 506   *     to login (at their identity provider (if they aren't already)), mnet
 507   *     will prepare the necessary authentication information, then redirect
 508   *     them back to somewhere at the content provider(CP) moodle (this moodle)
 509   * @param array $url array with 2 elements
 510   *     0 - context the url was taken from, possibly just the url, possibly href="url"
 511   *     1 - the destination url
 512   * @return string the url the remote user should be supplied with.
 513   */
 514  function mnet_sso_apply_indirection ($jumpurl, $url) {
 515      global $USER, $CFG;
 516  
 517      $localpart='';
 518      $urlparts = parse_url($url[1]);
 519      if($urlparts) {
 520          if (isset($urlparts['path'])) {
 521              $path = $urlparts['path'];
 522              // if our wwwroot has a path component, need to strip that path from beginning of the
 523              // 'localpart' to make it relative to moodle's wwwroot
 524              $wwwrootpath = parse_url($CFG->wwwroot, PHP_URL_PATH);
 525              if (!empty($wwwrootpath) and strpos($path, $wwwrootpath) === 0) {
 526                  $path = substr($path, strlen($wwwrootpath));
 527              }
 528              $localpart .= $path;
 529          }
 530          if (isset($urlparts['query'])) {
 531              $localpart .= '?'.$urlparts['query'];
 532          }
 533          if (isset($urlparts['fragment'])) {
 534              $localpart .= '#'.$urlparts['fragment'];
 535          }
 536      }
 537      $indirecturl = $jumpurl . urlencode($localpart);
 538      //If we matched on more than just a url (ie an html link), return the url to an href format
 539      if ($url[0] != $url[1]) {
 540          $indirecturl = 'href="'.$indirecturl.'"';
 541      }
 542      return $indirecturl;
 543  }
 544  
 545  function mnet_get_app_jumppath ($applicationid) {
 546      global $DB;
 547      static $appjumppaths;
 548      if (!isset($appjumppaths[$applicationid])) {
 549          $ssojumpurl = $DB->get_field('mnet_application', 'sso_jump_url', array('id' => $applicationid));
 550          $appjumppaths[$applicationid] = $ssojumpurl;
 551      }
 552      return $appjumppaths[$applicationid];
 553  }
 554  
 555  
 556  /**
 557   * Output debug information about mnet.  this will go to the <b>error_log</b>.
 558   *
 559   * @param mixed $debugdata this can be a string, or array or object.
 560   * @param int   $debuglevel optional , defaults to 1. bump up for very noisy debug info
 561   */
 562  function mnet_debug($debugdata, $debuglevel=1) {
 563      global $CFG;
 564      $setlevel = get_config('', 'mnet_rpcdebug');
 565      if (empty($setlevel) || $setlevel < $debuglevel) {
 566          return;
 567      }
 568      if (is_object($debugdata)) {
 569          $debugdata = (array)$debugdata;
 570      }
 571      if (is_array($debugdata)) {
 572          mnet_debug('DUMPING ARRAY');
 573          foreach ($debugdata as $key => $value) {
 574              mnet_debug("$key: $value");
 575          }
 576          mnet_debug('END DUMPING ARRAY');
 577          return;
 578      }
 579      $prefix = 'MNET DEBUG ';
 580      if (defined('MNET_SERVER')) {
 581          $prefix .= " (server $CFG->wwwroot";
 582          if ($peer = get_mnet_remote_client() && !empty($peer->wwwroot)) {
 583              $prefix .= ", remote peer " . $peer->wwwroot;
 584          }
 585          $prefix .= ')';
 586      } else {
 587          $prefix .= " (client $CFG->wwwroot) ";
 588      }
 589      error_log("$prefix $debugdata");
 590  }
 591  
 592  /**
 593   * Return an array of information about all moodle's profile fields
 594   * which ones are optional, which ones are forced.
 595   * This is used as the basis of providing lists of profile fields to the administrator
 596   * to pick which fields to import/export over MNET
 597   *
 598   * @return array(forced => array, optional => array)
 599   */
 600  function mnet_profile_field_options() {
 601      global $DB;
 602      static $info;
 603      if (!empty($info)) {
 604          return $info;
 605      }
 606  
 607      $excludes = array(
 608          'id',              // makes no sense
 609          'mnethostid',      // makes no sense
 610          'timecreated',     // will be set to relative to the host anyway
 611          'timemodified',    // will be set to relative to the host anyway
 612          'auth',            // going to be set to 'mnet'
 613          'deleted',         // we should never get deleted users sent over, but don't send this anyway
 614          'confirmed',       // unconfirmed users can't log in to their home site, all remote users considered confirmed
 615          'password',        // no password for mnet users
 616          'theme',           // handled separately
 617          'lastip',          // will be set to relative to the host anyway
 618      );
 619  
 620      // these are the ones that user_not_fully_set_up will complain about
 621      // and also special case ones
 622      $forced = array(
 623          'username',
 624          'email',
 625          'firstname',
 626          'lastname',
 627          'auth',
 628          'wwwroot',
 629          'session.gc_lifetime',
 630          '_mnet_userpicture_timemodified',
 631          '_mnet_userpicture_mimetype',
 632      );
 633  
 634      // these are the ones we used to send/receive (pre 2.0)
 635      $legacy = array(
 636          'username',
 637          'email',
 638          'auth',
 639          'deleted',
 640          'firstname',
 641          'lastname',
 642          'city',
 643          'country',
 644          'lang',
 645          'timezone',
 646          'description',
 647          'mailformat',
 648          'maildigest',
 649          'maildisplay',
 650          'htmleditor',
 651          'wwwroot',
 652          'picture',
 653      );
 654  
 655      // get a random user record from the database to pull the fields off
 656      $randomuser = $DB->get_record('user', array(), '*', IGNORE_MULTIPLE);
 657      foreach ($randomuser as $key => $discard) {
 658          if (in_array($key, $excludes) || in_array($key, $forced)) {
 659              continue;
 660          }
 661          $fields[$key] = $key;
 662      }
 663      $info = array(
 664          'forced'   => $forced,
 665          'optional' => $fields,
 666          'legacy'   => $legacy,
 667      );
 668      return $info;
 669  }
 670  
 671  
 672  /**
 673   * Returns information about MNet peers
 674   *
 675   * @param bool $withdeleted should the deleted peers be returned too
 676   * @return array
 677   */
 678  function mnet_get_hosts($withdeleted = false) {
 679      global $CFG, $DB;
 680  
 681      $sql = "SELECT h.id, h.deleted, h.wwwroot, h.ip_address, h.name, h.public_key, h.public_key_expires,
 682                     h.transport, h.portno, h.last_connect_time, h.last_log_id, h.applicationid,
 683                     a.name as app_name, a.display_name as app_display_name, a.xmlrpc_server_url
 684                FROM {mnet_host} h
 685                JOIN {mnet_application} a ON h.applicationid = a.id
 686               WHERE h.id <> ?";
 687  
 688      if (!$withdeleted) {
 689          $sql .= "  AND h.deleted = 0";
 690      }
 691  
 692      $sql .= " ORDER BY h.deleted, h.name, h.id";
 693  
 694      return $DB->get_records_sql($sql, array($CFG->mnet_localhost_id));
 695  }
 696  
 697  
 698  /**
 699   * return an array information about services enabled for the given peer.
 700   * in two modes, fulldata or very basic data.
 701   *
 702   * @param mnet_peer $mnet_peer the peer to get information abut
 703   * @param boolean   $fulldata whether to just return which services are published/subscribed, or more information (defaults to full)
 704   *
 705   * @return array  If $fulldata is false, an array is returned like:
 706   *                publish => array(
 707   *                    serviceid => boolean,
 708   *                    serviceid => boolean,
 709   *                ),
 710   *                subscribe => array(
 711   *                    serviceid => boolean,
 712   *                    serviceid => boolean,
 713   *                )
 714   *                If $fulldata is true, an array is returned like:
 715   *                servicename => array(
 716   *                   apiversion => array(
 717   *                        name           => string
 718   *                        offer          => boolean
 719   *                        apiversion     => int
 720   *                        plugintype     => string
 721   *                        pluginname     => string
 722   *                        hostsubscribes => boolean
 723   *                        hostpublishes  => boolean
 724   *                   ),
 725   *               )
 726   */
 727  function mnet_get_service_info(mnet_peer $mnet_peer, $fulldata=true) {
 728      global $CFG, $DB;
 729  
 730      $requestkey = (!empty($fulldata) ? 'fulldata' : 'mydata');
 731  
 732      static $cache = array();
 733      if (array_key_exists($mnet_peer->id, $cache)) {
 734          return $cache[$mnet_peer->id][$requestkey];
 735      }
 736  
 737      $id_list = $mnet_peer->id;
 738      if (!empty($CFG->mnet_all_hosts_id)) {
 739          $id_list .= ', '.$CFG->mnet_all_hosts_id;
 740      }
 741  
 742      $concat = $DB->sql_concat('COALESCE(h2s.id,0) ', ' \'-\' ', ' svc.id', '\'-\'', 'r.plugintype', '\'-\'', 'r.pluginname');
 743  
 744      $query = "
 745          SELECT DISTINCT
 746              $concat as id,
 747              svc.id as serviceid,
 748              svc.name,
 749              svc.offer,
 750              svc.apiversion,
 751              r.plugintype,
 752              r.pluginname,
 753              h2s.hostid,
 754              h2s.publish,
 755              h2s.subscribe
 756          FROM
 757              {mnet_service2rpc} s2r,
 758              {mnet_rpc} r,
 759              {mnet_service} svc
 760          LEFT JOIN
 761              {mnet_host2service} h2s
 762          ON
 763              h2s.hostid in ($id_list) AND
 764              h2s.serviceid = svc.id
 765          WHERE
 766              svc.offer = '1' AND
 767              s2r.serviceid = svc.id AND
 768              s2r.rpcid = r.id
 769          ORDER BY
 770              svc.name ASC";
 771  
 772      $resultset = $DB->get_records_sql($query);
 773  
 774      if (is_array($resultset)) {
 775          $resultset = array_values($resultset);
 776      } else {
 777          $resultset = array();
 778      }
 779  
 780      require_once $CFG->dirroot.'/mnet/xmlrpc/client.php';
 781  
 782      $remoteservices = array();
 783      if ($mnet_peer->id != $CFG->mnet_all_hosts_id) {
 784          // Create a new request object
 785          $mnet_request = new mnet_xmlrpc_client();
 786  
 787          // Tell it the path to the method that we want to execute
 788          $mnet_request->set_method('system/listServices');
 789          $mnet_request->send($mnet_peer);
 790          if (is_array($mnet_request->response)) {
 791              foreach($mnet_request->response as $service) {
 792                  $remoteservices[$service['name']][$service['apiversion']] = $service;
 793              }
 794          }
 795      }
 796  
 797      $myservices = array();
 798      $mydata = array();
 799      foreach($resultset as $result) {
 800          $result->hostpublishes  = false;
 801          $result->hostsubscribes = false;
 802          if (isset($remoteservices[$result->name][$result->apiversion])) {
 803              if ($remoteservices[$result->name][$result->apiversion]['publish'] == 1) {
 804                  $result->hostpublishes  = true;
 805              }
 806              if ($remoteservices[$result->name][$result->apiversion]['subscribe'] == 1) {
 807                  $result->hostsubscribes  = true;
 808              }
 809          }
 810  
 811          if (empty($myservices[$result->name][$result->apiversion])) {
 812              $myservices[$result->name][$result->apiversion] = array('serviceid' => $result->serviceid,
 813                                                                      'name' => $result->name,
 814                                                                      'offer' => $result->offer,
 815                                                                      'apiversion' => $result->apiversion,
 816                                                                      'plugintype' => $result->plugintype,
 817                                                                      'pluginname' => $result->pluginname,
 818                                                                      'hostsubscribes' => $result->hostsubscribes,
 819                                                                      'hostpublishes' => $result->hostpublishes
 820                                                                      );
 821          }
 822  
 823          // allhosts_publish allows us to tell the admin that even though he
 824          // is disabling a service, it's still available to the host because
 825          // he's also publishing it to 'all hosts'
 826          if ($result->hostid == $CFG->mnet_all_hosts_id && $CFG->mnet_all_hosts_id != $mnet_peer->id) {
 827              $myservices[$result->name][$result->apiversion]['allhosts_publish'] = $result->publish;
 828              $myservices[$result->name][$result->apiversion]['allhosts_subscribe'] = $result->subscribe;
 829          } elseif (!empty($result->hostid)) {
 830              $myservices[$result->name][$result->apiversion]['I_publish'] = $result->publish;
 831              $myservices[$result->name][$result->apiversion]['I_subscribe'] = $result->subscribe;
 832          }
 833          $mydata['publish'][$result->serviceid] = $result->publish;
 834          $mydata['subscribe'][$result->serviceid] = $result->subscribe;
 835  
 836      }
 837  
 838      $cache[$mnet_peer->id]['fulldata'] = $myservices;
 839      $cache[$mnet_peer->id]['mydata'] = $mydata;
 840  
 841      return $cache[$mnet_peer->id][$requestkey];
 842  }
 843  
 844  /**
 845   * return an array of the profile fields to send
 846   * with user information to the given mnet host.
 847   *
 848   * @param mnet_peer $peer the peer to send the information to
 849   *
 850   * @return array (like 'username', 'firstname', etc)
 851   */
 852  function mnet_fields_to_send(mnet_peer $peer) {
 853      return _mnet_field_helper($peer, 'export');
 854  }
 855  
 856  /**
 857   * return an array of the profile fields to import
 858   * from the given host, when creating/updating user accounts
 859   *
 860   * @param mnet_peer $peer the peer we're getting the information from
 861   *
 862   * @return array (like 'username', 'firstname', etc)
 863   */
 864  function mnet_fields_to_import(mnet_peer $peer) {
 865      return _mnet_field_helper($peer, 'import');
 866  }
 867  
 868  /**
 869   * helper for {@see mnet_fields_to_import} and {@mnet_fields_to_send}
 870   *
 871   * @access private
 872   *
 873   * @param mnet_peer $peer the peer object
 874   * @param string    $key 'import' or 'export'
 875   *
 876   * @return array (like 'username', 'firstname', etc)
 877   */
 878  function _mnet_field_helper(mnet_peer $peer, $key) {
 879      $tmp = mnet_profile_field_options();
 880      $defaults = explode(',', get_config('moodle', 'mnetprofile' . $key . 'fields'));
 881      if ('1' === get_config('mnet', 'host' . $peer->id . $key . 'default')) {
 882          return array_merge($tmp['forced'], $defaults);
 883      }
 884      $hostsettings = get_config('mnet', 'host' . $peer->id . $key . 'fields');
 885      if (false === $hostsettings) {
 886          return array_merge($tmp['forced'], $defaults);
 887      }
 888      return array_merge($tmp['forced'], explode(',', $hostsettings));
 889  }
 890  
 891  
 892  /**
 893   * given a user object (or array) and a list of allowed fields,
 894   * strip out all the fields that should not be included.
 895   * This can be used both for outgoing data and incoming data.
 896   *
 897   * @param mixed $user array or object representing a database record
 898   * @param array $fields an array of allowed fields (usually from mnet_fields_to_{send,import}
 899   *
 900   * @return mixed array or object, depending what type of $user object was passed (datatype is respected)
 901   */
 902  function mnet_strip_user($user, $fields) {
 903      if (is_object($user)) {
 904          $user = (array)$user;
 905          $wasobject = true; // so we can cast back before we return
 906      }
 907  
 908      foreach ($user as $key => $value) {
 909          if (!in_array($key, $fields)) {
 910              unset($user[$key]);
 911          }
 912      }
 913      if (!empty($wasobject)) {
 914          $user = (object)$user;
 915      }
 916      return $user;
 917  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1