roundcube

This commit is contained in:
Stefan Liebl 2020-07-09 15:41:32 +02:00
parent 32d81b73bb
commit 79f4015906
57 changed files with 5675 additions and 2 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
db

View File

@ -1,3 +1,12 @@
# docker-compose-workshop
Update, run, inspect and stop server:
```
docker-compose pull
docker-compose up -d
docker-compose logs -t -f
docker-compose down
```
Login to roundcube
```
docker-compose exec roundcube sh
Docker-Compose workshop
```

26
docker-compose.yaml Normal file
View File

@ -0,0 +1,26 @@
version: '2'
services:
roundcube:
image: instrumentisto/roundcube:1.4.7-fpm
expose:
- "9000"
volumes:
- app-volume:/app
- ./db:/var/db
- ./roundcube.config.php:/app/config/config.inc.php:ro
- ./plugins/identity_smtp:/app/plugins/identity_smtp
- ./plugins/carddav:/app/plugins/carddav
- ./plugins/managesieve/config.inc.php:/app/plugins/managesieve/config.inc.php
nginx:
image: nginx:1.16.0-alpine
depends_on:
- roundcube
ports:
- "8081:80"
volumes:
- app-volume:/var/www
- ./fpm.nginx.conf:/etc/nginx/conf.d/default.conf:ro
volumes:
app-volume:

40
fpm.nginx.conf Normal file
View File

@ -0,0 +1,40 @@
server {
listen 80;
root /var/www/html;
index index.php;
charset utf-8;
location = /favicon.ico {
root /var/www/html/skins/larry/images;
access_log off;
log_not_found off;
expires max;
}
location = /robots.txt {
allow all;
access_log off;
log_not_found off;
}
location / {
try_files $uri $uri/ index.php;
}
location ~ /\.ht {
deny all;
access_log off;
log_not_found off;
}
location ~* \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass roundcube:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
}
}

3
plugins/carddav/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
config.inc.php
composer.phar
/vendor

52
plugins/carddav/ChangeLog Normal file
View File

@ -0,0 +1,52 @@
Changes from 0.7.0 to 0.8.0:
* Implement sync_token support for less overhead when syncing
* Use XPath XML parsing instead of self-rolled functions
* Automatically detect all addressbooks on server
* Only use sync methods the server advertises
* many small changes
Changes from 0.6.1 to 0.7.0:
* merge branch: cache
* support caching of entire addressbooks, thus speeding up repetetive queries
* add localizations: italian, french, swedish, hungarian
* improvements in handling multiple addressbooks
* improvements in handling different CardDAV servers
Changes from 0.6.0 to 0.6.1:
* merge branch: multipleaddressbooks
* support multiple authorization header elements, patch by Michael Stilkerich
* adapt config file and its handling to new multiple addressbook feature
* fix FS#25 attaching photo
* fix FS#27 use surname lastname if no nickname provided
* fix FS#29 displayname/nickname handling
Changes from 0.4.2 to 0.6.0:
* add group support
* faster vcard retrieval
* vcard parsing with library
* support for configuration file
* support full-featured addressbook of RC 0.6
Changes from 0.4.1 to 0.4.2:
* add some debug entries
* fix FS#7 - case-insensitive Authentication headers (Basic vs. basic, Digest vs. digest)
Changes from 0.4.0 to 0.4.1:
* use PHP HTTP Client class for the whole HTTP layer handling. This speeds up processing from taking 15s to being "snappy".
Changes from 0.3.1 to 0.4.0:
* check if PHP_VERSION >= 5.3.0 and display an error message in the preferences tab if too old.
This is because of https://bugs.php.net/bug.php?id=46035
* fix FS#14 - add function get_name
* do not use + for delimiters, but _ as rcm 0.6 will substitute + for _
Changes from 0.3.0 to 0.3.1:
* fix FS#10 - incomplete searchresults now complete
* fixes FS#11 - do not use ob_* functions but a custom error handler to catch fopen errors
* make password inputfield a password html-element to prevent shouldersurfing of password
* make size of url input field as long as the value, but at least 40
* remove stray write_log in myErrorHandler
* make list_records return false instead of an empty result list if a search for an entry returned no results
* use <href> tag for ID parsing instead of UID parameter in vCard
* try harder to authenticate against server - especially useful for PHP 5.2.x

37
plugins/carddav/INSTALL Normal file
View File

@ -0,0 +1,37 @@
0. Log out of Roundcube! This is necessary for proper setup of the databases later.
1a. Download the last release of RCMCardDAV here:
https://github.com/blind-coder/rcmcarddav/releases/tag/v3.0.3
Extract the archive and rename the extracted directory to 'carddav'.
1b. Copy/Clone the whole RCMCardDAV plugin directory to roundcubemail/plugins/ and rename
the directory to 'carddav'.
2. Only if you are installing a git clone, change to the directory
roundcubemail/plugins/carddav/ and install the dependencies using composer:
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
php composer.phar install
3. Change to the plugin directory roundcubemail/plugins/carddav and copy the
file 'config.inc.php.dist' to 'config.inc.php' to create the initial plugin
configuration file.
4. If necessary, customize the default plugin settings in the file created in
step 3.
5. Make sure that the file and directory ownership match the user which is used
to run your web server, e.g.:
chown -R wwwrun roundcubemail/plugins/carddav
chgrp -R nogroup roundcubemail/plugins/carddav
6. Install the curl and mbstring php extensions if not already present:
apt-get install php7.0-{mbstring,curl} # on Debian
7. Add 'carddav' to the $rcmail_config['plugins'] array in
roundcubemail/config/main.inc.php
8. Login to Roundcube and set-up your first CardDAV address book by accessing
'Settings -> Settings -> CardDAV'

View File

@ -0,0 +1,5 @@
- Clone the repository:
`cd roundcube/plugins && git clone https://github.com/blind-coder/rcmcarddav.git carddav`
- Install dependencies:
- Install composer as per the documentation: https://getcomposer.org/download/
- Run `php composer.phar install`

339
plugins/carddav/LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

47
plugins/carddav/README.md Normal file
View File

@ -0,0 +1,47 @@
RCMCardDAV
==========
CardDAV plugin for the RoundCube Webmailer
Upgrading from 1.0
==================
There is no upgrade path from the 1.0 version. You need to manually remove RCMCardDAV 1.0, drop its tables from your database and start with a fresh installation.
Upgrading from 2.0.x
==================
There is no supported upgrade path from the 2.0.x version. You need to manually remove RCMCardDAV 2.0.x, drop its tables from your database and start with a fresh installation.
Requirements
============
RCMCardDAV requires at least PHP 5.6.18. Older versions might work if the version check is disabled using the `$prefs['_GLOBAL']['suppress_version_warning']` configuration entry, but this is unsupported.
Installation
============
RCMCardDAV can be installed via composer, from a release tarball or from a git clone. This list is in increasing difficulty, with composer being the easiest method.
Please note that due to version incompatibilities of depending libraries, this plugin might be incompatible to Kolabs calendar plugin. There is a compatible version available here: `http://git.faster-it.de/roundcube_calendar/`.
Intallation steps:
- Log out of Roundcube!
This is important because RCMCardDAV runs its database initialisation / update procedure only when a user logs in!
- Get RCMCardDAV
- Via composer:
- Add `"roundcube/carddav": "dev-master"` to your composer.json file and install with `php composer.phar install`.
- Via release tarball:
- Download and extract the release tarball into `roundcube/plugins` directory and rename the extracted directory to `carddav`. The tarball contains all necessary dependencies and does not need composer.
- Via git:
- Please do not do not do this unless you have a very good reason for it! Check the file [INSTALLFROMGIT.md](INSTALLFROMGIT.md) for instructions.
- Configure RCMCardDAV
If you want to configure preset addressbooks for your users, copy the file `config.inc.php.dist` to `config.inc.php` and edit it as you need.
- Make sure that the files and directories are owned by the user and group that your webserver runs as. For Debian GNU/Linux that would be:
`chown -R www-data:www-data roundcubemail/plugins/carddav`
- Install the curl php extension if not already present:
`sudo apt-get install php5-curl`
- Enable RCMCardDAV in Roundcube:
Open the file `roundcube/config/config.inc.php` and add `carddav` to the array `$config['plugins']`.
- Login to Roundcube and setup your addressbook by navigation to the Settings page and click on CardDAV.
In case of errors, check the files `roundcube/logs/*`.

684
plugins/carddav/carddav.php Normal file
View File

@ -0,0 +1,684 @@
<?php
/*
RCM CardDAV Plugin
Copyright (C) 2011-2016 Benjamin Schieder <rcmcarddav@wegwerf.anderdonau.de>,
Michael Stilkerich <ms@mike2k.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
class carddav extends rcube_plugin
{
private static $helper;
// the dummy task is used by the calendar plugin, which requires
// the addressbook to be initialized
public $task = 'addressbook|login|mail|settings|dummy';
public function checkMigrations(){
$dbh = rcmail::get_instance()->db;
$db_backend = "unknown";
switch ($dbh->db_provider){
case "mysql":
$db_backend = "mysql";
break;
case "sqlite":
$db_backend = "sqlite3";
break;
case "pgsql":
case "postgres":
$db_backend = "postgres";
break;
}
if ($db_backend == "unknown"){
rcmail::write_log("carddav", "Unknown database backend: ".$dbh->db_provider);
return;
}
# first initialize the carddav_migrations table if it doesn't exist.
/*
$query = file_get_contents(dirname(__FILE__)."/dbinit/".$db_backend.".sql");
if (strlen($query) > 0){
$query = str_replace("TABLE_PREFIX", $config->get('db_prefix', ""), $query);
$dbh->query($query);
rcmail::write_log("carddav", "Processed initialization of carddav_migrations table");
} else {
rcmail::write_log("carddav", "Can't find migration: /dbinit/".$db_backend.".sql");
}
*/
$config = rcmail::get_instance()->config;
$migrations = array_diff(scandir(dirname(__FILE__)."/dbmigrations/"), array('..', '.'));
$mignew = array();
foreach ($migrations as $k => $v){
$mignew[] = $v;
}
$migrations = $mignew;
$qmarks = "?";
for ($i=1;$i<count($migrations);$i++){
$qmarks .= ",?";
}
$dbh->set_option('ignore_key_errors', true);
$sql_result = $dbh->query('SELECT * FROM '.
$dbh->table_name('carddav_migrations') .
' WHERE filename IN ('.$qmarks.');', $migrations);
if ($sql_result){
while ($processed = $dbh->fetch_assoc($sql_result)) {
if(($key = array_search($processed['filename'], $migrations)) !== false) {
unset($migrations[$key]);
}
}
}
$dbh->set_option('ignore_key_errors', null);
foreach ($migrations as $migration) {
rcmail::write_log('carddav', "In migration: ".$migration);
$queries_raw = file_get_contents(dirname(__FILE__)."/dbmigrations/".$migration."/".$db_backend.".sql");
$match_count = preg_match_all('/(.+?;)/s', $queries_raw, $matches);
rcmail::write_log('carddav', 'Found '.$match_count.' matches');
if($match_count > 0){
foreach ($matches[0] as $query){ // array will have two elements, each holding all queries. Only iterate over one of them
if (strlen($query) > 0){
$query = str_replace("TABLE_PREFIX", $config->get('db_prefix', ""), $query);
$dbh->query($query);
}
}
$dbh->query("INSERT INTO ".$dbh->table_name("carddav_migrations")." (filename) VALUES (?)", $migration);
}else{
rcmail::write_log('carddav', "Did not match any instructions from migration ".$migration);
}
}
}
public function init()
{{{
$this->rc = rcmail::get_instance();
$tasks = explode('|', $this->task);
// Since other plugins may also use the Sabre library
// In order to avoid version conflicts between Sabre libraries
// which might be used by other plugins
// It is better to restrict the loading of Sabre library
// under necessary tasks
if(!in_array($this->rc->task, $tasks))
return;
else {
require_once('carddav_backend.php');
require_once('carddav_discovery.php');
require_once('carddav_common.php');
}
self::$helper = new carddav_common('BACKEND: ');
$this->add_hook('addressbooks_list', array($this, 'address_sources'));
$this->add_hook('addressbook_get', array($this, 'get_address_book'));
$this->add_hook('preferences_list', array($this, 'cd_preferences'));
$this->add_hook('preferences_save', array($this, 'cd_save'));
$this->add_hook('preferences_sections_list',array($this, 'cd_preferences_section'));
$this->add_hook('login_after',array($this, 'checkMigrations'));
$this->add_hook('login_after',array($this, 'init_presets'));
if(!array_key_exists('user_id', $_SESSION))
return;
// use this address book for autocompletion queries
// (maybe this should be configurable by the user?)
$config = rcmail::get_instance()->config;
$sources = (array) $config->get('autocomplete_addressbooks', array('sql'));
$dbh = rcmail::get_instance()->db;
$sql_result = $dbh->query('SELECT id FROM ' .
$dbh->table_name('carddav_addressbooks') .
' WHERE user_id=? AND active=1',
$_SESSION['user_id']);
while ($abookrow = $dbh->fetch_assoc($sql_result)) {
$abookname = "carddav_" . $abookrow['id'];
if (!in_array($abookname, $sources)) {
$sources[] = $abookname;
}
}
$config->set('autocomplete_addressbooks', $sources);
$skin_path = $this->local_skin_path();
$this->include_stylesheet($skin_path . '/carddav.css');
}}}
public function init_presets()
{{{
$dbh = rcmail::get_instance()->db;
$prefs = carddav_common::get_adminsettings();
// migrate old settings
carddav_backend::migrateconfig();
// read existing presets from DB
$sql_result = $dbh->query('SELECT * FROM ' .
$dbh->table_name('carddav_addressbooks') .
' WHERE user_id=? AND presetname is not null',
$_SESSION['user_id']);
$existing_presets = array( );
while ($abookrow = $dbh->fetch_assoc($sql_result)) {
$pn = $abookrow['presetname'];
if(!array_key_exists($pn,$existing_presets)) {
$existing_presets[$pn] = array();
}
$existing_presets[$pn][] = $abookrow;
}
// add not existing preset addressbooks
foreach($prefs as $presetname => $preset) {
if($presetname === '_GLOBAL') continue;
// addressbooks exist for this preset => update settings
if(array_key_exists($presetname, $existing_presets)) {
if(is_array($preset['fixed'])) {
// update all existing addressbooks for this preset
foreach($existing_presets[$presetname] as $abookrow) {
// decrypt password so that the comparison works
$abookrow['password'] = self::$helper->decrypt_password($abookrow['password']);
// update: only admin fix keys, only if it's fixed
// otherwise there may be user changes that should not be destroyed
$pa = array();
foreach($preset['fixed'] as $k) {
if(array_key_exists($k, $abookrow) && array_key_exists($k,$preset)) {
// only update the name if it is used
if($k === 'name') {
if(!$preset['carddav_name_only']) {
$fullname = $abookrow['name'];
$cnpos = strpos($fullname, ' (');
if($cnpos === FALSE && strcmp($preset['name'],$fullname)!==0) {
$pa['name'] = $preset['name'];
} else if($cnpos !== FALSE && strcmp($preset['name'],substr($fullname,0,$cnpos))!==0) {
$pa['name'] = $preset['name'] . substr($fullname, $cnpos);
}
}
} else if ($abookrow[$k] != $preset[$k]) {
$pa[$k] = $preset[$k];
}
}
}
// only update if something changed
if(count($pa)===0) continue;
self::update_abook($abookrow['id'],$pa);
}
}
unset($existing_presets[$presetname]);
} else { // create new
$preset['presetname'] = $presetname;
$preset['password'] = self::$helper->encrypt_password($preset['password']);
$abname = $preset['name'];
$discovery = new carddav_discovery();
$srvs = $discovery->find_addressbooks($preset['url'], $preset['username'], $preset['password']);
if(is_array($srvs)) {
foreach($srvs as $srv){
if($srv['name']) {
if($preset['carddav_name_only'])
$preset['name'] = $srv['name'];
else
$preset['name'] = "$abname (" . $srv['name'] . ')';
} else {
$preset['name'] = $abname;
}
$preset['url'] = $srv['href'];
self::insert_abook($preset);
}}
}
}
// delete existing preset addressbooks that where removed by admin
foreach($existing_presets as $ep) {
foreach($ep as $abookrow) {
self::delete_abook($abookrow['id']);
}
}
}}}
public function address_sources($p)
{{{
$dbh = rcmail::get_instance()->db;
$prefs = carddav_common::get_adminsettings();
$sql_result = $dbh->query('SELECT id,name,presetname FROM ' .
$dbh->table_name('carddav_addressbooks') .
' WHERE user_id=? AND active=1',
$_SESSION['user_id']);
while ($abookrow = $dbh->fetch_assoc($sql_result)) {
$ro = false;
if($abookrow['presetname'] && $prefs[$abookrow['presetname']]['readonly'])
$ro = true;
$p['sources']["carddav_".$abookrow['id']] = array(
'id' => "carddav_".$abookrow['id'],
'name' => $abookrow['name'],
'groups' => true,
'autocomplete' => true,
'readonly' => $ro,
);
}
return $p;
}}}
public function get_address_book($p)
{{{
if (preg_match(";^carddav_(\d+)$;", $p['id'], $match)){
$p['instance'] = new carddav_backend($match[1]);
}
return $p;
}}}
private static function process_cd_time($refresht)
{{{
if(preg_match('/^(\d+)(:([0-5]?\d))?(:([0-5]?\d))?$/', $refresht, $match)) {
$refresht = sprintf("%02d:%02d:%02d", $match[1],
count($match)>3 ? $match[3] : 0,
count($match)>5 ? $match[5] : 0);
} else {
$refresht = '01:00:00';
}
return $refresht;
}}}
private static function no_override($pref, $abook, $prefs)
{{{
$pn = $abook['presetname'];
if(!$pn) return false;
// never enable user change for preset URLs
if($pref === 'url') return true;
if(!is_array($prefs[$pn])) return false;
if(!is_array($prefs[$pn]['fixed'])) return false;
return in_array($pref,$prefs[$pn]['fixed']);
}}}
/**
* Builds a setting block for one address book for the preference page.
*/
private function cd_preferences_buildblock($blockheader,$abook,$prefs)
{{{
$abookid = $abook['id'];
$rcmail = rcmail::get_instance();
if (self::no_override('active', $abook, $prefs)) {
$content_active = $prefs[$abook['presetname']] ? $this->gettext('cd_enabled') : $this->gettext('cd_disabled');
} else {
// check box for activating
$checkbox = new html_checkbox(array('name' => $abookid.'_cd_active', 'value' => 1));
$content_active = $checkbox->show($abook['active']?1:0);
}
if (self::no_override('use_categories', $abook, $prefs) || $abook['id'] !== "new") {
$content_use_categories = $abook['use_categories'] ? $this->gettext('cd_enabled') : $this->gettext('cd_disabled');
} else {
// check box for use categories
$checkbox = new html_checkbox(array('name' => $abookid.'_cd_use_categories', 'value' => 1));
$content_use_categories = $checkbox->show($abook['use_categories']?1:0);
}
if (self::no_override('username', $abook, $prefs)) {
// %V parses username for macosx, replaces periods and @ by _, work around bugs in contacts.app
$content_username = $abook['username'] === '%V' ? str_replace('@','_', str_replace('.','_',$_SESSION['username'])) : $abook['username'] === '%u' ? $_SESSION['username'] : $abook['username'] === '%l' ? $rcmail->user->get_username('local') : $abook['username'];
} else {
// input box for username
$input = new html_inputfield(array('name' => $abookid.'_cd_username', 'type' => 'text', 'autocomplete' => 'off', 'value' => $abook['username']));
$content_username = $input->show();
}
if (self::no_override('password', $abook, $prefs)) {
$content_password = "***";
} else {
// input box for password
$input = new html_inputfield(array('name' => $abookid.'_cd_password', 'type' => 'password', 'autocomplete' => 'off', 'value' => ''));
$content_password = $input->show();
}
if (self::no_override('url', $abook, $prefs)) {
$content_url = str_replace("%u", $abook['username'], $abook['url']);
} else {
// input box for URL
$size = max(strlen($abook['url']),40);
$input = new html_inputfield(array('name' => $abookid.'_cd_url', 'type' => 'text', 'autocomplete' => 'off', 'value' => $abook['url'], 'size' => $size));
$content_url = $input->show();
}
// input box for refresh time
if (self::no_override('refresh_time', $abook, $prefs)) {
$content_refresh_time = $abook['refresh_time'];
} else {
$input = new html_inputfield(array('name' => $abookid.'_cd_refresh_time', 'type' => 'text', 'autocomplete' => 'off', 'value' => $abook['refresh_time'], 'size' => 10));
$content_refresh_time = $input->show();
}
if (self::no_override('name', $abook, $prefs)) {
$content_name = $abook['name'];
} else {
$input = new html_inputfield(array('name' => $abookid.'_cd_name', 'type' => 'text', 'autocomplete' => 'off', 'value' => $abook['name'], 'size' => 40));
$content_name = $input->show();
}
$retval = array(
'options' => array(
array('title'=> self::$helper->Q($this->gettext('cd_name')), 'content' => $content_name),
array('title'=> self::$helper->Q($this->gettext('cd_active')), 'content' => $content_active),
array('title'=> self::$helper->Q($this->gettext('cd_use_categories')), 'content' => $content_use_categories),
array('title'=> self::$helper->Q($this->gettext('cd_username')), 'content' => $content_username),
array('title'=> self::$helper->Q($this->gettext('cd_password')), 'content' => $content_password),
array('title'=> self::$helper->Q($this->gettext('cd_url')), 'content' => $content_url),
array('title'=> self::$helper->Q($this->gettext('cd_refresh_time')), 'content' => $content_refresh_time),
),
'name' => $blockheader
);
if (!$abook['presetname'] && preg_match('/^\d+$/',$abookid)) {
$checkbox = new html_checkbox(array('name' => $abookid.'_cd_delete', 'value' => 1));
$content_delete = $checkbox->show(0);
$retval['options'][] = array('title'=> self::$helper->Q($this->gettext('cd_delete')), 'content' => $content_delete);
}
return $retval;
}}}
// user preferences
function cd_preferences($args)
{{{
if($args['section'] != 'cd_preferences')
return;
$this->include_stylesheet($this->local_skin_path().'/carddav.css');
$this->add_texts('localization/', false);
$prefs = carddav_common::get_adminsettings();
if (!$prefs['_GLOBAL']['suppress_version_warning']){
if (version_compare(PHP_VERSION, '5.6.18', '<')) {
$args['blocks']['cd_preferences'] = array(
'options' => array(
array('title'=> self::$helper->Q($this->gettext('cd_php_too_old')), 'content' => PHP_VERSION)
),
'name' => self::$helper->Q($this->gettext('cd_title'))
);
return $args;
}
}
$abooks = carddav_backend::get_dbrecord($_SESSION['user_id'],'*','addressbooks',false,'user_id');
foreach($abooks as $abook) {
$presetname = $abook['presetname'];
if (empty($presetname) ||
(!isset($prefs[$presetname]['hide']) || (isset($prefs[$presetname]['hide']) && $prefs[$presetname]['hide'] === FALSE))) {
$abookid = $abook['id'];
$blockhdr = $abook['name'];
if($abook['presetname'])
$blockhdr .= str_replace("_PRESETNAME_", $abook['presetname'], self::$helper->Q($this->gettext('cd_frompreset')));
$args['blocks']['cd_preferences'.$abookid] = $this->cd_preferences_buildblock($blockhdr,$abook,$prefs);
}
}
if(!array_key_exists('_GLOBAL', $prefs) || !$prefs['_GLOBAL']['fixed']) {
$args['blocks']['cd_preferences_section_new'] = $this->cd_preferences_buildblock(
self::$helper->Q($this->gettext('cd_newabboxtitle')),
array(
'id' => 'new',
'active' => 1,
'use_categories' => 1,
'username' => '',
'url' => '',
'name' => '',
'refresh_time' => 1,
'presetname' => '',
), $prefs);
}
return($args);
}}}
// add a section to the preferences tab
function cd_preferences_section($args)
{{{
$prefs = carddav_common::get_adminsettings();
if (!isset($prefs['_GLOBAL']['hide_preferences']) || (isset($prefs['_GLOBAL']['hide_preferences']) && $prefs['_GLOBAL']['hide_preferences'] === FALSE)) {
$this->add_texts('localization/', false);
$args['list']['cd_preferences'] = array(
'id' => 'cd_preferences',
'section' => self::$helper->Q($this->gettext('cd_title'))
);
}
return($args);
}}}
// save preferences
function cd_save($args)
{{{
$this->add_texts('localization/', false);
if($args['section'] != 'cd_preferences')
return;
$prefs = carddav_common::get_adminsettings();
if (isset($prefs['_GLOBAL']['hide_preferences']) && $prefs['_GLOBAL']['hide_preferences'] === TRUE) {
return;
}
// update existing in DB
$abooks = carddav_backend::get_dbrecord($_SESSION['user_id'],'id,presetname',
'addressbooks', false, 'user_id');
foreach($abooks as $abook) {
$abookid = $abook['id'];
if( isset($_POST[$abookid."_cd_delete"]) ) {
self::delete_abook($abookid);
} else {
$newset = array (
'name' => rcube_utils::get_input_value($abookid."_cd_name", rcube_utils::INPUT_POST),
'username' => rcube_utils::get_input_value($abookid."_cd_username", rcube_utils::INPUT_POST, true),
'url' => rcube_utils::get_input_value($abookid."_cd_url", rcube_utils::INPUT_POST),
'active' => isset($_POST[$abookid.'_cd_active']) ? 1 : 0,
'use_categories' => isset($_POST[$abookid.'_cd_use_categories']) ? 1 : 0,
'refresh_time' => rcube_utils::get_input_value($abookid."_cd_refresh_time", rcube_utils::INPUT_POST),
);
// only set the password if the user entered a new one
$password = rcube_utils::get_input_value($abookid."_cd_password", rcube_utils::INPUT_POST, true);
if(strlen($password) > 0) {
$newset['password'] = $password;
}
// remove admin only settings
foreach($newset as $pref => $value) {
if(self::no_override($pref, $abook, $prefs)) {
unset($newset[$pref]);
}
}
self::update_abook($abookid, $newset);
}
}
// add a new address book?
$new = rcube_utils::get_input_value('new_cd_name', rcube_utils::INPUT_POST);
if ( (!array_key_exists('_GLOBAL', $prefs) || !$prefs['_GLOBAL']['fixed']) && strlen($new) > 0) {
$srv = rcube_utils::get_input_value('new_cd_url', rcube_utils::INPUT_POST);
$usr = rcube_utils::get_input_value('new_cd_username', rcube_utils::INPUT_POST, true);
$pass = rcube_utils::get_input_value('new_cd_password', rcube_utils::INPUT_POST, true);
$pass = self::$helper->encrypt_password($pass);
$abname = rcube_utils::get_input_value('new_cd_name', rcube_utils::INPUT_POST);
$use_categories = intval(rcube_utils::get_input_value('new_cd_use_categories', rcube_utils::INPUT_POST, true), 0);
$discovery = new carddav_discovery();
$srvs = $discovery->find_addressbooks($srv, $usr, $pass);
if(is_array($srvs) && count($srvs)>0) {
foreach($srvs as $srv){
self::$helper->debug("ADDING ABOOK " . print_r($srv,true));
$this_abname = $abname;
if($srv['name']) {
$this_abname .= ' (' . $srv['name'] . ')';
}
self::insert_abook(array(
'name' => $this_abname,
'username' => $usr,
'password' => $pass,
'use_categories' => $use_categories,
'url' => $srv['href'],
'refresh_time' => rcube_utils::get_input_value('new_cd_refresh_time', rcube_utils::INPUT_POST)
));
}
} else {
$args['abort'] = true;
$args['message'] = $abname . ': ' . $this->gettext('cd_err_noabfound');
}
}
return($args);
}}}
private static function delete_abook($abookid)
{{{
carddav_backend::delete_dbrecord($abookid,'addressbooks');
// we explicitly delete all data belonging to the addressbook, since
// cascaded deleted are not supported by all database backends
// ...contacts
carddav_backend::delete_dbrecord($abookid,'contacts','abook_id');
// ...custom subtypes
carddav_backend::delete_dbrecord($abookid,'xsubtypes','abook_id');
// ...groups and memberships
$delgroups = carddav_backend::get_dbrecord($abookid, 'id as group_id', 'groups', false, 'abook_id');
carddav_backend::delete_dbrecord($abookid,'groups','abook_id');
carddav_backend::delete_dbrecord($delgroups,'group_user','group_id');
}}}
private static function insert_abook($pa)
{{{
$dbh = rcmail::get_instance()->db;
// check parameters
if(array_key_exists('refresh_time', $pa)) {
$pa['refresh_time'] = self::process_cd_time($pa['refresh_time']);
}
/* Ensure field lengths */
if (array_key_exists('name', $pa)) {
if (strlen($pa['name']) > 64){
$pa['name'] = substr($pa['name'], 0, 64);
}
}
if (array_key_exists('username', $pa)) {
if (strlen($pa['username']) > 255){
$pa['username'] = substr($pa['username'], 0, 255);
}
}
if (array_key_exists('presetname', $pa)) {
if (strlen($pa['presetname']) > 255){
$pa['presetname'] = substr($pa['presetname'], 0, 255);
}
}
$pa['user_id'] = $_SESSION['user_id'];
// required fields
$qf=array('name','username','password','url','user_id');
$qv=array();
foreach($qf as $f) {
if(!array_key_exists($f,$pa)) return false;
$qv[] = $pa[$f];
}
// optional fields
$qfo = array('active','presetname','use_categories','refresh_time');
foreach($qfo as $f) {
if(array_key_exists($f,$pa)) {
$qf[] = $f;
$qv[] = $pa[$f];
}
}
$dbh->query('INSERT INTO ' . $dbh->table_name('carddav_addressbooks') .
'('. implode(',',$qf) .') ' .
'VALUES (?'. str_repeat(',?', count($qf)-1) . ')',
$qv
);
}}}
public static function update_abook($abookid, $pa)
{{{
$dbh = rcmail::get_instance()->db;
// check parameters
if(array_key_exists('refresh_time', $pa))
$pa['refresh_time'] = self::process_cd_time($pa['refresh_time']);
// encrypt the password before storing it
if(array_key_exists('password', $pa))
$pa['password'] = self::$helper->encrypt_password($pa['password']);
/* Ensure field lengths */
if (array_key_exists('name', $pa)) {
if (strlen($pa['name']) > 64){
$pa['name'] = substr($pa['name'], 0, 64);
}
}
if (array_key_exists('username', $pa)) {
if (strlen($pa['username']) > 255){
$pa['username'] = substr($pa['username'], 0, 255);
}
}
if (array_key_exists('presetname', $pa)) {
if (strlen($pa['presetname']) > 255){
$pa['presetname'] = substr($pa['presetname'], 0, 255);
}
}
// optional fields
$qfo=array('name','username','password','url','active','refresh_time','sync_token');
$qf=array();
$qv=array();
foreach($qfo as $f) {
if(array_key_exists($f,$pa)) {
$qf[] = $f;
$qv[] = $pa[$f];
}
}
if(count($qf) <= 0) return true;
$qv[] = $abookid;
$dbh->query('UPDATE ' .
$dbh->table_name('carddav_addressbooks') .
' SET ' . implode('=?,', $qf) . '=?' .
' WHERE id=?',
$qv
);
}}}
}
?>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,397 @@
<?php
/*
RCM CardDAV Plugin
Copyright (C) 2011-2016 Benjamin Schieder <rcmcarddav@wegwerf.anderdonau.de>,
Michael Stilkerich <ms@mike2k.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
if (file_exists(__DIR__ . '/vendor/autoload.php'))
require_once __DIR__ . '/vendor/autoload.php';
\Httpful\Bootstrap::init();
class carddav_common
{
const DEBUG = false; // set to true for basic debugging
const DEBUG_HTTP = false; // set to true for debugging raw http stream
const NSDAV = 'DAV:';
const NSCARDDAV = 'urn:ietf:params:xml:ns:carddav';
// admin settings from config.inc.php
private static $admin_settings;
// encryption scheme
public static $pwstore_scheme = 'base64';
private $module_prefix = '';
public function __construct($module_prefix = '')
{{{
$this->module_prefix = $module_prefix;
}}}
public static function concaturl($str, $cat)
{{{
preg_match(";(^https?://[^/]+)(.*);", $str, $match);
$hostpart = $match[1];
$urlpart = $match[2];
// is $cat already a full URL?
if(strpos($cat, '://') !== FALSE) {
return $cat;
}
// is $cat a simple filename?
// then attach it to the URL
if (substr($cat, 0, 1) != "/"){
$urlpart .= "/$cat";
// $cat is a full path, the append it to the
// hostpart only
} else {
$urlpart = $cat;
}
// remove // in the path
$urlpart = preg_replace(';//+;','/',$urlpart);
return $hostpart.$urlpart;
}}}
// log helpers
private function getCaller()
{{{
// determine calling function for debug output
if (version_compare(PHP_VERSION, "5.4", ">=")){
$caller=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,3);
} else {
$caller=debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
$caller=$caller[2]['function'];
return $caller;
}}}
public function warn()
{{{
$caller=self::getCaller();
rcmail::write_log("carddav.warn", $this->module_prefix . "($caller) " . implode(' ', func_get_args()));
}}}
public function debug()
{{{
if(self::DEBUG) {
$caller=self::getCaller();
rcmail::write_log("carddav", $this->module_prefix . "($caller) " . implode(' ', func_get_args()));
}
}}}
public function debug_http()
{{{
if(self::DEBUG_HTTP) {
$caller=self::getCaller();
rcmail::write_log("carddav", $this->module_prefix . "($caller) " . implode(' ', func_get_args()));
}
}}}
// XML helpers
public function checkAndParseXML($reply) {
if(!is_array($reply))
return false;
if(!self::check_contenttype($reply['headers']['content-type'], ';(text|application)/xml;'))
return false;
$xml = new SimpleXMLElement($reply['body']);
$this->registerNamespaces($xml);
return $xml;
}
public function registerNamespaces($xml) {
// Use slightly complex prefixes to avoid conflicts
$xml->registerXPathNamespace('RCMCC', self::NSCARDDAV);
$xml->registerXPathNamespace('RCMCD', self::NSDAV);
}
// HTTP helpers
/**
* @param $url: url of the requested resource
*
* @param $http_opts: Options for the HTTP request, keys:
* - method: request method (GET, PROPFIND, etc.)
* - content: request body
* - header: array of HTTP request headers as simple strings
*
* @param $carddav: config array containing at least the keys
* - url: base url, used if $url is a relative url
* - username
* - password: password (encoded/encrypted form as stored in DB)
*/
public function cdfopen($url, $http_opts, $carddav)
{{{
$redirect_limit = 5;
$rcmail = rcmail::get_instance();
$username=$carddav['username'];
$password = self::decrypt_password($carddav['password']);
$baseurl=$carddav['url'];
// determine calling function for debug output
$caller=self::getCaller();
$local = $rcmail->user->get_username('local');
$domain = $rcmail->user->get_username('domain');
// Substitute Placeholders
$username = str_replace( '%u', $_SESSION['username'], $username);
$username = str_replace( '%V' ,str_replace('@','_', str_replace('.','_',$_SESSION['username'])), $username);
$username = str_replace( '%l', $local, $username);
$username = str_replace( '%d', $domain, $username);
if($password == '%p')
$password = $rcmail->decrypt($_SESSION['password']);
$baseurl = str_replace("%u", $username, $carddav['url']);
$url = str_replace("%u", $username, $url);
$baseurl = str_replace("%l", $local, $baseurl);
$url = str_replace("%l", $local, $url);
$baseurl = str_replace("%d", $domain, $baseurl);
$url = str_replace("%d", $domain, $url);
// if $url is relative, prepend the base url
$url = self::concaturl($baseurl, $url);
do {
$isRedirect = false;
if (self::DEBUG){ $this->debug("$caller requesting $url as user $username [RL $redirect_limit]"); }
$httpful = \Httpful\Request::init();
$scheme = strtolower($carddav['authentication_scheme']);
if ($scheme != "basic" && $scheme != "digest" && $scheme != "negotiate"){
/* figure out authentication */
$httpful->addHeader("User-Agent", "RCM CardDAV plugin/3.0.3");
$httpful->uri($url);
$httpful->method($http_opts['method']);
$error = $httpful->send();
$httpful = \Httpful\Request::init();
$scheme = "unknown";
// Using raw_headers since there might be multiple www-authenticate headers
if (preg_match("/^(.*\n)*WWW-Authenticate:\s+Negotiate\b/i", $error->raw_headers) && !empty($_SERVER['KRB5CCNAME'])){
$httpful->negotiateAuth($username, $password);
$scheme = "negotiate";
} else if (preg_match("/\bDigest\b/i", $error->headers["www-authenticate"])){
$httpful->digestAuth($username, $password);
$scheme = "digest";
} else if (preg_match("/\bBasic\b/i", $error->headers["www-authenticate"])){
$httpful->basicAuth($username, $password);
$scheme = "basic";
}
if ($scheme != "unknown")
carddav_backend::update_addressbook($carddav['abookid'], array("authentication_scheme"), array($scheme));
} else {
// if we have KRB5CCNAME, use negotiate even if current scheme is basic
if ((strtolower($scheme) == "negotiate" || strtolower($scheme) == "basic") && !empty($_SERVER['KRB5CCNAME'])) {
$httpful->negotiateAuth($username, $password);
} else if (strtolower($scheme) == "digest"){
$httpful->digestAuth($username, $password);
// if we don't have KRB5CCNAME, use basic even if current scheme is negotiate
} else if (strtolower($scheme) == "negotiate" || strtolower($scheme) == "basic"){
$httpful->basicAuth($username, $password);
}
}
$httpful->addHeader("User-Agent", "RCM CardDAV plugin/3.0.3");
$httpful->uri($url);
$httpful->method($http_opts['method']);
if (array_key_exists('content',$http_opts) && strlen($http_opts['content'])>0 && $http_opts['method'] != "GET"){
$httpful->body($http_opts['content']);
}
if(array_key_exists('header',$http_opts)) {
foreach ($http_opts['header'] as $header){
$h = explode(": ", $header);
if (strlen($h[0]) > 0 && strlen($h[1]) > 0){
// Only append headers with key AND value
$httpful->addHeader($h[0], $h[1]);
}
}
}
$reply = $httpful->send();
$scode = $reply->code;
if (self::DEBUG){ $this->debug("Code: $scode"); }
$isRedirect = ($scode>300 && $scode<304) || $scode==307;
if($isRedirect && strlen($reply->headers['location'])>0) {
$url = self::concaturl($baseurl, $reply->headers['location']);
} else {
$retVal["status"] = $scode;
$retVal["headers"] = $reply->headers;
$retVal["body"] = $reply->raw_body;
if (self::DEBUG_HTTP){ $this->debug_http("success: ".var_export($retVal, true)); }
return $retVal;
}
} while($redirect_limit-->0 && $isRedirect);
return $reply->code;
}}}
public function check_contenttype($ctheader, $expectedct)
{{{
if(!is_array($ctheader)) {
$ctheader = array($ctheader);
}
foreach($ctheader as $ct) {
if(preg_match($expectedct, $ct))
return true;
}
return false;
}}}
// password helpers
private function carddav_des_key()
{{{
$rcmail = rcmail::get_instance();
$imap_password = $rcmail->decrypt($_SESSION['password']);
while(strlen($imap_password)<24) {
$imap_password .= $imap_password;
}
return substr($imap_password, 0, 24);
}}}
public function encrypt_password($clear)
{{{
if(strcasecmp(self::$pwstore_scheme, 'plain')===0)
return $clear;
if(strcasecmp(self::$pwstore_scheme, 'encrypted')===0) {
// return {IGNORE} scheme if session password is empty (krb_authentication plugin)
if(empty($_SESSION['password'])) return '{IGNORE}';
// encrypted with IMAP password
$rcmail = rcmail::get_instance();
$imap_password = self::carddav_des_key();
$deskey_backup = $rcmail->config->set('carddav_des_key', $imap_password);
$crypted = $rcmail->encrypt($clear, 'carddav_des_key');
// there seems to be no way to unset a preference
$deskey_backup = $rcmail->config->set('carddav_des_key', '');
return '{ENCRYPTED}'.$crypted;
}
if(strcasecmp(self::$pwstore_scheme, 'des_key')===0) {
// encrypted with global des_key
$rcmail = rcmail::get_instance();
$crypted = $rcmail->encrypt($clear);
return '{DES_KEY}'.$crypted;
}
// default: base64-coded password
return '{BASE64}'.base64_encode($clear);
}}}
public function password_scheme($crypt)
{{{
if(strpos($crypt, '{IGNORE}') === 0)
return 'ignore';
if(strpos($crypt, '{ENCRYPTED}') === 0)
return 'encrypted';
if(strpos($crypt, '{DES_KEY}') === 0)
return 'des_key';
if(strpos($crypt, '{BASE64}') === 0)
return 'base64';
// unknown scheme, assume cleartext
return 'plain';
}}}
public function decrypt_password($crypt)
{{{
if(strpos($crypt, '{ENCRYPTED}') === 0) {
// return {IGNORE} scheme if session password is empty (krb_authentication plugin)
if (empty($_SESSION['password'])) return '{IGNORE}';
$crypt = substr($crypt, strlen('{ENCRYPTED}'));
$rcmail = rcmail::get_instance();
$imap_password = self::carddav_des_key();
$deskey_backup = $rcmail->config->set('carddav_des_key', $imap_password);
$clear = $rcmail->decrypt($crypt, 'carddav_des_key');
// there seems to be no way to unset a preference
$deskey_backup = $rcmail->config->set('carddav_des_key', '');
return $clear;
}
if(strpos($crypt, '{DES_KEY}') === 0) {
$crypt = substr($crypt, strlen('{DES_KEY}'));
$rcmail = rcmail::get_instance();
return $rcmail->decrypt($crypt);
}
if(strpos($crypt, '{BASE64}') === 0) {
$crypt = substr($crypt, strlen('{BASE64}'));
return base64_decode($crypt);
}
// unknown scheme, assume cleartext
return $crypt;
}}}
// admin settings from config.inc.php
public static function get_adminsettings()
{{{
if(is_array(self::$admin_settings))
return self::$admin_settings;
$rcmail = rcmail::get_instance();
$prefs = array();
$configfile = dirname(__FILE__)."/config.inc.php";
if (file_exists($configfile)){
require("$configfile");
}
self::$admin_settings = $prefs;
if(is_array($prefs['_GLOBAL'])) {
$scheme = $prefs['_GLOBAL']['pwstore_scheme'];
if(preg_match("/^(plain|base64|encrypted|des_key)$/", $scheme))
self::$pwstore_scheme = $scheme;
}
return $prefs;
}}}
// short form for deprecated Q helper function
public function Q($str, $mode='strict', $newlines=true)
{{{
return rcube_utils::rep_specialchars_output($str, 'html', $mode, $newlines);
}}}
}
?>

View File

@ -0,0 +1,280 @@
<?php
/*
RCM CardDAV Plugin
Copyright (C) 2011-2016 Benjamin Schieder <rcmcarddav@wegwerf.anderdonau.de>,
Michael Stilkerich <ms@mike2k.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
require_once("carddav_backend.php");
require_once("carddav_common.php");
class carddav_discovery
{
private static $helper;
/**
* Determines the location of the addressbook for the current user on the
* CardDAV server.
*
* Returns: Array of found addressbook. Each array element is array with keys:
* - name: Name of the addressbook reported by server
* - href: URL to the addressbook collection
*
* On error, false is returned.
*/
public function find_addressbooks($url, $user, $password)
{{{
if (!preg_match(';^(([^:]+)://)?(([^/:]+)(:([0-9]+))?)(/?.*)$;', $url, $match))
return false;
$protocol = $match[2]; // optional
$host = $match[4]; // mandatory
$port = $match[6]; // optional
$path = $match[7]; // optional
// plain is only used if http was explicitly given
$use_ssl = !($protocol == "http");
// setup default values if no user values given
if($use_ssl) {
$protocol = $protocol?$protocol:'https';
$port = $port ?$port :443;
} else {
$protocol = $protocol?$protocol:'http';
$port = $port ?$port :80;
}
$services = $this->find_servers($host, $use_ssl);
// Fallback: If no DNS provided, we use the data given by the user/defaults
if(count($services) == 0) {
$services[] = array(
'host' => $host,
'port' => ($port ? $port : ($use_ssl ? 443 : 80)),
'baseurl' => "$protocol://$host:$port",
);
}
$services = $this->find_baseurls($services);
// if the user specified a full URL, we try that first
if(strlen($path) > 2) {
$userspecified = array(
'host' => $host,
'port' => ($port ? $port : ($use_ssl ? 443 : 80)),
'baseurl' => "$protocol://$host:$port",
'paths' => array($path),
);
array_unshift($services, $userspecified);
}
$cdfopen_cfg = array('username'=>$user, 'password'=>$password);
// now check each of them until we find something (or don't)
foreach($services as $service) {
$cdfopen_cfg['url'] = $service['baseurl'];
foreach($service['paths'] as $path) {
$aBooks = $this->retrieve_addressbooks($path, $cdfopen_cfg);
if(is_array($aBooks) && count($aBooks)>0)
return $aBooks;
}
}
return false;
}}}
private function retrieve_addressbooks($path, $cdfopen_cfg, $recurse=false)
{{{
$baseurl = $cdfopen_cfg['url'];
$url = carddav_common::concaturl($baseurl, $path);
$depth = ($recurse ? 1 : 0);
self::$helper->debug("SEARCHING $url (Depth: ".$depth.")");
// check if the given URL points to an addressbook
$opts = array(
'method'=>"PROPFIND",
'header'=>array("Depth: " . $depth, 'Content-Type: application/xml; charset="utf-8"'),
'content'=> <<<EOF
<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav"><D:prop>
<D:current-user-principal/>
<D:resourcetype />
<D:displayname />
<C:addressbook-home-set/>
</D:prop></D:propfind>
EOF
);
$reply = self::$helper->cdfopen($url, $opts, $cdfopen_cfg);
$xml = self::$helper->checkAndParseXML($reply);
if($xml === false) return false;
$aBooks = array();
// Check if we found addressbooks at the URL
$xpresult = $xml->xpath('//RCMCD:response[descendant::RCMCD:resourcetype/RCMCC:addressbook]');
foreach($xpresult as $ab) {
self::$helper->registerNamespaces($ab);
$aBook = array();
list($aBook['href']) = $ab->xpath('child::RCMCD:href');
list($aBook['name']) = $ab->xpath('descendant::RCMCD:displayname');
$aBook['href'] = (string) $aBook['href'];
$aBook['name'] = (string) $aBook['name'];
if(strlen($aBook['href']) > 0) {
$aBook['href'] = carddav_common::concaturl($baseurl, $aBook['href']);
self::$helper->debug("found abook: ".$aBook['name']." at ".$aBook['href']);
$aBooks[] = $aBook;
}
}
// found -> done
if(count($aBooks) > 0) return $aBooks;
if ($recurse === false) {
// Check some additional URLs:
$additional_urls = array();
// (1) original URL
$additional_urls[] = $url;
// (2) see if the server told us the addressbook home location
$abookhome = $xml->xpath('//RCMCC:addressbook-home-set/RCMCD:href');
if (count($abookhome) != 0) {
self::$helper->debug("addressbook home: ".$abookhome[0]);
$additional_urls[] = $abookhome[0];
}
// (3) see if we got a principal URL
$princurl = $xml->xpath('//RCMCD:current-user-principal/RCMCD:href');
if (count($princurl) != 0) {
self::$helper->debug("principal URL: ".$princurl[0]);
$additional_urls[] = $princurl[0];
}
foreach($additional_urls as $other_url) {
self::$helper->debug("Searching additional URL: $other_url");
if(strlen($other_url) <= 0) continue;
// if the server returned a full URL, adjust the base url
if (preg_match(';^[^/]+://[^/]+;', $other_url, $match)) {
$cdfopen_cfg['url'] = $match[0];
} else {
// restore original baseurl, may have changed in prev URL check
$cdfopen_cfg['url'] = $baseurl;
}
$aBooks = $this->retrieve_addressbooks($other_url, $cdfopen_cfg, $other_url != $princurl[0]);
// found -> done
if (!($aBooks === false) && count($aBooks) > 0) return $aBooks;
}
}
// (4) there is no more we can do -> fail
self::$helper->debug("no principal URL found");
return false;
}}}
// get services by querying DNS SRV records
private function find_servers($host, $ssl)
{{{
if($ssl) {
$srvpfx = '_carddavs';
$defport = 443;
$protocol = 'https';
} else {
$srvpfx = '_carddav';
$defport = 80;
$protocol = 'http';
}
$srv = "$srvpfx._tcp.$host";
// query SRV records
$dnsresults = dns_get_record($srv, DNS_SRV);
// order according to priority and weight
// TODO weight is not quite correctly handled atm, see RFC2782,
// but this is not crucial to functionality
$sortPrioWeight = function($a, $b) {
if ($a['pri'] != $b['pri']) {
return $b['pri'] - $a['pri'];
}
return $a['weight'] - $b['weight'];
};
usort($dnsresults, $sortPrioWeight);
// build results
$result = array();
foreach($dnsresults as $dnsres) {
$target = $dnsres['target'];
$port = $dnsres['port'] ? $dnsres['port'] : $defport;
$baseurl = "$protocol://$target:$port";
if($target) {
self::$helper->debug("found service: $baseurl");
$result[] = array(
'host' => $target,
'port' => $port,
'baseurl' => $baseurl,
'dnssrv' => "$srvpfx._tcp.$target",
);
}
}
return $result;
}}}
// discover path and add default paths to services
private function find_baseurls($services)
{{{
foreach($services as &$service) {
$baseurl = $service['baseurl'];
$dnssrv = $service['dnssrv'];
$paths = array();
$dnsresults = dns_get_record($dnssrv, DNS_TXT);
foreach($dnsresults as $dnsresult) {
if($dnsresult['host'] != $dnssrv) continue;
foreach($dnsresult['entries'] as $ent) {
if (preg_match('^path=(.+)', $ent, $match))
$paths[] = $match[1];
}
}
// as fallback try these default paths
$paths[] = '/.well-known/carddav';
$paths[] = '/';
$service['paths'] = $paths;
}
return $services;
}}}
public static function initClass()
{{{
self::$helper = new carddav_common('DISCOVERY: ');
}}}
}
carddav_discovery::initClass();
?>

View File

@ -0,0 +1,38 @@
{
"name": "roundcube/carddav",
"type": "roundcube-plugin",
"description": "CardDAV adapter for connecting to CardDAV-enabled addressbooks",
"keywords": ["addressbook","carddav","contacts","owncloud","davical"],
"homepage": "https://www.benjamin-schieder.de/carddav.html",
"license": "GPL-2.0",
"version": "3.0.3",
"authors": [
{
"name": "Benjamin Schieder",
"email": "carddav@wegwerf.anderdonau.de",
"homepage": "https://www.benjamin.schieder.de/",
"role": "Developer"
},
{
"name": "Michael Stilkerich",
"email": "michael@stilkerich.eu",
"role": "Developer"
}
],
"repositories": [
{
"type": "vcs",
"url": "git://github.com/blind-coder/rcmcarddav.git"
}
],
"require": {
"php": ">=5.6.18",
"nategood/httpful": "~0.2",
"sabre/vobject": "~3.4"
},
"extra": {
"roundcube": {
"min-version": "1.0.0"
}
}
}

136
plugins/carddav/composer.lock generated Normal file
View File

@ -0,0 +1,136 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c645717601239af47e93d45ac34cb26e",
"packages": [
{
"name": "nategood/httpful",
"version": "0.2.20",
"source": {
"type": "git",
"url": "https://github.com/nategood/httpful.git",
"reference": "c1cd4d46a4b281229032cf39d4dd852f9887c0f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nategood/httpful/zipball/c1cd4d46a4b281229032cf39d4dd852f9887c0f6",
"reference": "c1cd4d46a4b281229032cf39d4dd852f9887c0f6",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"psr-0": {
"Httpful": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nate Good",
"email": "me@nategood.com",
"homepage": "http://nategood.com"
}
],
"description": "A Readable, Chainable, REST friendly, PHP HTTP Client",
"homepage": "http://github.com/nategood/httpful",
"keywords": [
"api",
"curl",
"http",
"requests",
"rest",
"restful"
],
"time": "2015-10-26T16:11:30+00:00"
},
{
"name": "sabre/vobject",
"version": "3.5.3",
"source": {
"type": "git",
"url": "https://github.com/sabre-io/vobject.git",
"reference": "129d80533a9ec0d9cacfb50b51180c34edb6874c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sabre-io/vobject/zipball/129d80533a9ec0d9cacfb50b51180c34edb6874c",
"reference": "129d80533a9ec0d9cacfb50b51180c34edb6874c",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.1"
},
"require-dev": {
"phpunit/phpunit": "*",
"squizlabs/php_codesniffer": "*"
},
"bin": [
"bin/vobject",
"bin/generate_vcards"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabre\\VObject\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Evert Pot",
"email": "me@evertpot.com",
"homepage": "http://evertpot.com/",
"role": "Developer"
},
{
"name": "Dominik Tobschall",
"email": "dominik@fruux.com",
"homepage": "http://tobschall.de/",
"role": "Developer"
}
],
"description": "The VObject library for PHP allows you to easily parse and manipulate iCalendar and vCard objects",
"homepage": "http://sabre.io/vobject/",
"keywords": [
"VObject",
"iCalendar",
"jCal",
"jCard",
"vCard"
],
"time": "2016-10-07T03:20:40+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.6.18"
},
"platform-dev": []
}

View File

@ -0,0 +1,217 @@
<?php
//// RCMCardDAV Plugin Admin Settings
//// ** GLOBAL SETTINGS
// Disallow users to add / edit / delete custom addressbooks (default: false)
//
// If true, User cannot add custom addressbooks
// If false, user can add / edit / delete custom addressbooks
//
// This option only affects custom addressbooks. Preset addressbooks (see below)
// are not affected.
// $prefs['_GLOBAL']['fixed'] = true;
// When enabled, this option hides the 'CardDAV' section inside Preferences.
// $prefs['_GLOBAL']['hide_preferences'] = true;
// Scheme for storing the CardDAV passwords, in order from least to best security.
// Options:
// plain: store as plaintext
// base64: store encoded with base64 (default)
// des_key: store encrypted with global des_key of roundcube
// encrypted: store encrypted with IMAP password of the user
// NOTE: if the IMAP password of the user changes, the stored
// CardDAV passwords cannot be decrypted anymore and the user
// needs to reenter them.
// $prefs['_GLOBAL']['pwstore_scheme'] = 'base64';
// Allow suppression of the warning that PHP is too old.
//
// If true, the PHP version is not checked. Use at own risk.
// If false, the PHP version is checked and RCMCardDAV will not run if it is
// too old.
$prefs['_GLOBAL']['suppress_version_warning'] = false;
// Enable a workaround for broken sync-collection support in the
// server. RFC 6578 specifies the "sync-collection" method for
// synchronizing collections of things over WebDAV. It is more
// efficient -- but also more complicated -- than simply retrieving
// the whole collection again as necessary. As a result, some server
// implementations are buggy. Specifically DAViCal and Radicale are
// known to have problems. If changes (updates, deletions) from one
// connection do not sync to another, you can try enabling this
// workaround to revert to the inefficient-but-simple method.
$prefs['_GLOBAL']['sync_collection_workaround'] = false;
//// ** ADDRESSBOOK PRESETS
// Each addressbook preset takes the following form:
/*
$prefs['<Presetname>'] = array(
// required attributes
'name' => '<Addressbook Name>',
'username' => '<CardDAV Username>',
'password' => '<CardDAV Password>',
'url' => '<CardDAV URL>',
// optional attributes
'active' => <true or false>,
'readonly' => <true or false>,
'refresh_time' => '<Refresh Time in Hours, Format HH[:MM[:SS]]>',
// attributes that are fixed (i.e., not editable by the user) and
// auto-updated for this preset
'fixed' => array( < 0 or more of the other attribute keys > ),
// always require these attributes, even for addressbook view
'require_always' => ['email'],
// hide this preset from CalDAV preferences section so users can't even
// see it
'hide' => <true or false>,
);
*/
// All values in angle brackets <VALUE> have to be substituted.
//
// The meaning of the different parameters is as follows:
//
// <Presetname>: Unique preset name, must not be '_GLOBAL'. The presetname is
// not user visible and only used for an internal mapping between
// addressbooks created from a preset and the preset itself. You
// should never change this throughout its lifetime.
//
// The following parameters are REQUIRED and need to be specified for any preset.
//
// name: User-visible name of the addressbook. If the server provides
// an additional display name for the addressbooks found for the
// preset, it will be appended in brackets to this name, except
// if carddav_name_only is true (see below).
//
// username: CardDAV username to access the addressbook. Set this setting
// to '%u' to use the roundcube username.
// In case one uses an email address as username there is the
// additional option to choose '%l', which will only use the
// local part of the username (eg: user.name@example.com will
// become user.name).
// Also, %d is available to get only the domain part of the
// username (eg: user.name@example.com will become example.com).
// In some cases %V might be usefull: it replaces @ and . by _.
// (eg: user.name@example.com will become user_name_example_com)
//
// password: CardDAV password to access the addressbook. Set this setting
// to '%p' to use the roundcube password. The password will not
// be stored in the database when using %p.
//
// url: URL where to find the CardDAV addressbook(s). If the given URL
// refers directly to an addressbook, only this single
// addressbook will be added. If the URL points somewhere in the
// CardDAV space, but _not_ to the location of a particular
// addressbook, the server will be queried for the available
// addressbooks and all of them will be added. You can use %u
// within the URL as a placeholder for the CardDAV username.
// '%l' works the same way as it does for the username field.
//
// The following parameters are OPTIONAL and need to be specified only if the default
// value is not acceptable.
//
// active: If this parameter is false, the addressbook is not used by roundcube
// unless the user changes this setting.
// Default: true
//
// carddav_name_only:
// If this parameter is true, only the server provided displayname
// is used for addressbooks created from this preset, except if
// the server does not provide a display name.
// Default: false
//
// readonly: If this parameter is true, the addressbook will only be
// accessible in read-only mode, i.e., the user will not be able
// to add, modify or delete contacts in the addressbook.
// Default: false
//
// refresh_time: Time interval for that cached versions of the addressbook
// entries should be used, in hours. After this time interval has
// passed since the last pull from the server, it will be
// refreshed when the addressbook is accessed the next time.
// Default: 01:00:00
//
// use_categories: If this parameter is true, the addressbook will use the
// categories as groups unless the user changes this setting.
// Default: false
//
// fixed: Array of parameter keys that must not be changed by the user.
// Note that only fixed parameters will be automatically updated
// for existing addressbooks created from presets. Otherwise the
// user may already have changed the setting, and his change
// would be lost. You can add any of the above keys, but it the
// setting only affects parameters that can be changed via the
// settings pane (e.g., readonly cannot be changed by the user
// anyway). Still only parameters listed as fixed will
// automatically updated if the preset is changed.
// Default: empty, all settings modifiable by user
//
// !!! WARNING: Only add 'url' to the list of fixed addressbooks
// if it _directly_ points to an address book collection.
// Otherwise, the plugin will initially lookup the URLs for the
// collections on the server, and at the next login overwrite it
// with the fixed value stored here. Therefore, if you change the
// URL, you have two options:
// 1) If the new URL is a variation of the old one (e.g. hostname
// change), you can run an SQL UPDATE query directly in the
// database to adopt all addressbooks.
// 2) If the new URL is not easily derivable from the old one,
// change the key of the preset and change the URL. Addressbooks
// belonging to the old preset will be deleted upon the next
// login of the user and freshly created.
//
// require_always: If set, this database field is required to be non-empty
// for ALL queries, even just for displaying members. This may be
// useful if you have shared, read-only addressbooks with a lot
// of contacts that do not have an email address.
//
// hide: Whether this preset should be hidden from the CalDAV listing
// on the preferences page.
// How Preset Updates work
//
// Preset addressbooks are created for a user as she logs in.
//// ** ADDRESSBOOK PRESETS - EXAMPLE: Two Addressbook Presets
//// Preset 1: Personal
/*
$prefs['Personal'] = array(
// required attributes
'name' => 'Personal',
// will be substituted for the roundcube username
'username' => '%u',
// will be substituted for the roundcube password
'password' => '%p',
// %u will be substituted for the CardDAV username
'url' => 'https://ical.example.org/caldav.php/%u/Personal',
'active' => true,
'readonly' => false,
'refresh_time' => '02:00:00',
'fixed' => array( 'username' ),
'hide' => false,
);
*/
//// Preset 2: Corporate
/*
$prefs['Work'] = array(
'name' => 'Corporate',
'username' => 'CorpUser',
'password' => 'C0rpPasswo2d',
'url' => 'https://ical.example.org/caldav.php/%u/Corporate',
'fixed' => array( 'name', 'username', 'password' ),
'hide' => true,
);
*/

View File

@ -0,0 +1,84 @@
-- table to store the configured address books
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_addressbooks (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
username VARCHAR(64) NOT NULL,
password VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
active TINYINT UNSIGNED NOT NULL DEFAULT 1,
user_id INT(10) UNSIGNED NOT NULL,
last_updated TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:01', -- time stamp of the last update of the local database
refresh_time TIME NOT NULL DEFAULT '01:00:00', -- time span after that the local database will be refreshed, default 1h
sync_token VARCHAR(255) NOT NULL DEFAULT '', -- sync-token the server sent us for the last sync
authentication_scheme VARCHAR(64) NOT NULL DEFAULT "auto", -- the HTTP authentication scheme to use, auto will be overwritten
presetname VARCHAR(64), -- presetname
PRIMARY KEY(id),
FOREIGN KEY (user_id) REFERENCES TABLE_PREFIXusers(user_id) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET utf8 COLLATE utf8_unicode_ci /*!40000 ENGINE=INNODB */;
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_contacts (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
abook_id INT UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL, -- display name
email VARCHAR(255), -- ", " separated list of mail addresses
firstname VARCHAR(255),
surname VARCHAR(255),
organization VARCHAR(255),
showas VARCHAR(32) NOT NULL DEFAULT '', -- special display type (e.g., as a company)
vcard LONGTEXT NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
PRIMARY KEY(id),
INDEX (abook_id),
UNIQUE INDEX(uri,abook_id),
UNIQUE INDEX(cuid,abook_id),
FOREIGN KEY (abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET utf8 COLLATE utf8_unicode_ci /*!40000 ENGINE=INNODB */;
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_xsubtypes (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
typename VARCHAR(128) NOT NULL, -- name of the type
subtype VARCHAR(128) NOT NULL, -- name of the subtype
abook_id INT UNSIGNED NOT NULL,
PRIMARY KEY(id),
UNIQUE INDEX(typename,subtype,abook_id),
FOREIGN KEY (abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET utf8 COLLATE utf8_unicode_ci /*!40000 ENGINE=INNODB */;
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_groups (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
abook_id INT UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL, -- display name
vcard TEXT NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
PRIMARY KEY(id),
UNIQUE(uri,abook_id),
UNIQUE(cuid,abook_id),
FOREIGN KEY (abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET utf8 COLLATE utf8_unicode_ci /*!40000 ENGINE=INNODB */;
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_group_user (
group_id INT UNSIGNED NOT NULL,
contact_id INT UNSIGNED NOT NULL,
PRIMARY KEY(group_id,contact_id),
FOREIGN KEY(group_id) REFERENCES TABLE_PREFIXcarddav_groups(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(contact_id) REFERENCES TABLE_PREFIXcarddav_contacts(id) ON DELETE CASCADE ON UPDATE CASCADE
) CHARACTER SET utf8 COLLATE utf8_unicode_ci /*!40000 ENGINE=INNODB */;
CREATE TABLE TABLE_PREFIXcarddav_migrations (
`ID` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`filename` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL ,
`processed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
UNIQUE (
`filename`
)
) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

View File

@ -0,0 +1,105 @@
CREATE SEQUENCE TABLE_PREFIXcarddav_addressbooks_seq
INCREMENT BY 1
NO MAXVALUE
MINVALUE 1
CACHE 1;
-- table to store the configured address books
CREATE TABLE TABLE_PREFIXcarddav_addressbooks (
id integer DEFAULT nextval('TABLE_PREFIXcarddav_addressbooks_seq'::text) PRIMARY KEY,
name VARCHAR(64) NOT NULL,
username VARCHAR(64) NOT NULL,
password VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
active SMALLINT NOT NULL DEFAULT 1,
user_id integer NOT NULL REFERENCES TABLE_PREFIXusers (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
last_updated TIMESTAMP NOT NULL DEFAULT '-infinity', -- time stamp of the last update of the local database
refresh_time INTERVAL NOT NULL DEFAULT '01:00:00', -- time span after that the local database will be refreshed, default 1h
sync_token VARCHAR(255) NOT NULL DEFAULT '', -- sync-token the server sent us for the last sync
authentication_scheme VARCHAR(64) NOT NULL DEFAULT 'auto', -- the HTTP authentication scheme to use, auto will be overwritten
presetname VARCHAR(64) -- presetname
);
CREATE SEQUENCE TABLE_PREFIXcarddav_contacts_seq
INCREMENT BY 1
NO MAXVALUE
MINVALUE 1
CACHE 1;
CREATE TABLE TABLE_PREFIXcarddav_contacts (
id integer DEFAULT nextval('TABLE_PREFIXcarddav_contacts_seq'::text) PRIMARY KEY,
abook_id integer NOT NULL REFERENCES TABLE_PREFIXcarddav_addressbooks (id) ON DELETE CASCADE ON UPDATE CASCADE,
name VARCHAR(255) NOT NULL, -- display name
email VARCHAR(255), -- ", " separated list of mail addresses
firstname VARCHAR(255),
surname VARCHAR(255),
organization VARCHAR(255),
showas VARCHAR(32) NOT NULL DEFAULT '', -- special display type (e.g., as a company)
vcard text NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
UNIQUE(uri,abook_id),
UNIQUE(cuid,abook_id)
);
CREATE INDEX TABLE_PREFIXcarddav_contacts_abook_id_idx ON TABLE_PREFIXcarddav_contacts(abook_id);
CREATE SEQUENCE TABLE_PREFIXcarddav_xsubtypes_seq
INCREMENT BY 1
NO MAXVALUE
MINVALUE 1
CACHE 1;
CREATE TABLE TABLE_PREFIXcarddav_xsubtypes (
id integer DEFAULT nextval('TABLE_PREFIXcarddav_xsubtypes_seq'::text) PRIMARY KEY,
typename VARCHAR(128) NOT NULL, -- name of the type
subtype VARCHAR(128) NOT NULL, -- name of the subtype
abook_id integer NOT NULL REFERENCES TABLE_PREFIXcarddav_addressbooks (id) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE (typename,subtype,abook_id)
);
CREATE SEQUENCE TABLE_PREFIXcarddav_groups_seq
INCREMENT BY 1
NO MAXVALUE
MINVALUE 1
CACHE 1;
CREATE TABLE TABLE_PREFIXcarddav_groups (
id integer DEFAULT nextval('TABLE_PREFIXcarddav_groups_seq'::text) PRIMARY KEY,
abook_id integer NOT NULL REFERENCES TABLE_PREFIXcarddav_addressbooks (id) ON DELETE CASCADE ON UPDATE CASCADE,
name VARCHAR(255) NOT NULL, -- display name
vcard TEXT NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
UNIQUE(uri,abook_id),
UNIQUE(cuid,abook_id)
);
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_group_user (
group_id integer NOT NULL,
contact_id integer NOT NULL,
PRIMARY KEY(group_id,contact_id),
FOREIGN KEY(group_id) REFERENCES TABLE_PREFIXcarddav_groups(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(contact_id) REFERENCES TABLE_PREFIXcarddav_contacts(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE SEQUENCE TABLE_PREFIXcarddav_migrations_seq
INCREMENT BY 1
NO MAXVALUE
MINVALUE 1
CACHE 1;
CREATE TABLE TABLE_PREFIXcarddav_migrations (
ID integer DEFAULT nextval('TABLE_PREFIXcarddav_migrations_seq'::text) PRIMARY KEY,
filename VARCHAR(64) NOT NULL,
processed_at TIMESTAMP NOT NULL DEFAULT now(),
UNIQUE(filename)
);

View File

@ -0,0 +1,89 @@
-- table to store the finished migrations
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_migrations (
ID integer NOT NULL PRIMARY KEY,
filename VARCHAR(64) NOT NULL,
processed_at TIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE(filename)
);
-- table to store the configured address books
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_addressbooks (
id integer NOT NULL PRIMARY KEY,
name VARCHAR(64) NOT NULL,
username VARCHAR(64) NOT NULL,
password VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
active TINYINT UNSIGNED NOT NULL DEFAULT 1,
user_id integer NOT NULL,
last_updated DATETIME NOT NULL DEFAULT 0, -- time stamp of the last update of the local database
refresh_time TIME NOT NULL DEFAULT '01:00:00', -- time span after that the local database will be refreshed
sync_token VARCHAR(255) NOT NULL DEFAULT '', -- sync-token the server sent us for the last sync
authentication_scheme VARCHAR(64) NOT NULL DEFAULT "auto", -- the HTTP authentication scheme to use, auto will be overwritten
presetname VARCHAR(64), -- presetname
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(user_id) REFERENCES TABLE_PREFIXusers(user_id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_contacts (
id integer NOT NULL PRIMARY KEY,
abook_id integer NOT NULL,
name VARCHAR(255) NOT NULL, -- display name
email VARCHAR(255), -- ", " separated list of mail addresses
firstname VARCHAR(255),
surname VARCHAR(255),
organization VARCHAR(255),
showas VARCHAR(32) NOT NULL DEFAULT '', -- special display type (e.g., as a company)
vcard TEXT NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
UNIQUE(uri,abook_id),
UNIQUE(cuid,abook_id),
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE INDEX TABLE_PREFIXcarddav_contacts_abook_id_idx ON TABLE_PREFIXcarddav_contacts(abook_id);
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_xsubtypes (
id integer NOT NULL PRIMARY KEY,
typename VARCHAR(128) NOT NULL, -- name of the type
subtype VARCHAR(128) NOT NULL, -- name of the subtype
abook_id integer NOT NULL,
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE(typename,subtype,abook_id)
);
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_groups (
id integer NOT NULL PRIMARY KEY,
abook_id integer NOT NULL,
name VARCHAR(255) NOT NULL, -- display name
vcard TEXT NOT NULL, -- complete vcard
etag VARCHAR(255) NOT NULL, -- entity tag, can be used to check if card changed on server
uri VARCHAR(255) NOT NULL, -- path of the card on the server
cuid VARCHAR(255) NOT NULL, -- unique identifier of the card within the collection
UNIQUE(uri,abook_id),
UNIQUE(cuid,abook_id),
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(abook_id) REFERENCES TABLE_PREFIXcarddav_addressbooks(id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_group_user (
group_id integer NOT NULL,
contact_id integer NOT NULL,
PRIMARY KEY(group_id,contact_id),
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(group_id) REFERENCES TABLE_PREFIXcarddav_groups(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY(contact_id) REFERENCES TABLE_PREFIXcarddav_contacts(id) ON DELETE CASCADE ON UPDATE CASCADE
);

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- Just an example migration.

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- Just an example migration.

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- Just an example migration.

View File

@ -0,0 +1 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks ADD `use_categories` INT NOT NULL DEFAULT '0' AFTER `presetname`;

View File

@ -0,0 +1 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks ADD COLUMN use_categories SMALLINT NOT NULL DEFAULT 0;

View File

@ -0,0 +1 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks ADD use_categories TINYINT NOT NULL DEFAULT 0;

View File

@ -0,0 +1,2 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks MODIFY COLUMN `username` VARCHAR(255);
ALTER TABLE TABLE_PREFIXcarddav_addressbooks MODIFY COLUMN `presetname` VARCHAR(255);

View File

@ -0,0 +1,2 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks ALTER COLUMN username TYPE VARCHAR(255);
ALTER TABLE TABLE_PREFIXcarddav_addressbooks ALTER COLUMN presetname TYPE VARCHAR(255);

View File

@ -0,0 +1,32 @@
PRAGMA foreign_keys=OFF;
CREATE TABLE IF NOT EXISTS TABLE_PREFIXcarddav_addressbooks_X (
id integer NOT NULL PRIMARY KEY,
name VARCHAR(64) NOT NULL,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
active TINYINT UNSIGNED NOT NULL DEFAULT 1,
user_id integer NOT NULL,
last_updated DATETIME NOT NULL DEFAULT 0, -- time stamp of the last update of the local database
refresh_time TIME NOT NULL DEFAULT '01:00:00', -- time span after that the local database will be refreshed
sync_token VARCHAR(255) NOT NULL DEFAULT '', -- sync-token the server sent us for the last sync
authentication_scheme VARCHAR(64) NOT NULL DEFAULT "auto", -- the HTTP authentication scheme to use, auto will be overwritten
presetname VARCHAR(255), -- presetname
use_categories TINYINT NOT NULL DEFAULT 0,
-- not enforced by sqlite < 3.6.19
FOREIGN KEY(user_id) REFERENCES TABLE_PREFIXusers(user_id) ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO TABLE_PREFIXcarddav_addressbooks_X SELECT * FROM TABLE_PREFIXcarddav_addressbooks;
DROP TABLE TABLE_PREFIXcarddav_addressbooks;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks_X RENAME TO TABLE_PREFIXcarddav_addressbooks;
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;

View File

@ -0,0 +1 @@
This migration has been disabled because it was broken. It got fixed and replaced by 0004-fixtimestampdefaultvalue.

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- Just an example migration.

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1 @@
ALTER TABLE TABLE_PREFIXcarddav_addressbooks MODIFY COLUMN `last_updated` TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:01';

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1,56 @@
-- table to store the configured address books
ALTER TABLE TABLE_PREFIXcarddav_addressbooks DROP INDEX `user_id`, ADD UNIQUE `user_id` (`user_id`, `presetname`(191)) USING BTREE;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `name` `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `username` `username` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `password` `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `url` `url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `sync_token` `sync_token` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `authentication_scheme` `authentication_scheme` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_addressbooks CHANGE `presetname` `presetname` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts DROP INDEX `uri`, ADD UNIQUE `uri` (`uri`(191), `abook_id`) USING BTREE;
ALTER TABLE TABLE_PREFIXcarddav_contacts DROP INDEX `cuid`, ADD UNIQUE `cuid` (`cuid`(191), `abook_id`) USING BTREE;
ALTER TABLE TABLE_PREFIXcarddav_contacts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `name` `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `email` `email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `firstname` `firstname` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `surname` `surname` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `organization` `organization` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `showas` `showas` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `vcard` `vcard` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `etag` `etag` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `uri` `uri` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_contacts CHANGE `cuid` `cuid` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_groups DROP INDEX `uri`, ADD UNIQUE `uri` (`uri`(191), `abook_id`) USING BTREE;
ALTER TABLE TABLE_PREFIXcarddav_groups DROP INDEX `cuid`, ADD UNIQUE `cuid` (`cuid`(191), `abook_id`) USING BTREE;
ALTER TABLE TABLE_PREFIXcarddav_groups CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_groups CHANGE `name` `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_groups CHANGE `etag` `etag` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_groups CHANGE `uri` `uri` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_groups CHANGE `cuid` `cuid` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_group_user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_migrations CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_migrations CHANGE `filename` `filename` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_xsubtypes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_xsubtypes CHANGE `typename` `typename` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE TABLE_PREFIXcarddav_xsubtypes CHANGE `subtype` `subtype` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
REPAIR TABLE TABLE_PREFIXcarddav_addressbooks;
REPAIR TABLE TABLE_PREFIXcarddav_contacts;
REPAIR TABLE TABLE_PREFIXcarddav_groups;
REPAIR TABLE TABLE_PREFIXcarddav_group_user;
REPAIR TABLE TABLE_PREFIXcarddav_migrations;
REPAIR TABLE TABLE_PREFIXcarddav_xsubtypes;
OPTIMIZE TABLE TABLE_PREFIXcarddav_addressbooks;
OPTIMIZE TABLE TABLE_PREFIXcarddav_contacts;
OPTIMIZE TABLE TABLE_PREFIXcarddav_groups;
OPTIMIZE TABLE TABLE_PREFIXcarddav_group_user;
OPTIMIZE TABLE TABLE_PREFIXcarddav_migrations;
OPTIMIZE TABLE TABLE_PREFIXcarddav_xsubtypes;

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1 @@
SELECT * FROM TABLE_PREFIXcarddav_migrations; -- No migration

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Aktivovat adresář kontaktů CardDAV",
'cd_newabboxtitle'=> "Přidat nový adresář kontaktů CardDAV",
'cd_username'=> "Uživatel",
'cd_password'=> "Heslo",
'cd_url'=> "URL",
'cd_php_too_old'=> "\"Nainstalovaná verze PHP je zastaralá. Aktualizujte PHP na verzi 5.6.18 nebo vyšší! V současnosti máte nainstalovanou následující verzi:",
'cd_name'=> "Název adresáře kontaktů CardDAV",
'cd_delete'=> "Odstranit tento adresář kontaktů",
'cd_name_new'=> "Nastavit nový adresář kontaktů",
'cd_refresh_time'=> "Interval aktualizace (hodiny)",
'cd_err_noabfound'=> "no addressbook found",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
);
?>

View File

@ -0,0 +1,21 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "CardDAV-Adressbuch aktivieren",
'cd_newabboxtitle'=> "Neues Adressbuch hinzufügen",
'cd_username'=> "Benutzername",
'cd_password'=> "Passwort",
'cd_url'=> "URL",
'cd_php_too_old'=> "Ihre PHP Version ist zu alt! Es wird mindestens 5.6.18 benoetigt! Sie haben folgende Version installiert:",
'cd_name'=> "Name des Adressbuches",
'cd_use_categories' => "Modernen Gruppenmechanismus verwenden\n(Alte RCMCardDAV Gruppen gehen verloren!)",
'cd_delete'=> "Dieses Adressbuch entfernen",
'cd_name_new'=> "Neues Adressbuch konfigurieren",
'cd_refresh_time'=> "Aktualisierungsintervall (Stunden)",
'cd_err_noabfound'=> "Kein Adressbuch gefunden.",
'cd_preemptive_auth'=> "Immer am Server authentifizieren. Für ownCloud aktivieren!",
'cd_enabled' => 'Aktiv',
'cd_disabled' => 'Inaktiv',
'cd_frompreset' => ' (aus Vorgabe _PRESETNAME_)',
);
?>

View File

@ -0,0 +1,21 @@
<?php
$labels = Array(
'cd_title' => 'CardDAV',
'cd_active' => 'Activate CardDAV-Addressbook',
'cd_newabboxtitle'=> "Add new addressbook",
'cd_username' => 'Username',
'cd_password' => 'Password',
'cd_url' => 'URL',
'cd_php_too_old' => "Your version of PHP is too old! Please update to at least 5.6.18! You got the following version installed:",
'cd_name' => "Name of the addressbook",
'cd_use_categories' => "Use modern group mechanism\n(you will lose old RCMCardDAV groups!)",
'cd_delete' => "Remove this addressbook",
'cd_name_new' => "Configure new addressbook",
'cd_refresh_time' => "Update interval (hours)",
'cd_err_noabfound' => 'no addressbook found',
'cd_preemptive_auth' => 'Preemptively authenticate against server. Use for ownCloud!',
'cd_enabled' => 'Enabled',
'cd_disabled' => 'Disabled',
'cd_frompreset' => ' (from preset _PRESETNAME_)',
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Activar Libreta de Direcciones CardDAV",
'cd_newabboxtitle'=> "Añadir nueva Libreta de Direcciones",
'cd_username'=> "Nombre de usuario",
'cd_password'=> "Contraseña",
'cd_url'=> "URL",
'cd_php_too_old'=> "La versión de PHP es demasiado antigua! Por favor, actualiza al menos a la 5.6.18! La versión instalada es la siguiente:",
'cd_name'=> "Nombre de la libreta de direcciones",
'cd_delete'=> "Eliminar esta libreta de direcciones",
'cd_name_new'=> "Configurar una nueva libreta de direcciones",
'cd_refresh_time'=> "Intervalo de actualización (horas)",
'cd_err_noabfound'=> "libreta de direcciones no encontrada",
'cd_preemptive_auth'=> "Autenticar previamente contra el servidor. Necesario para OwnCloud!",
);
?>

View File

@ -0,0 +1,20 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Activer ce carnet d'adresses",
'cd_newabboxtitle'=> "Ajouter un nouveau carnet d'adresses",
'cd_username'=> "Nom d'utilisateur",
'cd_password'=> "Mot de passe",
'cd_url'=> "URL",
'cd_php_too_old'=> "Votre version de PHP est trop ancienne. Veuillez mettre à jour avec la version 5.6.18 minimum! La version actuellement installée est :",
'cd_name'=> "Nom du carnet d'adresses",
'cd_use_categories' => "Utilise les categories comme des groupes",
'cd_delete'=> "Supprimer ce carnet d'adresses",
'cd_name_new'=> "Ajouter un nouveau carnet d'adresses",
'cd_refresh_time'=> "Validité du cache local (en heures)",
'cd_err_noabfound'=> "Aucun carnet d'adresses trouvé",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
'cd_enabled'=>'Activé',
'cd_disabled'=>'Désactivé',
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "CardDAV címjegyzék aktiválása",
'cd_newabboxtitle'=> "Add new addressbook",
'cd_username'=> "Felhasználónév",
'cd_password'=> "Jelszó",
'cd_url'=> "URL",
'cd_php_too_old'=> "A PHP verziód túl régi, e program futásához legalább 5.6.18-ás verzióra van szükség. A jelenlegi PHP verzió:",
'cd_name'=> "Címjegyzék neve",
'cd_delete'=> "Címjegyzék törlése",
'cd_name_new'=> "Címjegyzék beállításai",
'cd_refresh_time'=> "Frissítési intervallum (óra)",
'cd_err_noabfound'=> "no addressbook found",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Aktifkan buku alamat berbasis CardDAV",
'cd_newabboxtitle'=> "Add new addressbook",
'cd_username'=> "Nama pengguna",
'cd_password'=> "Kata sandi",
'cd_url'=> "URL",
'cd_php_too_old'=> "Versi PHP yang digunakan adalah versi yang terlalu tua! Mohon di-update sedikitnya ke versi 5.6.18! Versi PHP yang digunakan oleh Anda:",
'cd_name'=> "Nama buku alamat",
'cd_delete'=> "Menghapuskan buku alamat ini",
'cd_name_new'=> "Mengkonfigurasi buku alamat baru",
'cd_refresh_time'=> "Interval update (dalam jam)",
'cd_err_noabfound'=> "no addressbook found",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Utilizza rubrica CardDAV",
'cd_newabboxtitle'=> "Aggiungi una nuova Rubrica",
'cd_username'=> "Nome utente",
'cd_password'=> "Password",
'cd_url'=> "Indirizzo server (URL)",
'cd_php_too_old'=> "E' necessario aggiornare PHP alla versione 5.6.18 o superiore! Attualmente hai la versione:",
'cd_name'=> "Nome nuova rubrica",
'cd_delete'=> "Elimina rubrica",
'cd_name_new'=> "Crea una nuova rubrica",
'cd_refresh_time'=> "Frequenza di aggiornamento (ore)",
'cd_err_noabfound'=> "Nessuna Rubrica Trovata",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
);
?>

View File

@ -0,0 +1,18 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Aktywuj książkę adresową CardDAV",
'cd_newabboxtitle'=> "Dodaj nową książkę adresową",
'cd_username'=> "Nazwa użytkownika",
'cd_password'=> "Hasło",
'cd_url'=> "URL",
'cd_php_too_old'=> "Twoja wersja PHP jest nieaktualna! Proszę zaktualizuj do minimum wersji 5.6.18! Twoja wersja to:",
'cd_name'=> "Nazwa książki adresowej",
'cd_use_categories' => "Użyj nowego mechanizmu grup\n(stracisz stare grupy RCMCardDAV!)",
'cd_delete'=> "Usuń książkę adresową",
'cd_name_new'=> "Konfiguracja nowej książki adresowej",
'cd_refresh_time'=> "Interwał aktualizacji (godziny)",
'cd_err_noabfound'=> "nie znaleziono książek adresowych",
'cd_preemptive_auth'=> "Zapobiegawczo uwierzytelnij z serwerem. Używaj dla ownCloud!",
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Активировать адресную книгу CardDAV",
'cd_newabboxtitle'=> "Добавить новую адресную книгу",
'cd_username'=> "Имя пользователя",
'cd_password'=> "Пароль",
'cd_url'=> "URL",
'cd_php_too_old'=> "Ваша версия PHP слишком устарела! Пожалуйста, обновитесь по крайней мере до 5.6.18! У Вас установлена следующая версия: ",
'cd_name'=> "Название адресной книги",
'cd_delete'=> "Удалить адресную книгу",
'cd_name_new'=> "Настроить новую адресную книгу",
'cd_refresh_time'=> "Частота обновления (часов)",
'cd_err_noabfound'=> "Не найдено ни одной адресной книги",
'cd_preemptive_auth'=> "Предварительно авторизоваться на сервере. Используйте с ownCloud!",
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Aktivera CardDAV-adressboken",
'cd_newabboxtitle'=> "Add new addressbook",
'cd_username'=> "Användarnamn",
'cd_password'=> "Lösenord",
'cd_url'=> "URL",
'cd_php_too_old'=> "Din version av PHP är för gammal! Vänligen uppdatera till åtminstone 5.0.3! Du har följande version installerad: ",
'cd_name'=> "Namn på adressboken",
'cd_delete'=> "Radera denna adressbok",
'cd_name_new'=> "Konfigurera en ny addressbok",
'cd_refresh_time'=> "Uppdateringsintervall (timmar)",
'cd_err_noabfound'=> "no addressbook found",
'cd_preemptive_auth'=> "Preemptively authenticate against server. Use for ownCloud!",
);
?>

View File

@ -0,0 +1,17 @@
<?php
$labels = Array(
'cd_title'=> "CardDAV",
'cd_active'=> "Увімкнути список контактів CardDAV",
'cd_newabboxtitle'=> "Додати новий список контактів",
'cd_username'=> "Ім'я користувача",
'cd_password'=> "Пароль",
'cd_url'=> "URL",
'cd_php_too_old'=> "Ваша версія PHP занадто стара! Будь ласка, оновіть її принаймні до 5.6.18! У Вас встановлена така версія:",
'cd_name'=> "Назва списку контактів",
'cd_delete'=> "Видалити цей список контактів",
'cd_name_new'=> "Налаштувати цей список контактів",
'cd_refresh_time'=> "Період оновлення (в годинах)",
'cd_err_noabfound'=> "не знайдено жодного списку контактів",
'cd_preemptive_auth'=> "Попередньо авторизуватись на сервері. Використовуйте для ownCloud!",
);
?>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>carddav</name>
<channel>www.benjamin-schieder.de</channel>
<summary>CardDAV plugin for Roundcube</summary>
<description>
Adds functionality to use, synchronize and manipulate CardDAV accounts with Roundcube.
Tested with davical, owncloud, MacOS Addressbook and others.
</description>
<lead>
<name>Benjamin Schieder</name>
<user>blindcoder</user>
<email>carddav@wegwerf.anderdonau.de</email>
<active>yes</active>
</lead>
<developer>
<name>Michael Stilkerich</name>
<user>mike2k</user>
<email>ms@mike2k.de</email>
<active>yes</active>
</developer>
<date>2018-10-01</date>
<version>
<release>3.0.3</release>
</version>
<stability>
<release>stable</release>
</stability>
<license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
<notes>-</notes>
<contents>
<dir baseinstalldir="/" name="/">
<file name="carddav_discovery.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="carddav_common.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="carddav_backend.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="carddav.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="localization/cs_CZ.inc" role="data"></file>
<file name="localization/de_DE.inc" role="data"></file>
<file name="localization/en_US.inc" role="data"></file>
<file name="localization/es_ES.inc" role="data"></file>
<file name="localization/fr_FR.inc" role="data"></file>
<file name="localization/hu_HU.inc" role="data"></file>
<file name="localization/id_ID.inc" role="data"></file>
<file name="localization/it_IT.inc" role="data"></file>
<file name="localization/pl_PL.inc" role="data"></file>
<file name="localization/ru_RU.inc" role="data"></file>
<file name="localization/sv_SE.inc" role="data"></file>
<file name="localization/uk_UK.inc" role="data"></file>
</dir>
<!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>5.6.18</min>
</php>
</required>
</dependencies>
<phprelease/>
</package>

View File

@ -0,0 +1,6 @@
#sections-table #rcmrowcd_preferences td.section {
background-position: 6px -766px;
}
#sections-table #rcmrowcd_preferences.selected td.section {
background-position: 6px -791px;
}

View File

@ -0,0 +1,3 @@
.listing.iconized tr.cd_preferences>td.section:before {
content: "\f47f"
}

View File

@ -0,0 +1,6 @@
#sections-table #rcmrowcd_preferences td.section {
background-position: 6px -766px;
}
#sections-table #rcmrowcd_preferences.selected td.section {
background-position: 6px -791px;
}

View File

@ -0,0 +1,110 @@
<?php
// managesieve server port. When empty the port will be determined automatically
// using getservbyname() function, with 4190 as a fallback.
// $config['managesieve_port'] = null;
$config['managesieve_port'] = 4190;
// managesieve server address, default is localhost.
// Replacement variables supported in host name:
// %h - user's IMAP hostname
// %n - http hostname ($_SERVER['SERVER_NAME'])
// %d - domain (http hostname without the first part)
// For example %n = mail.domain.tld, %d = domain.tld
$config['managesieve_host'] = '%h';
// authentication method. Can be CRAM-MD5, DIGEST-MD5, PLAIN, LOGIN, EXTERNAL
// or none. Optional, defaults to best method supported by server.
$config['managesieve_auth_type'] = null;
// Optional managesieve authentication identifier to be used as authorization proxy.
// Authenticate as a different user but act on behalf of the logged in user.
// Works with PLAIN and DIGEST-MD5 auth.
$config['managesieve_auth_cid'] = null;
// Optional managesieve authentication password to be used for imap_auth_cid
$config['managesieve_auth_pw'] = null;
// use or not TLS for managesieve server connection
// Note: tls:// prefix in managesieve_host is also supported
// $config['managesieve_usetls'] = false;
$config['managesieve_usetls'] = true;
// Connection scket context options
// See http://php.net/manual/en/context.ssl.php
// The example below enables server certificate validation
//$config['managesieve_conn_options'] = array(
// 'ssl' => array(
// 'verify_peer' => true,
// 'verify_depth' => 3,
// 'cafile' => '/etc/openssl/certs/ca.crt',
// ),
// );
// Note: These can be also specified as an array of options indexed by hostname
$config['managesieve_conn_options'] = null;
// default contents of filters script (eg. default spam filter)
$config['managesieve_default'] = '/etc/dovecot/sieve/global';
// The name of the script which will be used when there's no user script
$config['managesieve_script_name'] = 'managesieve';
// Sieve RFC says that we should use UTF-8 endcoding for mailbox names,
// but some implementations does not covert UTF-8 to modified UTF-7.
// Defaults to UTF7-IMAP
$config['managesieve_mbox_encoding'] = 'UTF-8';
// I need this because my dovecot (with listescape plugin) uses
// ':' delimiter, but creates folders with dot delimiter
$config['managesieve_replace_delimiter'] = '';
// disabled sieve extensions (body, copy, date, editheader, encoded-character,
// envelope, environment, ereject, fileinto, ihave, imap4flags, index,
// mailbox, mboxmetadata, regex, reject, relational, servermetadata,
// spamtest, spamtestplus, subaddress, vacation, variables, virustest, etc.
// Note: not all extensions are implemented
$config['managesieve_disabled_extensions'] = array();
// Enables debugging of conversation with sieve server. Logs it into <log_dir>/sieve
$config['managesieve_debug'] = false;
// Enables features described in http://wiki.kolab.org/KEP:14
$config['managesieve_kolab_master'] = false;
// Script name extension used for scripts including. Dovecot uses '.sieve',
// Cyrus uses '.siv'. Doesn't matter if you have managesieve_kolab_master disabled.
$config['managesieve_filename_extension'] = '.sieve';
// List of reserved script names (without extension).
// Scripts listed here will be not presented to the user.
$config['managesieve_filename_exceptions'] = array();
// List of domains limiting destination emails in redirect action
// If not empty, user will need to select domain from a list
$config['managesieve_domains'] = array();
// Enables separate management interface for vacation responses (out-of-office)
// 0 - no separate section (default),
// 1 - add Vacation section,
// 2 - add Vacation section, but hide Filters section
$config['managesieve_vacation'] = 0;
// Default vacation interval (in days).
// Note: If server supports vacation-seconds extension it is possible
// to define interval in seconds here (as a string), e.g. "3600s".
$config['managesieve_vacation_interval'] = 0;
// Some servers require vacation :addresses to be filled with all
// user addresses (aliases). This option enables automatic filling
// of these on initial vacation form creation.
$config['managesieve_vacation_addresses_init'] = false;
// Sometimes you want to always reply with mail email address
// This option enables automatic filling of :from field on initial vacation form creation.
$config['managesieve_vacation_from_init'] = false;
// Supported methods of notify extension. Default: 'mailto'
$config['managesieve_notify_methods'] = array('mailto');
// Enables scripts RAW editor feature
$config['managesieve_raw_editor'] = true;

101
roundcube.config.php Normal file
View File

@ -0,0 +1,101 @@
<?php
/*
+-----------------------------------------------------------------------+
| Local configuration for the Roundcube Webmail installation. |
| |
| This is a sample configuration file only containing the minimum |
| setup required for a functional installation. Copy more options |
| from defaults.inc.php to this file to override the defaults. |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
+-----------------------------------------------------------------------+
*/
$config = array();
$config['auto_create_user'] = true;
/* Do not set db_dsnw here, use dpkg-reconfigure roundcube-core to configure database ! */
// include_once("/etc/roundcube/debian-db-roundcube.php");
$config['db_dsnw'] = 'sqlite:////var/db/roundcube.db?mode=0640';
// The mail host chosen to perform the log-in.
// Leave blank to show a textbox at login, give a list of hosts
// to display a pulldown menu or set one host as string.
// To use SSL/TLS connection, enter hostname with prefix ssl:// or tls://
// Supported replacement variables:
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %s - domain name after the '@' from e-mail address provided at login screen
// For example %n = mail.domain.tld, %t = domain.tld
//$config['default_host'] = '';
$config['default_host'] = array(
'192.168.50.61' => 'toptica',
'ssl://imap.gmx.net' => 'GMX',
'ssl://imap.web.de' => 'Web',
);
// SMTP server host (for sending mails).
// To use SSL/TLS connection, enter hostname with prefix ssl:// or tls://
// If left blank, the PHP mail() function is used
// Supported replacement variables:
// %h - user's IMAP hostname
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
// %z - IMAP domain (IMAP hostname without the first part)
// For example %n = mail.domain.tld, %t = domain.tld
//$config['smtp_server'] = 'mail.lima-city.de';
// SMTP port (default is 25; use 587 for STARTTLS or 465 for the
// deprecated SSL over SMTP (aka SMTPS))
$config['smtp_port'] = 25;
// SMTP username (if required) if you use %u as the username Roundcube
// will use the current username for login
// $config['smtp_user'] = '';
// SMTP password (if required) if you use %p as the password Roundcube
// will use the current user's password for login
// $config['smtp_pass'] = '';
// provide an URL where a user can get support for this Roundcube installation
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
//$config['support_url'] = 'https://web.hilie.de/roundcube';
// Name your service. This is displayed on the login screen and in the window title
$config['product_name'] = 'Roundcube Webmail';
// this key is used to encrypt the users imap password which is stored
// in the session record (and the client cookie if remember password is enabled).
// please provide a string of exactly 24 chars.
// YOUR KEY MUST BE DIFFERENT THAN THE SAMPLE VALUE FOR SECURITY REASONS
$config['des_key'] = 'sv5KuHfUagjggBU2KzXI2Uo';
// $config['des_key'] = 'Change_me_I_am_example!!';
// List of active plugins (in plugins/ directory)
$config['plugins'] = array(
'archive',
'zipdownload',
// 'managesieve',
// 'identity_smtp',
// 'carddav',
'newmail_notifier',
// 'html5_notifier',
);
// skin name: folder from skins/
// $config['skin'] = 'larry';
//$config['debug_level'] = 4;
//$config['imap_debug'] = true;
$config['imap_cache'] = 'db';
$config['messages_cache'] = true;
$config['ip_check'] = false;