<commit_before><?php
/*
 * fedmsg-emit.php
 * -------------------------
 *
 * A MediaWiki plugin that emits messages to the Fedora Infrastructure Message
 * Bus.
 *
 * Installation Instructions
 * -------------------------
 *
 * You need to enable the following in /etc/php.ini for this to fly:
 *
 *    extension=zmq.so
 *
 * To install the plugin for mediawiki, you need to copy this file to:
 *
 *    /usr/share/mediawiki/fedmsg-emit.php
 *
 * And you also need to enable it by adding the following to the bottom of
 * /var/www/html/wiki/LocalSettings.php
 *
 *    require_once("$IP/fedmsg-emit.php");
 *
 * This corner of the fedmsg topology requires that an instance of fedmsg-relay
 * be running somewhere.  The reason being that multiple php processes get run
 * by apache at the same time which would necessitate `n` different bind
 * addresses for each process.  Here, as opposed to 'normal' fedmsg
 * configurations in our python webapps, each php process actively connects to
 * the relay and emits messages.  Our 'normal' python webapps establish a
 * passive endpoint on which they broadcast messages.
 *
 * Miscellaneous
 * -------------
 *
 * Version:   0.2.0
 * License:   LGPLv2+
 * Author:    <NAME>
 * Source:    http://github.com/fedora-infra/fedmsg
 */

if (!defined('MEDIAWIKI')) {echo("Cannot be run outside MediaWiki"); die(1);}

// globals
$config = 0;
$queue = 0;

// A utility function, taken from the comments at
// http://php.net/manual/en/language.types.boolean.php
function to_bool ($_val) {
  $_trueValues = array('yes', 'y', 'true');
  $_forceLowercase = true;

  if (is_string($_val)) {
    return (in_array(
      ($_forceLowercase?strtolower($_val):$_val),
      $_trueValues
    ));
  } else {
    return (boolean) $_val;
  }
}

function initialize() {
  global $config, $queue;
  /* Load the config.  Create a publishing socket. */

  // Danger! Danger!
  $json = shell_exec("fedmsg-config");
  $config = json_decode($json, true);

  /* Just make sure everything is sane with the fedmsg config */
  if (!array_key_exists('relay_inbound', $config)) {
    echo("fedmsg-config has no 'relay_inbound'");
    return false;
  }

  $context = new ZMQContext(1, true);
  $queue = $context->getSocket(ZMQ::SOCKET_PUB, "pub-a-dub-dub");
  $queue->setSockOpt(ZMQ::SOCKOPT_LINGER, $config['zmq_linger']);

  if (is_array($config['relay_inbound'])) {
    // API for fedmsg >= 0.5.2
    // TODO - be more robust here and if connecting to the first one fails, try
    // the next, and the next, and etc...
    $queue->connect($config['relay_inbound'][0]);
  } else {
    // API for fedmsg <= 0.5.1
    $queue->connect($config['relay_inbound']);
  }
  return true;
}

# Register our hooks with mediawiki
$wgHooks['ArticleSaveComplete'][] = 'article_save';
$wgHooks['UploadComplete'][] = 'upload_complete';

# This is a reimplementation of the python code in fedmsg/crypto.py
# That file is authoritative.  Changes there should be reflected here.
function sign_message($message_obj) {
  global $config;

  # This is required so that the string we sign is identical in python and in
  # php.  Ordereddict is used there; ksort here.
  ksort($message_obj);

  $message = json_encode($message_obj);

  # Step 0) - Find our cert.
  $fqdn = gethostname();
  $tokens = explode('.', $fqdn);
  $hostname = $tokens[0];
  $ssldir = $config['ssldir'];
  $certname = $config['certnames']['mediawiki.'.$hostname];

  # Step 1) - Load and encode the X509 cert
  $cert_obj = openssl_x509_read(file_get_contents(
    $ssldir.'/'.$certname.".crt"
  ));
  $cert = "";
  openssl_x509_export($cert_obj, $cert);
  $cert = base64_encode($cert);

  # Step 2) - Load and sign the jsonified message with the RSA private key
  $rsa_private = openssl_get_privatekey(file_get_contents(
    $ssldir.'/'.$certname.".key"
  ));
  $signature = "";
  openssl_sign($message, $signature, $rsa_private);
  $signature = base64_encode($signature);

  # Step 3) - Stuff it back in the message and return
  $message_obj['signature'] = $signature;
  $message_obj['certificate'] = $cert;

  return $message_obj;
}


function emit_message($subtopic, $message) {
  global $config, $queue;

  # Re-implement some of the logc from fedmsg/core.py
  # We'll have to be careful to keep this up to date.
  $prefix = "org.fedoraproject." . $config['environment'] . ".wiki.";
  $topic = $prefix . $subtopic;

  $message_obj = array(
    "topic" => $topic,
    "msg" => $message,
    "timestamp" => time(),
    # TODO -> we don't have a good way to increment this counter from php yet.
    "i" => 1,
  );
  if (array_key_exists('sign_messages', $config) and to_bool($config['sign_messages'])) {
    $message_obj = sign_message($message_obj);
  }

  $envelope = json_encode($message_obj);
  $queue->send($topic, ZMQ::MODE_SNDMORE | ZMQ::MODE_NOBLOCK);
  $queue->send($envelope, ZMQ::MODE_NOBLOCK);
}

function article_save(
  &$article,
  &$user,
  $text,
  $summary,
  $minoredit,
  $watchthis,
  $sectionanchor,
  &$flags,
  $revision,
  &$status,
  $baseRevId
) {

  # If for some reason or another we can't create our socket, then bail.<commit_msg>Also round timestamp in the php mediawiki plugin.<commit_after><?php
/*
 * fedmsg-emit.php
 * -------------------------
 *
 * A MediaWiki plugin that emits messages to the Fedora Infrastructure Message
 * Bus.
 *
 * Installation Instructions
 * -------------------------
 *
 * You need to enable the following in /etc/php.ini for this to fly:
 *
 *    extension=zmq.so
 *
 * To install the plugin for mediawiki, you need to copy this file to:
 *
 *    /usr/share/mediawiki/fedmsg-emit.php
 *
 * And you also need to enable it by adding the following to the bottom of
 * /var/www/html/wiki/LocalSettings.php
 *
 *    require_once("$IP/fedmsg-emit.php");
 *
 * This corner of the fedmsg topology requires that an instance of fedmsg-relay
 * be running somewhere.  The reason being that multiple php processes get run
 * by apache at the same time which would necessitate `n` different bind
 * addresses for each process.  Here, as opposed to 'normal' fedmsg
 * configurations in our python webapps, each php process actively connects to
 * the relay and emits messages.  Our 'normal' python webapps establish a
 * passive endpoint on which they broadcast messages.
 *
 * Miscellaneous
 * -------------
 *
 * Version:   0.2.0
 * License:   LGPLv2+
 * Author:    <NAME>
 * Source:    http://github.com/fedora-infra/fedmsg
 */

if (!defined('MEDIAWIKI')) {echo("Cannot be run outside MediaWiki"); die(1);}

// globals
$config = 0;
$queue = 0;

// A utility function, taken from the comments at
// http://php.net/manual/en/language.types.boolean.php
function to_bool ($_val) {
  $_trueValues = array('yes', 'y', 'true');
  $_forceLowercase = true;

  if (is_string($_val)) {
    return (in_array(
      ($_forceLowercase?strtolower($_val):$_val),
      $_trueValues
    ));
  } else {
    return (boolean) $_val;
  }
}

function initialize() {
  global $config, $queue;
  /* Load the config.  Create a publishing socket. */

  // Danger! Danger!
  $json = shell_exec("fedmsg-config");
  $config = json_decode($json, true);

  /* Just make sure everything is sane with the fedmsg config */
  if (!array_key_exists('relay_inbound', $config)) {
    echo("fedmsg-config has no 'relay_inbound'");
    return false;
  }

  $context = new ZMQContext(1, true);
  $queue = $context->getSocket(ZMQ::SOCKET_PUB, "pub-a-dub-dub");
  $queue->setSockOpt(ZMQ::SOCKOPT_LINGER, $config['zmq_linger']);

  if (is_array($config['relay_inbound'])) {
    // API for fedmsg >= 0.5.2
    // TODO - be more robust here and if connecting to the first one fails, try
    // the next, and the next, and etc...
    $queue->connect($config['relay_inbound'][0]);
  } else {
    // API for fedmsg <= 0.5.1
    $queue->connect($config['relay_inbound']);
  }
  return true;
}

# Register our hooks with mediawiki
$wgHooks['ArticleSaveComplete'][] = 'article_save';
$wgHooks['UploadComplete'][] = 'upload_complete';

# This is a reimplementation of the python code in fedmsg/crypto.py
# That file is authoritative.  Changes there should be reflected here.
function sign_message($message_obj) {
  global $config;

  # This is required so that the string we sign is identical in python and in
  # php.  Ordereddict is used there; ksort here.
  ksort($message_obj);

  $message = json_encode($message_obj);

  # Step 0) - Find our cert.
  $fqdn = gethostname();
  $tokens = explode('.', $fqdn);
  $hostname = $tokens[0];
  $ssldir = $config['ssldir'];
  $certname = $config['certnames']['mediawiki.'.$hostname];

  # Step 1) - Load and encode the X509 cert
  $cert_obj = openssl_x509_read(file_get_contents(
    $ssldir.'/'.$certname.".crt"
  ));
  $cert = "";
  openssl_x509_export($cert_obj, $cert);
  $cert = base64_encode($cert);

  # Step 2) - Load and sign the jsonified message with the RSA private key
  $rsa_private = openssl_get_privatekey(file_get_contents(
    $ssldir.'/'.$certname.".key"
  ));
  $signature = "";
  openssl_sign($message, $signature, $rsa_private);
  $signature = base64_encode($signature);

  # Step 3) - Stuff it back in the message and return
  $message_obj['signature'] = $signature;
  $message_obj['certificate'] = $cert;

  return $message_obj;
}


function emit_message($subtopic, $message) {
  global $config, $queue;

  # Re-implement some of the logc from fedmsg/core.py
  # We'll have to be careful to keep this up to date.
  $prefix = "org.fedoraproject." . $config['environment'] . ".wiki.";
  $topic = $prefix . $subtopic;

  $message_obj = array(
    "topic" => $topic,
    "msg" => $message,
    "timestamp" => round(time(), 3),
    # TODO -> we don't have a good way to increment this counter from php yet.
    "i" => 1,
  );
  if (array_key_exists('sign_messages', $config) and to_bool($config['sign_messages'])) {
    $message_obj = sign_message($message_obj);
  }

  $envelope = json_encode($message_obj);
  $queue->send($topic, ZMQ::MODE_SNDMORE | ZMQ::MODE_NOBLOCK);
  $queue->send($envelope, ZMQ::MODE_NOBLOCK);
}

function article_save(
  &$article,
  &$user,
  $text,
  $summary,
  $minoredit,
  $watchthis,
  $sectionanchor,
  &$flags,
  $revision,
  &$status,
  $baseRevId
) {

  # If for some reason or another we can't create our socket, then bail.
{'fraction_non_alphanumeric': 0.15055436685469753, 'fraction_numerical': 0.0067107566621279905, 'mean_word_length': 3.5061349693251533, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7535511', 'n_tokens_mistral': 3491, 'n_tokens_neox': 3359, 'n_words': 989}