This file is indexed.

/usr/share/pyshared/desktopcouch/application/migration/__init__.py is in python-desktopcouch-application 1.0.8-0ubuntu3.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright 2010 Canonical Ltd.
#
# This file is part of desktopcouch.
#
# desktopcouch is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# desktopcouch is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with desktopcouch.  If not, see <http://www.gnu.org/licenses/>.
#
# Authors: Eric Casteleijn <eric.casteleijn@canonical.com>
#          Vincenzo Di Somma <vincenzo.di.somma@canonical.com>

"""Basic migration infrastructure"""

import logging
import os
import glob
import itertools
import xdg.BaseDirectory

from desktopcouch.records import database
from desktopcouch.application import server
from desktopcouch.application.platform import find_port
from desktopcouch.application.server import DesktopDatabase


MIGRATION_DESIGN_DOCUMENT = 'dc_migration'


def update_design_documents(ctx=None):
    """Check system design documents and update any that need updating

    A database should be created if
    $XDG_DATA_DIRs/desktop-couch/databases/dbname/database.cfg exists

    Design docs are defined by the existence of
    $XDG_DATA_DIRs/desktop-couch/databases/dbname/_design/designdocname\
        /views/viewname/map.js

    reduce.js may also exist in the same folder.
    """
    from desktopcouch.application import local_files
    if ctx is None:
        ctx = local_files.DEFAULT_CONTEXT
    ctx_data_dir = os.path.split(ctx.db_dir)[0]
    for base in itertools.chain([ctx_data_dir],
                                xdg.BaseDirectory.xdg_data_dirs):
        # FIXME base may have magic chars. assert not glob.has_magic(base)?
        db_spec = os.path.join(
            base, "desktop-couch", "databases", "*", "database.cfg")
        for database_path in glob.glob(db_spec):
            database_root = os.path.split(database_path)[0]
            database_name = os.path.split(database_root)[1]
            # Just the presence of database.cfg is enough to create
            # the database
            logging.info("Updating database %s", database_name)
            db = DesktopDatabase(database_name, create=True, ctx=ctx)
            # look for design documents
            dd_spec = os.path.join(
                database_root, "_design", "*", "views", "*", "map.js")
            # FIXME: dd_path may have magic chars.
            for dd_path in glob.glob(dd_spec):
                view_root = os.path.split(dd_path)[0]
                view_name = os.path.split(view_root)[1]
                dd_root = os.path.split(os.path.split(view_root)[0])[0]
                dd_name = os.path.split(dd_root)[1]

                def load_js_file(filename_no_extension):
                    """Load javascript view definition from a file."""
                    fn = os.path.join(
                        view_root, "%s.js" % (filename_no_extension))
                    if not os.path.isfile(fn):
                        return None
                    fp = open(fn)
                    data = fp.read()
                    fp.close()
                    return data

                mapjs = load_js_file("map")
                reducejs = load_js_file("reduce")

                # XXX check whether this already exists or not, rather
                # than inefficiently just overwriting it regardless
                if reducejs is not None:
                    db.add_view(
                        view_name, mapjs, reduce_js=reducejs,
                        design_doc=dd_name)
                else:
                    db.add_view(
                        view_name, mapjs, design_doc=dd_name)


def _all_dbs(ctx):
    """Get all the non private dbs from a server."""
    port = find_port(ctx=ctx)
    uri = "http://localhost:%s" % port
    couchdb_server = server.DesktopServer(uri, oauth_tokens=None, ctx=ctx)
    return [x for x in couchdb_server if not
            x.startswith('_') and not x == database.DCTRASH]


def register(migration_method, dbs=None):
    """Register a migration script, with the view and the dbs it is meant to
    migrate."""
    if dbs is None:
        dbs = []
    MIGRATIONS_REGISTRY.append(
        {'method': migration_method, 'dbs': dbs})


def run_needed_migrations(ctx=None):
    """Run the actual migration."""
    from desktopcouch.application import local_files
    if ctx is None:
        ctx = local_files.DEFAULT_CONTEXT
    all_dbs = _all_dbs(ctx)
    for migration in MIGRATIONS_REGISTRY:
        dbs = migration['dbs']
        if not dbs:
            dbs = all_dbs
        for db_name in dbs:
            db = server.DesktopDatabase(database=db_name, ctx=ctx)
            try:
                migration['method'](db)
                logging.info("Migrating DB: %s", db_name)
            except database.NoSuchDatabase:
                pass

DELETION_VIEW_NAME = 'migrate_deleted'

DELETION_VIEW_CODE = """
    function(doc) {
        if (doc['application_annotations']['Ubuntu One']
        ['private_application_annotations']['deleted'] && doc.record_type) {
        emit(doc.id, doc.id);
        }
    }"""


def migrate_from_deleted_to_trash(migrateable_db):
    """Migrate from deleted flag to the dc_trash database."""
    migrateable_db.add_view(
        DELETION_VIEW_NAME, map_js=DELETION_VIEW_CODE,
        design_doc=MIGRATION_DESIGN_DOCUMENT)
    view_result = migrateable_db.execute_view(
        view_name=DELETION_VIEW_NAME, design_doc=MIGRATION_DESIGN_DOCUMENT)
    for result in view_result:
        migrateable_db.delete_record(result.id)


MIGRATIONS_REGISTRY = [{'method': migrate_from_deleted_to_trash, 'dbs': []}]