Statistics
| Branch: | Tag: | Revision:

root / pithos / backends / simple.py @ f7667baf

History | View | Annotate | Download (41.3 kB)

1 5635f9ef Antony Chazapis
# Copyright 2011 GRNET S.A. All rights reserved.
2 5635f9ef Antony Chazapis
# 
3 5635f9ef Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 5635f9ef Antony Chazapis
# without modification, are permitted provided that the following
5 5635f9ef Antony Chazapis
# conditions are met:
6 5635f9ef Antony Chazapis
# 
7 5635f9ef Antony Chazapis
#   1. Redistributions of source code must retain the above
8 5635f9ef Antony Chazapis
#      copyright notice, this list of conditions and the following
9 5635f9ef Antony Chazapis
#      disclaimer.
10 5635f9ef Antony Chazapis
# 
11 5635f9ef Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 5635f9ef Antony Chazapis
#      copyright notice, this list of conditions and the following
13 5635f9ef Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 5635f9ef Antony Chazapis
#      provided with the distribution.
15 5635f9ef Antony Chazapis
# 
16 5635f9ef Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 5635f9ef Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 5635f9ef Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 5635f9ef Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 5635f9ef Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 5635f9ef Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 5635f9ef Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 5635f9ef Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 5635f9ef Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 5635f9ef Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 5635f9ef Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 5635f9ef Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5635f9ef Antony Chazapis
# 
29 5635f9ef Antony Chazapis
# The views and conclusions contained in the software and
30 5635f9ef Antony Chazapis
# documentation are those of the authors and should not be
31 5635f9ef Antony Chazapis
# interpreted as representing official policies, either expressed
32 5635f9ef Antony Chazapis
# or implied, of GRNET S.A.
33 5635f9ef Antony Chazapis
34 b956618e Antony Chazapis
import os
35 b956618e Antony Chazapis
import time
36 b956618e Antony Chazapis
import sqlite3
37 b956618e Antony Chazapis
import logging
38 b956618e Antony Chazapis
import hashlib
39 a156c8b3 Antony Chazapis
import binascii
40 b956618e Antony Chazapis
41 cca6c617 Antony Chazapis
from base import NotAllowedError, BaseBackend
42 a156c8b3 Antony Chazapis
from pithos.lib.hashfiler import Mapper, Blocker
43 b956618e Antony Chazapis
44 b956618e Antony Chazapis
45 b956618e Antony Chazapis
logger = logging.getLogger(__name__)
46 b956618e Antony Chazapis
47 30b46c1d Antony Chazapis
def backend_method(func=None, autocommit=1):
48 30b46c1d Antony Chazapis
    if func is None:
49 30b46c1d Antony Chazapis
        def fn(func):
50 30b46c1d Antony Chazapis
            return backend_method(func, autocommit)
51 30b46c1d Antony Chazapis
        return fn
52 30b46c1d Antony Chazapis
53 30b46c1d Antony Chazapis
    if not autocommit:
54 30b46c1d Antony Chazapis
        return func
55 30b46c1d Antony Chazapis
    def fn(self, *args, **kw):
56 30b46c1d Antony Chazapis
        self.con.execute('begin deferred')
57 30b46c1d Antony Chazapis
        try:
58 30b46c1d Antony Chazapis
            ret = func(self, *args, **kw)
59 30b46c1d Antony Chazapis
            self.con.commit()
60 30b46c1d Antony Chazapis
            return ret
61 30b46c1d Antony Chazapis
        except:
62 30b46c1d Antony Chazapis
            self.con.rollback()
63 30b46c1d Antony Chazapis
            raise
64 30b46c1d Antony Chazapis
    return fn
65 30b46c1d Antony Chazapis
66 b956618e Antony Chazapis
67 b956618e Antony Chazapis
class SimpleBackend(BaseBackend):
68 22dab079 Antony Chazapis
    """A simple backend.
69 22dab079 Antony Chazapis
    
70 22dab079 Antony Chazapis
    Uses SQLite for storage.
71 22dab079 Antony Chazapis
    """
72 22dab079 Antony Chazapis
    
73 d065f612 Antony Chazapis
    # TODO: Create account if not present in all functions.
74 d065f612 Antony Chazapis
    
75 22dab079 Antony Chazapis
    def __init__(self, db):
76 a156c8b3 Antony Chazapis
        self.hash_algorithm = 'sha256'
77 a156c8b3 Antony Chazapis
        self.block_size = 4 * 1024 * 1024 # 4MB
78 b956618e Antony Chazapis
        
79 3ab38c43 Antony Chazapis
        self.default_policy = {'quota': 0, 'versioning': 'auto'}
80 3ab38c43 Antony Chazapis
        
81 22dab079 Antony Chazapis
        basepath = os.path.split(db)[0]
82 22dab079 Antony Chazapis
        if basepath and not os.path.exists(basepath):
83 b956618e Antony Chazapis
            os.makedirs(basepath)
84 a156c8b3 Antony Chazapis
        if not os.path.isdir(basepath):
85 a156c8b3 Antony Chazapis
            raise RuntimeError("Cannot open database at '%s'" % (db,))
86 22dab079 Antony Chazapis
        
87 a156c8b3 Antony Chazapis
        self.con = sqlite3.connect(basepath + '/db', check_same_thread=False)
88 84846143 Antony Chazapis
        
89 84846143 Antony Chazapis
        sql = '''pragma foreign_keys = on'''
90 84846143 Antony Chazapis
        self.con.execute(sql)
91 84846143 Antony Chazapis
        
92 58a6c894 Antony Chazapis
        sql = '''create table if not exists versions (
93 58a6c894 Antony Chazapis
                    version_id integer primary key,
94 58a6c894 Antony Chazapis
                    name text,
95 104626e3 Antony Chazapis
                    user text,
96 84846143 Antony Chazapis
                    tstamp integer not null,
97 58a6c894 Antony Chazapis
                    size integer default 0,
98 58a6c894 Antony Chazapis
                    hide integer default 0)'''
99 b956618e Antony Chazapis
        self.con.execute(sql)
100 b956618e Antony Chazapis
        sql = '''create table if not exists metadata (
101 84846143 Antony Chazapis
                    version_id integer,
102 84846143 Antony Chazapis
                    key text,
103 84846143 Antony Chazapis
                    value text,
104 84846143 Antony Chazapis
                    primary key (version_id, key)
105 84846143 Antony Chazapis
                    foreign key (version_id) references versions(version_id)
106 84846143 Antony Chazapis
                    on delete cascade)'''
107 84846143 Antony Chazapis
        self.con.execute(sql)
108 84846143 Antony Chazapis
        sql = '''create table if not exists policy (
109 84846143 Antony Chazapis
                    name text, key text, value text, primary key (name, key))'''
110 b956618e Antony Chazapis
        self.con.execute(sql)
111 84846143 Antony Chazapis
        
112 b0a2d1a6 Antony Chazapis
        # Access control tables.
113 02c0c3fa Antony Chazapis
        sql = '''create table if not exists groups (
114 b0a2d1a6 Antony Chazapis
                    account text, gname text, user text)'''
115 02c0c3fa Antony Chazapis
        self.con.execute(sql)
116 3436eeb0 Antony Chazapis
        sql = '''create table if not exists permissions (
117 b0a2d1a6 Antony Chazapis
                    name text, op text, user text)'''
118 3436eeb0 Antony Chazapis
        self.con.execute(sql)
119 e0f916bb Antony Chazapis
        sql = '''create table if not exists public (
120 e0f916bb Antony Chazapis
                    name text, primary key (name))'''
121 02c0c3fa Antony Chazapis
        self.con.execute(sql)
122 b0a2d1a6 Antony Chazapis
        
123 b956618e Antony Chazapis
        self.con.commit()
124 a156c8b3 Antony Chazapis
        
125 a156c8b3 Antony Chazapis
        params = {'blocksize': self.block_size,
126 a156c8b3 Antony Chazapis
                  'blockpath': basepath + '/blocks',
127 a156c8b3 Antony Chazapis
                  'hashtype': self.hash_algorithm}
128 a156c8b3 Antony Chazapis
        self.blocker = Blocker(**params)
129 a156c8b3 Antony Chazapis
        
130 a156c8b3 Antony Chazapis
        params = {'mappath': basepath + '/maps',
131 a156c8b3 Antony Chazapis
                  'namelen': self.blocker.hashlen}
132 a156c8b3 Antony Chazapis
        self.mapper = Mapper(**params)
133 b956618e Antony Chazapis
    
134 30b46c1d Antony Chazapis
    @backend_method
135 f6c97079 Antony Chazapis
    def list_accounts(self, user, marker=None, limit=10000):
136 f6c97079 Antony Chazapis
        """Return a list of accounts the user can access."""
137 f6c97079 Antony Chazapis
        
138 f6c97079 Antony Chazapis
        allowed = self._allowed_accounts(user)
139 f6c97079 Antony Chazapis
        start, limit = self._list_limits(allowed, marker, limit)
140 f6c97079 Antony Chazapis
        return allowed[start:start + limit]
141 f6c97079 Antony Chazapis
    
142 f6c97079 Antony Chazapis
    @backend_method
143 83dd59c5 Antony Chazapis
    def get_account_meta(self, user, account, until=None):
144 b956618e Antony Chazapis
        """Return a dictionary with the account metadata."""
145 b956618e Antony Chazapis
        
146 58a6c894 Antony Chazapis
        logger.debug("get_account_meta: %s %s", account, until)
147 cca6c617 Antony Chazapis
        if user != account:
148 f6c97079 Antony Chazapis
            if until or account not in self._allowed_accounts(user):
149 f6c97079 Antony Chazapis
                raise NotAllowedError
150 d065f612 Antony Chazapis
        else:
151 d065f612 Antony Chazapis
            self._create_account(user, account)
152 58a6c894 Antony Chazapis
        try:
153 58a6c894 Antony Chazapis
            version_id, mtime = self._get_accountinfo(account, until)
154 58a6c894 Antony Chazapis
        except NameError:
155 d065f612 Antony Chazapis
            # TODO: Make sure this doesn't happen.
156 58a6c894 Antony Chazapis
            version_id = None
157 60be837c Antony Chazapis
            mtime = 0
158 58a6c894 Antony Chazapis
        count, bytes, tstamp = self._get_pathstats(account, until)
159 31a1c80d Antony Chazapis
        if mtime > tstamp:
160 31a1c80d Antony Chazapis
            tstamp = mtime
161 58a6c894 Antony Chazapis
        if until is None:
162 58a6c894 Antony Chazapis
            modified = tstamp
163 58a6c894 Antony Chazapis
        else:
164 58a6c894 Antony Chazapis
            modified = self._get_pathstats(account)[2] # Overall last modification
165 31a1c80d Antony Chazapis
            if mtime > modified:
166 31a1c80d Antony Chazapis
                modified = mtime
167 22dab079 Antony Chazapis
        
168 22dab079 Antony Chazapis
        # Proper count.
169 58a6c894 Antony Chazapis
        sql = 'select count(name) from (%s) where name glob ? and not name glob ?'
170 58a6c894 Antony Chazapis
        sql = sql % self._sql_until(until)
171 22dab079 Antony Chazapis
        c = self.con.execute(sql, (account + '/*', account + '/*/*'))
172 22dab079 Antony Chazapis
        row = c.fetchone()
173 22dab079 Antony Chazapis
        count = row[0]
174 22dab079 Antony Chazapis
        
175 f6c97079 Antony Chazapis
        if user != account:
176 f6c97079 Antony Chazapis
            meta = {'name': account}
177 f6c97079 Antony Chazapis
        else:
178 f6c97079 Antony Chazapis
            meta = self._get_metadata(account, version_id)
179 f6c97079 Antony Chazapis
            meta.update({'name': account, 'count': count, 'bytes': bytes})
180 f6c97079 Antony Chazapis
            if until is not None:
181 f6c97079 Antony Chazapis
                meta.update({'until_timestamp': tstamp})
182 d065f612 Antony Chazapis
        meta.update({'modified': modified})
183 b956618e Antony Chazapis
        return meta
184 b956618e Antony Chazapis
    
185 30b46c1d Antony Chazapis
    @backend_method
186 83dd59c5 Antony Chazapis
    def update_account_meta(self, user, account, meta, replace=False):
187 b956618e Antony Chazapis
        """Update the metadata associated with the account."""
188 b956618e Antony Chazapis
        
189 22dab079 Antony Chazapis
        logger.debug("update_account_meta: %s %s %s", account, meta, replace)
190 cca6c617 Antony Chazapis
        if user != account:
191 cca6c617 Antony Chazapis
            raise NotAllowedError
192 a156c8b3 Antony Chazapis
        self._put_metadata(user, account, meta, replace, False)
193 58a6c894 Antony Chazapis
    
194 30b46c1d Antony Chazapis
    @backend_method
195 02c0c3fa Antony Chazapis
    def get_account_groups(self, user, account):
196 02c0c3fa Antony Chazapis
        """Return a dictionary with the user groups defined for this account."""
197 58a6c894 Antony Chazapis
        
198 02c0c3fa Antony Chazapis
        logger.debug("get_account_groups: %s", account)
199 cca6c617 Antony Chazapis
        if user != account:
200 f6c97079 Antony Chazapis
            if account not in self._allowed_accounts(user):
201 f6c97079 Antony Chazapis
                raise NotAllowedError
202 f6c97079 Antony Chazapis
            return {}
203 d065f612 Antony Chazapis
        self._create_account(user, account)
204 02c0c3fa Antony Chazapis
        return self._get_groups(account)
205 b956618e Antony Chazapis
    
206 30b46c1d Antony Chazapis
    @backend_method
207 02c0c3fa Antony Chazapis
    def update_account_groups(self, user, account, groups, replace=False):
208 02c0c3fa Antony Chazapis
        """Update the groups associated with the account."""
209 b956618e Antony Chazapis
        
210 02c0c3fa Antony Chazapis
        logger.debug("update_account_groups: %s %s %s", account, groups, replace)
211 cca6c617 Antony Chazapis
        if user != account:
212 cca6c617 Antony Chazapis
            raise NotAllowedError
213 d065f612 Antony Chazapis
        self._create_account(user, account)
214 b0a2d1a6 Antony Chazapis
        self._check_groups(groups)
215 b0a2d1a6 Antony Chazapis
        self._put_groups(account, groups, replace)
216 b956618e Antony Chazapis
    
217 30b46c1d Antony Chazapis
    @backend_method
218 84846143 Antony Chazapis
    def put_account(self, user, account):
219 84846143 Antony Chazapis
        """Create a new account with the given name."""
220 84846143 Antony Chazapis
        
221 84846143 Antony Chazapis
        logger.debug("put_account: %s", account)
222 84846143 Antony Chazapis
        if user != account:
223 84846143 Antony Chazapis
            raise NotAllowedError
224 84846143 Antony Chazapis
        try:
225 84846143 Antony Chazapis
            version_id, mtime = self._get_accountinfo(account)
226 84846143 Antony Chazapis
        except NameError:
227 84846143 Antony Chazapis
            pass
228 84846143 Antony Chazapis
        else:
229 84846143 Antony Chazapis
            raise NameError('Account already exists')
230 d065f612 Antony Chazapis
        self._put_version(account, user)
231 84846143 Antony Chazapis
    
232 30b46c1d Antony Chazapis
    @backend_method
233 02c0c3fa Antony Chazapis
    def delete_account(self, user, account):
234 02c0c3fa Antony Chazapis
        """Delete the account with the given name."""
235 b956618e Antony Chazapis
        
236 02c0c3fa Antony Chazapis
        logger.debug("delete_account: %s", account)
237 cca6c617 Antony Chazapis
        if user != account:
238 cca6c617 Antony Chazapis
            raise NotAllowedError
239 84846143 Antony Chazapis
        count = self._get_pathstats(account)[0]
240 22dab079 Antony Chazapis
        if count > 0:
241 02c0c3fa Antony Chazapis
            raise IndexError('Account is not empty')
242 84846143 Antony Chazapis
        sql = 'delete from versions where name = ?'
243 84846143 Antony Chazapis
        self.con.execute(sql, (account,))
244 b0a2d1a6 Antony Chazapis
        self._del_groups(account)
245 02c0c3fa Antony Chazapis
    
246 30b46c1d Antony Chazapis
    @backend_method
247 b18ef3ad Antony Chazapis
    def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None):
248 02c0c3fa Antony Chazapis
        """Return a list of containers existing under an account."""
249 02c0c3fa Antony Chazapis
        
250 02c0c3fa Antony Chazapis
        logger.debug("list_containers: %s %s %s %s", account, marker, limit, until)
251 02c0c3fa Antony Chazapis
        if user != account:
252 f6c97079 Antony Chazapis
            if until or account not in self._allowed_accounts(user):
253 17629fea Antony Chazapis
                raise NotAllowedError
254 f6c97079 Antony Chazapis
            allowed = self._allowed_containers(user, account)
255 f6c97079 Antony Chazapis
            start, limit = self._list_limits(allowed, marker, limit)
256 f6c97079 Antony Chazapis
            return allowed[start:start + limit]
257 b18ef3ad Antony Chazapis
        else:
258 b18ef3ad Antony Chazapis
            if shared:
259 b18ef3ad Antony Chazapis
                allowed = [x.split('/', 2)[1] for x in self._shared_paths(account)]
260 b18ef3ad Antony Chazapis
                start, limit = self._list_limits(allowed, marker, limit)
261 b18ef3ad Antony Chazapis
                return allowed[start:start + limit]
262 f6c97079 Antony Chazapis
        return [x[0] for x in self._list_objects(account, '', '/', marker, limit, False, [], until)]
263 b956618e Antony Chazapis
    
264 30b46c1d Antony Chazapis
    @backend_method
265 83dd59c5 Antony Chazapis
    def get_container_meta(self, user, account, container, until=None):
266 b956618e Antony Chazapis
        """Return a dictionary with the container metadata."""
267 b956618e Antony Chazapis
        
268 58a6c894 Antony Chazapis
        logger.debug("get_container_meta: %s %s %s", account, container, until)
269 cca6c617 Antony Chazapis
        if user != account:
270 f6c97079 Antony Chazapis
            if until or container not in self._allowed_containers(user, account):
271 f6c97079 Antony Chazapis
                raise NotAllowedError
272 58a6c894 Antony Chazapis
        path, version_id, mtime = self._get_containerinfo(account, container, until)
273 58a6c894 Antony Chazapis
        count, bytes, tstamp = self._get_pathstats(path, until)
274 31a1c80d Antony Chazapis
        if mtime > tstamp:
275 31a1c80d Antony Chazapis
            tstamp = mtime
276 58a6c894 Antony Chazapis
        if until is None:
277 58a6c894 Antony Chazapis
            modified = tstamp
278 58a6c894 Antony Chazapis
        else:
279 31a1c80d Antony Chazapis
            modified = self._get_pathstats(path)[2] # Overall last modification
280 31a1c80d Antony Chazapis
            if mtime > modified:
281 31a1c80d Antony Chazapis
                modified = mtime
282 58a6c894 Antony Chazapis
        
283 f6c97079 Antony Chazapis
        if user != account:
284 f6c97079 Antony Chazapis
            meta = {'name': container, 'modified': modified}
285 f6c97079 Antony Chazapis
        else:
286 f6c97079 Antony Chazapis
            meta = self._get_metadata(path, version_id)
287 f6c97079 Antony Chazapis
            meta.update({'name': container, 'count': count, 'bytes': bytes, 'modified': modified})
288 f6c97079 Antony Chazapis
            if until is not None:
289 f6c97079 Antony Chazapis
                meta.update({'until_timestamp': tstamp})
290 b956618e Antony Chazapis
        return meta
291 b956618e Antony Chazapis
    
292 30b46c1d Antony Chazapis
    @backend_method
293 83dd59c5 Antony Chazapis
    def update_container_meta(self, user, account, container, meta, replace=False):
294 b956618e Antony Chazapis
        """Update the metadata associated with the container."""
295 b956618e Antony Chazapis
        
296 58a6c894 Antony Chazapis
        logger.debug("update_container_meta: %s %s %s %s", account, container, meta, replace)
297 cca6c617 Antony Chazapis
        if user != account:
298 cca6c617 Antony Chazapis
            raise NotAllowedError
299 58a6c894 Antony Chazapis
        path, version_id, mtime = self._get_containerinfo(account, container)
300 a156c8b3 Antony Chazapis
        self._put_metadata(user, path, meta, replace, False)
301 b956618e Antony Chazapis
    
302 30b46c1d Antony Chazapis
    @backend_method
303 02c0c3fa Antony Chazapis
    def get_container_policy(self, user, account, container):
304 02c0c3fa Antony Chazapis
        """Return a dictionary with the container policy."""
305 02c0c3fa Antony Chazapis
        
306 02c0c3fa Antony Chazapis
        logger.debug("get_container_policy: %s %s", account, container)
307 3ab38c43 Antony Chazapis
        if user != account:
308 f6c97079 Antony Chazapis
            if container not in self._allowed_containers(user, account):
309 f6c97079 Antony Chazapis
                raise NotAllowedError
310 f6c97079 Antony Chazapis
            return {}
311 3ab38c43 Antony Chazapis
        path = self._get_containerinfo(account, container)[0]
312 3ab38c43 Antony Chazapis
        return self._get_policy(path)
313 02c0c3fa Antony Chazapis
    
314 30b46c1d Antony Chazapis
    @backend_method
315 02c0c3fa Antony Chazapis
    def update_container_policy(self, user, account, container, policy, replace=False):
316 02c0c3fa Antony Chazapis
        """Update the policy associated with the account."""
317 02c0c3fa Antony Chazapis
        
318 02c0c3fa Antony Chazapis
        logger.debug("update_container_policy: %s %s %s %s", account, container, policy, replace)
319 3ab38c43 Antony Chazapis
        if user != account:
320 3ab38c43 Antony Chazapis
            raise NotAllowedError
321 3ab38c43 Antony Chazapis
        path = self._get_containerinfo(account, container)[0]
322 3ab38c43 Antony Chazapis
        self._check_policy(policy)
323 3ab38c43 Antony Chazapis
        if replace:
324 3ab38c43 Antony Chazapis
            for k, v in self.default_policy.iteritems():
325 3ab38c43 Antony Chazapis
                if k not in policy:
326 3ab38c43 Antony Chazapis
                    policy[k] = v
327 3ab38c43 Antony Chazapis
        for k, v in policy.iteritems():
328 3ab38c43 Antony Chazapis
            sql = 'insert or replace into policy (name, key, value) values (?, ?, ?)'
329 3ab38c43 Antony Chazapis
            self.con.execute(sql, (path, k, v))
330 02c0c3fa Antony Chazapis
    
331 30b46c1d Antony Chazapis
    @backend_method
332 02c0c3fa Antony Chazapis
    def put_container(self, user, account, container, policy=None):
333 02c0c3fa Antony Chazapis
        """Create a new container with the given name."""
334 02c0c3fa Antony Chazapis
        
335 02c0c3fa Antony Chazapis
        logger.debug("put_container: %s %s %s", account, container, policy)
336 02c0c3fa Antony Chazapis
        if user != account:
337 02c0c3fa Antony Chazapis
            raise NotAllowedError
338 02c0c3fa Antony Chazapis
        try:
339 02c0c3fa Antony Chazapis
            path, version_id, mtime = self._get_containerinfo(account, container)
340 02c0c3fa Antony Chazapis
        except NameError:
341 3ab38c43 Antony Chazapis
            pass
342 02c0c3fa Antony Chazapis
        else:
343 02c0c3fa Antony Chazapis
            raise NameError('Container already exists')
344 3ab38c43 Antony Chazapis
        if policy:
345 3ab38c43 Antony Chazapis
            self._check_policy(policy)
346 f7667baf Antony Chazapis
        path = '/'.join((account, container))
347 d065f612 Antony Chazapis
        version_id = self._put_version(path, user)[0]
348 3ab38c43 Antony Chazapis
        for k, v in self.default_policy.iteritems():
349 3ab38c43 Antony Chazapis
            if k not in policy:
350 3ab38c43 Antony Chazapis
                policy[k] = v
351 3ab38c43 Antony Chazapis
        for k, v in policy.iteritems():
352 3ab38c43 Antony Chazapis
            sql = 'insert or replace into policy (name, key, value) values (?, ?, ?)'
353 3ab38c43 Antony Chazapis
            self.con.execute(sql, (path, k, v))
354 02c0c3fa Antony Chazapis
    
355 30b46c1d Antony Chazapis
    @backend_method
356 84846143 Antony Chazapis
    def delete_container(self, user, account, container, until=None):
357 84846143 Antony Chazapis
        """Delete/purge the container with the given name."""
358 02c0c3fa Antony Chazapis
        
359 84846143 Antony Chazapis
        logger.debug("delete_container: %s %s %s", account, container, until)
360 02c0c3fa Antony Chazapis
        if user != account:
361 02c0c3fa Antony Chazapis
            raise NotAllowedError
362 02c0c3fa Antony Chazapis
        path, version_id, mtime = self._get_containerinfo(account, container)
363 84846143 Antony Chazapis
        
364 84846143 Antony Chazapis
        if until is not None:
365 c428326e Antony Chazapis
            sql = '''select version_id from versions where name like ? and tstamp <= ?
366 c428326e Antony Chazapis
                        and version_id not in (select version_id from (%s))'''
367 c428326e Antony Chazapis
            sql = sql % self._sql_until() # Do not delete current versions.
368 84846143 Antony Chazapis
            c = self.con.execute(sql, (path + '/%', until))
369 84846143 Antony Chazapis
            for v in [x[0] for x in c.fetchall()]:
370 84846143 Antony Chazapis
                self._del_version(v)
371 84846143 Antony Chazapis
            return
372 84846143 Antony Chazapis
        
373 84846143 Antony Chazapis
        count = self._get_pathstats(path)[0]
374 02c0c3fa Antony Chazapis
        if count > 0:
375 02c0c3fa Antony Chazapis
            raise IndexError('Container is not empty')
376 2f51ce47 Antony Chazapis
        sql = 'delete from versions where name = ? or name like ?' # May contain hidden items.
377 2f51ce47 Antony Chazapis
        self.con.execute(sql, (path, path + '/%',))
378 84846143 Antony Chazapis
        sql = 'delete from policy where name = ?'
379 84846143 Antony Chazapis
        self.con.execute(sql, (path,))
380 a156c8b3 Antony Chazapis
        self._copy_version(user, account, account, True, False) # New account version (for timestamp update).
381 02c0c3fa Antony Chazapis
    
382 30b46c1d Antony Chazapis
    @backend_method
383 b18ef3ad Antony Chazapis
    def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], shared=False, until=None):
384 b956618e Antony Chazapis
        """Return a list of objects existing under a container."""
385 b956618e Antony Chazapis
        
386 b18ef3ad Antony Chazapis
        logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, virtual, keys, shared, until)
387 f6c97079 Antony Chazapis
        allowed = []
388 cca6c617 Antony Chazapis
        if user != account:
389 f6c97079 Antony Chazapis
            if until:
390 f6c97079 Antony Chazapis
                raise NotAllowedError
391 f7667baf Antony Chazapis
            allowed = self._allowed_paths(user, '/'.join((account, container)))
392 f6c97079 Antony Chazapis
            if not allowed:
393 f6c97079 Antony Chazapis
                raise NotAllowedError
394 b18ef3ad Antony Chazapis
        else:
395 b18ef3ad Antony Chazapis
            if shared:
396 f7667baf Antony Chazapis
                allowed = self._shared_paths('/'.join((account, container)))
397 58a6c894 Antony Chazapis
        path, version_id, mtime = self._get_containerinfo(account, container, until)
398 f6c97079 Antony Chazapis
        return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, until, allowed)
399 22dab079 Antony Chazapis
    
400 30b46c1d Antony Chazapis
    @backend_method
401 83dd59c5 Antony Chazapis
    def list_object_meta(self, user, account, container, until=None):
402 22dab079 Antony Chazapis
        """Return a list with all the container's object meta keys."""
403 b956618e Antony Chazapis
        
404 58a6c894 Antony Chazapis
        logger.debug("list_object_meta: %s %s %s", account, container, until)
405 f6c97079 Antony Chazapis
        allowed = []
406 cca6c617 Antony Chazapis
        if user != account:
407 f6c97079 Antony Chazapis
            if until:
408 f6c97079 Antony Chazapis
                raise NotAllowedError
409 f7667baf Antony Chazapis
            allowed = self._allowed_paths(user, '/'.join((account, container)))
410 f6c97079 Antony Chazapis
            if not allowed:
411 f6c97079 Antony Chazapis
                raise NotAllowedError
412 58a6c894 Antony Chazapis
        path, version_id, mtime = self._get_containerinfo(account, container, until)
413 58a6c894 Antony Chazapis
        sql = '''select distinct m.key from (%s) o, metadata m
414 58a6c894 Antony Chazapis
                    where m.version_id = o.version_id and o.name like ?'''
415 58a6c894 Antony Chazapis
        sql = sql % self._sql_until(until)
416 f6c97079 Antony Chazapis
        param = (path + '/%',)
417 f6c97079 Antony Chazapis
        if allowed:
418 f6c97079 Antony Chazapis
            for x in allowed:
419 f6c97079 Antony Chazapis
                sql += ' and o.name like ?'
420 f6c97079 Antony Chazapis
                param += (x,)
421 f6c97079 Antony Chazapis
        c = self.con.execute(sql, param)
422 22dab079 Antony Chazapis
        return [x[0] for x in c.fetchall()]
423 b956618e Antony Chazapis
    
424 30b46c1d Antony Chazapis
    @backend_method
425 83dd59c5 Antony Chazapis
    def get_object_meta(self, user, account, container, name, version=None):
426 b956618e Antony Chazapis
        """Return a dictionary with the object metadata."""
427 b956618e Antony Chazapis
        
428 58a6c894 Antony Chazapis
        logger.debug("get_object_meta: %s %s %s %s", account, container, name, version)
429 cca6c617 Antony Chazapis
        self._can_read(user, account, container, name)
430 104626e3 Antony Chazapis
        path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name, version)
431 58a6c894 Antony Chazapis
        if version is None:
432 58a6c894 Antony Chazapis
            modified = mtime
433 58a6c894 Antony Chazapis
        else:
434 104626e3 Antony Chazapis
            modified = self._get_version(path, version)[2] # Overall last modification
435 58a6c894 Antony Chazapis
        
436 58a6c894 Antony Chazapis
        meta = self._get_metadata(path, version_id)
437 104626e3 Antony Chazapis
        meta.update({'name': name, 'bytes': size})
438 104626e3 Antony Chazapis
        meta.update({'version': version_id, 'version_timestamp': mtime})
439 104626e3 Antony Chazapis
        meta.update({'modified': modified, 'modified_by': muser})
440 b956618e Antony Chazapis
        return meta
441 b956618e Antony Chazapis
    
442 30b46c1d Antony Chazapis
    @backend_method
443 83dd59c5 Antony Chazapis
    def update_object_meta(self, user, account, container, name, meta, replace=False):
444 b956618e Antony Chazapis
        """Update the metadata associated with the object."""
445 b956618e Antony Chazapis
        
446 22dab079 Antony Chazapis
        logger.debug("update_object_meta: %s %s %s %s %s", account, container, name, meta, replace)
447 cca6c617 Antony Chazapis
        self._can_write(user, account, container, name)
448 104626e3 Antony Chazapis
        path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name)
449 104626e3 Antony Chazapis
        self._put_metadata(user, path, meta, replace)
450 b956618e Antony Chazapis
    
451 30b46c1d Antony Chazapis
    @backend_method
452 3436eeb0 Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
453 cca6c617 Antony Chazapis
        """Return the path from which this object gets its permissions from,\
454 cca6c617 Antony Chazapis
        along with a dictionary containing the permissions."""
455 3436eeb0 Antony Chazapis
        
456 3436eeb0 Antony Chazapis
        logger.debug("get_object_permissions: %s %s %s", account, container, name)
457 cca6c617 Antony Chazapis
        self._can_read(user, account, container, name)
458 3436eeb0 Antony Chazapis
        path = self._get_objectinfo(account, container, name)[0]
459 cca6c617 Antony Chazapis
        return self._get_permissions(path)
460 3436eeb0 Antony Chazapis
    
461 30b46c1d Antony Chazapis
    @backend_method
462 3436eeb0 Antony Chazapis
    def update_object_permissions(self, user, account, container, name, permissions):
463 3436eeb0 Antony Chazapis
        """Update the permissions associated with the object."""
464 3436eeb0 Antony Chazapis
        
465 3436eeb0 Antony Chazapis
        logger.debug("update_object_permissions: %s %s %s %s", account, container, name, permissions)
466 cca6c617 Antony Chazapis
        if user != account:
467 cca6c617 Antony Chazapis
            raise NotAllowedError
468 3436eeb0 Antony Chazapis
        path = self._get_objectinfo(account, container, name)[0]
469 3436eeb0 Antony Chazapis
        r, w = self._check_permissions(path, permissions)
470 3436eeb0 Antony Chazapis
        self._put_permissions(path, r, w)
471 3436eeb0 Antony Chazapis
    
472 30b46c1d Antony Chazapis
    @backend_method
473 02c0c3fa Antony Chazapis
    def get_object_public(self, user, account, container, name):
474 02c0c3fa Antony Chazapis
        """Return the public URL of the object if applicable."""
475 02c0c3fa Antony Chazapis
        
476 02c0c3fa Antony Chazapis
        logger.debug("get_object_public: %s %s %s", account, container, name)
477 e0f916bb Antony Chazapis
        self._can_read(user, account, container, name)
478 e0f916bb Antony Chazapis
        path = self._get_objectinfo(account, container, name)[0]
479 e0f916bb Antony Chazapis
        if self._get_public(path):
480 e0f916bb Antony Chazapis
            return '/public/' + path
481 02c0c3fa Antony Chazapis
        return None
482 02c0c3fa Antony Chazapis
    
483 30b46c1d Antony Chazapis
    @backend_method
484 02c0c3fa Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
485 02c0c3fa Antony Chazapis
        """Update the public status of the object."""
486 02c0c3fa Antony Chazapis
        
487 02c0c3fa Antony Chazapis
        logger.debug("update_object_public: %s %s %s %s", account, container, name, public)
488 e0f916bb Antony Chazapis
        self._can_write(user, account, container, name)
489 e0f916bb Antony Chazapis
        path = self._get_objectinfo(account, container, name)[0]
490 e0f916bb Antony Chazapis
        self._put_public(path, public)
491 02c0c3fa Antony Chazapis
    
492 30b46c1d Antony Chazapis
    @backend_method
493 83dd59c5 Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
494 22dab079 Antony Chazapis
        """Return the object's size and a list with partial hashes."""
495 b956618e Antony Chazapis
        
496 22dab079 Antony Chazapis
        logger.debug("get_object_hashmap: %s %s %s %s", account, container, name, version)
497 cca6c617 Antony Chazapis
        self._can_read(user, account, container, name)
498 104626e3 Antony Chazapis
        path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name, version)
499 a156c8b3 Antony Chazapis
        hashmap = self.mapper.map_retr(version_id)
500 a156c8b3 Antony Chazapis
        return size, [binascii.hexlify(x) for x in hashmap]
501 22dab079 Antony Chazapis
    
502 30b46c1d Antony Chazapis
    @backend_method
503 cca6c617 Antony Chazapis
    def update_object_hashmap(self, user, account, container, name, size, hashmap, meta={}, replace_meta=False, permissions=None):
504 22dab079 Antony Chazapis
        """Create/update an object with the specified size and partial hashes."""
505 b956618e Antony Chazapis
        
506 cfe6939d Antony Chazapis
        logger.debug("update_object_hashmap: %s %s %s %s %s", account, container, name, size, hashmap)
507 cca6c617 Antony Chazapis
        if permissions is not None and user != account:
508 cca6c617 Antony Chazapis
            raise NotAllowedError
509 cca6c617 Antony Chazapis
        self._can_write(user, account, container, name)
510 a156c8b3 Antony Chazapis
        missing = self.blocker.block_ping([binascii.unhexlify(x) for x in hashmap])
511 76985443 Sofia Papagiannaki
        if missing:
512 76985443 Sofia Papagiannaki
            ie = IndexError()
513 76985443 Sofia Papagiannaki
            ie.data = missing
514 76985443 Sofia Papagiannaki
            raise ie
515 58a6c894 Antony Chazapis
        path = self._get_containerinfo(account, container)[0]
516 f7667baf Antony Chazapis
        path = '/'.join((path, name))
517 cca6c617 Antony Chazapis
        if permissions is not None:
518 3436eeb0 Antony Chazapis
            r, w = self._check_permissions(path, permissions)
519 104626e3 Antony Chazapis
        src_version_id, dest_version_id = self._copy_version(user, path, path, not replace_meta, False)
520 58a6c894 Antony Chazapis
        sql = 'update versions set size = ? where version_id = ?'
521 58a6c894 Antony Chazapis
        self.con.execute(sql, (size, dest_version_id))
522 a156c8b3 Antony Chazapis
        self.mapper.map_stor(dest_version_id, [binascii.unhexlify(x) for x in hashmap])
523 83dd59c5 Antony Chazapis
        for k, v in meta.iteritems():
524 83dd59c5 Antony Chazapis
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
525 83dd59c5 Antony Chazapis
            self.con.execute(sql, (dest_version_id, k, v))
526 cca6c617 Antony Chazapis
        if permissions is not None:
527 b0a2d1a6 Antony Chazapis
            self._put_permissions(path, r, w)
528 22dab079 Antony Chazapis
    
529 30b46c1d Antony Chazapis
    @backend_method
530 cca6c617 Antony Chazapis
    def copy_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None, src_version=None):
531 22dab079 Antony Chazapis
        """Copy an object's data and metadata."""
532 b956618e Antony Chazapis
        
533 3436eeb0 Antony Chazapis
        logger.debug("copy_object: %s %s %s %s %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions, src_version)
534 cca6c617 Antony Chazapis
        if permissions is not None and user != account:
535 cca6c617 Antony Chazapis
            raise NotAllowedError
536 cca6c617 Antony Chazapis
        self._can_read(user, account, src_container, src_name)
537 cca6c617 Antony Chazapis
        self._can_write(user, account, dest_container, dest_name)
538 6d817842 Antony Chazapis
        self._get_containerinfo(account, src_container)
539 58a6c894 Antony Chazapis
        if src_version is None:
540 58a6c894 Antony Chazapis
            src_path = self._get_objectinfo(account, src_container, src_name)[0]
541 22dab079 Antony Chazapis
        else:
542 f7667baf Antony Chazapis
            src_path = '/'.join((account, src_container, src_name))
543 58a6c894 Antony Chazapis
        dest_path = self._get_containerinfo(account, dest_container)[0]
544 f7667baf Antony Chazapis
        dest_path = '/'.join((dest_path, dest_name))
545 cca6c617 Antony Chazapis
        if permissions is not None:
546 3436eeb0 Antony Chazapis
            r, w = self._check_permissions(dest_path, permissions)
547 104626e3 Antony Chazapis
        src_version_id, dest_version_id = self._copy_version(user, src_path, dest_path, not replace_meta, True, src_version)
548 58a6c894 Antony Chazapis
        for k, v in dest_meta.iteritems():
549 58a6c894 Antony Chazapis
            sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
550 58a6c894 Antony Chazapis
            self.con.execute(sql, (dest_version_id, k, v))
551 cca6c617 Antony Chazapis
        if permissions is not None:
552 b0a2d1a6 Antony Chazapis
            self._put_permissions(dest_path, r, w)
553 b956618e Antony Chazapis
    
554 30b46c1d Antony Chazapis
    @backend_method
555 cca6c617 Antony Chazapis
    def move_object(self, user, account, src_container, src_name, dest_container, dest_name, dest_meta={}, replace_meta=False, permissions=None):
556 b956618e Antony Chazapis
        """Move an object's data and metadata."""
557 b956618e Antony Chazapis
        
558 3436eeb0 Antony Chazapis
        logger.debug("move_object: %s %s %s %s %s %s %s %s", account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions)
559 3436eeb0 Antony Chazapis
        self.copy_object(user, account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions, None)
560 83dd59c5 Antony Chazapis
        self.delete_object(user, account, src_container, src_name)
561 b956618e Antony Chazapis
    
562 30b46c1d Antony Chazapis
    @backend_method
563 84846143 Antony Chazapis
    def delete_object(self, user, account, container, name, until=None):
564 84846143 Antony Chazapis
        """Delete/purge an object."""
565 b956618e Antony Chazapis
        
566 84846143 Antony Chazapis
        logger.debug("delete_object: %s %s %s %s", account, container, name, until)
567 cca6c617 Antony Chazapis
        if user != account:
568 cca6c617 Antony Chazapis
            raise NotAllowedError
569 84846143 Antony Chazapis
        
570 84846143 Antony Chazapis
        if until is not None:
571 f7667baf Antony Chazapis
            path = '/'.join((account, container, name))
572 84846143 Antony Chazapis
            sql = '''select version_id from versions where name = ? and tstamp <= ?'''
573 84846143 Antony Chazapis
            c = self.con.execute(sql, (path, until))
574 84846143 Antony Chazapis
            for v in [x[0] in c.fetchall()]:
575 84846143 Antony Chazapis
                self._del_version(v)
576 84846143 Antony Chazapis
            try:
577 84846143 Antony Chazapis
                version_id = self._get_version(path)[0]
578 84846143 Antony Chazapis
            except NameError:
579 84846143 Antony Chazapis
                pass
580 84846143 Antony Chazapis
            else:
581 84846143 Antony Chazapis
                self._del_sharing(path)
582 84846143 Antony Chazapis
            return
583 84846143 Antony Chazapis
        
584 104626e3 Antony Chazapis
        path = self._get_objectinfo(account, container, name)[0]
585 104626e3 Antony Chazapis
        self._put_version(path, user, 0, 1)
586 84846143 Antony Chazapis
        self._del_sharing(path)
587 58a6c894 Antony Chazapis
    
588 30b46c1d Antony Chazapis
    @backend_method
589 83dd59c5 Antony Chazapis
    def list_versions(self, user, account, container, name):
590 83dd59c5 Antony Chazapis
        """Return a list of all (version, version_timestamp) tuples for an object."""
591 58a6c894 Antony Chazapis
        
592 83dd59c5 Antony Chazapis
        logger.debug("list_versions: %s %s %s", account, container, name)
593 cca6c617 Antony Chazapis
        self._can_read(user, account, container, name)
594 58a6c894 Antony Chazapis
        # This will even show deleted versions.
595 f7667baf Antony Chazapis
        path = '/'.join((account, container, name))
596 84846143 Antony Chazapis
        sql = '''select distinct version_id, tstamp from versions where name = ? and hide = 0'''
597 58a6c894 Antony Chazapis
        c = self.con.execute(sql, (path,))
598 c9af0703 Antony Chazapis
        return [(int(x[0]), int(x[1])) for x in c.fetchall()]
599 b956618e Antony Chazapis
    
600 30b46c1d Antony Chazapis
    @backend_method(autocommit=0)
601 22dab079 Antony Chazapis
    def get_block(self, hash):
602 22dab079 Antony Chazapis
        """Return a block's data."""
603 22dab079 Antony Chazapis
        
604 cfe6939d Antony Chazapis
        logger.debug("get_block: %s", hash)
605 a156c8b3 Antony Chazapis
        blocks = self.blocker.block_retr((binascii.unhexlify(hash),))
606 a156c8b3 Antony Chazapis
        if not blocks:
607 22dab079 Antony Chazapis
            raise NameError('Block does not exist')
608 a156c8b3 Antony Chazapis
        return blocks[0]
609 b956618e Antony Chazapis
    
610 30b46c1d Antony Chazapis
    @backend_method(autocommit=0)
611 22dab079 Antony Chazapis
    def put_block(self, data):
612 22dab079 Antony Chazapis
        """Create a block and return the hash."""
613 22dab079 Antony Chazapis
        
614 cfe6939d Antony Chazapis
        logger.debug("put_block: %s", len(data))
615 a156c8b3 Antony Chazapis
        hashes, absent = self.blocker.block_stor((data,))
616 a156c8b3 Antony Chazapis
        return binascii.hexlify(hashes[0])
617 22dab079 Antony Chazapis
    
618 30b46c1d Antony Chazapis
    @backend_method(autocommit=0)
619 22dab079 Antony Chazapis
    def update_block(self, hash, data, offset=0):
620 22dab079 Antony Chazapis
        """Update a known block and return the hash."""
621 22dab079 Antony Chazapis
        
622 cfe6939d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
623 cfe6939d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
624 cfe6939d Antony Chazapis
            return self.put_block(data)
625 a156c8b3 Antony Chazapis
        h, e = self.blocker.block_delta(binascii.unhexlify(hash), ((offset, data),))
626 a156c8b3 Antony Chazapis
        return binascii.hexlify(h)
627 b956618e Antony Chazapis
    
628 58a6c894 Antony Chazapis
    def _sql_until(self, until=None):
629 58a6c894 Antony Chazapis
        """Return the sql to get the latest versions until the timestamp given."""
630 58a6c894 Antony Chazapis
        if until is None:
631 58a6c894 Antony Chazapis
            until = int(time.time())
632 84846143 Antony Chazapis
        sql = '''select version_id, name, tstamp, size from versions v
633 58a6c894 Antony Chazapis
                    where version_id = (select max(version_id) from versions
634 2f51ce47 Antony Chazapis
                                        where v.name = name and tstamp <= %s)
635 58a6c894 Antony Chazapis
                    and hide = 0'''
636 84846143 Antony Chazapis
        return sql % (until,)
637 58a6c894 Antony Chazapis
    
638 58a6c894 Antony Chazapis
    def _get_pathstats(self, path, until=None):
639 58a6c894 Antony Chazapis
        """Return count and sum of size of everything under path and latest timestamp."""
640 58a6c894 Antony Chazapis
        
641 58a6c894 Antony Chazapis
        sql = 'select count(version_id), total(size), max(tstamp) from (%s) where name like ?'
642 58a6c894 Antony Chazapis
        sql = sql % self._sql_until(until)
643 58a6c894 Antony Chazapis
        c = self.con.execute(sql, (path + '/%',))
644 b956618e Antony Chazapis
        row = c.fetchone()
645 58a6c894 Antony Chazapis
        tstamp = row[2] if row[2] is not None else 0
646 58a6c894 Antony Chazapis
        return int(row[0]), int(row[1]), int(tstamp)
647 58a6c894 Antony Chazapis
    
648 58a6c894 Antony Chazapis
    def _get_version(self, path, version=None):
649 cb146cf9 Antony Chazapis
        if version is None:
650 84846143 Antony Chazapis
            sql = '''select version_id, user, tstamp, size, hide from versions where name = ?
651 58a6c894 Antony Chazapis
                        order by version_id desc limit 1'''
652 58a6c894 Antony Chazapis
            c = self.con.execute(sql, (path,))
653 58a6c894 Antony Chazapis
            row = c.fetchone()
654 104626e3 Antony Chazapis
            if not row or int(row[4]):
655 58a6c894 Antony Chazapis
                raise NameError('Object does not exist')
656 b956618e Antony Chazapis
        else:
657 104626e3 Antony Chazapis
            # The database (sqlite) will not complain if the version is not an integer.
658 84846143 Antony Chazapis
            sql = '''select version_id, user, tstamp, size from versions where name = ?
659 58a6c894 Antony Chazapis
                        and version_id = ?'''
660 58a6c894 Antony Chazapis
            c = self.con.execute(sql, (path, version))
661 58a6c894 Antony Chazapis
            row = c.fetchone()
662 58a6c894 Antony Chazapis
            if not row:
663 58a6c894 Antony Chazapis
                raise IndexError('Version does not exist')
664 104626e3 Antony Chazapis
        return str(row[0]), str(row[1]), int(row[2]), int(row[3])
665 58a6c894 Antony Chazapis
    
666 104626e3 Antony Chazapis
    def _put_version(self, path, user, size=0, hide=0):
667 84846143 Antony Chazapis
        tstamp = int(time.time())
668 84846143 Antony Chazapis
        sql = 'insert into versions (name, user, tstamp, size, hide) values (?, ?, ?, ?, ?)'
669 84846143 Antony Chazapis
        id = self.con.execute(sql, (path, user, tstamp, size, hide)).lastrowid
670 d065f612 Antony Chazapis
        return str(id), tstamp
671 b956618e Antony Chazapis
    
672 104626e3 Antony Chazapis
    def _copy_version(self, user, src_path, dest_path, copy_meta=True, copy_data=True, src_version=None):
673 58a6c894 Antony Chazapis
        if src_version is not None:
674 104626e3 Antony Chazapis
            src_version_id, muser, mtime, size = self._get_version(src_path, src_version)
675 58a6c894 Antony Chazapis
        else:
676 58a6c894 Antony Chazapis
            # Latest or create from scratch.
677 58a6c894 Antony Chazapis
            try:
678 104626e3 Antony Chazapis
                src_version_id, muser, mtime, size = self._get_version(src_path)
679 58a6c894 Antony Chazapis
            except NameError:
680 58a6c894 Antony Chazapis
                src_version_id = None
681 58a6c894 Antony Chazapis
                size = 0
682 58a6c894 Antony Chazapis
        if not copy_data:
683 58a6c894 Antony Chazapis
            size = 0
684 d065f612 Antony Chazapis
        dest_version_id = self._put_version(dest_path, user, size)[0]
685 58a6c894 Antony Chazapis
        if copy_meta and src_version_id is not None:
686 58a6c894 Antony Chazapis
            sql = 'insert into metadata select %s, key, value from metadata where version_id = ?'
687 58a6c894 Antony Chazapis
            sql = sql % dest_version_id
688 58a6c894 Antony Chazapis
            self.con.execute(sql, (src_version_id,))
689 58a6c894 Antony Chazapis
        if copy_data and src_version_id is not None:
690 a156c8b3 Antony Chazapis
            # TODO: Copy properly.
691 a156c8b3 Antony Chazapis
            hashmap = self.mapper.map_retr(src_version_id)
692 a156c8b3 Antony Chazapis
            self.mapper.map_stor(dest_version_id, hashmap)
693 58a6c894 Antony Chazapis
        return src_version_id, dest_version_id
694 58a6c894 Antony Chazapis
    
695 58a6c894 Antony Chazapis
    def _get_versioninfo(self, account, container, name, until=None):
696 58a6c894 Antony Chazapis
        """Return path, latest version, associated timestamp and size until the timestamp given."""
697 58a6c894 Antony Chazapis
        
698 58a6c894 Antony Chazapis
        p = (account, container, name)
699 22dab079 Antony Chazapis
        try:
700 58a6c894 Antony Chazapis
            p = p[:p.index(None)]
701 58a6c894 Antony Chazapis
        except ValueError:
702 58a6c894 Antony Chazapis
            pass
703 f7667baf Antony Chazapis
        path = '/'.join(p)
704 58a6c894 Antony Chazapis
        sql = '''select version_id, tstamp, size from (%s) where name = ?'''
705 58a6c894 Antony Chazapis
        sql = sql % self._sql_until(until)
706 58a6c894 Antony Chazapis
        c = self.con.execute(sql, (path,))
707 58a6c894 Antony Chazapis
        row = c.fetchone()
708 58a6c894 Antony Chazapis
        if row is None:
709 58a6c894 Antony Chazapis
            raise NameError('Path does not exist')
710 58a6c894 Antony Chazapis
        return path, str(row[0]), int(row[1]), int(row[2])
711 58a6c894 Antony Chazapis
    
712 58a6c894 Antony Chazapis
    def _get_accountinfo(self, account, until=None):
713 58a6c894 Antony Chazapis
        try:
714 58a6c894 Antony Chazapis
            path, version_id, mtime, size = self._get_versioninfo(account, None, None, until)
715 58a6c894 Antony Chazapis
            return version_id, mtime
716 58a6c894 Antony Chazapis
        except:
717 58a6c894 Antony Chazapis
            raise NameError('Account does not exist')
718 58a6c894 Antony Chazapis
    
719 58a6c894 Antony Chazapis
    def _get_containerinfo(self, account, container, until=None):
720 58a6c894 Antony Chazapis
        try:
721 58a6c894 Antony Chazapis
            path, version_id, mtime, size = self._get_versioninfo(account, container, None, until)
722 58a6c894 Antony Chazapis
            return path, version_id, mtime
723 58a6c894 Antony Chazapis
        except:
724 22dab079 Antony Chazapis
            raise NameError('Container does not exist')
725 22dab079 Antony Chazapis
    
726 22dab079 Antony Chazapis
    def _get_objectinfo(self, account, container, name, version=None):
727 f7667baf Antony Chazapis
        path = '/'.join((account, container, name))
728 104626e3 Antony Chazapis
        version_id, muser, mtime, size = self._get_version(path, version)
729 104626e3 Antony Chazapis
        return path, version_id, muser, mtime, size
730 22dab079 Antony Chazapis
    
731 d065f612 Antony Chazapis
    def _create_account(self, user, account):
732 d065f612 Antony Chazapis
        try:
733 d065f612 Antony Chazapis
            self._get_accountinfo(account)
734 d065f612 Antony Chazapis
        except NameError:
735 d065f612 Antony Chazapis
            self._put_version(account, user)
736 d065f612 Antony Chazapis
    
737 58a6c894 Antony Chazapis
    def _get_metadata(self, path, version):
738 58a6c894 Antony Chazapis
        sql = 'select key, value from metadata where version_id = ?'
739 58a6c894 Antony Chazapis
        c = self.con.execute(sql, (version,))
740 58a6c894 Antony Chazapis
        return dict(c.fetchall())
741 58a6c894 Antony Chazapis
    
742 a156c8b3 Antony Chazapis
    def _put_metadata(self, user, path, meta, replace=False, copy_data=True):
743 58a6c894 Antony Chazapis
        """Create a new version and store metadata."""
744 58a6c894 Antony Chazapis
        
745 a156c8b3 Antony Chazapis
        src_version_id, dest_version_id = self._copy_version(user, path, path, not replace, copy_data)
746 58a6c894 Antony Chazapis
        for k, v in meta.iteritems():
747 a6eb13e9 Antony Chazapis
            if not replace and v == '':
748 a6eb13e9 Antony Chazapis
                sql = 'delete from metadata where version_id = ? and key = ?'
749 a6eb13e9 Antony Chazapis
                self.con.execute(sql, (dest_version_id, k))
750 a6eb13e9 Antony Chazapis
            else:
751 a6eb13e9 Antony Chazapis
                sql = 'insert or replace into metadata (version_id, key, value) values (?, ?, ?)'
752 a6eb13e9 Antony Chazapis
                self.con.execute(sql, (dest_version_id, k, v))
753 58a6c894 Antony Chazapis
    
754 3ab38c43 Antony Chazapis
    def _check_policy(self, policy):
755 3ab38c43 Antony Chazapis
        for k in policy.keys():
756 3ab38c43 Antony Chazapis
            if policy[k] == '':
757 3ab38c43 Antony Chazapis
                policy[k] = self.default_policy.get(k)
758 3ab38c43 Antony Chazapis
        for k, v in policy.iteritems():
759 3ab38c43 Antony Chazapis
            if k == 'quota':
760 3ab38c43 Antony Chazapis
                q = int(v) # May raise ValueError.
761 3ab38c43 Antony Chazapis
                if q < 0:
762 3ab38c43 Antony Chazapis
                    raise ValueError
763 3ab38c43 Antony Chazapis
            elif k == 'versioning':
764 3ab38c43 Antony Chazapis
                if v not in ['auto', 'manual', 'none']:
765 3ab38c43 Antony Chazapis
                    raise ValueError
766 3ab38c43 Antony Chazapis
            else:
767 3ab38c43 Antony Chazapis
                raise ValueError
768 3ab38c43 Antony Chazapis
    
769 3ab38c43 Antony Chazapis
    def _get_policy(self, path):
770 3ab38c43 Antony Chazapis
        sql = 'select key, value from policy where name = ?'
771 3ab38c43 Antony Chazapis
        c = self.con.execute(sql, (path,))
772 3ab38c43 Antony Chazapis
        return dict(c.fetchall())
773 3ab38c43 Antony Chazapis
    
774 f6c97079 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
775 f6c97079 Antony Chazapis
        start = 0
776 f6c97079 Antony Chazapis
        if marker:
777 f6c97079 Antony Chazapis
            try:
778 f6c97079 Antony Chazapis
                start = listing.index(marker) + 1
779 f6c97079 Antony Chazapis
            except ValueError:
780 f6c97079 Antony Chazapis
                pass
781 f6c97079 Antony Chazapis
        if not limit or limit > 10000:
782 f6c97079 Antony Chazapis
            limit = 10000
783 f6c97079 Antony Chazapis
        return start, limit
784 f6c97079 Antony Chazapis
    
785 f6c97079 Antony Chazapis
    def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None, allowed=[]):
786 b0a2d1a6 Antony Chazapis
        cont_prefix = path + '/'
787 b0a2d1a6 Antony Chazapis
        if keys and len(keys) > 0:
788 b0a2d1a6 Antony Chazapis
            sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and
789 f6c97079 Antony Chazapis
                        m.version_id = o.version_id and m.key in (%s)'''
790 b0a2d1a6 Antony Chazapis
            sql = sql % (self._sql_until(until), ', '.join('?' * len(keys)))
791 b0a2d1a6 Antony Chazapis
            param = (cont_prefix + prefix + '%',) + tuple(keys)
792 f6c97079 Antony Chazapis
            if allowed:
793 f6c97079 Antony Chazapis
                for x in allowed:
794 f6c97079 Antony Chazapis
                    sql += ' and o.name like ?'
795 f6c97079 Antony Chazapis
                    param += (x,)
796 f6c97079 Antony Chazapis
            sql += ' order by o.name'
797 b0a2d1a6 Antony Chazapis
        else:
798 f6c97079 Antony Chazapis
            sql = 'select name, version_id from (%s) where name like ?'
799 b0a2d1a6 Antony Chazapis
            sql = sql % self._sql_until(until)
800 b0a2d1a6 Antony Chazapis
            param = (cont_prefix + prefix + '%',)
801 f6c97079 Antony Chazapis
            if allowed:
802 f6c97079 Antony Chazapis
                for x in allowed:
803 f6c97079 Antony Chazapis
                    sql += ' and name like ?'
804 f6c97079 Antony Chazapis
                    param += (x,)
805 f6c97079 Antony Chazapis
            sql += ' order by name'
806 b0a2d1a6 Antony Chazapis
        c = self.con.execute(sql, param)
807 b0a2d1a6 Antony Chazapis
        objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]
808 b0a2d1a6 Antony Chazapis
        if delimiter:
809 b0a2d1a6 Antony Chazapis
            pseudo_objects = []
810 b0a2d1a6 Antony Chazapis
            for x in objects:
811 b0a2d1a6 Antony Chazapis
                pseudo_name = x[0]
812 b0a2d1a6 Antony Chazapis
                i = pseudo_name.find(delimiter, len(prefix))
813 b0a2d1a6 Antony Chazapis
                if not virtual:
814 b0a2d1a6 Antony Chazapis
                    # If the delimiter is not found, or the name ends
815 b0a2d1a6 Antony Chazapis
                    # with the delimiter's first occurence.
816 b0a2d1a6 Antony Chazapis
                    if i == -1 or len(pseudo_name) == i + len(delimiter):
817 b0a2d1a6 Antony Chazapis
                        pseudo_objects.append(x)
818 b0a2d1a6 Antony Chazapis
                else:
819 b0a2d1a6 Antony Chazapis
                    # If the delimiter is found, keep up to (and including) the delimiter.
820 b0a2d1a6 Antony Chazapis
                    if i != -1:
821 b0a2d1a6 Antony Chazapis
                        pseudo_name = pseudo_name[:i + len(delimiter)]
822 b0a2d1a6 Antony Chazapis
                    if pseudo_name not in [y[0] for y in pseudo_objects]:
823 b0a2d1a6 Antony Chazapis
                        if pseudo_name == x[0]:
824 b0a2d1a6 Antony Chazapis
                            pseudo_objects.append(x)
825 b0a2d1a6 Antony Chazapis
                        else:
826 b0a2d1a6 Antony Chazapis
                            pseudo_objects.append((pseudo_name, None))
827 b0a2d1a6 Antony Chazapis
            objects = pseudo_objects
828 b0a2d1a6 Antony Chazapis
        
829 f6c97079 Antony Chazapis
        start, limit = self._list_limits([x[0] for x in objects], marker, limit)
830 b0a2d1a6 Antony Chazapis
        return objects[start:start + limit]
831 b0a2d1a6 Antony Chazapis
    
832 b0a2d1a6 Antony Chazapis
    def _del_version(self, version):
833 b0a2d1a6 Antony Chazapis
        self.mapper.map_remv(version)
834 b0a2d1a6 Antony Chazapis
        sql = 'delete from versions where version_id = ?'
835 b0a2d1a6 Antony Chazapis
        self.con.execute(sql, (version,))
836 b0a2d1a6 Antony Chazapis
    
837 b0a2d1a6 Antony Chazapis
    # Access control functions.
838 b0a2d1a6 Antony Chazapis
    
839 b0a2d1a6 Antony Chazapis
    def _check_groups(self, groups):
840 b0a2d1a6 Antony Chazapis
        # Example follows.
841 b0a2d1a6 Antony Chazapis
        # for k, v in groups.iteritems():
842 b0a2d1a6 Antony Chazapis
        #     if True in [False or ',' in x for x in v]:
843 b0a2d1a6 Antony Chazapis
        #         raise ValueError('Bad characters in groups')
844 b0a2d1a6 Antony Chazapis
        pass
845 b0a2d1a6 Antony Chazapis
    
846 b0a2d1a6 Antony Chazapis
    def _get_groups(self, account):
847 b0a2d1a6 Antony Chazapis
        sql = 'select gname, user from groups where account = ?'
848 b0a2d1a6 Antony Chazapis
        c = self.con.execute(sql, (account,))
849 b0a2d1a6 Antony Chazapis
        groups = {}
850 b0a2d1a6 Antony Chazapis
        for row in c.fetchall():
851 b0a2d1a6 Antony Chazapis
            if row[0] not in groups:
852 b0a2d1a6 Antony Chazapis
                groups[row[0]] = []
853 b0a2d1a6 Antony Chazapis
            groups[row[0]].append(row[1])
854 b0a2d1a6 Antony Chazapis
        return groups
855 b0a2d1a6 Antony Chazapis
    
856 b0a2d1a6 Antony Chazapis
    def _put_groups(self, account, groups, replace=False):
857 b0a2d1a6 Antony Chazapis
        if replace:
858 b0a2d1a6 Antony Chazapis
            self._del_groups(account)
859 b0a2d1a6 Antony Chazapis
        for k, v in groups.iteritems():
860 b0a2d1a6 Antony Chazapis
            sql = 'delete from groups where account = ? and gname = ?'
861 b0a2d1a6 Antony Chazapis
            self.con.execute(sql, (account, k))
862 b0a2d1a6 Antony Chazapis
            if v:
863 b0a2d1a6 Antony Chazapis
                sql = 'insert into groups (account, gname, user) values (?, ?, ?)'
864 b0a2d1a6 Antony Chazapis
                self.con.executemany(sql, [(account, k, x) for x in v])
865 b0a2d1a6 Antony Chazapis
    
866 b0a2d1a6 Antony Chazapis
    def _del_groups(self, account):
867 b0a2d1a6 Antony Chazapis
        sql = 'delete from groups where account = ?'
868 b0a2d1a6 Antony Chazapis
        self.con.execute(sql, (account,))
869 b0a2d1a6 Antony Chazapis
    
870 3436eeb0 Antony Chazapis
    def _check_permissions(self, path, permissions):
871 3436eeb0 Antony Chazapis
        # Check for existing permissions.
872 3436eeb0 Antony Chazapis
        sql = '''select name from permissions
873 3436eeb0 Antony Chazapis
                    where name != ? and (name like ? or ? like name || ?)'''
874 3436eeb0 Antony Chazapis
        c = self.con.execute(sql, (path, path + '%', path, '%'))
875 1993fea9 Antony Chazapis
        row = c.fetchone()
876 1993fea9 Antony Chazapis
        if row:
877 1993fea9 Antony Chazapis
            ae = AttributeError()
878 1993fea9 Antony Chazapis
            ae.data = row[0]
879 1993fea9 Antony Chazapis
            raise ae
880 3436eeb0 Antony Chazapis
        
881 cca6c617 Antony Chazapis
        # Format given permissions.
882 cca6c617 Antony Chazapis
        if len(permissions) == 0:
883 b0a2d1a6 Antony Chazapis
            return [], []
884 3436eeb0 Antony Chazapis
        r = permissions.get('read', [])
885 3436eeb0 Antony Chazapis
        w = permissions.get('write', [])
886 b0a2d1a6 Antony Chazapis
        # Examples follow.
887 b0a2d1a6 Antony Chazapis
        # if True in [False or ',' in x for x in r]:
888 b0a2d1a6 Antony Chazapis
        #     raise ValueError('Bad characters in read permissions')
889 b0a2d1a6 Antony Chazapis
        # if True in [False or ',' in x for x in w]:
890 b0a2d1a6 Antony Chazapis
        #     raise ValueError('Bad characters in write permissions')
891 b0a2d1a6 Antony Chazapis
        return r, w
892 3436eeb0 Antony Chazapis
    
893 3436eeb0 Antony Chazapis
    def _get_permissions(self, path):
894 a6eb13e9 Antony Chazapis
        # Check for permissions at path or above.
895 b0a2d1a6 Antony Chazapis
        sql = 'select name, op, user from permissions where ? like name || ?'
896 a6eb13e9 Antony Chazapis
        c = self.con.execute(sql, (path, '%'))
897 b0a2d1a6 Antony Chazapis
        name = path
898 b0a2d1a6 Antony Chazapis
        perms = {} # Return nothing, if nothing is set.
899 b0a2d1a6 Antony Chazapis
        for row in c.fetchall():
900 b0a2d1a6 Antony Chazapis
            name = row[0]
901 b0a2d1a6 Antony Chazapis
            if row[1] not in perms:
902 b0a2d1a6 Antony Chazapis
                perms[row[1]] = []
903 b0a2d1a6 Antony Chazapis
            perms[row[1]].append(row[2])
904 b0a2d1a6 Antony Chazapis
        return name, perms
905 3436eeb0 Antony Chazapis
    
906 3436eeb0 Antony Chazapis
    def _put_permissions(self, path, r, w):
907 b0a2d1a6 Antony Chazapis
        sql = 'delete from permissions where name = ?'
908 b0a2d1a6 Antony Chazapis
        self.con.execute(sql, (path,))
909 b0a2d1a6 Antony Chazapis
        sql = 'insert into permissions (name, op, user) values (?, ?, ?)'
910 b0a2d1a6 Antony Chazapis
        if r:
911 b0a2d1a6 Antony Chazapis
            self.con.executemany(sql, [(path, 'read', x) for x in r])
912 b0a2d1a6 Antony Chazapis
        if w:
913 b0a2d1a6 Antony Chazapis
            self.con.executemany(sql, [(path, 'write', x) for x in w])
914 3436eeb0 Antony Chazapis
    
915 e0f916bb Antony Chazapis
    def _get_public(self, path):
916 e0f916bb Antony Chazapis
        sql = 'select name from public where name = ?'
917 e0f916bb Antony Chazapis
        c = self.con.execute(sql, (path,))
918 e0f916bb Antony Chazapis
        row = c.fetchone()
919 e0f916bb Antony Chazapis
        if not row:
920 e0f916bb Antony Chazapis
            return False
921 e0f916bb Antony Chazapis
        return True
922 e0f916bb Antony Chazapis
    
923 e0f916bb Antony Chazapis
    def _put_public(self, path, public):
924 e0f916bb Antony Chazapis
        if not public:
925 e0f916bb Antony Chazapis
            sql = 'delete from public where name = ?'
926 e0f916bb Antony Chazapis
        else:
927 e0f916bb Antony Chazapis
            sql = 'insert or replace into public (name) values (?)'
928 e0f916bb Antony Chazapis
        self.con.execute(sql, (path,))
929 e0f916bb Antony Chazapis
    
930 84846143 Antony Chazapis
    def _del_sharing(self, path):
931 84846143 Antony Chazapis
        sql = 'delete from permissions where name = ?'
932 84846143 Antony Chazapis
        self.con.execute(sql, (path,))
933 84846143 Antony Chazapis
        sql = 'delete from public where name = ?'
934 84846143 Antony Chazapis
        self.con.execute(sql, (path,))
935 17629fea Antony Chazapis
    
936 17629fea Antony Chazapis
    def _is_allowed(self, user, account, container, name, op='read'):
937 17629fea Antony Chazapis
        if user == account:
938 17629fea Antony Chazapis
            return True
939 f7667baf Antony Chazapis
        path = '/'.join((account, container, name))
940 17629fea Antony Chazapis
        if op == 'read' and self._get_public(path):
941 17629fea Antony Chazapis
            return True
942 17629fea Antony Chazapis
        perm_path, perms = self._get_permissions(path)
943 17629fea Antony Chazapis
        
944 17629fea Antony Chazapis
        # Expand groups.
945 17629fea Antony Chazapis
        for x in ('read', 'write'):
946 17629fea Antony Chazapis
            g_perms = set()
947 17629fea Antony Chazapis
            for y in perms.get(x, []):
948 17629fea Antony Chazapis
                if ':' in y:
949 17629fea Antony Chazapis
                    g_account, g_name = y.split(':', 1)
950 17629fea Antony Chazapis
                    groups = self._get_groups(g_account)
951 17629fea Antony Chazapis
                    if g_name in groups:
952 17629fea Antony Chazapis
                        g_perms.update(groups[g_name])
953 17629fea Antony Chazapis
                else:
954 17629fea Antony Chazapis
                    g_perms.add(y)
955 17629fea Antony Chazapis
            perms[x] = g_perms
956 17629fea Antony Chazapis
        
957 17629fea Antony Chazapis
        if op == 'read' and ('*' in perms['read'] or user in perms['read']):
958 17629fea Antony Chazapis
            return True
959 17629fea Antony Chazapis
        if '*' in perms['write'] or user in perms['write']:
960 17629fea Antony Chazapis
            return True
961 17629fea Antony Chazapis
        return False
962 17629fea Antony Chazapis
    
963 17629fea Antony Chazapis
    def _can_read(self, user, account, container, name):
964 17629fea Antony Chazapis
        if not self._is_allowed(user, account, container, name, 'read'):
965 17629fea Antony Chazapis
            raise NotAllowedError
966 17629fea Antony Chazapis
    
967 17629fea Antony Chazapis
    def _can_write(self, user, account, container, name):
968 17629fea Antony Chazapis
        if not self._is_allowed(user, account, container, name, 'write'):
969 17629fea Antony Chazapis
            raise NotAllowedError
970 17629fea Antony Chazapis
    
971 17629fea Antony Chazapis
    def _allowed_paths(self, user, prefix=None):
972 17629fea Antony Chazapis
        sql = '''select distinct name from permissions where (user = ?
973 17629fea Antony Chazapis
                    or user in (select account || ':' || gname from groups where user = ?))'''
974 17629fea Antony Chazapis
        param = (user, user)
975 17629fea Antony Chazapis
        if prefix:
976 17629fea Antony Chazapis
            sql += ' and name like ?'
977 17629fea Antony Chazapis
            param += (prefix + '/%',)
978 17629fea Antony Chazapis
        c = self.con.execute(sql, param)
979 17629fea Antony Chazapis
        return [x[0] for x in c.fetchall()]
980 17629fea Antony Chazapis
    
981 17629fea Antony Chazapis
    def _allowed_accounts(self, user):
982 17629fea Antony Chazapis
        allow = set()
983 17629fea Antony Chazapis
        for path in self._allowed_paths(user):
984 17629fea Antony Chazapis
            allow.add(path.split('/', 1)[0])
985 17629fea Antony Chazapis
        return sorted(allow)
986 17629fea Antony Chazapis
    
987 17629fea Antony Chazapis
    def _allowed_containers(self, user, account):
988 17629fea Antony Chazapis
        allow = set()
989 17629fea Antony Chazapis
        for path in self._allowed_paths(user, account):
990 17629fea Antony Chazapis
            allow.add(path.split('/', 2)[1])
991 17629fea Antony Chazapis
        return sorted(allow)
992 b18ef3ad Antony Chazapis
    
993 b18ef3ad Antony Chazapis
    def _shared_paths(self, prefix):
994 b18ef3ad Antony Chazapis
        sql = 'select distinct name from permissions where name like ?'
995 b18ef3ad Antony Chazapis
        c = self.con.execute(sql, (prefix + '/%',))
996 b18ef3ad Antony Chazapis
        return [x[0] for x in c.fetchall()]