Projects
Kolab:16
pykolab
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 9
View file
pykolab.spec
Changed
@@ -28,18 +28,15 @@ Summary: Kolab Groupware Solution Name: pykolab -Version: 0.8.0 -Release: 1%{?dist} +Version: 0.8.1 +Release: 2%{?dist} License: GPLv3+ Group: Applications/System URL: http://kolab.org/ -Source0: pykolab-0.8.0.tar.gz +Source0: pykolab-0.8.1.tar.gz Source1: pykolab.logrotate -Patch0001: 0001-Fix-mistake-in-manticore-configuration-template.patch -Patch0002: 0002-Fix-default-template-for-Guam.patch - BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildArch: noarch %if 0%{?suse_version} @@ -54,7 +51,14 @@ BuildRequires: gettext BuildRequires: glib2-devel BuildRequires: intltool +%if 0%{?suse_version} +BuildRequires: python-mysql +%else +BuildRequires: MySQL-python +%endif BuildRequires: python +BuildRequires: python-augeas +BuildRequires: python-gnupg BuildRequires: python-icalendar BuildRequires: python-kolab BuildRequires: python-kolabformat @@ -205,6 +209,7 @@ %else Requires: MySQL-python %endif +Requires: python-gnupg Requires: python-icalendar >= 3.0 Requires: %{name}-xml = %{version}-%{release} @@ -214,9 +219,6 @@ %prep %setup -q -%patch0001 -p1 -%patch0002 -p1 - %build autoreconf -v || automake --add-missing && autoreconf -v %configure @@ -542,6 +544,9 @@ %attr(0700,%{kolab_user},%{kolab_group}) %dir %{_var}/spool/pykolab/wallace %changelog +* Wed Mar 09 2016 Timotheus Pokorra <tp@tbits.net> - 0.8.1-2 +- wallace requires python-gnupg to be installed. avoid ImportError: No module named gnupg + * Wed Oct 14 2015 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 0.7.16-1 - New upstream release 0.7.16
View file
0001-Fix-mistake-in-manticore-configuration-template.patch
Deleted
@@ -1,25 +0,0 @@ -From 6769c10da417e65698f71e0f34d0b15a597a3641 Mon Sep 17 00:00:00 2001 -From: "Jeroen van Meeuwen (Kolab Systems)" <vanmeeuwen@kolabsys.com> -Date: Tue, 19 Jan 2016 12:55:44 +0100 -Subject: [PATCH 1/2] Fix mistake in manticore configuration template - ---- - share/templates/manticore.js.tpl | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/share/templates/manticore.js.tpl b/share/templates/manticore.js.tpl -index 90a75ec..c3a31bd 100644 ---- a/share/templates/manticore.js.tpl -+++ b/share/templates/manticore.js.tpl -@@ -17,7 +17,7 @@ module.exports = { - CHWALA_SERVER: 'http://$fqdn/chwala/api/document', - ROUNDCUBE_SERVER: 'http://$fqdn/roundcubemail', - -- AUTH_ENCRYPTION_KEY: '$auth_secret', -+ AUTH_ENCRYPTION_KEY: '$auth_key', - - LDAP_SERVER: 'ldap://$server_host:389', - LDAP_BASE: '$user_base_dn', --- -2.4.3 -
View file
0002-Fix-default-template-for-Guam.patch
Deleted
@@ -1,25 +0,0 @@ -From 19912492bdadb1f3c90d0ee06ba5119193ced726 Mon Sep 17 00:00:00 2001 -From: "Jeroen van Meeuwen (Kolab Systems)" <vanmeeuwen@kolabsys.com> -Date: Mon, 25 Jan 2016 13:58:17 +0100 -Subject: [PATCH 2/2] Fix default template for Guam - ---- - share/templates/guam.sys.config.tpl | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/share/templates/guam.sys.config.tpl b/share/templates/guam.sys.config.tpl -index 2ad9456..955e17b 100644 ---- a/share/templates/guam.sys.config.tpl -+++ b/share/templates/guam.sys.config.tpl -@@ -18,7 +18,7 @@ - { - imap, [ - { port, 143 }, -- { imap_server, imap }, -+ { imap_server, imaps }, - { - rules, [ - { filter_groupware, [] } --- -2.4.3 -
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +pykolab (0.8.1-0~kolab1) unstable; urgency=low + + * Fix setup + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Tue, 23 Feb 2016 01:49:00 +0100 + pykolab (0.8.0-0~kolab2) unstable; urgency=low * Fix guam template
View file
debian.control
Changed
@@ -23,7 +23,6 @@ python-icalendar, python-minimal, python-nose, - python-support, univention-config-dev | bash Standards-Version: 3.9.3 Homepage: http://www.kolab.org
View file
debian.series
Changed
@@ -1,3 +1,1 @@ cyrus-imapd.conf-cert-paths.patch -p1 -0001-Fix-mistake-in-manticore-configuration-template.patch -p1 -0002-Fix-default-template-for-Guam.patch -p1
View file
pykolab-0.8.0.tar.gz/kolabd/__init__.py -> pykolab-0.8.1.tar.gz/kolabd/__init__.py
Changed
@@ -218,7 +218,7 @@ exitcode = 1 traceback.print_exc() print >> sys.stderr, _("Traceback occurred, please report a " + \ - "bug at http://bugzilla.kolabsys.com") + "bug at https://issues.kolab.org") except TypeError, errmsg: exitcode = 1 @@ -229,7 +229,7 @@ exitcode = 2 traceback.print_exc() print >> sys.stderr, _("Traceback occurred, please report a " + \ - "bug at http://bugzilla.kolabsys.com") + "bug at https://issues.kolab.org") sys.exit(exitcode)
View file
pykolab-0.8.0.tar.gz/pykolab/auth/ldap/__init__.py -> pykolab-0.8.1.tar.gz/pykolab/auth/ldap/__init__.py
Changed
@@ -1195,7 +1195,7 @@ try: self.ldap.simple_bind_s(bind_dn, bind_pw) self.bind = True - except ldap.SERVER_DOWN: + except ldap.SERVER_DOWN, errmsg: log.error(_("LDAP server unavailable: %r") % (errmsg)) log.error(_("%s") % (traceback.format_exc())) except ldap.INVALID_CREDENTIALS:
View file
pykolab-0.8.0.tar.gz/pykolab/imap/__init__.py -> pykolab-0.8.1.tar.gz/pykolab/imap/__init__.py
Changed
@@ -522,6 +522,9 @@ if not hasattr(self, 'domain'): self.domain == None + if self.domain == None and len(mailbox_base_name.split('@')) > 1: + self.domain = mailbox_base_name.split('@')[1] + if not self.domain == None: if conf.has_option(self.domain, "autocreate_folders"): _additional_folders = conf.get_raw( @@ -529,6 +532,25 @@ "autocreate_folders" ) + else: + from pykolab.auth import Auth + auth = Auth() + auth.connect() + + domains = auth.list_domains(self.domain) + + auth.disconnect() + + if len(domains.keys()) > 0: + if domains.has_key(self.domain): + primary = domains[self.domain] + + if conf.has_option(primary, "autocreate_folders"): + _additional_folders = conf.get_raw( + primary, + "autocreate_folders" + ) + if _additional_folders == None: if conf.has_option('kolab', "autocreate_folders"): _additional_folders = conf.get_raw(
View file
pykolab-0.8.0.tar.gz/pykolab/imap/cyrus.py -> pykolab-0.8.1.tar.gz/pykolab/imap/cyrus.py
Changed
@@ -25,12 +25,14 @@ import pykolab +from pykolab.constants import * from pykolab.imap import IMAP from pykolab.translate import _ log = pykolab.getLogger('pykolab.imap') conf = pykolab.getConf() + class Cyrus(cyruslib.CYRUS): """ Abstraction class for some common actions to do exclusively in Cyrus. @@ -70,14 +72,19 @@ self.server = hostname - self.uri = "%s://%s:%s" % (scheme,hostname,port) + self.uri = "%s://%s:%s" % (scheme, hostname, port) while 1: try: cyruslib.CYRUS.__init__(self, self.uri) break except cyruslib.CYRUSError: - log.warning(_("Could not connect to Cyrus IMAP server %r") % (self.uri)) + log.warning( + _("Could not connect to Cyrus IMAP server %r") % ( + self.uri + ) + ) + time.sleep(10) if conf.debuglevel > 8: @@ -134,17 +141,32 @@ """ cyruslib.CYRUS.login(self, *args, **kw) self.separator = self.SEP + try: + self._id() + except Exception, errmsg: + pass + + log.debug( + _("Continuing with separator: %r") % (self.separator), + level=8 + ) - log.debug(_("Continuing with separator: %r") % (self.separator), level=8) self.murder = False for capability in self.m.capabilities: if capability.startswith("MUPDATE="): - log.debug(_("Detected we are running in a Murder topology"), level=8) + log.debug( + _("Detected we are running in a Murder topology"), + level=8 + ) + self.murder = True if not self.murder: - log.debug(_("This system is not part of a murder topology"), level=8) + log.debug( + _("This system is not part of a murder topology"), + level=8 + ) def find_mailfolder_server(self, mailfolder): annotations = {} @@ -153,8 +175,13 @@ prefix = _mailfolder['path_parts'].pop(0) mbox = _mailfolder['path_parts'].pop(0) - if not _mailfolder['domain'] == None: - mailfolder = "%s%s%s@%s" % (prefix,self.separator,mbox,_mailfolder['domain']) + if _mailfolder['domain'] is not None: + mailfolder = "%s%s%s@%s" % ( + prefix, + self.separator, + mbox, + _mailfolder['domain'] + ) # TODO: Workaround for undelete if len(self.lm(mailfolder)) < 1: @@ -165,14 +192,20 @@ if not self.murder: return self.server - log.debug(_("Checking actual backend server for folder %s through annotations") % (mailfolder), level=8) + log.debug( + _("Checking actual backend server for folder %s " + + "through annotations") % ( + mailfolder + ), + level=8 + ) if self.mbox.has_key(mailfolder): log.debug( _( - "Possibly reproducing the find " + \ - "mailfolder server answer from " + \ - "previously detected and stored " + \ + "Possibly reproducing the find " + + "mailfolder server answer from " + + "previously detected and stored " + "annotation value: %r" ) % ( self.mbox[mailfolder] @@ -185,26 +218,53 @@ max_tries = 20 num_try = 0 + + annotation_path = "/shared/vendor/cmu/cyrus-imapd/server" + while 1: num_try += 1 - annotations = self._getannotation(mailfolder, "/vendor/cmu/cyrus-imapd/server") + annotations = self._getannotation( + mailfolder, + annotation_path + ) if annotations.has_key(mailfolder): break if max_tries <= num_try: - log.error(_("Could not get the annotations after %s tries.") % (num_try)) - annotations = { mailfolder: { '/shared/vendor/cmu/cyrus-imapd/server': self.server }} + log.error( + _("Could not get the annotations after %s tries.") % ( + num_try + ) + ) + + annotations = { + mailfolder: { + annotation_path: self.server + } + } + break - log.warning(_("No annotations for %s: %r") % (mailfolder,annotations)) + log.warning( + _("No annotations for %s: %r") % ( + mailfolder, + annotations + ) + ) time.sleep(1) - server = annotations[mailfolder]['/shared/vendor/cmu/cyrus-imapd/server'] + server = annotations[mailfolder][annotation_path] self.mbox[mailfolder] = server - log.debug(_("Server for INBOX folder %s is %s") % (mailfolder,server), level=8) + log.debug( + _("Server for INBOX folder %s is %s") % ( + mailfolder, + server + ), + level=8 + ) return server @@ -216,33 +276,68 @@ from pykolab import imap_utf7 return imap_utf7.decode(folder) + def _id(self, identity=None): + if identity is None: + identity = '("name" "Python/Kolab" "version" "%s")' % (__version__) + + typ, dat = self.m._simple_command('ID', identity) + res, dat = self.m._untagged_response(typ, dat, 'ID') + def _setquota(self, mailfolder, quota): """ Login to the actual backend server. """ server = self.find_mailfolder_server(mailfolder) - #print "server:", server - self.connect(self.uri.replace(self.server,server)) + self.connect(self.uri.replace(self.server, server)) + + log.debug( + _("Setting quota for folder %s to %s") % ( + mailfolder, + quota + ), + level=8
View file
pykolab-0.8.0.tar.gz/pykolab/setup/__init__.py -> pykolab-0.8.1.tar.gz/pykolab/setup/__init__.py
Changed
@@ -21,6 +21,7 @@ import sys import pykolab +from pykolab.translate import _ log = pykolab.getLogger('pykolab.setup') conf = pykolab.getConf() @@ -40,6 +41,36 @@ to_execute.append(sys.argv[arg_num].replace('-','_')) def run(self): + if os.path.isfile('/sys/fs/selinux/enforce'): + if os.access('/sys/fs/selinux/enforce', os.R_OK): + # Set a gentle default because strictly speaking, + # setup won't fail (run-time does) + enforce = "0" + + with open('/sys/fs/selinux/enforce', 'r') as f: + enforce = f.read() + + if enforce.strip() == "1": + log.fatal( + _("SELinux currently enforcing. Read " + \ + "https://git.kolab.org/u/1") + ) + + sys.exit(1) + + if os.path.isfile('/etc/selinux/config'): + if os.access('/etc/selinux/config', os.R_OK): + with open('/etc/selinux/config', 'r') as f: + for line in f: + if line.strip() == "SELINUX=enforcing": + log.fatal( + _("SELinux configured to enforce a " + \ + "policy on startup. Read " + \ + "https://git.kolab.org/u/1") + ) + + sys.exit(1) + components.execute('_'.join(to_execute)) if os.path.exists('/tmp/kolab-setup-my.cnf'):
View file
pykolab-0.8.0.tar.gz/pykolab/setup/setup_mta.py -> pykolab-0.8.1.tar.gz/pykolab/setup/setup_mta.py
Changed
@@ -442,6 +442,20 @@ myaugeas.save() myaugeas.close() + if os.path.isfile('/usr/lib/systemd/system/clamd@.service'): + from ConfigParser import SafeConfigParser + unitfile = SafeConfigParser() + unitfile.optionxform = str + unitfile.read('/usr/lib/systemd/system/clamd@.service') + if not unitfile.has_section('Install'): + unitfile.add_section('Install') + + if not unitfile.has_option('Install', 'WantedBy'): + unitfile.set('Install', 'WantedBy', 'multi-user.target') + + with open('/etc/systemd/system/clamd@.service', 'wb') as f: + unitfile.write(f) + amavisservice = 'amavisd.service' clamavservice = 'clamd@amavisd.service' if os.path.isfile('/usr/lib/systemd/system/amavis.service'):
View file
pykolab-0.8.0.tar.gz/pykolab/setup/setup_roundcube.py -> pykolab-0.8.1.tar.gz/pykolab/setup/setup_roundcube.py
Changed
@@ -176,7 +176,8 @@ if len(schema_files) > 0: break - break + if len(schema_files) > 0: + break for root, directories, filenames in os.walk(rcpath + 'plugins/calendar/drivers/kolab/'): for filename in filenames: @@ -192,6 +193,22 @@ if not schema_filepath in schema_files: schema_files.append(schema_filepath) + for root, directories, filenames in os.walk('/usr/share/doc/'): + directories.sort() + for directory in directories: + if directory.startswith("chwala"): + for nested_root, nested_directories, nested_filenames in os.walk(os.path.join(root, directory)): + for filename in nested_filenames: + if filename.startswith('mysql.initial') and filename.endswith('.sql'): + schema_filepath = os.path.join(nested_root,filename) + if not schema_filepath in schema_files: + schema_files.append(schema_filepath) + + if len(schema_files) > 0: + break + if len(schema_files) > 0: + break + if not os.path.isfile('/tmp/kolab-setup-my.cnf'): utils.multiline_message( """Please supply the MySQL root password"""
View file
pykolab-0.8.0.tar.gz/saslauthd/__init__.py -> pykolab-0.8.1.tar.gz/saslauthd/__init__.py
Changed
@@ -166,7 +166,7 @@ except AttributeError, e: exitcode = 1 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com") + print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") except TypeError, e: exitcode = 1 traceback.print_exc() @@ -174,7 +174,7 @@ except: exitcode = 2 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com") + print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") sys.exit(exitcode)
View file
pykolab-0.8.0.tar.gz/setup-kolab.py -> pykolab-0.8.1.tar.gz/setup-kolab.py
Changed
@@ -29,6 +29,7 @@ import pykolab from pykolab.setup import Setup +from pykolab.translate import _ try: from pykolab.constants import *
View file
pykolab-0.8.0.tar.gz/share/templates/guam.sys.config.tpl -> pykolab-0.8.1.tar.gz/share/templates/guam.sys.config.tpl
Changed
@@ -18,7 +18,7 @@ { imap, [ { port, 143 }, - { imap_server, imap }, + { imap_server, imaps }, { rules, [ { filter_groupware, [] }
View file
pykolab-0.8.0.tar.gz/share/templates/manticore.js.tpl -> pykolab-0.8.1.tar.gz/share/templates/manticore.js.tpl
Changed
@@ -17,7 +17,7 @@ CHWALA_SERVER: 'http://$fqdn/chwala/api/document', ROUNDCUBE_SERVER: 'http://$fqdn/roundcubemail', - AUTH_ENCRYPTION_KEY: '$auth_secret', + AUTH_ENCRYPTION_KEY: '$auth_key', LDAP_SERVER: 'ldap://$server_host:389', LDAP_BASE: '$user_base_dn',
View file
pykolab-0.8.0.tar.gz/share/templates/roundcubemail/kolab_delegation.inc.php.tpl -> pykolab-0.8.1.tar.gz/share/templates/roundcubemail/kolab_delegation.inc.php.tpl
Changed
@@ -8,18 +8,6 @@ // Note: LDAP addressbook defined for kolab_auth plugin is used \$config['kolab_delegation_delegate_field'] = 'kolabDelegate'; - // User authentication ID field (from fieldmap configuration) - // See kolab_auth plugin config - \$config['kolab_delegation_login_field'] = 'email'; - - // Use this fields (from fieldmap configuration) for identities - // If the value array contains more than one field, first non-empty will be used - // Note: These are not LDAP attributes, but field names in config - // Note: If there are more than one email address, as many identities will be created - // See kolab_auth plugin config - \$config['kolab_delegation_name_field'] = array('name', 'cn'); - \$config['kolab_delegation_email_field'] = array('email'); - // 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.8.0.tar.gz/wallace/__init__.py -> pykolab-0.8.1.tar.gz/wallace/__init__.py
Changed
@@ -464,7 +464,7 @@ except AttributeError, e: exitcode = 1 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com") + print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") except TypeError, e: exitcode = 1 @@ -473,7 +473,7 @@ except: exitcode = 2 traceback.print_exc() - print >> sys.stderr, _("Traceback occurred, please report a bug at http://bugzilla.kolabsys.com") + print >> sys.stderr, _("Traceback occurred, please report a bug at https://issues.kolab.org") sys.exit(exitcode)
View file
pykolab-0.8.0.tar.gz/wallace/module_gpgencrypt.py -> pykolab-0.8.1.tar.gz/wallace/module_gpgencrypt.py
Changed
@@ -28,6 +28,12 @@ from email.utils import formataddr from email.utils import getaddresses +import email.mime.application +import email.mime.multipart +import email.mime.text +import email.encoders +import gnupg + import modules import pykolab @@ -45,6 +51,40 @@ def description(): return """Encrypt messages to the recipient(s).""" +def pgp_mime(msg, recepients): + gpg = gnupg.GPG(gnupghome='/var/lib/kolab/.gnupg', verbose=conf.debuglevel > 8) + gpg.encoding = 'utf-8' + + msg = msg + + msg_boundary = str(msg.get_boundary()) + msg_content_type = str(msg.get_content_type()) + payload = msg.get_payload() + + content = "Content-Type: " + msg_content_type + ";" + "\n boundary=\"" + msg_boundary + "\"\n\n" + payload + encrypted_content = gpg.encrypt(content, recepients, always_trust=True) + msg.set_type("multipart/encrypted") + msg.set_param("protocol","application/pgp-encrypted") + + msg_boundary_gpg = "--boundary-gpg-encryption-42" + + msg_preamble = "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\n\ +" + msg_boundary_gpg + "\n\ +Content-Type: application/pgp-encrypted\n\ +Content-Description: PGP/MIME version identification\n\ +\n\ +Version: 1\n\ +\n\ +" + msg_boundary_gpg + "\n\ +Content-Type: application/octet-stream; name=\"encrypted.asc\"\n\ +Content-Description: OpenPGP encrypted message\n\ +Content-Disposition: inline; filename=\"encrypted.asc\"\n\n" + + msg.set_boundary(msg_boundary_gpg) + msg.set_payload(msg_preamble + str(encrypted_content) + "\n" + msg_boundary_gpg) + + return msg + def execute(*args, **kw): if not os.path.isdir(mybasepath): os.makedirs(mybasepath) @@ -175,7 +215,6 @@ nocrypt_rcpts = [] - import gnupg gpg = gnupg.GPG(gnupghome='/var/lib/kolab/.gnupg', verbose=conf.debuglevel > 8) gpg.encoding = 'utf-8' @@ -211,16 +250,29 @@ encrypt_rcpts.append(key_local) payload = message.get_payload() - print "payload:", payload + #print "payload:", payload if len(encrypt_rcpts) < 1: return filepath - encrypted_data = gpg.encrypt(payload, encrypt_rcpts, always_trust=True) - encrypted_string = str(encrypted_data) + if "multipart" in message.get_content_type(): + + log.debug(_("Mime Message - we need to build multipart/encrypted structure"), level=8) + + msg = message + enc_mime_message = pgp_mime(msg, encrypt_rcpts) + + message = enc_mime_message + + else: + + log.debug(_("No Mime Message - encypt plain"), level=8) + + encrypted_data = gpg.encrypt(payload, encrypt_rcpts, always_trust=True) + encrypted_string = str(encrypted_data) - print "encrypted string:", encrypted_string + message.set_payload(encrypted_string) - message.set_payload(encrypted_string) + message.add_header('X-wallace-gpg-encrypted', 'true') (fp, new_filepath) = tempfile.mkstemp(dir="/var/spool/pykolab/wallace/gpgencrypt/ACCEPT") os.write(fp, message.as_string())
View file
pykolab.dsc
Changed
@@ -2,7 +2,7 @@ Source: pykolab Binary: pykolab, kolab-cli, kolab-conf, kolab-saslauthd, kolab-server, kolab-telemetry, kolab-xml, wallace Architecture: all -Version: 0.8.0-0~kolab2 +Version: 0.8.1-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org @@ -28,7 +28,6 @@ python-icalendar, python-minimal, python-nose, - python-support, univention-config-dev | bash Package-List: kolab-cli deb python optional @@ -40,5 +39,5 @@ pykolab deb python optional wallace deb python optional Files: - 00000000000000000000000000000000 0 pykolab-0.8.0.tar.gz + 00000000000000000000000000000000 0 pykolab-0.8.1.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
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
.