diff --git a/gajim/common/connection.py b/gajim/common/connection.py
index 6a0f2c63a..f004520cd 100644
--- a/gajim/common/connection.py
+++ b/gajim/common/connection.py
@@ -1486,6 +1486,9 @@ class Connection(CommonConnection, ConnectionHandlers):
             # Get bookmarks
             self.get_module('Bookmarks').request_bookmarks()
 
+            # Enable Software Version
+            self.get_module('SoftwareVersion').set_enabled(True)
+
             # Get annotations
             self.get_module('Annotations').request_annotations()
 
diff --git a/gajim/common/modules/software_version.py b/gajim/common/modules/software_version.py
index 56f891b8a..86f5cb0fa 100644
--- a/gajim/common/modules/software_version.py
+++ b/gajim/common/modules/software_version.py
@@ -6,101 +6,39 @@
 #
 # Gajim is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 # GNU General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
-# along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
 
 # XEP-0092: Software Version
 
-import nbxmpp
-from nbxmpp.structs import StanzaHandler
-
 from gajim.common import app
 from gajim.common.helpers import get_os_info
-from gajim.common.nec import NetworkIncomingEvent
 from gajim.common.modules.base import BaseModule
 
 
 class SoftwareVersion(BaseModule):
+
+    _nbxmpp_extends = 'SoftwareVersion'
+    _nbxmpp_methods = [
+        'set_software_version',
+        'request_software_version',
+        'disable',
+    ]
+
     def __init__(self, con):
         BaseModule.__init__(self, con)
 
-        self.handlers = [
-            StanzaHandler(name='iq',
-                          callback=self._answer_request,
-                          typ='get',
-                          ns=nbxmpp.NS_VERSION),
-        ]
-
-    def request_os_info(self, jid, resource):
-        if not app.account_is_connected(self._account):
-            return
-        # If we are invisible, do not request
-        if self._con.connected == app.SHOW_LIST.index('invisible'):
-            return
-
-        if resource:
-            jid += '/' + resource
-        iq = nbxmpp.Iq(to=jid, typ='get', queryNS=nbxmpp.NS_VERSION)
-
-        self._log.info('Requested: %s', jid)
-
-        self._con.connection.SendAndCallForResponse(iq, self._result_received)
-
-    def _result_received(self, stanza):
-        client_info, os_info = None, None
-        if not nbxmpp.isResultNode(stanza):
-            self._log.info('Error: %s', stanza.getError())
+    def set_enabled(self, enabled):
+        if enabled:
+            if not app.config.get_per('accounts', self._account, 'send_os_info'):
+                return
+            self._nbxmpp('SoftwareVersion').set_software_version(
+                'Gajim', app.version, get_os_info())
         else:
-            try:
-                client_info, os_info = self._extract_info(stanza)
-            except Exception:
-                self._log.exception('Error')
-                self._log.error(stanza)
-
-        self._log.info('Received: %s %s %s',
-                       stanza.getFrom(), client_info, os_info)
-
-        app.nec.push_incoming_event(
-            VersionResultReceivedEvent(None, conn=self._con,
-                                       jid=stanza.getFrom(),
-                                       client_info=client_info,
-                                       os_info=os_info))
-
-    @staticmethod
-    def _extract_info(stanza):
-        query = stanza.getTag('query')
-        name = query.getTag('name').getData()
-        version = query.getTag('version').getData()
-        client_info = '%s %s' % (name, version)
-        os_info = query.getTag('os')
-        if os_info is not None:
-            os_info = os_info.getData()
-        return client_info, os_info
-
-    def _answer_request(self, _con, stanza, _properties):
-        self._log.info('%s asked for the software version', stanza.getFrom())
-        if app.config.get_per('accounts', self._account, 'send_os_info'):
-            os_info = get_os_info()
-            iq = stanza.buildReply('result')
-            query = iq.getQuery()
-            query.setTagData('name', 'Gajim')
-            query.setTagData('version', app.version)
-            query.setTagData('os', os_info)
-            self._log.info('Answer: Gajim %s %s', app.version, os_info)
-        else:
-            iq = stanza.buildReply('error')
-            err = nbxmpp.ErrorNode(nbxmpp.ERR_SERVICE_UNAVAILABLE)
-            iq.addChild(node=err)
-            self._log.info('Send service-unavailable')
-        self._con.connection.send(iq)
-        raise nbxmpp.NodeProcessed
-
-
-class VersionResultReceivedEvent(NetworkIncomingEvent):
-    name = 'version-result-received'
+            self._nbxmpp('SoftwareVersion').disable()
 
 
 def get_instance(*args, **kwargs):
diff --git a/gajim/gtk/preferences.py b/gajim/gtk/preferences.py
index 2b542c5ab..f0b4bf776 100644
--- a/gajim/gtk/preferences.py
+++ b/gajim/gtk/preferences.py
@@ -879,6 +879,8 @@ class Preferences(Gtk.ApplicationWindow):
     def on_send_os_info_checkbutton_toggled(self, widget):
         widget.set_inconsistent(False)
         self.on_per_account_checkbutton_toggled(widget, 'send_os_info')
+        for con in app.connections.values():
+            con.get_module('SoftwareVersion').set_enabled(widget.get_active())
 
     def on_ignore_events_from_unknown_contacts_checkbutton_toggled(self, widget):
         widget.set_inconsistent(False)
diff --git a/gajim/gtk/server_info.py b/gajim/gtk/server_info.py
index 7aa4e2b4a..670d7b54a 100644
--- a/gajim/gtk/server_info.py
+++ b/gajim/gtk/server_info.py
@@ -17,6 +17,7 @@ from collections import namedtuple
 from datetime import timedelta
 
 import nbxmpp
+from nbxmpp.util import is_error_result
 from gi.repository import Gtk
 from gi.repository import Gdk
 
@@ -24,6 +25,8 @@ from gajim.common import app
 from gajim.common import ged
 from gajim.common.i18n import _
 
+from gajim.gtk.util import ensure_not_destroyed
+
 log = logging.getLogger('gajim.gtk.serverinfo')
 
 
@@ -34,6 +37,7 @@ class ServerInfoDialog(Gtk.Dialog):
                          destroy_with_parent=True)
 
         self.account = account
+        self._destroyed = False
         self.set_transient_for(app.interface.roster.window)
         self.set_resizable(False)
 
@@ -69,10 +73,6 @@ class ServerInfoDialog(Gtk.Dialog):
         self.connect('response', self.on_response)
         self.connect('destroy', self.on_destroy)
 
-        app.ged.register_event_handler('version-result-received',
-                                       ged.CORE,
-                                       self._nec_version_result_received)
-
         app.ged.register_event_handler('server-disco-received',
                                        ged.GUI1,
                                        self._server_disco_received)
@@ -81,7 +81,8 @@ class ServerInfoDialog(Gtk.Dialog):
         self.uptime = ''
         self.hostname = app.get_hostname_from_account(account)
         con = app.connections[account]
-        con.get_module('SoftwareVersion').request_os_info(self.hostname, None)
+        con.get_module('SoftwareVersion').request_software_version(
+            self.hostname, callback=self._software_version_received)
         self.request_last_activity()
 
         for feature in self.get_features():
@@ -138,10 +139,12 @@ class ServerInfoDialog(Gtk.Dialog):
                 'days': delta.days, 'hours': hours}
             self.update(self.get_infos, self.info_listbox)
 
-    def _nec_version_result_received(self, obj):
-        if obj.jid != self.hostname:
-            return
-        self.version = obj.client_info or _('Unknown')
+    @ensure_not_destroyed
+    def _software_version_received(self, result):
+        if is_error_result(result):
+            self.version = _('Unknown')
+        else:
+            self.version = '%s %s' % (result.name, result.version)
         self.update(self.get_infos, self.info_listbox)
 
     def _server_disco_received(self, obj):
@@ -229,11 +232,8 @@ class ServerInfoDialog(Gtk.Dialog):
             self.destroy()
 
     def on_destroy(self, *args):
+        self._destroyed = True
         del app.interface.instances[self.account]['server_info']
-        app.ged.remove_event_handler('version-result-received',
-                                     ged.CORE,
-                                     self._nec_version_result_received)
-
         app.ged.remove_event_handler('server-disco-received',
                                      ged.GUI1,
                                      self._server_disco_received)
diff --git a/gajim/remote_control.py b/gajim/remote_control.py
index 45364b1d6..ca1ee742a 100644
--- a/gajim/remote_control.py
+++ b/gajim/remote_control.py
@@ -267,9 +267,6 @@ class GajimRemote(Server):
             <signal name='NewMessage'>
                 <arg type='av' />
             </signal>
-            <signal name='OsInfo'>
-                <arg type='av' />
-            </signal>
             <signal name='Roster'>
                 <arg type='av' />
             </signal>
@@ -299,8 +296,6 @@ class GajimRemote(Server):
         super().__init__(self.con, '/org/gajim/dbus/RemoteObject')
         self.first_show = True
 
-        app.ged.register_event_handler('version-result-received', ged.POSTGUI,
-            self.on_os_info)
         app.ged.register_event_handler('time-result-received', ged.POSTGUI,
             self.on_time)
         app.ged.register_event_handler('roster-info', ged.POSTGUI,
@@ -346,12 +341,6 @@ class GajimRemote(Server):
         self.raise_signal('MessageSent', (obj.conn.name, [
             obj.jid, obj.message, obj.keyID, chatstate]))
 
-    def on_os_info(self, obj):
-        self.raise_signal('OsInfo', (obj.conn.name, [obj.jid.getStripped(),
-                                                     obj.jid.getResource(),
-                                                     obj.client_info,
-                                                     obj.os_info]))
-
     def on_time(self, obj):
         self.raise_signal('EntityTime', (obj.conn.name, [obj.jid.getStripped(),
                                                          obj.jid.getResource(),
diff --git a/gajim/vcard.py b/gajim/vcard.py
index 9dae488e6..ca3550e8e 100644
--- a/gajim/vcard.py
+++ b/gajim/vcard.py
@@ -33,6 +33,8 @@ from gi.repository import Gtk
 from gi.repository import GLib
 from gi.repository import Gdk
 from nbxmpp.structs import AnnotationNote
+from nbxmpp.util import is_error_result
+from nbxmpp.protocol import JID
 
 from gajim import gtkgui_helpers
 from gajim.gui_menu_builder import show_save_as_menu
@@ -102,8 +104,6 @@ class VcardWindow:
         self.update_progressbar_timeout_id = GLib.timeout_add(self.update_intervall,
             self.update_progressbar)
 
-        app.ged.register_event_handler('version-result-received', ged.GUI1,
-            self.set_os_info)
         app.ged.register_event_handler('time-result-received', ged.GUI1,
             self.set_entity_time)
 
@@ -148,8 +148,6 @@ class VcardWindow:
         if note is None or new_annotation != note.data:
             new_note = AnnotationNote(jid=self.contact.jid, data=new_annotation)
             con.get_module('Annotations').set_note(new_note)
-        app.ged.remove_event_handler('version-result-received', ged.GUI1,
-            self.set_os_info)
         app.ged.remove_event_handler('time-result-received', ged.GUI1,
             self.set_entity_time)
 
@@ -263,34 +261,27 @@ class VcardWindow:
         self.clear_values()
         self._set_values(vcard, jid)
 
-    def set_os_info(self, obj):
-        if obj.conn.name != self.account:
-            return
+    def set_os_info(self, result, jid):
         if self.xml.get_object('information_notebook').get_n_pages() < 5:
             return
-        if self.gc_contact:
-            if obj.jid != self.contact.jid:
-                return
-        elif obj.jid.getStripped() != self.contact.jid:
-            return
+
+        error = is_error_result(result)
         i = 0
         client = ''
         os_info = ''
         while i in self.os_info:
-            if self.os_info[i]['resource'] == obj.jid.getResource():
-                if obj.client_info:
-                    self.os_info[i]['client'] = obj.client_info
+            if self.os_info[i]['resource'] == JID(jid).getResource():
+                if not error:
+                    self.os_info[i]['client'] = '%s %s' % (result.name,
+                                                           result.version)
                 else:
                     self.os_info[i]['client'] = Q_('?Client:Unknown')
-                if obj.os_info:
-                    self.os_info[i]['os'] = obj.os_info
+
+                if not error and result.os is not None:
+                    self.os_info[i]['os'] = result.os
                 else:
                     self.os_info[i]['os'] = Q_('?OS:Unknown')
-            else:
-                if not self.os_info[i]['client']:
-                    self.os_info[i]['client'] = Q_('?Client:Unknown')
-                if not self.os_info[i]['os']:
-                    self.os_info[i]['os'] = Q_('?OS:Unknown')
+
             if i > 0:
                 client += '\n'
                 os_info += '\n'
@@ -417,12 +408,16 @@ class VcardWindow:
             self.os_info_arrived = True
         else: # Request os info if contact is connected
             if self.gc_contact:
-                j, r = app.get_room_and_nick_from_fjid(self.real_jid)
-                GLib.idle_add(con.get_module('SoftwareVersion').request_os_info,
-                              j, r)
+                con.get_module('SoftwareVersion').request_software_version(
+                    self.real_jid,
+                    callback=self.set_os_info,
+                    user_data=self.real_jid)
             else:
-                GLib.idle_add(con.get_module('SoftwareVersion').request_os_info,
-                              self.contact.jid, self.contact.resource)
+                jid = self.contact.get_full_jid()
+                con.get_module('SoftwareVersion').request_software_version(
+                    jid,
+                    callback=self.set_os_info,
+                    user_data=jid)
 
         # do not wait for entity_time if contact is not connected or has error
         # additional check for observer is needed, as show is offline for him
@@ -451,8 +446,9 @@ class VcardWindow:
                     uf_resources += '\n' + c.resource + \
                             _(' resource with priority ') + str(c.priority)
                     if c.show not in ('offline', 'error'):
-                        GLib.idle_add(con.get_module('SoftwareVersion').request_os_info,
-                                      c.jid, c.resource)
+                        jid = c.get_full_jid()
+                        con.get_module('SoftwareVersion').request_software_version(
+                            jid, callback=self.set_os_info, user_data=jid)
                         GLib.idle_add(con.get_module('EntityTime').request_entity_time,
                                       c.jid, c.resource)
                     self.os_info[i] = {'resource': c.resource, 'client': '',