Index: CHANGELOG
===================================================================
--- CHANGELOG	(revision d3105370870a8c51aaeb6a18f561311202da3356)
+++ CHANGELOG	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -2,4 +2,6 @@
 ===========================
 
+- PEAR::Net_SMTP 1.5.2, fixed timeout issue (#1487843)
+- Fix bug where template name without plugin prefix was used in render_page hook
 - Support 'abort' and 'result' response in 'preferences_save' hook, add error handling
 - Fix bug where some content would cause hang on html2text conversion (#1487863)
@@ -9,5 +11,4 @@
 - Fix bug where default_charset was not used for text messages (#1487836)
 - Stateless request tokens. No keep-alive necessary on login page (#1487829)
-- PEAR::Net_SMTP 1.5.1
 - Force names of unique constraints in PostgreSQL DDL
 - Add code for prevention from IMAP connection hangs when server closes socket unexpectedly
@@ -17,5 +18,5 @@
 - Fix handling of mime-encoded words with non-integral number of octets in a word (#1487801)
 - Fix parsing links with non-printable characters inside (#1487805)
-- Fixed de_CH Localization bugs (#1487773)
+- Fixed de_CH/de_DE localization bugs (#1487773)
 - Add variable for 'Today' label in date_today option (#1486120)
 - Applied plugin changes since 0.5-stable release
Index: program/include/rcube_smtp.php
===================================================================
--- program/include/rcube_smtp.php	(revision b46e5b7407940499964d8a553c3eada05850f29d)
+++ program/include/rcube_smtp.php	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -106,5 +106,5 @@
     $this->conn = new Net_SMTP($smtp_host, $smtp_port, $helo_host);
 
-    if($RCMAIL->config->get('smtp_debug'))
+    if ($RCMAIL->config->get('smtp_debug'))
       $this->conn->setDebug(true, array($this, 'debug_handler'));
 
@@ -117,4 +117,11 @@
       $this->conn = null;
       return false;
+    }
+
+    // workaround for timeout bug in Net_SMTP 1.5.[0-1] (#1487843)
+    if (method_exists($this->conn, 'setTimeout')
+      && ($timeout = ini_get('default_socket_timeout'))
+    ) {
+      $this->conn->setTimeout($timeout);
     }
 
Index: program/include/rcube_template.php
===================================================================
--- program/include/rcube_template.php	(revision b46e5b7407940499964d8a553c3eada05850f29d)
+++ program/include/rcube_template.php	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -374,14 +374,17 @@
     {
         $skin_path = $this->config['skin_path'];
-        $plugin = false;
+        $plugin    = false;
+        $realname  = $name;
+        $temp      = explode('.', $name, 2);
         $this->plugin_skin_path = null;
 
-        $temp = explode(".", $name, 2);
         if (count($temp) > 1) {
-            $plugin = $temp[0];
-            $name = $temp[1];
-            $skin_dir = $plugin . '/skins/' . $this->config['skin'];
+            $plugin    = $temp[0];
+            $name      = $temp[1];
+            $skin_dir  = $plugin . '/skins/' . $this->config['skin'];
             $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir;
-            if (!is_dir($skin_path)) {  // fallback to default skin
+
+            // fallback to default skin
+            if (!is_dir($skin_path)) {
                 $skin_dir = $plugin . '/skins/default';
                 $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir;
@@ -391,10 +394,11 @@
         $path = "$skin_path/templates/$name.html";
 
-        if (!is_readable($path) && $this->deprecated_templates[$name]) {
-            $path = "$skin_path/templates/".$this->deprecated_templates[$name].".html";
+        if (!is_readable($path) && $this->deprecated_templates[$realname]) {
+            $path = "$skin_path/templates/".$this->deprecated_templates[$realname].".html";
             if (is_readable($path))
                 raise_error(array('code' => 502, 'type' => 'php',
                     'file' => __FILE__, 'line' => __LINE__,
-                    'message' => "Using deprecated template '".$this->deprecated_templates[$name]."' in ".$this->config['skin_path']."/templates. Please rename to '".$name."'"),
+                    'message' => "Using deprecated template '".$this->deprecated_templates[$realname]
+                        ."' in ".$this->config['skin_path']."/templates. Please rename to '".$realname."'"),
                 true, false);
         }
@@ -407,5 +411,5 @@
                 'line' => __LINE__,
                 'file' => __FILE__,
-                'message' => 'Error loading template for '.$name
+                'message' => 'Error loading template for '.$realname
                 ), true, true);
             return false;
@@ -423,5 +427,5 @@
 
         // trigger generic hook where plugins can put additional content to the page
-        $hook = $this->app->plugins->exec_hook("render_page", array('template' => $name, 'content' => $output));
+        $hook = $this->app->plugins->exec_hook("render_page", array('template' => $realname, 'content' => $output));
 
         // add debug console
Index: program/js/app.js
===================================================================
--- program/js/app.js	(revision d3105370870a8c51aaeb6a18f561311202da3356)
+++ program/js/app.js	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -4651,15 +4651,16 @@
   this.set_message_coltypes = function(coltypes, repl)
   {
+    var list = this.message_list,
+      thead = list ? list.list.tHead : null,
+      cell, col, n, len, th, tr;
+
     this.env.coltypes = coltypes;
-
-    // set correct list titles
-    var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null,
-      cell, col, n, len;
 
     // replace old column headers
     if (thead) {
       if (repl) {
-        var th = document.createElement('thead'),
-          tr = document.createElement('tr');
+        th = document.createElement('thead');
+        tr = document.createElement('tr');
+
         for (c=0, len=repl.length; c < len; c++) {
           cell = document.createElement('td');
@@ -4695,6 +4696,6 @@
     if ((n = $.inArray('subject', this.env.coltypes)) >= 0) {
       this.set_env('subject_col', n);
-      if (this.message_list)
-        this.message_list.subject_col = n;
+      if (list)
+        list.subject_col = n;
     }
     if ((n = $.inArray('flag', this.env.coltypes)) >= 0)
@@ -4703,5 +4704,6 @@
       this.set_env('status_col', n);
 
-    this.message_list.init_header();
+    if (list)
+      list.init_header();
   };
 
Index: program/js/list.js
===================================================================
--- program/js/list.js	(revision 1ce4420b7ee91c23be4e6a7f799794901f4dead7)
+++ program/js/list.js	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -103,6 +103,6 @@
   // make references in internal array and set event handlers
   if (row && String(row.id).match(/rcmrow([a-z0-9\-_=\+\/]+)/i)) {
-    var self = this;
-    var uid = RegExp.$1;
+    var self = this,
+      uid = RegExp.$1;
     row.uid = uid;
     this.rows[uid] = {uid:uid, id:row.id, obj:row};
@@ -171,4 +171,8 @@
   if (sel)
     this.clear_selection();
+
+  // reset scroll position (in Opera)
+  if (this.frame)
+    this.frame.scrollTop = 0;
 },
 
@@ -213,8 +217,8 @@
 focus: function(e)
 {
-  var id;
+  var n, id;
   this.focused = true;
 
-  for (var n in this.selection) {
+  for (n in this.selection) {
     id = this.selection[n];
     if (this.rows[id] && this.rows[id].obj) {
@@ -237,7 +241,7 @@
 blur: function()
 {
-  var id;
+  var n, id;
   this.focused = false;
-  for (var n in this.selection) {
+  for (n in this.selection) {
     id = this.selection[n];
     if (this.rows[id] && this.rows[id].obj) {
@@ -431,6 +435,5 @@
 expand: function(row)
 {
-  var depth, new_row;
-  var last_expanded_parent_depth;
+  var r, p, depth, new_row, last_expanded_parent_depth;
 
   if (row) {
@@ -450,5 +453,5 @@
   while (new_row) {
     if (new_row.nodeType == 1) {
-      var r = this.rows[new_row.uid];
+      r = this.rows[new_row.uid];
       if (r) {
         if (row && (!r.depth || r.depth <= depth))
@@ -456,5 +459,5 @@
 
         if (r.parent_uid) {
-          var p = this.rows[r.parent_uid];
+          p = this.rows[r.parent_uid];
           if (p && p.expanded) {
             if ((row && p == row) || last_expanded_parent_depth >= p.depth - 1) {
@@ -697,7 +700,8 @@
 select_next: function()
 {
-  var next_row = this.get_next_row();
-  var prev_row = this.get_prev_row();
-  var new_row = (next_row) ? next_row : prev_row;
+  var next_row = this.get_next_row(),
+    prev_row = this.get_prev_row(),
+    new_row = (next_row) ? next_row : prev_row;
+
   if (new_row)
     this.select_row(new_row.uid, false, false);
@@ -711,11 +715,14 @@
 {
   var row = this.get_first_row();
-  if (row && mod_key) {
-    this.shift_select(row, mod_key);
-    this.triggerEvent('select');
-    this.scrollto(row);
-  }
-  else if (row)
-    this.select(row);
+  if (row) {
+    if (mod_key) {
+      this.shift_select(row, mod_key);
+      this.triggerEvent('select');
+      this.scrollto(row);
+    }
+    else {
+      this.select(row);
+    }
+  }
 },
 
@@ -727,11 +734,14 @@
 {
   var row = this.get_last_row();
-  if (row && mod_key) {
-    this.shift_select(row, mod_key);
-    this.triggerEvent('select');
-    this.scrollto(row);
-  }
-  else if (row)
-    this.select(row);
+  if (row) {
+    if (mod_key) {
+      this.shift_select(row, mod_key);
+      this.triggerEvent('select');
+      this.scrollto(row);
+    }
+    else {
+      this.select(row);
+    }
+  }
 },
 
@@ -745,6 +755,7 @@
     return;
 
-  var depth = this.rows[uid].depth;
-  var row = this.rows[uid].obj.nextSibling;
+  var depth = this.rows[uid].depth,
+    row = this.rows[uid].obj.nextSibling;
+
   while (row) {
     if (row.nodeType == 1) {
@@ -769,5 +780,5 @@
     this.shift_start = id;
 
-  var from_rowIndex = this.rows[this.shift_start].obj.rowIndex,
+  var n, from_rowIndex = this.rows[this.shift_start].obj.rowIndex,
     to_rowIndex = this.rows[id].obj.rowIndex,
     i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex),
@@ -775,5 +786,5 @@
 
   // iterate through the entire message list
-  for (var n in this.rows) {
+  for (n in this.rows) {
     if (this.rows[n].obj.rowIndex >= i && this.rows[n].obj.rowIndex <= j) {
       if (!this.in_selection(n)) {
@@ -782,5 +793,5 @@
     }
     else {
-      if  (this.in_selection(n) && !control) {
+      if (this.in_selection(n) && !control) {
         this.highlight_row(n, true);
       }
@@ -795,5 +806,5 @@
 in_selection: function(id)
 {
-  for(var n in this.selection)
+  for (var n in this.selection)
     if (this.selection[n]==id)
       return true;
@@ -812,8 +823,8 @@
 
   // reset but remember selection first
-  var select_before = this.selection.join(',');
+  var n, select_before = this.selection.join(',');
   this.selection = [];
 
-  for (var n in this.rows) {
+  for (n in this.rows) {
     if (!filter || this.rows[n][filter] == true) {
       this.last_selected = n;
@@ -844,7 +855,7 @@
 
   // remember old selection
-  var select_before = this.selection.join(',');
-
-  for (var n in this.rows)
+  var n, select_before = this.selection.join(',');
+
+  for (n in this.rows)
     this.highlight_row(n, true);
 
@@ -864,9 +875,9 @@
 clear_selection: function(id)
 {
-  var num_select = this.selection.length;
+  var n, num_select = this.selection.length;
 
   // one row
   if (id) {
-    for (var n in this.selection)
+    for (n in this.selection)
       if (this.selection[n] == id) {
         this.selection.splice(n,1);
@@ -876,5 +887,5 @@
   // all rows
   else {
-    for (var n in this.selection)
+    for (n in this.selection)
       if (this.rows[this.selection[n]]) {
         $(this.rows[this.selection[n]].obj).removeClass('selected').removeClass('unfocused');
@@ -928,7 +939,8 @@
     }
     else { // unselect row
-      var p = $.inArray(id, this.selection);
-      var a_pre = this.selection.slice(0, p);
-      var a_post = this.selection.slice(p+1, this.selection.length);
+      var p = $.inArray(id, this.selection),
+        a_pre = this.selection.slice(0, p),
+        a_post = this.selection.slice(p+1, this.selection.length);
+
       this.selection = a_pre.concat(a_post);
       $(this.rows[id].obj).removeClass('selected').removeClass('unfocused');
@@ -946,6 +958,6 @@
     return true;
 
-  var keyCode = rcube_event.get_keycode(e);
-  var mod_key = rcube_event.get_modifier(e);
+  var keyCode = rcube_event.get_keycode(e),
+    mod_key = rcube_event.get_modifier(e);
 
   switch (keyCode) {
@@ -1372,5 +1384,5 @@
 column_replace: function(from, to)
 {
-  var cells = this.list.tHead.rows[0].cells,
+  var len, cells = this.list.tHead.rows[0].cells,
     elem = cells[from],
     before = cells[to],
@@ -1385,5 +1397,5 @@
 
   // replace list cells
-  for (r=0; r<this.list.tBodies[0].rows.length; r++) {
+  for (r=0, len=this.list.tBodies[0].rows.length; r<len; r++) {
     row = this.list.tBodies[0].rows[r];
 
Index: program/lib/Net/SMTP.php
===================================================================
--- program/lib/Net/SMTP.php	(revision 5228a5558f0ee9af785f1b4cdcef4d97b17b33f6)
+++ program/lib/Net/SMTP.php	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -107,4 +107,12 @@
 
     /**
+     * Array of socket options that will be passed to Net_Socket::connect().
+     * @see stream_context_create()
+     * @var array
+     * @access private
+     */
+    var $_socket_options = null;
+
+    /**
      * The socket I/O timeout value in seconds.
      * @var int
@@ -157,4 +165,5 @@
      * @param boolean $pipeling   Use SMTP command pipelining
      * @param integer $timeout    Socket I/O timeout in seconds.
+     * @param array   $socket_options Socket stream_context_create() options.
      *
      * @access  public
@@ -162,5 +171,5 @@
      */
     function Net_SMTP($host = null, $port = null, $localhost = null,
-        $pipelining = false, $timeout = 0)
+        $pipelining = false, $timeout = 0, $socket_options = null)
     {
         if (isset($host)) {
@@ -176,4 +185,5 @@
 
         $this->_socket = new Net_Socket();
+        $this->_socket_options = $socket_options;
         $this->_timeout = $timeout;
 
@@ -406,5 +416,6 @@
         $this->_greeting = null;
         $result = $this->_socket->connect($this->host, $this->port,
-                                          $persistent, $timeout);
+                                          $persistent, $timeout,
+                                          $this->_socket_options);
         if (PEAR::isError($result)) {
             return PEAR::raiseError('Failed to connect socket: ' .
@@ -418,6 +429,8 @@
          * and all other socket operations.
          */
-        if (PEAR::isError($error = $this->setTimeout($this->_timeout))) {
-            return $error;
+        if ($this->_timeout > 0) {
+            if (PEAR::isError($error = $this->setTimeout($this->_timeout))) {
+                return $error;
+            }
         }
 
Index: program/localization/de_CH/messages.inc
===================================================================
--- program/localization/de_CH/messages.inc	(revision 01a3689c209f5ff480c178da8bd32de4a61a4e5c)
+++ program/localization/de_CH/messages.inc	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -136,6 +136,6 @@
 $messages['folderupdated'] = 'Der Ordner wurde erfolgreich aktualisiert';
 $messages['foldercreated'] = 'Der Ordner wurde erfolgreich erstellt';
-$messages['errorreadonly'] = 'Die Aktion nicht ausgefÃŒhrt werden. Der Ordner ist schreibgeschÃŒtzt.';
-$messages['errornoperm'] = 'Die Aktion nicht ausgefÃŒhrt werden. Zugriff verweigert.';
+$messages['errorreadonly'] = 'Die Aktion kann nicht ausgefÃŒhrt werden. Der Ordner ist schreibgeschÃŒtzt.';
+$messages['errornoperm'] = 'Die Aktion kann nicht ausgefÃŒhrt werden. Zugriff verweigert.';
 
 ?>
Index: program/localization/de_DE/messages.inc
===================================================================
--- program/localization/de_DE/messages.inc	(revision 98cb0f179206843ceaa87df6bfb3d1da045ed8ad)
+++ program/localization/de_DE/messages.inc	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -25,6 +25,6 @@
 $messages['servererror'] = 'Serverfehler!';
 $messages['servererrormsg'] = 'Serverfehler: $msg';
-$messages['errorreadonly'] = 'Die Aktion nicht ausgefÃŒhrt werden. Der Ordner ist schreibgeschÃŒtzt.';
-$messages['errornoperm'] = 'Die Aktion nicht ausgefÃŒhrt werden. Zugriff verweigert.';
+$messages['errorreadonly'] = 'Die Aktion kann nicht ausgefÃŒhrt werden. Der Ordner ist schreibgeschÃŒtzt.';
+$messages['errornoperm'] = 'Die Aktion kann nicht ausgefÃŒhrt werden. Zugriff verweigert.';
 $messages['invalidrequest'] = 'UngÃŒltige Anfrage! Es wurden keine Daten gespeichert.';
 $messages['nomessagesfound'] = 'Keine Nachrichten in diesem Ordner';
@@ -118,5 +118,5 @@
 $messages['smtptoerror'] = 'SMTP Fehler ($code): Der EmpfÃ€nger "$to" konnte nicht gesetzt werden ($msg)';
 $messages['smtprecipientserror'] = 'SMTP Fehler: Die EmpfÃ€ngerliste konnte nicht verarbeitet werden';
-$messages['smtpdsnerror'] = 'SMTP-Fehler: EmpfangsbestÃ€tigung werden nicht unterstÃŒtzt';
+$messages['smtpdsnerror'] = 'SMTP-Fehler: EmpfangsbestÃ€tigungen werden nicht unterstÃŒtzt';
 $messages['smtperror'] = 'SMTP Fehler: $msg';
 $messages['emailformaterror'] = 'UngÃŒltige E-Mail-Adresse: $email';
Index: program/localization/index.inc
===================================================================
--- program/localization/index.inc	(revision 382b8b1351e1a4598f04ab184cec803b96d7e4b7)
+++ program/localization/index.inc	(revision 8ffd08a7398d044dc7e9f5deecc76a29c6aa270e)
@@ -33,5 +33,5 @@
   'eu_ES' => 'Basque (Euskara)',
   'bn_BD' => 'Bengali (àŠ¬àŠŸàŠàŠ²àŠŸ)',
-  'bs_BA' => 'Bosnian (BoÅ¡njaÄki)',
+  'bs_BA' => 'Bosnian (Bosanski)',
   'br'	  => 'Breton (Brezhoneg)',
   'bg_BG' => 'Bulgarian (ÐÑÐ»Ð³Ð°ÑÑÐºÐž)',
