Projects
Kolab:3.4
kolab-webadmin
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 27
View file
kolab-webadmin.spec
Changed
@@ -34,8 +34,8 @@ %global kolabr_group_id 414 Name: kolab-webadmin -Version: 3.1.3 -Release: 2%{?dist} +Version: 3.1.4 +Release: 1%{?dist} Summary: Kolab Groupware Server Web Administration Interface License: AGPLv3+ Group: Productivity/Office/Organizers @@ -43,9 +43,6 @@ Source0: http://mirror.kolabsys.com/pub/releases/kolab-webadmin-%{version}.tar.gz -Patch0001: 0001-Fix-lost-object_type-in-validate-arguments.patch -Patch0002: 0002-Fix-so-exception-is-thrown-on-alias-addresses-valida.patch - BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -72,9 +69,6 @@ %prep %setup -q -%patch0001 -p1 -%patch0002 -p1 - for file in `find . -type f -name "*.enterprise"`; do if [ 0%{?kolab_enterprise} -gt 0 ]; then mv -v $file $(echo $file | sed -e 's/.enterprise$//g') @@ -160,6 +154,9 @@ %attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Tue Jan 7 2014 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 3.1.4-1 +- New upstream release + * Wed Nov 27 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 3.1.3-2 - Fix #2596, which incidentally allowed the primary address to be defined as a secondary address.
View file
0001-Fix-lost-object_type-in-validate-arguments.patch
Deleted
@@ -1,24 +0,0 @@ -From 56a5b6d36535025933270842a3aa80f16d67c0ee Mon Sep 17 00:00:00 2001 -From: Aleksander Machniak <alec@alec.pl> -Date: Tue, 26 Nov 2013 19:43:17 +0100 -Subject: [PATCH 1/2] Fix lost object_type in validate() arguments - ---- - lib/kolab_api_service.php | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/lib/kolab_api_service.php b/lib/kolab_api_service.php -index 1972cb0..4e5a029 100644 ---- a/lib/kolab_api_service.php -+++ b/lib/kolab_api_service.php -@@ -284,6 +284,7 @@ abstract class kolab_api_service - $form_service = $this->controller->get_service('form_value'); - - // With the result, start validating the input -+ $attribs['object_type'] = $object_name; - $validate_result = $form_service->validate(null, $attribs); - - $special_attr_validate = Array(); --- -1.8.3.1 -
View file
0002-Fix-so-exception-is-thrown-on-alias-addresses-valida.patch
Deleted
@@ -1,77 +0,0 @@ -From e6f1a694efe87e39606580cdf6235110b6395dbe Mon Sep 17 00:00:00 2001 -From: Aleksander Machniak <alec@alec.pl> -Date: Wed, 27 Nov 2013 09:20:31 +0100 -Subject: [PATCH 2/2] Fix so exception is thrown on alias addresses validation - when one of validated addresses is a primary mail (Bug #2596) - ---- - lib/Auth/LDAP.php | 2 ++ - lib/api/kolab_api_service_form_value.php | 25 +++++++++++++++++++++---- - 2 files changed, 23 insertions(+), 4 deletions(-) - -diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php -index bf02ba1..7de9017 100644 ---- a/lib/Auth/LDAP.php -+++ b/lib/Auth/LDAP.php -@@ -605,6 +605,8 @@ class LDAP extends Net_LDAP3 { - ); - } - -+ $this->config_set('return_attributes', $mail_attrs); -+ - $result = $this->search_entries($this->config_get('root_dn'), '(objectclass=*)', 'sub', null, $search); - - if ($result && $result->count() > 0) { -diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php -index 3ea1670..1d47a9c 100644 ---- a/lib/api/kolab_api_service_form_value.php -+++ b/lib/api/kolab_api_service_form_value.php -@@ -1070,7 +1070,7 @@ class kolab_api_service_form_value extends kolab_api_service - * Checks if specified list of email addresses is already - * in use by another user - */ -- private function _email_addresses_in_use($addresses, $postdata) -+ private function _email_addresses_in_use($addresses, $attr_name, $postdata) - { - $auth = Auth::get_instance(); - -@@ -1083,8 +1083,25 @@ class kolab_api_service_form_value extends kolab_api_service - $user_found_unique_attr = $this->unique_attribute_value($user_found_dn); - - if ($user_found_unique_attr == $postdata['id']) { -- Log::trace(__FUNCTION__ . ": Entry with address $addr is actually us."); -- continue; -+ // check if the address is in another field, we prevent here -+ // from e.g. adding primary mail address into aliases list -+ $found = false; -+ $user = $users[$user_found_dn]; -+ unset($user[$attr_name]); -+ -+ foreach ($user as $attr => $list) { -+ if (in_array($addr, (array) $list)) { -+ $found = true; -+ break; -+ } -+ } -+ -+ if (!$found) { -+ Log::trace(__FUNCTION__ . ": Entry with address $addr is actually us."); -+ continue; -+ } -+ -+ // @TODO: throw different exception? - } - } - -@@ -1121,7 +1138,7 @@ class kolab_api_service_form_value extends kolab_api_service - - // Check if addresses are not already in use - if ($validation_type == self::VALIDATE_EXTENDED) { -- $this->_email_addresses_in_use($value, $postdata); -+ $this->_email_addresses_in_use($value, 'alias', $postdata); - } - - return 'OK'; --- -1.8.3.1 -
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +kolab-webadmin (3.1.4-0~kolab1) unstable; urgency=low + + * New upstream release 3.1.4 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Tue, 7 Jan 2014 22:08:00 +0100 + kolab-webadmin (3.1.3-0~kolab2) unstable; urgency=low * Fix #2596, which incidentally allowed the primary address to be
View file
debian.series
Changed
@@ -1,2 +0,0 @@ -0001-Fix-lost-object_type-in-validate-arguments.patch -p1 -0002-Fix-so-exception-is-thrown-on-alias-addresses-valida.patch -p1
View file
kolab-webadmin-3.1.3.tar.gz/lib/Auth/LDAP.php -> kolab-webadmin-3.1.4.tar.gz/lib/Auth/LDAP.php
Changed
@@ -309,16 +309,14 @@ } // Query the ACI for the primary domain - $domain_filter = $this->conf->get('ldap', 'domain_filter'); - $domain_filter = '(&(' . $domain_name_attribute . '=' . $primary_domain . ')' . $domain_filter . ')'; - $results = $this->_search($domain_base_dn, $domain_filter); - $entries = $results->entries(true); - $domain_entry = array_shift($entries); - - if (in_array('inetdomainbasedn', $domain_entry)) { - $_base_dn = $domain_entry['inetdomainbasedn']; + if ($domain_entry = $this->_find_domain($primary_domain)) { + $domain_entry = array_shift($domain_entry); + if (in_array('inetdomainbasedn', $domain_entry)) { + $_base_dn = $domain_entry['inetdomainbasedn']; + } } - else { + + if (empty($_base_dn)) { $_base_dn = $this->_standard_root_dn($primary_domain); } @@ -344,7 +342,7 @@ // @TODO: this list should be configurable or auto-created somehow $self_attrs = array( 'carLicense', 'description', 'displayName', 'facsimileTelephoneNumber', 'homePhone', - 'homePostalAddress', 'initials', 'jpegPhoto', 'labeledURI', 'mobile', 'o', 'pager', 'photo', + 'homePostalAddress', 'initials', 'jpegPhoto', 'l', 'labeledURI', 'mobile', 'o', 'pager', 'photo', 'postOfficeBox', 'postalAddress', 'postalCode', 'preferredDeliveryMethod', 'preferredLanguage', 'registeredAddress', 'roomNumber', 'secretary', 'seeAlso', 'st', 'street', 'telephoneNumber', 'telexNumber', 'title', 'userCertificate', 'userPassword', 'userSMIMECertificate', @@ -363,7 +361,7 @@ ), 'aci' => array( // Self-modification - "(targetattr = \"" . implode(" || ", $self_attrs) . "\")(version 3.0; acl \"Enable self write for common attributes\"; allow (write) userdn=\"ldap:///self\";)", + "(targetattr = \"" . implode(" || ", $self_attrs) . "\")(version 3.0; acl \"Enable self write for common attributes\"; allow (read,compare,search,write) userdn=\"ldap:///self\";)", // Directory Administrators "(targetattr = \"*\")(version 3.0; acl \"Directory Administrators Group\"; allow (all) (groupdn=\"ldap:///cn=Directory Administrators," . $inetdomainbasedn . "\" or roledn=\"ldap:///cn=kolab-admin," . $inetdomainbasedn . "\");)", // Configuration Administrators @@ -511,27 +509,15 @@ $domain_dn = $this->entry_dn($domain, array(), $domain_base_dn); if (!$domain_dn) { - $domain_filter = $this->conf->get('ldap', 'domain_filter'); - $domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute'); - $domain_filter = "(&" . $domain_filter . "(" . $domain_name_attribute . "=" . $domain . "))"; - - $this->_log(LOG_DEBUG, "Auth::LDAP::domain_info() uses _search()"); - if ($result = $this->_search($domain_base_dn, $domain_filter, $attributes)) { - $result = $result->entries(true); - } + $result = $this->_find_domain($domain, $attributes); } else { - $this->_log(LOG_DEBUG, "Auth::LDAP::domain_info() uses _read()"); $result = $this->_read($domain_dn, $attributes); } - if (!$result) { - return false; - } - $this->_log(LOG_DEBUG, "Auth::LDAP::domain_info() result: " . var_export($result, true)); - return $result; + return $result ? $result : false; } /** @@ -540,12 +526,11 @@ */ public function effective_rights($subject) { - $cache = $this->get_cache(); $ckey = $_SESSION['user']->user_bind_dn . '#' . md5($this->domain . '::' . $subject . '::' . $_SESSION['user']->user_bind_pw); // use memcache - if ($cache && ($result = $cache->get($ckey))) { + if ($result = $this->get_cache_data($ckey)) { return $result; } // use internal cache @@ -579,12 +564,7 @@ $result = $this->legacy_rights($subject); } - if ($cache) { - if (!$cache->replace($ckey, $result, MEMCACHE_COMPRESSED, 3600)) { - $cache->set($ckey, $result, MEMCACHE_COMPRESSED, 3600); - } - } - else { + if (!$this->set_cache_data($ckey, $result)) { $this->icache[$ckey] = $result; } @@ -593,6 +573,10 @@ public function find_recipient($address) { + if (strpos($address, '@') === false) { + return false; + } + $this->bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw); $mail_attrs = $this->conf->get_list('mail_attributes') ?: array('mail', 'alias'); @@ -605,13 +589,15 @@ ); } + $this->config_set('return_attributes', $mail_attrs); + $result = $this->search_entries($this->config_get('root_dn'), '(objectclass=*)', 'sub', null, $search); if ($result && $result->count() > 0) { - return $result->entries(TRUE); + return $result->entries(true); } - return FALSE; + return false; } public function get_attributes($subject_dn, $attributes) @@ -1282,7 +1268,7 @@ $subject = $subject->entries(true); $attributes = $this->attributes_allowed($subject[$subject_dn]['objectclass']); - $attributes = array_merge($attributes['may'], $attributes['must']); + $attributes = array_merge((array)$attributes['may'], (array)$attributes['must']); foreach ($attributes as $attribute) { $rights['attributeLevelRights'][$attribute] = $standard_rights; @@ -1382,6 +1368,15 @@ */ private function domain_root_dn($domain) { + if (empty($domain)) { + return false; + } + + $ckey = 'domain.root::' . $domain; + if ($result = $this->icache[$ckey]) { + return $result; + } + if (!$this->connect()) { $this->_log(LOG_DEBUG, "Could not connect"); return false; @@ -1391,54 +1386,41 @@ $bind_pw = $this->config_get("service_bind_pw", $this->conf->get("service_bind_pw")); if (!$this->bind($bind_dn, $bind_pw)) { - $this->_log(LOG_DEBUG, "Could not connect"); return false; } - $this->_log(LOG_DEBUG, "Auth::LDAP::domain_root_dn(\$domain = $domain) called"); - if (empty($domain)) { - return false; - } - - $domain_base_dn = $this->conf->get('ldap', 'domain_base_dn'); - $domain_filter = $this->conf->get('ldap', 'domain_filter'); - $domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute'); - - if (empty($domain_name_attribute)) { - $domain_name_attribute = 'associateddomain'; - } - - $domain_filter = "(&" . $domain_filter . "(" . $domain_name_attribute . "=" . $domain . "))"; + $this->_log(LOG_DEBUG, "Auth::LDAP::domain_root_dn(\$domain = $domain)"); - $result = $this->_search($domain_base_dn, $domain_filter); + if ($entry_attrs = $this->_find_domain($domain)) { + $entry_attrs = array_shift($entry_attrs); - if (!$result) { - return $this->_standard_root_dn($domain); - } - - $entries = $result->entries(true); - $entry_dn = key($entries); - $entry_attrs = $entries[$entry_dn]; - - if (is_array($entry_attrs)) { - if (array_key_exists('inetdomainbasedn', $entry_attrs) && !empty($entry_attrs['inetdomainbasedn'])) { - $domain_root_dn = $entry_attrs['inetdomainbasedn']; + $domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute'); + if (empty($domain_name_attribute)) { + $domain_name_attribute = 'associateddomain'; } - else { - if (is_array($entry_attrs[$domain_name_attribute])) { - $domain_root_dn = $this->_standard_root_dn($entry_attrs[$domain_name_attribute][0]); + + if (is_array($entry_attrs)) { + if (array_key_exists('inetdomainbasedn', $entry_attrs) && !empty($entry_attrs['inetdomainbasedn'])) { + $domain_root_dn = $entry_attrs['inetdomainbasedn']; } else { - $domain_root_dn = $this->_standard_root_dn($entry_attrs[$domain_name_attribute]); + if (is_array($entry_attrs[$domain_name_attribute])) { + $domain_root_dn = $this->_standard_root_dn($entry_attrs[$domain_name_attribute][0]); + } + else { + $domain_root_dn = $this->_standard_root_dn($entry_attrs[$domain_name_attribute]); + } } } } - else { + + if (empty($domain_root_dn)) { $domain_root_dn = $this->_standard_root_dn($domain); } - return $domain_root_dn; + $this->icache[$ckey] = $domain_root_dn; + return $domain_root_dn; } /** @@ -1496,6 +1478,47 @@ } /** + * Find domain by name + * + * @param string $domain Domain name + * @param array $attributes Result attributes + * + * @return array Domain records indexed by base DN + */ + private function _find_domain($domain, $attributes = array('*')) + { + $this->_log(LOG_DEBUG, "Auth::LDAP::_find_domain($domain)"); + + $ckey = 'domain::' . $domain; + + // use memcache + if ($domain_dn = $this->get_cache_data($ckey)) { + return $this->_read($domain_dn, $attributes); + } + + $domain_base_dn = $this->conf->get('ldap', 'domain_base_dn'); + $domain_filter = $this->conf->get('ldap', 'domain_filter'); + $domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute'); + + if (empty($domain_name_attribute)) { + $domain_name_attribute = 'associateddomain'; + } + + $domain_filter = "(&" . $domain_filter . "(" . $domain_name_attribute . "=" . $domain . "))"; + + if ($result = $this->_search($domain_base_dn, $domain_filter, $attributes)) { + $result = $result->entries(true); + + // cache domain DN + if (count($result) == 1) { + $this->set_cache_data($ckey, key($result)); + } + + return $result; + } + } + + /** * From a domain name, such as 'kanarip.com', create a standard root * dn, such as 'dc=kanarip,dc=com'. * @@ -1539,6 +1562,7 @@ $this->memcache = false; return false; } + // add all configured hosts to pool $pconnect = $this->conf->get('kolab_wap', 'memcache_pconnect', Conf::BOOL); $hosts = $this->conf->get('kolab_wap', 'memcache_hosts'); @@ -1561,10 +1585,10 @@ $this->mc_available += intval($this->memcache->addServer( $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); } - } - // test connection and failover (will result in $this->mc_available == 0 on complete failure) - $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist + // test connection and failover (will result in $this->mc_available == 0 on complete failure) + $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist + } if (!$this->mc_available) { $this->memcache = false; @@ -1588,4 +1612,40 @@ } } + /** + * Get cached data + * + * @param string $key Cache key + * + * @return mixed Cached value + */ + public function get_cache_data($key) + { + if ($cache = $this->get_cache()) { + return $cache->get($ckey); + } + } + + /** + * Store cached data + * + * @param string $key Cache key + * @param mixed $data Data + * @param int $ttl Cache TTL in seconds + * + * @return bool False on failure or when cache is disabled, True if data was saved succesfully + */ + public function set_cache_data($key, $data, $ttl = 3600) + { + if ($cache = $this->get_cache()) { + if (!$cache->replace($ckey, $data, MEMCACHE_COMPRESSED, $ttl)) { + return $cache->set($ckey, $data, MEMCACHE_COMPRESSED, $ttl); + } + else { + return true; + } + } + + return false; + } }
View file
kolab-webadmin-3.1.3.tar.gz/lib/api/kolab_api_service_form_value.php -> kolab-webadmin-3.1.4.tar.gz/lib/api/kolab_api_service_form_value.php
Changed
@@ -1070,7 +1070,7 @@ * Checks if specified list of email addresses is already * in use by another user */ - private function _email_addresses_in_use($addresses, $postdata) + private function _email_addresses_in_use($addresses, $attr_name, $postdata) { $auth = Auth::get_instance(); @@ -1083,8 +1083,28 @@ $user_found_unique_attr = $this->unique_attribute_value($user_found_dn); if ($user_found_unique_attr == $postdata['id']) { - Log::trace(__FUNCTION__ . ": Entry with address $addr is actually us."); - continue; + // check if the address is in another field, we prevent here + // from e.g. adding primary mail address into aliases list + $found = false; + $user = $users[$user_found_dn]; + $addr = mb_strtolower($addr); + + unset($user[$attr_name]); + + foreach ($user as $attr => $list) { + $list = array_map('mb_strtolower', (array) $list); + if (in_array($addr, $list)) { + $found = true; + break; + } + } + + if (!$found) { + Log::trace(__FUNCTION__ . ": Entry with address $addr is actually us."); + continue; + } + + // @TODO: throw different exception? } } @@ -1121,7 +1141,7 @@ // Check if addresses are not already in use if ($validation_type == self::VALIDATE_EXTENDED) { - $this->_email_addresses_in_use($value, $postdata); + $this->_email_addresses_in_use($value, 'alias', $postdata); } return 'OK';
View file
kolab-webadmin-3.1.3.tar.gz/lib/api/kolab_api_service_users.php -> kolab-webadmin-3.1.4.tar.gz/lib/api/kolab_api_service_users.php
Changed
@@ -39,6 +39,7 @@ 'uidnumber', 'gidnumber', 'mailhost', + 'mailquota', 'entrydn', );
View file
kolab-webadmin-3.1.3.tar.gz/lib/ext/Net/LDAP3.php -> kolab-webadmin-3.1.4.tar.gz/lib/ext/Net/LDAP3.php
Changed
@@ -815,12 +815,19 @@ { $this->_debug("entry_dn on subject $subject"); $is_dn = ldap_explode_dn($subject, 1); - $this->_debug($is_dn ? "entry_dn is a dn" : "entry_dn is not a dn"); if (is_array($is_dn) && array_key_exists("count", $is_dn) && $is_dn["count"] > 0) { + $this->_debug("$subject is a dn"); return $subject; } + $this->_debug("$subject is not a dn"); + + if (strlen($subject) < 32 || preg_match('/[^a-fA-F0-9-]/', $subject)) { + $this->_debug("$subject is not a unique identifier"); + return; + } + $unique_attr = $this->config_get('unique_attribute', 'nsuniqueid'); $this->_debug("Using unique_attribute " . var_export($unique_attr, TRUE) . " at " . __FILE__ . ":" . __LINE__);
View file
kolab-webadmin-3.1.3.tar.gz/lib/kolab_api_service.php -> kolab-webadmin-3.1.4.tar.gz/lib/kolab_api_service.php
Changed
@@ -284,6 +284,7 @@ $form_service = $this->controller->get_service('form_value'); // With the result, start validating the input + $attribs['object_type'] = $object_name; $validate_result = $form_service->validate(null, $attribs); $special_attr_validate = Array(); @@ -507,6 +508,7 @@ $attrs = array_merge($attrs, $extra_attrs); } } + // Replace unique attribute with 'id' key $attrs['id'] = $attrs[$unique_attr]; unset($attrs[$unique_attr]);
View file
kolab-webadmin-3.1.3.tar.gz/lib/kolab_client_task.php -> kolab-webadmin-3.1.4.tar.gz/lib/kolab_client_task.php
Changed
@@ -1063,6 +1063,7 @@ $admin_auto_fields_rw = $this->config_get('admin_auto_fields_rw', false, Conf::BOOL); foreach ($fields as $idx => $field) { + $readonly = null; if (!array_key_exists($idx, $attribute_rights)) { // If the entry level rights contain 'add' and 'delete', well, you're an admin if (in_array('add', $entry_rights) && in_array('delete', $entry_rights)) { @@ -1071,7 +1072,7 @@ } } else { - $fields[$idx]['readonly'] = true; + $fields[$idx]['readonly'] = $readonly = true; } } else { @@ -1081,10 +1082,18 @@ } } // Explicit attribute level rights, check for 'write' - elseif (!in_array('write', $attribute_rights[$idx])) { - $fields[$idx]['readonly'] = true; + else if (!in_array('write', $attribute_rights[$idx])) { + $fields[$idx]['readonly'] = $readonly = true; } } + + // disable auto-fields updates, user has no rights to modify them anyway + if (is_bool($readonly) && $readonly) { + if (($s_idx = array_search($idx, $auto_attribs)) !== false) { + unset($auto_attribs[$s_idx]); + } + unset($auto_fields[$idx]); + } } // Register list of auto-generated fields
View file
kolab-webadmin-3.1.3.tar.gz/lib/kolab_recipient_policy.php -> kolab-webadmin-3.1.4.tar.gz/lib/kolab_recipient_policy.php
Changed
@@ -50,7 +50,7 @@ } if (!empty($locale)) { - setlocale(LC_ALL, $locale.'utf8', $locale.'UTF-8', $locale); + setlocale(LC_ALL, $locale.'.utf8', $locale.'.UTF-8', $locale); } if (!is_array($groupdata[$key])) { @@ -97,7 +97,7 @@ } if (!empty($locale)) { - setlocale(LC_ALL, $locale.'utf8', $locale.'UTF-8', $locale); + setlocale(LC_ALL, $locale.'.utf8', $locale.'.UTF-8', $locale); } if (!is_array($userdata[$_key])) {
View file
kolab-webadmin-3.1.3.tar.gz/public_html/js/kolab_admin.js -> kolab-webadmin-3.1.4.tar.gz/public_html/js/kolab_admin.js
Changed
@@ -1328,7 +1328,8 @@ } } - this.api_post('form_value.generate', data, 'form_value_response'); + if (data.attributes.length) + this.api_post('form_value.generate', data, 'form_value_response'); this.set_busy(false); };
View file
kolab-webadmin.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-webadmin Binary: kolab-webadmin Architecture: all -Version: 3.1.3-0~kolab2 +Version: 3.1.4-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org/ @@ -13,5 +13,5 @@ Package-List: kolab-webadmin deb admin extra Files: - 00000000000000000000000000000000 0 kolab-webadmin-3.1.3.tar.gz + 00000000000000000000000000000000 0 kolab-webadmin-3.1.4.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
.