Projects
Kolab:16:Testing:Candidate
pykolab-python3
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 2
View file
pykolab.spec
Changed
@@ -45,23 +45,21 @@ %global pythonmysql python-mysql %else %if 0%{?use_python3} -%global pythonmysql python3-mysqlclient +%global pythonmysql python3-PyMySQL %else %global pythonmysql MySQL-python %endif %endif -%global upstream_version 0.9.0 - Summary: Kolab Groupware Solution Name: pykolab -Version: 0.9.0.7 +Version: 0.9.0 Release: 1%{?dist} License: GPLv3+ Group: Applications/System URL: http://kolab.org/ -Source0: pykolab-%{upstream_version}.tar.gz +Source0: pykolab-%{version}.tar.gz Source1: pykolab.logrotate BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root @@ -250,7 +248,19 @@ This is the Kolab Content Filter, with plugins %prep -%setup -q -n %{name}-%{upstream_version} +%setup -q + +%if 0%{?use_python3} +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' kolabd.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' saslauthd.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' conf.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' setup-kolab.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' wallace.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' kolab-cli.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' bin/kolab_parse_telemetry.py +sed -i 's|#!/usr/bin/python|#!/usr/bin/python3|g' bin/kolab_smtp_access_policy.py +%endif + %build autoreconf -v || automake --add-missing && autoreconf -v
View file
cyrus-imapd.conf-cert-paths.patch
Changed
@@ -1,24 +1,20 @@ diff -ur pykolab-0.8.6.orig/share/templates/guam.sys.config.tpl pykolab-0.8.6/share/templates/guam.sys.config.tpl --- pykolab-0.8.6.orig/share/templates/guam.sys.config.tpl 2016-11-18 12:24:34.000000000 +0100 +++ pykolab-0.8.6/share/templates/guam.sys.config.tpl 2016-11-18 13:35:14.350503922 +0100 -@@ -26,9 +26,7 @@ +@@ -26,7 +26,7 @@ }, { tls_config, -- { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" }, -- { cacertfile, "/etc/pki/cyrus-imapd/cyrus-imapd-ca.pem" }, -- { keyfile, "/etc/pki/cyrus-imapd/cyrus-imapd-key.pem" } +- { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" } + { certfile, "/etc/ssl/private/cyrus-imapd.pem" } } -@@ -43,9 +43,7 @@ +@@ -43,7 +43,7 @@ }, { tls_config, -- { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" }, -- { cacertfile, "/etc/pki/cyrus-imapd/cyrus-imapd-ca.pem" }, -- { keyfile, "/etc/pki/cyrus-imapd/cyrus-imapd-key.pem" } +- { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" } + { certfile, "/etc/ssl/private/cyrus-imapd.pem" } }
View file
debian.changelog
Changed
@@ -1,6 +1,6 @@ -pykolab (0.9.0.7-0~kolab4) unstable; urgency=low +pykolab (0.9.0-0~kolab1) unstable; urgency=low - * Release of version 0.9.0 + * Release of version 0.8.0 -- Christian Mollekopf <mollekopf@apheleia-it.ch> Tue, 16 Aug 2022 01:49:00 +0100
View file
debian.control
Changed
@@ -45,7 +45,6 @@ python3-pymysql, python3-six, python3-sqlalchemy, - ${python3:Depends}, ${misc:Depends}, ${shlibs:Depends}, ${ucs:Depends} @@ -58,7 +57,6 @@ python3, python3-augeas, python3-cheetah, - ${python3:Depends}, ${misc:Depends}, ${ucs:Depends} Description: Command-line utilities for Kolab @@ -66,13 +64,7 @@ Package: kolab-conf Architecture: all -Depends: pykolab (= ${binary:Version}), - kolab-ldap, - python3, - ${python3:Depends}, - ${misc:Depends}, - python3-augeas, - python3-cheetah +Depends: pykolab (= ${binary:Version}), kolab-ldap, ${python3:Depends}, python3, ${misc:Depends}, python3-augeas, python3-cheetah Description: Configuration management for Kolab This package includes configuration management utilities for Kolab Groupware @@ -82,7 +74,6 @@ Depends: lsb-base (>= 3.0-6), pykolab (= ${binary:Version}), python3, - ${python3:Depends}, sasl2-bin, ${misc:Depends} Description: SASL Authentication Daemon for Kolab @@ -90,14 +81,14 @@ Package: kolab-server Architecture: all -Depends: pykolab (= ${binary:Version}), python3, ${python3:Depends}, ${misc:Depends}, lsb-base (>= 3.0-6) +Depends: pykolab (= ${binary:Version}), python3, ${misc:Depends}, lsb-base (>= 3.0-6) Description: Kolab Groupware Server Server daemon synchronizing the mutations between various Kolab Groupware components. Package: kolab-telemetry Architecture: all -Depends: kolab-cli (= ${binary:Version}), python3, ${python3:Depends}, ${misc:Depends} +Depends: kolab-cli (= ${binary:Version}), python3, ${misc:Depends} Description: Kolab Telemetry Logging Capabilities Cyrus IMAP Telemetry logging handling capabilities for Kolab Groupware @@ -107,7 +98,6 @@ python3, python3-kolabformat, ${misc:Depends}, - ${python3:Depends}, python3-icalendar, python3-tzlocal Description: Kolab XML format wrapper for pykolab @@ -119,7 +109,6 @@ kolab-xml (= ${binary:Version}), python3-gnupg, python3, - ${python3:Depends}, ${misc:Depends}, lsb-base (>= 3.0-6), python3-dateutil,
View file
debian.rules
Changed
@@ -10,14 +10,11 @@ export DH_VERBOSE=1 %: - dh $@ --with python3 --without python2 --with autotools-dev --with autoreconf + dh $@ --with python3 --with autotools-dev --with autoreconf override_dh_auto_test: dh_auto_test || echo “ignoring test failure” -override_dh_python3: - dh_python3 --shebang=/usr/bin/python3 - override_dh_install: #dh_install --list-missing if -x "$$(which univention-install-config-registry 2>/dev/null)" ; then \
View file
pykolab-0.9.0.tar.gz/bin/kolab_parse_telemetry.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/bin/kolab_smtp_access_policy.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/conf.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
View file
pykolab-0.9.0.tar.gz/conf/kolab.conf
Changed
@@ -240,7 +240,8 @@ ; The base DN, scope and filter to use when searching for additional domain ; name spaces in this environment. -domain_base_dn = cn=kolab,cn=config +domain_base_dn = ou=Domains,%(base_dn)s + domain_filter = (&(associatedDomain=*)) domain_name_attribute = associateddomain ; Attribute that holds the root dn for the domain name space. If this attribute @@ -410,7 +411,7 @@ ; The URI to use to connect to IMAP. Note that pykolab itself can detect whether ; or not Cyrus IMAP is deployed in a Murder topology, and should be able to ; connect to individual backends as well. -uri = imaps://localhost:9993 +uri = imaps://localhost:993 ; The login username to use for global administration. admin_login = cyrus-admin ; The corresponding password.
View file
pykolab-0.9.0.tar.gz/cyruslib.py
Changed
@@ -139,6 +139,7 @@ # TODO handle escape sequences? +# TODO handle literal continuations def parseLiteral(data, offset): i = offset while i < len(data): @@ -148,16 +149,6 @@ i += 1 -def parseContinuation(data, offset): - i = offset - while i < len(data): - c = datai:i+1 - if c == b'}': - length = int(dataoffset:i) - return datai + 2:i + length + 1, i + length + 2 - i += 1 - - def parse(data, offset): result = i = offset @@ -171,12 +162,6 @@ continue if c == b')': return result, i + 1 - if c == b'{': - res, newOffset = parseContinuation(data, i + 1) - # print("Found continuation", res, newOffset) - result.append(res) - i = newOffset - continue if c == b'"': res, newOffset = parseLiteral(data, i + 1) # print("Found literal", res, newOffset) @@ -859,11 +844,11 @@ ann = {} annotations = - # Deal with partial metadata responses by making sure we have an even count of brackets + # Deal with partial metadata responses concat_items = for item in data: if isinstance(item, tuple): - item = b' '.join(list(item)) + item = ' '.join(str(x) for x in item) if len(concat_items) > 0: concat_items.append(item) @@ -872,28 +857,28 @@ if joined.count(b'(') == joined.count(b')'): annotations.append(joined) concat_items = - - continue - - if item.count(b'(') == item.count(b')'): - annotations.append(item) - continue - - concat_items.append(item) + continue + else: + if item.count(b'(') == item.count(b')'): + annotations.append(item) + continue + else: + concat_items.append(item) + continue for annotation in annotations: annotation = annotation.strip() tokens = tokenize(annotation) folder = tokens0 - if mailbox != b'*' and folder != mailbox: + if folder != mailbox: quoted_mailbox = "\"%s\"" % (mailbox) if folder != quoted_mailbox: # print("mismatch") # print(quoted_mailbox) self.__verbose( - 'GETMETADATA %s Mailbox \'%s\' is not the same as \'%s\'' - % (mailbox, quoted_mailbox, folder) + 'GETMETADATA %s Mailbox \'%s\' is not the same as \'%s\'' \ + % (mailbox, quoted_mailbox, folder) ) return {}
View file
pykolab-0.9.0.tar.gz/ext/python/Tools/freeze/freeze.py
Changed
@@ -1,4 +1,4 @@ -#! /usr/bin/env python3 +#! /usr/bin/env python """Freeze a Python script into a binary.
View file
pykolab-0.9.0.tar.gz/kolab-cli.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/kolabd.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/pykolab/auth/__init__.py
Changed
@@ -232,8 +232,8 @@ def find_user(self, attr, value, **kw): return self._auth.search_entry_by_attribute(attr, value, **kw) - def find_user_dn(self, login, kolabuser=False, domain=None): - return self._auth._find_user_dn(login, kolabuser, domain); + def find_user_dn(self, login, kolabuser=False): + return self._auth._find_user_dn(login, kolabuser); def list_recipient_addresses(self, user): return self._auth.list_recipient_addresses(user)
View file
pykolab-0.9.0.tar.gz/pykolab/auth/ldap/__init__.py
Changed
@@ -124,7 +124,13 @@ log.error(_l("Authentication cache failed: %r") % (errmsg)) if base_dn is None: - base_dn = self._base_dn() + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) + + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn try: auth_cache.set_entry(self.domain, base_dn) @@ -453,11 +459,18 @@ return entry_id'dn' unique_attribute = self.config_get('unique_attribute') + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) + + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn _filter = "(%s=%s)" % (unique_attribute, ldap.filter.escape_filter_chars(entry_id)) _search = self.ldap.search_ext( - self._base_dn(), + base_dn, ldap.SCOPE_SUBTREE, _filter, 'entrydn' @@ -701,13 +714,19 @@ _filter = "%s%s%s" % (__filter_prefix, _filter, __filter_suffix) - base_dn = self._base_dn() - - log.debug(_l("Finding recipient with filter %r in %s") % (_filter, base_dn), level=8) + log.debug(_l("Finding recipient with filter %r") % (_filter), level=8) if len(_filter) <= 6: return None + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) + + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn + _results = self.ldap.search_s( base_dn, scope=ldap.SCOPE_SUBTREE, @@ -771,13 +790,13 @@ _filter = "%s%s%s" % (__filter_prefix, _filter, __filter_suffix) + log.debug(_l("Finding resource with filter %r") % (_filter), level=8) + if len(_filter) <= 6: return None resource_base_dn = self._object_base_dn('resource') - log.debug(_l("Finding resource with filter %s in %s") % (_filter, resource_base_dn), level=8) - _results = self.ldap.search_s( resource_base_dn, scope=ldap.SCOPE_SUBTREE, @@ -1201,9 +1220,13 @@ _filter = "(%s=%s)" % (attr, ldap.filter.escape_filter_chars(value)) - base_dn = self._base_dn() + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) - log.debug(_l("Finding entry %s in %s") % (_filter, base_dn), level=8) + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn _results = self._search( base_dn, @@ -1298,14 +1321,22 @@ _filter = "(&%s(modifytimestamp>=%s))" % (_filter, modified_after) + log.debug(_l("Synchronization is using filter %r") % (_filter), level=8) + if mode != 0: override_search = mode else: - override_search = None + override_search = False - base_dn = self._base_dn() + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) - log.debug(_l("Synchronization is searching for %s in %s") % (_filter, base_dn), level=8) + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn + + log.debug(_l("Synchronization is searching against base DN: %s") % (base_dn), level=8) if callback is None: callback = self._synchronize_callback @@ -2398,7 +2429,14 @@ self._bind() entry_dn = self.entry_dn(entry_id) - base_dn = self._base_dn() + + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) + + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn for _type in 'user', 'group', 'sharedfolder': __filter = self.config_get('kolab_%s_filter' % (_type)) @@ -2424,14 +2462,14 @@ return None - def _find_user_dn(self, login, kolabuser=False, domain=None): + def _find_user_dn(self, login, kolabuser=False): """ Find the distinguished name (DN) for a (Kolab) user entry in LDAP. """ conf_prefix = 'kolab_' if kolabuser else '' - user_base_dn = self._object_base_dn('user', conf_prefix, domain) + user_base_dn = self._object_base_dn('user', conf_prefix) auth_attrs = self.config_get_list('auth_attributes') @@ -2644,12 +2682,18 @@ return domains - def _object_base_dn(self, objectType, prefix='', domain=None): + def _object_base_dn(self, objectType, prefix=''): """ Get configured base DN for specified Kolab object type """ - object_base_dn = self.config_get_raw(prefix + objectType + '_base_dn') - base_dn = self._base_dn(domain) + object_base_dn = self.config_get(prefix + objectType + '_base_dn') + config_base_dn = self.config_get('base_dn') + ldap_base_dn = self._kolab_domain_root_dn(self.domain) + + if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: + base_dn = ldap_base_dn + else: + base_dn = config_base_dn if object_base_dn is None: object_base_dn = base_dn @@ -2658,15 +2702,6 @@ return object_base_dn - def _base_dn(self, domain=None): - config_base_dn = self.config_get('base_dn') - ldap_base_dn = self._kolab_domain_root_dn(domain if domain is not None else self.domain) - - if ldap_base_dn is not None and not ldap_base_dn == config_base_dn: - return ldap_base_dn - - return config_base_dn - def _synchronize_callback(self, *args, **kw): """ Determine the characteristics of the callback being placed, and @@ -2809,7 +2844,7 @@ attrlist=None, attrsonly=0, timeout=-1, - callback=None, + callback=False, primary_domain=None, secondary_domains= ): @@ -2825,8 +2860,6 @@ ) ) - log.debug(_l("Searching with filter %r in %s") % (filterstr, base_dn), level=8) - _search = self.ldap.search_ext( base_dn, scope=scope, @@ -2905,7 +2938,7 @@ attrlist=None, attrsonly=0, timeout=-1, - callback=None, + callback=False, primary_domain=None, secondary_domains= ): @@ -2915,8 +2948,6 @@ server_page_control = ldap.controls.libldap.SimplePagedResultsControl(size=page_size,cookie='') - log.debug(_l("Searching for %r in %s") % (filterstr, base_dn), level=8) - _search = self.ldap.search_ext( base_dn, scope=scope, @@ -2994,7 +3025,7 @@ attrlist=None, attrsonly=0, timeout=-1, - callback=None, + callback=False, primary_domain=None, secondary_domains= ): @@ -3008,7 +3039,7 @@ attrlist=None, attrsonly=0, timeout=-1, - callback=None, + callback=False, primary_domain=None, secondary_domains= ): @@ -3054,7 +3085,7 @@ attrlist=None, attrsonly=0, timeout=None, - callback=None, + callback=False, primary_domain=None, secondary_domains= ): @@ -3062,7 +3093,7 @@ if timeout is None: timeout = float(self.config_get('ldap', 'timeout', default=10)) - log.debug(_l("Searching for %r in %s") % (filterstr, base_dn), level=8) + log.debug(_l("Searching with filter %r") % (filterstr), level=8) _search = self.ldap.search( base_dn, @@ -3092,8 +3123,8 @@ attrlist=None, attrsonly=0, timeout=None, - override_search=None, - callback=None, + override_search=False, + callback=False, primary_domain=None, secondary_domains= ): @@ -3150,7 +3181,7 @@ _results = - if override_search: + if override_search is not False: _use_ldap_controls = override_search else: _use_ldap_controls = self.ldap.supported_controls
View file
pykolab-0.9.0.tar.gz/pykolab/auth/ldap/syncrepl.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python try: import anydbm
View file
pykolab-0.9.0.tar.gz/pykolab/cli/cmd_delete_mailbox.py
Changed
@@ -37,17 +37,6 @@ def description(): return """Delete a mailbox or sub-folder. Note that the mailbox or folder is removed recursively.""" -def cli_options(): - my_option_group = conf.add_cli_parser_option_group(_("CLI Options")) - my_option_group.add_option( - '--server', - dest="connect_server", - action="store", - default=None, - metavar="SERVER", - help=_("Delete mailboxes on server SERVER only.") - ) - def execute(*args, **kw): """ Delete mailbox @@ -59,10 +48,7 @@ imap = IMAP() - if not conf.connect_server == None: - imap.connect(server=conf.connect_server) - else: - imap.connect() + imap.connect() delete_folders = while len(conf.cli_args) > 0:
View file
pykolab-0.9.0.tar.gz/pykolab/cli/cmd_list_deleted_mailboxes.py
Changed
@@ -104,7 +104,7 @@ print("Deleted folders:") for folder in folders: - utf8_folder = imap_utf7.decode(folder) + utf8_folder = imap_utf7.decode(folder).encode('utf-8') mbox_parts = imap.parse_mailfolder(utf8_folder) ts = datetime.datetime.fromtimestamp(int(mbox_parts'hex_timestamp', 16))
View file
pykolab-0.9.0.tar.gz/pykolab/cli/cmd_list_mailbox_metadata.py
Changed
@@ -92,6 +92,6 @@ if folder in metadata: for annotation in metadatafolder: print(" %-49s %s" % ( - annotation.decode('utf-8', 'replace'), - metadatafolderannotation.decode('utf-8', 'replace') + annotation, + metadatafolderannotation ))
View file
pykolab-0.9.0.tar.gz/pykolab/cli/cmd_list_mailboxes.py
Changed
@@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # -import datetime from . import commands @@ -98,13 +97,7 @@ folders.extend(imap.lm(imap_utf7.encode(search))) for folder in folders: - if conf.raw: - print(folder) + if not conf.raw: + print(imap_utf7.decode(folder)) else: - utf8_folder = imap_utf7.decode(folder) - if 'DELETED' in folder: - mbox_parts = imap.parse_mailfolder(utf8_folder) - ts = datetime.datetime.fromtimestamp(int(mbox_parts'hex_timestamp', 16)) - print("%s (Deleted at %s)" % (folder, ts)) - else: - print(utf8_folder) + print(folder)
View file
pykolab-0.9.0.tar.gz/pykolab/imap/__init__.py
Changed
@@ -305,13 +305,13 @@ def folder_quote(self, folder): return u'"' + str(folder).strip('"') + '"' - def get_metadata(self, folder, pattern='*'): + def get_metadata(self, folder): """ Obtain all metadata entries on a folder """ metadata = {} - _metadata = self.imap.getmetadata(self.folder_utf7(folder), pattern) + _metadata = self.imap.getmetadata(self.folder_utf7(folder), '*') for (k, v) in _metadata.items(): metadataself.folder_utf8(k) = v
View file
pykolab-0.9.0.tar.gz/pykolab/imap/cyrus.py
Changed
@@ -201,14 +201,13 @@ ) # TODO: Workaround for undelete - if 'hex_timestamp' in _mailfolder: - mailfolder = "DELETED/%s%s%s@%s" % ( + if len(self.lm(mailfolder)) < 1 and 'hex_timestamp' in _mailfolder: + mailfolder = self.folder_utf7("DELETED/%s%s%s@%s" % ( self.separator.join(_mailfolder'path_parts'), self.separator, _mailfolder'hex_timestamp', _mailfolder'domain') - - mailfolder = self.folder_utf7(mailfolder) + ) # TODO: Murder capabilities may have been suppressed using Cyrus IMAP # configuration. @@ -242,15 +241,18 @@ max_tries = 20 num_try = 0 - ann_path = b"/vendor/cmu/cyrus-imapd/server" - s_ann_path = b"/shared%s" % (ann_path) + ann_path = "/vendor/cmu/cyrus-imapd/server" + s_ann_path = "/shared%s" % (ann_path) while 1: num_try += 1 - annotations = self._getmetadata(mailfolder, ann_path) + annotations = self._getmetadata( + '"%s"' % (mailfolder), + ann_path + ) if mailfolder in annotations: - if s_ann_path.encode('utf-8') in annotationsmailfolder: + if s_ann_path in annotationsmailfolder: break if max_tries <= num_try: @@ -277,7 +279,7 @@ time.sleep(1) - server = annotationsmailfolders_ann_path.encode('utf-8') + server = annotationsmailfolders_ann_path self.mboxmailfolder = server log.debug( @@ -452,11 +454,6 @@ for undelete_folder in undelete_folders: undelete_mbox = self.parse_mailfolder(undelete_folder) - if undelete_mbox is None: - print(_("The folder to undelete does not exist") % ( - undelete_mbox - ), file=sys.stdout) - return prefix = undelete_mbox'path_parts'.pop(0) mbox = undelete_mbox'path_parts'.pop(0) @@ -502,12 +499,8 @@ ) ) + target_server = self.find_mailfolder_server(target_folder) source_server = self.find_mailfolder_server(undelete_folder) - try: - target_server = self.find_mailfolder_server(target_folder) - except cyruslib.CYRUSError as errmsg: - # This is normal for a toplevel folder, the target does not exist - target_server = source_server if hasattr(conf, 'dry_run') and not conf.dry_run: undelete_folder = self.folder_utf7(undelete_folder)
View file
pykolab-0.9.0.tar.gz/pykolab/setup/setup_imap.py
Changed
@@ -61,7 +61,7 @@ partition_default = "/var/spool/cyrus/mail/" imapd_settings = { - "ldap_uri": conf.get('ldap', 'ldap_uri'), + "ldap_servers": conf.get('ldap', 'ldap_uri'), "ldap_base": conf.get('ldap', 'base_dn'), "ldap_bind_dn": conf.get('ldap', 'service_bind_dn'), "ldap_password": conf.get('ldap', 'service_bind_pw'),
View file
pykolab-0.9.0.tar.gz/pykolab/setup/setup_ldap.py
Changed
@@ -56,14 +56,6 @@ ) ldap_group.add_option( - "--domain", - dest = "domain", - action = "store", - default = None, - help = _("Specify the domain (defaults to one level up from fqdn).") - ) - - ldap_group.add_option( "--allow-anonymous", dest = "anonymous", action = "store_true", @@ -256,13 +248,19 @@ # TODO: Verify the user and group exist. - _input'fqdn' = conf.fqdn - _input'hostname' = conf.fqdn.split('.')0 - if conf.domain: - _input'domain' = conf.domain - else: - _input'domain' = '.'.join(conf.fqdn.split('.')1:) + # TODO: This takes the system fqdn, domainname and hostname, rather then + # the desired fqdn, domainname and hostname. + # + # TODO^2: This should be confirmed. + if conf.fqdn: + _input'fqdn' = conf.fqdn + _input'hostname' = conf.fqdn.split('.')0 + _input'domain' = '.'.join(conf.fqdn.split('.')1:) + else: + _input'fqdn' = fqdn + _input'hostname' = hostname.split('.')0 + _input'domain' = domainname _input'nodotdomain' = _input'domain'.replace('.','_') _input'rootdn' = utils.standard_root_dn(_input'domain')
View file
pykolab-0.9.0.tar.gz/pykolab/setup/setup_mysql.py
Changed
@@ -181,35 +181,54 @@ confirm=True ) - mysql_commands = - "UPDATE mysql.user SET Password=PASSWORD('%s') WHERE User='root';" % ( - mysql_root_password - ), - "UPDATE mysql.user SET authentication_string=PASSWORD('%s') WHERE User='root';" % ( - mysql_root_password - ), - """ - UPDATE mysql.user - SET plugin='mysql_native_password' - WHERE User='root' AND plugin='auth_socket'; - """, - "SET PASSWORD FOR 'root'@localhost = PASSWORD('%s');" % (mysql_root_password), - "FLUSH PRIVILEGES;" - - - for mysql_command in mysql_commands: - p1 = subprocess.Popen( - - 'echo', - mysql_command - , - stdout=subprocess.PIPE - ) + p1 = subprocess.Popen( + + 'echo', + 'UPDATE mysql.user SET Password=PASSWORD(\'%s\') WHERE User=\'root\';' % ( + mysql_root_password + ) + , + stdout=subprocess.PIPE + ) - p2 = subprocess.Popen('mysql', stdin=p1.stdout) - p1.stdout.close() - p2.communicate() + p2 = subprocess.Popen('mysql', stdin=p1.stdout) + p1.stdout.close() + p2.communicate() + p1 = subprocess.Popen( + + 'echo', + "UPDATE mysql.user SET authentication_string=PASSWORD('%s') WHERE User='root';" % ( + mysql_root_password + ) + , + stdout=subprocess.PIPE + ) + + p2 = subprocess.Popen('mysql', stdin=p1.stdout) + p1.stdout.close() + p2.communicate() + + p1 = subprocess.Popen( + + 'echo', + """ + UPDATE mysql.user + SET plugin='mysql_native_password' + WHERE User='root' AND plugin='auth_socket'; + """ + , + stdout=subprocess.PIPE + ) + + p2 = subprocess.Popen('mysql', stdin=p1.stdout) + p1.stdout.close() + p2.communicate() + + p1 = subprocess.Popen('echo', 'FLUSH PRIVILEGES;', stdout=subprocess.PIPE) + p2 = subprocess.Popen('mysql', stdin=p1.stdout) + p1.stdout.close() + p2.communicate() socket_path = None socket_paths =
View file
pykolab-0.9.0.tar.gz/pykolab/telemetry.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/pykolab/utils.py
Changed
@@ -398,9 +398,6 @@ if 'sn' in result: result'surname' = ensure_str(result'sn').replace(' ', '') - if 'givenname' in result: - result'givenname' = ensure_str(result'givenname').replace(' ', '') - if 'mail' in result: if isinstance(result'mail', list): result'mail' = result'mail'0
View file
pykolab-0.9.0.tar.gz/pykolab/xml/contact.py
Changed
@@ -14,9 +14,6 @@ pass def contact_from_string(string): - if isinstance(string, bytes): - string = string.decode('utf-8') - _xml = kolabformat.readContact(string, False) return Contact(_xml)
View file
pykolab-0.9.0.tar.gz/pykolab/xml/event.py
Changed
@@ -31,8 +31,6 @@ return Event(from_ical=ical, from_string=string) def event_from_string(string): - if isinstance(string, bytes): - string = string.decode('utf-8') return Event(from_string=string) def event_from_message(message):
View file
pykolab-0.9.0.tar.gz/pykolab/xml/note.py
Changed
@@ -7,8 +7,6 @@ from pykolab.xml.utils import ustr def note_from_string(string): - if isinstance(string, bytes): - string = string.decode('utf-8') _xml = kolabformat.readNote(string, False) return Note(_xml)
View file
pykolab-0.9.0.tar.gz/pykolab/xml/todo.py
Changed
@@ -19,8 +19,6 @@ return Todo(from_ical=ical, from_string=string) def todo_from_string(string): - if isinstance(string, bytes): - string = string.decode('utf-8') return Todo(from_string=string) def todo_from_message(message):
View file
pykolab-0.9.0.tar.gz/saslauthd.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/setup-kolab.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 -u +#!/usr/bin/python -u # -*- coding: utf-8 -*- # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com)
View file
pykolab-0.9.0.tar.gz/share/templates/guam.sys.config.tpl
Changed
@@ -26,9 +26,7 @@ }, { tls_config, - { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" }, - { cacertfile, "/etc/pki/cyrus-imapd/cyrus-imapd-ca.pem" }, - { keyfile, "/etc/pki/cyrus-imapd/cyrus-imapd-key.pem" } + { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" } } @@ -45,9 +43,7 @@ }, { tls_config, - { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" }, - { cacertfile, "/etc/pki/cyrus-imapd/cyrus-imapd-ca.pem" }, - { keyfile, "/etc/pki/cyrus-imapd/cyrus-imapd-key.pem" } + { certfile, "/etc/pki/cyrus-imapd/cyrus-imapd.pem" } }
View file
pykolab-0.9.0.tar.gz/share/templates/imapd.conf.tpl
Changed
@@ -14,7 +14,7 @@ auth_mech: pts pts_module: ldap ptloader_sock: /var/lib/imap/socket/ptsock -ldap_uri: $ldap_uri +ldap_servers: $ldap_servers ldap_sasl: 0 ldap_base: $ldap_base ldap_bind_dn: $ldap_bind_dn @@ -33,7 +33,7 @@ unixhierarchysep: 1 virtdomains: userid annotation_definitions: /etc/imapd.annotations.conf -sieve_extensions: fileinto reject envelope body vacation imap4flags notify include regex subaddress relational copy date index +sieve_extensions: fileinto reject envelope body vacation imapflags notify include regex subaddress relational copy date index allowallsubscribe: 0 allowusermoves: 1 altnamespace: 1
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/acl.inc.php.tpl
Changed
@@ -7,6 +7,8 @@ \$config'acl_groups' = true; \$config'acl_group_prefix' = 'group:'; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER"HTTP_HOST" ?? null) . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER"HTTP_HOST" ?? null) . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/calendar.inc.php.tpl
Changed
@@ -24,7 +24,7 @@ \$config'calendar_resources_driver' = 'ldap'; - \$config'calendar_resources_directory' = + \$config'calendar_resources_directory' = array( 'name' => 'Kolab Resources', 'hosts' => 'localhost', 'port' => 389, @@ -39,11 +39,11 @@ 'search_filter' => '(&(objectClass=inetOrgPerson)(mail=%fu))', 'ldap_version' => 3, 'filter' => '$ldap_resource_filter', - 'search_fields' => 'cn', - 'sort' => 'cn', + 'search_fields' => array('cn'), + 'sort' => array('cn'), 'scope' => 'sub', 'fuzzy_search' => true, - 'fieldmap' => + 'fieldmap' => array( // Internal => LDAP 'name' => 'cn', 'email' => 'mail', @@ -54,18 +54,20 @@ // these mappings are required for owner display 'phone' => 'telephoneNumber', 'mobile' => 'mobile', - , + ), - 'class_type_map' => + 'class_type_map' => array( 'kolabsharedfolder' => 'resource', 'groupofuniquenames' => 'collection', - , + ), - 'groups' => + 'groups' => array( 'name_attr' => 'cn', - , - ; + ), + ); - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/config.inc.php.tpl
Changed
@@ -1,10 +1,10 @@ <?php - \$config = ; + \$config = array(); \$config'db_dsnw' = '$mysql_uri'; - \$config'des_key' = "$des_key"; \$config'session_domain' = ''; + \$config'des_key' = "$des_key"; \$config'username_domain' = '$primary_domain'; \$config'use_secure_urls' = true; \$config'assets_path' = '/roundcubemail/assets/'; @@ -13,20 +13,20 @@ \$config'mail_domain' = ''; // IMAP Server Settings - \$config'default_host' = 'ssl://localhost'; - \$config'default_port' = 9993; + \$config'default_host' = 'tls://localhost'; + \$config'default_port' = 143; \$config'imap_delimiter' = '/'; \$config'imap_force_lsub' = true; // IMAP Connection TLS settings, adjust for Production // Required for PHP >= 5.6 - \$config'imap_conn_options' = - 'ssl' => + \$config'imap_conn_options' = Array( + 'ssl' => Array( 'verify_peer_name' => false, 'verify_peer' => false, 'allow_self_signed' => true - - ; + ) + ); // Caching and storage settings \$config'imap_cache' = 'db'; @@ -40,17 +40,17 @@ \$config'smtp_port' = 587; \$config'smtp_user' = '%u'; \$config'smtp_pass' = '%p'; - \$config'smtp_helo_host' = \$_SERVER'HTTP_HOST' ?? null; + \$config'smtp_helo_host' = \$_SERVER"HTTP_HOST"; // SMTP Connection TLS settings, adjust for Production // Required for PHP >= 5.6 - \$config'smtp_conn_options' = - 'ssl' => + \$config'smtp_conn_options' = Array( + 'ssl' => Array( 'verify_peer_name' => false, 'verify_peer' => false, 'allow_self_signed' => true - - ; + ) + ); // LDAP Settings \$config'ldap_cache' = 'db'; @@ -66,7 +66,7 @@ \$config'mdn_use_from' = true; // Plugins - \$config'plugins' = + \$config'plugins' = array( 'kolab_auth', 'acl', 'archive', @@ -88,7 +88,8 @@ 'tasklist', // contextmenu must be after kolab_addressbook (#444) 'contextmenu', - ; + ); + // Do not show deleted messages, mark deleted messages as read, // and flag them as deleted instead of moving them to the Trash @@ -109,7 +110,7 @@ \$config'spellcheck_ignore_caps' = true; \$config'spellcheck_ignore_nums' = true; \$config'spellcheck_ignore_syms' = true; - \$config'spellcheck_languages' = + \$config'spellcheck_languages' = array( 'da' => 'Dansk', 'de' => 'Deutsch', 'en' => 'English', @@ -120,11 +121,13 @@ 'pt' => 'Português', 'ru' => 'Русский', 'sv' => 'Svenska' - ; + ); \$config'undo_timeout' = 10; + \$config'upload_progress' = 2; \$config'address_template' = '{street}<br/>{locality} {zipcode}<br/>{country} {region}'; - \$config'mail_read_time' = 0; + \$config'preview_pane' = true; + \$config'preview_pane_mark_read' = 0; \$config'autoexpand_threads' = 2; \$config'top_posting' = 0; @@ -133,30 +136,28 @@ \$config'mdn_default' = false; \$config'dsn_default' = false; \$config'reply_same_folder' = false; - \$config'htmleditor' = 0; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } // Re-apply mandatory settings here. + \$config'debug_level' = 1; \$config'devel_mode' = false; \$config'log_driver' = 'file'; \$config'log_date_format' = 'd-M-Y H:i:s,u O'; \$config'syslog_id' = 'roundcube'; \$config'syslog_facility' = LOG_USER; - + \$config'smtp_log' = false; + \$config'log_logins' = true; + \$config'log_session' = false; \$config'sql_debug' = false; \$config'memcache_debug' = false; \$config'imap_debug' = false; \$config'ldap_debug' = false; \$config'smtp_debug' = false; - \$config'smtp_log' = false; - \$config'log_logins' = true; - \$config'log_session' = false; - \$config'skin' = '$skin'; \$config'skin_include_php' = false; \$config'mime_magic' = null; @@ -169,15 +170,16 @@ \$config'archive_mbox' = 'Archive'; // The Kolab daemon by default creates 'Spam' \$config'junk_mbox' = 'Spam'; + \$config'default_folders' = array('INBOX', 'Drafts', 'Sent', 'Spam', 'Trash', 'Archive'); \$config'address_book_type' = 'ldap'; \$config'autocomplete_min_length' = 3; \$config'autocomplete_threads' = 0; \$config'autocomplete_max' = 15; - \$config'ldap_public' = - 'kolab_addressbook' => + \$config'ldap_public' = array( + 'kolab_addressbook' => array( 'name' => 'Global Address Book', - 'hosts' => 'localhost', + 'hosts' => Array('localhost'), 'port' => 389, 'use_tls' => false, 'base_dn' => '$ldap_user_base_dn', @@ -189,12 +191,12 @@ 'search_bind_pw' => '$ldap_service_bind_pw', 'search_filter' => '(&(objectClass=inetOrgPerson)(mail=%fu))', 'writable' => false, - 'LDAP_Object_Classes' => "top", "inetOrgPerson", - 'required_fields' => "cn", "sn", "mail", + 'LDAP_Object_Classes' => array("top", "inetOrgPerson"), + 'required_fields' => array("cn", "sn", "mail"), 'LDAP_rdn' => 'uid', - 'ldap_version' => 3, - 'search_fields' => 'displayname', 'mail', - 'sort' => 'displayname', 'sn', 'givenname', 'cn', + 'ldap_version' => 3, // using LDAPv3 + 'search_fields' => array('displayname', 'mail'), + 'sort' => array('displayname', 'sn', 'givenname', 'cn'), 'scope' => 'sub', 'filter' => '(objectClass=inetOrgPerson)', 'vlv' => false, @@ -202,42 +204,46 @@ 'fuzzy_search' => true, 'sizelimit' => '0', 'timelimit' => '0', - 'fieldmap' => - // Roundcube => LDAP - 'name' => 'displayName', - 'surname' => 'sn', - 'firstname' => 'givenName', - 'middlename' => 'initials', - 'email:primary' => 'mail', - 'email:alias' => 'alias', - 'email:personal' => 'mailalternateaddress', - 'phone:main' => 'telephoneNumber', - 'phone:work' => 'alternateTelephoneNumber', - 'phone:mobile' => 'mobile', - 'phone:work2' => 'blackberry', - 'jobtitle' => 'title', - 'manager' => 'manager', - 'assistant' => 'secretary', - 'photo' => 'jpegphoto' - , - 'groups' => - 'base_dn' => '$ldap_group_base_dn', - 'filter' => '(&' . '$ldap_group_filter' . '(mail=*))', - 'object_classes' => "top", "groupOfUniqueNames", - 'member_attr' => 'uniqueMember', - , - , - ; - - \$config'autocomplete_addressbooks' = + 'fieldmap' => Array( + // Roundcube => LDAP + 'name' => 'displayName', + 'surname' => 'sn', + 'firstname' => 'givenName', + 'middlename' => 'initials', + 'email:primary' => 'mail', + 'email:alias' => 'alias', + 'email:personal' => 'mailalternateaddress', + 'phone:main' => 'telephoneNumber', + 'phone:work' => 'alternateTelephoneNumber', + 'phone:mobile' => 'mobile', + 'phone:work2' => 'blackberry', + 'jobtitle' => 'title', + 'manager' => 'manager', + 'assistant' => 'secretary', + 'photo' => 'jpegphoto' + ), + 'groups' => Array( + 'base_dn' => '$ldap_group_base_dn', + 'filter' => '(&' . '$ldap_group_filter' . '(mail=*))', + 'object_classes' => Array("top", "groupOfUniqueNames"), + 'member_attr' => 'uniqueMember', + ), + ), + ); + + \$config'autocomplete_addressbooks' = Array( 'kolab_addressbook' - ; + ); \$config'autocomplete_single' = true; - \$config'kolab_http_request' = + \$config'htmleditor' = 0; + + \$config'kolab_http_request' = Array( 'ssl_verify_host' => false, 'ssl_verify_peer' => false, - ; + ); @include('/etc/roundcubemail/kolab_syncroton.inc.php'); + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/kolab_addressbook.inc.php.tpl
Changed
@@ -16,3 +16,5 @@ // %n - Folder name // %i - Folder UUID \$config'kolab_addressbook_carddav_url' = 'http://%h/iRony/addressbooks/%u/%i'; + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/kolab_auth.inc.php.tpl
Changed
@@ -2,36 +2,36 @@ // The id of the LDAP address book (which refers to the rcmail_config'ldap_public') // or complete addressbook definition array. - \$config'kolab_auth_addressbook' = - 'name' => 'Kolab Auth', - 'hosts' => 'localhost', - 'port' => 389, - 'use_tls' => false, - 'user_specific' => false, - 'base_dn' => '$ldap_user_base_dn', - 'bind_dn' => '$ldap_service_bind_dn', - 'bind_pass' => '$ldap_service_bind_pw', - 'writable' => false, - 'ldap_version' => 3, - 'fieldmap' => - 'name' => 'displayname', - 'email' => 'mail', - 'email:alias' => 'alias', - 'role' => 'nsroledn', - , - 'sort' => 'displayname', - 'scope' => 'sub', - 'filter' => '(objectClass=*)', - 'fuzzy_search' => true, - 'sizelimit' => '0', - 'timelimit' => '0', - 'groups' => - 'base_dn' => '$ldap_group_base_dn', - 'filter' => '$ldap_group_filter', - 'object_classes' => 'top', 'groupOfUniqueNames', - 'member_attr' => 'uniqueMember', - , - ; + \$config'kolab_auth_addressbook' = Array( + 'name' => 'Kolab Auth', + 'hosts' => Array('localhost'), + 'port' => 389, + 'use_tls' => false, + 'user_specific' => false, + 'base_dn' => '$ldap_user_base_dn', + 'bind_dn' => '$ldap_service_bind_dn', + 'bind_pass' => '$ldap_service_bind_pw', + 'writable' => false, + 'ldap_version' => 3, // using LDAPv3 + 'fieldmap' => Array( + 'name' => 'displayname', + 'email' => 'mail', + 'email:alias' => 'alias', + 'role' => 'nsroledn', + ), + 'sort' => 'displayname', + 'scope' => 'sub', + 'filter' => '(objectClass=*)', + 'fuzzy_search' => true, + 'sizelimit' => '0', + 'timelimit' => '0', + 'groups' => Array( + 'base_dn' => '$ldap_group_base_dn', + 'filter' => '$ldap_group_filter', + 'object_classes' => Array('top', 'groupOfUniqueNames'), + 'member_attr' => 'uniqueMember', + ), + ); // This will overwrite defined filter @@ -45,7 +45,7 @@ \$config'kolab_auth_alias' = 'alias'; \$config'kolab_auth_email' = 'email'; - if (preg_match('/\/helpdesk-login\//', \$_SERVER'REQUEST_URI' ?? '')) { + if (preg_match('/\/helpdesk-login\//', \$_SERVER"REQUEST_URI") ) { // Login and password of the admin user. Enables "Login As" feature. \$config'kolab_auth_admin_login' = '$imap_admin_login'; @@ -63,6 +63,8 @@ // which adds privilege to login as another user. \$config'kolab_auth_group' = 'Kolab Helpdesk'; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER"HTTP_HOST" ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER"HTTP_HOST" ?? ''). '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/kolab_delegation.inc.php.tpl
Changed
@@ -11,3 +11,4 @@ // Remove all user identities which do not match the users primary or alias // addresses and delegators addresses \$config'kolab_delegation_purge_identities' = false; +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/kolab_files.inc.php.tpl
Changed
@@ -4,7 +4,7 @@ \$config'kolab_files_url' = '/chwala/'; // List of files list columns. Available are: name, size, mtime, type -\$config'kolab_files_list_cols' = 'name', 'mtime', 'size'; +\$config'kolab_files_list_cols' = array('name', 'mtime', 'size'); // Name of the column to sort files list by \$config'kolab_files_sort_col' = 'name'; @@ -25,3 +25,4 @@ // The LDAP search filter will be combined with search queries \$config'kolab_files_users_filter' = ''; +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/kolab_folders.inc.php.tpl
Changed
@@ -14,6 +14,8 @@ \$config'kolab_folders_mail_outbox' = ''; \$config'kolab_folders_mail_wastebasket' = 'Trash'; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/libkolab.inc.php.tpl
Changed
@@ -2,8 +2,8 @@ \$config'kolab_freebusy_server' = '/freebusy'; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } \$config'kolab_cache' = true; @@ -12,3 +12,5 @@ \$config'kolab_ssl_verify_peer' = false; \$config'kolab_use_subscriptions' = true; + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/managesieve.inc.php.tpl
Changed
@@ -8,7 +8,7 @@ \$config'managesieve_default' = '/etc/dovecot/sieve/global'; \$config'managesieve_mbox_encoding' = 'UTF-8'; \$config'managesieve_replace_delimiter' = ''; - \$config'managesieve_disabled_extensions' = ; + \$config'managesieve_disabled_extensions' = array(); \$config'managesieve_debug' = false; \$config'managesieve_vacation' = 1; @@ -16,14 +16,16 @@ \$config'managesieve_kolab_master' = true; // ManageSieve Connection TLS settings, adjust for Production - \$config'managesieve_conn_options' = - 'ssl' => + \$config'managesieve_conn_options' = Array( + 'ssl' => Array( 'verify_peer_name' => false, 'verify_peer' => false, 'allow_self_signed' => true - - ; + ) + ); - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/mimetypes.php.tpl
Changed
@@ -1,6 +1,6 @@ <?php -return +return array( 'xls' => 'application/vnd.ms-excel', 'xlm' => 'application/vnd.ms-excel', 'xla' => 'application/vnd.ms-excel', @@ -44,4 +44,6 @@ 'rar' => 'application/x-rar-compressed', 'vcf' => 'text/vcard', 'ics' => 'text/calendar', -; +); + +?> \ No newline at end of file
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/password.inc.php.tpl
Changed
@@ -26,7 +26,7 @@ // ----------------------------------- // LDAP server name to connect to. // You can provide one or several hosts in an array in which case the hosts are tried from left to right. - // Exemple: 'ldap1.exemple.com', 'ldap2.exemple.com'; + // Exemple: array('ldap1.exemple.com', 'ldap2.exemple.com'); // Default: 'localhost' \$config'password_ldap_host' = 'localhost'; @@ -148,6 +148,8 @@ // Whenever the password is changed, the attribute will be updated if set \$config'password_ldap_samba_lchattr' = ''; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/recipient_to_contact.inc.php.tpl
Changed
@@ -1,3 +1,4 @@ <?php - \$config'recipient_to_contact_addressbooks' = ; + \$config'recipient_to_contact_addressbooks' = array(); \$config'recipient_to_contact_enabled_by_default' = true; +?> \ No newline at end of file
View file
pykolab-0.9.0.tar.gz/share/templates/roundcubemail/terms.inc.php.tpl
Changed
@@ -15,6 +15,8 @@ // always request terms agreement after login \$config'terms_always' = false; - if (file_exists(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__))) { - include_once(RCUBE_CONFIG_DIR . '/' . (\$_SERVER'HTTP_HOST' ?? '') . '/' . basename(__FILE__)); + if (file_exists(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__))) { + include_once(RCUBE_CONFIG_DIR . '/' . \$_SERVER"HTTP_HOST" . '/' . basename(__FILE__)); } + +?>
View file
pykolab-0.9.0.tar.gz/ucs/kolab_sieve.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/ucs/listener.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/wallace.py
Changed
@@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/python # # Copyright 2010-2013 Kolab Systems AG (http://www.kolabsys.com) #
View file
pykolab-0.9.0.tar.gz/wallace/__init__.py
Changed
@@ -154,50 +154,6 @@ break -class WallaceSMTPChannel(smtpd.SMTPChannel): - callback = None - - def __init__(self, connection, address, callback): - self.callback = callback - if conf.debuglevel > 8: - smtpd.DEBUGSTREAM = pykolab.logger.StderrToLogger(log) - super().__init__(self, connection, address) - - def data_header(self, mailfrom, rcpttos): - return "X-Kolab-From: " + mailfrom + "\r\n" + \ - "X-Kolab-To: " + ', '.join(rcpttos) + "\r\n" - - def process_message(self, peer, mailfrom, rcpttos, data, **kwargs): - """ - We have retrieved the message. This should be as fast as possible, - and not ever block. - """ - - header = self.data_header(mailfrom, rcpttos) - - try: - (fp, filename) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/") - - # @TODO: and add line separator (\n or \r\n?) - # we should make sure there's only one line separator between - # kolab headers and the original message (data) - os.write(fp, bytes(header.encode("UTF-8"))) - os.write(fp, data) - except Exception as errmsg: - log.warning( - _l("failed to write to file: %s" % (errmsg)) - ) - finally: - os.close(fp) - - self.callback(filename) - - return "250 OK Message %s queued" % (filename) - - def loop(self): - asyncore.loop() - - class WallaceDaemon: heartbeat = None timer = None @@ -389,22 +345,25 @@ time.sleep(0.5) pair = s.accept() + log.debug( + _l("Accepted connection %r with address %r") % ( + pair if pair is not None else (None, None) + ), + level=8 + ) if pair is not None: + self.current_connections += 1 connection, address = pair - log.debug( - _l("Accepted connection %r with address %r") % ( - connection, address - ), - level=8 - ) - def callback(filename): - self.pool.apply_async(pickup_message, (filename, (self.modules))) + _smtpd = smtpd + # Set DEBUGSTREAM of smtpd to log to pykolab logger + if conf.debuglevel > 8: + _smtpd.DEBUGSTREAM = pykolab.logger.StderrToLogger(log) - self.current_connections += 1 - WallaceSMTPChannel(connection, address, callback).loop() - self.current_connections -= 1 + log.debug(_l("Creating SMTPChannel for accepted message"), level=8) + _smtpd.SMTPChannel(self, connection, address) + asyncore.loop() else: log.error(_l("Socket accepted, but (conn, address) tuple is None.")) @@ -419,6 +378,13 @@ self.timer.cancel() self.timer.join() + # pylint: disable=no-self-use + def data_header(self, mailfrom, rcpttos): + COMMASPACE = ', ' + + return "X-Kolab-From: " + mailfrom + "\r\n" + \ + "X-Kolab-To: " + COMMASPACE.join(rcpttos) + "\r\n" + def pickup_spool_messages(self, sync=False): # Mind you to include the trailing slash pickup_path = '/var/spool/pykolab/wallace/' @@ -508,6 +474,30 @@ self.current_connections -= 1 + def process_message(self, peer, mailfrom, rcpttos, data): + """ + We have retrieved the message. This should be as fast as possible, + and not ever block. + """ + + header = self.data_header(mailfrom, rcpttos) + + (fp, filename) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/") + + # @TODO: and add line separator (\n or \r\n?) + # we should make sure there's only one line separator between + # kolab headers and the original message (data) + os.write(fp, header) + os.write(fp, data) + os.close(fp) + + log.debug(_l("Started processing accepted message %s") % filename, level=8) + self.pool.apply_async(pickup_message, (filename, (self.modules))) + + self.current_connections -= 1 + + return "250 OK Message %s queued" % (filename) + def reload_config(self, *args, **kwargs): pass
View file
pykolab-0.9.0.tar.gz/wallace/module_footer.py
Changed
@@ -85,17 +85,16 @@ if not os.path.isdir(mybasepath): os.makedirs(mybasepath) - for stage in 'incoming': + for stage in 'incoming', 'ACCEPT': if not os.path.isdir(os.path.join(mybasepath, stage)): os.makedirs(os.path.join(mybasepath, stage)) - # FIXME: This section of the code is probably not needed since we do not ACCEPT the mail anymore if 'stage' in kw: log.debug(_("Issuing callback after processing to stage %s") % (kw'stage'), level=8) log.debug(_("Testing cb_action_%s()") % (kw'stage'), level=8) if hasattr(modules, 'cb_action_%s' % (kw'stage')): log.debug(_("Attempting to execute cb_action_%s()") % (kw'stage'), level=8) - exec('modules.cb_action_%s(%r, %r)' % (kw'stage','footer',filepath)) + exec('modules.cb_action_%s(%r, %r)' % (kw'stage','optout',filepath)) return log.debug(_("Executing module footer for %r, %r") % (args, kw), level=8) @@ -113,28 +112,13 @@ footer = {} footer_position = conf.get('wallace', 'footer_position') - footer_html_file = conf.get('wallace', 'footer_html') or '' - footer_text_file = conf.get('wallace', 'footer_text') or '' - sender_exceptions = conf.get('wallace', 'footer_sender_exceptions') - - if sender_exceptions: - sender = message.get("X-Kolab-From") - log.debug("Checking sender %r" % (sender), level=8) - - if sender: - for item in sender_exceptions.split(','): - item = item.strip() - regex = '@' + item.replace('*', '^@+').replace('.', '\\.') + '$' - if item == 'NONE': - regex = '^<>$' - - if re.search(regex, sender, re.IGNORECASE): - log.debug("Sender matches %s, pass along" % (item), level=8) - return filepath + footer_html_file = conf.get('wallace', 'footer_html') + footer_text_file = conf.get('wallace', 'footer_text') if not os.path.isfile(footer_text_file) and not os.path.isfile(footer_html_file): - log.warning(_("No contents configured for footer module, pass along")) - return filepath + log.warning(_("No contents configured for footer module")) + exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', filepath)) + return if os.path.isfile(footer_text_file): footer'plain' = open(footer_text_file, 'r').read() @@ -147,7 +131,8 @@ footer'html' = '<p>' + footer'plain' + '</p>' if footer'plain' == "" and footer'html' == "<p></p>": - return filepath + exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', filepath)) + return footer_added = False @@ -157,7 +142,8 @@ pass if _footer_added == "YES": - return filepath + exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', filepath)) + return for part in message.walk(): disposition = None @@ -188,15 +174,12 @@ footer_added = set_part_content(part, content) if footer_added: + log.debug("Footer attached.") message.add_header("X-Wallace-Footer", "YES") - (fp, new_filepath) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/footer/incoming") - - log.debug("Footer attached, saving mail to %s and passing along" % (new_filepath), level=8) - - os.write(fp, message.as_string()) - os.close(fp) - os.unlink(filepath) - filepath = new_filepath + (fp, new_filepath) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/footer/ACCEPT") + os.write(fp, message.as_string()) + os.close(fp) + os.unlink(filepath) - return filepath + exec('modules.cb_action_%s(%r, %r)' % ('ACCEPT','footer', new_filepath))
View file
pykolab-0.9.0.tar.gz/wallace/module_gpgencrypt.py
Changed
@@ -21,6 +21,7 @@ import tempfile import time +from email import message_from_string from email.mime.base import MIMEBase from email.mime.text import MIMEText from email.parser import Parser
View file
pykolab-0.9.0.tar.gz/wallace/module_invitationpolicy.py
Changed
@@ -34,7 +34,7 @@ import traceback import re -from email import message_from_bytes +from email import message_from_string from email.parser import Parser from email.utils import formataddr from email.utils import getaddresses @@ -706,15 +706,13 @@ if local_domains is not None: local_domains = list(set(local_domains.keys())) - domain = email_address.split('@')1 - - if not domain in local_domains: + if not email_address.split('@')1 in local_domains: user_dn_from_email_address.cacheemail_address = None return None log.debug(_("Checking if email address %r belongs to a local user") % (email_address), level=8) - user_dn = auth.find_user_dn(email_address, True, domain) + user_dn = auth.find_user_dn(email_address, True) if isinstance(user_dn, string_types): log.debug(_("User DN: %r") % (user_dn), level=8) @@ -809,7 +807,7 @@ if not imap_proxy_auth(user_rec): return result - folders = imap.get_metadata('*', FOLDER_TYPE_ANNOTATION) + folders = imap.get_metadata('*') log.debug( _("List %r folders for user %r: %r") % ( @@ -822,17 +820,16 @@ (ns_personal, ns_other, ns_shared) = imap.namespaces() - _type = _type.encode('utf-8') _folders = {} # Filter the folders by type relevance for folder, metadata in folders.items(): - key = ('/shared' + FOLDER_TYPE_ANNOTATION).encode('utf-8') + key = '/shared' + FOLDER_TYPE_ANNOTATION if key in metadata: if metadatakey.startswith(_type): _foldersfolder = metadata - key = ('/private' + FOLDER_TYPE_ANNOTATION).encode('utf-8') + key = '/private' + FOLDER_TYPE_ANNOTATION if key in metadata: if metadatakey.startswith(_type): _foldersfolder = metadata @@ -860,25 +857,33 @@ if len(_ns for _ns in ns_shared if folder.startswith(_ns)) > 0: continue - key = ('/private' + FOLDER_TYPE_ANNOTATION).encode('utf-8') - if key in metadata and metadatakey.startswith(_type): - # store default,private and confidential folders in user record - if metadatakey.endswith(b'.default'): - if '_default_folder' not in user_rec: - user_rec'_default_folder' = folder - elif metadatakey.endswith(b'.confidential'): + key = '/shared' + FOLDER_TYPE_ANNOTATION + if key in metadata: + if metadatakey.startswith(_type): + result.append(folder) + + key = '/private' + FOLDER_TYPE_ANNOTATION + if key in metadata: + if metadatakey.startswith(_type): + result.append(folder) + + # store default folder in user record + if metadatakey.endswith('.default'): + user_rec'_default_folder' = folder + continue + + # store private and confidential folders in user record + if metadatakey.endswith('.confidential'): if '_confidential_folder' not in user_rec: user_rec'_confidential_folder' = folder - elif metadatakey.endswith(b'.private'): + + continue + + if metadatakey.endswith('.private'): if '_private_folder' not in user_rec: user_rec'_private_folder' = folder - result.append(folder) - continue - - key = ('/shared' + FOLDER_TYPE_ANNOTATION).encode('utf-8') - if key in metadata and metadatakey.startswith(_type): - result.append(folder) + continue # cache with user record user_rec'_imap_folders' = result @@ -909,16 +914,16 @@ res, data = imap.imap.m.fetch(num, '(UID RFC822)') try: - msguid = re.search(r"\WUID (\d+)", data00.decode('utf-8')).group(1) + msguid = re.search(r"\WUID (\d+)", data00).group(1) except Exception: log.error(_("No UID found in IMAP response: %r") % (data00)) continue try: if type == 'task': - event = todo_from_message(message_from_bytes(data01)) + event = todo_from_message(message_from_string(data01)) else: - event = event_from_message(message_from_bytes(data01)) + event = event_from_message(message_from_string(data01)) # find instance in a recurring series if recurrence_id and (event.is_recurring() or event.has_exceptions() or event.get_recurrence_id()): @@ -976,7 +981,7 @@ res, data = imap.imap.m.fetch(num, '(RFC822)') try: - event = event_from_message(message_from_bytes(data01)) + event = event_from_message(message_from_string(data01)) except Exception as errmsg: log.error(_("Failed to parse event from message %s/%s: %r") % (folder, num, errmsg)) continue @@ -1050,7 +1055,7 @@ def get_lock_key(user, uid): - return hashlib.md5(("%s/%s" % (user'mail', uid)).encode('utf-8')).hexdigest() + return hashlib.md5("%s/%s" % (user'mail', uid)).hexdigest() def update_object(object, user_rec, master=None): @@ -1118,7 +1123,7 @@ imap.folder_utf7(targetfolder), None, None, - saveobj.to_message(creator="Kolab Server <wallace@localhost>").as_string().encode('utf-8') + saveobj.to_message(creator="Kolab Server <wallace@localhost>").as_string() ) return result
View file
pykolab-0.9.0.tar.gz/wallace/module_resources.py
Changed
@@ -22,7 +22,7 @@ import datetime import sys -from email import message_from_bytes +from email import message_from_string from email.parser import Parser from email.utils import formataddr from email.utils import getaddresses @@ -665,7 +665,7 @@ typ, data = imap.imap.m.fetch(num, '(RFC822)') try: - event = event_from_message(message_from_bytes(data01)) + event = event_from_message(message_from_string(data01)) # pylint: disable=broad-except except Exception as errmsg: log.error(_("Failed to parse event from message %s/%s: %r") % (mailbox, num, errmsg)) @@ -915,14 +915,14 @@ typ, data = imap.imap.m.fetch(num, '(UID RFC822)') try: - msguid = re.search(r"\WUID (\d+)", data00.decode('utf-8')).group(1) + msguid = re.search(r"\WUID (\d+)", data00).group(1) # pylint: disable=broad-except except Exception: log.error(_("No UID found in IMAP response: %r") % (data00)) continue try: - event = event_from_message(message_from_bytes(data01)) + event = event_from_message(message_from_string(data01)) # pylint: disable=broad-except except Exception as e: log.error(_("Failed to parse event from message %s/%s: %r") % (mailbox, num, e)) @@ -977,14 +977,14 @@ typ, data = imap.imap.m.fetch(num, '(UID RFC822)') try: - msguid = re.search(r"\WUID (\d+)", data00.decode('utf-8')).group(1) + msguid = re.search(r"\WUID (\d+)", data00).group(1) # pylint: disable=broad-except except Exception: log.error(_("No UID found in IMAP response: %r") % (data00)) continue try: - event = event_from_message(message_from_bytes(data01)) + event = event_from_message(message_from_string(data01)) # find instance in a recurring series if recurrence_id and (event.is_recurring() or event.has_exceptions()):
View file
pykolab-0.9.0.tar.gz/wallace/modules.py
Changed
@@ -24,6 +24,7 @@ import sys import time +from email import message_from_string from email.message import Message from email.mime.base import MIMEBase from email.mime.message import MIMEMessage @@ -139,11 +140,6 @@ # NOTE: Use "127.0.0.1" here for IPv6 (see also the service # definition in master.cf). - # Sanity check. We run into this if we end up reading an empty file, because sender ends up being an empty list - if not sender or not recipients: - log.warning(_("Sending email failed from %r to %r") % (sender, recipients)) - return False - sl = pykolab.logger.StderrToLogger(log) smtplib.stderr = sl @@ -152,14 +148,6 @@ if conf.debuglevel > 8: smtp.set_debuglevel(1) - # Workaround smtplib's issue with 8bit encoded messages - if isinstance(msg, str): - msg = msg.encode('utf-8') - - # Sender can't be a list - if isinstance(sender, list): - sender = sender0 - success = False attempt = 1
View file
pykolab.dsc
Changed
@@ -2,8 +2,7 @@ Source: pykolab Binary: pykolab, kolab-cli, kolab-conf, kolab-saslauthd, kolab-server, kolab-telemetry, kolab-xml, wallace Architecture: all -Version: 0.9.0.7-1~kolab1 -DEBTRANSFORM-RELEASE: 1 +Version: 0.9.0-0~kolab1 Maintainer: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org
View file
release.sh
Deleted
@@ -1,15 +0,0 @@ -#!/bin/bash - -./buildtarball.sh - -# Autobump the version -CURRENT_VERSION=$(grep '^Version: ' ./*.spec | sed 's/Version: //') -NEW_VERSION=$(echo "$CURRENT_VERSION" | awk -F. '/0-9+\./{$NF++;print}' OFS=.) -echo "Bumping from $CURRENT_VERSION to $NEW_VERSION" - -sed -i "s/$CURRENT_VERSION/$NEW_VERSION/" debian.changelog -sed -i "s/^Version:.*/Version: $NEW_VERSION-1~kolab1/" ./*.dsc -sed -i "s/^Version:.*/Version: $NEW_VERSION/" ./*.spec - -osc ci -m "New release $NEW_VERSION" -osc sr Kolab:16 --yes -m "New release $NEW_VERSION"
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.