Code

Added option to select asterisk extensions delimiter
[gosa.git] / gosa-plugins / gofon / gofon / phoneaccount / class_phoneAccount.inc
index 6b2edea24e34be15b2e6e6147845ac105edcf909..b4f4dede09b12f06af36c6ecc8284ccfe41e9470 100644 (file)
@@ -7,6 +7,8 @@ class phoneAccount extends plugin
   var $plDescription= "This does something";
   var $plIcon       = "plugins/gofon/images/phoneAccount.png";
 
+  var $delimiter = "|";
+
   /* Attributes */
   var $telephoneNumber        = array();
   var $goFonHardware          = "automatic";
@@ -32,7 +34,9 @@ class phoneAccount extends plugin
   var $goFonHomeServers       = array();        // Contains all available server configurations 
 
   var $context                = "default";
+  var $sip_contexts           = array();
   var $voice_context          = "default";
+  var $voicemail_contexts     = array();
 
   /* attribute list for save action */
   var $CopyPasteVars          = array("phoneNumbers","macroarray","macrostillavailable"/*"phoneNumbers" -Reset- */,
@@ -55,6 +59,8 @@ class phoneAccount extends plugin
   {
     plugin::plugin ($config, $dn, $parent);
 
+    $this->delimiter = $config->get_cfg_value("asteriskParameterDelimiter","|");
+
     /* Assemble phone numbers */
     if (isset($this->attrs['telephoneNumber'])){
       for ($i= 0; $i<$this->attrs['telephoneNumber']['count']; $i++){
@@ -63,6 +69,9 @@ class phoneAccount extends plugin
       }
     }
 
+    $this->sip_contexts = $this->get_asterisk_sip_contexts();
+    $this->voicemail_contexts = $this->get_asterisk_voicemail_contexts();
+
     /* Set parent object to tab object */
     if(is_object($parent)){
       $this->parent = $parent->parent;
@@ -78,6 +87,7 @@ class phoneAccount extends plugin
     }
     if(isset($this->attrs['mail'][0])){
       $this->mailAddress = $this->attrs['mail'][0];
+      $this->has_mailAccount = true;
     }
     if(isset($this->attrs['pager'][0])){
       $this->pager = $this->attrs['pager'][0];
@@ -178,7 +188,7 @@ class phoneAccount extends plugin
      */
     $deps_a = array(
         get_people_ou(),
-        get_ou("ogroupou"),
+        get_ou("ogroupRDN"),
         get_ou("serverRDN"),
         get_ou("terminalRDN"),
         get_ou("workstationRDN"),
@@ -200,7 +210,7 @@ class phoneAccount extends plugin
      * Search for all Macros that are visible and create 
      *  an array with name and parameters 
      */
-    $tmp = get_sub_list("(&(objectClass=goFonMacro)(goFonMacroVisible=1))","gofonmacro",array(get_ou("macroou")),
+    $tmp = get_sub_list("(&(objectClass=goFonMacro)(goFonMacroVisible=1))","gofonmacro",array(get_ou("phoneMacroRDN")),
                   $this->config->current['BASE'],array("displayName","goFonMacroParameter","dn","cn"), GL_NO_ACL_CHECK | GL_SUBSEARCH );
     
 
@@ -217,7 +227,7 @@ class phoneAccount extends plugin
       /* Skip all macros we are not able to read 
           execpt, the currently selected macro.
        */
-      if(!preg_match("/r/",$acl) && !preg_match("/^".normalizePreg($attrs['dn'])."/",$this->goFonMacro)){
+      if(!preg_match("/r/",$acl) && !preg_match("/^".preg_quote($attrs['dn'], '/')."/",$this->goFonMacro)){
         continue;
       }
 
@@ -238,23 +248,49 @@ class phoneAccount extends plugin
 
       /* Go through available parameters and parse all attributes, like parametername, type, default ...*/
       if((isset($attrs['goFonMacroParameter']))&&(is_array($attrs['goFonMacroParameter']))){
+          foreach($attrs['goFonMacroParameter'] as $pkey=>$pval){
+              /* Split Data in readable values, by delimiter !  */
+              $data = explode("!",$attrs['goFonMacroParameter'][$pkey]);
+
+              $string = $data[3];
+              $string=preg_replace("/%uid/i",$this->uid,$string);
+              $string=preg_replace("/%pager/i",$this->pager,$string);
+              $string=preg_replace("/%context/i",$this->context,$string);
+              $string=preg_replace("/%voicemailcontext/i",$this->voice_context,$string);
+
+              if(isset($this->cn)){
+                  $string=preg_replace("/%cn/i",$this->cn,$string);
+              }
 
-        foreach($attrs['goFonMacroParameter'] as $pkey=>$pval){
-          /* Split Data in readable values, by delimiter !  */
-          $data = split("!",$attrs['goFonMacroParameter'][$pkey]);
-
-          /* Set all attrs */
-          $id = $data[0];
-          $this->macroarray[$attrs['dn']][$id]['var']    ="var".$id;
-          $this->macroarray[$attrs['dn']][$id]['choosen']=$data[3]; 
-          $this->macroarray[$attrs['dn']][$id]['id']     =$id;
-          $this->macroarray[$attrs['dn']][$id]['name']   =$data[1];
-          $this->macroarray[$attrs['dn']][$id]['type']   =$data[2];
-          $this->macroarray[$attrs['dn']][$id]['default']=$data[3];
-          if($data[2] == "bool"){
-            $this->macroarray[$attrs['dn']][$id]['choosen']=$data[3];
-          }
-        }//foreach
+
+              $tmp = array();
+              foreach($this->phoneNumbers as $phonenum){
+                  $tmp[] = $phonenum;
+              }
+
+              for($i = 0 ; $i < 10; $i++){
+                  if(isset($tmp[$i])){
+                      $string = preg_replace("/%telephoneNumber_".($i+1)."/i",$tmp[$i],$string);
+                  }
+              }
+              if(isset($tmp[0])){
+                  $string = preg_replace("/%telephoneNumber/i",$tmp[0],$string);
+              }
+              $data[3] = $string;
+
+
+              /* Set all attrs */
+              $id = $data[0];
+              $this->macroarray[$attrs['dn']][$id]['var']    ="var".$id;
+              $this->macroarray[$attrs['dn']][$id]['choosen']=$data[3];
+              $this->macroarray[$attrs['dn']][$id]['id']     =$id;
+              $this->macroarray[$attrs['dn']][$id]['name']   =$data[1];
+              $this->macroarray[$attrs['dn']][$id]['type']   =$data[2];
+              $this->macroarray[$attrs['dn']][$id]['default']=$data[3];
+              if($data[2] == "bool"){
+                  $this->macroarray[$attrs['dn']][$id]['choosen']=$data[3];
+              }
+          }//foreach
       }//is_array
     }//while
 
@@ -263,7 +299,7 @@ class phoneAccount extends plugin
      * If we have a macro selected, parse it and set values 
      *  in $this->macroarray[$this->macro]. 
      */
-    $tmp = split("!",$this->goFonMacro);
+    $tmp = explode("!",$this->goFonMacro);
     if(is_array($tmp)){
 
       /* First value is the macroname */
@@ -283,7 +319,7 @@ class phoneAccount extends plugin
       foreach($tmp as $var){
 
         /* Split this, so we have $varar[0] = parameterID $varar[1] = SelectedValue */
-        $varar = split("#",$var);
+        $varar = explode("#",$var);
 
         /* Only insert if the parameter still exists */
         if(isset($this->macroarray[$this->macro][$varar[0]])){
@@ -296,12 +332,17 @@ class phoneAccount extends plugin
 
     $this->a_old_telenums = $this->phoneNumbers;
 
+
     /* Get voicemail PIN from MySQL DB 
      * Because every user can change his PIN directly from the phone
      *  without any update to the ldap
      * This means, the PIN in the DB is up to date
      */
     // Connect to DB server
+
+
+    $num = key($this->phoneNumbers);
+
     if( (is_callable("mysql_pconnect"))&&
         (isset($cur_cfg))&&
         (isset($cur_cfg['SERVER']))&&
@@ -310,19 +351,27 @@ class phoneAccount extends plugin
 
       $r_con =  @mysql_pconnect($cur_cfg['SERVER'],$cur_cfg['LOGIN'],$cur_cfg['PASSWORD']);
       if($r_con){
+
+        // Try to select the gophone database
         $r_db  =  @mysql_select_db($cur_cfg['DB'],$r_con);
-  
+        if(!$r_db){
+          msg_dialog::display(_("Warning"), msgPool::dbselect($cur_cfg['DB'],mysql_error()), WARNING_DIALOG);
+        }
+
         $query_tmp = "SELECT ".$cur_cfg['VOICE_TABLE'].".context as 'v_context', 
                              ".$cur_cfg['SIP_TABLE'].".context, 
                              ".$cur_cfg['VOICE_TABLE'].".password 
                        FROM  ".$cur_cfg['VOICE_TABLE'].", 
                              ".$cur_cfg['SIP_TABLE']." 
-                       WHERE customer_id = sip_users.mailbox AND name='".$this->uid."'";
-
-
+                       WHERE ".$cur_cfg['VOICE_TABLE'].".mailbox = ".$num." 
+                          AND ".$cur_cfg['SIP_TABLE'].".name='".$this->uid."'";
         $res = mysql_query($query_tmp);
         $vp  = mysql_fetch_assoc($res);
-  
+        if(!isset($vp['context'])){
+          $this->is_modified= TRUE;
+          msg_dialog::display(_("Warning"), sprintf(_("Cannot identify telephone extension in database, please try to save again.")), WARNING_DIALOG);
+        } 
         @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query_tmp, "Database query");
         if((isset($vp['password']))&&(!empty($vp['password']))){
           $this->goFonPINVoice = $vp['password'];
@@ -343,6 +392,66 @@ class phoneAccount extends plugin
   }
 
 
+  /* Transaction will only work with InnoDB tables 
+   */
+  public static function checkRealtimeTables($config)
+  {
+    $ret =TRUE;
+
+    // Connect to DB server
+    if( (is_callable("mysql_pconnect"))&&
+        (isset($config))&&
+        (isset($config['SERVER']))&&
+        (isset($config['LOGIN']))&&
+        (isset($config['PASSWORD']))){
+
+      $r_con =  @mysql_pconnect($config['SERVER'],$config['LOGIN'],$config['PASSWORD']);
+      if($r_con){
+        $r_db  =  @mysql_select_db($config['DB'],$r_con);
+
+        /* Validate Table Type - it must be InnoDB to be able to use transactions 
+         */
+        $inno_tables = array("SIP_TABLE","EXT_TABLE","VOICE_TABLE","QUEUE_TABLE","QUEUE_MEMBER_TABLE"); 
+        foreach($inno_tables as $inno_table){
+          $sql = "show table status like '".$config[$inno_table]."';";
+          $res = mysql_query($sql);
+          $vp  = mysql_fetch_assoc($res);
+          if(!preg_match("/^InnoDB$/i",$vp['Engine'])){
+
+            /* Not an InnoDB Table type, try to modify type. 
+             */
+            $sql = "ALTER TABLE `".$config[$inno_table]."` ENGINE = INNODB; ";
+            $res = mysql_query($sql);
+            if(!$res){
+              @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>".$sql."</b>", 
+                  "<b>FAILED!</b>. Transactions will not work!");
+              $ret = FALSE;
+            }else{
+              @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>".$sql."</b>", 
+                  "<i>Table '".$config[$inno_table]."' is now of type InnoDB, this enables transactions.</i>");
+            }
+          }else{
+            @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"",
+                "<i>Table type of '".$config[$inno_table]."' OK, using transactions!</i>");
+          }
+        }
+      }
+    }
+    return($ret);
+  }
+
+  function stripInvalidChars($tele){
+    /* Strip invalid chars, but maintain a leading + for international numbers */
+    $t_tele= preg_replace("/[^0-9]/","",$tele);
+    if (preg_match('/^\+/', $tele)) {
+      $t_tele= "+".$t_tele;
+    }
+    return($t_tele);
+  }
+    
+
+
+
   /* This function generates the Database entries. 
    * The Parameter 'save' could be true or false.
    *  false - means only testing no database transactions.
@@ -390,7 +499,8 @@ class phoneAccount extends plugin
     $s_host         = NULL;                   // Contains host for Sip entry
     $s_qualify      = "yes";                  // Qualify entry
     $s_pin          = NULL;                   // Entry for secret
-    $s_type         = NULL;                   // Entry for phone type (friend , peer ..)
+    $s_type         = "friend";               // Entry for phone type (friend , peer ..)
+                                              // Set the default to the default of the db
 
     $sip_data_array = array();                // Contains complete sip entry, to generate SQL syntax
     $i_old_key      = false;                  // Contains index for first old phonenumber, to delete old entries corectly
@@ -409,11 +519,12 @@ class phoneAccount extends plugin
 
     /* Prepare some basic attributes */
     $oldnums = array();
+
     foreach($this->a_old_telenums as $tele){
-      $oldnums[]= preg_replace("/[^0-9]/","",$tele);
+      $oldnums[]= $this->stripInvalidChars($tele);
     }
     foreach($this->phoneNumbers as $tele){
-      $newnums[]= preg_replace("/[^0-9]/","",$tele);
+      $newnums[]= $this->stripInvalidChars($tele);
     }
 
     if(empty($this->uid)) trigger_error("Uid is empty.");
@@ -434,6 +545,7 @@ class phoneAccount extends plugin
 
     /* Get phonehardware to setup sip entry  */
     $ldap         = $this->config->get_ldap_link();
+    $ldap->cd ($this->config->current['BASE']);
     $r_res        = $ldap->search("(&(objectClass=goFonHardware)(cn=".$this->goFonHardware."))", array('*'));
     $a_ldap_attrs = $ldap->fetch();
 
@@ -524,6 +636,7 @@ class phoneAccount extends plugin
       }
     }
 
+
     /* Save means that we must save changes, not only test  */
     if($save == true){
     
@@ -535,14 +648,29 @@ class phoneAccount extends plugin
        * If there is an old entry, get callerid and remove voicemail and extensions 
        */
       if($old_connection){
+
+        /* Check table definitions
+         */
+        if(!phoneAccount::checkRealtimeTables($a_Remove)){
+          msg_dialog::display(_("Warning"),
+              sprintf(_("GOsa identified problems with your MySQL table definition!")),
+              WARNING_DIALOG);
+        }
+
         $query  = "SELECT id,name,callerid FROM ".$a_Remove['SIP_TABLE']." WHERE name='".$this->uid."';";
         $rid    = mysql_query($query,$old_connection);
-        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Database query");
+        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, 
+            "<i>Reguest callerid to be able to identify the user.</i>");
 
         /* Old entry found, remove it */
         $query_a = array();
         if(mysql_affected_rows($old_connection)){
           $result = mysql_fetch_assoc($rid);
+
+          /* Set mode to strict
+             Strict disallows the addition of entries that do not match the targets field length.
+           */
+          $query_a[]= "SET @@sql_mode = STRICT_ALL_TABLES;";
           $query_a[]= "DELETE FROM ".$a_Remove['SIP_TABLE']." WHERE name='".$this->uid."';";
           $query_a[]= "DELETE FROM ".$a_Remove['VOICE_TABLE']." WHERE customer_id='".$result['callerid']."';";
           $query_a[]= "DELETE FROM ".$a_Remove['EXT_TABLE']." WHERE exten='".$this->uid."';";
@@ -550,30 +678,68 @@ class phoneAccount extends plugin
             $query_a[]= "DELETE FROM ".$a_Remove['EXT_TABLE']." WHERE exten='".$s_telenums."';";
           }
 
-          foreach($query_a as $qry){
-                 @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$qry, "Database query");
-            if(!mysql_query($qry,$old_connection)){
-              trigger_error(mysql_error($old_connection));
+          /* Start transaction, to be able to rollback 
+           */
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Removing entry from old server---</b>","");
+
+          mysql_query("begin;",$old_connection);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>begin;</b>","<i>Starting transaction!</i>");
+
+          foreach($query_a as $query){
+            @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>".$query."</b>", "");
+            if(!mysql_query($query,$old_connection)){
+              $err = mysql_error($old_connection);
+              @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"\n".$err, "<b>FAILED</b>");
+              msg_dialog::display(_("Error"), 
+                  msgPool::mysqlerror($err,__CLASS__)."&nbsp;".
+                  "\n<p>"._("Please activate debugging for details!")."</p>",
+                  ERROR_DIALOG);
+
+              mysql_query("rollback;",$old_connection);
+              @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>rollback;</b>", "<b>ERROR</b> Rollback transaction!");
+              @mysql_close($old_connection);
+              return(false);
             } 
           }
+
+          /* Let changes get active, everything was fine;
+           */ 
+          mysql_query("commit;",$old_connection);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>commit;</b>", "");
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Transaction sucessful!---</b>", "");
         }
       }
 
+
       /********************** 
        * Update / Insert sip_users entry  
        **********************/
 
+      /* Check table definitions
+       */
+      if(!phoneAccount::checkRealtimeTables($a_New)){
+        msg_dialog::display(_("Warning"),
+            sprintf(_("GOsa identified problems with your MySQL table definition!")),
+            WARNING_DIALOG);
+      }
+
+
       /* Set the first given phone number as callerid */
       reset($newnums);        
       $i_new_key = key($newnums);
       $sip_data_array['callerid']  =$newnums[$i_new_key];
-      $sip_data_array['mailbox']   =$newnums[$i_new_key];
+      $sip_data_array['mailbox']   =$newnums[$i_new_key]."@".$this->voice_context;
 
       /* Check if there is already an entry in sip_users for this uid */
       $SQL_query_array = array();
+
+      /* Enforce strict mode, ensures inout validation, e.g. target field length 
+       */
+      $SQL_query_array[] = "SET @@sql_mode = STRICT_ALL_TABLES;";
+
       $query = "SELECT * FROM ".$a_New['SIP_TABLE']." WHERE name='".$this->uid."';\n"; 
       $rid = mysql_query($query,$new_connection);
-      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Database query");
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Receive current mysql entries.");
       if(mysql_affected_rows($new_connection)){
 
         /********************** 
@@ -605,12 +771,11 @@ class phoneAccount extends plugin
           $SQL_query_array[] = $query;
         }
       } else {
+
         /********************** 
          * Insert sip_users entry 
          **********************/
         //generate SIP entry
-        $sip_data_array['id']           = "";
         $sip_data_array['name']         = $this->uid;
         $sip_data_array['accountcode']  = NULL;          
         $sip_data_array['amaflags']     = NULL;
@@ -623,7 +788,7 @@ class phoneAccount extends plugin
         $sip_data_array['host']         = $s_host;
         $sip_data_array['insecure']     = NULL;
         $sip_data_array['language']     = NULL;
-        $sip_data_array['mailbox']      = $newnums[$i_new_key];
+        $sip_data_array['mailbox']      = $newnums[$i_new_key]."@".$this->voice_context;
         $sip_data_array['md5secret']    = NULL;
         $sip_data_array['nat']          = "no";
         $sip_data_array['permit']       = NULL;
@@ -667,13 +832,12 @@ class phoneAccount extends plugin
        **********************/
 
       $customer_id = $newnums[$i_new_key];
-
       $query  = "SELECT id,name,callerid FROM ".$a_New['SIP_TABLE']." WHERE name='".$this->uid."';";
-
       $rid    = mysql_query($query,$new_connection);
-      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Database query");
+
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Receive callerid");
       $result = mysql_fetch_assoc($rid);
-  
+
       $old_customer_id = ""; 
       if($result){
         $old_customer_id = $result['callerid'];
@@ -691,16 +855,16 @@ class phoneAccount extends plugin
 
       /* Check if there is already an entry in sip_users for this uid */
       $query_tmp = "SELECT * FROM ".$a_New['VOICE_TABLE']." WHERE customer_id='".$old_customer_id."';\n";
-
-
       $rid = mysql_query($query_tmp,$new_connection);
-      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query_tmp, "Database query");
+
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query_tmp, "Check if voicemail entry exists");
       if(mysql_affected_rows($new_connection)){
 
         /********************** 
          * Update Voice mail entry  
          **********************/
 
+        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"", "<i>Voicemail entry exists, adding updating to queue.</i>");
         $result = mysql_fetch_assoc($rid)  ;
 
         foreach($voice_data_array as $name => $value){
@@ -724,8 +888,9 @@ class phoneAccount extends plugin
         /********************** 
          * Insert Voice mail entry  
          **********************/
+        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"", "<i>No voicemail entry found, add 'create' to queue.</i>");
         $voice_data_array['context'] = $this->voice_context;
-  
+
         /* There is currently no voice mail entry for this user. 
          * We should create one 
          */
@@ -742,40 +907,34 @@ class phoneAccount extends plugin
         $SQL_query_array[] ="INSERT INTO ".$a_New['VOICE_TABLE']." (".$s_voi_keys.") VALUES (".$s_voi_values.");";
       }
 
-     
+
       /********************** 
        * Remove/Insert extension entries
        **********************/
-      
-      /* Remove old entries */
-      $query = array();
-      $query[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$this->uid."\";";
+
+      /* Initiate transaction 
+       */
+      $SQL_query_array[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$this->uid."\";";
       $oldnums= array();
       foreach($oldnums as $s_telenums){
-        $query[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$s_telenums."\";";
+        $SQL_query_array[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$s_telenums."\";";
       }
       foreach($newnums as $s_telenums){
-        $query[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$s_telenums."\";";
+        $SQL_query_array[]= "DELETE FROM ".$a_New['EXT_TABLE']." WHERE exten=\"".$s_telenums."\";";
       }
-      foreach($query as $qry){
-        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$qry, "Database query");
-        if(!mysql_query($qry,$new_connection)){
-          trigger_error(mysql_error($new_connection));
-        } 
-      }
+
       /********************** 
        * Insert extension entries
        **********************/
+
       // Get selected Macro Parameter and create parameter entry 
       if(isset($this->macroarray[$this->macro])){
         foreach($this->macroarray[$this->macro] as $key => $val ){
-          $s_parameter .= $val['choosen']."|";
+          $s_parameter .= $val['choosen'].$this->delimiter;
         }
-        $s_parameter = preg_replace("/\|$/","",$s_parameter);
+        $s_parameter = rtrim($s_parameter, $this->delimiter);
       }
-     
+
       $i = 0; 
       $EXT = array();
       if(!is_numeric($this->uid)){
@@ -783,7 +942,7 @@ class phoneAccount extends plugin
         $EXT[$i]['exten']   = $this->uid;
         $EXT[$i]['priority']= 1;
         $EXT[$i]['app']     = "Goto";
-        $EXT[$i]['appdata'] = $newnums[$i_new_key]."|1";
+        $EXT[$i]['appdata'] = $newnums[$i_new_key].$this->delimiter."1";
         $i ++;
       }
 
@@ -793,7 +952,7 @@ class phoneAccount extends plugin
         /* Hint Entry */
         $EXT[$i]['context'] = 'GOsa';
         $EXT[$i]['exten']   = $s_telenums;
-        $EXT[$i]['priority']= "Hint";
+        $EXT[$i]['priority']= 0;
         $EXT[$i]['app']     = 'SIP/'.$this->uid;
         $i ++;  
         /* SetCID */
@@ -809,10 +968,10 @@ class phoneAccount extends plugin
           $macroname = preg_replace("/,.*$/","",$this->macro);        
           $macroname = preg_replace("/^.*=/","",$macroname);        
           $s_app = "Macro";$macroname;
-          $s_par = $macroname."|".$s_parameter; 
+          $s_par = $macroname.$this->delimiter.$s_parameter; 
         }else{
           $s_app = "Dial";
-          $s_par = 'SIP/'.$this->uid."|20|r";
+          $s_par = 'SIP/'.$this->uid.$this->delimiter."20".$this->delimiter."r";
         }
 
         $EXT[$i]['context'] = 'GOsa';
@@ -841,16 +1000,35 @@ class phoneAccount extends plugin
         $SQL_syn ="";
       }
 
-      // Perform queries ...
-      if($this->goFonHardware != "automatic"){
-        foreach($SQL_query_array as $query){
-          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Database query");
-          if(!@mysql_query($query,$new_connection)){
-            $this->generate_error =  msgPool::dbquery("GOfon",@mysql_error($old_connection));
-            return false;
-          }
-        }
+      /* Start transaction, to be able to rollback 
+       */
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Insert/Update new entry---</b>","");
+
+      mysql_query("begin;",$new_connection);
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>begin;</b>","<i>Starting transaction!</i>");
+
+      foreach($SQL_query_array as $query){
+        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>".$query."</b>", "");
+        if(!mysql_query($query,$new_connection)){
+          $err = mysql_error($new_connection);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"\n".$err, "<b>FAILED</b>");
+          msg_dialog::display(_("Error"), 
+              msgPool::mysqlerror($err,__CLASS__)."&nbsp;".
+              "\n<p>"._("Please activate debugging for details!")."</p>",
+              ERROR_DIALOG);
+
+          mysql_query("rollback;",$new_connection);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>rollback;</b>", "<b>ERROR</b> Rollback transaction!");
+          @mysql_close($new_connection);
+          return(false);
+        } 
       }
+     
+      /* Let changes get active, everything was fine;
+       */ 
+      mysql_query("commit;",$new_connection);
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>commit;</b>", "Perform transaction!");
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Transaction sucessful!---</b>", "");
     }
     @mysql_close($new_connection);
     return true;
@@ -874,7 +1052,7 @@ class phoneAccount extends plugin
     if(empty($this->macro)&&(!empty($this->goFonMacro))){
 
       /* Go through already saved values, for a parameter */
-      $tmp = split("!",$this->goFonMacro);
+      $tmp = explode("!",$this->goFonMacro);
 
       /* it is possible that nothing has been saved yet */
       if(is_array($tmp)){
@@ -896,7 +1074,7 @@ class phoneAccount extends plugin
         foreach($tmp as $var){
 
           /* Split this, so we have $varar[0] = parameterID $varar[1] = SelectedValue */
-          $varar = split("#",$var);
+          $varar = explode("#",$var);
 
           /* Only insert if the parameter still exists */
           if(isset($this->macroarray[$this->macro][$varar[0]])){
@@ -965,7 +1143,8 @@ class phoneAccount extends plugin
     $smarty->assign("macro", $this->macro);   
 
     /* Assign contexts */
-    $smarty->assign("contexts",$this->get_asterisk_contexts());
+    $smarty->assign("voicemail_contexts",$this->voicemail_contexts);
+    $smarty->assign("sip_contexts",$this->sip_contexts);
     $smarty->assign("context" ,$this->context);
     $smarty->assign("voice_context" ,$this->voice_context);
 
@@ -995,6 +1174,9 @@ class phoneAccount extends plugin
           $string = $paras['default'];
 
           $string=preg_replace("/%uid/i",$this->uid,$string);
+          $string=preg_replace("/%pager/i",$this->pager,$string);
+          $string=preg_replace("/%context/i",$this->context,$string);
+          $string=preg_replace("/%voicemailcontext/i",$this->voice_context,$string);
 
           if(isset($this->cn)){
             $string=preg_replace("/%cn/i",$this->cn,$string);
@@ -1033,7 +1215,7 @@ class phoneAccount extends plugin
 
           case "combo":
             $str= "<select name='".$var."' ".$dis." >";
-          foreach(split(":",$default) as $choice){
+          foreach(explode(":",$default) as $choice){
             if($choosen==$choice){
               $str.= "\n<option value='".$choice."' selected>".$choice."&nbsp;</option>";
             }else{
@@ -1071,8 +1253,11 @@ class phoneAccount extends plugin
 
     /* Add phone number */
     if (isset($_POST["add_phonenumber"]) && $_POST['phonenumber']){
-      if (tests::is_phone_nr($_POST['phonenumber'])){
-        $number= $_POST["phonenumber"];
+
+      if(strlen(trim($_POST["phonenumber"])) > 20 ){
+        msg_dialog::display(_("Error"), msgPool::toobig("Phone number"), ERROR_DIALOG);
+      }elseif (tests::is_phone_nr($_POST['phonenumber'])){
+        $number= trim($_POST["phonenumber"]);
         $this->phoneNumbers[$number]= $number;
         $this->is_modified= TRUE;
       } else {
@@ -1145,7 +1330,7 @@ class phoneAccount extends plugin
 
 
     foreach($this->attributes as $attr){
-      if(in_array($attr,$this->multi_boxes)){
+      if(in_array_strict($attr,$this->multi_boxes)){
         $smarty->assign("use_".$attr,TRUE);
       }else{
         $smarty->assign("use_".$attr,FALSE);
@@ -1153,7 +1338,7 @@ class phoneAccount extends plugin
     }
 
     foreach(array("goFonVoiceMailContext","goFonContext") as $attr){
-      if(in_array($attr,$this->multi_boxes)){
+      if(in_array_strict($attr,$this->multi_boxes)){
         $smarty->assign("use_".$attr,TRUE);
       }else{
         $smarty->assign("use_".$attr,FALSE);
@@ -1243,9 +1428,9 @@ class phoneAccount extends plugin
             if(isset($_POST['post_success'])){
               if($this->macroarray[$this->macro][$key]['type']=="bool"){
                 if(isset($_POST[$this->macroarray[$this->macro][$key]['var']])) {
-                  $this->macroarray[$this->macro][$key]['choosen']=$_POST[$paras['var']];
+                  $this->macroarray[$this->macro][$key]['choosen']=1;
                 }else{
-                  $this->macroarray[$this->macro][$key]['choosen']=false;
+                  $this->macroarray[$this->macro][$key]['choosen']=0;
                 }
               }
             }
@@ -1288,8 +1473,9 @@ class phoneAccount extends plugin
     }
 
     if ($this->initially_was_account != $this->is_account || $this->is_modified){
-      if(!$this->generate_mysql_entension_entries()){
-        $message[] = $this->generate_error;
+      $str = $this->generate_mysql_entension_entries(false);
+      if(empty($str)){
+        msg_dialog::display(_("Error"), $str, ERROR_DIALOG);
       }
     }
 
@@ -1298,6 +1484,27 @@ class phoneAccount extends plugin
       $message[]= msgPool::required("Phone number");
     }
 
+    /* Do not allow to save duplicate phone numbers 
+     *  this may destroy the extensions table.
+     */ 
+    $ldap = $this->config->get_ldap_link();
+    $ldap->cd($this->config->current['BASE']);  
+    $numberFilter = "";
+    foreach($this->phoneNumbers as $number){
+      $numberFilter .= "(telephoneNumber={$number})";
+    }
+    $ldap->search("(&(!(uid=".$this->uid."))(objectClass=goFonAccount)(|{$numberFilter}))",array("dn","telephoneNumber"));
+    $res = array();
+    while($attrs = $ldap->fetch()){
+      unset($attrs['telephoneNumber']['count']);
+      $res = array_merge($res,array_intersect($attrs['telephoneNumber'], $this->phoneNumbers));
+    }
+    $res = array_unique($res);
+    if(count($res)){
+      $message[] = msgPool::duplicated(_("Phone number"))."&nbsp;<br>".
+        implode(array_intersect($res, $this->phoneNumbers), ", ");
+    }
+
     /* check for ! in any parameter setting*/
     if(isset($this->macroarray[$this->macro])){
       foreach($this->macroarray[$this->macro] as $val){
@@ -1346,11 +1553,11 @@ class phoneAccount extends plugin
 
     $this->attrs['goFonForwarding']=array();
 
-    if ($this->initially_was_account != $this->is_account || $this->is_modified){
-      $str = $this->generate_mysql_entension_entries(true);
-      if(empty($str)){
-        msg_dialog::display(_("Error"), $str, ERROR_DIALOG);
-      }
+    /*
+     */
+    $str = $this->generate_mysql_entension_entries(true);
+    if(!$str){
+      msg_dialog::display(_("Error"),_("An error occured while updating the database entries!") , ERROR_DIALOG);
     }
 
     if($this->attrs['goFonMacro']==""){
@@ -1397,7 +1604,7 @@ class phoneAccount extends plugin
     plugin::adapt_from_template($dn, $skip);
 
     /* Assemble phone numbers */
-    if (isset($this->attrs['telephoneNumber']) && !in_array("telephoneNumber", $skip)){
+    if (isset($this->attrs['telephoneNumber']) && !in_array_strict("telephoneNumber", $skip)){
       for ($i= 0; $i<$this->attrs['telephoneNumber']['count']; $i++){
         $number= $this->attrs['telephoneNumber'][$i];
         $this->phoneNumbers[$number]= $number;
@@ -1416,6 +1623,14 @@ class phoneAccount extends plugin
       $a_SETUP = $this->goFonHomeServers[$this->init_HomeServer];
       $s_parameter  ="";
 
+      /* Check table definitions
+       */
+      if(!phoneAccount::checkRealtimeTables($a_SETUP)){
+        msg_dialog::display(_("Warning"),
+            sprintf(_("GOsa identified problems with your MySQL table definition!")),
+            WARNING_DIALOG);
+      }
+
       // Connect to DB server
       $r_con =  @mysql_pconnect($a_SETUP['SERVER'],$a_SETUP['LOGIN'],$a_SETUP['PASSWORD']);
 
@@ -1437,7 +1652,7 @@ class phoneAccount extends plugin
       }
 
       $SQL="";
-
+      $SQL[]= "SET @@sql_mode = STRICT_ALL_TABLES;";
 
       $first_num = false;
       // Delete old entries
@@ -1458,17 +1673,44 @@ class phoneAccount extends plugin
         $callerid = $result['callerid'];
       }
 
+      /* Set mode to strict
+         Strict disallows the addition of entries that do not match the targets field length.
+       */
       $SQL[] = "DELETE FROM ".$a_SETUP['VOICE_TABLE']." WHERE customer_id='".$callerid."';";
       $SQL[] = "DELETE FROM ".$a_SETUP['EXT_TABLE']." WHERE exten='".$this->uid."';\n";
       $SQL[] = "DELETE FROM ".$a_SETUP['SIP_TABLE']." WHERE name='".$this->uid."';\n";
 
+      /* Start transaction, to be able to rollback
+       */
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Removing entry from server---</b>","");
+
+      mysql_query("begin;",$r_con);
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>begin;</b>","<i>Starting transaction!</i>");
+
       foreach($SQL as $query){
-        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,$query, "Database query");
-        if(!@mysql_query($query,$r_con)){
-          msg_dialog::display(_("Error"), msgPool::dbquery("GOfon",@mysql_error()), ERROR_DIALOG);
-          return false;
+        @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>".$query."</b>", "");
+
+        if(!mysql_query($query,$r_con)){
+          $err = mysql_error($r_con);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"\n".$err, "<b>FAILED</b>");
+          msg_dialog::display(_("Error"),
+              msgPool::mysqlerror($err,__CLASS__)."&nbsp;".
+              "\n<p>"._("Please activate debugging for details!")."</p>",
+              ERROR_DIALOG);
+
+          mysql_query("rollback;",$r_con);
+          @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>rollback;</b>", "<b>ERROR</b> Rollback transaction!");
+          @mysql_close($r_con);
+          return(false);
         }
       }
+
+      /* Let changes get active, everything was fine;
+       */
+      mysql_query("commit;",$r_con);
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>commit;</b>", "");
+      @DEBUG (DEBUG_MYSQL, __LINE__, __FUNCTION__, __FILE__,"<b>---Transaction sucessful!---</b>", "");
+
     }else{
       msg_dialog::display(_("Configuration error"), msgPool::missingext("php-mysql"), WARNING_DIALOG);
       return false;
@@ -1498,7 +1740,7 @@ class phoneAccount extends plugin
     $ldap->cd($this->config->current['BASE']);
     $ldap->search("(&(objectClass=goFonQueue)(member=*))", array("member"));
     while($attr = $ldap->fetch()){
-      if(in_array($this->dn,$attr['member'])){
+      if(in_array_strict($this->dn,$attr['member'])){
         $new =new ogrouptabs($this->config, $this->config->data['TABS']['OGROUPTABS'],$attr['dn']);
         unset($new->by_object['ogroup']->memberList[$this->dn]);
         unset($new->by_object['ogroup']->member[$this->dn]);
@@ -1696,30 +1938,30 @@ class phoneAccount extends plugin
   {
     $message = plugin::multiple_check();
 
-    if(!count($this->goFonHomeServers) && in_array("goFonHomeServers",$this->multi_boxes)){
+    if(!count($this->goFonHomeServers) && in_array_strict("goFonHomeServers",$this->multi_boxes)){
       $message[] = _("There is currently no asterisk server defined!");
     }
 
-    if(empty($this->goFonHomeServer) && in_array("goFonHomeServers",$this->multi_boxes)){
+    if(empty($this->goFonHomeServer) && in_array_strict("goFonHomeServers",$this->multi_boxes)){
       $message[] = _("Asterisk server is invalid!");
     }
 
-    if(in_array("goFonVoicemailPIN",$this->multi_boxes) && 
+    if(in_array_strict("goFonVoicemailPIN",$this->multi_boxes) && 
         ( (strlen($this->goFonVoicemailPIN)==0)||
           (strlen($this->goFonVoicemailPIN)>4))){
       $message[]=(_("Voicemail PIN must be 4 characters long!"));
     }else{
-      if(preg_match("/[^0-9]/",$this->goFonVoicemailPIN) && in_array("goFonVoicemailPIN",$this->multi_boxes) ){
+      if(preg_match("/[^0-9]/",$this->goFonVoicemailPIN) && in_array_strict("goFonVoicemailPIN",$this->multi_boxes) ){
         $message[]=(_("Voicemail PIN contains invalid characters!"));
       }
     }
 
-    if(preg_match("/[^0-9a-z]/i",$this->goFonPIN) && in_array("goFonPIN",$this->multi_boxes)){
+    if(preg_match("/[^0-9a-z]/i",$this->goFonPIN) && in_array_strict("goFonPIN",$this->multi_boxes)){
       $message[]=(_("Phone pin contains invalid characters!"));
     }
 
     /* check for ! in any parameter setting*/
-    if(isset($this->macroarray[$this->macro]) && in_array("macro",$this->multi_boxes)){
+    if(isset($this->macroarray[$this->macro]) && in_array_strict("macro",$this->multi_boxes)){
       foreach($this->macroarray[$this->macro] as $val){
         if((strstr($val['choosen'],"!"))||(strstr($val['choosen'],"#"))){
           $message[] = sprintf(_("The parameter %s contains invalid char. '!,#' is used as delimiter"),$val['name']);
@@ -1733,7 +1975,7 @@ class phoneAccount extends plugin
   function get_multi_edit_values()
   {
     $ret = plugin::get_multi_edit_values();
-    if(in_array("macro",$this->multi_boxes)){
+    if(in_array_strict("macro",$this->multi_boxes)){
       $ret['macro'] = $this->macro;
       $ret['macroarray'] = $this->macroarray;
       $ret['macros'] = $this->macros;
@@ -1745,18 +1987,24 @@ class phoneAccount extends plugin
   /* Return asterisk contexts
    * Additionaly read contexts from file.
    */
-  function get_asterisk_contexts()
+  function get_asterisk_voicemail_contexts()
+  {
+    return($this->get_asterisk_contexts(CONFIG_DIR."/asterisk/voicemail_context.conf"));
+  }
+  function get_asterisk_sip_contexts()
+  {
+    return($this->get_asterisk_contexts(CONFIG_DIR."/asterisk/sip_context.conf"));
+  }
+  function get_asterisk_contexts($file)
   {
     $contexts = array();
-    $contexts[] = "default";
-    $contexts[] = "parkedcalls";
-    $contexts[] = "from-sip";
-    $contexts[] = "from-capi";
-    $file = "/etc/gosa/asterisk_contexts.conf";
     if(file_exists($file) && is_readable($file)){
       foreach(file($file) as $context){
         $contexts[] = trim($context);
       }
+    }else{
+      msg_dialog::display(_("Warning"), msgPool::cannotReadFile($file),WARNING_DIALOG);
+      $contexts[] = "default";
     }
     array_unique($contexts);
     return($contexts);