+ const MODE_TYPE_A = 1, MODE_TYPE_B = 2, MODE_TYPE_C = 3, MODE_TYPE_D = 4;
+ /** http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
+ * Section 3.3 CHANMODES
+ *
+ * Type A: Modes that add or remove an address to or from a list.
+ * These modes always take a parameter when sent by the server to a
+ * client; when sent by a client, they may be specified without a
+ * parameter, which requests the server to display the current
+ * contents of the corresponding list on the channel to the client.
+ *
+ * Type B: Modes that change a setting on the channel. These modes
+ * always take a parameter.
+ *
+ * Type C: Modes that change a setting on the channel. These modes
+ * take a parameter only when set; the parameter is absent when the
+ * mode is removed both in the client's and server's MODE command.
+ *
+ * Type D: Modes that change a setting on the channel. These modes
+ * never take a parameter.
+ **/
+ private static $modes = array(
+ "b" => self::MODE_TYPE_A,
+ "k" => self::MODE_TYPE_B,
+ "a" => self::MODE_TYPE_C,
+ "l" => self::MODE_TYPE_C,
+ "f" => self::MODE_TYPE_C,
+ "F" => self::MODE_TYPE_C,
+ "c" => self::MODE_TYPE_D,
+ "C" => self::MODE_TYPE_D,
+ "i" => self::MODE_TYPE_D,
+ "m" => self::MODE_TYPE_D,
+ "M" => self::MODE_TYPE_D,
+ "n" => self::MODE_TYPE_D,
+ "N" => self::MODE_TYPE_D,
+ "p" => self::MODE_TYPE_D,
+ "r" => self::MODE_TYPE_D,
+ "s" => self::MODE_TYPE_D,
+ "t" => self::MODE_TYPE_D,
+ "u" => self::MODE_TYPE_D,
+ "D" => self::MODE_TYPE_D,
+ "d" => self::MODE_TYPE_D,
+ "R" => self::MODE_TYPE_D,
+ "z" => self::MODE_TYPE_D,
+
+ //special behavior
+ "o" => self::MODE_TYPE_B,
+ "v" => self::MODE_TYPE_B
+ );
+ private static $modevalues = null;
+ private $modeflags = 0;
+ private $modeparams = array();
+ private $channel;
+
+ public function __construct($channel) {
+ if(self::$modevalues == null) {
+ //build modevalues array
+ $flag = 1;
+ self::$modevalues = array();
+ foreach(self::$modes as $mode => $type) {
+ self::$modevalues[$mode] = $flag;
+ $flag <<= 1;
+ }
+ }
+ $this->channel = $channel;
+ }
+
+ public function parseModes($modes) {
+ $args = explode(" ",$modes);
+ $c = 1;
+ for($i = 0; $i < strlen($args[0]); $i++) {
+ $mode = $args[0][$i];
+ if($mode == "+") continue;
+ if($mode == "-") { //we have no - flag on parseModes???
+ trigger_error("unexpected MODE_DEL (-) on parseModes (".$modes.").", E_USER_WARNING);
+ break;
+ }
+ if(!array_key_exists($mode, self::$modevalues)) {
+ trigger_error("unknown mode (".$mode.") on parseModes (".$modes.").", E_USER_WARNING);
+ continue;
+ }
+ $flag = self::$modevalues[$mode];
+ if(self::$modevalues[$mode] == self::MODE_TYPE_A) continue; //we shouldn't get such a mode on parseModes
+ $this->modeflags |= $flag;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_B || self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ $this->modeparams[$mode] = $args[$c++];
+ }
+ }
+ return $c-1;
+ }
+
+ public function setModes($modes, $returndiff = false) {
+ $args = explode(" ",$modes);
+ $c = 1;
+ $add = true;
+ $modestradd = "+";
+ $paramstradd = "";
+ $modestrdel = "-";
+ $paramstrdel = "";
+ for($i = 0; $i < strlen($args[0]); $i++) {
+ $mode = $args[0][$i];
+ if($mode == "+") {
+ $add = true;
+ continue;
+ }
+ if($mode == "-") {
+ $add = false;
+ continue;
+ }
+ if(!array_key_exists($mode, self::$modevalues)) {
+ trigger_error("unknown mode (".$mode.") on setModes (".$modes.").", E_USER_WARNING);
+ continue;
+ }
+ if($mode == "o" || $mode == "v") {
+ if($this->setPrivs($add, $mode, $args[$c++])) {
+ if($returndiff && $add) {
+ $modestradd .= $mode;
+ $paramstradd .= " ".$args[$c-1];
+ } else if($returndiff && !$add) {
+ $modestrdel .= $mode;
+ $paramstrdel .= " ".$args[$c-1];
+ }
+ }
+ continue;
+ }
+ $flag = self::$modevalues[$mode];
+ if($add) {
+ if($returndiff && !($this->modeflags & $flag)) {
+ $modestradd .= $mode;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_B || self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ $paramstradd .= " ".$args[$c];
+ }
+ }
+ $this->modeflags |= $flag;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_B || self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ $this->modeparams[$mode] = $args[$c++];
+ }
+ } else {
+ if($returndiff && ($this->modeflags & $flag)) {
+ $modestrdel .= $mode;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ $paramstrdel .= " ".$args[$c];
+ }
+ }
+ $this->modeflags &= ~$flag;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_B || self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ unset($this->modeparams[$mode]);
+ }
+ if(self::$modevalues[$mode] == self::MODE_TYPE_C) $c++;
+ }
+ }
+ if($returndiff) {
+ $modediff = ($modestradd == "+" ? "" : $modestradd);
+ $modediff .= ($modestrdel == "-" ? "" : $modestrdel);
+ $modediff .= $paramstradd.$paramstrdel;
+ return $modediff;
+ }
+ }
+
+ private function setPrivs($add, $mode, $user) {
+ $user = P10_User::getUserByNum($user);
+ if($user == null) {
+ trigger_error("Tried to set privs on a User that does not exist.", E_USER_ERROR);
+ return;
+ }
+ $privs = $this->channel->getUserPrivs($user);
+ $privFlag = 0;
+ if($mode == "o") $privFlag = P10_Channel::USERPRIV_OPED;
+ if($mode == "v") $privFlag = P10_Channel::USERPRIV_VOICE;
+ if(!($add xor ($privs & $privFlag)))
+ return false;
+ if($add) $privs |= $privFlag;
+ else $privs &= ~$privFlag;
+ $this->channel->setUserPrivs($user, $privs);
+ return true;
+
+ }
+
+ public function getModeString() {
+ $modestr = "+";
+ $paramstr = "";
+ foreach(self::$modevalues as $mode => $flag) {
+ if(($this->modeflags & $flag)) {
+ $modestr .= $mode;
+ if(self::$modevalues[$mode] == self::MODE_TYPE_B || self::$modevalues[$mode] == self::MODE_TYPE_C) {
+ $paramstr .= " ".$this->modeparams[$mode];
+ }
+ }
+ }
+ return $modestr.$paramstr;
+ }
+
+ public function hasMode($mode) {
+ if(!array_key_exists($mode, self::$modevalues)) {
+ trigger_error("unknown mode (".$mode.") on setModes (".$modes.").", E_USER_WARNING);
+ continue;
+ }
+ $flag = self::$modevalues[$mode];
+ return ($this->modeflags & $flag);
+ }
+