title: ejabberd external authentification php sqlserver
body: <p>I have a ejabberd 15.03 server in a centOS virtual machine and i need to implement an external authentification method with php against SQL server.
  The official documentation shows a php/mysql example
  <a href="https://www.ejabberd.im/files/efiles/check_mysql.php.txt" rel="nofollow">php/mysql external authentification</a></p>
  
  <p>Are there a method to change this example to php/sqlserver and functions such as mysql_ping.</p>
  
  <p>Here is an example i wrote and put it under /opt/auth/ and configured ejabberd.yml to execute is as external authentification.</p>
  
  <pre><code>$auth = new JabberAuth();
  $auth-&gt;dbhost = "hostIP";
  $auth-&gt;dbuser = "user";
  $auth-&gt;dbpass = "pass";
  $auth-&gt;dbbase = "databaseName";
  
  
  
  $auth-&gt;play(); // We simply start process !
  
  class JabberAuth {
      var $dbhost; 
      var $dbuser; 
      var $dbpass;
      var $dbbase; 
  
      var $debug      = false;                      /* Debug mode */
      var $debugfile  = "/opt/auth/log/pipe-debug.log";  /* Debug output */
      var $logging    = false;                      /* Do we log requests ? */
      var $logfile    = "/opt/auth/log/pipe-log.log" ;   /* Log file ... */
      /*
       * For both debug and logging, ejabberd have to be able to write.
       */
  
      var $jabber_user;   /* This is the jabber user passed to the script. filled by $this-&gt;command() */
      var $jabber_pass;   /* This is the jabber user password passed to the script. filled by $this-&gt;command() */
      var $jabber_server; /* This is the jabber server passed to the script. filled by $this-&gt;command(). Useful for VirtualHosts */
      var $jid;           /* Simply the JID, if you need it, you have to fill. */
      var $data;          /* This is what SM component send to us. */
  
      var $dateformat = "M d H:i:s"; /* Check date() for string format. */
      var $command; /* This is the command sent ... */
      var $mysock;  /* MySQL connection ressource */
      var $stdin;   /* stdin file pointer */
      var $stdout;  /* stdout file pointer */
  
      function JabberAuth()
      {
          @define_syslog_variables();
          @openlog("pipe-auth", LOG_NDELAY, LOG_SYSLOG);
  
          if($this-&gt;debug) {
              @error_reporting(E_ALL);
              @ini_set("log_errors", "1");
              @ini_set("error_log", $this-&gt;debugfile);
          }
          $this-&gt;logg("Starting pipe-auth ..."); // We notice that it's starting ...
          $this-&gt;openstd();
      }
  
      function stop()
      {
          $this-&gt;logg("Shutting down ..."); // Sorry, have to go ...
          closelog();
          $this-&gt;closestd(); // Simply close files
          exit(0); // and exit cleanly
      }
  
      function openstd()
      {
          $this-&gt;stdout = @fopen("php://stdout", "w"); // We open STDOUT so we can read
          $this-&gt;stdin  = @fopen("php://stdin", "r"); // and STDIN so we can talk !
      }
  
      function readstdin()
      {
          $l      = @fgets($this-&gt;stdin, 3); // We take the length of string
          $length = @unpack("n", $l); // ejabberd give us something to play with ...
          $len    = $length["1"]; // and we now know how long to read.
          if($len &gt; 0) { // if not, we'll fill logfile ... and disk full is just funny once
              $this-&gt;logg("Reading $len bytes ... "); // We notice ...
              $data   = @fgets($this-&gt;stdin, $len+1);
              // $data = iconv("UTF-8", "ISO-8859-15", $data); // To be tested, not sure if still needed.
              $this-&gt;data = $data; // We set what we got.
              $this-&gt;logg("IN: ".$data);
          }
      }
  
      function closestd()
      {
          @fclose($this-&gt;stdin); // We close everything ...
          @fclose($this-&gt;stdout);
      }
  
      function out($message)
      {
          @fwrite($this-&gt;stdout, $message); // We reply ...
          $dump = @unpack("nn", $message);
          $dump = $dump["n"];
          $this-&gt;logg("OUT: ". $dump);
      }
  
      function myalive()
      {
          if(!is_resource($this-&gt;mysock) || !@mysql_ping($this-&gt;mysock)) { // check if we have a MySQL connection and if it's valid.
              $this-&gt;mysql(); // We try to reconnect if MySQL gone away ...
              return @mysql_ping($this-&gt;mysock); // we simply try again, to be sure ...
          } else {
              return true; // so good !
          }
      }
  
      function play()
      {
          do {
              $this-&gt;readstdin(); // get data
              $length = strlen($this-&gt;data); // compute data length
              if($length &gt; 0 ) { // for debug mainly ...
                  $this-&gt;logg("GO: ".$this-&gt;data);
                  $this-&gt;logg("data length is : ".$length);
              }
              $ret = $this-&gt;command(); // play with data !
              $this-&gt;logg("RE: " . $ret); // this is what WE send.
              $this-&gt;out($ret); // send what we reply.
              $this-&gt;data = NULL; // more clean. ...
          } while (true);
      }
  
      function command()
      {
          $data = $this-&gt;splitcomm(); // This is an array, where each node is part of what SM sent to us :
          // 0 =&gt; the command,
          // and the others are arguments .. e.g. : user, server, password ...
  
          if($this-&gt;myalive()) { // Check we can play with MySQL
              if(strlen($data[0]) &gt; 0 ) {
                  $this-&gt;logg("Command was : ".$data[0]);
              }
              switch($data[0]) {
                  case "isuser": // this is the "isuser" command, used to check for user existance
                          $this-&gt;jabber_user = $data[1];
                          $parms = $data[1];  // only for logging purpose
                          $return = $this-&gt;checkuser();
                      break;
  
                  case "auth": // check login, password
                          $this-&gt;jabber_user = $data[1];
                          $this-&gt;jabber_pass = $data[3];
                          $parms = $data[1].":".$data[2].":".md5($data[3]); // only for logging purpose
                          $return = $this-&gt;checkpass();
                      break;
  
                  case "setpass":
                          $return = false; // We do not want jabber to be able to change password
                      break;
  
                  default:
                          $this-&gt;stop(); // if it's not something known, we have to leave.
                          // never had a problem with this using ejabberd, but might lead to problem ?
                      break;
              }
  
              $return = ($return) ? 1 : 0;
  
              if(strlen($data[0]) &gt; 0 &amp;&amp; strlen($parms) &gt; 0) {
                  $this-&gt;logg("Command : ".$data[0].":".$parms." ==&gt; ".$return." ");
              }
              return @pack("nn", 2, $return);
          } else {
              // $this-&gt;prevenir(); // Maybe useful to tell somewhere there's a problem ...
              return @pack("nn", 2, 0); // it's so bad.
          }
      }
  
      function checkpass()
      {
  
  
  $hash=sha1($this-&gt;jabber_pass);
  $query = mssql_query("SELECT [attribute] FROM [DatabaseName].[dbo].[table] where (S_USERNAME='$this-&gt;jabber_user') AND  (SUBSTRING(S_PASSWORD,1,(LEN(S_PASSWORD)-3)))='$hash'");
  
  
  // Check if there were any records
  if (!mssql_num_rows($query)) {
      return false;
  } else {
      return true;
  }
      }
  
      function checkuser()
      {
  
  $query = mssql_query("SELECT [attribute] FROM [DatabaseName].[dbo].[table] where S_USERNAME='$this-&gt;jabber_user'");
  
  // Check if there were any records
  if (!mssql_num_rows($query)) {
      return false;
  } else {
      return true;
  }
  
      }
  
      function splitcomm() // simply split command and arugments into an array.
      {
          return explode(":", $this-&gt;data);
      }
  
      function mysql() // "MySQL abstraction", this opens a permanent MySQL connection, and fill the ressource
      {
  
          $this-&gt;mysock = @mssql_pconnect($this-&gt;dbhost, $this-&gt;dbuser, $this-&gt;dbpass);
          echo '1';
          var_dump($this-&gt;mysock);
          @mssql_select_db($this-&gt;dbbase, $this-&gt;mysock);
          $this-&gt;logg("MsSql :: ". (is_resource($this-&gt;mysock) ? "ConnectÃ©" : "DÃ©connectÃ©"));
      }
  
      function logg($message) // pretty simple, using syslog.
      // some says it doesn't work ? perhaps, but AFAIR, it was working.
      {
          if($this-&gt;logging) {
              @syslog(LOG_INFO, $message);
          }
      }
  }
  </code></pre>
  
  <p>This did not make any php error. But in ejabberd.log i'm always getting 
  'extauth script has exitted abruptly with reason 'normal'
  '</p>
  
  <p>This is the crash report in crash.log</p>
  
  <blockquote>
    <p>2015-04-01 17:44:12 =CRASH REPORT====   crasher:
        initial call: ejabberd_http:init/2
        pid: &lt;0.8779.0>
        registered_name: []
        exception error: bad argument: [{extauth,call_port,2,[{file,"src/extauth.erl"},{line,99}]},{ejabberd_auth_external,check_password_extauth,3,[{file,"src/ejabberd_auth_external.erl"},{line,182}]},{ejabberd_auth_external,check_password_external_cache,3,[{file,"src/ejabberd_auth_external.erl"},{line,244}]},{ejabberd_auth,check_password_loop,2,[{file,"src/ejabberd_auth.erl"},{line,158}]},{ejabberd_auth,check_password,3,[{file,"src/ejabberd_auth.erl"},{line,106}]},{ejabberd_web_admin,get_auth_account,5,[{file,"src/ejabberd_web_admin.erl"},{line,266}]},{ejabberd_web_admin,process,2,[{file,"src/ejabberd_web_admin.erl"},{line,221}]},{ejabberd_http,process,5,[{file,"src/ejabberd_http.erl"},{line,359}]}]
        ancestors: [ejabberd_http_sup,ejabberd_sup,&lt;0.37.0>]
        messages: []
        links: [&lt;0.327.0>,#Port&lt;0.12042>]
        dictionary: [{random_seed,{2036,6729,29501}}]
        trap_exit: false
        status: running
        heap_size: 2586
        stack_size: 27
        reductions: 1244   neighbours: 2015-04-01 17:44:12 =SUPERVISOR REPORT====
         Supervisor: {local,ejabberd_http_sup}
         Context:    child_terminated
         Reason:     badarg
         Offender:   [{pid,&lt;0.8779.0>},{name,undefined},{mfargs,{ejabberd_http,start_link,undefined}},{restart_type,temporary},{shutdown,1000},{child_type,worker}]</p>
  </blockquote>
  
  <p>It seem to be a misconfiguration error.
  This is what i added to ejabberd.yml</p>
  
  <pre><code>auth_method: external
  extauth_program: "php -f /etc/ejabberd-15.03/auth_script.php"
  extauth_cache: 600
  extauth_instances: 3
  </code></pre>