diff --git a/src/python/dm/common/client/__init__.py b/src/python/dm/common/client/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/python/dm/common/client/dmExceptionMapper.py b/src/python/dm/common/client/dmExceptionMapper.py
new file mode 100755
index 0000000000000000000000000000000000000000..4fc48c6ff77c68cdf41753861d2c9ec2cc4ea4dc
--- /dev/null
+++ b/src/python/dm/common/client/dmExceptionMapper.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+from dm.common.constants import dmStatus
+from dm.common.exceptions import dmExceptionMap
+from dm.common.exceptions.dmException import DmException
+
+class DmExceptionMapper:
+
+    @classmethod
+    def checkStatus(cls, httpHeaders):
+        """ Map dm status code into appropriate exception. """
+        code = httpHeaders.get('Dm-Status-Code', None)
+        msg = httpHeaders.get('Dm-Status-Message', 'Internal Error')
+        if code is None or code == str(dmStatus.DM_OK):
+            return
+        elif dmExceptionMap.DM_EXCEPTION_MAP.has_key(int(code)):
+            # Exception string is value of the form 'x.y.z'
+            # where 'x.y' is dm module, and 'z' class in that module
+            exStr = dmExceptionMap.DM_EXCEPTION_MAP.get(int(code))
+            exClass = exStr.split('.')[-1] # 'z' in 'x.y.z'
+            exModule = '.'.join(exStr.split('.')[:-1]) # 'x.y' in 'x.y.z'
+            exec 'from dm.common.exceptions.%s import %s' % (exModule, exClass)
+            exec 'ex = %s(msg)' % (exClass)
+            raise ex
+        else:
+            raise DmException(msg)
+
+# Testing.
+if __name__ == '__main__':
+    pass
diff --git a/src/python/dm/common/client/dmHttpsConnection.py b/src/python/dm/common/client/dmHttpsConnection.py
new file mode 100755
index 0000000000000000000000000000000000000000..3a7b7df2f53dd9042fd34ada6128950496002d6b
--- /dev/null
+++ b/src/python/dm/common/client/dmHttpsConnection.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+import socket
+import httplib
+import ssl
+
+from dm.common.utility.configurationManager import ConfigurationManager
+
+class DmHttpsConnection(httplib.HTTPSConnection):
+
+    def __init__(self, hostPort, timeout):
+        cm = ConfigurationManager.getInstance()
+        args = hostPort.split(':')
+        host = args[0]
+        if len(args) > 1:
+            port = int(args[1])
+        else:
+            port = cm.getServicePort()
+        keyFile = cm.getSslKeyFile()
+        certFile = cm.getSslCertFile()
+        caCertFile = cm.getSslCaCertFile()
+        certChain = None
+        strict = True
+        httplib.HTTPSConnection.__init__(self, host, port, keyFile, certFile, strict, timeout)
+        context = self.getContext(keyFile, certFile, caCertFile, certChain)
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.sock = context.wrap_socket(sock)
+        self.connect()
+
+    def connect(self):
+        self.sock.connect((self.host,self.port))
+
+    def getContext(self, keyFile, certFile, caCertFile=None, certChain=None):
+        """Return SSL Context from self attributes."""
+        #context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
+        if caCertFile is not None:
+            context.verify_mode = ssl.CERT_REQUIRED
+            context.load_verify_locations(caCertFile)
+        else:
+            context.verify_mode = ssl.CERT_NONE
+        if certFile is not None and keyFile is not None:
+            context.load_cert_chain(certFile, keyFile)
+        if certChain:
+            context.load_verify_locations(certChain)
+        return context
+
+#######################################################################
+# Testing.
+
+if __name__ == '__main__':
+    pass
+
diff --git a/src/python/dm/common/client/dmHttpsHandler.py b/src/python/dm/common/client/dmHttpsHandler.py
new file mode 100755
index 0000000000000000000000000000000000000000..076e0ece9c9899db90e412cfd20ad946801a3a71
--- /dev/null
+++ b/src/python/dm/common/client/dmHttpsHandler.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+import urllib2
+import ssl
+from dm.common.client.dmHttpsConnection import DmHttpsConnection
+
+class DmHttpsHandler(urllib2.HTTPSHandler):
+
+    def https_open(self, req):
+        return self.do_open(DmHttpsConnection,req)
+
+#######################################################################
+# Testing.
+
+if __name__ == '__main__':
+    from dm.common.utility.configurationManager import ConfigurationManager 
+    cm = ConfigurationManager.getInstance()
+    cm.setSslCaCertFile("/home/sveseli/Work/DM/etc/ssl/cacert.pem")
+
+    print "Installing opener"
+    opener = urllib2.build_opener(DmHttpsHandler)
+    urllib2.install_opener(opener)
+
+    url = "https://zagreb.svdev.net:10232/dm/directory/list?path=/tmp"
+    print "Opening URL: ", url
+    #context = ssl.create_default_context(cafile="/home/sveseli/Work/DM/etc/ssl/cacert.pem")
+    #ssl._create_default_https_context = ssl._create_unverified_context
+    #f = urllib2.urlopen(url, context=context)
+    f = urllib2.urlopen(url)
+    print f.code
+    print f.read()
+
diff --git a/src/python/dm/common/client/sessionManager.py b/src/python/dm/common/client/sessionManager.py
new file mode 100755
index 0000000000000000000000000000000000000000..e5c14d9fa6569c935002974bd1eb529075611d34
--- /dev/null
+++ b/src/python/dm/common/client/sessionManager.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+
+import urllib
+import urllib2
+import urlparse
+import time
+import getpass
+import os
+import stat
+import ssl
+import types
+
+from dm.common.constants import dmServiceConstants
+from dm.common.exceptions.configurationError import ConfigurationError
+from dm.common.exceptions.authorizationError import AuthorizationError
+from dm.common.exceptions.urlError import UrlError
+from dm.common.utility.loggingManager import LoggingManager
+from dm.common.utility.configurationManager import ConfigurationManager
+from dm.common.utility.osUtility import OsUtility
+from dm.common.client.dmExceptionMapper import DmExceptionMapper
+from dm.common.client.dmHttpsHandler import DmHttpsHandler
+
+class SessionManager:
+    """ Class for session management. """
+
+    def __init__(self):
+        self.sessionCookie = None
+        self.host = None
+        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
+        self.username = ''
+        self.password = ''
+        self.urlOpener = None
+        cm = ConfigurationManager.getInstance()
+        self.sessionCacheFile = cm.getSessionCacheFile()
+        self.requireSessionCredentials = cm.getRequireSessionCredentials()
+
+    def setHost(self, host):
+        self.host = host
+
+    def hasSession(self):
+        """ Return true if we have session established. """
+        if self.sessionCookie is not None:
+            return True
+        return False
+
+    def establishSession(self, url, username, password, selector='/dm/login'):
+        self.host = url
+        self.username = username
+        self.password = password
+        self.sessionCookie = self.loadSession()
+        if self.sessionCookie is not None:
+            return
+
+        # Could not load session.
+        try:
+            # User will be asked for username/password if they are not
+            # provided.
+            data = { 'username' : self.getUsername(username), 'password' : self.getPassword(password) }
+            self.logger.debug('Establishing session for user %s @ %s)' % (username, url))
+            (response,responseData) = self.sendRequest(url='%s%s' % (url, selector), method='POST', contentType='application/x-www-form-urlencoded', data=urllib.urlencode(data))
+        except urllib2.URLError, ex:
+            self.logger.exception('%s' % ex)
+            raise UrlError(exception=ex)
+
+        #self.logger.debug('Got headers: %s' % response.headers)
+        # This will save session cookie.
+        self.sessionCookie = self.checkResponseHeadersForErrorsAndSaveSession(response.headers)
+        self.logger.debug('User %s session cookie: %s' % (username, self.sessionCookie))
+
+    def getUsername(self, username):
+        if not len(username) and self.requireSessionCredentials:
+            return self.askForUsername()
+        return username
+
+    def askForUsername(self):
+        defaultUsername = getpass.getuser()
+        username = raw_input('Username [%s]: ' % defaultUsername)
+        username = username.strip()
+        if not len(username):
+            username = defaultUsername
+        return username
+
+    def getPassword(self, password):
+        if not len(password) and self.requireSessionCredentials:
+            return self.askForPassword()
+        return password
+
+    def askForPassword(self):
+        password = getpass.getpass()
+        password = password.strip()
+        if not len(password):
+            raise AuthorizationError('Empty password provided.')
+        return password
+
+    def clearSessionFile(self):
+        if self.sessionCacheFile is None:
+            return
+        try:
+            self.logger.debug('Clearing session cache: %s' % (self.sessionCacheFile))
+            OsUtility.removeFile(self.sessionCacheFile)
+        except Exception, ex:
+            # ignore errors.
+            self.logger.warn('Could not clear session cache: %s' % (ex))
+            pass
+
+    def saveSession(self, sessionCookie):
+        if self.sessionCacheFile is None:
+            return
+        if sessionCookie is None:
+            return
+        try:
+            f = open(self.sessionCacheFile, 'w')
+            f.write('%s' % sessionCookie)
+            f.close()
+            os.chmod(self.sessionCacheFile, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR)
+        except Exception, ex:
+            self.logger.warn('Could not save session: %s' % (ex))
+
+    def loadSession(self):
+        if self.sessionCacheFile is None:
+            return None
+        try:
+            f = open(self.sessionCacheFile, 'r')
+            session = f.read()
+            expires = session.split(';')[1].split('=')[-1]
+            t = time.mktime(time.strptime(expires, '%a, %d %b %Y %H:%M:%S %Z'))
+            now = time.time()
+            if t < now:
+                return None
+            else:
+                self.logger.debug('Loaded session from %s: %s' % (self.sessionCacheFile, session))
+                return session
+        except Exception, ex:
+            self.logger.warn('Could not load session: %s' % (ex))
+        return None
+
+    def getUrlOpener(self, protocol):
+        if not self.urlOpener:
+            if protocol == dmServiceConstants.DM_SERVICE_PROTOCOL_HTTPS:
+                # HTTPS, use custom https handler, which 
+                # should work even if any of cert/key/cacert files is None
+                cm = ConfigurationManager.getInstance()
+                keyFile = cm.getSslKeyFile()
+                certFile = cm.getSslCertFile()
+                self.urlOpener = urllib2.build_opener(DmHttpsHandler)
+                #self.logger.debug('Using Dm HTTPS Handler')
+            else:
+                # HTTP, use standard http handler
+                self.urlOpener = urllib2.build_opener(urllib2.HTTPHandler)
+        # Install opener before returning it.
+        urllib2.install_opener(self.urlOpener)
+        return self.urlOpener
+
+    def sendRequest(self, url, method, contentType='html', data={}):
+        """ Send http request without cookies. """
+        if url.find('://') < 0:
+            url = '%s%s' % (self.host, url)
+        parsedUrl = urlparse.urlparse(url)
+        protocol = parsedUrl[0]
+        path = parsedUrl[2]
+        self.logger.debug('Sending request: %s' % url)
+        encodedData = ''
+        if data is not None:
+            if type(data) == types.DictType and len(data):
+                encodedData=urllib.urlencode(data)
+                contentType='application/x-www-form-urlencoded'
+            elif type(data) == types.StringType:
+                encodedData = data
+        request = urllib2.Request(url, data=encodedData)
+        request.get_method = lambda: method
+        request.add_header('Content-Type', contentType)
+        request.add_header('Content-Length', str(len(data)))
+        if self.sessionCookie != None:
+            request.add_header('Cookie', self.sessionCookie)
+        try:
+            opener = self.getUrlOpener(protocol)
+            response = opener.open(request)
+        except urllib2.HTTPError, ex:
+            # If we see dm headers, dm exception will be thrown,
+            # otherwise we'll throw UrlError
+            self.checkResponseHeadersForErrors(ex.hdrs)
+            self.logger.exception('%s' % ex)
+            raise UrlError(exception=ex)
+        except urllib2.URLError, ex:
+            self.logger.exception('%s' % ex)
+            raise UrlError(exception=ex)
+
+        # Check headers for errors and update session cookie
+        sessionCookie = self.checkResponseHeadersForErrorsAndSaveSession(response.headers)
+        if sessionCookie != None:
+            self.sessionCookie = sessionCookie
+        responseData = response.read()
+        return (response, responseData)
+
+    def sendSessionRequest(self, url, method, contentType='html', data={}):
+        """ Send session request. """
+        if self.sessionCookie is None:
+            self.establishSession(self.host, self.username, self.password)
+        return self.sendRequest(url, method, contentType, data)
+
+    def checkResponseHeadersForErrorsAndSaveSession(self, responseHeaders):
+        try:
+            DmExceptionMapper.checkStatus(responseHeaders)
+            sessionCookie = responseHeaders.get('Set-Cookie')
+            self.saveSession(sessionCookie)
+            return sessionCookie
+        except AuthorizationError, ex:
+            self.clearSessionFile()
+            raise
+
+    def checkResponseHeadersForErrors(self, responseHeaders):
+        try:
+            DmExceptionMapper.checkStatus(responseHeaders)
+        except AuthorizationError, ex:
+            self.clearSessionFile()
+            raise
+
+#######################################################################
+# Testing.
+if __name__ == '__main__':
+    sm = SessionManager.createSession()