Location: PHPKode > scripts > Virtual Mail ManaGer Interface > virtual-mail-manager-interface/vm/vm.interface.php
<?php

/*
 *  Interface to the vmailmgr daemon (protocol version 2)
 *
 *  Acknowledgements go to Mike Bell <hide@address.com>
 *  His interface helped me getting a clue how vmailmgr's
 *  (not necessarily self-evident) protocol works.
 *
 *  This interface is distributed under the LGPL (see LICENSE.LGPL)
 *
 *  Authors:
 *    Daniel Lorch <hide@address.com>
 *
 */

class vmailmgr {

  /* private: domain and password */
  var $domain;
  var $pass;

  /* private: vmailmgr daemon's host and port: either TCP or UNIX domain socket */
  var $tcp_host;
  var $tcp_port;

  var $unix_socket;

  /* private: vmailmgr's text response. use last_response() to access */
  var $response;

  /* public: constructor */
  function vmailmgr($domain, $pass) {
    $this->domain = $domain;
    $this->pass   = $pass;
  }
  
  /* private: return argument length in 2 bytes */
  function arglen($a) {
    return chr((strlen($a) >> 8) & 255) . chr(strlen($a) & 255);
  }

  /* private: send raw command */
  function raw($cmd, $args=array()) {
    /* number of arguments */
    $vmcmd = chr(count($args));

    /* command */
    $vmcmd .= $this->arglen($cmd) . $cmd;
    
    /* arguments */
    for($i=0; $i<count($args); ++$i)
      $vmcmd .= $this->arglen($args[$i]) . $args[$i];
      
    /* finally prepend protocol version (2) and total length of data */
    $vmcmd = chr(2) . $this->arglen($vmcmd) . $vmcmd;

    /* send command and receive response */
    if(($response = $this->send($vmcmd)) === false)
      return false;

    /* parse response */    
    $response_code   = ord($response[$i=0]);
    $response_length = (ord($response[++$i]) << 8) + ord($response[++$i]);

    $this->response = substr($response, $i+1);

    /* validate response */
    return $response_code === 0;
  }
  
  /* private: send data to socket and receive response */
  function send($data) {
    if(isset($this->tcp_host)) {
      $sock = fsockopen($this->tcp_host, $this->tcp_port);
    }
    else if(isset($this->unix_socket)) {
      $sock = fsockopen($this->unix_socket, 0);
    }
    
    if(!$sock) {
      return false;
    }
    
    @fputs($sock, $data);
    
    while(!feof($sock) && ($buf=@fread($sock, 4096)) !== false)
      $response .= $buf;
      
    fclose($sock);  

    return $response;  
  }

  /* public: list all users */  
  function list_users() {
    if(!$this->raw("listdomain", array($this->domain, $this->pass)))
      return false;

    /* make copy, discard response as the user must not see it  */
    $response = $this->response;
    $this->response = '';

    $count = 0;

    do {
      /* discard first byte (always 0x00), then read two bytes (data length) */
      if(($length = (ord($response[$i=1]) << 8) + ord($response[++$i])) == 0)
        break;

      /* go through every element */
      $element  = substr($response, $i+1, $length);
      
      /* remainder (also skip one leading 0x00 (+1) two length bytes (+2)) */
      $response = substr($response, $length + 3);

      /* parse */
      $f=0;
      while($element[$f] != chr(0) && $f < strlen($element)) {
         ++$f;
      }

      $info[$count]['username'] = substr($element, 0, $f);

      /* flags: pairs of bytes (flag number and value); string starting
                with record format version (0x02). */

      $record_format_ver = ord($element[++$f]);

      while($f < strlen($element) - 1) { 
        $flag_num = ord($element[++$f]);
        $flag_val = ord($element[++$f]);

        if($flag_num == 0) {
          break;
        }

	/* flags defined in vmailmr source lib/vdomain/vdomain.h */
	switch($flag_num) {
	  case 1:
	    $info[$count]['flags']['pass'] = ($flag_val == 1);
	    break;
	  
	  case 2:
	    $info[$count]['flags']['dest'] = ($flag_val == 1);
	    break;
	    
	  case 3:
	    $info[$count]['flags']['hardquota'] = ($flag_val == 1);
	    break;

	  case 4:
	    $info[$count]['flags']['softquota'] = ($flag_val == 1);
	    break;

	  case 5:
	    $info[$count]['flags']['msgsize'] = ($flag_val == 1);
	    break;

	  case 6:
	    $info[$count]['flags']['msgcount'] = ($flag_val == 1);
	    break;

	  case 7:
	    $info[$count]['flags']['expiry'] = ($flag_val == 1);
	    break;

	  case 8:
	    $info[$count]['flags']['mailbox_enabled'] = ($flag_val == 1);
	    break;

	  case 9:
	    $info[$count]['flags']['personal'] = ($flag_val == 1);
	    break;	    
	}

      }

      $element = substr($element, $f);

      $list = explode(chr(0), $element);

      $info[$count]['password']     = ($list[$i=0] != '*');
      $info[$count]['mailboxdir']   = $list[++$i];

      while($list[++$i] !== '')
        $info[$count]['forwards'][] = $list[$i];

      $info[$count]['personal']     = $list[++$i];
      $info[$count]['hardquota']    = $list[++$i];
      $info[$count]['softquota']    = $list[++$i];
      $info[$count]['messagesize']  = $list[++$i];
      $info[$count]['messagecount'] = $list[++$i];
      $info[$count]['creation']     = $list[++$i];
      $info[$count]['expiry']       = $list[++$i];

      ++$count;
    }
    while ($response != '');

    return $info;
  }

  /* public: retrieve info on user */
  function user_info($user) {
    if(!$this->raw("lookup", array($this->domain, $user, $this->pass)))
      return false;

    /* make copy, discard response as the user must not see it  */
    $response = $this->response;
    $this->response = '';

    /* flags: pairs of bytes (flag number and value); string starting
              with record format version (0x02). */

    $record_format_ver = ord($response[0]);
    
    $f=0;
    
    while($f < strlen($response) - 1) { 
      $flag_num = ord($response[++$f]);
      $flag_val = ord($response[++$f]);

      if($flag_num == 0) {
        break;
      }

      /* flags defined in vmailmr source lib/vdomain/vdomain.h */
      switch($flag_num) {
        case 1:
	  $info['flags']['pass'] = ($flag_val == 1);
	  break;
	  
	case 2:
	  $info['flags']['dest'] = ($flag_val == 1);
	  break;
	    
	case 3:
	  $info['flags']['hardquota'] = ($flag_val == 1);
	  break;

	case 4:
	  $info['flags']['softquota'] = ($flag_val == 1);
	  break;

	case 5:
	  $info['flags']['msgsize'] = ($flag_val == 1);
	  break;

	case 6:
	  $info['flags']['msgcount'] = ($flag_val == 1);
	  break;

	case 7:
	  $info['flags']['expiry'] = ($flag_val == 1);
	  break;

	case 8:
	  $info['flags']['mailbox_enabled'] = ($flag_val == 1);
	  break;

	case 9:
	  $info['flags']['personal'] = ($flag_val == 1);
	  break;	    
      }
    }

    $response = substr($response, $f);

    $list = explode(chr(0), $response);

    $info['username']     = $user;
    $info['password']     = ($list[$i=0] != '*'); 
    $info['mailboxdir']   = $list[++$i];

    while($list[++$i] !== '')
      $info['forwards'][] = $list[$i];
    
    $info['personal']     = $list[++$i];
    $info['hardquota']    = $list[++$i];
    $info['softquota']    = $list[++$i];
    $info['messagesize']  = $list[++$i];
    $info['messagecount'] = $list[++$i];
    $info['creation']     = $list[++$i];
    $info['expiry']       = $list[++$i];
    
    return $info;
  }

  function add_account($user, $pass, $forwards=array()) {
    return $this->raw("adduser2", array_merge($this->domain, $user, $this->pass, $pass, $user, $forwards));
  }

  function add_alias($user, $pass="", $forwards=array()) {
    return $this->raw("adduser2", array_merge($this->domain, $user, $this->pass, $pass, "", $forwards));
  }
  
  function delete_user($user) {
    return $this->raw("deluser", array($this->domain, $user, $this->pass));
  }

  function set_password($user, $pass) {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 1, $pass));
  }

  function set_forwards($user, $forwards=NULL) {
    if(!isset($forwards) || count($forwards) == 0)
      $forwards = '';
  
    return $this->raw("chattr", array_merge($this->domain, $user, $this->pass, 2, $forwards));
  }

  function set_hardquota($user, $quota='-') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 3, trim($quota) == '' ? '-' : $quota));
  }

  function set_softquota($user, $quota='-') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 4, trim($quota) == '' ? '-' : $quota));
  }

  function set_messagesize($user, $size='-') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 5, trim($size) == '' ? '-' : $size));
  }

  function set_messagecount($user, $count='-') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 6, trim($count) == '' ? '-' : $count));  
  }

  function set_expiry($user, $timestamp='-') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 7, trim($timestamp) == '' ? '-' : $timestamp));
  }

  function set_enabled($user, $enable) {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 8, $enable ? '1' : '0'));
  }

  function set_personal($user, $text='') {
    return $this->raw("chattr", array($this->domain, $user, $this->pass, 9, $text));  
  }

  function set_catchall($user=NULL) {
    $this->delete_user("+");

    if(!isset($user)) {
      return true;
    }
    else {
      return $this->add_alias("+", "", $user)
	  && $this->set_personal("+", "Catchall Alias");
    }
  }

  function autoresponse_set($user, $message) {
    /* autoresponse text can only be set when it is activated */
    if(!($status = $this->autoresponse_isset($user))) {
      $this->autoresponse_set_enabled($user, true);
    }

    $result = $this->raw("autoresponse", array($this->domain, $user, $this->pass, "write", $message));

    if(!$status) {
      $this->autoresponse_set_enabled($user, $status);
    }

    return $result;
  }

  function autoresponse_get($user) {
    /* autoresponse text can only be retrieved when it is activated */
    if(!($status = $this->autoresponse_isset($user))) {
      $this->autoresponse_set_enabled($user, true);
    }

    /* the response consists of: autoresponder text, 0x00, two length bytes, response text */
    if(!$this->raw("autoresponse", array($this->domain, $user, $this->pass, "read"))) 
      return false;

    /* make copy */
    $response = $this->response;

    while($response[$i] != chr(0) && $i<strlen($response))
      ++$i;
    $message = substr($response, 0, $i);

    $length = (ord($response[++$i]) << 8) + ord($response[++$i]);
    $this->response = substr($response, $i+1, $length);

    if(!$status) {
      $this->autoresponse_set_enabled($user, $status);
    }
    
    return $message;
  }

  function autoresponse_set_enabled($user, $enable) {
    return $this->raw("autoresponse", array($this->domain, $user, $this->pass, $enable ? 'enable' : 'disable'));
  }

  function autoresponse_isset($user) {
    $this->raw("autoresponse", array($this->domain, $user, $this->pass, "status"));
    return $this->response === 'enabled';
  }
  
  function autoresponse_parse($message) {
    if(preg_match("/(.*?)\r?\n\r?\n(.*)/s", $message, $matches)) {
      $result['body'] = $matches[2];    

      $headers = preg_split("/\r?\n/", $matches[1]);

      for($i=0; $i<count($headers); ++$i) {
        if(preg_match("/([^:]+): (.*)/", $headers[$i], $matches)) {
	  $result['headers'][$matches[1]] = $matches[2];
	}
      }
    }
    else {
      $result['body'] = $message;
    }
  
    return $result;
  }
  
  /* return vmailmgr's last text message */
  function last_response() {
    return $this->response;
  }
}

?>
Return current item: Virtual Mail ManaGer Interface