<?php

//
// To install this plugin, add a section like the following to LocalSettings.php:
//
// require_once('extensions/AuthP4/AuthP4.php');
// $wgP4EXEC   = 'p4';              //path to the p4 client executable
// $wgP4PORT   = 'perforce:1666';   //Perforce server address:port
// $wgP4USER   = 'user';            //Perforce user with at least "list" access
// $wgP4PASSWD = 'password';        //Password/ticket for that user
//

$wgExtensionCredits['other'][] = array(
	'name' => 'AuthP4',
	'url' => 'http://public.perforce.com/wiki/index.php/AuthP4_%28MediaWiki_authentication_plugin%29',
	'author' => 'Sam Stafford',
	'description' => 'Authenticate via Perforce server',
);

require_once('AuthPlugin.php');

class AuthP4 extends AuthPlugin 
{

  function AuthP4() { }

  function userExists( $username )
  {
     $inf = array();
     $err = array();
     $user = escapeshellarg( wiki_to_p4( $username ) );
     run_p4_s( "groups $user", $inf, $err );
     return( count( $inf ) );
  }

  function authenticate( $username, $password )
  {
     global $wgP4EXEC;
     global $wgP4PORT;
     $inf = array();
     $err = array();
     $user = escapeshellarg( wiki_to_p4( $username ) );
     $pass = $password; //passed thru pipe, not arg!
  
     $descriptors = array
     (
        0 => array("pipe", "r"),
        1 => array("pipe", "w")
     );
     $p4 = "$wgP4EXEC -p $wgP4PORT -u $user -s";
     $loginproc = proc_open( "$p4 login -p", $descriptors, $pipes);
     if ( !is_resource( $loginproc ) ) { return false; }
     
     fwrite( $pipes[0], $pass );
     fclose( $pipes[0] );
    
     while ( !feof( $pipes[1] ) )
     {
        sort_p4_s_output( fgets( $pipes[1] ), $inf, $err );
     }
     fclose( $pipes[1] );
     proc_close( $loginproc );
   
     return( count( $inf ) );
  }

  function modifyUITemplate( &$template )
  {
    //Disable mail password (since it doesn't work), the
    //account creation (since we want registration to always
    //go through the Perforce account signup process), and the
    //domain box (since it's irrelevant).
    $template->set('useemail', false);
    $template->set('create', false);
    $template->set('domain', false);
    $template->set('usedomain', false);

    //Tweak "nologin" prompt to point at a custom page instead of
    //the disabled "create account" special page.
    global $wgP4PORT;
    global $wgServer;
    global $wgScriptPath;
    $reglink = $wgServer.$wgScriptPath.'/index.php?title=Registration';
    $template->set('link', "Log in with the user/password you use on <b>$wgP4PORT</b>.<br>Don't have a registered Public Depot account?  <a href=$reglink>Register here.</a>");
  }

  function updateUser( &$user )
  {
     //Doesn't do anything, but needs to return true so MWiki
     //doesn't think there was a problem.
     return true;
  }

  function autoCreate()
  {
     //Create wiki accounts automagically if Perforce accounts
     //exists.  This eliminates the need to set up wiki accounts
     //seperately.
     return true;
  }

  //Make people use "p4 passwd" for now.
  //This could be implemented with "p4 passwd $USER"
  //at some point if desired.
  function allowPasswordChange()
  {
     return false;
  }
  function setPassword( $user, $password )
  {
     return false;
  }

  function updateExternalDB( $user )
  {
     //Could be implemented with "p4 user -f USER", but
     //we'll leave it alone for now.  Still needs to return
     //true or automatic account creation will fail.
     return true;
  }

  //Not allowed to use local-only accounts or create Perforce
  //accounts.
  function canCreateAccounts()
  { 
     return false; 
  }
  function addUser( $user, $password, $email='', $realname='' ) 
  { 
     return false; 
  }
  function strict() 
  { 
     return true; 
  }

  function initUser( &$user )
  {
     //Called on account creation.
     //Pull fullname and email from Perforce.  The user
     //may change them later if desired.

     $p4username = escapeshellarg( wiki_to_p4( $user->getName() ) );
     $p4user = array();

     run_p4_tag( "user -o $p4username", $p4user );

     if ( isset( $p4user['Email'] ) )
     {
        $user->setEmail( $p4user['Email'] );
     }
     if ( isset( $p4user['FullName'] ) )
     {
        $user->setRealName( $p4user['FullName'] );
     }
  }

  function getCanonicalName( $username )
  {
     //Force names to standard casing to prevent duplicates.
     $user = trim( $username );
     $user = str_replace( "_", " ", $user );  // 'JOHN_DOE' -> 'JOHN DOE'
     $user = strtolower( $user );             // 'JOHN DOE' -> 'john doe'
     $user = ucwords( $user );                // 'john doe' -> 'John Doe'
     return $user;
  }
  
} // end AuthPlugin class

//Things below this line aren't part of the AuthPlugin interface.
  

function run_p4_s( $command, &$info, &$error )
{
   global $wgP4EXEC;
   global $wgP4PORT;
   global $wgP4USER;
   global $wgP4PASSWD;
   $p4 = "$wgP4EXEC -p $wgP4PORT -u $wgP4USER -P $wgP4PASSWD -s";
   $exit = false;

   $out = array();
   exec( "$p4 $command", $out );

   foreach( $out as $line )
   {
      if ( $line == "exit: 1" )
      {
         $exit = false;
      }
      elseif ( $line == "exit: 0" )
      {
         $exit = true;
      }
      else sort_p4_s_output( $line, $info, $error );
   }

   return $exit;
}

function run_p4_tag( $command, &$tagdict )
{
   global $wgP4EXEC;
   global $wgP4PORT;
   global $wgP4USER;
   global $wgP4PASSWD;
   $p4 = "$wgP4EXEC -p $wgP4PORT -u $wgP4USER -P $wgP4PASSWD -Ztag";

   $out = array();
   exec( "$p4 $command", $out );

   foreach( $out as $line )
   {
      sort_p4_tag_output( $line, $tagdict );
   }
}

function sort_p4_s_output( $line, &$info, &$error )
{
   if ( substr( $line, 0, 6 ) == "info: " )
   {
      array_push( $info, substr( $line, 6 ) );
   }
   elseif ( substr( $line, 0, 7 ) == "error: " )
   {
      array_push( $error, substr( $line, 7 ) );
   }
}

function sort_p4_tag_output( $line, &$tagdict )
{
   if ( !substr( $line, 0, 4 ) == "... " ) { return; }
   
   $line = substr( $line, 4 );
   $tagend = strpos( $line, " " );
   $tag = substr( $line, 0, $tagend );
   $data = substr( $line, $tagend + 1 );
   $tagdict[$tag] = $data;
}

function wiki_to_p4( $user )
{
   $user = trim( $user );
   $user = str_replace( " ", "_", $user );
   $user = strtolower( $user );
   return $user;
}

function testUserExists( $user )
{
   if ( AuthP4::userExists( $user ) )
   {
      print( "$user exists.\n" );
   }
   else
   {  
      print( "$user does not exist.\n" );
   }
}

function testAuthenticate( $user, $pass )
{
   if ( AuthP4::authenticate( $user, $pass ) )
   {
      print( "$user authenticated with $pass\n" );
   }
   else
   {
      print( "$user failed authentication with $pass\n" );
   }
}

$wgAuth = new AuthP4();

?>