Install openchange_user_cleanup in usr/sbin/
Instead of usr/share/doc/sogo/pull/12/head
parent
4fb3492ea8
commit
a53c66e8a4
|
@ -1,306 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import getopt
|
|
||||||
import imaplib
|
|
||||||
import ldb
|
|
||||||
import plistlib
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
imaphost = '127.0.0.1'
|
|
||||||
imapport = 143
|
|
||||||
|
|
||||||
sambaprivate = '/var/lib/samba/private'
|
|
||||||
mapistorefolder = "%s/mapistore" % (sambaprivate)
|
|
||||||
sogoSysDefaultsFile = "/etc/sogo/sogo.conf"
|
|
||||||
sogoUserDefaultsFile = os.path.expanduser("~sogo/GNUstep/Defaults/.GNUstepDefaults")
|
|
||||||
|
|
||||||
# - takes a username and optionally its password
|
|
||||||
# - removes the entry in samba's ldap tree via ldbedit (NOTYET)
|
|
||||||
# - remove the user's directory under mapistore/ and mapistore/SOGo
|
|
||||||
# - cleanup Junk Folders and Sync Issues imap folders
|
|
||||||
# - Delete the socfs_ table for the username.
|
|
||||||
|
|
||||||
def usage():
|
|
||||||
print """
|
|
||||||
%s [-i imaphost] ] [-p imapport] [-s sambaprivate] username [password]
|
|
||||||
-i imaphost IMAP host to connect to [%s]
|
|
||||||
-p imappost IMAP port to use [%d]
|
|
||||||
-s sambaprivate samba private directory [%s]
|
|
||||||
""" % (os.path.basename(sys.argv[0]), imaphost, imapport, sambaprivate)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
global sambaprivate
|
|
||||||
global mapistorefolder
|
|
||||||
global imaphost
|
|
||||||
global imapport
|
|
||||||
try:
|
|
||||||
opts, args = getopt.getopt(sys.argv[1:], "i:p:s:")
|
|
||||||
except getopt.GetoptError, err:
|
|
||||||
print str(err)
|
|
||||||
usage()
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
for o, a in opts:
|
|
||||||
if o == "-i":
|
|
||||||
imaphost = a
|
|
||||||
elif o == "-p":
|
|
||||||
imapport = a
|
|
||||||
elif o == "-s":
|
|
||||||
sambaprivate = a
|
|
||||||
mapistorefolder = "%s/mapistore" % (sambaprivate)
|
|
||||||
else:
|
|
||||||
assert False, "unhandled option"
|
|
||||||
|
|
||||||
argslen = len(args)
|
|
||||||
if (argslen == 2):
|
|
||||||
username = args[0]
|
|
||||||
userpass = args[1]
|
|
||||||
elif (argslen == 1):
|
|
||||||
username = args[0]
|
|
||||||
userpass = username
|
|
||||||
print "Using username as password"
|
|
||||||
else:
|
|
||||||
usage()
|
|
||||||
print "Specify a user (and optionally the password)"
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
# cleanup starts here
|
|
||||||
try:
|
|
||||||
imapCleanup(imaphost, imapport, username, userpass)
|
|
||||||
except Exception as e:
|
|
||||||
print "Error during imapCleanup, continuing: %s" % str(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
mapistoreCleanup(mapistorefolder, username)
|
|
||||||
except (shutil.Error, OSError) as e:
|
|
||||||
print "Error during mapistoreCleanup, continuing: %s" % str(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
ldbCleanup(sambaprivate, username)
|
|
||||||
except ldb.LdbError as e:
|
|
||||||
print "Error during ldbCleanup, continuing: %s" % str(e)
|
|
||||||
|
|
||||||
try:
|
|
||||||
sqlCleanup(username)
|
|
||||||
except Exception as e:
|
|
||||||
print "Error during sqlCleanup, continuing: %s" % str(e)
|
|
||||||
|
|
||||||
def getsep(client):
|
|
||||||
seq = None
|
|
||||||
(code, data) = client.list("", "")
|
|
||||||
if code == "OK" and data is not None:
|
|
||||||
# yes this is ugly but it works on cyrus and dovecot.
|
|
||||||
# (\\Noinferiors \\HasNoChildren) "/" INBOX
|
|
||||||
m = re.search(".*\s+[\"\']?(.)[\"\']?\s+[\"\']?.*[\"\']?$", data[0])
|
|
||||||
sep = m.group(1)
|
|
||||||
return sep
|
|
||||||
|
|
||||||
def extractmb(si):
|
|
||||||
inparen = False
|
|
||||||
inquote = False
|
|
||||||
part = []
|
|
||||||
parts = []
|
|
||||||
|
|
||||||
for char in si:
|
|
||||||
if inparen:
|
|
||||||
if char == ")":
|
|
||||||
inparen = False
|
|
||||||
parts.append("".join(part))
|
|
||||||
else:
|
|
||||||
part.append(char)
|
|
||||||
elif inquote:
|
|
||||||
if char == "\"":
|
|
||||||
inquote = False
|
|
||||||
parts.append("".join(part))
|
|
||||||
else:
|
|
||||||
part.append(char)
|
|
||||||
else:
|
|
||||||
if char == "(":
|
|
||||||
inparen = True
|
|
||||||
elif char == "\"":
|
|
||||||
inquote = True
|
|
||||||
elif char == " ":
|
|
||||||
part = []
|
|
||||||
else:
|
|
||||||
part.append(char)
|
|
||||||
|
|
||||||
return parts[-1]
|
|
||||||
|
|
||||||
def cleanupmb(mb, sep, client):
|
|
||||||
(code, data) = client.list("%s%s" % (mb, sep), "%")
|
|
||||||
if code == "OK":
|
|
||||||
for si in data:
|
|
||||||
if si is not None:
|
|
||||||
submb = extractmb(si)
|
|
||||||
cleanupmb(submb, sep, client)
|
|
||||||
else:
|
|
||||||
raise Exception, "Failure while cleaning up '%s'" % mb
|
|
||||||
client.unsubscribe(mb)
|
|
||||||
(code, data) = client.delete(mb)
|
|
||||||
if code == "OK":
|
|
||||||
print "mailbox '%s' deleted" % mb
|
|
||||||
else:
|
|
||||||
print "mailbox '%s' coult NOT be deleted (code = '%s')" % (mb, code)
|
|
||||||
|
|
||||||
def imapCleanup(imaphost, imapport, username, userpass):
|
|
||||||
print "Starting IMAP cleanup"
|
|
||||||
client = imaplib.IMAP4(imaphost, imapport)
|
|
||||||
(code, data) = client.login(username, userpass)
|
|
||||||
if code != "OK":
|
|
||||||
raise Exception, "Login failure"
|
|
||||||
|
|
||||||
print "Logged in as '%s'" % username
|
|
||||||
|
|
||||||
sep = getsep(client)
|
|
||||||
if not sep:
|
|
||||||
client.logout()
|
|
||||||
return
|
|
||||||
|
|
||||||
for foldername in ("Sync Issues", "Junk E-mail",
|
|
||||||
"INBOX%sSync Issues" % sep, "INBOX%sJunk E-mail" % sep):
|
|
||||||
(code, data) = client.list(foldername, "%")
|
|
||||||
if code == "OK":
|
|
||||||
for si in data:
|
|
||||||
if si is not None:
|
|
||||||
mb = extractmb(si)
|
|
||||||
cleanupmb(mb, sep, client)
|
|
||||||
client.logout()
|
|
||||||
|
|
||||||
def mapistoreCleanup(mapistorefolder, username):
|
|
||||||
print "Starting MAPIstore cleanup"
|
|
||||||
|
|
||||||
# delete the user's folder under the mapistore and under mapistore/SOGo
|
|
||||||
mapistoreUserDir = "%s/%s" % (mapistorefolder, username)
|
|
||||||
for dirpath, dirnames, filenames in os.walk(mapistoreUserDir):
|
|
||||||
for f in filenames:
|
|
||||||
if f != "password":
|
|
||||||
os.unlink("%s/%s" % (dirpath,f))
|
|
||||||
break #one level only
|
|
||||||
|
|
||||||
shutil.rmtree("%s/SOGo/%s" % (mapistorefolder, username), ignore_errors=True)
|
|
||||||
|
|
||||||
def ldbCleanup(sambaprivate, username):
|
|
||||||
conn = ldb.Ldb("%s/openchange.ldb" % (sambaprivate))
|
|
||||||
# find the DN of the user
|
|
||||||
entries = conn.search(None, expression="(cn=%s)" % (username), scope=ldb.SCOPE_SUBTREE)
|
|
||||||
if not entries:
|
|
||||||
print "cn = %s not found in openchange.ldb" %(username)
|
|
||||||
return
|
|
||||||
|
|
||||||
for entry in entries:
|
|
||||||
# search again, but with the user's DN as a base
|
|
||||||
subentries = conn.search(entry.dn.extended_str(), expression="(distinguishedName=*)", scope=ldb.SCOPE_SUBTREE)
|
|
||||||
for subentry in subentries:
|
|
||||||
print "Deleting %s" % (subentry.dn)
|
|
||||||
conn.delete(subentry.dn)
|
|
||||||
|
|
||||||
def mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
|
|
||||||
try:
|
|
||||||
import MySQLdb
|
|
||||||
except ImportError:
|
|
||||||
msg ="""The python 'MySQLdb' module is not available
|
|
||||||
On Debian based distro, install it using 'apt-get install python-mysqlbd'
|
|
||||||
On RHEL, install it using 'yum install MySQL-python'"""
|
|
||||||
raise Exception(msg)
|
|
||||||
|
|
||||||
conn = MySQLdb.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, db=dbname)
|
|
||||||
c=conn.cursor()
|
|
||||||
tablename="socfs_%s" % (username)
|
|
||||||
c.execute("TRUNCATE TABLE %s" % tablename)
|
|
||||||
print "Table %s emptied"
|
|
||||||
|
|
||||||
|
|
||||||
def postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, username):
|
|
||||||
try:
|
|
||||||
import pg
|
|
||||||
except ImportError:
|
|
||||||
msg ="""The python 'pg' module is not available
|
|
||||||
On Debian based distro, install it using 'apt-get install python-pygresql'
|
|
||||||
On RHEL, install it using 'yum install python-pgsql'"""
|
|
||||||
raise Exception(msg)
|
|
||||||
|
|
||||||
conn = pg.connect(host=dbhost, port=int(dbport), user=dbuser, passwd=dbpass, dbname=dbname)
|
|
||||||
tablename = "socfs_%s" % username
|
|
||||||
conn.query("DELETE FROM %s" % tablename)
|
|
||||||
print "Table '%s' emptied" % tablename
|
|
||||||
|
|
||||||
def getOCSFolderInfoURL():
|
|
||||||
global sogoSysDefaultsFile, sogoUserDefaultsFile
|
|
||||||
|
|
||||||
OCSFolderInfoURL = ""
|
|
||||||
|
|
||||||
# read defaults from defaults files
|
|
||||||
# order is important, user defaults must have precedence
|
|
||||||
for f in [sogoSysDefaultsFile, sogoUserDefaultsFile]:
|
|
||||||
if os.path.exists(f):
|
|
||||||
# FIXME: this is ugly, we should have a python plist parser
|
|
||||||
# plistlib only supports XML plists.
|
|
||||||
# the following magic replaces this shell pipeline:
|
|
||||||
# sogo-tool dump-defaults -f %s | awk -F\\" '/ OCSFolderInfoURL =/ {print $2}'
|
|
||||||
p1 = subprocess.Popen(["sogo-tool", "dump-defaults", "-f", f], stdout=subprocess.PIPE)
|
|
||||||
p2 = subprocess.Popen(["awk", "-F\"", "/ OCSFolderInfoURL =/ {print $2}"], stdin=p1.stdout, stdout=subprocess.PIPE)
|
|
||||||
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
|
|
||||||
tmp = p2.communicate()[0]
|
|
||||||
|
|
||||||
if tmp: OCSFolderInfoURL = tmp
|
|
||||||
|
|
||||||
return OCSFolderInfoURL
|
|
||||||
|
|
||||||
def asCSSIdentifier(inputString):
|
|
||||||
cssEscapingCharMap = {"_" : "_U_",
|
|
||||||
"." : "_D_",
|
|
||||||
"#" : "_H_",
|
|
||||||
"@" : "_A_",
|
|
||||||
"*" : "_S_",
|
|
||||||
":" : "_C_",
|
|
||||||
"," : "_CO_",
|
|
||||||
" " : "_SP_",
|
|
||||||
"'" : "_SQ_",
|
|
||||||
"&" : "_AM_",
|
|
||||||
"+" : "_P_"}
|
|
||||||
|
|
||||||
newChars = []
|
|
||||||
|
|
||||||
for c in inputString:
|
|
||||||
if c in cssEscapingCharMap:
|
|
||||||
newChars.append(cssEscapingCharMap[c])
|
|
||||||
else:
|
|
||||||
newChars.append(c)
|
|
||||||
|
|
||||||
return "".join(newChars)
|
|
||||||
|
|
||||||
def sqlCleanup(username):
|
|
||||||
print "Starting SQL cleanup"
|
|
||||||
OCSFolderInfoURL = getOCSFolderInfoURL()
|
|
||||||
if OCSFolderInfoURL is None:
|
|
||||||
raise Exception("Couldn't fetch OCSFolderInfoURL or it is not set. the socfs_%s table should be truncated manually" % (username))
|
|
||||||
|
|
||||||
# postgresql://sogo:sogo@127.0.0.1:5432/sogo/sogo_folder_info
|
|
||||||
m = re.search("(.+)://(.+):(.+)@(.+):(\d+)/(.+)/(.+)", OCSFolderInfoURL)
|
|
||||||
|
|
||||||
if not m:
|
|
||||||
raise Exception("Unable to parse OCSFolderInfoURL: %s" % OCSFolderInfoURL)
|
|
||||||
|
|
||||||
proto = m.group(1)
|
|
||||||
dbuser = m.group(2)
|
|
||||||
dbpass = m.group(3)
|
|
||||||
dbhost = m.group(4)
|
|
||||||
dbport = m.group(5)
|
|
||||||
dbname = m.group(6)
|
|
||||||
# 7 is folderinfo table
|
|
||||||
|
|
||||||
encodedUserName = asCSSIdentifier(username)
|
|
||||||
|
|
||||||
if (proto == "postgresql"):
|
|
||||||
postgresqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
|
|
||||||
elif (proto == "mysql"):
|
|
||||||
mysqlCleanup(dbhost, dbport, dbuser, dbpass, dbname, encodedUserName)
|
|
||||||
else:
|
|
||||||
raise Exception("Unknown sql proto: %s" % (proto))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -78,6 +78,7 @@ install-arch: build-arch
|
||||||
install -D -m 644 Scripts/logrotate debian/tmp/etc/logrotate.d/sogo
|
install -D -m 644 Scripts/logrotate debian/tmp/etc/logrotate.d/sogo
|
||||||
install -d -m 750 debian/tmp/etc/sogo/
|
install -d -m 750 debian/tmp/etc/sogo/
|
||||||
install -D -m 640 Scripts/sogo.conf debian/tmp/etc/sogo/sogo.conf
|
install -D -m 640 Scripts/sogo.conf debian/tmp/etc/sogo/sogo.conf
|
||||||
|
install -D -m 755 Scripts/openchange_user_cleanup debian/tmp/usr/sbin/openchange_user_cleanup
|
||||||
|
|
||||||
# Build architecture dependant packages using the common target.
|
# Build architecture dependant packages using the common target.
|
||||||
binary-arch: build-arch install-arch
|
binary-arch: build-arch install-arch
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
NEWS
|
NEWS
|
||||||
TODO
|
TODO
|
||||||
Scripts/*.sh
|
Scripts/*.sh
|
||||||
Scripts/*.py
|
|
||||||
Scripts/updates.php
|
Scripts/updates.php
|
||||||
Apache/SOGo-apple-ab.conf
|
Apache/SOGo-apple-ab.conf
|
||||||
|
|
|
@ -83,6 +83,7 @@ install-arch: build-arch
|
||||||
install -D -m 644 Scripts/logrotate debian/tmp/etc/logrotate.d/sogo
|
install -D -m 644 Scripts/logrotate debian/tmp/etc/logrotate.d/sogo
|
||||||
install -d -m 750 debian/tmp/etc/sogo
|
install -d -m 750 debian/tmp/etc/sogo
|
||||||
install -D -m 640 Scripts/sogo.conf debian/tmp/etc/sogo/sogo.conf
|
install -D -m 640 Scripts/sogo.conf debian/tmp/etc/sogo/sogo.conf
|
||||||
|
install -D -m 755 Scripts/openchange_user_cleanup debian/tmp/usr/sbin/openchange_user_cleanup
|
||||||
|
|
||||||
# Build architecture dependant packages using the common target.
|
# Build architecture dependant packages using the common target.
|
||||||
binary-arch: build-arch install-arch
|
binary-arch: build-arch install-arch
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
NEWS
|
NEWS
|
||||||
TODO
|
TODO
|
||||||
Scripts/*.sh
|
Scripts/*.sh
|
||||||
Scripts/*.py
|
|
||||||
Scripts/updates.php
|
Scripts/updates.php
|
||||||
Apache/SOGo-apple-ab.conf
|
Apache/SOGo-apple-ab.conf
|
||||||
|
|
|
@ -147,7 +147,7 @@ rm -fr ${RPM_BUILD_ROOT}
|
||||||
# small tweak to the python script for RHEL5
|
# small tweak to the python script for RHEL5
|
||||||
# if hex(sys.hexversion) < 0x02060000
|
# if hex(sys.hexversion) < 0x02060000
|
||||||
%if %{python_sys_pyver} < 33947648
|
%if %{python_sys_pyver} < 33947648
|
||||||
sed -i 's!/usr/bin/env python!/usr/bin/env python2.6!' Scripts/openchange_cleanup.py
|
sed -i 's!/usr/bin/env python!/usr/bin/env python2.6!' Scripts/openchange_user_cleanup
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -203,6 +203,7 @@ install -d ${RPM_BUILD_ROOT}/var/run/sogo
|
||||||
install -d ${RPM_BUILD_ROOT}/var/spool/sogo
|
install -d ${RPM_BUILD_ROOT}/var/spool/sogo
|
||||||
install -d -m 750 -o %sogo_user -g %sogo_user ${RPM_BUILD_ROOT}/etc/sogo
|
install -d -m 750 -o %sogo_user -g %sogo_user ${RPM_BUILD_ROOT}/etc/sogo
|
||||||
install -m 640 -o %sogo_user -g %sogo_user Scripts/sogo.conf ${RPM_BUILD_ROOT}/etc/sogo/
|
install -m 640 -o %sogo_user -g %sogo_user Scripts/sogo.conf ${RPM_BUILD_ROOT}/etc/sogo/
|
||||||
|
install -m 755 Scripts/openchange_user_cleanup ${RPM_BUILD_ROOT}/%{_sbindir}
|
||||||
cat Apache/SOGo.conf | sed -e "s@/lib/@/%{_lib}/@g" > ${RPM_BUILD_ROOT}/etc/httpd/conf.d/SOGo.conf
|
cat Apache/SOGo.conf | sed -e "s@/lib/@/%{_lib}/@g" > ${RPM_BUILD_ROOT}/etc/httpd/conf.d/SOGo.conf
|
||||||
install -m 600 Scripts/sogo.cron ${RPM_BUILD_ROOT}/etc/cron.d/sogo
|
install -m 600 Scripts/sogo.cron ${RPM_BUILD_ROOT}/etc/cron.d/sogo
|
||||||
cp Scripts/tmpwatch ${RPM_BUILD_ROOT}/etc/cron.daily/sogo-tmpwatch
|
cp Scripts/tmpwatch ${RPM_BUILD_ROOT}/etc/cron.daily/sogo-tmpwatch
|
||||||
|
@ -239,6 +240,7 @@ rm -fr ${RPM_BUILD_ROOT}
|
||||||
%dir %attr(0700, %sogo_user, %sogo_user) %{_var}/spool/sogo
|
%dir %attr(0700, %sogo_user, %sogo_user) %{_var}/spool/sogo
|
||||||
%dir %attr(0750, root, %sogo_user) %{_sysconfdir}/sogo
|
%dir %attr(0750, root, %sogo_user) %{_sysconfdir}/sogo
|
||||||
%{_sbindir}/sogod
|
%{_sbindir}/sogod
|
||||||
|
%{_sbindir}/openchange_user_cleanup
|
||||||
%{_libdir}/libSOGo.so.*
|
%{_libdir}/libSOGo.so.*
|
||||||
%{_libdir}/libSOGoUI.so.*
|
%{_libdir}/libSOGoUI.so.*
|
||||||
%{_libdir}/libOGoContentStore.so*
|
%{_libdir}/libOGoContentStore.so*
|
||||||
|
@ -258,7 +260,7 @@ rm -fr ${RPM_BUILD_ROOT}
|
||||||
%config(noreplace) %{_sysconfdir}/cron.d/sogo
|
%config(noreplace) %{_sysconfdir}/cron.d/sogo
|
||||||
%config(noreplace) %{_sysconfdir}/httpd/conf.d/SOGo.conf
|
%config(noreplace) %{_sysconfdir}/httpd/conf.d/SOGo.conf
|
||||||
%config(noreplace) %{_sysconfdir}/sysconfig/sogo
|
%config(noreplace) %{_sysconfdir}/sysconfig/sogo
|
||||||
%doc ChangeLog NEWS Scripts/*sh Scripts/*py Scripts/updates.php Apache/SOGo-apple-ab.conf
|
%doc ChangeLog NEWS Scripts/*sh Scripts/updates.php Apache/SOGo-apple-ab.conf
|
||||||
|
|
||||||
%files -n sogo-tool
|
%files -n sogo-tool
|
||||||
%{_sbindir}/sogo-tool
|
%{_sbindir}/sogo-tool
|
||||||
|
@ -337,6 +339,9 @@ fi
|
||||||
|
|
||||||
# ********************************* changelog *************************
|
# ********************************* changelog *************************
|
||||||
%changelog
|
%changelog
|
||||||
|
* Thu Apr 17 2013 Jean Raby <jraby@inverse.ca>
|
||||||
|
- Install openchange_user_cleanup in sbindir instead of doc
|
||||||
|
|
||||||
* Wed Apr 10 2013 Jean Raby <jraby@inverse.ca>
|
* Wed Apr 10 2013 Jean Raby <jraby@inverse.ca>
|
||||||
- use %sogo_user instead of 'sogo'
|
- use %sogo_user instead of 'sogo'
|
||||||
- install a sample sogo.conf in /etc/sogo
|
- install a sample sogo.conf in /etc/sogo
|
||||||
|
|
Loading…
Reference in New Issue