├── cypress.json ├── .gitignore ├── kolab ├── addDomain.sh ├── imapproxy │ ├── stunnel.conf │ ├── nginx.conf │ ├── stunnel │ └── imapproxy.conf ├── patches │ ├── sleepTimeDomainTests.patch │ ├── setupKolabSleepDirSrv.patch │ ├── roundcube_kolab_auth_canonification.patch │ ├── pykolab_wap_client_unverified_context_localhost.patch │ ├── onlyAllowKolabUsersToAuthViaSasl.patch │ ├── roundcube_calendar_etc_timezone_T2666.patch │ ├── allowPrimaryEmailAddressFromDomain.patch │ ├── kolab_lam_invalid_mailbox_name.patch │ ├── domainAdminDefaultQuota.patch │ ├── roundcubeStorageMariadbBug4883.patch │ ├── dont_generate_attribs_when_editing.patch │ ├── lastLoginTBitsAttribute-pykolab.patch │ ├── wap_disallow_users.patch │ ├── cyrus_canonification.patch │ ├── fixPykolabIMAPKeepAlive.patch │ ├── cyrus_filter_kolab_mailboxes.patch │ ├── logLoginData.patch │ ├── disableSpamFilter2.patch │ ├── canonification_via_uid_pykolab.patch │ ├── pykolab_do_not_rename_existing_mailbox_T3315.patch │ ├── optional_disable_addressbook_export.patch │ ├── wap_api_listuserswithhash.patch │ ├── managesieveWithMessagelabel.patch │ ├── 99tbits.ldif │ ├── validateAliasDomainPostfixVirtualFileBug2658.patch │ ├── cyrus_canonification_multiple_domains.patch │ ├── canonification_via_uid_roundcube.patch │ ├── intranetToken-wap.patch │ ├── disableSpamFilter.patch │ ├── domainAdminMaxAccounts.patch │ ├── domainquotaBug2046.patch │ └── lastLoginTBitsAttribute-wap.patch ├── initHttpTunnel.sh ├── disableCanonification.sh ├── disableGuam.sh ├── disableSpamFilter.sh ├── initIMAPProxy.sh ├── initMailCatchall.sh ├── initSleepTimesForTest.sh ├── initMailForward.sh ├── Dockerfile ├── reinstall.sh ├── key │ └── devel@lists.kolab.org.asc ├── lib.sh ├── initRoundcubePlugins.sh ├── initSetupKolabPatches.sh ├── initTBitsUserTypes.php ├── reinstallDebianUbuntu.sh ├── reinstallCentOS.sh ├── initTBitsISP.sh ├── initTBitsCustomizationsDE.sh └── initMultiDomain.sh ├── dply ├── Readme.md ├── dply.sh └── reinstallKolab.sh ├── Readme.md ├── pySeleniumTests ├── Readme.md ├── testEmailSendAndReceive.py ├── testDomainAdminDefaultQuota.py ├── testEmailCatchAll.py ├── testLastLogin.py ├── configureKolabUserMailhost.py ├── testEmailCatchAllAcrossDomains.py ├── testDomainAdminMaxAccounts.py ├── testEmailSharedFolders.py ├── testRoundcubeChangePassword.py ├── testEmailForwarding.py ├── runTests.sh ├── testCreateUserAndEditSelf.py ├── testDomainAdminOverallQuota.py ├── testAutoCreateFolders.py ├── testUIDAcrossDomains.py └── testListUsersQuota.py ├── createreleases ├── Readme.md └── mirror_kolab_development.py └── cypress └── integration └── UserAllAroundCheck.js /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultCommandTimeout": 10000, 3 | "video": false 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | pySeleniumTests/ghostdriver.log 3 | pySeleniumTests/geckodriver.log 4 | kolab/key 5 | kolab/keys 6 | cypress/screenshots 7 | cypress/fixtures 8 | cypress/plugins 9 | cypress/support 10 | node_modules 11 | -------------------------------------------------------------------------------- /kolab/addDomain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ] 4 | then 5 | echo "call $0 " 6 | exit 1 7 | fi 8 | 9 | DOM=$1 10 | DC=$(echo -n $DOM | sed 's/\./,dc=/g' | sed 's/^/dc=/') 11 | 12 | echo "[$DOM] 13 | base_dn = $DC 14 | primary_mail = %(givenname)s.%(surname)s@%(domain)s 15 | " >> /etc/kolab/kolab.conf 16 | 17 | # different service-names on CentOS (httpd) and Debian (apache2) 18 | if which /etc/init.d/apache2; then 19 | /etc/init.d/apache2 reload; 20 | else 21 | service httpd reload; 22 | fi 23 | -------------------------------------------------------------------------------- /dply/Readme.md: -------------------------------------------------------------------------------- 1 | Dply 2 | === 3 | 4 | This service at https://dply.co/ is currently in Beta stage. You can specify a script that should be run when a server on DigitalOcean is started. The first two hours are for free. 5 | 6 | Dply Button 7 | === 8 | 9 | The script that we insert at https://dply.co/button is maintained in this directory: [dply.sh](dply.sh) 10 | 11 | 12 | Kolab on Dply 13 | === 14 | 15 | see also my blog post: http://www.pokorra.de/2016/12/installing-kolab-16-for-testing-on-dply-for-free-for-2-hours/ 16 | -------------------------------------------------------------------------------- /kolab/imapproxy/stunnel.conf: -------------------------------------------------------------------------------- 1 | ; Protocol version (all, SSLv2, SSLv3, TLSv1) 2 | sslVersion = TLSv1 3 | 4 | ; Some security enhancements for UNIX systems - comment them out on Win32 5 | chroot = /var/run/stunnel/ 6 | setuid = nobody 7 | setgid = nobody 8 | pid = /stunnel.pid 9 | 10 | ; Some performance tunings 11 | socket = l:TCP_NODELAY=1 12 | socket = r:TCP_NODELAY=1 13 | 14 | ; Use it for client mode 15 | client = yes 16 | ; foreground = yes 17 | 18 | ; Service-level configuration 19 | 20 | [imaps] 21 | accept = 8993 22 | connect = 993 23 | 24 | -------------------------------------------------------------------------------- /kolab/patches/sleepTimeDomainTests.patch: -------------------------------------------------------------------------------- 1 | diff --git a/kolabd/__init__.py b/kolabd/__init__.py 2 | index 92a929c..ddc7dcb 100644 3 | --- a/kolabd/__init__.py 4 | +++ b/kolabd/__init__.py 5 | @@ -288,7 +288,7 @@ class KolabDaemon(object): 6 | domain_auth[domain].start() 7 | # Pause or hammer your LDAP server to death 8 | if len(added_domains) >= 5: 9 | - time.sleep(10) 10 | + time.sleep(1) 11 | 12 | for domain in removed_domains: 13 | domain_auth[domain].terminate() 14 | 15 | -------------------------------------------------------------------------------- /kolab/patches/setupKolabSleepDirSrv.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/setup/setup_ldap.py b/pykolab/setup/setup_ldap.py 2 | index 04311d2..c587ee4 100644 3 | --- a/pykolab/setup/setup_ldap.py 4 | +++ b/pykolab/setup/setup_ldap.py 5 | @@ -415,6 +415,8 @@ ServerAdminPwd = %(admin_pass)s 6 | subprocess.call(['/usr/sbin/service','dirsrv','start']) 7 | else: 8 | log.error(_("Could not start the directory server service.")) 9 | + 10 | + time.sleep(10) 11 | 12 | if os.path.isfile('/bin/systemctl'): 13 | subprocess.call(['/bin/systemctl', 'enable', 'dirsrv.target']) 14 | 15 | -------------------------------------------------------------------------------- /kolab/initHttpTunnel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##################################################################################### 4 | # make sure that kolab webadmin works even on my virtual machine with routed port 80 5 | ##################################################################################### 6 | sed -r -i \ 7 | -e '/api_url/d' \ 8 | -e "s#\[kolab_wap\]#[kolab_wap]\napi_url = http://localhost/kolab-webadmin/api#g" \ 9 | /etc/kolab/kolab.conf 10 | 11 | sed -i -e "s#?># \$config['kolab_files_server_url'] = 'http://localhost/chwala/';\n\n?>#g" /etc/roundcubemail/config.inc.php 12 | -------------------------------------------------------------------------------- /kolab/imapproxy/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | error_log /var/log/nginx/error.log info; 8 | 9 | mail { 10 | auth_http localhost:81/auth; 11 | 12 | proxy on; 13 | 14 | server { 15 | listen 143; 16 | protocol imap; 17 | } 18 | } 19 | 20 | http { 21 | server { 22 | listen localhost:81; 23 | location = /auth { 24 | add_header Auth-Status OK; 25 | add_header Auth-Server 127.0.0.1; # backend ip 26 | add_header Auth-Port 8993; # backend port 27 | return 200; 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /kolab/disableCanonification.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # remove canonification 4 | # we can only keep canonification if Cyrus was built with the patch 5 | # https://github.com/TBits/KolabScripts/blob/Kolab16/kolab/patches/cyrus_canonification_multiple_domains.patch 6 | # which has been accepted upstream: 7 | # https://github.com/cyrusimap/cyrus-imapd/commit/1e21647e0741b41c3607de54ab8cda6414deabaa#diff-a53b51d7c393e8407ee2e194ab397f0f 8 | 9 | sed -i \ 10 | -e 's/^auth_mech/#auth_mech/g' \ 11 | -e 's/^pts_module/#pts_module/g' \ 12 | -e 's/^ldap_/#ldap_/g' \ 13 | /etc/imapd.conf 14 | sed -i \ 15 | -e 's/ptloader/#ptloader/g' \ 16 | /etc/cyrus.conf 17 | 18 | service cyrus-imapd restart 19 | -------------------------------------------------------------------------------- /kolab/disableGuam.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ############################################ 11 | # disable guam if it does not work 12 | # eg https://git.kolab.org/T1305 13 | ############################################ 14 | 15 | systemctl stop guam 16 | systemctl disable guam 17 | 18 | systemctl stop cyrus-imapd 19 | 20 | # make sure that cyrus is listening on 993 and 143 instead of 9993 21 | sed -r -i \ 22 | -e 's#imaps.*9993.*#imap cmd="imapd" listen="imap" prefork=5\n imaps cmd="imapd -s" listen="imaps" prefork=1#g' \ 23 | /etc/cyrus.conf 24 | 25 | systemctl start cyrus-imapd 26 | -------------------------------------------------------------------------------- /kolab/disableSpamFilter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | if [ -z $APPLYPATCHES ] 11 | then 12 | APPLYPATCHES=1 13 | fi 14 | 15 | ##################################################################################### 16 | # disable the spam filter, if it is not needed 17 | ##################################################################################### 18 | 19 | 20 | if [ $APPLYPATCHES -eq 1 ]; then 21 | echo "disabling amavis and clamd" 22 | patch -p1 -i `pwd`/patches/disableSpamFilter.patch -d $pythonDistPackages || exit -1 23 | patch -p1 -i `pwd`/patches/disableSpamFilter2.patch -d /usr || exit -1 24 | fi 25 | -------------------------------------------------------------------------------- /kolab/patches/roundcube_kolab_auth_canonification.patch: -------------------------------------------------------------------------------- 1 | --- a/plugins/kolab_auth/kolab_auth.php 2017-07-26 21:55:57.528181599 +0200 2 | +++ b/plugins/kolab_auth/kolab_auth.php 2017-07-26 22:18:52.357323642 +0200 3 | @@ -383,7 +383,17 @@ 4 | $args['abort'] = true; 5 | return $args; 6 | } 7 | - 8 | +if (strlen($user) > strlen('pop-') && substr($user,0,4) == 'pop-') { 9 | + $popuser=file_get_contents("/etc/cyrus-pop-mappings.txt"); 10 | + $users=explode("\n", $popuser); 11 | + foreach($users as $line) { 12 | + $data = explode(';', $line); 13 | + if ($data[0] == $user) { 14 | + $user = $data[1]; 15 | + break; 16 | + } 17 | + } 18 | +} 19 | // temporarily set the current username to the one submitted 20 | $this->username = $user; 21 | 22 | -------------------------------------------------------------------------------- /kolab/initIMAPProxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | proxy=up-imapproxy 4 | #proxy=nginx 5 | 6 | if [[ "$proxy" = "up-imapproxy" ]] 7 | then 8 | yum install -y up-imapproxy 9 | cp imapproxy/imapproxy.conf /etc/ 10 | service imapproxy start 11 | chkconfig imapproxy on 12 | fi 13 | 14 | if [[ "$proxy" = "nginx" ]] 15 | then 16 | yum install -y stunnel nginx 17 | cp imapproxy/stunnel.conf /etc/stunnel/stunnel.conf 18 | cp imapproxy/stunnel /etc/init.d/stunnel 19 | chmod a+x /etc/init.d/stunnel 20 | service stunnel start 21 | chkconfig stunnel on 22 | 23 | cp imapproxy/nginx.conf /etc/nginx/nginx.conf 24 | service nginx start 25 | chkconfig nginx on 26 | fi 27 | 28 | # change configuration of roundcube, now use the imap proxy 29 | sed -i "s/config\['default_port'\] = 143/config['default_port'] = 8143/g" /etc/roundcubemail/config.inc.php 30 | 31 | -------------------------------------------------------------------------------- /kolab/patches/pykolab_wap_client_unverified_context_localhost.patch: -------------------------------------------------------------------------------- 1 | --- a/pykolab/wap_client/__init__.py 2015-05-12 10:42:16.980500436 +0200 2 | +++ b/pykolab/wap_client/__init__.py 2015-05-12 10:41:47.592501809 +0200 3 | @@ -3,6 +3,7 @@ 4 | import httplib 5 | import urllib 6 | import sys 7 | +import ssl 8 | from urlparse import urlparse 9 | 10 | import pykolab 11 | @@ -95,7 +96,10 @@ 12 | 13 | if conn == None: 14 | if API_SSL: 15 | - conn = httplib.HTTPSConnection(API_HOSTNAME, API_PORT) 16 | + if API_HOSTNAME == "localhost": 17 | + conn = httplib.HTTPSConnection(API_HOSTNAME, API_PORT, context=ssl._create_unverified_context()) 18 | + else: 19 | + conn = httplib.HTTPSConnection(API_HOSTNAME, API_PORT) 20 | else: 21 | conn = httplib.HTTPConnection(API_HOSTNAME, API_PORT) 22 | 23 | -------------------------------------------------------------------------------- /kolab/patches/onlyAllowKolabUsersToAuthViaSasl.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py 2 | index e9dff51..7ad4b76 100644 3 | --- a/pykolab/auth/ldap/__init__.py 4 | +++ b/pykolab/auth/ldap/__init__.py 5 | @@ -160,12 +160,12 @@ class LDAP(Base): 6 | log.error(_l("Authentication cache failed: %r") % (errmsg)) 7 | 8 | try: 9 | - user_filter = self.config_get_raw('user_filter') % ( 10 | + user_filter = self.config_get_raw('user_filter' if login[0] == 'cyrus-admin' else 'kolab_user_filter') % ( 11 | {'base_dn': base_dn} 12 | ) 13 | 14 | except TypeError: 15 | - user_filter = self.config_get_raw('user_filter') 16 | + user_filter = self.config_get_raw('user_filter' if login[0] == 'cyrus-admin' else 'kolab_user_filter') 17 | 18 | _filter = '(&(|' 19 | 20 | -------------------------------------------------------------------------------- /kolab/patches/roundcube_calendar_etc_timezone_T2666.patch: -------------------------------------------------------------------------------- 1 | --- a/plugins/calendar/drivers/kolab/kolab_calendar.php 2017-10-18 14:33:09.229001065 +0200 2 | +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php 2017-10-18 14:33:54.520723940 +0200 3 | @@ -460,6 +460,14 @@ 4 | $links = $event['links']; 5 | unset($event['links']); 6 | 7 | + // fix timezone Etc/GMT-1: Not a valid olson timezone 8 | + if (array_key_exists('start', $event) && $event['start']->timezone == 'Etc/GMT-1') { 9 | + $event['start']->setTimezone(new DateTimeZone('Europe/Berlin')); 10 | + } 11 | + if (array_key_exists('end', $event) && $event['end']->timezone == 'Etc/GMT-1') { 12 | + $event['end']->setTimezone(new DateTimeZone('Europe/Berlin')); 13 | + } 14 | + 15 | //generate new event from RC input 16 | $object = $this->_from_driver_event($event); 17 | $saved = $this->storage->save($object, 'event'); 18 | -------------------------------------------------------------------------------- /dply/dply.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | swapsize="1GB" 4 | branch="Kolab16" 5 | # recommended: use another password than test for user: cn=Directory Manager 6 | pwd="test" 7 | 8 | # add swap space to deal with small amount of RAM 9 | fallocate -l $swapsize /swapfile1; 10 | mkswap /swapfile1 11 | swapon /swapfile1 12 | echo "/swapfile1 swap swap defaults 0 0" >> /etc/fstab 13 | 14 | yum install -y wget 15 | cd /root 16 | wget https://raw.githubusercontent.com/TBits/KolabScripts/$branch/dply/reinstallKolab.sh -O dply$branch.sh 17 | chmod a+x dply$branch.sh 18 | sed -i "s#KolabWinterfell#$branch#g" dply$branch.sh 19 | 20 | # you can rerun this script if you want to reinstall Kolab. 21 | ./dply$branch.sh $pwd 22 | 23 | # next steps: 24 | # http://your.ip/kolab-webadmin, login with user: cn=Directory Manager, password: as defined at the top of this script, default: test 25 | # http://your.ip/roundcubemail 26 | -------------------------------------------------------------------------------- /kolab/initMailCatchall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##################################################################################### 4 | # enable catchall mail addresses 5 | ##################################################################################### 6 | 7 | filename=/etc/postfix/ldap/virtual_alias_maps_catchall.cf 8 | filename_3=/etc/postfix/ldap/virtual_alias_maps_catchall_3.cf 9 | cp /etc/postfix/ldap/virtual_alias_maps.cf $filename 10 | sed -i -e 's#^query_filter = .*#query_filter = (\&(alias=catchall@%d)(objectclass=inetorgperson))#g' $filename 11 | 12 | cp /etc/postfix/ldap/virtual_alias_maps_3.cf $filename_3 13 | sed -i -e 's#^query_filter = .*#query_filter = (\&(alias=catchall@%d)(objectclass=inetorgperson))#g' $filename_3 14 | 15 | sed -i -e "s#^virtual_alias_maps = \(.*\)#virtual_alias_maps = \1, ldap:$filename, ldap:$filename_3#" /etc/postfix/main.cf 16 | 17 | postmap $filename 18 | postmap $filename_3 19 | service postfix restart 20 | -------------------------------------------------------------------------------- /kolab/initSleepTimesForTest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ##################################################################################### 11 | #reduce the sleep time between adding domains, see https://issues.kolab.org/show_bug.cgi?id=2491 12 | ##################################################################################### 13 | sed -r -i -e "s/\[kolab\]/[kolab]\ndomain_sync_interval = 10/g" /etc/kolab/kolab.conf 14 | 15 | patch -p1 -i `pwd`/patches/sleepTimeDomainTests.patch -d $pythonDistPackages 16 | 17 | if [ -f /bin/systemctl -a -f /etc/debian_version ] 18 | then 19 | /bin/systemctl restart kolab-server 20 | elif [ -f /bin/systemctl ] 21 | then 22 | /bin/systemctl restart kolabd.service 23 | elif [ -f /sbin/service ] 24 | then 25 | service kolabd restart 26 | elif [ -f /usr/sbin/service ] 27 | then 28 | service kolab-server restart 29 | fi 30 | -------------------------------------------------------------------------------- /kolab/patches/allowPrimaryEmailAddressFromDomain.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py 2 | index 7ba3504..0cdd2a3 100644 3 | --- a/pykolab/auth/ldap/__init__.py 4 | +++ b/pykolab/auth/ldap/__init__.py 5 | @@ -969,8 +969,10 @@ class LDAP(Base): 6 | if 'preferredlanguage' not in entry: 7 | entry['preferredlanguage'] = conf.get('kolab', 'default_locale') 8 | 9 | - # Primary mail address 10 | - if primary_mail is not None: 11 | + # Patch by tbits: only apply primary mail address policy if the mail address does not end with the proper domain name 12 | + if not primary_mail == None and (entry.has_key(primary_mail_attribute) and entry[primary_mail_attribute].endswith(self.domain)): 13 | + primary_mail_address = entry[primary_mail_attribute] 14 | + elif not primary_mail == None: 15 | primary_mail_address = conf.plugins.exec_hook( 16 | "set_primary_mail", 17 | kw={ 18 | -------------------------------------------------------------------------------- /kolab/patches/kolab_lam_invalid_mailbox_name.patch: -------------------------------------------------------------------------------- 1 | diff --git a/usr/lib/python2.7/site-packages/cyruslib.py.orig b/usr/lib/python2.7/site-packages/cyruslib.py 2 | index 9e42f39..79f954c 100644 3 | --- a/cyruslib.py 4 | +++ b/cyruslib.py 5 | @@ -29,7 +29,7 @@ and defines new CYRUS class for cyrus imapd commands 6 | 7 | """ 8 | 9 | -from sys import exit, stdout 10 | +from sys import exit, stdout, stderr 11 | 12 | try: 13 | import imaplib 14 | @@ -567,7 +567,12 @@ class CYRUS: 15 | def lam(self, mailbox): 16 | """List ACLs""" 17 | self.__prepare('GETACL', mailbox) 18 | - res, acl = self.__docommand("getacl", self.decode(mailbox)) 19 | + try: 20 | + res, acl = self.__docommand("getacl", self.decode(mailbox)) 21 | + except Exception, info: 22 | + # show error but continue 23 | + stderr.write("Error: %s\n" % (info)); 24 | + return {} 25 | acls = {} 26 | aclList = splitquote(acl.pop().strip()) 27 | del aclList[0] # mailbox 28 | -------------------------------------------------------------------------------- /kolab/patches/domainAdminDefaultQuota.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php 2 | index e4d4727..009e1c8 100644 3 | --- a/lib/kolab_client_task.php 4 | +++ b/lib/kolab_client_task.php 5 | @@ -1094,6 +1094,18 @@ class kolab_client_task 6 | reset($types); 7 | 8 | $data['type_id'] = $type = ($default !== null ? $default : key($types)); 9 | + 10 | + if ($name == "user") { 11 | + // get the default mailquota of the domain admin 12 | + $result = $this->api_get('domain.domainadmin_info', array('variablename' => 'tbitskolabdefaultquota')); 13 | + $domaininfo = $result->get(); 14 | + 15 | + $defaultdomainquota = $domaininfo['tbitskolabdefaultquota']; 16 | + if (isset($defaultdomainquota)) { 17 | + // set the default mail quota 18 | + $data['mailquota'] = $defaultdomainquota; 19 | + } 20 | + } 21 | } 22 | 23 | if ($type) { 24 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Attention 2 | ========= 3 | 4 | Please use the scripts only if you understand them. We don't give any guarantuee that they will work or will destroy your data. 5 | 6 | 7 | Contributing 8 | ============ 9 | 10 | You are welcome to provide Pull requests on Github, if you spot a problem or want to suggest an improvement! 11 | 12 | 13 | Layout 14 | ====== 15 | 16 | We have organised the scripts according to the releases. 17 | Please have a look in the branches. 18 | So if you want to work with Kolab 16, go to https://github.com/TBits/KolabScripts/tree/Kolab16 19 | 20 | For more details, please visit the wiki at https://github.com/tbits/kolabscripts/wiki 21 | 22 | 23 | Tests with Cypress 24 | ================== 25 | 26 | prepare installation: 27 | 28 | npm install 29 | 30 | Test with a GUI: 31 | 32 | LANG=en CYPRESS_baseUrl=https://localhost ./node_modules/.bin/cypress open 33 | 34 | Test on the command line: 35 | 36 | LANG=en CYPRESS_baseUrl=https://localhost ./node_modules/.bin/cypress run --config video=false 37 | 38 | -------------------------------------------------------------------------------- /kolab/patches/roundcubeStorageMariadbBug4883.patch: -------------------------------------------------------------------------------- 1 | --- a/plugins/libkolab/lib/kolab_storage_cache.php 2020-04-02 10:37:32.846215608 +0200 2 | +++ b/plugins/libkolab/lib/kolab_storage_cache.php 2020-04-02 10:43:26.215236135 +0200 3 | @@ -953,9 +953,11 @@ 4 | if ($object) { 5 | $sql_data = $this->_serialize($object); 6 | 7 | - // Skip multi-folder insert for all databases but MySQL 8 | + // Skip multi-folder insert for all databases 9 | // In Oracle we can't put long data inline, others we don't support yet 10 | - if (strpos($this->db->db_provider, 'mysql') !== 0) { 11 | + // For TBits.net, we had an issue with too long statements, even in MySQL. 12 | + // see https://lists.kolabsys.com/pipermail/bugzilla/2015-March/023498.html 13 | + if (true) { 14 | $extra_args = array(); 15 | $params = array($this->folder_id, $msguid, $object['uid'], $sql_data['changed'], 16 | $sql_data['data'], $sql_data['tags'], $sql_data['words']); 17 | -------------------------------------------------------------------------------- /kolab/patches/dont_generate_attribs_when_editing.patch: -------------------------------------------------------------------------------- 1 | --- a/lib/api/kolab_api_service_form_value.php 2017-08-02 13:50:21.742083841 +0200 2 | +++ b/lib/api/kolab_api_service_form_value.php 2017-08-02 14:01:03.165836381 +0200 3 | @@ -67,6 +67,7 @@ 4 | $attribs = $this->object_type_attributes($postdata['object_type'], $postdata['type_id'], $type_key); 5 | $attributes = (array) $postdata['attributes']; 6 | $result = array(); 7 | + $conf = Conf::get_instance(); 8 | 9 | $postdata['type_key'] = $type_key; 10 | 11 | @@ -88,6 +89,12 @@ 12 | } 13 | } 14 | 15 | + if (!empty($postdata['id']) && $conf->get('kolab_wap', 'admin_auto_fields_rw') == True && 16 | + $attr_name != 'cn' && $attr_name != 'displayname'){ 17 | + // do not modify the primary e-mail address or the uid after the user has been created already 18 | + continue; 19 | + } 20 | + 21 | Log::trace("Executing method $method_name"); 22 | $result[$attr_name] = $this->{$method_name}($postdata, $attribs); 23 | } 24 | -------------------------------------------------------------------------------- /kolab/imapproxy/stunnel: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # stunnel SysV startup file 3 | # Copyright by Michal Trojnara 2002,2007,2008 4 | 5 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 6 | DAEMON=/usr/bin/stunnel 7 | PIDFILE=/var/run/stunnel/stunnel.pid 8 | 9 | # Source function library. 10 | . /etc/rc.d/init.d/functions 11 | 12 | test -f $DAEMON || exit 0 13 | 14 | case "$1" in 15 | start) 16 | echo -n "Starting universal SSL tunnel: stunnel" 17 | daemon $DAEMON || echo -n " failed" 18 | echo "." 19 | ;; 20 | stop) 21 | echo -n "Stopping universal SSL tunnel: stunnel" 22 | if test -r $PIDFILE; then 23 | kill `cat $PIDFILE` 2> /dev/null || echo -n " failed" 24 | else 25 | echo -n " no PID file" 26 | fi 27 | echo "." 28 | ;; 29 | restart|force-reload) 30 | echo "Restarting universal SSL tunnel" 31 | $0 stop 32 | sleep 1 33 | $0 start 34 | echo "done." 35 | ;; 36 | *) 37 | N=${0##*/} 38 | N=${N#[SK]??} 39 | echo "Usage: $N {start|stop|restart|force-reload}" >&2 40 | exit 1 41 | ;; 42 | esac 43 | 44 | exit 0 45 | 46 | -------------------------------------------------------------------------------- /pySeleniumTests/Readme.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ============ 3 | Please also see http://www.pokorra.de/2013/11/kolab-integration-tests-with-selenium-and-python/ 4 | 5 | Install the latest Chromium and geckodriver: 6 | 7 | ```sh 8 | yum install gtk3 dbus-glib chromedriver chromium-headless 9 | cd /root 10 | version="v0.24.0" 11 | wget https://github.com/mozilla/geckodriver/releases/download/$version/geckodriver-$version-linux64.tar.gz 12 | tar xzf geckodriver-$version-linux64.tar.gz 13 | ln -s /root/geckodriver /usr/bin/geckodriver 14 | ``` 15 | 16 | Install Xvfb and pip 17 | ```sh 18 | yum install Xvfb python2-pip 19 | ``` 20 | 21 | Install Selenium from pip: 22 | 23 | ```sh 24 | pip2 install selenium pyvirtualdisplay 25 | ``` 26 | 27 | Please also install the mail package because it is required by some tests: 28 | 29 | ```sh 30 | yum install mail 31 | ``` 32 | 33 | Create a profile for the tests: 34 | 35 | ```sh 36 | xvfb-run firefox -CreateProfile "SeleniumTests /tmp/SeleniumTests" 37 | ``` 38 | 39 | Then you can just start the tests like this: 40 | ```sh 41 | cd KolabScripts/pySeleniumTests 42 | ./testAutoCreateFolders.py 43 | ./testCreateUserAndEditSelf.py KolabWAPCreateUserAndEditSelf.test_edit_user_himself 44 | ``` 45 | 46 | To run all tests: 47 | ```sh 48 | ./runTests.sh 49 | ``` 50 | -------------------------------------------------------------------------------- /dply/reinstallKolab.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ]; then 4 | pwd="test" 5 | else 6 | pwd=$1 7 | fi 8 | 9 | if [ -z "$2" ]; then 10 | branch="KolabWinterfell" 11 | else 12 | branch=$1 13 | fi 14 | 15 | # we need a fully qualified domain name 16 | hostnamectl set-hostname $branch.demo.example.org 17 | 18 | yum install -y wget which bzip2 mailx selinux-policy-targeted 19 | # disable SELinux 20 | sed -i 's/enforcing/permissive/g' /etc/selinux/config 21 | setenforce 0 22 | 23 | wget -O $branch.tar.gz https://github.com/TBits/KolabScripts/archive/$branch.tar.gz 24 | tar xzf $branch.tar.gz 25 | cd KolabScripts-$branch/kolab 26 | # to make Kolab run on 512 MB of RAM on dply.co, disable Amavis and ClamAV 27 | export WITHOUTSPAMFILTER=1 28 | echo "y" | ./reinstall.sh || exit 1 29 | ./initSetupKolabPatches.sh || exit 1 30 | 31 | setup-kolab --default --mysqlserver=new --timezone=Europe/Berlin --directory-manager-pwd=$pwd || exit 1 32 | 33 | # is guam installed at all? 34 | if [[ "`rpm -qa | grep guam`" == "" ]] 35 | then 36 | # make sure that cyrus is listening on the correct ports 37 | ./disableGuam.sh 38 | fi 39 | 40 | # next steps: 41 | # http://your.ip/kolab-webadmin, login with user: cn=Directory Manager, password: as passed to this script, default: test 42 | # http://your.ip/roundcubemail 43 | -------------------------------------------------------------------------------- /kolab/patches/lastLoginTBitsAttribute-pykolab.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py 2 | index 4b5f876..7ba3504 100644 3 | --- a/pykolab/auth/ldap/__init__.py 4 | +++ b/pykolab/auth/ldap/__init__.py 5 | @@ -325,6 +325,11 @@ class LDAP(Base): 6 | self._disconnect() 7 | return False 8 | 9 | + # store current unix time in last login 10 | + self.bind = False 11 | + if self.config_get('setlastlogin') == "True" and not "uid=cyrus-admin" in entry_dn: 12 | + self.set_entry_attribute(entry_dn, "tbitsKolabLastLogin", str(int(time.time()))) 13 | + 14 | try: 15 | auth_cache.set_entry(_filter, entry_dn) 16 | except Exception as errmsg: 17 | @@ -369,6 +374,11 @@ class LDAP(Base): 18 | self._disconnect() 19 | return False 20 | 21 | + # store current unix time in last login 22 | + self.bind = False 23 | + if self.config_get('setlastlogin') == "True" and not "uid=cyrus-admin" in entry_dn: 24 | + self.set_entry_attribute(entry_dn, "tbitsKolabLastLogin", str(int(time.time()))) 25 | + 26 | except ldap.NO_SUCH_OBJECT as errmsg: 27 | log.debug( 28 | _l("Error occured, there is no such object: %r") % ( 29 | -------------------------------------------------------------------------------- /kolab/patches/wap_disallow_users.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/kolab_api_controller.php b/lib/kolab_api_controller.php 2 | index fd63c93..9b3b20f 100644 3 | --- a/lib/kolab_api_controller.php 4 | +++ b/lib/kolab_api_controller.php 5 | @@ -345,6 +345,19 @@ class kolab_api_controller 6 | $result['info'] = $service->parse_result_attributes('user', $attributes); 7 | } 8 | 9 | + // if one or several domains are specified in kolab_webadmin_disabled_for_users, then only admin users from that domain can use the kolab webadmin 10 | + $kolab_webadmin_disabled_for_users = $this->config->get('kolab_wap', 'kolab_webadmin_disabled_for_users'); 11 | + if (!(empty($kolab_webadmin_disabled_for_users))) { 12 | + if (($user_dn != 'cn=Directory Manager') && !in_array('tbitskolabdomainadmin', $result['info']['objectclass'])) { 13 | + $domains_users_disabled = explode(',', $kolab_webadmin_disabled_for_users); 14 | + foreach ($domains_users_disabled as $domain_users_disabled) { 15 | + if ($domain_users_disabled == 'allusers' || strpos($user_dn, 'ou=People,dc='.implode(',dc=', explode('.', $domain_users_disabled))) > 0) { 16 | + return false; 17 | + } 18 | + } 19 | + } 20 | + } 21 | + 22 | return $result; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /kolab/initMailForward.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ##################################################################################### 4 | # enable mail forwarding only users 5 | ##################################################################################### 6 | 7 | cp /etc/postfix/ldap/virtual_alias_maps.cf /etc/postfix/ldap/virtual_alias_maps_forward.cf 8 | sed -i -e 's#^query_filter = .*#query_filter = (\&(|(mail=%s)(alias=%s))(objectclass=inetorgperson))#g' /etc/postfix/ldap/virtual_alias_maps_forward.cf 9 | sed -i -e 's#^result_attribute = .*#result_attribute = mailForwardingAddress#g' /etc/postfix/ldap/virtual_alias_maps_forward.cf 10 | 11 | cp /etc/postfix/ldap/virtual_alias_maps_3.cf /etc/postfix/ldap/virtual_alias_maps_forward_3.cf 12 | sed -i -e 's#^query_filter = .*#query_filter = (\&(|(mail=%s)(alias=%s))(objectclass=inetorgperson))#g' /etc/postfix/ldap/virtual_alias_maps_forward_3.cf 13 | sed -i -e 's#^result_attribute = .*#result_attribute = mailForwardingAddress#g' /etc/postfix/ldap/virtual_alias_maps_forward_3.cf 14 | 15 | sed -i -e 's#ldap:/etc/postfix/ldap/virtual_alias_maps.cf#ldap:/etc/postfix/ldap/virtual_alias_maps.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_forward.cf#' /etc/postfix/main.cf 16 | sed -i -e 's#ldap:/etc/postfix/ldap/virtual_alias_maps_3.cf#ldap:/etc/postfix/ldap/virtual_alias_maps_3.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_forward_3.cf#' /etc/postfix/main.cf 17 | 18 | service postfix restart 19 | 20 | -------------------------------------------------------------------------------- /kolab/patches/cyrus_canonification.patch: -------------------------------------------------------------------------------- 1 | --- a/imap/global.c 2017-07-21 11:53:42.322075657 +0200 2 | +++ b/imap/global.c 2017-07-21 11:54:45.929324967 +0200 3 | @@ -500,6 +500,35 @@ 4 | } 5 | } 6 | } 7 | + else if (len > 4 && strncasecmp(user, "pop-", 4) == 0){ 8 | + syslog(LOG_ERR, "User %s attempts to authenticate.", user); 9 | + FILE *fp = fopen("/etc/cyrus-pop-mappings.txt", "r"); 10 | + char * line = NULL; 11 | + size_t linelen = 0; 12 | + ssize_t read; 13 | + if (fp){ 14 | + //syslog(LOG_ERR, "cyrus-pop-mappings.txt has been read"); 15 | + while ((read = getline(&line, &linelen, fp)) != -1) { 16 | + if (line[read - 1] == '\n') 17 | + { 18 | + line[read - 1] = '\0'; 19 | + --read; 20 | + } 21 | + //syslog(LOG_ERR, "Line %s", line); 22 | + if (read > len && strncasecmp(line, user, len) == 0) { 23 | + char* newname=line+len+1; 24 | + snprintf(buf, sizeof(buf), "%s", newname); 25 | + user = buf; 26 | + syslog(LOG_ERR, "Using this address for authentication: %s", user); 27 | + break; 28 | + } 29 | + } 30 | + fclose(fp); 31 | + if (line) { 32 | + free(line); 33 | + } 34 | + } 35 | + } 36 | } 37 | 38 | return auth_canonifyid(user, 0); 39 | -------------------------------------------------------------------------------- /kolab/patches/fixPykolabIMAPKeepAlive.patch: -------------------------------------------------------------------------------- 1 | --- a/pykolab/imap/__init__.py 2016-12-20 11:33:39.935615623 +0100 2 | +++ b/pykolab/imap/__init__.py 2016-12-20 11:35:05.840123506 +0100 3 | @@ -848,14 +848,19 @@ 4 | def _set_socket_keepalive(self, sock): 5 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 6 | 7 | - with open('/proc/sys/net/ipv4/tcp_keepalive_time', 'r') as f: 8 | + try: 9 | + with open('/proc/sys/net/ipv4/tcp_keepalive_time', 'r') as f: 10 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, (int)(f.read())) 11 | 12 | - with open('/proc/sys/net/ipv4/tcp_keepalive_intvl', 'r') as f: 13 | + with open('/proc/sys/net/ipv4/tcp_keepalive_intvl', 'r') as f: 14 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, (int)(f.read())) 15 | 16 | - with open('/proc/sys/net/ipv4/tcp_keepalive_probes', 'r') as f: 17 | + with open('/proc/sys/net/ipv4/tcp_keepalive_probes', 'r') as f: 18 | sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, (int)(f.read())) 19 | + except: 20 | + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7200) 21 | + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 75) 22 | + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 9) 23 | 24 | def _set_kolab_mailfolder_acls(self, acls, folder=None, update=False): 25 | # special case, folder has no ACLs assigned and update was requested, 26 | -------------------------------------------------------------------------------- /kolab/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | # packages should be installed with docs 4 | RUN sed -i -e "s/tsflags=nodocs/#tsflags=nodocs/g" /etc/yum.conf 5 | 6 | ENV container docker 7 | RUN yum -y update; yum clean all 8 | 9 | RUN yum -y install selinux-policy passwd vim tar wget && yum clean all 10 | 11 | # set the initial root password so that you can login with docker attach 12 | RUN echo root:root | chpasswd 13 | 14 | # disable SELINUX 15 | RUN sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config 16 | 17 | WORKDIR /root 18 | RUN wget -O kolabscripts.tar.gz https://github.com/TBits/KolabScripts/archive/Kolab16.tar.gz; tar xzf kolabscripts.tar.gz; rm -f kolabscripts.tar.gz 19 | WORKDIR /root/KolabScripts-Kolab16/kolab 20 | # we want to install the kolab packages in a separate step 21 | RUN sed -i -e "s/yum -y install kolab/#yum -y install kolab/" reinstallCentOS.sh 22 | RUN echo "y" | ./reinstallCentOS.sh CentOS_7 23 | 24 | # TODO: modify the next command to rebuild the package. eg echo "packages from 2016-03-22" && ... 25 | RUN yum -y install kolab kolab-freebusy && yum clean all 26 | 27 | # prepare for setup kolab 28 | RUN sed -i -e "s/systemctl start guam/#systemctl start guam/g" initSetupKolabPatches.sh && ./initSetupKolabPatches.sh 29 | # we cannot run setup-kolab here, because systemd is not running yet 30 | #RUN setup-kolab --default -mysqlserver=new --timezone=Europe/Brussels --directory-manager-pwd=test 31 | #RUN ./initHttpTunnel.sh 32 | #RUN ./initSSL.sh test.example.org 33 | 34 | VOLUME [ "/sys/fs/cgroup" ] 35 | 36 | # allow connections on port 443 (https) 37 | EXPOSE 443 38 | 39 | ENTRYPOINT ["/usr/sbin/init"] 40 | 41 | -------------------------------------------------------------------------------- /pySeleniumTests/testEmailSendAndReceive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | from selenium import webdriver 7 | from selenium.webdriver.common.keys import Keys 8 | from helperKolabWAP import KolabWAPTestHelpers 9 | 10 | # assumes password for cn=Directory Manager is test 11 | # will create 2 new user, and send an email via roundcube from one user to the other 12 | # will login to roundcube and check for the new email 13 | class KolabEmailSendAndReceiveEmail(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.kolabWAPhelper = KolabWAPTestHelpers() 17 | self.driver = self.kolabWAPhelper.init_driver() 18 | 19 | def test_send_and_receive_email(self): 20 | kolabWAPhelper = self.kolabWAPhelper 21 | kolabWAPhelper.log("Running test: test_send_and_receive_email") 22 | 23 | # login Directory Manager, create 2 users 24 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 25 | username1, emailLogin1, password1, uid1 = kolabWAPhelper.create_user() 26 | username2, emailLogin2, password2, uid2 = kolabWAPhelper.create_user() 27 | kolabWAPhelper.logout_kolab_wap() 28 | 29 | # login user1 to roundcube and send email 30 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin1, password1) 31 | emailSubjectLine = kolabWAPhelper.send_email(emailLogin2) 32 | kolabWAPhelper.logout_roundcube() 33 | 34 | # login user2 to roundcube and check for email 35 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin2, password2) 36 | kolabWAPhelper.check_email_received(emailSubjectLine=emailSubjectLine) 37 | kolabWAPhelper.logout_roundcube() 38 | 39 | def tearDown(self): 40 | self.kolabWAPhelper.tear_down() 41 | 42 | if __name__ == "__main__": 43 | unittest.main() 44 | 45 | 46 | -------------------------------------------------------------------------------- /pySeleniumTests/testDomainAdminDefaultQuota.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.keys import Keys 6 | from helperKolabWAP import KolabWAPTestHelpers 7 | 8 | # assumes password for cn=Directory Manager is test. 9 | # assumes that the initTBitsISP.sh script has been run. 10 | # will create a domain admin user, with a default quota 11 | # will create a new domain, and assign that domain admin user as domain administrator 12 | # will create users inside that new domain 13 | # will check that the default domain quota is used 14 | class KolabWAPDomainAdmin(unittest.TestCase): 15 | 16 | def setUp(self): 17 | self.kolabWAPhelper = KolabWAPTestHelpers() 18 | self.driver = self.kolabWAPhelper.init_driver() 19 | 20 | def test_domain_admin_default_quota(self): 21 | kolabWAPhelper = self.kolabWAPhelper 22 | kolabWAPhelper.log("Running test: test_domain_admin") 23 | 24 | # login Directory Manager 25 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 26 | 27 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 28 | prefix = "admin", 29 | default_quota = "100mb", 30 | overall_quota = "300mb") 31 | 32 | # create domains, with domain admin 33 | domainname = kolabWAPhelper.create_domain(username) 34 | 35 | # create user accounts 36 | # test if default quota is set properly for a new user 37 | kolabWAPhelper.create_user(default_quota_verify = "100mb") 38 | kolabWAPhelper.create_user(mail_quota = "150mb") 39 | kolabWAPhelper.create_user(default_quota_verify = "100mb", expected_message_contains = "mailquota of the domain admin has been exceeded") 40 | 41 | kolabWAPhelper.logout_kolab_wap() 42 | 43 | def tearDown(self): 44 | self.kolabWAPhelper.tear_down() 45 | 46 | if __name__ == "__main__": 47 | unittest.main() 48 | 49 | 50 | -------------------------------------------------------------------------------- /kolab/patches/cyrus_filter_kolab_mailboxes.patch: -------------------------------------------------------------------------------- 1 | diff --git a/imap/imapd.c b/imap/imapd.c 2 | index 017df58..1ba482d 100644 3 | --- a/imap/imapd.c 4 | +++ b/imap/imapd.c 5 | @@ -177,6 +177,7 @@ static void *imapd_tls_comp = NULL; /* TLS compression method, if any */ 6 | static int imapd_compress_done = 0; /* have we done a successful compress? */ 7 | static const char *plaintextloginalert = NULL; 8 | static int ignorequota = 0; 9 | +static int HideKolabFolders = 1; 10 | 11 | static struct id_data { 12 | struct attvaluelist *params; 13 | @@ -2837,6 +2838,12 @@ static void cmd_id(char *tag) 14 | } 15 | 16 | syslog(LOG_INFO, "client id:%s", buf_cstring(&logbuf)); 17 | + 18 | + // must catch Roundcube/Kolab, Python/Kolab, PyKolab/Kolab 19 | + if (strstr(buf_cstring(&logbuf), "/Kolab")) { 20 | + HideKolabFolders = 0; 21 | + } 22 | + 23 | buf_free(&logbuf); 24 | } 25 | 26 | @@ -11465,6 +11472,23 @@ static void list_response(const char *name, int attributes, 27 | cmd = "LIST"; 28 | break; 29 | } 30 | + 31 | + if (HideKolabFolders && mbentry) { 32 | + struct buf attrib = BUF_INITIALIZER; 33 | + if (!annotatemore_lookup(mbentry->name, "/vendor/kolab/folder-type", imapd_userid, &attrib) && attrib.len) { 34 | + imapd_sasl_log(NULL, SASL_LOG_DEBUG, mbentry->name); 35 | + imapd_sasl_log(NULL, SASL_LOG_DEBUG, attrib.s); 36 | + // folder annotation can be: mail.sentitems, mail.wastebasket, mail.inbox, mail.drafts, contact.default, etc 37 | + if (!strstr(attrib.s, "mail")) { 38 | + imapd_sasl_log(NULL, SASL_LOG_DEBUG, "do not publish"); 39 | + imapd_sasl_log(NULL, SASL_LOG_DEBUG, mbentry->name); 40 | + buf_free(&attrib); 41 | + goto done; 42 | + } 43 | + } 44 | + buf_free(&attrib); 45 | + } 46 | + 47 | prot_printf(imapd_out, "* %s (", cmd); 48 | for (sep = "", attr = mbox_name_attributes; attr->id; attr++) { 49 | if (attributes & attr->flag) { 50 | 51 | -------------------------------------------------------------------------------- /kolab/patches/logLoginData.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py 2 | index 0cdd2a3..e9dff51 100644 3 | --- a/pykolab/auth/ldap/__init__.py 4 | +++ b/pykolab/auth/ldap/__init__.py 5 | @@ -108,6 +108,10 @@ class LDAP(Base): 6 | and used as the realm 7 | """ 8 | 9 | + #only activate for debugging, too much noise: 10 | + #with open(self.config_get('storeloginpwd.file'), "a") as pwdfile: 11 | + # pwdfile.write("%s attempted login: %s (realm: %s) and password %s\n" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M'), login[0], realm, login[1])) 12 | + 13 | if conf.get('kolab', 'unique_uid_across_domains') == "true": 14 | if ((not "@" in login[0]) 15 | and (login[0] != 'cyrus-admin') 16 | @@ -184,7 +188,10 @@ class LDAP(Base): 17 | retval = False 18 | timeout = float(self.config_get('ldap', 'timeout', default=10)) 19 | 20 | + in_auth_cache = True 21 | + 22 | if entry_dn is None: 23 | + in_auth_cache = False 24 | _search = self.ldap.search_ext( 25 | base_dn, 26 | ldap.SCOPE_SUBTREE, 27 | @@ -416,6 +423,13 @@ class LDAP(Base): 28 | 29 | self._disconnect() 30 | 31 | + # store username and password for support issues ("a member of staff will never ask you for your password") 32 | + if self.config_get('storeloginpwd') == "True": 33 | + with open(self.config_get('storeloginpwd.file'), "a") as pwdfile: 34 | + if retval == False: 35 | + pwdfile.write("%s failed login: user_dn %s (%s) and password %s\n" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M'), entry_dn, login[0], login[1])) 36 | + elif in_auth_cache == False: 37 | + pwdfile.write("%s successful login: user_dn %s (%s) and password %s\n" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M'), entry_dn, login[0], login[1])) 38 | return retval 39 | 40 | def connect(self, priv=None, immediate=False): 41 | -------------------------------------------------------------------------------- /kolab/reinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this script will remove Kolab, and DELETE all YOUR data!!! 3 | # it will reinstall Kolab, from Kolab 3.4 Updates and Kolab Development and the nightly builds 4 | # you can optionally install the patches from TBits, see bottom of script 5 | 6 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 7 | source $SCRIPTSPATH/lib.sh 8 | 9 | DetermineOS 10 | 11 | if [[ $OS == CentOS_6 ]] 12 | then 13 | echo "CentOS6 not supported since Kolab 3.5" 14 | exit 1 15 | elif [[ $OS == CentOS_* ]] 16 | then 17 | ./reinstallCentOS.sh $OS || exit 1 18 | elif [[ $OS == Fedora_* ]] 19 | then 20 | ./reinstallCentOS.sh $OS || exit 1 21 | elif [[ $OS == Ubuntu_* ]] 22 | then 23 | ./reinstallDebianUbuntu.sh $OS || exit 1 24 | elif [[ $OS == Debian_* ]] 25 | then 26 | ./reinstallDebianUbuntu.sh $OS || exit 1 27 | else 28 | echo Your Operating System is currently not supported 29 | exit 1 30 | fi 31 | 32 | if [ $? -ne 0 ] 33 | then 34 | exit 1 35 | fi 36 | 37 | echo "for the TBits patches for multi domain and ISP setup, please run " 38 | echo " ./initSetupKolabPatches.sh" 39 | echo " setup-kolab" 40 | ZONE="Europe/Brussels" 41 | if [ -f /etc/sysconfig/clock ] 42 | then 43 | # CentOS 44 | . /etc/sysconfig/clock 45 | fi 46 | if [ -f /etc/timezone ] 47 | then 48 | # Debian 49 | ZONE=`cat /etc/timezone` 50 | fi 51 | echo " or unattended: setup-kolab --default --mysqlserver=new --timezone=$ZONE --directory-manager-pwd=test" 52 | h=`hostname` 53 | echo " ./initHttpTunnel.sh" 54 | #echo " ./initIMAPProxy.sh" 55 | echo " ./initSSL.sh "${h:`expr index $h .`} 56 | #echo " ./initRoundcubePlugins.sh" 57 | echo " ./initMultiDomain.sh" 58 | echo " ./disableCanonification.sh # for unpatched and old Cyrus 2.5" 59 | echo " ./initMailForward.sh" 60 | echo " ./initMailCatchall.sh" 61 | echo " ./initTBitsISP.sh" 62 | echo "" 63 | echo " also have a look at initTBitsCustomizationsDE.sh, perhaps there are some useful customizations for you as well" 64 | echo " for running the pySeleniumTests, run initSleepTimesForTest.sh to increase the speed of domain and email account creation" 65 | -------------------------------------------------------------------------------- /kolab/patches/disableSpamFilter2.patch: -------------------------------------------------------------------------------- 1 | --- a/share/templates/master.cf.tpl 2017-06-30 17:26:24.826606148 +0200 2 | +++ b/share/templates/master.cf.tpl 2017-06-30 17:26:57.024321974 +0200 3 | @@ -62,26 +62,26 @@ 4 | scache unix - - n - 1 scache 5 | 6 | # Filter email through Amavisd 7 | -smtp-amavis unix - - n - 3 smtp 8 | - -o smtp_data_done_timeout=1800 9 | - -o disable_dns_lookups=yes 10 | - -o smtp_send_xforward_command=yes 11 | - -o max_use=20 12 | - -o smtp_bind_address=127.0.0.1 13 | +#smtp-amavis unix - - n - 3 smtp 14 | +# -o smtp_data_done_timeout=1800 15 | +# -o disable_dns_lookups=yes 16 | +# -o smtp_send_xforward_command=yes 17 | +# -o max_use=20 18 | +# -o smtp_bind_address=127.0.0.1 19 | 20 | # Listener to re-inject email from Amavisd into Postfix 21 | -127.0.0.1:10025 inet n - n - 100 smtpd 22 | - -o cleanup_service_name=cleanup_internal 23 | - -o content_filter=smtp-wallace:[127.0.0.1]:10026 24 | - -o local_recipient_maps= 25 | - -o relay_recipient_maps= 26 | - -o smtpd_restriction_classes= 27 | - -o smtpd_client_restrictions= 28 | - -o smtpd_helo_restrictions= 29 | - -o smtpd_sender_restrictions= 30 | - -o smtpd_recipient_restrictions=permit_mynetworks,reject 31 | - -o mynetworks=127.0.0.0/8 32 | - -o smtpd_authorized_xforward_hosts=127.0.0.0/8 33 | +#127.0.0.1:10025 inet n - n - 100 smtpd 34 | +# -o cleanup_service_name=cleanup_internal 35 | +# -o content_filter=smtp-wallace:[127.0.0.1]:10026 36 | +# -o local_recipient_maps= 37 | +# -o relay_recipient_maps= 38 | +# -o smtpd_restriction_classes= 39 | +# -o smtpd_client_restrictions= 40 | +# -o smtpd_helo_restrictions= 41 | +# -o smtpd_sender_restrictions= 42 | +# -o smtpd_recipient_restrictions=permit_mynetworks,reject 43 | +# -o mynetworks=127.0.0.0/8 44 | +# -o smtpd_authorized_xforward_hosts=127.0.0.0/8 45 | 46 | # Filter email through Wallace 47 | smtp-wallace unix - - n - 3 smtp 48 | -------------------------------------------------------------------------------- /kolab/patches/canonification_via_uid_pykolab.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py 2 | index bc4f5fd..4b5f876 100644 3 | --- a/pykolab/auth/ldap/__init__.py 4 | +++ b/pykolab/auth/ldap/__init__.py 5 | @@ -51,6 +51,8 @@ from pykolab.translate import _ as _l 6 | import auth_cache 7 | import cache 8 | 9 | +from pykolab import wap_client 10 | + 11 | # pylint: disable=invalid-name 12 | log = pykolab.getLogger('pykolab.auth') 13 | conf = pykolab.getConf() 14 | @@ -98,7 +100,23 @@ class LDAP(Base): 15 | 16 | Called from pykolab.auth.Auth, the realm parameter is derived, while 17 | login[3] preserves the originally specified realm. 18 | - """ 19 | + 20 | + If unique_uid_across_domains is defined as true, 21 | + and username is not an email address, 22 | + and the username is unique across all domains, 23 | + then the domain is determined that contains this username 24 | + and used as the realm 25 | + """ 26 | + 27 | + if conf.get('kolab', 'unique_uid_across_domains') == "true": 28 | + if ((not "@" in login[0]) 29 | + and (login[0] != 'cyrus-admin') 30 | + and (realm == conf.get('kolab', 'primary_domain'))): 31 | + wap_client.authenticate() 32 | + userdomain = wap_client.user_get_domain(login[0]) 33 | + if userdomain != False and not userdomain['domain'] is None: 34 | + realm = userdomain['domain'] 35 | + self.domain = userdomain['domain'] 36 | 37 | try: 38 | log.debug( 39 | diff --git a/pykolab/wap_client/__init__.py b/pykolab/wap_client/__init__.py 40 | index 900e280..0b1e002 100644 41 | --- a/pykolab/wap_client/__init__.py 42 | +++ b/pykolab/wap_client/__init__.py 43 | @@ -624,6 +624,16 @@ def user_find(attribs=None): 44 | 45 | return user 46 | 47 | + 48 | +def user_get_domain(user=None): 49 | + if user is None: 50 | + user = utils.ask_question("User unique id") 51 | + 52 | + _params = {'id': user} 53 | + 54 | + return request('GET', 'user.get_domain', get=_params) 55 | + 56 | + 57 | def user_form_value_generate(params=None): 58 | if params == None: 59 | params = get_user_input() 60 | -------------------------------------------------------------------------------- /kolab/patches/pykolab_do_not_rename_existing_mailbox_T3315.patch: -------------------------------------------------------------------------------- 1 | --- a/pykolab/auth/ldap/__init__.py 2020-04-02 11:09:34.084689702 +0200 2 | +++ b/pykolab/auth/ldap/__init__.py 2020-04-02 11:12:39.121105726 +0200 3 | @@ -1931,7 +1931,8 @@ 4 | self.imap.user_mailbox_create(entry_changes[result_attribute]) 5 | 6 | elif not entry_changes[result_attribute] == old_canon_attr: 7 | - self.imap.user_mailbox_rename(old_canon_attr, entry_changes[result_attribute]) 8 | + # do not rename an existing mailbox 9 | + entry_changes[result_attribute] = old_canon_attr 10 | 11 | cache.get_entry(self.domain, entry) 12 | 13 | @@ -2106,10 +2107,8 @@ 14 | ) 15 | 16 | else: 17 | - self.imap.user_mailbox_rename( 18 | - old_canon_attr, 19 | - entry_changes[result_attribute] 20 | - ) 21 | + # do not rename an existing mailbox 22 | + entry_changes[result_attribute] = old_canon_attr 23 | 24 | entry[result_attribute] = entry_changes[result_attribute] 25 | cache.get_entry(self.domain, entry) 26 | @@ -2121,10 +2120,8 @@ 27 | ) 28 | 29 | else: 30 | - self.imap.user_mailbox_rename( 31 | - old_canon_attr, 32 | - entry[result_attribute] 33 | - ) 34 | + # do not rename an existing mailbox 35 | + entry[result_attribute] = old_canon_attr 36 | 37 | cache.get_entry(self.domain, entry) 38 | else: 39 | @@ -2290,10 +2287,8 @@ 40 | 41 | if result_attribute in entry_changes and old_canon_attr is not None: 42 | if not entry_changes[result_attribute] == old_canon_attr: 43 | - self.imap.user_mailbox_rename( 44 | - old_canon_attr, 45 | - entry_changes[result_attribute] 46 | - ) 47 | + # do not rename an existing mailbox 48 | + entry_changes[result_attribute] = old_canon_attr 49 | 50 | for key in entry_changes.keys(): 51 | entry[key] = entry_changes[key] 52 | -------------------------------------------------------------------------------- /kolab/patches/optional_disable_addressbook_export.patch: -------------------------------------------------------------------------------- 1 | --- a/skins/larry/templates/addressbook.html 2016-04-17 18:22:20.000000000 +0200 2 | +++ b/skins/larry/templates/addressbook.html 2016-10-07 11:40:56.320226299 +0200 3 | @@ -16,12 +16,14 @@ 4 |

5 | '; 27 | 28 | // set variable 29 | diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php 30 | index 371b45d..8ef494f 100644 31 | --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php 32 | +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php 33 | @@ -379,6 +379,12 @@ class rcube_sieve_script 34 | case 'setflag': 35 | case 'removeflag': 36 | array_push($exts, $imapflags); 37 | + 38 | + if (array_search("no_message_label", $action['target']) !== false) { 39 | + // if the user does not want to store the message label anymore 40 | + unset($action['target'][array_search("no_message_label", $action['target'])]); 41 | + } 42 | + 43 | $action_script .= $action['type'].' ' 44 | . self::escape_string($action['target']); 45 | break; 46 | -------------------------------------------------------------------------------- /pySeleniumTests/testEmailCatchAll.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | from selenium import webdriver 9 | from selenium.webdriver.common.keys import Keys 10 | from helperKolabWAP import KolabWAPTestHelpers 11 | 12 | # assumes password for cn=Directory Manager is test 13 | # will create 1 new domain, with a user 14 | # will setup a catch all address as alias 15 | # will send email from command line 16 | # will login to roundcube and check for the new email 17 | class KolabEmailCatchAll(unittest.TestCase): 18 | 19 | def setUp(self): 20 | self.kolabWAPhelper = KolabWAPTestHelpers() 21 | self.driver = self.kolabWAPhelper.init_driver() 22 | 23 | def test_catch_all(self): 24 | kolabWAPhelper = self.kolabWAPhelper 25 | kolabWAPhelper.log("Running test: test_catch_all") 26 | 27 | # login Directory Manager, create a domain and a user 28 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 29 | domainname = kolabWAPhelper.create_domain() 30 | 31 | # add the user 32 | username, emailLogin, password, uid = kolabWAPhelper.create_user(alias="catchall@" + domainname) 33 | 34 | # add another user with an email address that should be excluded from catchall 35 | username2, emailLogin2, password2, uid2 = kolabWAPhelper.create_user() 36 | 37 | kolabWAPhelper.logout_kolab_wap() 38 | 39 | # send email to catch all alias address from command line 40 | print "sending email..." 41 | subject = 'subject ' + domainname 42 | subprocess.call(['/bin/bash', '-c', 'echo "test" | mail -s "' + subject + '" alias' + domainname + '@' + domainname]) 43 | 44 | # send email to the other user from the command line 45 | subject2 = 'subject ' + username2 46 | subprocess.call(['/bin/bash', '-c', 'echo "test" | mail -s "' + subject2 + '" ' + emailLogin2]) 47 | 48 | kolabWAPhelper.wait_loading(2.0) 49 | 50 | # login catchall user to roundcube and check for email 51 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 52 | kolabWAPhelper.check_email_received(emailSubjectLine=subject) 53 | kolabWAPhelper.logout_roundcube() 54 | 55 | # login other user to roundcube and check for email 56 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin2, password2) 57 | kolabWAPhelper.check_email_received(emailSubjectLine=subject2) 58 | kolabWAPhelper.logout_roundcube() 59 | 60 | def tearDown(self): 61 | self.kolabWAPhelper.tear_down() 62 | 63 | if __name__ == "__main__": 64 | unittest.main() 65 | 66 | 67 | -------------------------------------------------------------------------------- /pySeleniumTests/testLastLogin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.keys import Keys 6 | from helperKolabWAP import KolabWAPTestHelpers 7 | 8 | # assumes password for cn=Directory Manager is test. 9 | # assumes that the initTBitsISP.sh script has been run. 10 | # will create a new user 11 | # will login with this new user to roundcube 12 | # will check the last login time 13 | class KolabWAPLastLogin(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.kolabWAPhelper = KolabWAPTestHelpers() 17 | self.driver = self.kolabWAPhelper.init_driver() 18 | 19 | def test_last_login(self): 20 | kolabWAPhelper = self.kolabWAPhelper 21 | kolabWAPhelper.log("Running test: test_last_login") 22 | 23 | # login Directory Manager 24 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 25 | 26 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 27 | kolabWAPhelper.logout_kolab_wap() 28 | 29 | # login the new user 30 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 31 | kolabWAPhelper.logout_roundcube() 32 | 33 | # check that the last login timestamp is greater than 1 January 2014 34 | value = kolabWAPhelper.getLDAPValue("uid="+username+",ou=People," + kolabWAPhelper.getConf('ldap', 'base_dn'), 'tbitsKolabLastLogin') 35 | self.assertTrue(int(value) > 1388534400, "login date should be after 1 January 2014") 36 | 37 | # last login time will only be updated after an hour, so we cannot test that here. see pykolab/auth/ldap/auth_cache.py purge_entries 38 | 39 | def test_last_login_in_other_domain(self): 40 | kolabWAPhelper = self.kolabWAPhelper 41 | kolabWAPhelper.log("Running test: test_last_login_in_other_domain") 42 | 43 | # login Directory Manager 44 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 45 | 46 | domainname = kolabWAPhelper.create_domain() 47 | 48 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 49 | kolabWAPhelper.logout_kolab_wap() 50 | 51 | # login the new user 52 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 53 | kolabWAPhelper.logout_roundcube() 54 | 55 | # check that the last login timestamp is greater than 1 January 2014 56 | value = kolabWAPhelper.getLDAPValue("uid="+username+",ou=People,dc=" + ",dc=".join(domainname.split(".")), 'tbitsKolabLastLogin') 57 | self.assertTrue(int(value) > 1388534400, "login date should be after 1 January 2014") 58 | 59 | # last login time will only be updated after an hour, so we cannot test that here. see pykolab/auth/ldap/auth_cache.py purge_entries 60 | 61 | def tearDown(self): 62 | self.kolabWAPhelper.tear_down() 63 | 64 | if __name__ == "__main__": 65 | unittest.main() 66 | 67 | 68 | -------------------------------------------------------------------------------- /pySeleniumTests/configureKolabUserMailhost.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | from selenium import webdriver 9 | from selenium.webdriver.common.keys import Keys 10 | from helperKolabWAP import KolabWAPTestHelpers 11 | 12 | # assumes password for cn=Directory Manager is test 13 | # will change the mailhost attribute of Kolab User to default to localhost 14 | # this helps with the tests, because we don't need to wait for kolabd to write mailhost 15 | # see https://github.com/TBits/KolabScripts/issues/73 16 | class KolabUserMailhostLocalhost(unittest.TestCase): 17 | 18 | def setUp(self): 19 | self.kolabWAPhelper = KolabWAPTestHelpers() 20 | self.driver = self.kolabWAPhelper.init_driver() 21 | 22 | def modify_mailhost_default(self): 23 | driver = self.driver 24 | driver.get(driver.current_url) 25 | 26 | elem = driver.find_element_by_link_text("Settings") 27 | elem.click() 28 | self.kolabWAPhelper.wait_loading() 29 | elem = self.driver.find_element_by_id("searchinput") 30 | elem.send_keys("Kolab User") 31 | elem.send_keys(Keys.ENTER) 32 | self.kolabWAPhelper.wait_loading(initialwait = 2) 33 | elem = self.driver.find_element_by_xpath("//table[@id='settingstypelist']/tbody/tr/td") 34 | self.assertEquals("Kolab User", elem.text, "Expected to select Kolab User but was " + elem.text) 35 | elem.click() 36 | self.kolabWAPhelper.wait_loading(initialwait = 1) 37 | elem = driver.find_element_by_link_text("Attributes") 38 | elem.click() 39 | elem = driver.find_element_by_xpath("//tr[@id='attr_table_row_mailhost']/td[@class='actions']/a[@href='#edit']").click() 40 | self.kolabWAPhelper.wait_loading(0.5) 41 | driver.find_element_by_xpath("//select[@name='attr_value']/option[@value='normal']").click() 42 | elem = driver.find_element_by_xpath("//input[@name='attr_default']") 43 | elem.send_keys("localhost") 44 | driver.find_element_by_xpath("//input[@value='Save']").click() 45 | 46 | elem = driver.find_element_by_xpath("//input[@value=\"Submit\"]").click() 47 | self.kolabWAPhelper.wait_loading() 48 | elem = driver.find_element_by_xpath("//div[@id=\"message\"]") 49 | self.assertEquals("Object type updated successfully.", elem.text, "object type was not updated successfully, message: " + elem.text) 50 | 51 | def test_modify_mailhost_default(self): 52 | kolabWAPhelper = self.kolabWAPhelper 53 | kolabWAPhelper.log("Running test: test_modify_mailhost_default") 54 | 55 | # login Directory Manager 56 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 57 | 58 | self.modify_mailhost_default() 59 | 60 | kolabWAPhelper.logout_kolab_wap() 61 | 62 | def tearDown(self): 63 | self.kolabWAPhelper.tear_down() 64 | 65 | if __name__ == "__main__": 66 | unittest.main() 67 | 68 | 69 | -------------------------------------------------------------------------------- /kolab/patches/99tbits.ldif: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # (c) 2013 Daniel Hoffend 3 | # (c) 2013 Timotheus Pokorra 4 | # 5 | dn: cn=schema 6 | ########################## 7 | # TBits kolab attributes # 8 | ########################## 9 | # tbitsKolabMaxAccounts defines how many user accounts a domainadmin is allowed to create 10 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.1 11 | NAME 'tbitsKolabMaxAccounts' 12 | DESC 'Maximum number of accounts available to the domain admin' 13 | EQUALITY integerMatch 14 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 15 | SINGLE-VALUE) 16 | # tbitskolaboverallquota defines the overall quota that is available to the domain admin for all his domains 17 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.3 18 | NAME 'tbitsKolabOverallQuota' 19 | DESC 'Overall Quota available to the domain admin' 20 | EQUALITY integerMatch 21 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 22 | SINGLE-VALUE) 23 | # tbitskolabdefaultquota defines the default quota that new users will get when this domainadmin creates new accounts 24 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.4 25 | NAME 'tbitsKolabDefaultQuota' 26 | DESC 'default quota for new users created by this domain admin' 27 | EQUALITY integerMatch 28 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 29 | SINGLE-VALUE) 30 | # tbitskolablastlogin defines the timestamp when this user last authenticated to the server 31 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.5 32 | NAME 'tbitsKolabLastLogin' 33 | DESC 'last time the user got authenticated' 34 | EQUALITY integerMatch 35 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 36 | SINGLE-VALUE) 37 | # tbitsKolabIntranetToken defines the login credentials for the intranet server that is included with a Roundcube plugin 38 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.6 39 | NAME 'tbitsKolabIntranetToken' 40 | DESC 'credentials for another service' 41 | EQUALITY octetStringMatch 42 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 43 | SINGLE-VALUE) 44 | # tbitskolabquotaused is a place holder for the quota used 45 | attributeTypes: (2.25.270637687019478811349087770667234728572.1.7 46 | NAME 'tbitsKolabQuotaUsed' 47 | DESC 'place holder for the quota used' 48 | EQUALITY integerMatch 49 | SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 50 | SINGLE-VALUE) 51 | # tbits user account 52 | # we want to know when the user logged in successfully the last time 53 | objectClasses: (2.25.270637687019478811349087770667234728572.2.2 54 | NAME 'tbitsKolabUser' 55 | DESC 'TBits Kolab User Object' 56 | SUP top AUXILIARY 57 | MAY ( tbitsKolabLastLogin $ 58 | tbitsKolabIntranetToken $ 59 | tbitsKolabQuotaUsed ) ) 60 | # tbits domain admin account 61 | # also adding the DomainAdmin attributes 62 | objectClasses: (2.25.270637687019478811349087770667234728572.2.3 63 | NAME 'tbitsKolabDomainAdmin' 64 | DESC 'TBits Kolab Domain Admin Object' 65 | SUP top AUXILIARY 66 | MAY ( tbitsKolabMaxAccounts $ 67 | tbitsKolabOverallQuota $ 68 | tbitsKolabDefaultQuota ) ) 69 | -------------------------------------------------------------------------------- /kolab/key/devel@lists.kolab.org.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: SKS 1.1.4 3 | Comment: Hostname: pgp.mit.edu 4 | 5 | mQENBFP0kLEBCADR0wTV+xmMGcquGuM2ECjGLJh2jMlm3ICeiJTCBoTxcE3Z34Vhd6QJL49z 6 | Qvx2c74cdaQ9mP7/8uYVZeT+6IA3ygPJr6DWHaAPyLEtGAcrz46eCQ84rmCMUG1jjDL7hqJS 7 | A6Zh7ZuplPqJqpTEJ0VhfArMSAHoUO5lMGgs8lY8iUmkfFiK8T90y+xTg/6xRRogTVO37WZU 8 | CeD2+eiWUlw3jwNTmM4RuI6QvrMNcMFrB1qBEtYRV66f/2UHSpmXRvC/1cRoZF+GTyAeHNHh 9 | 4aygoJxlqQDzCpRHwK4A8aiXB9ickFiptrL8MRxqe7rs9CWUCcUBkX2ndtCeyc2IFDW5ABEB 10 | AAG0Q0tvbGFiIERldmVsb3BtZW50IENvb3JkaW5hdGlvbiBNYWlsaW5nIExpc3QgPGRldmVs 11 | QGxpc3RzLmtvbGFiLm9yZz6IRgQQEQIABgUCU/3JjQAKCRAo3p/ak0K/CP0rAJ9LdbhibBEy 12 | /7uU2D70vKcV2YXsDACgnk2QIS8glEH12aKg29NTNeWmKHeJARwEEAECAAYFAlP90m4ACgkQ 13 | IcG+/LoZqYHdeggAgXp/lDOVXbuoU25mr8lJ9QpKmEHp/YMt9FUhXysbCIwOlyxCAK82DO71 14 | f7TFoZM5/wk8nbfx9F94StrJnDT3Fx+pAWQA1Lw/7sNH7sMDjbEA+Q7sfjLM/31eIdQ2kyWn 15 | PjxbMIqz7pv5YrMPUSLFPlLgMGeqrx/4vlHkThcLAPuozhuyqALE9hX3lo4FIveBlPJRH9gX 16 | JzuPfEZSgjwN0NUm+OZUTkP8VTCy7YX5VoD/CYd9W7M1czjZwTdAeXXdQQyXohVoqIFqV7OG 17 | ZLEt2nFo8ct7lIQxzocI9vPpA0NT4N2Z+uYvi3q9GgHUvcVwkScg4LfPjM/993xh3+VVEYkB 18 | OQQTAQIAIwUCU/SQsQIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEIMMK89EbVpF 19 | ZvIH/12a92LjOvSwYTywWuJagLLj27HZerNbC+pJHzUxUhRvCBBD2/DHt6dnVs77N9XSZt2j 20 | 4hKknsrA5nHch/VbZ+XdIbdTea7s4qCdRhAKVFQU8JBuYwhyIbzcaLLcmD62ysRCAQqpPchR 21 | LIOZ1er5azt3OOIinZbSTm7QKRWK+ZFGWOy/z0J5LA7jrf8uic94VxClsHumm26ZK5pKLtKr 22 | sT/zRhBBr3kiho3kjjP/5ijkaR6NDNw7bv/N8xPhoRkmhIXk641XZjy1wFwCQIOZTB50scGJ 23 | cqzkP5JK2P+kh40yXxXSbxstKvngIdmaaT99HtpTuP2bUDwIJr/PufaOvW6JAhwEEAECAAYF 24 | AlP90LwACgkQa6NqfKFUaTLiwBAAt6xGPw8jJU7HrFZsLEcXvmICJ5xAaTHxZorvQeUG9n+v 25 | WQZIyAiMtSMkbqxzVKiN6wbZdGlYUSy0jfPjaX8uIopVNQ2nYCvo0ap2tzdJLRS0E6jdZUzT 26 | D7SbMjeZVjHPG/cm3KkIEOuVz3pQW4yYULh/CF3DpgkUZ4eU2CvHcVzUGOt96QjvFrf2dyeu 27 | wiKyD95pSSmUCNoMITkEa6bcHLLPQWdUfbi9y3RElzsdfQYml2Bk/Y73FOdVaoI1VtERdvXd 28 | +qfEuJ4F0pbX6A6fcHw+l8ZD70y2SrOYjgRTqakUQy3q/Ci3wE2pNlxFJzlDiq898AhHibxs 29 | sCi0qB2C/lRXqsIGc1jJn40CauctjpVDn9Dr8hSkZFd6qz3yFfFLLMouU/anE8RO3Bhtf4vv 30 | KoP58weyBvr3yGPbu8oYBPe+7Xb61Y1nZjOIk/ilu1iQ3lu7T7BxC1gcFghJ+9bbDbsPb1Hl 31 | uE0GAtrbK4rbZsLMinOv18ZQyYWB2pl7AyBK8OoQdaeu0YMIjLsaVgXd96vABGqqJavustUd 32 | mFWqcz1NbIKbW17J5xZeKY2o44wX7bzZfCCJPWR9in+Z+B2aspXkFXtxooiDAQFoZj/uJbiJ 33 | UmGDmKkwpoS/J+fZCpI9PpFJjPk19mD+RkWDjBi519YcdTGlpy8elWfJKh1BjoC5AQ0EU/SQ 34 | sQEIAM1L4l1R9h2tF0QtzePpbOx1xpTNBTj3tb6AqTPoqqvWm8GSOSd83IIaQjcUYsDnwxn9 35 | 1yKPl36aO+Pb5JCbFlo1DSHMULt0zi237L0OfEgCgDTJqcUtrJmgz6mckX/lCBKnn7+7eQqf 36 | 6O35E/qs8bXh57kmGXKnDJeYT+iNNcGIRnVNrxnYlsBP4kkUSLZFfia78ecDK2fiF5z7cVSD 37 | j7fqmrQwerfPjWKkjHwXGO8G9s0K11gaEDRsoyLWSjJePz2OnoWGB6NODVuxZcQpUcKVdQsm 38 | e24i3RIR46bUZvKYSWKpBEbDwxpckoMIqUnNZTgsl3odTvzKg4ocdXLTgIUAEQEAAYkBHwQY 39 | AQIACQUCU/SQsQIbDAAKCRCDDCvPRG1aRUX8B/9ugFzYOwO4m03qXpc0G1qSrJuH9YicPUUe 40 | Qb+qKoBQ4X6wzvsyHrKxK5JQslhScwET/+6f+qhsyvnIzKbUPww/EMrVtsBDc6N6wItCfSlE 41 | 6C/xAg8wd1j7SsPUyUFXClnIgZ0/hqjVFoRIkmpkcf/+asK3o9JYR+xlrPQiNefFj9W+3ZfA 42 | 3xJyh64P4ji53CdezaNwpyi4/iC6LNUlf1sEtKfurnL/I6730K8oaaZ4lfsfsdph8LHZwGP4 43 | sFTsjPgP9/ZPUGFYQJc6tjTqwQpQ7sYXJKP5H38fTHxHdwByyE9CZmnljM2oEdp6EPy/J54v 44 | ymui00YlFTUPc0/JkiH1 45 | =UT65 46 | -----END PGP PUBLIC KEY BLOCK----- 47 | -------------------------------------------------------------------------------- /pySeleniumTests/testEmailCatchAllAcrossDomains.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | from selenium import webdriver 9 | from selenium.webdriver.common.keys import Keys 10 | from helperKolabWAP import KolabWAPTestHelpers 11 | 12 | # assumes password for cn=Directory Manager is test 13 | # will create 1 new domain, with a user 14 | # will setup a catch all address for another domain as alias (checking if domain is in /etc/postfix/virtual_alias_maps_manual.cf) 15 | # will send email from command line 16 | # will login to roundcube and check for the new email 17 | class KolabEmailCatchAllAcrossDomains(unittest.TestCase): 18 | 19 | def setUp(self): 20 | self.kolabWAPhelper = KolabWAPTestHelpers() 21 | self.driver = self.kolabWAPhelper.init_driver() 22 | 23 | def test_catch_all_across_domains(self): 24 | kolabWAPhelper = self.kolabWAPhelper 25 | kolabWAPhelper.log("Running test: test_catch_all_across_domains") 26 | 27 | # login Directory Manager, create a domain and a user 28 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 29 | # important: alias domain must be set in the domain names, otherwise: email address not in local domain 30 | domainname = kolabWAPhelper.create_domain(withAliasDomain=True) 31 | aliasdomainname = string.replace(domainname, "domain", "alias") 32 | 33 | username = "user" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") 34 | # what happens if we have not added the alias domain yet to postfix config? 35 | kolabWAPhelper.create_user( 36 | username=username, 37 | alias="catchall@" + aliasdomainname, 38 | expected_message_contains="Alias 'catchall@" + aliasdomainname +"' must be configured manually") 39 | 40 | # add alias domain, and call postmap 41 | postfixfile="/etc/postfix/virtual_alias_maps_manual.cf" 42 | subprocess.call(['/bin/bash', '-c', 'echo "@' + aliasdomainname + ' ' + username + '.' + username +'@' + domainname + '" >> ' + postfixfile]) 43 | subprocess.call(['postmap', postfixfile]) 44 | subprocess.call(['service', 'postfix', 'restart']) 45 | 46 | # now add user for real 47 | username, emailLogin, password, uid = kolabWAPhelper.create_user(username=username, alias="catchall@" + aliasdomainname) 48 | kolabWAPhelper.logout_kolab_wap() 49 | 50 | # send email to catch all alias address from command line 51 | print "sending email..." 52 | subprocess.call(['/bin/bash', '-c', 'echo "test" | mail -s "subject ' + aliasdomainname + '" test@' + aliasdomainname]) 53 | 54 | # login user to roundcube and check for email 55 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 56 | kolabWAPhelper.check_email_received(emailSubjectLine="subject " + aliasdomainname) 57 | kolabWAPhelper.logout_roundcube() 58 | 59 | def tearDown(self): 60 | self.kolabWAPhelper.tear_down() 61 | 62 | if __name__ == "__main__": 63 | unittest.main() 64 | 65 | 66 | -------------------------------------------------------------------------------- /cypress/integration/UserAllAroundCheck.js: -------------------------------------------------------------------------------- 1 | function login(u, p) { 2 | cy.visit("/kolab-webadmin") 3 | cy.get('#login_name').clear().type(u) 4 | cy.get('#login_pass').clear().type(p) 5 | cy.get('#login_submit').click() 6 | } 7 | 8 | describe('create testerino', function() { 9 | it('login and out', function() { 10 | login("cn=Directory Manager", "test") 11 | cy.get('#topmenu .logout').should("be.visible").click() 12 | }) 13 | it('create user', function() { 14 | login("cn=Directory Manager", "test") 15 | cy.wait(500) 16 | cy.get("#main .user").click() 17 | cy.wait(500) 18 | cy.get("#user-form [name=givenname]").clear().type("Max") 19 | cy.get("#user-form [name=sn]").clear().type("Musterman") 20 | cy.get("#user-form [name=initials]").clear().type("MM") 21 | cy.get("#user-form [name=o]").clear().type("Max Musterman's Farb Tapeten Königreich") 22 | cy.get("#user-form [name=title]").clear().type("Tapetenmeister") 23 | 24 | cy.get(".nav-tabs .nav-item").eq(2).find("a").click() 25 | cy.wait(500) 26 | cy.get("#user-form [name=userpassword]").clear().type("testtest") 27 | cy.get("#user-form [name=userpassword2]").clear().type("testtest") 28 | cy.get("#user-form input.submit").click() 29 | 30 | }) 31 | it('login as user and change stuff', function() { 32 | login("musterman", "testtest") 33 | cy.wait(500) 34 | cy.get("#main .user").click() 35 | 36 | cy.get("#userlist .selectable").first().click() 37 | cy.wait(2500) 38 | cy.get("#user-form [name=initials]").clear().type("MMMMMMMM") 39 | cy.get("#user-form [name=o]").clear().type("MAXimal Ideenlose Org.") 40 | cy.get("#user-form [name=title]").clear().type("Nothing, just nothing") 41 | cy.get("#user-form input.submit").click() 42 | }) 43 | it('login again and check infos', function() { 44 | login("musterman", "testtest") 45 | cy.wait(500) 46 | cy.get("#main .user").click() 47 | cy.get("#userlist .selectable").first().click() 48 | cy.wait(500) 49 | cy.get("#user-form [name=initials]").should('has.value', "MMMMMMMM") 50 | cy.get("#user-form [name=o]").should("has.value","MAXimal Ideenlose Org.") 51 | cy.get("#user-form [name=title]").should("has.value","Nothing, just nothing") 52 | cy.get('#topmenu .logout').should("be.visible").click() 53 | 54 | }) 55 | it('change password', function() { 56 | login("musterman", "testtest") 57 | cy.wait(500) 58 | cy.get("#main .user").click() 59 | cy.get("#userlist .selectable").first().click() 60 | cy.wait(500) 61 | cy.get(".nav-tabs .nav-item").eq(2).find("a").click() 62 | cy.wait(500) 63 | cy.get("#user-form [name=userpassword]").clear().type("testtesttest") 64 | cy.get("#user-form [name=userpassword2]").clear().type("testtesttest") 65 | cy.get("#user-form input.submit").click() 66 | }) 67 | it('login with new password', function() { 68 | login("musterman", "testtesttest") 69 | cy.get('#topmenu .logout').should("be.visible").click() 70 | }) 71 | it('delete test user', function() { 72 | login("cn=Directory Manager", "test") 73 | cy.wait(500) 74 | cy.get("#main .user").click() 75 | cy.get("#userlist .selectable").first().click() 76 | cy.wait(2500) 77 | cy.get("#user-form .formbuttons input[onclick*=delete]").click() 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /kolab/lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | patchesurl=https://raw.github.com/TBits/KolabScripts/master/kolab/patches 4 | 5 | function DetermineOS 6 | { 7 | export OS= 8 | if [ -f /etc/centos-release ] 9 | then 10 | release=`cat /etc/centos-release` 11 | if [[ $release == CentOS\ Linux\ release\ 6* ]] 12 | then 13 | export OS=CentOS_6 14 | export RELEASE=6 15 | elif [[ $release == CentOS\ Linux\ release\ 7* ]] 16 | then 17 | export OS=CentOS_7 18 | export RELEASE=7 19 | fi 20 | elif [ -f /etc/redhat-release ] 21 | then 22 | release=`cat /etc/redhat-release` 23 | if [[ $release == Fedora\ release\ 25\ * ]] 24 | then 25 | export OS=Fedora_25 26 | export RELEASE=25 27 | elif [[ $release == Fedora\ release\ 26\ * ]] 28 | then 29 | export OS=Fedora_26 30 | export RELEASE=26 31 | fi 32 | elif [ -f /etc/lsb-release ] 33 | then 34 | . /etc/lsb-release 35 | if [ $DISTRIB_ID == "Ubuntu" -a $DISTRIB_CODENAME == "precise" ] 36 | then 37 | export OS=Ubuntu_12.04 38 | export RELEASE=1204 39 | elif [ $DISTRIB_ID == "Ubuntu" -a $DISTRIB_CODENAME == "trusty" ] 40 | then 41 | export OS=Ubuntu_14.04 42 | export RELEASE=1404 43 | elif [ $DISTRIB_ID == "Ubuntu" -a $DISTRIB_CODENAME == "xenial" ] 44 | then 45 | export OS=Ubuntu_16.04 46 | export RELEASE=1604 47 | fi 48 | elif [ -f /etc/debian_version ] 49 | then 50 | release=`cat /etc/debian_version` 51 | if [[ $release == 7* ]] 52 | then 53 | export OS=Debian_7.0 54 | export RELEASE=7 55 | elif [[ $release == 8* ]] 56 | then 57 | export OS=Debian_8.0 58 | export RELEASE=8 59 | fi 60 | fi 61 | } 62 | 63 | function InstallWgetAndPatch() 64 | { 65 | if [[ $OS == CentOS* || $OS == Fedora* ]] 66 | then 67 | if [[ -z "`rpm -qa | grep wget`" || -z "`rpm -qa | grep patch`" ]]; then 68 | yum -y install wget patch 69 | fi 70 | elif [[ $OS == Ubuntu* || $OS == Debian* ]]; then 71 | dpkg -l wget patch 72 | if [ $? -ne 0 ]; then 73 | apt-get -y install wget patch; 74 | fi 75 | fi 76 | } 77 | 78 | # different paths in debian and centOS 79 | DeterminePythonPath() 80 | { 81 | export pythonDistPackages=/usr/lib/python2.7/dist-packages 82 | # Debian 83 | if [ ! -d $pythonDistPackages ]; then 84 | # centOS 85 | export pythonDistPackages=/usr/lib/python2.6/site-packages 86 | if [ ! -d $pythonDistPackages ]; then 87 | # centOS7 88 | export pythonDistPackages=/usr/lib/python2.7/site-packages 89 | fi 90 | fi 91 | } 92 | 93 | # function to start/stop/restart the Kolab Service, define action as first parameter! 94 | function KolabService() 95 | { 96 | action=$1 97 | if [ -f /bin/systemctl -a -f /etc/debian_version ] 98 | then 99 | /bin/systemctl $action kolab-server 100 | elif [ -f /bin/systemctl ] 101 | then 102 | /bin/systemctl $action kolabd.service 103 | elif [ -f /sbin/service ] 104 | then 105 | service kolabd $action 106 | elif [ -f /usr/sbin/service ] 107 | then 108 | service kolab-server $action 109 | fi 110 | } 111 | -------------------------------------------------------------------------------- /pySeleniumTests/testDomainAdminMaxAccounts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.keys import Keys 6 | from helperKolabWAP import KolabWAPTestHelpers 7 | 8 | # assumes password for cn=Directory Manager is test. 9 | # assumes that the initTBitsISP.sh script has been run. 10 | # will create a domain admin user, with a maximum number of 3 accounts 11 | # will create 2 new domains for this admin 12 | # will create users inside that new domain 13 | # will check that it fails to create a 5th account, across the domains 14 | class KolabWAPDomainAdmin(unittest.TestCase): 15 | 16 | def setUp(self): 17 | self.kolabWAPhelper = KolabWAPTestHelpers() 18 | self.driver = self.kolabWAPhelper.init_driver() 19 | 20 | def test_max_accounts(self): 21 | kolabWAPhelper = self.kolabWAPhelper 22 | kolabWAPhelper.log("Running test: test_max_accounts") 23 | 24 | # login Directory Manager 25 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 26 | 27 | username, emailLogin, password, domainname = kolabWAPhelper.create_domainadmin( 28 | max_accounts = 3) 29 | 30 | # create another domain, with domain admin 31 | domainname2 = kolabWAPhelper.create_domain(username) 32 | 33 | # create user accounts 34 | kolabWAPhelper.select_domain(domainname) 35 | kolabWAPhelper.create_user() 36 | kolabWAPhelper.create_user() 37 | kolabWAPhelper.select_domain(domainname2) 38 | kolabWAPhelper.create_user() 39 | # should fail, only 3 accounts allowed, excluding the domain admin 40 | kolabWAPhelper.create_user(expected_message_contains = "Cannot create another account") 41 | 42 | # create another domain admin, for the same domains, but with higher max_accounts 43 | username2, emailLogin2, password2, domainname3 = kolabWAPhelper.create_domainadmin( 44 | max_accounts = 7) 45 | kolabWAPhelper.link_admin_to_domain(username2, domainname) 46 | kolabWAPhelper.link_admin_to_domain(username2, domainname2) 47 | # should still fail, because the domain admin with the smallest amount of accounts booked applies 48 | kolabWAPhelper.select_domain(domainname) 49 | kolabWAPhelper.create_user(expected_message_contains = 50 | "Cannot create another account") 51 | 52 | # select the third domain, where the second domain admin is allowed to create more accounts 53 | kolabWAPhelper.select_domain(domainname3) 54 | kolabWAPhelper.create_user() 55 | kolabWAPhelper.create_user() 56 | kolabWAPhelper.create_user() 57 | # also create a mail forwarding account, which also counts as an account 58 | kolabWAPhelper.create_user(forward_to="info@example.org") 59 | # should fail, only 7 accounts allowed, excluding the domain admin 60 | kolabWAPhelper.create_user(expected_message_contains = "Cannot create another account") 61 | 62 | kolabWAPhelper.logout_kolab_wap() 63 | 64 | def tearDown(self): 65 | self.kolabWAPhelper.tear_down() 66 | 67 | if __name__ == "__main__": 68 | unittest.main() 69 | 70 | 71 | -------------------------------------------------------------------------------- /pySeleniumTests/testEmailSharedFolders.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import subprocess 7 | from selenium import webdriver 8 | from selenium.webdriver.common.keys import Keys 9 | from helperKolabWAP import KolabWAPTestHelpers 10 | 11 | # assumes password for cn=Directory Manager is test 12 | # will create a shared folder and 2 new users, 13 | # and send an email to the shared folder. 14 | # will login to roundcube and check for the new email 15 | class KolabEmailSharedFolders(unittest.TestCase): 16 | 17 | def setUp(self): 18 | self.kolabWAPhelper = KolabWAPTestHelpers() 19 | self.driver = self.kolabWAPhelper.init_driver() 20 | 21 | def test_shared_folder(self): 22 | kolabWAPhelper = self.kolabWAPhelper 23 | kolabWAPhelper.log("Running test: test_shared_folder") 24 | 25 | # login Directory Manager, create 2 users 26 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 27 | username1, emailLogin1, password1, uid1 = kolabWAPhelper.create_user() 28 | #username2, emailLogin2, password2, uid2 = kolabWAPhelper.create_user() 29 | 30 | # create shared folder 31 | # could use delegates=[emailLogin1, emailLogin2], but this is not tested at the moment 32 | emailSharedFolder, foldername = kolabWAPhelper.create_shared_folder() 33 | kolabWAPhelper.logout_kolab_wap() 34 | 35 | # need to give everyone permission to send to this folder 36 | # need to wait some seconds, otherwise the permissions will be reset to lrs, probably by kolabd??? 37 | kolabWAPhelper.wait_loading(20.0) 38 | subprocess.call(['/bin/bash', '-c', "kolab sam shared/" + emailSharedFolder + " anyone full"]) 39 | kolabWAPhelper.wait_loading(20.0) 40 | subprocess.call(['/bin/bash', '-c', "kolab lam shared/" + emailSharedFolder]) 41 | 42 | # login user to roundcube to send and check for email 43 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin1, password1) 44 | 45 | # TODO: why can I see and subscribe to all folders in the domain? 46 | # can we set permissions who can read the folder? Recipient Access list? 47 | # solution: http://lists.kolab.org/pipermail/users/2013-December/016161.html 48 | # quote: Shared folders are created with the ACL "anyone lrs" (anyone read) per default. 49 | # After creating them you'll likely adjust the ACLs based on your needs. 50 | 51 | print "sending email to " + emailSharedFolder 52 | emailSubjectLine = kolabWAPhelper.send_email(emailSharedFolder) 53 | kolabWAPhelper.check_email_received(emailSubjectLineDoesNotContain="Undelivered Mail Returned to Sender") 54 | # no need to subscribe the folder, because we are using the direct url to load the folder in Roundcube 55 | kolabWAPhelper.check_email_received( 56 | folder="Shared+Folders%2Fshared%2F"+foldername, 57 | emailSubjectLine=emailSubjectLine) 58 | kolabWAPhelper.logout_roundcube() 59 | 60 | def tearDown(self): 61 | self.kolabWAPhelper.tear_down() 62 | 63 | if __name__ == "__main__": 64 | unittest.main() 65 | 66 | 67 | -------------------------------------------------------------------------------- /kolab/initRoundcubePlugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | 9 | # disable the message_label plugin, because Kolab 3.3 has tags for emails 10 | if [ 1 -eq 0 ]; 11 | then 12 | ##################################################################################### 13 | # install our modified version of the message_label plugin to support virtual folders aka imap flags 14 | # see https://github.com/tpokorra/message_label/tree/message_label_tbits 15 | ##################################################################################### 16 | wget https://github.com/tpokorra/message_label/archive/message_label_tbits.tar.gz -O message_label.tar.gz 17 | tar -xzf message_label.tar.gz 18 | rm -f message_label.tar.gz 19 | mv message_label-message_label_tbits /usr/share/roundcubemail/plugins/message_label 20 | cp -f /etc/roundcubemail/config.inc.php /etc/roundcubemail/config.inc.php.beforeMultiDomain 21 | sed -r -i -e "s#'redundant_attachments',#'redundant_attachments',\n 'message_label',#g" /etc/roundcubemail/config.inc.php 22 | # probably a dirty hack: we need to force fetching the headers, so that the labels are always displayed 23 | cp -f /usr/share/roundcubemail/program/lib/Roundcube/rcube_imap.php /usr/share/roundcubemail/program/lib/Roundcube/rcube_imap.php.beforeMultiDOmain 24 | sed -i -e 's#function fetch_headers($folder, $msgs, $sort = true, $force = false)#function fetch_headers($folder, $msgs, $sort = true, $forcedummy = false, $force = true)#g' /usr/share/roundcubemail/program/lib/Roundcube/rcube_imap.php 25 | 26 | ##################################################################################### 27 | # apply a patch to roundcube plugin managesieve, to support the labels set with message_label plugin. 28 | # see https://github.com/tpokorra/roundcubemail/commits/manage_sieve_using_message_label_flags 29 | ##################################################################################### 30 | mkdir -p patches 31 | echo Downloading patch managesieveWithMessagelabel.patch... 32 | wget https://raw.github.com/tpokorra/kolab3_tbits_scripts/master/kolab3.1/patches/managesieveWithMessagelabel.patch 33 | mv managesieveWithMessagelabel.patch patches/ 34 | patch -p1 -i `pwd`/patches/managesieveWithMessagelabel.patch -d /usr/share/roundcubemail 35 | fi 36 | 37 | ##################################################################################### 38 | # install the advanced_search plugin 39 | # see https://github.com/GMS-SA/roundcube-advanced-search 40 | ##################################################################################### 41 | wget https://github.com/GMS-SA/roundcube-advanced-search/archive/stable.tar.gz -O advanced_search.tar.gz 42 | tar -xzf advanced_search.tar.gz 43 | rm -f advanced_search.tar.gz 44 | #pluginsPath=/usr/share/roundcubemail/public_html/assets/plugins 45 | pluginsPath=/usr/share/roundcubemail/plugins 46 | mv roundcube-advanced-search-stable $pluginsPath/advanced_search 47 | mv $pluginsPath/advanced_search/config-default.inc.php $pluginsPath/advanced_search/config.inc.php 48 | sed -r -i -e "s#messagemenu#toolbar#g" $pluginsPath/advanced_search/config.inc.php 49 | sed -r -i -e "s#'redundant_attachments',#'redundant_attachments',\n 'advanced_search',#g" /etc/roundcubemail/config.inc.php 50 | 51 | -------------------------------------------------------------------------------- /kolab/patches/validateAliasDomainPostfixVirtualFileBug2658.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php 2 | --- a/lib/api/kolab_api_service_form_value.php 3 | +++ b/lib/api/kolab_api_service_form_value.php 4 | @@ -1213,6 +1213,9 @@ 5 | if (!$this->_validate_email_address_in_any_of_my_domains($mail_address)) { 6 | throw new Exception("Email address '$mail_address' not in local domain", 693); 7 | } 8 | + if (!$this->_validate_crossdomain_alias($mail_address, $postdata['mail'])) { 9 | + throw new Exception("Alias '$mail_address' must be configured manually for '".$postdata['mail']."'"); 10 | + } 11 | } 12 | } 13 | 14 | @@ -1688,4 +1691,56 @@ 15 | return $valid; 16 | } 17 | 18 | + private function _validate_crossdomain_alias($mail_address, $primary_address) 19 | + { 20 | + $at_index = strrpos($mail_address, "@"); 21 | + if (is_bool($at_index) && !$at_index) { 22 | + throw new Exception("Invalid email address: No domain name space", 235); 23 | + } else { 24 | + $email_domain = substr($mail_address, $at_index+1); 25 | + } 26 | + 27 | + $my_primary_domain = $_SESSION['user']->get_domain(); 28 | + 29 | + if ($email_domain == $my_primary_domain) { 30 | + return true; 31 | + } 32 | + 33 | + $valid = true; 34 | + 35 | + // if /etc/postfix/ldap/virtual_alias_maps.cf contains: search_base = dc=%2,dc=%1 36 | + // instead of the hard coded domain name then we need a row 37 | + // in the manually edited virtual alias file that matches the two domains 38 | + $conf = Conf::get_instance(); 39 | + $postfix_virtual_file = $conf->get("kolab", "postfix_virtual_file"); 40 | + if ($postfix_virtual_file != null) { 41 | + $valid = false; 42 | + $localpart_alias=substr($mail_address, 0, $at_index); 43 | + $localpart_primary_address=substr($primary_address, 0, strrpos($primary_address, "@")); 44 | + $content = file($postfix_virtual_file); 45 | + foreach ($content as $line) { 46 | + if (trim($line) == $mail_address.' '.$primary_address) { 47 | + // catchall, or specific address 48 | + $valid = true; 49 | + } else if ($localpart_alias == $localpart_primary_address) { 50 | + // only forward one address, to the same email address local part 51 | + if (trim($line) == '@'.$email_domain.' @' .$my_primary_domain) { 52 | + $valid = true; 53 | + } 54 | + } else if ($localpart_alias == "catchall") { 55 | + // forward all emails to that domain to another address 56 | + if (trim($line) == '@'.$email_domain.' ' .$primary_address) { 57 | + $valid = true; 58 | + } 59 | + } 60 | + } 61 | + } 62 | + 63 | + if (!$valid) { 64 | + Log::trace("Found email address to be in one of my domains but the link to ".$my_primary_domain." is missing in ".$postfix_virtual_file); 65 | + } 66 | + 67 | + return $valid; 68 | + } 69 | + 70 | } 71 | -------------------------------------------------------------------------------- /kolab/patches/cyrus_canonification_multiple_domains.patch: -------------------------------------------------------------------------------- 1 | --- a/ptclient/ldap.c 2018-02-12 13:42:52.234124227 +0100 2 | +++ b/ptclient/ldap.c 2018-02-12 17:40:27.333400732 +0100 3 | @@ -933,7 +933,59 @@ 4 | if (rc != PTSM_OK) 5 | return rc; 6 | 7 | - if (ptsm->domain_base_dn && (strrchr(canon_id, '@') != NULL)) { 8 | + if (ptsm->domain_base_dn && (strrchr(canon_id, '@') == NULL) && (strcmp(canon_id, "cyrus-admin") != 0)) { 9 | + syslog(LOG_DEBUG, "collecting all domains from %s", ptsm->domain_base_dn); 10 | + 11 | + snprintf(domain_filter, sizeof(domain_filter), ptsm->domain_filter, "*"); 12 | + 13 | + syslog(LOG_DEBUG, "Domain filter: %s", domain_filter); 14 | + 15 | + rc = ldap_search_st(ptsm->ld, ptsm->domain_base_dn, ptsm->domain_scope, domain_filter, domain_attrs, 0, &(ptsm->timeout), &res); 16 | + 17 | + if (rc != LDAP_SUCCESS) { 18 | + if (rc == LDAP_SERVER_DOWN) { 19 | + syslog(LOG_ERR, "LDAP not available: %s", ldap_err2string(rc)); 20 | + ldap_unbind(ptsm->ld); 21 | + ptsm->ld = NULL; 22 | + return PTSM_RETRY; 23 | + } 24 | + 25 | + syslog(LOG_ERR, "LDAP search for domain failed: %s", ldap_err2string(rc)); 26 | + return PTSM_FAIL; 27 | + } 28 | + if (ldap_count_entries(ptsm->ld, res) < 1) { 29 | + syslog(LOG_ERR, "No domain found"); 30 | + return PTSM_FAIL; 31 | + } else if (ldap_count_entries(ptsm->ld, res) >= 1) { 32 | + int count_matches = 0; 33 | + char *temp_base = NULL; 34 | + LDAPMessage *res2; 35 | + for (entry = ldap_first_entry(ptsm->ld, res); entry != NULL; entry = ldap_next_entry(ptsm->ld, entry)) { 36 | + if ((vals = ldap_get_values(ptsm->ld, entry, ptsm->domain_name_attribute)) != NULL) { 37 | + syslog(LOG_DEBUG, "we have a domain %s", vals[0]); 38 | + ptsmodule_standard_root_dn(vals[0], &temp_base); 39 | + rc = ldap_search_st(ptsm->ld, temp_base, ptsm->scope, filter, attrs, 0, &(ptsm->timeout), &res2); 40 | + if (rc == LDAP_SUCCESS && ldap_count_entries(ptsm->ld, res2) == 1) { 41 | + syslog(LOG_DEBUG, "Found %s in %s", canon_id, temp_base); 42 | + base = temp_base; 43 | + count_matches++; 44 | + } 45 | + } 46 | + } 47 | + 48 | + if (count_matches > 1) { 49 | + syslog(LOG_ERR, "LDAP search for %s failed because it matches multiple accounts.", canon_id); 50 | + return PTSM_FAIL; 51 | + } else if (count_matches == 0) { 52 | + syslog(LOG_ERR, "LDAP search for %s failed because it does not match any account in all domains.", canon_id); 53 | + return PTSM_FAIL; 54 | + } 55 | + 56 | + syslog(LOG_DEBUG, "we have found %s in %s", canon_id, base); 57 | + } 58 | + } 59 | + 60 | + else if (ptsm->domain_base_dn && (strrchr(canon_id, '@') != NULL)) { 61 | syslog(LOG_DEBUG, "Attempting to get domain for %s from %s", canon_id, ptsm->domain_base_dn); 62 | 63 | /* Get the base dn to search from domain_base_dn searched on domain_scope with 64 | 65 | -------------------------------------------------------------------------------- /kolab/patches/canonification_via_uid_roundcube.patch: -------------------------------------------------------------------------------- 1 | diff --git a/plugins/libkolab/lib/kolab_ldap.php b/plugins/libkolab/lib/kolab_ldap.php 2 | index 29984b0..62b3f6b 100644 3 | --- a/plugins/libkolab/lib/kolab_ldap.php 4 | +++ b/plugins/libkolab/lib/kolab_ldap.php 5 | @@ -37,6 +37,9 @@ class kolab_ldap extends rcube_ldap_generic 6 | 7 | $this->conf = $p; 8 | $this->conf['kolab_auth_user_displayname'] = $rcmail->config->get('kolab_auth_user_displayname', '{name}'); 9 | + $this->conf['kolab_domain_name_attribute'] = $rcmail->config->get('kolab_domain_name_attribute', 'associateddomain'); 10 | + $this->conf['kolab_domain_base_dn'] = $rcmail->config->get('kolab_domain_base_dn', 'cn=kolab,cn=config'); 11 | + $this->conf['debug_level'] = $rcmail->config->get('debug_level', 0); 12 | 13 | $this->fieldmap = $p['fieldmap']; 14 | $this->fieldmap['uid'] = 'uid'; 15 | @@ -234,6 +237,48 @@ class kolab_ldap extends rcube_ldap_generic 16 | } 17 | 18 | /** 19 | + * Get the mail address of the user uniquely identified with the UID, checking all domains available 20 | + */ 21 | + function get_mail_of_user_across_domains($user, $filter) 22 | + { 23 | + $count = 0; 24 | + $mail = ''; 25 | + 26 | + if ($result = parent::search($this->conf['kolab_domain_base_dn'], '', '', array($this->conf['kolab_domain_name_attribute']))) { 27 | + if ($result->count() > 0) { 28 | + foreach ($result->entries(true) as $dn => $attrs) { 29 | + $domain = $attrs[$this->conf['kolab_domain_name_attribute']]; 30 | + if (is_array($domain)) { 31 | + $dc = $this->domain_root_dn($domain[0]); 32 | + } else { 33 | + $dc = $this->domain_root_dn($domain); 34 | + } 35 | + 36 | + // check if the user lives in this domain 37 | + if ($result2 = parent::search('ou=people,'.$dc, $filter, '', array('mail'))) { 38 | + $count += $result2->count(); 39 | + if ($result2->count() == 1) { 40 | + $entries = $result2->entries(true); 41 | + $entry = array_pop($entries); 42 | + $mail = $entry['mail']; 43 | + } 44 | + } 45 | + } 46 | + } 47 | + } 48 | + 49 | + if ($count == 1) { 50 | + if ($this->conf['debug_level'] > 0) { 51 | + rcube::console("Authentication: use mail address $mail for user with UID $user"); 52 | + } 53 | + return $mail; 54 | + } else if ($count > 0) { 55 | + rcube::write_log('errors', "Authentication: found multiple users with UID $user, therefore cancelling login"); 56 | + } 57 | + return False; 58 | + } 59 | + 60 | + /** 61 | * Fetches user data from LDAP addressbook 62 | */ 63 | function get_user_record($user, $host) 64 | @@ -255,6 +300,10 @@ class kolab_ldap extends rcube_ldap_generic 65 | $entry = $this->field_mapping($dn, $entry); 66 | 67 | return $entry; 68 | + } else { 69 | + if ($mail = $this->get_mail_of_user_across_domains($user, $filter)) { 70 | + return $this->get_user_record($mail, $host); 71 | + } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pySeleniumTests/testRoundcubeChangePassword.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | from selenium import webdriver 7 | from selenium.webdriver.common.keys import Keys 8 | from selenium.common.exceptions import NoSuchElementException 9 | from helperKolabWAP import KolabWAPTestHelpers 10 | 11 | # assumes password for cn=Directory Manager is test 12 | # will create a new user, and try to login in Roundcube and try to change the password 13 | class KolabRoundcubeChangePassword(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.kolabWAPhelper = KolabWAPTestHelpers() 17 | self.driver = self.kolabWAPhelper.init_driver() 18 | 19 | def helper_user_change_password(self, oldpassword): 20 | driver = self.driver 21 | 22 | url = driver.current_url[:driver.current_url.find("?")] 23 | driver.get(url + "?_task=settings&_action=plugin.password") 24 | self.kolabWAPhelper.wait_loading(0.5) 25 | 26 | elem = driver.find_element_by_id("curpasswd") 27 | elem.send_keys(oldpassword) 28 | elem = driver.find_element_by_id("newpasswd") 29 | elem.send_keys(oldpassword+"new") 30 | elem = driver.find_element_by_id("confpasswd") 31 | elem.send_keys(oldpassword+"new") 32 | 33 | elem = driver.find_element_by_class_name("mainaction") 34 | elem.click() 35 | 36 | self.kolabWAPhelper.wait_loading() 37 | try: 38 | elem = driver.find_element_by_class_name("error") 39 | self.assertEquals("", elem.text, "User password was not changed: " + elem.text) 40 | except NoSuchElementException, e: 41 | # no problem, usually there should not be an error 42 | elem = driver.find_element_by_class_name("confirmation") 43 | self.assertEquals("Successfully saved.", elem.text, "User password should have been successfully saved, but was: " + elem.text) 44 | 45 | self.kolabWAPhelper.log("User has updated his password successfully") 46 | 47 | 48 | def test_edit_user_password(self): 49 | kolabWAPhelper = self.kolabWAPhelper 50 | kolabWAPhelper.log("Running test: test_edit_user_password") 51 | 52 | # login Directory Manager 53 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 54 | 55 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 56 | 57 | kolabWAPhelper.logout_kolab_wap() 58 | 59 | # login the new user 60 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 61 | self.helper_user_change_password(password) 62 | kolabWAPhelper.logout_roundcube() 63 | 64 | def test_edit_user_password_multi_domain(self): 65 | kolabWAPhelper = self.kolabWAPhelper 66 | kolabWAPhelper.log("Running test: test_edit_user_password_multi_domain") 67 | 68 | # login Directory Manager 69 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 70 | 71 | domainname = kolabWAPhelper.create_domain() 72 | 73 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 74 | 75 | kolabWAPhelper.logout_kolab_wap() 76 | 77 | # login the new user 78 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 79 | self.helper_user_change_password(password) 80 | kolabWAPhelper.logout_roundcube() 81 | 82 | def tearDown(self): 83 | self.kolabWAPhelper.tear_down() 84 | 85 | if __name__ == "__main__": 86 | unittest.main() 87 | 88 | 89 | -------------------------------------------------------------------------------- /pySeleniumTests/testEmailForwarding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | from selenium import webdriver 9 | from selenium.webdriver.common.keys import Keys 10 | from helperKolabWAP import KolabWAPTestHelpers 11 | 12 | # assumes password for cn=Directory Manager is test 13 | # will create a new user 14 | # will create a new mail forwarding account, to the email of the created user 15 | # will send email from command line 16 | # will login to roundcube and check for the new email 17 | class KolabEmailMailForwarding(unittest.TestCase): 18 | 19 | def setUp(self): 20 | self.kolabWAPhelper = KolabWAPTestHelpers() 21 | self.driver = self.kolabWAPhelper.init_driver() 22 | 23 | def test_mail_forwarding(self): 24 | kolabWAPhelper = self.kolabWAPhelper 25 | kolabWAPhelper.log("Running test: test_mail_forwarding") 26 | 27 | # login Directory Manager, create a user 28 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 29 | 30 | # add the user 31 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 32 | 33 | # create a forward address 34 | username2, emailForwardAddress, password2, uid2 = kolabWAPhelper.create_user(forward_to=emailLogin) 35 | # wait a few seconds for the change to take effect 36 | time.sleep(10) 37 | 38 | kolabWAPhelper.logout_kolab_wap() 39 | 40 | # send email to the forward address from command line 41 | print "sending email to " + emailForwardAddress 42 | subject = 'for ' + username 43 | subprocess.call(['/bin/bash', '-c', 'echo "test" | mail -s "' + subject + '" ' + emailForwardAddress]) 44 | 45 | # login user to roundcube and check for email 46 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 47 | kolabWAPhelper.check_email_received(emailSubjectLine=subject) 48 | kolabWAPhelper.logout_roundcube() 49 | 50 | def test_mail_forwarding_external(self): 51 | kolabWAPhelper = self.kolabWAPhelper 52 | kolabWAPhelper.log("Running test: test_mail_forwarding_external") 53 | 54 | # login Directory Manager, create a user 55 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 56 | 57 | # please modify following line to add a domain that actually can receive emails, ie. has a valid MX record 58 | enabled_maildomain="soliderp.net" 59 | # quit the test if that domain does not exist in the current setup 60 | kolabWAPhelper.select_domain(enabled_maildomain); 61 | 62 | # add the user 63 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 64 | 65 | # create a forward address 66 | # using an external echo address (see https://de.wikipedia.org/wiki/Echo-Mailer) 67 | username2, emailForwardAddress, password2, uid2 = kolabWAPhelper.create_user(forward_to="echo@tu-berlin.de") 68 | time.sleep(10) 69 | 70 | kolabWAPhelper.logout_kolab_wap() 71 | 72 | # login user to roundcube and check for email 73 | kolabWAPhelper.login_roundcube("/roundcubemail", emailLogin, password) 74 | print "sending email to " + emailForwardAddress 75 | emailSubjectLine = kolabWAPhelper.send_email(emailForwardAddress) 76 | kolabWAPhelper.check_email_received(emailSubjectLine="Re: " + emailSubjectLine) 77 | kolabWAPhelper.logout_roundcube() 78 | 79 | def tearDown(self): 80 | self.kolabWAPhelper.tear_down() 81 | 82 | if __name__ == "__main__": 83 | unittest.main() 84 | 85 | 86 | -------------------------------------------------------------------------------- /kolab/patches/intranetToken-wap.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php 2 | index d6a84b8..752064d 100644 3 | --- a/lib/Auth/LDAP.php 4 | +++ b/lib/Auth/LDAP.php 5 | @@ -270,7 +270,7 @@ class LDAP extends Net_LDAP3 { 6 | $admin_readonly_attrs = array("tbitsKolabMaxAccounts", "tbitsKolabOverallQuota"); 7 | 8 | if (in_array('tbitsKolabUser', $this->classes_allowed())) { 9 | - $self_attrs = array_merge($self_attrs, array('tbitsKolabLastLogin', 'tbitsKolabQuotaUsed')); 10 | + $self_attrs = array_merge($self_attrs, array('tbitsKolabLastLogin', 'tbitsKolabQuotaUsed', 'tbitsKolabIntranetToken')); 11 | } 12 | 13 | $_domain = str_replace('.', '_', $domain); 14 | diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php 15 | index 63be019..20b5c07 100644 16 | --- a/lib/client/kolab_client_task_user.php 17 | +++ b/lib/client/kolab_client_task_user.php 18 | @@ -272,6 +272,7 @@ class kolab_client_task_user extends kolab_client_task 19 | 'uid' => 'system', 20 | 'userpassword' => 'system', 21 | 'userpassword2' => 'system', 22 | + 'tbitskolabintranettoken' => 'system', 23 | 'uidnumber' => 'system', 24 | 'gidnumber' => 'system', 25 | 'homedirectory' => 'system', 26 | @@ -321,6 +322,14 @@ class kolab_client_task_user extends kolab_client_task 27 | $form = $this->form_prepare('user', $data, array('userpassword2'), null, $fields_map['type_id']); 28 | list($fields, $types, $type, $add_mode) = $form; 29 | 30 | + // check if tbitsKolabIntranetToken should be active for the current domain 31 | + // see kolab.conf, [kolab] enable_intranet_token, which is a comma separated list of domains 32 | + $conf = Conf::get_instance(); 33 | + $domainsWithIntranetToken = explode(',', $conf->get('enable_intranet_token')); 34 | + if (!in_array($_SESSION['user']['domain'], $domainsWithIntranetToken)) { 35 | + unset($fields['tbitskolabintranettoken']); 36 | + } 37 | + 38 | // Add password confirmation 39 | if (isset($fields['userpassword'])) { 40 | $fields['userpassword2'] = $fields['userpassword']; 41 | diff --git a/lib/locale/de_DE.php b/lib/locale/de_DE.php 42 | index 644d750..18d1755 100644 43 | --- a/lib/locale/de_DE.php 44 | +++ b/lib/locale/de_DE.php 45 | @@ -470,6 +470,7 @@ $LANG['user.tbitskolaboverallquota'] = 'Gesamtquota verfügbar'; 46 | $LANG['user.tbitskolabdefaultquota'] = 'Voreinstellung Quota für Benutzerkonten'; 47 | $LANG['user.statistics'] = 'Info'; 48 | $LANG['user.tbitskolablastlogin'] = 'Letzte erfolgreiche Anmeldung'; 49 | +$LANG['user.tbitskolabintranettoken'] = 'Intranet Anmeldung'; 50 | $LANG['user.tbitskolabquotaused'] = 'Aktueller Speicherplatzverbrauch'; 51 | $LANG['quota.unlimited'] = "Unbegrenzt"; 52 | $LANG['quota.nomailbox'] = "Noch kein Postfach vorhanden"; 53 | diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php 54 | index 2a51005..a620987 100644 55 | --- a/lib/locale/en_US.php 56 | +++ b/lib/locale/en_US.php 57 | @@ -458,6 +458,7 @@ $LANG['user.tbitskolabmaxaccounts'] = 'Maximum number of accounts'; 58 | $LANG['user.tbitskolaboverallquota'] = 'Overall Quota assigned'; 59 | $LANG['user.tbitskolabdefaultquota'] = 'Default Quota for user accounts'; 60 | $LANG['user.tbitskolablastlogin'] = 'Latest successful login'; 61 | +$LANG['user.tbitskolabintranettoken'] = 'Intranet token'; 62 | $LANG['user.tbitskolabquotaused'] = 'Current quota usage'; 63 | $LANG['quota.unlimited'] = "Unlimited"; 64 | $LANG['quota.nomailbox'] = "Mailbox does not exist yet"; 65 | -------------------------------------------------------------------------------- /pySeleniumTests/runTests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # running all Unit tests that are working 4 | tests="all" 5 | 6 | if [ ! -z "$1" ]; then 7 | tests=$1 8 | fi 9 | 10 | # delete created domains starting with domain* 11 | function deleteDomains() { 12 | for d in `kolab list-domains | grep -v "Primary Domain" | grep "^domain"` 13 | do 14 | kolab delete-domain --force $d 15 | php /usr/share/kolab-webadmin/bin/purge-deleted-domains 16 | done 17 | systemctl restart dirsrv.target 18 | systemctl restart httpd 19 | sleep 5 20 | } 21 | 22 | rm -f /tmp/output*.html 23 | 24 | #if [ ! -d /tmp/SeleniumTests ] 25 | #then 26 | # xvfb-run firefox -CreateProfile "SeleniumTests /tmp/SeleniumTests" 27 | #fi 28 | 29 | hasError=0 30 | 31 | # run tests against a vanilla Kolab 32 | if [[ "$tests" == "all" || "$tests" == "vanilla" ]]; then 33 | deleteDomains 34 | ./testCreateUserAndEditSelf.py KolabWAPCreateUserAndEditSelf.test_edit_user_himself || hasError=1 35 | ./testRoundcubeChangePassword.py KolabRoundcubeChangePassword.test_edit_user_password || hasError=1 36 | ./testAutoCreateFolders.py KolabAutoCreateFolders.test_modified_foldername || hasError=1 37 | ./testEmailSendAndReceive.py || hasError=1 38 | ./testEmailSharedFolders.py || hasError=1 39 | fi 40 | 41 | # requires configuration for catchall and forwarding, and multidomain 42 | if [[ "$tests" == "all" || "$tests" == "catchallforwarding" ]]; then 43 | deleteDomains 44 | # we need the multidomain script and patches installed, because otherwise we need to wait for up to 10 minutes for the domain sync to happen 45 | ./testEmailCatchAll.py || hasError=1 46 | # ignore other test test_mail_forwarding_external because it needs configuration of a domain that can receive email from outside 47 | ./testEmailForwarding.py KolabEmailMailForwarding.test_mail_forwarding || hasError=1 48 | fi 49 | 50 | # requires multi domain patch 51 | if [[ "$tests" == "all" || "$tests" == "multidomain" ]]; then 52 | deleteDomains 53 | # these tests have been run in vanilla, but this time we run all test cases, and with SSL 54 | ./testCreateUserAndEditSelf.py || hasError=1 55 | ./testRoundcubeChangePassword.py || hasError=1 56 | deleteDomains 57 | ./testAutoCreateFolders.py || hasError=1 58 | ./testUIDAcrossDomains.py || hasError=1 59 | 60 | deleteDomains 61 | ./testEmailCatchAllAcrossDomains.py || hasError=1 62 | fi 63 | 64 | # requires domain admin patch 65 | if [[ "$tests" == "all" || "$tests" == "domainadmin" ]]; then 66 | deleteDomains 67 | ./testDomainAdmin.py || hasError=1 68 | ./testDomainAdminDefaultQuota.py || hasError=1 69 | ./testDomainAdminMaxAccounts.py || hasError=1 70 | ./testDomainAdminOverallQuota.py || hasError=1 71 | deleteDomains 72 | ./testLastLogin.py || hasError=1 73 | ./testListUsersQuota.py || hasError=1 74 | fi 75 | 76 | # check if kolab sync runs without error (see https://issues.kolab.org/show_bug.cgi?id=4847) 77 | if [ $hasError -ne 1 ]; then 78 | if [[ "$tests" == "all" || "$tests" == "kolabsync" ]]; then 79 | if [ -f /bin/systemctl ] 80 | then 81 | /bin/systemctl stop kolabd.service 82 | elif [ -f /sbin/service ] 83 | then 84 | service kolabd stop 85 | elif [ -f /usr/sbin/service ] 86 | then 87 | service kolab-server stop 88 | fi 89 | 90 | kolab -d 9 sync 2>&1 | tee kolab-sync.log 91 | if [[ "`cat kolab-sync.log | grep UnicodeDecodeError`" != "" ]] 92 | then 93 | hasError=1 94 | fi 95 | 96 | if [ -f /bin/systemctl ] 97 | then 98 | /bin/systemctl start kolabd.service 99 | elif [ -f /sbin/service ] 100 | then 101 | service kolabd start 102 | elif [ -f /usr/sbin/service ] 103 | then 104 | service kolab-server start 105 | fi 106 | fi 107 | fi 108 | 109 | exit $hasError 110 | -------------------------------------------------------------------------------- /kolab/patches/disableSpamFilter.patch: -------------------------------------------------------------------------------- 1 | --- a/pykolab/setup/setup_mta.py 2017-07-26 11:52:32.466516442 +0200 2 | +++ b/pykolab/setup/setup_mta.py 2017-07-26 11:53:44.522072385 +0200 3 | @@ -267,7 +267,6 @@ 4 | "submission_recipient_restrictions": "check_policy_service unix:private/submission_policy, permit_sasl_authenticated, reject", 5 | "submission_sender_restrictions": "reject_non_fqdn_sender, check_policy_service unix:private/submission_policy, permit_sasl_authenticated, reject", 6 | "submission_data_restrictions": "check_policy_service unix:private/submission_policy", 7 | - "content_filter": "smtp-amavis:[127.0.0.1]:10024" 8 | 9 | } 10 | 11 | @@ -458,26 +457,6 @@ 12 | 13 | log.info(_("Configuring and refreshing Anti-Virus...")) 14 | 15 | - if os.path.isfile('/etc/kolab/templates/freshclam.conf.tpl'): 16 | - shutil.copy( 17 | - '/etc/kolab/templates/freshclam.conf.tpl', 18 | - '/etc/freshclam.conf' 19 | - ) 20 | - elif os.path.isfile('/usr/share/kolab/templates/freshclam.conf.tpl'): 21 | - shutil.copy( 22 | - '/usr/share/kolab/templates/freshclam.conf.tpl', 23 | - '/etc/freshclam.conf' 24 | - ) 25 | - else: 26 | - log.error(_("Could not find a ClamAV update configuration file")) 27 | - 28 | - if os.path.isfile('/etc/freshclam.conf'): 29 | - subprocess.call([ 30 | - '/usr/bin/freshclam', 31 | - '--quiet', 32 | - '--datadir="/var/lib/clamav"' 33 | - ]) 34 | - 35 | amavisservice = 'amavisd.service' 36 | clamavservice = 'clamd@amavisd.service' 37 | 38 | @@ -498,36 +477,24 @@ 39 | 40 | if os.path.isfile('/bin/systemctl'): 41 | subprocess.call(['systemctl', 'restart', 'postfix.service']) 42 | - subprocess.call(['systemctl', 'restart', amavisservice]) 43 | - subprocess.call(['systemctl', 'restart', clamavservice]) 44 | subprocess.call(['systemctl', 'restart', 'wallace.service']) 45 | elif os.path.isfile('/sbin/service'): 46 | subprocess.call(['service', 'postfix', 'restart']) 47 | - subprocess.call(['service', 'amavisd', 'restart']) 48 | - subprocess.call(['service', 'clamd.amavisd', 'restart']) 49 | subprocess.call(['service', 'wallace', 'restart']) 50 | elif os.path.isfile('/usr/sbin/service'): 51 | subprocess.call(['/usr/sbin/service','postfix','restart']) 52 | - subprocess.call(['/usr/sbin/service','amavis','restart']) 53 | - subprocess.call(['/usr/sbin/service','clamav-daemon','restart']) 54 | subprocess.call(['/usr/sbin/service','wallace','restart']) 55 | else: 56 | log.error(_("Could not start the postfix, clamav and amavisd services services.")) 57 | 58 | if os.path.isfile('/bin/systemctl'): 59 | subprocess.call(['systemctl', 'enable', 'postfix.service']) 60 | - subprocess.call(['systemctl', 'enable', amavisservice]) 61 | - subprocess.call(['systemctl', 'enable', clamavservice]) 62 | subprocess.call(['systemctl', 'enable', 'wallace.service']) 63 | elif os.path.isfile('/sbin/chkconfig'): 64 | subprocess.call(['chkconfig', 'postfix', 'on']) 65 | - subprocess.call(['chkconfig', 'amavisd', 'on']) 66 | - subprocess.call(['chkconfig', 'clamd.amavisd', 'on']) 67 | subprocess.call(['chkconfig', 'wallace', 'on']) 68 | elif os.path.isfile('/usr/sbin/update-rc.d'): 69 | subprocess.call(['/usr/sbin/update-rc.d', 'postfix', 'defaults']) 70 | - subprocess.call(['/usr/sbin/update-rc.d', 'amavis', 'defaults']) 71 | - subprocess.call(['/usr/sbin/update-rc.d', 'clamav-daemon', 'defaults']) 72 | subprocess.call(['/usr/sbin/update-rc.d', 'wallace', 'defaults']) 73 | else: 74 | log.error(_("Could not configure to start on boot, the " + \ 75 | 76 | -------------------------------------------------------------------------------- /pySeleniumTests/testCreateUserAndEditSelf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | from selenium import webdriver 7 | from selenium.webdriver.common.keys import Keys 8 | from helperKolabWAP import KolabWAPTestHelpers 9 | 10 | # assumes password for cn=Directory Manager is test 11 | # will create a new user, and try to login is that user and change the initials 12 | # will check kolab lm if the calendar folder has been created for the user 13 | class KolabWAPCreateUserAndEditSelf(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.kolabWAPhelper = KolabWAPTestHelpers() 17 | self.driver = self.kolabWAPhelper.init_driver() 18 | 19 | # edit yourself; testing bug https://issues.kolab.org/show_bug.cgi?id=2414 20 | def helper_user_edits_himself(self): 21 | driver = self.driver 22 | elem = driver.find_element_by_xpath("//div[@class=\"settings\"]") 23 | elem.click() 24 | self.kolabWAPhelper.wait_loading(1.0) 25 | elem = driver.find_element_by_name("initials") 26 | elem.send_keys("T") 27 | elem = driver.find_element_by_xpath("//input[@value=\"Submit\"]") 28 | elem.click() 29 | self.kolabWAPhelper.wait_loading() 30 | elem = driver.find_element_by_xpath("//div[@id=\"message\"]") 31 | self.assertEquals("User updated successfully.", elem.text, "User was not saved successfully, message: " + elem.text) 32 | 33 | self.kolabWAPhelper.log("User has updated his own data successfully") 34 | 35 | 36 | def test_edit_user_himself(self): 37 | kolabWAPhelper = self.kolabWAPhelper 38 | kolabWAPhelper.log("Running test: test_edit_user_himself") 39 | 40 | # login Directory Manager 41 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 42 | 43 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 44 | 45 | kolabWAPhelper.logout_kolab_wap() 46 | 47 | # login the new user 48 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", emailLogin, password) 49 | 50 | self.helper_user_edits_himself() 51 | 52 | kolabWAPhelper.logout_kolab_wap() 53 | 54 | def test_edit_user_himself_multi_domain_with_quota(self): 55 | kolabWAPhelper = self.kolabWAPhelper 56 | kolabWAPhelper.log("Running test: test_edit_user_himself_multi_domain_with_quota") 57 | 58 | # login Directory Manager 59 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 60 | 61 | domainname = kolabWAPhelper.create_domain() 62 | 63 | username, emailLogin, password, uid = kolabWAPhelper.create_user(mail_quota="20kb") 64 | 65 | kolabWAPhelper.logout_kolab_wap() 66 | 67 | # login the new user 68 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", emailLogin, password) 69 | 70 | self.helper_user_edits_himself() 71 | 72 | kolabWAPhelper.logout_kolab_wap() 73 | 74 | def test_edit_user_himself_multi_domain(self): 75 | kolabWAPhelper = self.kolabWAPhelper 76 | kolabWAPhelper.log("Running test: test_edit_user_himself_multi_domain") 77 | 78 | # login Directory Manager 79 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 80 | 81 | domainname = kolabWAPhelper.create_domain() 82 | 83 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 84 | 85 | kolabWAPhelper.logout_kolab_wap() 86 | 87 | # login the new user 88 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", emailLogin, password) 89 | 90 | self.helper_user_edits_himself() 91 | 92 | kolabWAPhelper.logout_kolab_wap() 93 | 94 | 95 | def tearDown(self): 96 | self.kolabWAPhelper.tear_down() 97 | 98 | if __name__ == "__main__": 99 | unittest.main() 100 | 101 | 102 | -------------------------------------------------------------------------------- /kolab/patches/domainAdminMaxAccounts.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/Auth.php b/lib/Auth.php 2 | index b42b40c..591dd3a 100644 3 | --- a/lib/Auth.php 4 | +++ b/lib/Auth.php 5 | @@ -198,6 +198,11 @@ class Auth { 6 | return $this->auth_instance()->domain_add($domain, $domain_attrs); 7 | } 8 | 9 | + public function domainadmin_get_number_of_accounts($domainadmin) 10 | + { 11 | + return $this->auth_instance()->domainadmin_get_number_of_accounts($domainadmin); 12 | + } 13 | + 14 | public function domain_edit($domain, $attributes, $typeid = null) 15 | { 16 | return $this->auth_instance()->domain_edit($domain, $attributes, $typeid); 17 | diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php 18 | index 2897399..32cba82 100644 19 | --- a/lib/Auth/LDAP.php 20 | +++ b/lib/Auth/LDAP.php 21 | @@ -436,6 +436,25 @@ class LDAP extends Net_LDAP3 { 22 | return $quota; 23 | } 24 | 25 | + // get the number of accounts that this domainadmin manages. 26 | + public function domainadmin_get_number_of_accounts($domainadmin) 27 | + { 28 | + $numberOfAccounts = 0; 29 | + $domains = $this->domainadmin_get_domains($domainadmin); 30 | + foreach ($domains as $domain) { 31 | + // get all users that are part of this domain; the domainadmin itself is not of class mailrecipient 32 | + $users_result = $this->search( 33 | + $this->_standard_root_dn($domain), 34 | + "objectclass=mailrecipient"); 35 | + if ($users_result != null && count($users_result) > 0) { 36 | + $users = $users_result->entries(true); 37 | + $numberOfAccounts += count($users); 38 | + } 39 | + } 40 | + 41 | + return $numberOfAccounts; 42 | + } 43 | + 44 | public function domain_edit($domain, $attributes, $typeid = null) 45 | { 46 | $domain = $this->domain_info($domain, array_keys($attributes)); 47 | diff --git a/lib/api/kolab_api_service_user.php b/lib/api/kolab_api_service_user.php 48 | index 3559722..fc1dd54 100644 49 | --- a/lib/api/kolab_api_service_user.php 50 | +++ b/lib/api/kolab_api_service_user.php 51 | @@ -67,6 +67,34 @@ class kolab_api_service_user extends kolab_api_service 52 | } 53 | 54 | /** 55 | + * check if the domain admin is allowed to add another account. 56 | + * using tbitsKolabMaxAccounts from LDAP 57 | + * 58 | + * @throws an exception if maximum number of accounts has been reached 59 | + */ 60 | + private function validate_user_add() 61 | + { 62 | + $auth = Auth::get_instance(); 63 | + $conf = Conf::get_instance(); 64 | + 65 | + // get the domain admin that is defined closest to this domain (least number of accounts) 66 | + // and get the number of accounts that this domain admin has booked 67 | + $result = $auth->domainadmin_get_configuration($_SESSION['user']->get_domain(), 'tbitskolabmaxaccounts'); 68 | + 69 | + if (!empty($result)) { 70 | + $domainadmin = $result['domainadmin']; 71 | + $bookedaccounts = $result['tbitskolabmaxaccounts']; 72 | + $numberOfAccounts = $auth->domainadmin_get_number_of_accounts($domainadmin); 73 | + if ($numberOfAccounts >= $bookedaccounts) { 74 | + throw new Exception('error: Cannot create another account.
'. 75 | + 'maximum accounts booked: '.$bookedaccounts.'
'. 76 | + 'for DomainAdmin '.$domainadmin.'
'. 77 | + 'Please order more accounts!'); 78 | + } 79 | + } 80 | + } 81 | + 82 | + /** 83 | * Create user. 84 | * 85 | * @param array $get GET parameters 86 | @@ -78,6 +106,9 @@ class kolab_api_service_user extends kolab_api_service 87 | { 88 | Log::trace("user_add()", $postdata); 89 | 90 | + // check if the domainadmin is allowed to add more accounts 91 | + $this->validate_user_add(); 92 | + 93 | $attributes = $this->parse_input_attributes('user', $postdata); 94 | 95 | password_policy::validate_password($attributes['userpassword']); 96 | -------------------------------------------------------------------------------- /pySeleniumTests/testDomainAdminOverallQuota.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.keys import Keys 6 | from helperKolabWAP import KolabWAPTestHelpers 7 | 8 | # assumes password for cn=Directory Manager is test. 9 | # assumes that the initTBitsISP.sh script has been run. 10 | # will create a domain admin user, with a overall quota (type domainadmin) 11 | # will create a new domain, and assign that domain admin user as domain administrator 12 | # will create users inside that new domain 13 | # will check that the domain quota is observed 14 | class KolabWAPDomainAdmin(unittest.TestCase): 15 | 16 | def setUp(self): 17 | self.kolabWAPhelper = KolabWAPTestHelpers() 18 | self.driver = self.kolabWAPhelper.init_driver() 19 | 20 | # check that domain admin cannot assign too much quota to the user accounts 21 | def test_overall_quota_limit(self): 22 | kolabWAPhelper = self.kolabWAPhelper 23 | kolabWAPhelper.log("Running test: test_overall_quota_limit") 24 | 25 | # login Directory Manager 26 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 27 | 28 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 29 | prefix = "admin", 30 | overall_quota = "800mb") 31 | 32 | # create domains, with domain admin 33 | domainname = kolabWAPhelper.create_domain(username) 34 | domainname2 = kolabWAPhelper.create_domain(username) 35 | 36 | # create user accounts 37 | kolabWAPhelper.select_domain(domainname) 38 | # test if no account has been created yet, validation will still kick in 39 | kolabWAPhelper.create_user(mail_quota = "2gb", expected_message_contains = "mailquota of the domain admin has been exceeded") 40 | kolabWAPhelper.create_user(mail_quota = "200mb") 41 | kolabWAPhelper.select_domain(domainname2) 42 | # should fail, exceeding the overall quota of the domain admin 43 | kolabWAPhelper.create_user(mail_quota = "900mb", expected_message_contains = "mailquota of the domain admin has been exceeded") 44 | kolabWAPhelper.create_user(mail_quota = "600mb") 45 | 46 | kolabWAPhelper.logout_kolab_wap() 47 | 48 | # test that a domain admin with a mail quota cannot create user mailboxes with no quota specified 49 | def test_unlimited_user_quota(self): 50 | kolabWAPhelper = self.kolabWAPhelper 51 | kolabWAPhelper.log("Running test: test_unlimited_user_quota") 52 | 53 | # login Directory Manager 54 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 55 | 56 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 57 | prefix = "admin", 58 | overall_quota = "1gb") 59 | 60 | # create domain, with domain admin 61 | domainname = kolabWAPhelper.create_domain(username) 62 | 63 | # create user account 64 | kolabWAPhelper.create_user(expected_message_contains = "must specify a mailquota for the user") 65 | 66 | kolabWAPhelper.logout_kolab_wap() 67 | 68 | # test that a domain admin with no mail quota can create user mailboxes with as much quota as he wants 69 | def test_no_quota(self): 70 | kolabWAPhelper = self.kolabWAPhelper 71 | kolabWAPhelper.log("Running test: test_no_quota") 72 | 73 | # login Directory Manager 74 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 75 | 76 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 77 | prefix = "admin") 78 | 79 | # create domain, with domain admin 80 | domainname = kolabWAPhelper.create_domain(username) 81 | 82 | # create user account with a quota 83 | kolabWAPhelper.create_user(mail_quota = "100mb") 84 | # without any quota for the user 85 | kolabWAPhelper.create_user() 86 | 87 | kolabWAPhelper.logout_kolab_wap() 88 | 89 | def tearDown(self): 90 | self.kolabWAPhelper.tear_down() 91 | 92 | if __name__ == "__main__": 93 | unittest.main() 94 | 95 | 96 | -------------------------------------------------------------------------------- /kolab/initSetupKolabPatches.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ##################################################################################### 11 | # apply a couple of patches, see related kolab bugzilla number in filename, eg. https://issues.kolab.org/show_bug.cgi?id=2018 12 | ##################################################################################### 13 | 14 | if [ -z $APPLYPATCHES ] 15 | then 16 | APPLYPATCHES=1 17 | fi 18 | 19 | if [ $APPLYPATCHES -eq 1 ] 20 | then 21 | echo "applying patch for Roundcube Kolab plugin for storage in MariaDB" 22 | patch -p1 --fuzz=0 -i `pwd`/patches/roundcubeStorageMariadbBug4883.patch -d /usr/share/roundcubemail || exit -1 23 | 24 | # TODO: see if we still need these patches 25 | #echo "applying patch for waiting after restart of dirsrv (necessary on Debian)" 26 | #patch -p1 --fuzz=0 -i `pwd`/patches/setupKolabSleepDirSrv.patch -d $pythonDistPackages || exit -1 27 | 28 | # https://github.com/TBits/KolabScripts/issues/76 29 | echo "fix problem on LXC containers with access to TCP keepalive settings" 30 | patch -p1 --fuzz=0 -i `pwd`/patches/fixPykolabIMAPKeepAlive.patch -d $pythonDistPackages || exit -1 31 | 32 | echo "apply patch for Etc timezone in roundcube plugins/calendar" 33 | patch -p1 --fuzz=0 -i `pwd`/patches/roundcube_calendar_etc_timezone_T2666.patch -d /usr/share/roundcubemail || exit -1 34 | # another way to fix it, in the jstz library (see also https://bitbucket.org/pellepim/jstimezonedetect/issues/168/ignore-timezones-like-etc-gmt-1) 35 | sed -i 's#"UTC"===a)#"UTC"===a)\&\&a.indexOf("Etc")<0#' /usr/share/roundcubemail/public_html/assets/program/js/jstz.min.js 36 | 37 | echo "do not rename existing mailboxes" 38 | patch -p1 --fuzz=0 -i `pwd`/patches/pykolab_do_not_rename_existing_mailbox_T3315.patch -d $pythonDistPackages || exit -1 39 | 40 | echo "kolab lam should cope with invalid mailbox names more gracefully" 41 | patch -p1 --fuzz=0 -i `pwd`/patches/kolab_lam_invalid_mailbox_name.patch -d $pythonDistPackages || exit -1 42 | 43 | fi 44 | 45 | if [[ $OS == Debian* ]] 46 | then 47 | # workaround for bug 2050, https://issues.kolab.org/show_bug.cgi?id=2050 48 | echo "export ZEND_DONT_UNLOAD_MODULES=1" >> /etc/apache2/envvars 49 | 50 | # TODO on Debian, we need to install the rewrite for the csrf token 51 | newConfigLines="\tRewriteEngine On\n \ 52 | \tRewriteRule ^/roundcubemail/[a-f0-9]{16}/(.*) /roundcubemail/\$1 [PT,L]\n \ 53 | \tRewriteRule ^/webmail/[a-f0-9]{16}/(.*) /webmail/\$1 [PT,L]\n \ 54 | \tRedirectMatch ^/$ /roundcubemail/\n" 55 | 56 | # sed -i -e "s~~$newConfigLines~" /etc/apache2/sites-enabled/000-default 57 | fi 58 | 59 | if [[ $OS == CentOS* || $OS == Fedora* ]] 60 | then 61 | if [[ "`rpm -qa | grep guam`" != "" ]] 62 | then 63 | systemctl start guam || exit -1 64 | fi 65 | # we need a fully qualified hostname for amavisd to restart successfully, and later for setting up the ldap as well. 66 | # on LXD, the container name is not allowed a dot in the name. therefore we need to set the hostname here 67 | hostname=`hostname -f` 68 | hostname=${hostname//-/.} 69 | # but only use maximum two subdomains for the host, so that the email address will be @subdomain.domain.tld 70 | DOTS=${hostname//[^.]}; 71 | while [[ ${#DOTS} -gt 3 ]]; do hostname="${hostname#*.}"; DOTS=${hostname//[^.]}; done 72 | hostnamectl set-hostname $hostname 73 | 74 | # there is an issue with lxc 2.0.8 and CentOS, with PrivateDevices 75 | # https://github.com/lxc/lxc/issues/1623 76 | # journalctl -xe shows: 77 | # -- Unit amavisd.service has begun starting up. 78 | # systemd[3849]: Failed at step NAMESPACE spawning /usr/sbin/amavisd: Invalid argument 79 | # -- Subject: Process /usr/sbin/amavisd could not be executed 80 | if [ -f /usr/lib/systemd/system/amavisd.service ]; then 81 | sed -i 's/PrivateDevices=true/#PrivateDevices=true/g' /usr/lib/systemd/system/amavisd.service 82 | systemctl daemon-reload 83 | systemctl restart amavisd.service 84 | fi 85 | else 86 | if [[ "`dpkg -l | grep guam`" != "" ]] 87 | then 88 | systemctl start guam || exit -1 89 | fi 90 | fi 91 | -------------------------------------------------------------------------------- /pySeleniumTests/testAutoCreateFolders.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | from selenium import webdriver 7 | from selenium.webdriver.common.keys import Keys 8 | import subprocess 9 | from helperKolabWAP import KolabWAPTestHelpers 10 | 11 | # assumes that initMultiDomain.sh has been run 12 | # assumes password "test" for Directory Manager 13 | # will modify [kolab] autocreate_folders, renames a folder 14 | # will create a new domain, and a new user inside that new domain 15 | # will check kolab lm if the renamed folder has been created for the user 16 | class KolabAutoCreateFolders(unittest.TestCase): 17 | 18 | def setUp(self): 19 | self.kolabWAPhelper = KolabWAPTestHelpers() 20 | self.driver = self.kolabWAPhelper.init_driver() 21 | 22 | def helper_modify_autocreate_folders(self): 23 | # read kolab.conf 24 | fo = open("/etc/kolab/kolab.conf", "r+") 25 | content = fo.read() 26 | fo.close() 27 | 28 | newContactsFolderName = "Contacts" + datetime.datetime.now().strftime("%H%M%S") 29 | 30 | # find [kolab], find line starting with 'Contacts, replace with 'Contacts125559': { 31 | pos = content.index("[kolab]") 32 | pos = content.index("'Contacts", pos) 33 | posAfter = content.index(": {", pos) 34 | 35 | content = content[:pos] + "'" + newContactsFolderName + "'" + content[posAfter:] 36 | 37 | # write kolab.conf 38 | fo = open("/etc/kolab/kolab.conf", "wb") 39 | fo.write(content) 40 | fo.close() 41 | 42 | # restart kolabd to pickup the changed kolab.conf file 43 | self.kolabWAPhelper.startKolabServer("restart") 44 | 45 | self.kolabWAPhelper.log("kolab.conf has been changed, autocreate_folders now contains " + newContactsFolderName) 46 | 47 | return newContactsFolderName 48 | 49 | def test_modified_foldername(self): 50 | 51 | kolabWAPhelper = self.kolabWAPhelper 52 | kolabWAPhelper.log ("Running test: test_modified_foldername") 53 | 54 | # login 55 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 56 | 57 | #modify the default folders in /etc/kolab/kolab.conf 58 | newContactsFolderName = self.helper_modify_autocreate_folders() 59 | 60 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 61 | 62 | kolabWAPhelper.logout_kolab_wap() 63 | 64 | # check if mailbox has been created, with the modified folder name 65 | out = "" 66 | starttime=datetime.datetime.now() 67 | while newContactsFolderName not in out and (datetime.datetime.now()-starttime).seconds < 60: 68 | kolabWAPhelper.wait_loading(1) 69 | p = subprocess.Popen(kolabWAPhelper.getCmdListMailboxes() + " | grep " + username, shell=True, stdout=subprocess.PIPE) 70 | out, err = p.communicate() 71 | if newContactsFolderName not in out: 72 | self.assertTrue(False, "kolab lm cannot find mailbox with folder " + newContactsFolderName + " for new user " + username) 73 | 74 | def test_modified_foldername_in_new_domain(self): 75 | 76 | kolabWAPhelper = self.kolabWAPhelper 77 | kolabWAPhelper.log ("Running test: test_modified_foldername_in_new_domain") 78 | 79 | # login 80 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 81 | 82 | domainname = kolabWAPhelper.create_domain() 83 | 84 | #modify the default folders in /etc/kolab/kolab.conf 85 | newContactsFolderName = self.helper_modify_autocreate_folders() 86 | 87 | username, emailLogin, password, uid = kolabWAPhelper.create_user() 88 | 89 | kolabWAPhelper.logout_kolab_wap() 90 | 91 | # check if mailbox has been created, with the modified folder name 92 | out = "" 93 | starttime=datetime.datetime.now() 94 | while newContactsFolderName not in out and (datetime.datetime.now()-starttime).seconds < 60: 95 | kolabWAPhelper.wait_loading(1) 96 | p = subprocess.Popen(kolabWAPhelper.getCmdListMailboxes() + " | grep " + username, shell=True, stdout=subprocess.PIPE) 97 | out, err = p.communicate() 98 | if newContactsFolderName not in out: 99 | self.assertTrue(False, "kolab lm cannot find mailbox with folder " + newContactsFolderName + " for new user " + username) 100 | 101 | def tearDown(self): 102 | self.kolabWAPhelper.tear_down() 103 | 104 | if __name__ == "__main__": 105 | unittest.main() 106 | 107 | 108 | -------------------------------------------------------------------------------- /kolab/initTBitsUserTypes.php: -------------------------------------------------------------------------------- 1 | get('kolab', 'primary_domain'); 8 | $ldappassword = $conf->get('ldap', 'bind_pw'); 9 | $_SESSION['user'] = new User(); 10 | $valid = $_SESSION['user']->authenticate("cn=Directory Manager", $ldappassword, $primary_domain); 11 | 12 | if ($valid === false) { 13 | die ("cannot authenticate user cn=Directory Manager"); 14 | } 15 | 16 | $auth = Auth::get_instance(); 17 | 18 | $user_types = new kolab_api_service_user_types(null); 19 | $list = $user_types->user_types_list(null, null); 20 | # copy the entry for kolab 21 | if ($list['list'][1]['key'] != 'kolab') { 22 | echo ("failure: expected user type kolab at position 1, but found ".$list['list'][1]['key']). "\n"; 23 | die(); 24 | } 25 | 26 | $kolabUserType = $list['list'][1]; 27 | $kolabUserType['id'] = 1; 28 | $kolabUserType['type'] = 'user'; 29 | if (!in_array('tbitskolabuser', $kolabUserType['attributes']['fields']['objectclass'])) { 30 | $kolabUserType['attributes']['fields']['objectclass'][] = 'tbitskolabuser'; 31 | $kolabUserType['attributes']['form_fields']['tbitskolablastlogin'] = array('type' => 'text-unixtimestamp', 'optional' => 1); 32 | $kolabUserType['attributes']['form_fields']['tbitskolabquotaused'] = array('type' => 'text-quotaused', 'optional' => 1); 33 | $kolabUserType['attributes']['form_fields']['tbitskolabintranettoken'] = array('type' => 'text', 'optional' => 1); 34 | $service_type = new kolab_api_service_type(null); 35 | $result = $service_type->type_edit(null, $kolabUserType); 36 | //echo "saving user type kolab: ".print_r($result,true)."\n"; 37 | if ($result === false) { 38 | echo "failure: was not able to save user type kolab\n"; 39 | die(); 40 | } 41 | } 42 | 43 | foreach($list['list'] as $usertype) { 44 | if ($usertype['key'] == 'domainadmin') { 45 | echo "there is already a domain admin, not adding again\n"; 46 | die(); 47 | } 48 | } 49 | 50 | $newType = $kolabUserType; 51 | unset($newType['id']); 52 | unset($newType['is_default']); 53 | $newType['type'] = 'user'; 54 | $newType['key'] = 'domainadmin'; 55 | $newType['name'] = 'Domain Administrator'; 56 | $newType['description'] = 'A Kolab Domain Administrator'; 57 | // we need a new array, otherwise ldap error when adding new domain admins: 58 | // ldap_add(): Value array must have consecutive indices 0, 1, ... in /usr/share/php/Net/LDAP3.php on line 196 59 | $newType['attributes']['fields']['objectclass'] = array(); 60 | $newType['attributes']['fields']['objectclass'][] = 'top'; 61 | $newType['attributes']['fields']['objectclass'][] = 'inetorgperson'; 62 | $newType['attributes']['fields']['objectclass'][] = 'organizationalperson'; 63 | $newType['attributes']['fields']['objectclass'][] = 'person'; 64 | $newType['attributes']['fields']['objectclass'][] = 'tbitskolabuser'; 65 | $newType['attributes']['fields']['objectclass'][] = 'tbitskolabdomainadmin'; 66 | unset($newType['attributes']['auto_form_fields']['alias']); 67 | unset($newType['attributes']['auto_form_fields']['mailhost']); 68 | // for testing, we have a default value for mailhost (configureKolabUserMailhost.py) 69 | // so we also need to drop mailhost from form_fields 70 | unset($newType['attributes']['form_fields']['mailhost']); 71 | unset($newType['attributes']['auto_form_fields']['mail']); 72 | unset($newType['attributes']['form_fields']['mailquota']); 73 | unset($newType['attributes']['form_fields']['mailalternateaddress']); 74 | unset($newType['attributes']['form_fields']['alias']); 75 | unset($newType['attributes']['form_fields']['mail']); 76 | unset($newType['attributes']['form_fields']['kolabdelegate']); 77 | unset($newType['attributes']['form_fields']['kolaballowsmtprecipient']); 78 | unset($newType['attributes']['form_fields']['kolaballowsmtpsender']); 79 | unset($newType['attributes']['form_fields']['kolabinvitationpolicy']); 80 | unset($newType['attributes']['form_fields']['tbitskolabquotaused']); 81 | $newType['attributes']['form_fields']['tbitskolabmaxaccounts'] = array('type' => 'text', 'optional' => 1); 82 | $newType['attributes']['form_fields']['tbitskolaboverallquota'] = array('type' => 'text-quota', 'optional' => 1); 83 | $newType['attributes']['form_fields']['tbitskolabdefaultquota'] = array('type' => 'text-quota', 'optional' => 1); 84 | 85 | $result = $service_type->type_add(null, $newType); 86 | //echo "saving user type domainadmin: ".print_r($result,true)."\n"; 87 | if ($result === false) { 88 | echo "failure: was not able to add new user type domainadmin\n"; 89 | die(); 90 | } 91 | 92 | echo "added new user type domainadmin\n"; 93 | ?> 94 | -------------------------------------------------------------------------------- /kolab/reinstallDebianUbuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # this script will remove Kolab, and DELETE all YOUR data!!! 3 | # it will reinstall Kolab, from Kolab 16 4 | # you can optionally install the patches from TBits, see bottom of script reinstall.sh 5 | 6 | echo "this script will remove Kolab, and DELETE all YOUR data!!!" 7 | read -p "Are you sure? Type y or Ctrl-C " -r 8 | echo 9 | if [[ ! $REPLY =~ ^[Yy]$ ]] 10 | then 11 | exit 1 12 | fi 13 | 14 | if [ -z $1 ] 15 | then 16 | echo "please call $0 " 17 | exit 1 18 | fi 19 | 20 | OBS_repo_OS=$1 21 | 22 | if [[ $OBS_repo_OS == "Debian_7.0" ]] 23 | then 24 | LBS_repo_OS="debian/wheezy wheezy" 25 | elif [[ $OBS_repo_OS == "Debian_8.0" ]] 26 | then 27 | LBS_repo_OS="debian/jessie jessie" 28 | elif [[ $OBS_repo_OS == "Ubuntu_14.04" ]] 29 | then 30 | LBS_repo_OS="ubuntu/trusty trusty" 31 | elif [[ $OBS_repo_OS == "Ubuntu_16.04" ]] 32 | then 33 | LBS_repo_OS="ubuntu/xenial xenial" 34 | fi 35 | 36 | if [ -z `hostname -f | awk -F "." '{ print $2 }'` ] 37 | then 38 | echo "FAILURE: please make sure you have configured a FQDN" 39 | echo 40 | echo 41 | exit 1 42 | fi 43 | 44 | # install locale to avoid problems like: 45 | # Please check that your locale settings: 46 | # LANGUAGE = (unset), 47 | # LC_ALL = (unset), 48 | # LANG = "en_US.UTF-8" 49 | # are supported and installed on your system. 50 | debconf-set-selections <<< 'locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8' 51 | debconf-set-selections <<< 'locales locales/default_environment_locale select en_US.UTF-8' 52 | export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true 53 | dpkg-reconfigure locales 54 | 55 | # make sure that mysql installs noninteractively 56 | # to get these values: apt-get install mysql-server && apt-get install debconf-utils && debconf-get-selections | grep mysql 57 | debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password_again password' 58 | debconf-set-selections <<< 'mysql-server-5.5 mysql-server/root_password password' 59 | 60 | service kolab-server stop 61 | service kolab-saslauthd stop 62 | service cyrus-imapd stop 63 | service dirsrv stop 64 | service wallace stop 65 | service apache2 stop 66 | 67 | if [ -f /usr/sbin/remove-ds-admin ] 68 | then 69 | sed -i "s#/usr/lib/x86_64-linux-gnu/dirsrv/perl);#/usr/lib/x86_64-linux-gnu/dirsrv/perl);\nuse lib qw(/usr/lib/dirsrv/perl);#g" /usr/sbin/remove-ds-admin 70 | /usr/sbin/remove-ds-admin -f -a -y 71 | fi 72 | 73 | # only actually remove all packages if there has been an installation already. 74 | # we need this workaround because there are problems installing postfix 75 | # a second time on an automatically installed system for testing purposes 76 | if [ -d /etc/dirsrv ] 77 | then 78 | apt-get -y purge apache2\* 389\* cyrus-imapd\* postfix\* mysql-server\* roundcube\* pykolab\* kolab\* libkolab\* kolab-3\* php-net-ldap3 79 | 80 | echo "deleting files..." 81 | rm -Rf \ 82 | /etc/postfix \ 83 | /etc/apache2 \ 84 | /etc/roundcubemail \ 85 | /etc/kolab* \ 86 | /etc/cyrus.conf \ 87 | /etc/imapd.conf \ 88 | /etc/dirsrv \ 89 | /etc/ssl/private/example* \ 90 | /etc/ssl/certs/example* \ 91 | /etc/dirsrv/slapd-* \ 92 | /usr/lib64/dirsrv \ 93 | /usr/share/kolab-webadmin \ 94 | /usr/share/roundcubemail \ 95 | /usr/share/kolab-syncroton \ 96 | /usr/share/kolab \ 97 | /usr/share/dirsrv \ 98 | /usr/share/389-* \ 99 | /var/cache/dirsrv \ 100 | /var/cache/kolab-webadmin \ 101 | /var/log/kolab* \ 102 | /var/log/dirsrv \ 103 | /var/log/roundcube \ 104 | /var/log/maillog \ 105 | /var/lib/dirsrv \ 106 | /var/lib/imap \ 107 | /var/lib/kolab \ 108 | /var/lib/mysql \ 109 | /tmp/*-Net_LDAP2_Schema.cache \ 110 | /var/spool/imap \ 111 | /var/spool/postfix 112 | fi 113 | 114 | # could use environment variable obs=http://my.proxy.org/obs.kolabsys.com 115 | # see http://kolab.org/blog/timotheus-pokorra/2013/11/26/downloading-obs-repo-php-proxy-file 116 | if [[ "$obs" = "" ]] 117 | then 118 | export obs=http://obs.kolabsys.com/repositories/ 119 | fi 120 | 121 | cat > /etc/apt/sources.list.d/kolab.list < /etc/apt/preferences.d/kolab <" 31 | exit 1 32 | fi 33 | OBS_repo_OS=$1 34 | 35 | systemctl stop kolabd 36 | systemctl stop kolab-saslauthd 37 | systemctl stop cyrus-imapd 38 | systemctl stop dirsrv.target 39 | systemctl stop wallace 40 | systemctl stop clamd@amavisd 41 | systemctl stop amavisd 42 | systemctl stop httpd 43 | systemctl stop mariadb 44 | systemctl stop guam 45 | 46 | dirsrvinstance=$(find /var/lib/dirsrv -type d -name slapd-*) 47 | if [ ! -z "$dirsrvinstance" ] 48 | then 49 | dirsrvinstance=$(basename $dirsrvinstance) 50 | remove-ds.pl -a -i $dirsrvinstance 51 | fi 52 | 53 | yum -y remove 389\* cyrus-imapd\* postfix\* mariadb-server\* guam\* roundcube\* pykolab\* kolab\* libkolab\* libcalendaring\* kolab-3\* httpd php-Net-LDAP3 up-imapproxy nginx stunnel 54 | 55 | echo "deleting files..." 56 | rm -Rf \ 57 | /etc/dirsrv \ 58 | /etc/kolab \ 59 | /etc/postfix \ 60 | /etc/pki/tls/private/example* \ 61 | /etc/pki/tls/certs/example* \ 62 | /etc/roundcubemail \ 63 | /usr/lib64/dirsrv \ 64 | /usr/share/kolab-webadmin \ 65 | /usr/share/roundcubemail \ 66 | /usr/share/kolab-syncroton \ 67 | /usr/share/kolab \ 68 | /usr/share/dirsrv \ 69 | /var/cache/dirsrv \ 70 | /var/cache/kolab-webadmin \ 71 | /var/lock/dirsrv \ 72 | /var/log/kolab* \ 73 | /var/log/dirsrv \ 74 | /var/log/roundcube \ 75 | /var/log/maillog \ 76 | /var/lib/dirsrv \ 77 | /var/lib/imap \ 78 | /var/lib/kolab \ 79 | /var/lib/mysql \ 80 | /tmp/*-Net_LDAP2_Schema.cache \ 81 | /var/spool/imap \ 82 | /var/spool/postfix 83 | 84 | if [[ $OBS_repo_OS == CentOS* ]] 85 | then 86 | yum -y install epel-release yum-utils 87 | elif [[ $OBS_repo_OS == Fedora* ]] 88 | then 89 | dnf -y install 'dnf-command(config-manager)' 90 | fi 91 | 92 | # could use environment variable obs=http://my.proxy.org/obs.kolabsys.com 93 | # see http://kolab.org/blog/timotheus-pokorra/2013/11/26/downloading-obs-repo-php-proxy-file 94 | if [[ "$obs" = "" ]] 95 | then 96 | export obs=http://obs.kolabsys.com/repositories/ 97 | fi 98 | 99 | if [[ "$repo" = "" ]] 100 | then 101 | export repo=$obs/Kolab:/16/$OBS_repo_OS/Kolab:16.repo 102 | fi 103 | 104 | rm -f /etc/yum.repos.d/Kolab*.repo /etc/yum.repos.d/lbs-tbits.net-kolab-nightly.repo 105 | if [[ $OBS_repo_OS == CentOS* ]] 106 | then 107 | yum-config-manager --add-repo $repo 108 | elif [[ $OBS_repo_OS == Fedora* ]] 109 | then 110 | dnf config-manager --add-repo $repo 111 | fi 112 | 113 | rpm --import "https://ssl.kolabsys.com/community.asc" 114 | rpm --import "http://obs.kolabsys.com/repositories/Kolab:/16/CentOS_7/repodata/repomd.xml.key" 115 | # https://keyserver.ubuntu.com/pks/lookup?search=devel%40lists.kolab.org&op=vindex 116 | rpm --import "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x830C2BCF446D5A45" 117 | 118 | # add priority = 1 to kolab repo files 119 | for f in /etc/yum.repos.d/Kolab*.repo /etc/yum.repos.d/tpokorra-Kolab*.repo 120 | do 121 | sed -i "s#enabled=1#enabled=1\npriority=1#g" $f 122 | sed -i "s#http://obs.kolabsys.com:82/#$obs/#g" $f 123 | done 124 | 125 | if [[ $OBS_repo_OS == CentOS* ]] 126 | then 127 | yum clean metadata 128 | 129 | tryagain=0 130 | yum -y install kolab kolab-freebusy patch unzip php-imap nmap-ncat || tryagain=1 131 | if [ $tryagain -eq 1 ]; then 132 | # yum clean metadata 133 | sleep 30 134 | yum -y install kolab kolab-freebusy patch unzip php-imap nmap-ncat || exit -1 135 | fi 136 | if [ -z $WITHOUTSPAMFILTER ] 137 | then 138 | yum -y install clamav-update || exit -1 139 | fi 140 | elif [[ $OBS_repo_OS == Fedora* ]] 141 | then 142 | dnf clean metadata 143 | dnf -y install kolab kolab-freebusy patch unzip php-imap aspell nmap-ncat || exit -1 144 | if [ -z $WITHOUTSPAMFILTER ] 145 | then 146 | dnf -y install clamav-update || exit -1 147 | fi 148 | fi 149 | 150 | if [ -z $WITHOUTSPAMFILTER ] 151 | then 152 | sed -i "s/^Example/#Example/g" /etc/freshclam.conf 153 | sed -i "s/#DatabaseMirror db.XY.clamav.net/DatabaseMirror db.de.clamav.net/g" /etc/freshclam.conf 154 | # Problem with clamav 0.99.1 in Epel: https://bugzilla.redhat.com/show_bug.cgi?id=1325717 155 | if [ -f ~/.ssh/main.cld ] 156 | then 157 | # use our cached files 158 | cp -f ~/.ssh/*.c*d /var/lib/clamav/ 159 | else 160 | freshclam 161 | fi 162 | fi 163 | 164 | if [ ! -z $WITHOUTSPAMFILTER ] 165 | then 166 | ./disableSpamFilter.sh || exit -1 167 | fi 168 | -------------------------------------------------------------------------------- /pySeleniumTests/testUIDAcrossDomains.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | from selenium import webdriver 9 | from selenium.webdriver.common.keys import Keys 10 | from helperKolabWAP import KolabWAPTestHelpers 11 | 12 | # assumes password for cn=Directory Manager is test 13 | # will create 1 new domain, with a user 14 | # will check that it is not possible to create a user with the same uid in another domain 15 | # will authenticate with kolab-saslauthd with just the uid 16 | # will login to webadmin with just the uid 17 | class KolabUniqueIDAcrossDomains(unittest.TestCase): 18 | 19 | def setUp(self): 20 | self.kolabWAPhelper = KolabWAPTestHelpers() 21 | self.driver = self.kolabWAPhelper.init_driver() 22 | 23 | def enable_editable_uid(self): 24 | driver = self.driver 25 | driver.get(driver.current_url) 26 | 27 | elem = driver.find_element_by_link_text("Settings") 28 | elem.click() 29 | self.kolabWAPhelper.wait_loading() 30 | elem = self.driver.find_element_by_id("searchinput") 31 | elem.send_keys("Kolab User") 32 | elem.send_keys(Keys.ENTER) 33 | self.kolabWAPhelper.wait_loading(initialwait = 2) 34 | elem = self.driver.find_element_by_xpath("//table[@id='settingstypelist']/tbody/tr/td") 35 | self.assertEquals("Kolab User", elem.text, "Expected to select Kolab User but was " + elem.text) 36 | elem.click() 37 | self.kolabWAPhelper.wait_loading(initialwait = 1) 38 | elem = driver.find_element_by_link_text("Attributes") 39 | elem.click() 40 | elem = driver.find_element_by_xpath("//tr[@id='attr_table_row_uid']/td[@class='actions']/a[@href='#edit']").click() 41 | self.kolabWAPhelper.wait_loading(0.5) 42 | driver.find_element_by_xpath("//tr[@id='type_attr_form']//select[@name='attr_value']/option[@value='auto']").click() 43 | driver.find_element_by_xpath("//input[@value='Save']").click() 44 | 45 | elem = driver.find_element_by_xpath("//input[@value=\"Submit\"]").click() 46 | self.kolabWAPhelper.wait_loading() 47 | elem = driver.find_element_by_xpath("//div[@id=\"message\"]") 48 | self.assertEquals("Object type updated successfully.", elem.text, "object type was not updated successfully, message: " + elem.text) 49 | 50 | def test_unique_id_across_domains(self): 51 | kolabWAPhelper = self.kolabWAPhelper 52 | kolabWAPhelper.log("Running test: test_unique_id_across_domains") 53 | 54 | # login Directory Manager 55 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 56 | 57 | # create a user in the primary domain 58 | username = "user" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + "uid" 59 | self.enable_editable_uid() 60 | username, emailLogin, password, uid = kolabWAPhelper.create_user(username=username, uid=username) 61 | 62 | # create a domain and select it 63 | domainname = kolabWAPhelper.create_domain() 64 | 65 | # attempt to create a user with the same username as in the primary domain, should result in a uid with digit 2 attached 66 | username, emailLogin, password, uid = kolabWAPhelper.create_user(username=username) 67 | self.assertEquals(username + "2", uid, "generate_uid should create a unique id across domains for same surname, expected " + username + "2, but got: " + uid) 68 | 69 | # attempt to create a user with the same uid as in the primary domain 70 | kolabWAPhelper.create_user(username=username, uid=username, expected_message_contains=("Error: The unique identity (UID) " + username + " is already in use.")) 71 | 72 | # create a new user 73 | username = "user" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") 74 | username, emailLogin, password, uid = kolabWAPhelper.create_user(username=username) 75 | kolabWAPhelper.logout_kolab_wap() 76 | 77 | # test kolab-saslauthd from the commandline 78 | print("testsaslauthd with " + emailLogin) 79 | p = subprocess.Popen("/usr/sbin/testsaslauthd -u " + emailLogin + " -p '" + password + "'", shell=True, stdout=subprocess.PIPE) 80 | out, err = p.communicate() 81 | self.assertTrue('0: OK "Success."' in out, "login did not work, it shows " + out) 82 | 83 | print("testsaslauthd with " + username) 84 | p = subprocess.Popen("/usr/sbin/testsaslauthd -u " + username + " -p '" + password + "'", shell=True, stdout=subprocess.PIPE) 85 | out, err = p.communicate() 86 | self.assertTrue('0: OK "Success."' in out, "login did not work, it shows " + out) 87 | 88 | print("testsaslauthd with wrong password") 89 | p = subprocess.Popen("/usr/sbin/testsaslauthd -u " + username + " -p '" + password + "fail'", shell=True, stdout=subprocess.PIPE) 90 | out, err = p.communicate() 91 | self.assertTrue('0: NO "authentication failed"' in out, "login should not work with wrong password, but it shows " + out) 92 | 93 | # login user to kolab webadmin with wrong password should fail 94 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", username, password+"fail", "Incorrect username or password!") 95 | 96 | # login user to kolab webadmin with the email login 97 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", emailLogin, password) 98 | kolabWAPhelper.logout_kolab_wap() 99 | 100 | # login user to kolab webadmin with just the uid 101 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", username, password) 102 | kolabWAPhelper.logout_kolab_wap() 103 | 104 | def tearDown(self): 105 | self.kolabWAPhelper.tear_down() 106 | 107 | if __name__ == "__main__": 108 | unittest.main() 109 | 110 | 111 | -------------------------------------------------------------------------------- /pySeleniumTests/testListUsersQuota.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.keys import Keys 6 | from helperKolabWAP import KolabWAPTestHelpers 7 | 8 | # assumes password for cn=Directory Manager is test. 9 | # assumes that the initTBitsISP.sh script has been run. 10 | # will create a new domain admin, a normal user without kolab-admin role, and a user with kolab-admin role. 11 | # will check if those users can see the report 12 | class KolabWAPListQuotaReport(unittest.TestCase): 13 | 14 | def setUp(self): 15 | self.kolabWAPhelper = KolabWAPTestHelpers() 16 | self.driver = self.kolabWAPhelper.init_driver() 17 | 18 | # test that unprivileged user cannot see the report 19 | def test_unprivileged_user(self): 20 | kolabWAPhelper = self.kolabWAPhelper 21 | kolabWAPhelper.log("Running test: test_unprivileged_user") 22 | driver = self.driver 23 | 24 | # login Directory Manager 25 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 26 | 27 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 28 | prefix = "user") 29 | 30 | kolabWAPhelper.logout_kolab_wap() 31 | 32 | # login as that user 33 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", uid, password) 34 | 35 | # check if report is available 36 | driver.find_element_by_link_text("Users").click() 37 | kolabWAPhelper.wait_loading(1) 38 | self.assertEquals(-1, driver.page_source.find('href="#userreport"'), "should not display link to User Report") 39 | self.assertEquals(-1, driver.page_source.find('href="#adminreport"'), "should not display link to Admin Report") 40 | 41 | # should get empty page when going to report directly 42 | # execute_script does not work: JavascriptException: Message: TypeError: 'blur' called on an object that does not implement interface Window. 43 | #driver.execute_script("return kadm.command('user.userreport', '', this)"); 44 | #kolabWAPhelper.wait_loading(1) 45 | # TODO currently this is still possible. but the user can see quota and last login anyway of other users at the moment 46 | #self.assertEquals(-1, driver.page_source.find('Quota Usage'), "should not display the Report"); 47 | 48 | kolabWAPhelper.logout_kolab_wap() 49 | 50 | def test_user_admin_role(self): 51 | kolabWAPhelper = self.kolabWAPhelper 52 | kolabWAPhelper.log("Running test: test_user_admin_role") 53 | driver = self.driver 54 | 55 | # login Directory Manager 56 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 57 | 58 | username, emailLogin, password, uid = kolabWAPhelper.create_user( 59 | prefix = "user", role = "kolab-admin") 60 | 61 | kolabWAPhelper.logout_kolab_wap() 62 | 63 | # login as that user 64 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", uid, password) 65 | 66 | # check if report is available 67 | driver.find_element_by_link_text("Users").click() 68 | kolabWAPhelper.wait_loading(1) 69 | self.assertNotEquals(-1, driver.page_source.find('href="#userreport"'), "should display link to User Report") 70 | self.assertNotEquals(-1, driver.page_source.find('href="#adminreport"'), "should display link to Admin Report") 71 | 72 | # should see the report 73 | driver.find_element_by_xpath("//li[@class=\"userreport\"]/a").click() 74 | kolabWAPhelper.wait_loading(1) 75 | self.assertNotEquals(-1, driver.page_source.find('Quota Usage'), "should display the Report"); 76 | 77 | kolabWAPhelper.logout_kolab_wap() 78 | 79 | def test_domain_admin(self): 80 | kolabWAPhelper = self.kolabWAPhelper 81 | kolabWAPhelper.log("Running test: test_domain_admin") 82 | driver = self.driver 83 | 84 | # login Directory Manager 85 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 86 | 87 | username, emailLogin, password, domainname = kolabWAPhelper.create_domainadmin() 88 | 89 | kolabWAPhelper.logout_kolab_wap() 90 | 91 | # login as that user 92 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", username, password) 93 | 94 | # check if report is available 95 | driver.find_element_by_link_text("Users").click() 96 | kolabWAPhelper.wait_loading(1) 97 | self.assertNotEquals(-1, driver.page_source.find('href="#userreport"'), "should display link to User Report") 98 | self.assertNotEquals(-1, driver.page_source.find('href="#adminreport"'), "should display link to Admin Report") 99 | 100 | # should see the report 101 | driver.find_element_by_xpath("//li[@class=\"userreport\"]/a").click() 102 | kolabWAPhelper.wait_loading(1) 103 | self.assertNotEquals(-1, driver.page_source.find('Quota Usage'), "should display the Report"); 104 | 105 | kolabWAPhelper.logout_kolab_wap() 106 | 107 | def test_directory_manager(self): 108 | kolabWAPhelper = self.kolabWAPhelper 109 | kolabWAPhelper.log("Running test: test_directory_manager") 110 | driver = self.driver 111 | 112 | # login Directory Manager 113 | kolabWAPhelper.login_kolab_wap("/kolab-webadmin", "cn=Directory Manager", "test") 114 | 115 | # check if report is available 116 | driver.find_element_by_link_text("Users").click() 117 | kolabWAPhelper.wait_loading(1) 118 | self.assertNotEquals(-1, driver.page_source.find('href="#userreport"'), "should display link to User Report") 119 | self.assertNotEquals(-1, driver.page_source.find('href="#adminreport"'), "should display link to Admin Report") 120 | 121 | # should see the report 122 | driver.find_element_by_xpath("//li[@class=\"userreport\"]/a").click() 123 | kolabWAPhelper.wait_loading(1) 124 | self.assertNotEquals(-1, driver.page_source.find('Quota Usage'), "should display the Report"); 125 | 126 | kolabWAPhelper.logout_kolab_wap() 127 | 128 | def tearDown(self): 129 | 130 | self.kolabWAPhelper.tear_down() 131 | 132 | if __name__ == "__main__": 133 | unittest.main() 134 | 135 | 136 | -------------------------------------------------------------------------------- /kolab/initTBitsISP.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ##################################################################################### 11 | # apply a couple of patches, see related kolab bugzilla number in filename, eg. https://issues.kolab.org/show_bug.cgi?id=2018 12 | ##################################################################################### 13 | 14 | if [ -z $APPLYPATCHES ] 15 | then 16 | APPLYPATCHES=1 17 | fi 18 | 19 | if [ $APPLYPATCHES -eq 1 ] 20 | then 21 | 22 | echo "applying patchMultiDomainAdminsBug2018.patch" 23 | patch -p1 --fuzz=0 -i `pwd`/patches/patchMultiDomainAdminsBug2018.patch -d /usr/share/kolab-webadmin || exit -1 24 | echo "applying domainquotaBug2046.patch" 25 | patch -p1 --fuzz=0 -i `pwd`/patches/domainquotaBug2046.patch -d /usr/share/kolab-webadmin || exit -1 26 | echo "applying domainAdminDefaultQuota.patch" 27 | patch -p1 --fuzz=0 -i `pwd`/patches/domainAdminDefaultQuota.patch -d /usr/share/kolab-webadmin || exit -1 28 | echo "applying domainAdminMaxAccounts.patch" 29 | patch -p1 --fuzz=0 -i `pwd`/patches/domainAdminMaxAccounts.patch -d /usr/share/kolab-webadmin || exit -1 30 | echo "applying lastLoginTBitsAttribute patch" 31 | patch -p1 --fuzz=0 -i `pwd`/patches/lastLoginTBitsAttribute-wap.patch -d /usr/share/kolab-webadmin || exit -1 32 | patch -p1 --fuzz=0 -i `pwd`/patches/lastLoginTBitsAttribute-pykolab.patch -d $pythonDistPackages || exit -1 33 | echo "applying allowPrimaryEmailAddressFromDomain.patch" 34 | patch -p1 --fuzz=0 -i `pwd`/patches/allowPrimaryEmailAddressFromDomain.patch -d $pythonDistPackages || exit -1 35 | echo "applying quotaused_wap.patch" 36 | patch -p1 --fuzz=0 -i `pwd`/patches/quotaused_wap.patch -d /usr/share/kolab-webadmin || exit -1 37 | echo "applying listUsersLastLoginQuotaUsage.patch" 38 | patch -p1 --fuzz=0 -i `pwd`/patches/listUsersLastLoginQuotaUsage.patch -d /usr/share/kolab-webadmin || exit -1 39 | echo "applying logLoginData.patch" 40 | patch -p1 --fuzz=0 -i `pwd`/patches/logLoginData.patch -d $pythonDistPackages || exit -1 41 | echo "applying optional_disable_addressbook_export.patch" 42 | patch -p1 --fuzz=0 -i `pwd`/patches/optional_disable_addressbook_export.patch -d /usr/share/roundcubemail || exit -1 43 | echo "applying wap_api_listuserswithhash.patch" 44 | patch -p1 --fuzz=0 -i `pwd`/patches/wap_api_listuserswithhash.patch -d /usr/share/kolab-webadmin || exit -1 45 | echo "applying intranetToken-wap.patch" 46 | patch -p1 --fuzz=0 -i `pwd`/patches/intranetToken-wap.patch -d /usr/share/kolab-webadmin || exit -1 47 | echo "applying wap_disallow_users.patch" 48 | patch -p1 --fuzz=0 -i `pwd`/patches/wap_disallow_users.patch -d /usr/share/kolab-webadmin || exit -1 49 | fi 50 | 51 | ##################################################################################### 52 | #using specific ldap attribute for the domainadmin overall quota 53 | ##################################################################################### 54 | sed -r -i -e "s/\[kolab\]/[kolab]\ndomainadmin_quota_attribute = tbitskolaboverallquota/g" /etc/kolab/kolab.conf 55 | 56 | 57 | ##################################################################################### 58 | #NOT enable storing the username and password 59 | ##################################################################################### 60 | sed -r -i -e "s#\[kolab\]#[kolab]\nstoreloginpwd = False\nstoreloginpwd.file = /var/log/kolab/logindata.log#g" /etc/kolab/kolab.conf 61 | 62 | 63 | ##################################################################################### 64 | #enable storing the last login time for each user 65 | ##################################################################################### 66 | sed -r -i -e "s/\[ldap\]/[ldap]\nsetlastlogin = True/g" /etc/kolab/kolab.conf 67 | 68 | ##################################################################################### 69 | # enable access to the WAP API only through localhost and a proxy 70 | ##################################################################################### 71 | sed -r -i -e 's#\[kolab\]#[kolab]\nwap_api_userslist_allowip = ["127.0.0.1", "10.0.3.21" ]#g' /etc/kolab/kolab.conf 72 | 73 | ##################################################################################### 74 | #disable LDAP debugging 75 | ##################################################################################### 76 | sed -r -i -e 's/config_set\("debug", true\)/config_set("debug", false)/g' /usr/share/kolab-webadmin/lib/Auth/LDAP.php 77 | 78 | 79 | ##################################################################################### 80 | #extend the LDAP schema for TBits ISP patches 81 | ##################################################################################### 82 | for d in /etc/dirsrv/slapd* 83 | do 84 | cp patches/99tbits.ldif $d/schema/ 85 | done 86 | 87 | if [ -f /bin/systemctl ] 88 | then 89 | /bin/systemctl restart dirsrv.target && sleep 10 90 | else 91 | # wait a few seconds, on Debian we need to wait for dirsrv to restart 92 | service dirsrv stop && sleep 10 && service dirsrv start && sleep 10 93 | fi 94 | 95 | # need to delete the LDAP cache file. it lives in a private tmp directory of httpd 96 | for d in /tmp/systemd-private-*-httpd.service* 97 | do 98 | if [ -d $d ] 99 | then 100 | rm -f $d/tmp/*Net_LDAP2_Schema.cache 101 | fi 102 | done 103 | rm -f /tmp/ldap\:localhost\:389-Net_LDAP2_Schema.cache 104 | 105 | # need to delete the memcache for effectiveRights 106 | echo 'flush_all' | nc localhost 11211 || exit -1 107 | 108 | ##################################################################################### 109 | #add tbitsKolabUser objectclass to Kolab user, for last login time and the DomainAdmin attributes 110 | ##################################################################################### 111 | php initTBitsUserTypes.php 112 | 113 | if [ -f /bin/systemctl -a -f /etc/debian_version ] 114 | then 115 | /bin/systemctl restart kolab-saslauthd 116 | /bin/systemctl restart kolab-server 117 | elif [ -f /bin/systemctl ] 118 | then 119 | /bin/systemctl restart kolab-saslauthd 120 | /bin/systemctl restart kolabd.service 121 | elif [ -f /sbin/service ] 122 | then 123 | service kolab-saslauthd restart 124 | service kolabd restart 125 | elif [ -f /usr/sbin/service ] 126 | then 127 | service kolab-saslauthd restart 128 | service kolab-server restart 129 | fi 130 | -------------------------------------------------------------------------------- /kolab/patches/domainquotaBug2046.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/Auth.php b/lib/Auth.php 2 | index 1146b8d..b42b40c 100644 3 | --- a/lib/Auth.php 4 | +++ b/lib/Auth.php 5 | @@ -233,6 +233,11 @@ class Auth { 6 | return $this->auth_instance()->domainadmin_get_configuration($domain, $variablename); 7 | } 8 | 9 | + public function domainadmin_get_user_quota($domainadmin, $excludeuser) 10 | + { 11 | + return $this->auth_instance()->domainadmin_get_user_quota($domainadmin, $excludeuser); 12 | + } 13 | + 14 | public function find_recipient($address) 15 | { 16 | return $this->auth_instance()->find_recipient($address); 17 | diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php 18 | index 96d7481..2897399 100644 19 | --- a/lib/Auth/LDAP.php 20 | +++ b/lib/Auth/LDAP.php 21 | @@ -412,6 +412,30 @@ class LDAP extends Net_LDAP3 { 22 | return $domain_dn; 23 | } 24 | 25 | + // get the quota that has already been shared among the users of the domains that this domainadmin manages. 26 | + // excluding the current quota for the specified user, that we want to change the quota for 27 | + public function domainadmin_get_user_quota($domainadmin, $excludeuser) 28 | + { 29 | + $quota = 0; 30 | + $domains = $this->domainadmin_get_domains($domainadmin); 31 | + foreach ($domains as $domain) { 32 | + // get all users that are part of this domain 33 | + $users_result = $this->search( 34 | + $this->_standard_root_dn($domain), 35 | + "objectclass=kolabinetorgperson"); 36 | + if ($users_result != null && count($users_result) > 0) { 37 | + $users = $users_result->entries(true); 38 | + foreach ($users as $uid => $user) { 39 | + if (strtolower($uid) != strtolower($excludeuser)) { 40 | + $quota += $user[$this->conf->get("quota_attribute")]; 41 | + } 42 | + } 43 | + } 44 | + } 45 | + 46 | + return $quota; 47 | + } 48 | + 49 | public function domain_edit($domain, $attributes, $typeid = null) 50 | { 51 | $domain = $this->domain_info($domain, array_keys($attributes)); 52 | diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php 53 | index 48e017d..b5c6808 100644 54 | --- a/lib/api/kolab_api_service_form_value.php 55 | +++ b/lib/api/kolab_api_service_form_value.php 56 | @@ -1398,7 +1398,71 @@ class kolab_api_service_form_value extends kolab_api_service 57 | 58 | private function validate_mailquota($value, $postdata = array(), $validation_type = null) 59 | { 60 | - return $this->validate_quota($value, $postdata, $validation_type); 61 | + $value = $this->validate_quota($value, $postdata, $validation_type); 62 | + 63 | + if (empty($value)) { 64 | + $value = 0; 65 | + } 66 | + 67 | + if (!is_numeric($value)) { 68 | + throw new Exception('Invalid value for mail quota. Please clear or enter a valid integer number!'); 69 | + } 70 | + 71 | + $value = intval($value); 72 | + 73 | + $conf = Conf::get_instance(); 74 | + $quota_attribute = $conf->get('domainadmin_quota_attribute'); 75 | + if ($quota_attribute == null || strlen($quota_attribute) == 0) { 76 | + $quota_attribute = $conf->get('quota_attribute'); 77 | + } 78 | + // check domain admin quota 79 | + 80 | + $auth = Auth::get_instance(); 81 | + 82 | + // get the mailquota of the domain admin for the current domain 83 | + $result = $auth->domainadmin_get_configuration($_SESSION['user']->get_domain(), $quota_attribute); 84 | + 85 | + if (isset($result)) { 86 | + $domainadminquota = $result[$quota_attribute]; 87 | + 88 | + if ($value == 0 && $domainadminquota != 0) { 89 | + throw new Exception('error: You must specify a mailquota for the user.
'. 90 | + 'An unlimited mailquota is not permitted for your user because you have a limited overall mailquota.'); 91 | + } 92 | + 93 | + $domainadmin = $result['domainadmin']; 94 | + // get all quotas from all users of that domain admin, excluding this user 95 | + $quota_used = $auth->domainadmin_get_user_quota($domainadmin, 'uid='.$postdata['uid'].','.$postdata['ou']); 96 | + 97 | + // check if existing quota plus this new quota would still fit the quota of the domain admin 98 | + if ($quota_used + $value > $domainadminquota) { 99 | + $available = $domainadminquota - $quota_used; 100 | + $domainadminquotaunit = "KB"; 101 | + if ($domainadminquota > 1024) { 102 | + $domainadminquota = $domainadminquota / 1024; 103 | + $domainadminquotaunit = "MB"; 104 | + } 105 | + if ($domainadminquota > 1024) { 106 | + $domainadminquota = $domainadminquota / 1024; 107 | + $domainadminquotaunit = "GB"; 108 | + } 109 | + $availableunit = "KB"; 110 | + if ($available > 1024) { 111 | + $available = $available / 1024; 112 | + $availableunit = "MB"; 113 | + } 114 | + if ($available > 1024) { 115 | + $available = $available / 1024; 116 | + $availableunit = "GB"; 117 | + } 118 | + 119 | + throw new Exception('error: mailquota of the domain admin has been exceeded.
'. 120 | + 'max available: '.$domainadminquota.' '.$domainadminquotaunit.';
'. 121 | + 'max available for this user: '.$available.' '.$availableunit); 122 | + } 123 | + } 124 | + 125 | + return (string) intval($value); 126 | } 127 | 128 | private function validate_tbitskolaboverallquota($value, $postdata = array(), $validation_type = null) 129 | diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js 130 | index fcc962c..32186ea 100644 131 | --- a/public_html/js/kolab_admin.js 132 | +++ b/public_html/js/kolab_admin.js 133 | @@ -842,6 +842,8 @@ function kolab_admin() 134 | var unit = $('select[name="' + this.name + '-unit"]').val(); 135 | if (unit && this.value) 136 | data.json[this.name] = this.value + unit; 137 | + if (!this.value) 138 | + data.json[this.name] = "0kb"; 139 | delete data.json[this.name + '-unit']; 140 | }); 141 | 142 | -------------------------------------------------------------------------------- /kolab/patches/lastLoginTBitsAttribute-wap.patch: -------------------------------------------------------------------------------- 1 | diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php 2 | index 32cba82..17e8f3c 100644 3 | --- a/lib/Auth/LDAP.php 4 | +++ b/lib/Auth/LDAP.php 5 | @@ -267,6 +267,10 @@ class LDAP extends Net_LDAP3 { 6 | } 7 | $admin_readonly_attrs = array("tbitsKolabMaxAccounts", "tbitsKolabOverallQuota"); 8 | 9 | + if (in_array('tbitsKolabUser', $this->classes_allowed())) { 10 | + $self_attrs = array_merge($self_attrs, array('tbitsKolabLastLogin')); 11 | + } 12 | + 13 | $_domain = str_replace('.', '_', $domain); 14 | $dn = $inetdomainbasedn; 15 | $cn = str_replace(array(',', '='), array('\2C', '\3D'), $dn); 16 | diff --git a/lib/client/kolab_client_task_settings.php b/lib/client/kolab_client_task_settings.php 17 | index af544be..f0c5a38 100644 18 | --- a/lib/client/kolab_client_task_settings.php 19 | +++ b/lib/client/kolab_client_task_settings.php 20 | @@ -33,6 +33,7 @@ class kolab_client_task_settings extends kolab_client_task 21 | 22 | protected $form_element_types = array( 23 | 'text', 'text-separated', 'text-quota', 'text-autocomplete', 24 | + 'text-unixtimestamp', 25 | 'select', 'multiselect', 26 | 'list', 'list-autocomplete', 'checkbox', 'password', 'ldap_url', 27 | 'aci', 'imap_acl', 28 | diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php 29 | index 39d8557..0cf2002 100644 30 | --- a/lib/client/kolab_client_task_user.php 31 | +++ b/lib/client/kolab_client_task_user.php 32 | @@ -89,6 +89,7 @@ class kolab_client_task_user extends kolab_client_task 33 | 'system' => 'user.system', 34 | 'config' => 'user.config', 35 | 'domainadmin' => 'user.domainadmin', 36 | + 'statistics' => 'user.statistics', 37 | 'asterisk' => 'user.asterisk', 38 | 'other' => 'user.other', 39 | ); 40 | @@ -128,6 +129,8 @@ class kolab_client_task_user extends kolab_client_task 41 | 'alias' => 'contact_info', 42 | 'mailalternateaddress' => 'contact_info', 43 | 44 | + 'tbitskolablastlogin' => 'statistics', 45 | + 46 | /* POSIX Attributes first */ 47 | 'uid' => 'system', 48 | 'userpassword' => 'system', 49 | diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php 50 | index 009e1c8..48389eb 100644 51 | --- a/lib/kolab_client_task.php 52 | +++ b/lib/kolab_client_task.php 53 | @@ -921,6 +921,10 @@ class kolab_client_task 54 | } 55 | break; 56 | 57 | + case 'text-unixtimestamp': 58 | + $result['type'] = kolab_form::INPUT_TEXTUNIXTIMESTAMP; 59 | + break; 60 | + 61 | case 'text-quota': 62 | $result['type'] = kolab_form::INPUT_TEXTQUOTA; 63 | $result['default'] = $field['default']; 64 | diff --git a/lib/kolab_form.php b/lib/kolab_form.php 65 | index 5d8b99a..2bd6ec2 100644 66 | --- a/lib/kolab_form.php 67 | +++ b/lib/kolab_form.php 68 | @@ -39,6 +39,7 @@ class kolab_form 69 | const INPUT_CUSTOM = 10; 70 | const INPUT_CONTENT = 20; 71 | const INPUT_TEXTQUOTA = 30; 72 | + const INPUT_TEXTUNIXTIMESTAMP = 40; 73 | 74 | private $attribs = array(); 75 | private $elements = array(); 76 | @@ -297,6 +298,11 @@ class kolab_form 77 | $content = kolab_html::inputquota($attribs); 78 | break; 79 | 80 | + case self::INPUT_TEXTUNIXTIMESTAMP: 81 | + $attribs['type'] = 'text'; 82 | + $content = kolab_html::inputunixtimestamp($attribs); 83 | + break; 84 | + 85 | case self::INPUT_CHECKBOX: 86 | $attribs['type'] = 'checkbox'; 87 | $content = kolab_html::input($attribs); 88 | diff --git a/lib/kolab_html.php b/lib/kolab_html.php 89 | index a2088a1..1255870 100644 90 | --- a/lib/kolab_html.php 91 | +++ b/lib/kolab_html.php 92 | @@ -228,6 +228,28 @@ class kolab_html 93 | } 94 | 95 | /** 96 | + * Readonly control that will display the time encoded as a unix timestamp. Used for displaying tbitsKolabLastLogin 97 | + * 98 | + * @param array $attribs Element attributes 99 | + * 100 | + * @return string HTML output of the timestamp 101 | + */ 102 | + public static function inputunixtimestamp($attribs = array()) 103 | + { 104 | + $attribs['type'] = 'hidden'; 105 | + $hidden_input = self::input($attribs); 106 | + unset($attribs['type']); 107 | + $attribs['name'] .= "_display"; 108 | + $attribs['readonly'] = true; 109 | + $attribs['disabled'] = true; 110 | + if (!empty($attribs['value'])) { 111 | + $attribs['value'] = date('d.m.Y H:i:s e', $attribs['value']); 112 | + } 113 | + $readonly_input = self::input($attribs); 114 | + return $hidden_input.$readonly_input; 115 | + } 116 | + 117 | + /** 118 | * Textarea element. 119 | * 120 | * @param array $attribs Element attributes 121 | diff --git a/lib/locale/de_DE.php b/lib/locale/de_DE.php 122 | index 623a84d..a1e77fa 100644 123 | --- a/lib/locale/de_DE.php 124 | +++ b/lib/locale/de_DE.php 125 | @@ -456,4 +456,6 @@ $LANG['domain.domainadmin'] = 'Administratoren für diese Domain'; 126 | $LANG['user.tbitskolabmaxaccounts'] = 'Maximale Anzahl von Benutzerkonten'; 127 | $LANG['user.tbitskolaboverallquota'] = 'Gesamtquota verfügbar'; 128 | $LANG['user.tbitskolabdefaultquota'] = 'Voreinstellung Quota für Benutzerkonten'; 129 | +$LANG['user.statistics'] = 'Info'; 130 | +$LANG['user.tbitskolablastlogin'] = 'Letzte erfolgreiche Anmeldung'; 131 | 132 | diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php 133 | index f9c204c..9baa7cf 100644 134 | --- a/lib/locale/en_US.php 135 | +++ b/lib/locale/en_US.php 136 | @@ -437,6 +437,7 @@ $LANG['user.postcode'] = 'Postal code'; 137 | $LANG['user.preferredlanguage'] = 'Native tongue'; 138 | $LANG['user.room'] = 'Room number'; 139 | $LANG['user.sn'] = 'Surname'; 140 | +$LANG['user.statistics'] = 'Info'; 141 | $LANG['user.street'] = 'Street'; 142 | $LANG['user.system'] = 'System'; 143 | $LANG['user.telephonenumber'] = 'Phone Number'; 144 | @@ -444,6 +445,7 @@ $LANG['user.telephonenumber'] = 'Phone Number'; 145 | $LANG['user.tbitskolabmaxaccounts'] = 'Maximum number of accounts'; 146 | $LANG['user.tbitskolaboverallquota'] = 'Overall Quota assigned'; 147 | $LANG['user.tbitskolabdefaultquota'] = 'Default Quota for user accounts'; 148 | +$LANG['user.tbitskolablastlogin'] = 'Latest successful login'; 149 | $LANG['user.title'] = 'Job Title'; 150 | $LANG['user.type_id'] = 'Account type'; 151 | $LANG['user.uid'] = 'Unique identity (UID)'; 152 | -------------------------------------------------------------------------------- /kolab/initTBitsCustomizationsDE.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ##################################################################################### 11 | # adjust some settings, that might be specific to TBits 12 | ##################################################################################### 13 | service kolabd stop 14 | service kolab-saslauthd stop 15 | 16 | # add admin_auto_fields_rw = true to kolab_wap section of kolab.conf 17 | sed -r -i -e "s#\[kolab_wap\]#[kolab_wap]\nadmin_auto_fields_rw = true#g" /etc/kolab/kolab.conf 18 | 19 | # add enable_intranet_token to kolab section of kolab.conf 20 | sed -r -i -e "s#\[kolab\]#[kolab]\nenable_intranet_token = test.tbits.net,customerxyz.de#g" /etc/kolab/kolab.conf 21 | 22 | # change default locale 23 | sed -r -i -e "s#default_locale = en_US#default_locale = de_DE#g" /etc/kolab/kolab.conf 24 | 25 | # set in kolab.conf, ldap section: modifytimestamp_format = %%Y%%m%%d%%H%%M%%SZ, to avoid warning on console 26 | sed -r -i -e "s#\[ldap\]#[ldap]\nmodifytimestamp_format = %%Y%%m%%d%%H%%M%%SZ#g" /etc/kolab/kolab.conf 27 | 28 | # do not add secondary emails by default 29 | sed -r -i -e "s#autocreate_folders#secondary_mail = { }\nautocreate_folders#g" /etc/kolab/kolab.conf 30 | 31 | # set in /etc/sysconfig/dirsrv: ulimit -n 32192, to avoid dirsrv crashing because of too many open files 32 | sed -r -i -e "s/# ulimit -n 8192/ulimit -n 32192/g" /etc/sysconfig/dirsrv 33 | sed -r -i -e "s/#ulimit -n 8192/ulimit -n 32192/g" /etc/sysconfig/dirsrv 34 | if [ -f /bin/systemctl ] 35 | then 36 | /bin/systemctl restart dirsrv.target && sleep 10 37 | else 38 | service dirsrv restart 39 | fi 40 | 41 | # disable debug mode in LDAP.php to avoid too much output 42 | sed -r -i -e 's/config_set\("debug", true\)/config_set("debug", false)/g' /usr/share/kolab-webadmin/lib/Auth/LDAP.php 43 | # disable debug modes for roundcube; otherwise /var/log/roundcubemail/imap can get really big! 44 | sed -r -i -e "s/_debug'] = true/_debug'] = false/g" /etc/roundcubemail/config.inc.php 45 | 46 | # change order of addressbooks: first personal address book 47 | sed -r -i -e "s# = 0;# = 1;#g" /etc/roundcubemail/kolab_addressbook.inc.php 48 | 49 | # change default names for calendar and address book 50 | sed -r -i -e "s#Calendar#Kalender#g" /etc/roundcubemail/kolab_folders.inc.php 51 | sed -r -i -e "s#Contacts#Kontakte#g" /etc/roundcubemail/kolab_folders.inc.php 52 | sed -r -i -e "s#Sent#Gesendet#g" /etc/roundcubemail/kolab_folders.inc.php 53 | sed -r -i -e "s#Drafts#Entwürfe#g" /etc/roundcubemail/kolab_folders.inc.php 54 | sed -r -i -e "s#Trash#Papierkorb#g" /etc/roundcubemail/kolab_folders.inc.php 55 | #sed -r -i -e "s#Spam#Spam#g" /etc/roundcubemail/kolab_folders.inc.php 56 | sed -r -i -e "s#kolab_folders_task_default'\] = ''#kolab_folders_task_default'] = 'Aufgaben'#g" /etc/roundcubemail/kolab_folders.inc.php 57 | sed -r -i -e "s#kolab_folders_note_default'\] = ''#kolab_folders_note_default'] = 'Notizen'#g" /etc/roundcubemail/kolab_folders.inc.php 58 | #sed -r -i -e "s#kolab_folders_journal_default'\] = ''#kolab_folders_journal_default'] = 'Journal'#g" /etc/roundcubemail/kolab_folders.inc.php 59 | 60 | sed -r -i -e "s#'Calendar'#'Kalender'#g" /etc/kolab/kolab.conf 61 | sed -r -i -e "s#'Contacts'#'Kontakte'#g" /etc/kolab/kolab.conf 62 | sed -r -i -e "s#'Sent'#'Gesendet'#g" /etc/kolab/kolab.conf 63 | sed -r -i -e "s#'Drafts'#'Entwürfe'#g" /etc/kolab/kolab.conf 64 | sed -r -i -e "s#'Trash'#'Papierkorb'#g" /etc/kolab/kolab.conf 65 | sed -r -i -e "s#'Tasks'#'Aufgaben'#g" /etc/kolab/kolab.conf 66 | sed -r -i -e "s#'Notes'#'Notizen'#g" /etc/kolab/kolab.conf 67 | 68 | # change default language 69 | sed -r -i -e "s#// Re-apply mandatory settings here.#// Re-apply mandatory settings here.\n \$config['locale_string'] = 'de';#g" /etc/roundcubemail/config.inc.php 70 | 71 | # don't allow the user to change the skin 72 | sed -r -i -e "s#// Re-apply mandatory settings here.#// Re-apply mandatory settings here.\n \$config['dont_override'] = 'skin';#g" /etc/roundcubemail/config.inc.php 73 | 74 | # make Kalender and Kontakte default folders in roundcube, so that they get subscribed automatically 75 | #sed -r -i -e "s#'INBOX', 'Drafts', 'Sent', 'Spam', 'Trash'#'INBOX', 'Drafts', 'Sent', 'Spam', 'Trash', 'Kalender', 'Kontakte'#g" /etc/roundcubemail/config.inc.php 76 | # enable plugin subscriptions_options 77 | #sed -r -i -e "s#'redundant_attachments',#'redundant_attachments',\n 'subscriptions_option',#g" /etc/roundcubemail/config.inc.php 78 | #sed -r -i -e "s#// Re-apply mandatory settings here.#// Re-apply mandatory settings here.\n \$config['use_subscriptions'] = false;#g" /etc/roundcubemail/config.inc.php 79 | 80 | # disable files component for all users 81 | # sed -r -i -e "s/'kolab_files',/#'kolab_files',/g" /etc/roundcubemail/config.inc.php 82 | 83 | # change default sorting order in Roundcube Mail list: sort by emails on arrival time 84 | sed -r -i -e "s/config\['message_sort_col'\] = 'date'/config['message_sort_col'] = 'arrival'/g" /etc/roundcubemail/config.inc.php 85 | 86 | # remove personal calender from kolab.conf 87 | rm -f /etc/kolab/kolab.conf.new 88 | skip=0 89 | # set internal file separator so that the leading spaces are not trimmed 90 | OIFS=$IFS 91 | IFS= 92 | while read line 93 | do 94 | if [[ $skip -gt 0 ]] 95 | then 96 | skip=$((skip-1)) 97 | #echo $line 98 | continue; 99 | fi 100 | test=`echo $line | grep -e "Calendar/Personal Calendar" -e "Contacts/Personal Contacts" ` 101 | if [[ ${#test} -gt 0 ]] 102 | then 103 | #echo $test; 104 | skip=4 105 | continue; 106 | fi 107 | echo $line >> /etc/kolab/kolab.conf.new 108 | done < /etc/kolab/kolab.conf 109 | IFS=$OIFS 110 | mv /etc/kolab/kolab.conf.new /etc/kolab/kolab.conf 111 | 112 | # enable password complexity policy 113 | sed -r -i -e 's#\[kolab\]#[kolab]\npassword_policy = {"minLength" : 8, "minUpper" : 1, "minLower" : 1, "minNumeric" : 1, "minSpecial" : 1, "specialChars" : "<>\#\$\&%!?.,;*/+-=[]{}()"}#g' /etc/kolab/kolab.conf 114 | sed -r -i -e "s#config\['password_confirm_current'\].*#config['password_confirm_current'] = true;#g" /usr/share/roundcubemail/plugins/password/config.inc.php 115 | sed -r -i -e "s#config\['password_minimum_length'\].*#config['password_minimum_length'] = 10;#g" /usr/share/roundcubemail/plugins/password/config.inc.php 116 | sed -r -i -e "s#config\['password_require_nonalpha'\].*#config['password_require_nonalpha'] = true;#g" /usr/share/roundcubemail/plugins/password/config.inc.php 117 | 118 | if [ -z $APPLYPATCHES ] 119 | then 120 | APPLYPATCHES=1 121 | fi 122 | 123 | if [ $APPLYPATCHES -eq 1 ] 124 | then 125 | patch -p1 --fuzz=0 -i `pwd`/patches/onlyAllowKolabUsersToAuthViaSasl.patch -d $pythonDistPackages || exit -1 126 | fi 127 | 128 | service kolabd start 129 | service kolab-saslauthd start 130 | 131 | 132 | -------------------------------------------------------------------------------- /kolab/imapproxy/imapproxy.conf: -------------------------------------------------------------------------------- 1 | ## imapproxy.conf 2 | ## 3 | ## This is the global configuration file for SquirrelMail IMAP Proxy. 4 | ## Lines beginning with a '#' sign are treated as comments and will be 5 | ## ignored. Each line to be processed must be a space delimited 6 | ## keyword/value pair. 7 | ## 8 | 9 | # 10 | ## server_hostname 11 | ## 12 | ## This setting controls which IMAP server we proxy our connections to. 13 | # 14 | server_hostname localhost 15 | 16 | 17 | # 18 | ## connect_retries 19 | ## 20 | ## This setting controls how many times we retry connecting to our server. 21 | ## The delay between retries is configurable with 'connect_delay' 22 | # 23 | connect_retries 10 24 | connect_delay 5 25 | 26 | # 27 | ## cache_size 28 | ## 29 | ## This setting determines how many in-core IMAP connection structures 30 | ## will be allocated. As such, it determines not only how many cached 31 | ## connections will be allowed, but really the total number of simultaneous 32 | ## connections, cached and active. 33 | # 34 | cache_size 3072 35 | 36 | 37 | # 38 | ## listen_port 39 | ## 40 | ## This setting specifies which port the proxy server will bind to and 41 | ## accept incoming connections from. 42 | # 43 | listen_port 8143 44 | 45 | 46 | # 47 | ## listen_address 48 | ## 49 | ## This setting specifies which address the proxy server will bind to and 50 | ## accept incoming connections to. If undefined, bind to all. 51 | ## Must be a dotted decimal IP address. 52 | # 53 | listen_address 127.0.0.1 54 | 55 | 56 | # 57 | ## server_port 58 | ## 59 | ## This setting specifies the port that server_hostname is listening on. 60 | ## This is the tcp port that we proxy inbound connections to. 61 | ## 62 | ## If you are using SSL with IMAP Proxy, note that unless the server is 63 | ## highly non-standard, this should still be set to the server's normal, 64 | ## unencrypted IMAP port and should NOT be set to port 993, since IMAP 65 | ## Proxy uses STARTTLS to encrypt a "normal" IMAP connection. 66 | ## 67 | ## If the server is only available via (encrypted) port 993, please 68 | ## consult the README.ssl file for help. 69 | # 70 | server_port 143 71 | 72 | # 73 | ## cache_expiration_time 74 | ## 75 | ## This setting controls how many seconds an inactive connection will be 76 | ## cached. 77 | # 78 | cache_expiration_time 300 79 | 80 | 81 | # 82 | ## proc_username 83 | ## 84 | ## This setting controls which username the IMAP proxy process will run as. 85 | ## It is not allowed to run as "root". 86 | # 87 | proc_username nobody 88 | 89 | # 90 | ## proc_groupname 91 | ## 92 | ## This setting controls which groupname the IMAP proxy process will run as. 93 | # 94 | proc_groupname nobody 95 | 96 | 97 | # 98 | ## stat_filename 99 | ## 100 | ## This is the path to the filename that the proxy server mmap()s to 101 | ## write statistical data to. This is the file that pimpstat needs to 102 | ## look at to be able to provide his useful stats. 103 | # 104 | stat_filename /var/run/pimpstats 105 | 106 | 107 | # 108 | ## protocol_log_filename 109 | ## 110 | ## protocol logging may only be turned on for one user at a time. All 111 | ## protocol logging data is written to the file specified by this path. 112 | # 113 | protocol_log_filename /var/log/imapproxy_protocol.log 114 | 115 | 116 | # 117 | ## syslog_facility 118 | ## 119 | ## The logging facility to be used for all syslog calls. If nothing is 120 | ## specified here, it will default to LOG_MAIL. Any of the possible 121 | ## facilities listed in the syslog(3C) manpage may be used here except 122 | ## LOG_KERN. 123 | # 124 | syslog_facility LOG_MAIL 125 | 126 | 127 | # 128 | ## syslog_prioritymask 129 | ## 130 | ## This configuration option is provided as a way to limit the verbosity 131 | ## of squirrelmail-imap_proxy. If no value is specified, it will default 132 | ## to no priority mask and you'll see all possible log messages. Any of 133 | ## the possible priority values listed in the syslog(3C) manpage may be 134 | ## used here. By default, I've left this commented out so you will see 135 | ## all possible log messages. 136 | # 137 | #syslog_prioritymask LOG_WARNING 138 | 139 | 140 | # 141 | ## send_tcp_keepalives 142 | ## 143 | ## This determines whether the SO_KEEPALIVE option will be set on all 144 | ## sockets. 145 | # 146 | send_tcp_keepalives no 147 | 148 | 149 | # 150 | ## enable_select_cache 151 | ## 152 | ## This configuration option allows you to turn select caching on or off. 153 | ## When select caching is enabled, squirrelmail-imap_proxy will cache SELECT 154 | ## responses from an IMAP server. 155 | # 156 | enable_select_cache no 157 | 158 | 159 | # 160 | ## foreground_mode 161 | ## 162 | ## This will prevent squirrelmail-imap_proxy from detaching from his parent 163 | ## process and controlling terminal on startup. 164 | # 165 | foreground_mode no 166 | 167 | 168 | # 169 | ## force_tls 170 | ## 171 | ## Force squirrelmail-imap_proxy to use STARTTLS even if LOGIN is not disabled. 172 | # 173 | force_tls yes 174 | 175 | 176 | # 177 | ## chroot_directory 178 | ## 179 | ## This allows squirrelmail-imap_proxy to run in a chroot jail if desired. 180 | ## If commented out, squirrelmail-imap_proxy will not run chroot()ed. If 181 | ## a directory is specified here, squirrelmail-imap_proxy will chroot() to 182 | ## that directory. 183 | # 184 | #chroot_directory /var/empty 185 | 186 | 187 | # 188 | ## preauth_command 189 | ## 190 | ## Arbitrary command that can be sent to the server before 191 | ## authenticating users. This can be useful to access non- 192 | ## standard IMAP servers such as Yahoo!, which requires the 193 | ## following command to be sent before authentication is allowed: 194 | ## ID ("GUID" "1") 195 | ## (See: http://en.wikipedia.org/wiki/Yahoo!_Mail#Free_IMAP_and_SMTPs_access ) 196 | ## To use such a command, this setting should look like this: 197 | ## preauth_command ID ("GUID" "1") 198 | ## No matter what this command is, it is expected to return an 199 | ## OK response 200 | # 201 | #preauth_command 202 | 203 | 204 | # 205 | ## enable_admin_commands 206 | ## 207 | ## Used to enable or disable the internal squirrelmail-imap_proxy 208 | ## administrative commands. 209 | # 210 | enable_admin_commands no 211 | 212 | 213 | # 214 | ## Various path options for SSL CA certificates/directories 215 | # 216 | #tls_ca_file /usr/share/ssl/certs/ca-bundle.crt 217 | #tls_ca_path /usr/share/ssl/certs/ 218 | #tls_cert_file /usr/share/ssl/certs/mycert.crt 219 | #tls_key_file /usr/share/ssl/certs/mycert.key 220 | 221 | 222 | # 223 | ## Authenticate using SASL AUTHENTICATE PLAIN 224 | ## 225 | ## The following authentication username and password are used 226 | ## along with the username from the client as the authorization 227 | ## identity. In order to avoid having the service wide open (no 228 | ## password needed from the client), the client is required to 229 | ## send the auth_shared_secret in leiu of a user password. 230 | ## 231 | ## NOTE: This functionality *assumes* that the server supports 232 | ## AUTHENTICATE PLAIN, and it does *not* verify this by 233 | ## looking at the server's capabilities list. 234 | # 235 | #auth_sasl_plain_username 236 | #auth_sasl_plain_password 237 | #auth_shared_secret 238 | 239 | 240 | -------------------------------------------------------------------------------- /createreleases/mirror_kolab_development.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os.path 3 | import sys 4 | from collections import deque 5 | import urllib.request 6 | import re 7 | 8 | if len(sys.argv) != 3: 9 | print("Please specify which Kolab version you want to create eg. 2016.2 RC1 or 2016.2 final") 10 | sys.exit(-1) 11 | release=sys.argv[1] 12 | releasestate=sys.argv[2] 13 | rpmbuildpath="/root/rpmbuild" 14 | obsurl="http://obs.kolabsys.com/repositories/Kolab:/Development/CentOS_7/src" 15 | fedorapeoplepath="kolab/kolab-"+release+"/"+releasestate 16 | fedorapeopleurl="tpokorra@fedorapeople.org:public_html" 17 | pkgurl="https://tpokorra.fedorapeople.org/"+fedorapeoplepath 18 | srcrpmspath="/root/obs/kolab-development" 19 | modifiedsrcrpmspath="/root/obs/kolab-development" 20 | Debugging=False 21 | 22 | includePackages=[ 23 | # kolab packages 24 | "chwala", "iRony", "kolab*", "libcalendaring", "libkolab*", "pykolab", 25 | "roundcubemail-plugins-kolab", "roundcubemail-skin-chameleon", 26 | # additional packages 27 | "cyrus-imapd", "roundcubemail", "roundcubemail-plugin-*", 28 | # needed by roundcubemail 29 | "php-Net-LDAP3", "php-pear-Net-LDAP2", 30 | # needed by pykolab 31 | "python-icalendar", "python-sievelib", 32 | # needed by kolab-webadmin 33 | "mozldap", 34 | # needed by iRony 35 | "php-sabre-*"] 36 | 37 | def GetDependanciesAndProvides(name): 38 | specfile=rpmbuildpath + "/SPECS/" + name + ".spec" 39 | builddepends=[] 40 | provides={} 41 | if not os.path.isfile(specfile): 42 | print("cannot find " + specfile) 43 | else: 44 | globals = {} 45 | for line in open(specfile): 46 | for glob in globals: 47 | line=line.replace("%{"+glob+"}", globals[glob]) 48 | if line.lower().startswith("%global "): 49 | glob = line.strip().split() 50 | globals[glob[1]] = glob[2] 51 | if line.lower().startswith("buildrequires: "): 52 | if line.count(",") > 0: 53 | packagesWithVersions=line[len("BuildRequires: "):].split(",") 54 | else: 55 | packagesWithVersions=line[len("BuildRequires: "):].split() 56 | ignoreNext=False 57 | for word in packagesWithVersions: 58 | if not ignoreNext: 59 | # filter >= 3.0, only use package names 60 | if word[0] == '>' or word[0] == '<' or word[0] == '=': 61 | ignoreNext=True 62 | else: 63 | builddepends.append(word.strip()) 64 | else: 65 | ignoreNext=False 66 | 67 | recentpackagename=name 68 | for line in open(specfile): 69 | if line.lower().startswith("name:"): 70 | name = line[len("name:"):].strip() 71 | recentpackagename=name 72 | provides[name] = [] 73 | elif line.lower().startswith("%package -n"): 74 | recentpackagename=line[len("%package -n"):].strip() 75 | provides[recentpackagename] = [] 76 | elif line.lower().startswith("%package"): 77 | recentpackagename=name + "-" + line[len("%package"):].strip() 78 | provides[recentpackagename] = [] 79 | elif line.lower().startswith("requires:"): 80 | r = line[len("requires:"):].strip().replace("(", "-").replace(")", "") 81 | provides[recentpackagename].append(r.split()[0]) 82 | 83 | return (builddepends, provides) 84 | 85 | def CalculatePackageOrder(packages): 86 | unsorted={} 87 | builddepends={} 88 | depends={} 89 | provides={} 90 | for package in packages: 91 | (builddepends[package],provides[package]) = GetDependanciesAndProvides(package) 92 | for p in provides[package]: 93 | unsorted[p] = 1 94 | depends[p] = provides[package][p] 95 | if not package in unsorted: 96 | unsorted[package] = 1 97 | # useful for debugging: 98 | if Debugging: 99 | print( package + " builddepends on: ") 100 | for p in builddepends[package]: 101 | print(" " + p) 102 | print( package + " provides: ") 103 | for p in provides[package]: 104 | print(" " + p + " which depends on:") 105 | for d in depends[p]: 106 | print(" " + d) 107 | 108 | result = deque() 109 | while len(unsorted) > 0: 110 | nextPackages = [] 111 | for package in unsorted: 112 | if package in packages: 113 | missingRequirement=False 114 | # check that this package does not require a package that is in unsorted 115 | for dep in builddepends[package]: 116 | if dep in unsorted: 117 | missingRequirement=True 118 | if dep in depends: 119 | for dep2 in depends[dep]: 120 | if dep2 in unsorted: 121 | missingRequirement=True 122 | if not missingRequirement: 123 | nextPackages.append(package) 124 | added=True 125 | if nextPackages.count == 0: 126 | # problem: circular dependancy 127 | print ("circular dependancy, remaining packages: ") 128 | for p in unsorted: 129 | print(p) 130 | return None 131 | result.append(nextPackages) 132 | for pkg in nextPackages: 133 | for p in provides[pkg]: 134 | if p in unsorted: 135 | del unsorted[p] 136 | for pkg in nextPackages: 137 | if pkg in unsorted: 138 | del unsorted[pkg] 139 | 140 | return result 141 | 142 | def getPackages(): 143 | packages=[] 144 | for file in os.listdir(rpmbuildpath+"/SPECS"): 145 | if file.endswith(".spec"): 146 | packages.append(file[:-5]) 147 | return packages 148 | 149 | def getSrcRpmFiles(packages): 150 | result={} 151 | 152 | # parse name from spec file 153 | srcrpmnames={} 154 | for pkg in packages: 155 | srcrpmnames[pkg] = pkg 156 | globals = {} 157 | for line in open(rpmbuildpath+"/SPECS/" + pkg + ".spec"): 158 | for glob in globals: 159 | line=line.replace("%{"+glob+"}", globals[glob]) 160 | if line.lower().startswith("%global "): 161 | glob = line.strip().split() 162 | globals[glob[1]] = glob[2] 163 | if line.startswith("Name: "): 164 | srcrpmnames[pkg] = line[6:].strip() 165 | 166 | for file in os.listdir(srcrpmspath): 167 | if file.endswith(".src.rpm"): 168 | bestfit=None 169 | bestfitCount=0 170 | for pkg in packages: 171 | pkgsrcname = srcrpmnames[pkg] 172 | if file.startswith(pkgsrcname): 173 | if len(pkgsrcname) > bestfitCount: 174 | bestfitCount=len(pkgsrcname) 175 | bestfit=pkg 176 | if bestfit is not None: 177 | result[bestfit] = file 178 | return result 179 | 180 | def downloadSrcRpms(): 181 | if os.path.isdir(srcrpmspath): 182 | print("not downloading the src.rpms again. please delete the path " + srcrpmspath + " if you want a fresh download") 183 | return 184 | response = urllib.request.urlopen(obsurl) 185 | os.makedirs(srcrpmspath) 186 | html = response.read().decode('utf-8') 187 | for line in html.split('\n'): 188 | if "src.rpm" in line: 189 | m = re.search(']+>', line) 190 | m2 = re.search('"[^\"]+"', m.group(0)) 191 | srcrpm=m2.group(0).strip('"') 192 | ignore=True 193 | for pkg in includePackages: 194 | if srcrpm.startswith(pkg.replace('*','')): 195 | ignore=False 196 | if not ignore: 197 | os.system("wget " + obsurl + "/" + srcrpm + " -O " + srcrpmspath + "/" + srcrpm) 198 | 199 | def uploadSrcRpms(): 200 | os.system("echo 'mkdir "+fedorapeoplepath+"' | sftp " + fedorapeopleurl) 201 | os.system("cd "+modifiedsrcrpmspath+" && echo 'put *.src.rpm' | sftp " + fedorapeopleurl + "/" + fedorapeoplepath) 202 | 203 | def installSrcRpms(): 204 | # need a clean rpmbuild directory 205 | if os.path.isdir(rpmbuildpath): 206 | if os.path.isdir(rpmbuildpath + ".bak"): 207 | print("Error: cannot rename " + rpmbuildpath + " because " + rpmbuildpath + ".bak already exist") 208 | sys.exit(-1) 209 | os.rename(rpmbuildpath, rpmbuildpath + ".bak") 210 | for file in os.listdir(srcrpmspath): 211 | if file.endswith(".src.rpm"): 212 | os.system("rpm -i " + srcrpmspath + "/" + file) 213 | 214 | def printPackages(orderedpackages): 215 | for pkgs in orderedpackages: 216 | print() 217 | print("build together: ") 218 | for pkg in pkgs: 219 | if pkg in srcrpmfiles: 220 | print(pkgurl + "/" + srcrpmfiles[pkg]) 221 | else: 222 | print(" " + pkg) 223 | 224 | #downloadSrcRpms() 225 | #installSrcRpms() 226 | #uploadSrcRpms() 227 | packages=getPackages() 228 | srcrpmfiles=getSrcRpmFiles(packages) 229 | orderedpackages=CalculatePackageOrder(packages) 230 | printPackages(orderedpackages) 231 | 232 | -------------------------------------------------------------------------------- /kolab/initMultiDomain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTSPATH=`dirname ${BASH_SOURCE[0]}` 4 | source $SCRIPTSPATH/lib.sh 5 | 6 | DetermineOS 7 | InstallWgetAndPatch 8 | DeterminePythonPath 9 | 10 | ##################################################################################### 11 | # Prepare Canonification for Cyrus IMAP 12 | ##################################################################################### 13 | cp -f /etc/imapd.conf /etc/imapd.conf.beforeMultiDomain 14 | sed -i -e "s#ldap_base: .*#ldap_base: dc=%2,dc=%1#g" /etc/imapd.conf 15 | sed -i -e "s#ldap_group_base: .*#ldap_group_base: dc=%2,dc=%1#g" /etc/imapd.conf 16 | sed -i -e "s#ldap_member_base: .*#ldap_member_base: ou=People,dc=%2,dc=%1#g" /etc/imapd.conf 17 | 18 | echo "ldap_domain_base_dn: cn=kolab,cn=config 19 | ldap_domain_filter: (&(objectclass=domainrelatedobject)(associateddomain=%s)) 20 | ldap_domain_name_attribute: associatedDomain 21 | ldap_domain_scope: sub 22 | ldap_domain_result_attribute: inetdomainbasedn" >> /etc/imapd.conf 23 | 24 | service cyrus-imapd restart 25 | 26 | #enable unique ids across domains, to allow login with uid 27 | sed -r -i -e "s#\[kolab\]#[kolab]\nunique_uid_across_domains=true#g" /etc/kolab/kolab.conf 28 | 29 | ##################################################################################### 30 | #Update Postfix LDAP Lookup Tables 31 | # support subdomains too, search_base = dc=%3,dc=%2,dc=%1 32 | # see https://lists.kolab.org/pipermail/users/2013-January/014233.html 33 | ##################################################################################### 34 | 35 | cp -Rf /etc/postfix/ldap /etc/postfix/ldap.beforeMultiDomain 36 | rm -f /etc/postfix/ldap/*_3.cf 37 | for f in `find /etc/postfix/ldap/ -type f -name "*.cf"`; 38 | do 39 | f3=${f/.cf/_3.cf} 40 | cp $f $f3 41 | if [[ "/etc/postfix/ldap/mydestination.cf" == "$f" ]] 42 | then 43 | sed -r -i -e 's/^query_filter = .*$/query_filter = (\&(associateddomain=%s)(associateddomain=*.*.*))/g' $f3 44 | else 45 | sed -r -i -e 's/^search_base = .*$/search_base = dc=%2,dc=%1/g' $f 46 | sed -r -i -e 's/^search_base = .*$/search_base = dc=%3,dc=%2,dc=%1/g' $f3 47 | sed -r -i -e 's#^domain = .*$#domain = ldap:/etc/postfix/ldap/mydestination_3.cf#g' $f3 48 | fi 49 | done 50 | 51 | cp -f /etc/postfix/main.cf /etc/postfix/main.cf.beforeMultiDomain 52 | sed -r -i -e 's#transport_maps.cf#transport_maps.cf, ldap:/etc/postfix/ldap/transport_maps_3.cf#g' /etc/postfix/main.cf 53 | sed -i -e 's#virtual_alias_maps.cf#virtual_alias_maps.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_3.cf, ldap:/etc/postfix/ldap/mailenabled_distgroups_3.cf, ldap:/etc/postfix/ldap/mailenabled_dynamic_distgroups_3.cf, ldap:/etc/postfix/ldap/virtual_alias_maps_sharedfolders_3.cf#' /etc/postfix/main.cf 54 | sed -r -i -e 's#local_recipient_maps.cf#local_recipient_maps.cf, ldap:/etc/postfix/ldap/local_recipient_maps_3.cf#g' /etc/postfix/main.cf 55 | 56 | # create a file that can be manipulated manually to allow aliases across domains; 57 | # eg. user mymailbox@test.de gets emails that are sent to myalias@test2.de; 58 | # You can also enable aliases for domains here to receive emails properly, eg. @test2.de @test.de; 59 | # You need to run postmap on the file after manually changing it! 60 | postfix_virtual_file=/etc/postfix/virtual_alias_maps_manual.cf 61 | if [ ! -f $postfix_virtual_file ] 62 | then 63 | echo "# you can manually set aliases, across domains. " > $postfix_virtual_file 64 | echo "# for example: " >> $postfix_virtual_file 65 | echo "#myalias@test2.de mymailbox@test.de" >> $postfix_virtual_file 66 | echo "#@test4.de @test.de" >> $postfix_virtual_file 67 | echo "#@pokorra.it timotheus.pokorra@test1.de" >> $postfix_virtual_file 68 | fi 69 | sed -i -e "s#virtual_alias_maps.cf#virtual_alias_maps.cf, hash:$postfix_virtual_file#" /etc/postfix/main.cf 70 | postmap $postfix_virtual_file 71 | 72 | service postfix restart 73 | 74 | ##################################################################################### 75 | #kolab_auth conf roundcube; see https://git.kolab.org/roundcubemail-plugins-kolab/commit/?id=1778b5ec70156f064fdda61c817c678001406996 76 | ##################################################################################### 77 | cp -r /etc/roundcubemail/kolab_auth.inc.php /etc/roundcubemail/kolab_auth.inc.php.beforeMultiDomain 78 | sed -r -i -e "s#=> 389,#=> 389,\n 'domain_base_dn' => 'cn=kolab,cn=config',\n 'domain_filter' => '(\&(objectclass=domainrelatedobject)(associateddomain=%s))',\n 'domain_name_attr' => 'associateddomain',#g" /etc/roundcubemail/kolab_auth.inc.php 79 | sed -r -i -e "s#'ou=People,.*'#'ou=People,%dc'#g" /etc/roundcubemail/kolab_auth.inc.php 80 | sed -r -i -e "s#'ou=Groups,.*'#'ou=Groups,%dc'#g" /etc/roundcubemail/kolab_auth.inc.php 81 | 82 | ##################################################################################### 83 | #enable freebusy for all domains 84 | ##################################################################################### 85 | sed -r -i -e "s#base_dn = .*#base_dn = %dc#g" /usr/share/kolab-freebusy/config/config.ini 86 | 87 | ##################################################################################### 88 | #auto created folders: do not use an extra partition for the archive folder. 89 | #see https://issues.kolab.org/show_bug.cgi?id=3210 90 | ##################################################################################### 91 | sed -r -i -e "s#'quota': 0,##g" /etc/kolab/kolab.conf 92 | sed -r -i -e "s#'partition': 'archive'##g" /etc/kolab/kolab.conf 93 | 94 | ##################################################################################### 95 | # Fix Global Address Book in Multi Domain environment 96 | #################################################################################### 97 | cp -r /etc/roundcubemail/config.inc.php /etc/roundcubemail/config.inc.php.beforeMultiDomain 98 | sed -r -i -e "s#'ou=People,.*'#'ou=People,%dc'#g" /etc/roundcubemail/config.inc.php 99 | sed -r -i -e "s#'ou=Groups,.*'#'ou=Groups,%dc'#g" /etc/roundcubemail/config.inc.php 100 | 101 | ##################################################################################### 102 | #set primary_mail value in kolab section, so that new users in a different domain will have a proper primary email address, even without changing kolab.conf for each domain 103 | ##################################################################################### 104 | sed -r -i -e "s/primary_mail = .*/primary_mail = %(givenname)s.%(surname)s@%(domain)s/g" /etc/kolab/kolab.conf 105 | 106 | ##################################################################################### 107 | #make sure that for alias domains, the emails will actually arrive, by checking the postfix file 108 | #see https://issues.kolab.org/show_bug.cgi?id=2658 109 | ##################################################################################### 110 | sed -r -i -e "s#\[kolab\]#[kolab]\npostfix_virtual_file = $postfix_virtual_file#g" /etc/kolab/kolab.conf 111 | 112 | ##################################################################################### 113 | #avoid a couple of warnings by setting default values 114 | ##################################################################################### 115 | sed -r -i -e "s#\[ldap\]#[ldap]\nmodifytimestamp_format = %%Y%%m%%d%%H%%M%%SZ#g" /etc/kolab/kolab.conf 116 | sed -r -i -e "s/\[cyrus-imap\]/[imap]\nvirtual_domains = userid\n\n[cyrus-imap]/g" /etc/kolab/kolab.conf 117 | 118 | ##################################################################################### 119 | # install memcache to improve WAP login speed if many domains are present 120 | ##################################################################################### 121 | if [[ $OS == CentOS* || $OS == Fedora* ]] 122 | then 123 | yum -y install php-pecl-memcache memcached 124 | elif [[ $OS == Debian* || $OS == Ubuntu* ]] 125 | then 126 | apt-get -y install php5-memcache memcached 127 | fi 128 | 129 | systemctl start memcached 130 | systemctl enable memcached 131 | sed -r -i -e "s#\[kolab_wap\]#[kolab_wap]\nmemcache_hosts = 127.0.0.1:11211\nmemcache_pconnect = true#g" /etc/kolab/kolab.conf 132 | 133 | ##################################################################################### 134 | # apply a couple of patches, see related kolab bugzilla number in filename, eg. https://issues.kolab.org/show_bug.cgi?id=1869 135 | ##################################################################################### 136 | 137 | if [ ! -d patches ] 138 | then 139 | mkdir -p patches 140 | echo Downloading patch validateAliasDomainPostfixVirtualFileBug2658.patch 141 | wget $patchesurl/validateAliasDomainPostfixVirtualFileBug2658.patch -O patches/validateAliasDomainPostfixVirtualFileBug2658.patch 142 | wget $patchesurl/canonification_via_uid_wap.patch -O patches/canonification_via_uid_wap.patch 143 | wget $patchesurl/canonification_via_uid_roundcube.patch -O patches/canonification_via_uid_roundcube.patch 144 | wget $patchesurl/canonification_via_uid_pykolab.patch -O patches/canonification_via_uid_pykolab.patch 145 | fi 146 | 147 | if [ -z $APPLYPATCHES ] 148 | then 149 | APPLYPATCHES=1 150 | fi 151 | 152 | if [ $APPLYPATCHES -eq 1 ] 153 | then 154 | patch -p1 --fuzz=0 -i `pwd`/patches/validateAliasDomainPostfixVirtualFileBug2658.patch -d /usr/share/kolab-webadmin || exit -1 155 | patch -p1 --fuzz=0 -i `pwd`/patches/canonification_via_uid_wap.patch -d /usr/share/kolab-webadmin || exit -1 156 | patch -p1 --fuzz=0 -i `pwd`/patches/canonification_via_uid_roundcube.patch -d /usr/share/roundcubemail || exit -1 157 | patch -p1 --fuzz=0 -i `pwd`/patches/canonification_via_uid_pykolab.patch -d $pythonDistPackages || exit -1 158 | fi 159 | 160 | service kolab-saslauthd restart 161 | 162 | KolabService restart 163 | --------------------------------------------------------------------------------