# SPDX-FileCopyrightText: 2012-2017 Cédric Krier
# SPDX-FileCopyrightText: 2017-2023 GNU Solidario <health@gnusolidario.org>
# SPDX-FileCopyrightText: 2017-2023 Luis Falcon <falcon@gnuhealth.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later

import os
import time
import urllib.request
import urllib.parse
import urllib.error
import encodings
import uuid
import datetime
import socket
from ast import literal_eval

from dateutil.relativedelta import relativedelta
from sql.functions import Extract
from sql.conditionals import Coalesce

from trytond.model import ModelView, ModelSQL, fields, Unique
from trytond.tools import reduce_ids
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.config import config
from trytond.pyson import Eval
from trytond.tools import grouped_slice

from .exceptions import InvalidAttachmentName

__all__ = [
    'Collection', 'Share', 'Attachment',
    ]


def get_webdav_url():
    if config.get('ssl', 'privatekey'):
        protocol = 'https'
    else:
        protocol = 'http'
    hostname = config.get('webdav', 'hostname') or socket.getfqdn()
    hostname = '.'.join(encodings.idna.ToASCII(part).decode() for
                        part in hostname.split('.'))
    return urllib.parse.urlunsplit((protocol, hostname,
                                    urllib.parse.quote(Transaction().database.name + '/'),
                                    '', ''))


class Collection(ModelSQL, ModelView):
    "Collection"
    __name__ = "webdav.collection"
    name = fields.Char('Name', required=True, select=True)
    parent = fields.Many2One('webdav.collection', 'Parent',
                             ondelete='RESTRICT',
                             domain=[('model', '=', None)])
    childs = fields.One2Many('webdav.collection', 'parent', 'Children')
    model = fields.Many2One('ir.model', 'Model')
    domain = fields.Char('Domain')
    complete_name = fields.Function(fields.Char('Complete Name'),
                                    'get_rec_name')

    @classmethod
    def __setup__(cls):
        super(Collection, cls).__setup__()
        table = cls.__table__()
        cls._sql_constraints += [
            ('name_parent_uniq', Unique(table, table.name, table.parent),
                'health_webdav3_server.msg_collection_file_name'),
        ]
        cls.ext2mime = {
            '.png': 'image/png',
            '.odt': 'application/vnd.oasis.opendocument.text',
            '.pdf': 'application/pdf',
        }

    @classmethod
    def order_complete_name(cls, tables):
        return cls.name.convert_order('name', tables, cls)

    @staticmethod
    def default_domain():
        return '[]'

    def get_rec_name(self, name):
        if self.parent:
            return self.parent.rec_name + '/' + self.name
        else:
            return self.name

    @classmethod
    def validate(cls, collections):
        super(Collection, cls).validate(collections)
        cls.check_attachment(collections)

    @classmethod
    def check_attachment(cls, collections):
        pool = Pool()
        Attachment = pool.get('ir.attachment')
        for collection in collections:
            if collection.parent:
                attachments = Attachment.search([
                    ('resource', '=', '%s,%s' %
                        (cls.__name__, collection.parent.id)),
                    ])
                for attachment in attachments:
                    if attachment.name == collection.name:
                        raise InvalidAttachmentName(
                            gettext('health.webdav3_server.'
                                    'collection_attachment_name',
                                    attachment=attachment.rec_name,
                                    collection=collection.rec_name))

    @classmethod
    def _uri2object(cls, uri, object_name=__name__, object_id=None,
                    cache=None):
        pool = Pool()
        Attachment = pool.get('ir.attachment')
        Report = pool.get('ir.action.report')
        cache_uri = uri

        if cache is not None:
            cache.setdefault('_uri2object', {})
            if cache_uri in cache['_uri2object']:
                return cache['_uri2object'][cache_uri]

        if not uri:
            if cache is not None:
                cache['_uri2object'][cache_uri] = (cls.__name__, None)
            return cls.__name__, None
        name, uri = (uri.split('/', 1) + [None])[0:2]
        if object_name == cls.__name__:
            collection_ids = None
            if cache is not None:
                cache.setdefault('_parent2collection_ids', {})
                if object_id in cache['_parent2collection_ids']:
                    collection_ids = cache['_parent2collection_ids'][
                        object_id].get(name, [])
            if collection_ids is None:
                collections = cls.search([
                    ('parent', '=', object_id),
                    ])
                collection_ids = []
                if cache is not None:
                    cache['_parent2collection_ids'].setdefault(object_id, {})
                for collection in collections:
                    if cache is not None:
                        cache['_parent2collection_ids'][object_id]\
                            .setdefault(collection.name, [])
                        cache['_parent2collection_ids'][object_id][
                            collection.name].append(collection.id)
                        cache.setdefault('_collection_name', {})
                        if collection.model and uri:
                            cache['_collection_name'][collection.id] = \
                                collection.model.model
                        else:
                            cache['_collection_name'][collection.id] = \
                                cls.__name__
                    if collection.name == name:
                        collection_ids.append(collection.id)
            if collection_ids:
                object_id = collection_ids[0]
                object_name2 = None
                if cache is not None:
                    cache.setdefault('_collection_name', {})
                    if object_id in cache['_collection_name']:
                        object_name2 = cache['_collection_name'][object_id]
                if object_name2 is None:
                    collection = cls(object_id)
                    if collection.model and uri:
                        object_name = collection.model.model
                        if cache is not None:
                            cache['_collection_name'][object_id] = object_name
                else:
                    object_name = object_name2
            else:
                if uri:
                    if cache is not None:
                        cache['_uri2object'][cache_uri] = (None, 0)
                    return None, 0

                attachment_ids = None
                if cache is not None:
                    cache.setdefault('_model&id2attachment_ids', {})
                    if (object_name, object_id) in \
                            cache['_model&id2attachment_ids']:
                        attachment_ids = cache['_model&id2attachment_ids'][
                            (object_name, object_id)].get(name, [])
                attachment_id = None
                if attachment_ids is None:
                    attachments = Attachment.search([
                        ('resource', '=', '%s,%s' % (object_name, object_id)),
                        ])
                    key = (object_name, object_id)
                    attachment_ids = []
                    if cache is not None:
                        cache['_model&id2attachment_ids'].setdefault(key, {})
                    for attachment in attachments:
                        if cache is not None:
                            cache.setdefault('_model&id&name2attachment_ids',
                                             {})
                            cache['_model&id&name2attachment_ids'].setdefault(
                                    key, {})
                            cache['_model&id&name2attachment_ids'][key]\
                                .setdefault(attachment.name, [])
                            cache['_model&id&name2attachment_ids'][key][
                                attachment.name].append(attachment.id)
                        if attachment.name == name:
                            attachment_id = attachment.id
                else:
                    key = (object_name, object_id)
                    cache.setdefault('_model&id&name2attachment_ids', {})
                    cache['_model&id&name2attachment_ids'].setdefault(key, {})
                    attachment_id = cache['_model&id&name2attachment_ids'][
                        key].get(name, [None])[0]
                if attachment_id:
                    object_name = 'ir.attachment'
                    object_id = attachment_id
                else:
                    object_name = None
                    object_id = None
        else:
            splitted_name = name.rsplit('-', 1)
            if len(splitted_name) != 2:
                if cache is not None:
                    cache['_uri2object'][cache_uri] = (object_name, 0)
                return object_name, 0
            object_id = int(splitted_name[1].strip())
            if uri:
                if '/' in uri:
                    if cache is not None:
                        cache['_uri2object'][cache_uri] = (None, 0)
                    return None, 0
                reports = Report.search([
                    ('model', '=', object_name),
                    ])
                for report in reports:
                    report_name = (report.name + '-' + str(report.id)
                                   + '.' + report.extension)
                    if uri == report_name:
                        if cache is not None:
                            cache['_uri2object'][cache_uri] = \
                                ('ir.action.report', object_id)
                        return 'ir.action.report', object_id
                name = uri
                attachment_ids = None
                if cache is not None:
                    cache.setdefault('_model&id2attachment_ids', {})
                    if (object_name, object_id) in \
                            cache['_model&id2attachment_ids']:
                        attachment_ids = cache['_model&id2attachment_ids'][
                            (object_name, object_id)].get(name, [])
                if attachment_ids is None:
                    attachments = Attachment.search([
                        ('resource', '=', '%s,%s' % (object_name, object_id)),
                        ])
                    key = (object_name, object_id)
                    attachment_ids = []
                    if cache is not None:
                        cache['_model&id2attachment_ids'].setdefault(key, {})
                    for attachment in attachments:
                        if cache is not None:
                            cache['_model&id2attachment_ids'][key]\
                                .setdefault(attachment.name, [])
                            cache['_model&id2attachment_ids'][key][
                                attachment.name].append(attachment.id)
                        if attachment.name == name:
                            attachment_ids.append(attachment.id)
                if attachment_ids:
                    object_name = 'ir.attachment'
                    object_id = attachment_ids[0]
                else:
                    object_name = None
                    object_id = None
                if cache is not None:
                    cache['_uri2object'][cache_uri] = (object_name, object_id)
                return object_name, object_id
        if uri:
            res = cls._uri2object(uri, object_name, object_id, cache=cache)
            if cache is not None:
                cache['_uri2object'][cache_uri] = res
            return res
        if cache is not None:
            cache['_uri2object'][cache_uri] = (object_name, object_id)
        return object_name, object_id

    @classmethod
    def get_childs(cls, uri, filter=None, cache=None):
        pool = Pool()
        Report = pool.get('ir.action.report')
        res = []
        if filter:
            return []
        if not uri:
            collections = cls.search([
                ('parent', '=', None),
                ])
            for collection in collections:
                if '/' in collection.name:
                    continue
                res.append(collection.name)
                if cache is not None:
                    cache.setdefault(cls.__name__, {})
                    cache[cls.__name__][collection.id] = {}
            return res
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name == cls.__name__ and object_id:
            collection = cls(object_id)
            if collection.model:
                Model = pool.get(collection.model.model)
                if not Model:
                    return res
                models = Model.search(
                    literal_eval(collection.domain))
                for child in models:
                    if '/' in child.rec_name:
                        continue
                    res.append(child.rec_name + '-' + str(child.id))
                    if cache is not None:
                        cache.setdefault(Model.__name__, {})
                        cache[Model.__name__][child.id] = {}
                return res
            else:
                for child in collection.childs:
                    if '/' in child.name:
                        continue
                    res.append(child.name)
                    if cache is not None:
                        cache.setdefault(cls.__name__, {})
                        cache[cls.__name__][child.id] = {}
        if object_name not in ('ir.attachment', 'ir.action.report'):
            reports = Report.search([
                ('model', '=', object_name),
                ])
            for report in reports:
                report_name = (report.name + '-' + str(report.id)
                               + '.' + report.extension)
                if '/' in report_name:
                    continue
                res.append(report_name)
                if cache is not None:
                    cache.setdefault(Report.__name__, {})
                    cache[Report.__name__][report.id] = {}

            Attachment = pool.get('ir.attachment')
            attachments = Attachment.search([
                    ('resource', '=', '%s,%s' % (object_name, object_id)),
                    ])
            for attachment in attachments:
                if attachment.name and not attachment.link:
                    if '/' in attachment.name:
                        continue
                    res.append(attachment.name)
                    if cache is not None:
                        cache.setdefault(Attachment.__name__, {})
                        cache[Attachment.__name__][attachment.id] = {}
        return res

    @classmethod
    def get_resourcetype(cls, uri, cache=None):
        from pywebdav.lib.constants import COLLECTION, OBJECT
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name in ('ir.attachment', 'ir.action.report'):
            return OBJECT
        return COLLECTION

    @classmethod
    def get_displayname(cls, uri, cache=None):
        object_name, object_id = cls._uri2object(uri, cache=cache)
        Model = Pool().get(object_name)
        return Model(object_id).rec_name

    @classmethod
    def get_contentlength(cls, uri, cache=None):
        pool = Pool()
        Attachment = pool.get('ir.attachment')

        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name == 'ir.attachment':

            if cache is not None:
                cache.setdefault('ir.attachment', {})
                ids = list(cache['ir.attachment'].keys())
                if object_id not in ids:
                    ids.append(object_id)
                elif 'contentlength' in cache['ir.attachment'][object_id]:
                    return cache['ir.attachment'][object_id]['contentlength']
            else:
                ids = [object_id]

            attachments = Attachment.browse(ids)

            res = '0'
            for attachment in attachments:
                size = '0'
                try:
                    if attachment.data_size:
                        size = str(attachment.data_size)
                except Exception:
                    pass
                if attachment.id == object_id:
                    res = size
                if cache is not None:
                    cache['ir.attachment'].setdefault(attachment.id, {})
                    cache['ir.attachment'][attachment.id]['contentlength'] = \
                        size
            return res
        return '0'

    @classmethod
    def get_contenttype(cls, uri, cache=None):
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name in ('ir.attachment', 'ir.action.report'):
            ext = os.path.splitext(uri)[1]
            if not ext:
                return "application/octet-stream"
            return cls.ext2mime.get(ext, 'application/octet-stream')
        return "application/octet-stream"

    @classmethod
    def get_creationdate(cls, uri, cache=None):
        pool = Pool()
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name == 'ir.attachment':
            Model = pool.get(object_name)
            if object_id:
                if cache is not None:
                    cache.setdefault(Model.__name__, {})
                    ids = list(cache[Model.__name__].keys())
                    if object_id not in ids:
                        ids.append(object_id)
                    elif 'creationdate' in cache[Model.__name__][object_id]:
                        return cache[Model.__name__][object_id][
                            'creationdate']
                else:
                    ids = [object_id]
                res = None
                cursor = Transaction().connection.cursor()
                table = Model.__table__()
                for sub_ids in grouped_slice(ids):
                    red_sql = reduce_ids(table.id, sub_ids)
                    cursor.execute(*table.select(table.id,
                                                 Extract('EPOCH',
                                                         table.create_date),
                                                 where=red_sql))
                    for object_id2, date in cursor.fetchall():
                        if object_id2 == object_id:
                            res = date
                        if cache is not None:
                            cache[Model.__name__].setdefault(object_id2, {})
                            cache[Model.__name__][object_id2][
                                'creationdate'] = date
                if res is not None:
                    return res
        return time.time()

    @classmethod
    def get_lastmodified(cls, uri, cache=None):
        pool = Pool()
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name == 'ir.attachment':
            Model = pool.get(object_name)
            if object_id:
                if cache is not None:
                    cache.setdefault(Model.__name__, {})
                    ids = list(cache[Model.__name__].keys())
                    if object_id not in ids:
                        ids.append(object_id)
                    elif 'lastmodified' in cache[Model.__name__][object_id]:
                        return cache[Model.__name__][object_id][
                            'lastmodified']
                else:
                    ids = [object_id]
                res = None
                cursor = Transaction().connection.cursor()
                table = Model.__table__()
                for sub_ids in grouped_slice(ids):
                    red_sql = reduce_ids(table.id, sub_ids)
                    cursor.execute(*table.select(table.id,
                                                 Extract('EPOCH', Coalesce(
                                                     table.write_date,
                                                     table.create_date)),
                                                 where=red_sql))
                    for object_id2, date in cursor.fetchall():
                        if object_id2 == object_id:
                            res = date
                        if cache is not None:
                            cache[Model.__name__].setdefault(object_id2, {})
                            cache[Model.__name__][object_id2][
                                'lastmodified'] = date
                if res is not None:
                    return res
        return time.time()

    @classmethod
    def get_data(cls, uri, cache=None):
        from pywebdav.lib.errors import DAV_NotFound
        pool = Pool()
        Attachment = pool.get('ir.attachment')
        Report = pool.get('ir.action.report')

        if uri:
            object_name, object_id = cls._uri2object(uri, cache=cache)

            if object_name == 'ir.attachment' and object_id:
                if cache is not None:
                    cache.setdefault('ir.attachment', {})
                    ids = list(cache['ir.attachment'].keys())
                    if object_id not in ids:
                        ids.append(object_id)
                    elif 'data' in cache['ir.attachment'][object_id]:
                        res = cache['ir.attachment'][object_id]['data']
                        if res == DAV_NotFound:
                            raise DAV_NotFound
                        return res
                else:
                    ids = [object_id]
                attachments = Attachment.browse(ids)

                res = DAV_NotFound
                for attachment in attachments:
                    data = DAV_NotFound
                    try:
                        if attachment.data is not None:
                            data = str(attachment.data)
                    except Exception:
                        pass
                    if attachment.id == object_id:
                        res = data
                    if cache is not None:
                        cache['ir.attachment'].setdefault(attachment.id, {})
                        cache['ir.attachment'][attachment.id]['data'] = data
                if res == DAV_NotFound:
                    raise DAV_NotFound
                return res

            if object_name == 'ir.action.report' and object_id:
                report_id = int(uri.rsplit('/', 1)[-1].
                                rsplit('-', 1)[-1].rsplit('.', 1)[0])
                report = Report(report_id)
                if report.report_name:
                    Report = pool.get(report.report_name, type='report')
                    val = Report.execute([object_id],
                                         {'id': object_id, 'ids': [object_id]})
                    return val[1]
        raise DAV_NotFound

    @classmethod
    def put(cls, uri, data, content_type, cache=None):
        from pywebdav.lib.errors import DAV_Forbidden
        from pywebdav.lib.utils import get_uriparentpath, get_urifilename
        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                                                 cache=cache)
        if not object_name \
                or object_name == 'ir.attachment' \
                or not object_id:
            raise DAV_Forbidden
        pool = Pool()
        Attachment = pool.get('ir.attachment')
        object_name2, object_id2 = cls._uri2object(uri, cache=cache)
        if not object_id2:
            name = get_urifilename(uri)
            try:
                Attachment.create([{
                            'name': name,
                            'data': data,
                            'resource': '%s,%s' % (object_name, object_id),
                            }])
            except Exception:
                raise DAV_Forbidden
        else:
            try:
                Attachment.write(object_id2, {
                    'data': data,
                    })
            except Exception:
                raise DAV_Forbidden
        return

    @classmethod
    def mkcol(cls, uri, cache=None):
        from pywebdav.lib.errors import DAV_Forbidden
        from pywebdav.lib.utils import get_uriparentpath, get_urifilename
        if uri[-1:] == '/':
            uri = uri[:-1]
        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                                                 cache=cache)
        if object_name != 'webdav.collection':
            raise DAV_Forbidden
        name = get_urifilename(uri)
        try:
            cls.create([{
                        'name': name,
                        'parent': object_id,
                        }])
        except Exception:
            raise DAV_Forbidden
        return 201

    @classmethod
    def rmcol(cls, uri, cache=None):
        from pywebdav.lib.errors import DAV_Forbidden
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name != 'webdav.collection' \
                or not object_id:
            raise DAV_Forbidden
        try:
            cls.delete([cls(object_id)])
        except Exception:
            raise DAV_Forbidden
        return 200

    @classmethod
    def rm(cls, uri, cache=None):
        from pywebdav.lib.errors import DAV_Forbidden
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if not object_name:
            raise DAV_Forbidden
        if object_name != 'ir.attachment' \
                or not object_id:
            raise DAV_Forbidden
        pool = Pool()
        Model = pool.get(object_name)
        try:
            Model.delete([Model(object_id)])
        except Exception:
            raise DAV_Forbidden
        return 200

    @classmethod
    def exists(cls, uri, cache=None):
        object_name, object_id = cls._uri2object(uri, cache=cache)
        if object_name and object_id:
            return 1
        return None

    @staticmethod
    def current_user_privilege_set(uri, cache=None):
        return ['create', 'read', 'write', 'delete']


class Share(ModelSQL, ModelView):
    "Share"
    __name__ = 'webdav.share'
    _rec_name = 'key'

    path = fields.Char('Path', required=True, select=True)
    key = fields.Char('Key', required=True, select=True,
                      states={'readonly': True, })
    user = fields.Many2One('res.user', 'User', required=True)
    expiration_date = fields.Date('Expiration Date', required=True)
    note = fields.Text('Note')
    url = fields.Function(fields.Char('URL'), 'get_url')

    @staticmethod
    def default_key():
        return uuid.uuid4().hex

    @staticmethod
    def default_user():
        return Transaction().user

    @staticmethod
    def default_expiration_date():
        return datetime.date.today() + relativedelta(months=1)

    def get_url(self, name):
        return urllib.parse.urljoin(get_webdav_url(),
                                    urllib.parse.urlunsplit(
                                        ('', '', urllib.
                                         parse.quote(self.path),
                                         urllib.parse.urlencode([('key', self.key)]),
                                         '')))

    @staticmethod
    def match(share, command, path):
        "Test if share match with command and path"
        today = datetime.date.today()
        return (path.startswith(share.path) and share.expiration_date > today
                and command == 'GET')

    @classmethod
    def get_login(cls, key, command, path):
        """Validate the key for the command and path
        Return the user id if succeed or None
        """
        shares = cls.search([
                ('key', '=', key),
                ])
        if not shares:
            return None
        for share in shares:
            if cls.match(share, command, path):
                return share.user.id
        return None


class Attachment(ModelSQL, ModelView):
    __name__ = 'ir.attachment'

    path = fields.Function(fields.Char('Path'), 'get_path')
    url = fields.Function(fields.Char('URL'), 'get_url')
    shares = fields.Function(fields.One2Many('webdav.share', None, 'Shares',
                                             domain=[
                                                    ('path', '=',
                                                     Eval('path')),
                                                    ],
                                             depends=['path']),
                             'get_shares', 'set_shares')

    @classmethod
    def validate(cls, attachments):
        super(Attachment, cls).validate(attachments)
        cls.check_collection(attachments)

    @classmethod
    def check_collection(cls, attachments):
        pool = Pool()
        Collection = pool.get('webdav.collection')
        for attachment in attachments:
            if attachment.resource:
                model_name = attachment.resource.__name__
                record_id = attachment.resource.id
                if model_name == 'webdav.collection':
                    collection = Collection(int(record_id))
                    for child in collection.childs:
                        if child.name == attachment.name:
                            raise InvalidAttachmentName(
                                gettext('health.webdav3_server'
                                        '.collection_attachment_name',
                                        attachment=attachment.rec_name,
                                        collection=collection.rec_name))

    @classmethod
    def get_path(cls, attachments, name):
        pool = Pool()
        Collection = pool.get('webdav.collection')
        paths = dict((a.id, None) for a in attachments)

        resources = {}
        resource2attachments = {}
        for attachment in attachments:
            if not attachment.resource:
                paths[attachment.id] = None
                continue
            model_name = attachment.resource.__name__
            record_id = attachment.resource.id
            resources.setdefault(model_name, set()).add(record_id)
            resource2attachments.setdefault((model_name, record_id),
                                            []).append(attachment)
        collections = Collection.search([
                ('model.model', 'in', list(resources.keys())),
                ])
        for collection in collections:
            model_name = collection.model.model
            Model = pool.get(model_name)
            ids = list(resources[model_name])
            domain = literal_eval(collection.domain)
            domain = [domain, ('id', 'in', ids)]
            records = Model.search(domain)
            for record in records:
                for attachment in resource2attachments[
                        (model_name, record.id)]:
                    paths[attachment.id] = '/'.join((collection.rec_name,
                                                     record.rec_name
                                                     + '-' + str(record.id),
                                                     attachment.name))
        if 'webdav.collection' in resources:
            collection_ids = list(resources['webdav.collection'])
            for collection in Collection.browse(collection_ids):
                for attachment in resource2attachments[
                        ('webdav.collection', collection.id)]:
                    paths[attachment.id] = '/'.join((collection.rec_name,
                                                     attachment.name))
        return paths

    def get_url(self, name):
        if self.path:
            return urllib.parse.urljoin(get_webdav_url(),
                                        urllib.parse.quote(self.path))

    @classmethod
    def get_shares(cls, attachments, name):
        Share = Pool().get('webdav.share')
        result = dict((a.id, []) for a in attachments)
        path2attachement = dict((a.path, a) for a in attachments)
        shares = Share.search([
                ('path', 'in', list(path2attachement.keys())),
                ])
        for share in shares:
            attachment = path2attachement[share.path]
            result[attachment.id].append(share.id)
        return result

    @classmethod
    def set_shares(cls, attachments, name, values):
        Share = Pool().get('webdav.share')

        if not values:
            return

        def create(vlist):
            to_create = []
            for attachment in attachments:
                for values in vlist:
                    values = values.copy()
                    values['path'] = attachment.path
                    to_create.append(values)
            if to_create:
                Share.create(to_create)

        def write(ids, values):
            Share.write(Share.browse(ids), values)

        def delete(share_ids):
            Share.delete(Share.browse(share_ids))

        actions = {
            'create': create,
            'write': write,
            'delete': delete,
            }
        for value in values:
            action = value[0]
            args = value[1:]
            actions[action](*args)
