look for DBUS_SESSION_BUS_ADDRESS if not set

if DBUS_SESSION_BUS_ADDRESS is not set (like if running from crontab)
we end up with a fresh dbus session. This breaks things like access to
secret service (password manager).

If DBUS_SESSION_BUS_ADDRESS is empty, look in several locations inside
XDG_RUNTIME_DIR (/run/user/<uid>/) and if an existing dbus session bus
address is found, update DBUS_SESSION_BUS_ADDRESS with that and
continue.
diff --git a/conn.go b/conn.go
index 02471a5..b38920b 100644
--- a/conn.go
+++ b/conn.go
@@ -77,8 +77,11 @@
 }
 
 func getSessionBusAddress() (string, error) {
-	address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
-	if address != "" && address != "autolaunch:" {
+	if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
+		return address, nil
+
+	} else if address := tryDiscoverDbusSessionBusAddress(); address != "" {
+		os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
 		return address, nil
 	}
 	return getSessionBusPlatformAddress()
diff --git a/conn_other.go b/conn_other.go
index 0a73c50..289044a 100644
--- a/conn_other.go
+++ b/conn_other.go
@@ -5,8 +5,13 @@
 import (
 	"bytes"
 	"errors"
+	"fmt"
+	"io/ioutil"
 	"os"
 	"os/exec"
+	"os/user"
+	"path"
+	"strings"
 )
 
 func getSessionBusPlatformAddress() (string, error) {
@@ -30,3 +35,57 @@
 	return addr, nil
 }
 
+// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session
+// and return the value of its DBUS_SESSION_BUS_ADDRESS.
+// It tries different techniques employed by different operating systems,
+// returning the first valid address it finds, or an empty string.
+//
+// * /run/user/<uid>/bus           if this exists, it *is* the bus socket. present on
+//                                 Ubuntu 18.04
+// * /run/user/<uid>/dbus-session: if this exists, it can be parsed for the bus
+//                                 address. present on Ubuntu 16.04
+//
+// See https://dbus.freedesktop.org/doc/dbus-launch.1.html
+func tryDiscoverDbusSessionBusAddress() string {
+	if runtimeDirectory, err := getRuntimeDirectory(); err == nil {
+
+		if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) {
+			// if /run/user/<uid>/bus exists, that file itself
+			// *is* the unix socket, so return its path
+			return fmt.Sprintf("unix:path=%s", runUserBusFile)
+		}
+		if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) {
+			// if /run/user/<uid>/dbus-session exists, it's a
+			// text file // containing the address of the socket, e.g.:
+			// DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG
+
+			if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil {
+				fileContent := string(f)
+
+				prefix := "DBUS_SESSION_BUS_ADDRESS="
+
+				if strings.HasPrefix(fileContent, prefix) {
+					address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r")
+					return address
+				}
+			}
+		}
+	}
+	return ""
+}
+
+func getRuntimeDirectory() (string, error) {
+	if currentUser, err := user.Current(); err != nil {
+		return "", err
+	} else {
+		return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil
+	}
+}
+
+func fileExists(filename string) bool {
+	if _, err := os.Stat(filename); !os.IsNotExist(err) {
+		return true
+	} else {
+		return false
+	}
+}