This file is indexed.

/usr/share/pyshared/desktopcouch/application/pair/couchdb_pairing/couchdb_io.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# Copyright 2009 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: Chad Miller <chad.miller@canonical.com>

"""Communicate with CouchDB."""

import logging
import urllib
import socket
import uuid
import datetime
from itertools import cycle
try:
    import simplejson as json
except ImportError:
    import json

from desktopcouch.application import local_files
from desktopcouch.application.platform import find_port
from desktopcouch.application import server
from desktopcouch.records import Record

RECTYPE_BASE = "http://www.freedesktop.org/wiki/Specifications/desktopcouch/"
PAIRED_SERVER_RECORD_TYPE = RECTYPE_BASE + "paired_server"
MY_ID_RECORD_TYPE = RECTYPE_BASE + "server_identity"


def obsfuscate(dictionary):
    """Obfuscate a string by replacing all secret values."""
    def maybe_hide(key, value):
        """Replace with gibberish where necessary."""
        if hasattr(key, "endswith") and key.endswith("secret"):
            return "".join(rep for rep, vi in zip(cycle('Hidden'), value))
        else:
            return value

    if not hasattr(dictionary, "iteritems"):
        return dictionary
    return dict(
        (key, maybe_hide(key, obsfuscate(value))) for key, value
        in dictionary.iteritems())


def mkuri(hostname, port, has_ssl=False, path="", auth_pair=None):
    """Create a URI from parts."""
    protocol = "https" if has_ssl else "http"
    if auth_pair:
        auth = (":".join(
            map(urllib.quote, auth_pair)) + "@")  # pylint: disable=W0141
    else:
        auth = ""
    if (protocol, port) in (("http", 80), ("https", 443)):
        return "%s://%s%s/%s" % (protocol, auth, hostname, path)
    else:
        port = str(port)
        return "%s://%s%s:%s/%s" % (protocol, auth, hostname, port, path)


def _get_db(name, create=True, uri=None, ctx=local_files.DEFAULT_CONTEXT):
    """Get (and create?) a database."""
    return server.DesktopDatabase(name, create=create, uri=uri, ctx=ctx)


def put_paired_host(oauth_data, uri=None,
                    ctx=local_files.DEFAULT_CONTEXT, **kwargs):
    """Create a new paired-host record.  OAuth information is required, and
    after the uri, keyword parameters are added to the record."""
    pairing_id = str(uuid.uuid4())
    data = {
        "ctime": datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
        "record_type": PAIRED_SERVER_RECORD_TYPE,
        "pairing_identifier": pairing_id,
    }
    if oauth_data:
        data["oauth"] = {
            "consumer_key": str(oauth_data["consumer_key"]),
            "consumer_secret": str(oauth_data["consumer_secret"]),
            "token": str(oauth_data["token"]),
            "token_secret": str(oauth_data["token_secret"]),
        }
    data.update(kwargs)
    database = _get_db("management", uri=uri, ctx=ctx)
    record = Record(data)
    record_id = database.put_record(record)
    return record_id


def put_static_paired_service(oauth_data, service_name, uri=None,
                              ctx=local_files.DEFAULT_CONTEXT):
    """Create a new service record."""
    return put_paired_host(oauth_data, uri=uri, service_name=service_name,
            pull_from_server=True, push_to_server=True, ctx=ctx)


def put_dynamic_paired_host(hostname, remote_uuid, oauth_data, uri=None,
                            ctx=local_files.DEFAULT_CONTEXT):
    """Create a new dynamic-host record."""
    return put_paired_host(oauth_data, uri=uri, pairing_identifier=remote_uuid,
            push_to_server=True, server=hostname, ctx=ctx)


def get_static_paired_hosts(uri=None,  # pylint: disable=R0914
                            ctx=local_files.DEFAULT_CONTEXT, port=None):
    """Retreive a list of static hosts' information in the form of
    (ID, service name, to_push, to_pull) ."""
    if not uri and port is not None:
        uri = "http://localhost:%d/" % (port,)
    db = _get_db("management", uri=uri, ctx=ctx)
    results = db.get_all_records(record_type=PAIRED_SERVER_RECORD_TYPE)
    found = dict()
    for result in results:
        try:
            if result["service_name"] != "":
                pairing_id = result["pairing_identifier"]
                to_push = result.get("push_to_server", True)
                to_pull = result.get("pull_from_server", False)
                service_name = result["service_name"]
                found[service_name] = pairing_id, to_pull, to_push
        except KeyError:
            pass
    unique_hosts = [(v1, sn, v2, v3) for
            (sn), (v1, v2, v3) in found.items()]
    logging.debug("static pairings are %s", unique_hosts)
    return unique_hosts


def get_database_names_replicatable(uri, oauth_tokens=None, service=False,
        ctx=local_files.DEFAULT_CONTEXT, user_id=None):
    """Find a list of local databases, minus dbs that we do not want to
    replicate (explicitly or implicitly)."""
    if not uri:
        port = find_port(ctx=ctx)
        uri = "http://localhost:%s" % port
    couchdb_server = server.DesktopServer(uri, oauth_tokens=oauth_tokens,
            ctx=ctx)
    try:
        if user_id is None:
            all_dbs = set([db_name for db_name in couchdb_server])
            logging.debug(
                "Got list of databases from %s:\n    %s",
                couchdb_server, all_dbs)
        else:
            code, _, res = couchdb_server.resource.get('_all_dbs',
                                                       user_id=user_id)
            if code != 200:
                logging.error("%s/_all_dbs?user_id=%s HTTP%d", couchdb_server,
                              user_id, code)
                return set()

            all_dbs = set(json.load(res))
            logging.debug(
                "Got list of databases from %s/_all_dbs?user_id=%s:\n    %r",
                couchdb_server, user_id, all_dbs)

    except socket.error:
        logging.error("Can't get list of databases from %s", couchdb_server)
        return set()

    excluded = set()
    excluded.add("management")
    excluded.add("users")
    if not service:
        excluded_msets = _get_management_data(PAIRED_SERVER_RECORD_TYPE,
                "excluded_names", uri=uri, ctx=ctx)
        for excluded_mset in excluded_msets:
            excluded.update(excluded_mset)

    return set([n for n in all_dbs - excluded if not n.startswith("_")])


def get_my_host_unique_id(uri=None, create=True,
                          ctx=local_files.DEFAULT_CONTEXT):
    """Returns a list of ids we call ourselves.  We complain in the log if it's
    more than one, but it's really no error.  If there are zero (id est, we've
    never paired with anyone), then returns None."""

    db = _get_db("management", uri=uri, ctx=ctx)
    db.ensure_full_commit()
    ids = _get_management_data(
        MY_ID_RECORD_TYPE, "self_identity", uri=uri, ctx=ctx)
    ids = list(set(ids))  # uniqify
    if len(ids) > 1:
        logging.error("DANGER!  We have more than one record claiming to be "
                "this host's unique identifier.  Which is right?  We will try "
                "to use them all, but this smells really funny.")
        return ids
    if len(ids) == 1:
        return ids

    if not create:
        return None
    else:
        data = {"self_identity": str(uuid.uuid4()),
                "record_type": MY_ID_RECORD_TYPE}
        data["_id"] = data["self_identity"]
        db.put_record(Record(data))
        logging.debug("set new self-identity value: %r", data["self_identity"])
        return [data["self_identity"]]


def get_all_known_pairings(uri=None, ctx=local_files.DEFAULT_CONTEXT):
    """Info dicts about all pairings, even if marked "unpaired", keyed on
    hostid with another dict as the value."""
    d = {}
    db = _get_db("management", uri=uri, ctx=ctx)
    for result in db.get_all_records(record_type=PAIRED_SERVER_RECORD_TYPE):
        value = {}
        value["record_id"] = result.record_id
        value["active"] = True
        if "oauth" in result:
            value["oauth"] = result["oauth"]
        if "unpaired" in result:
            value["active"] = not result["unpaired"]
        hostid = result["pairing_identifier"]
        d[hostid] = value
    return d


def _get_management_data(record_type, key, uri=None,
                         ctx=local_files.DEFAULT_CONTEXT):
    """Get the management data from couchdb."""
    db = _get_db("management", uri=uri, ctx=ctx)
    results = db.get_all_records(record_type=record_type)
    values = list()
    for record in results:
        if key in record:
            value = record[key]
            if value is not None:
                if "_order" in value:
                    # MergableList, so decode.
                    values.append([value[k] for k in value["_order"]])
                else:
                    values.append(value)
            else:
                logging.debug("skipping record empty %s", key)
        else:
            logging.debug(
                "skipping record %s with no %s", obsfuscate(record), key)
    logging.debug("found %d %s records", len(values), key)
    return values


def create_database(dst_host, dst_port, dst_name, use_ssl=False,
                    oauth_tokens=None):
    """Given parts, create a database."""
    dst_url = mkuri(dst_host, dst_port, use_ssl)
    return server.DesktopDatabase(dst_name, dst_url, create=True,
            oauth_tokens=oauth_tokens)


def replicate(source_database, target_database,   # pylint: disable=R0914
              target_host=None, target_port=None, source_host=None,
              source_port=None, source_ssl=False, target_ssl=False,
              source_oauth=None, target_oauth=None, local_uri=None, ctx=None):
    """This replication is instant and blocking, and does not persist. """
    try:
        if target_host:
            # Target databases must exist before replicating to them.
            logging.debug(
                "creating %r %s:%d %s", target_database, target_host,
                target_port, obsfuscate(target_oauth))
            create_database(
                target_host, target_port, target_database, use_ssl=target_ssl,
                oauth_tokens=target_oauth)
        else:
            server.DesktopDatabase(target_database, create=True, uri=local_uri,
                                   ctx=ctx)
        logging.debug("%r exists. Ready to replicate.", target_database)
    except:                             # pylint: disable=W0702
        logging.exception(
            "can't create/verify %r %s:%d  oauth=%s", target_database,
            target_host, target_port, obsfuscate(target_oauth))
    if source_host:
        source = mkuri(source_host, source_port, source_ssl, urllib.quote(
            source_database, safe=""))
    else:
        source = urllib.quote(source_database, safe="")

    if target_host:
        target = mkuri(target_host, target_port, target_ssl, urllib.quote(
            target_database, safe=""))
    else:
        target = urllib.quote(target_database, safe="")

    if source_oauth:
        assert "consumer_secret" in source_oauth
        source = dict(url=source, auth=dict(oauth=source_oauth))

    if target_oauth:
        assert "consumer_secret" in target_oauth
        target = dict(url=target, auth=dict(oauth=target_oauth))

    try:
        # regardless of source and target, we talk to our local couchdb  :(
        if not local_uri:
            port = int(find_port())
            local_uri = mkuri("localhost", port,)

        logging.debug(
                "asking %r to replicate %s to %s", obsfuscate(local_uri),
                obsfuscate(source), obsfuscate(target),)

        # All until python-couchdb gets a Server.replicate() function
        local_server = server.DesktopServer(local_uri, ctx=ctx)
        data = local_server.replicate(source, target)
        logging.debug("replicate result: %r", obsfuscate(data))
    except:                             # pylint: disable=W0702
        logging.exception("can't replicate %r %r <= %r to %r", source_database,
                local_uri, obsfuscate(source), obsfuscate(target))


def get_pairings(uri=None, ctx=local_files.DEFAULT_CONTEXT):
    """Get a list of paired servers."""
    db = _get_db("management", create=True, uri=uri, ctx=ctx)

    design_doc = "paired_servers"
    if not db.view_exists("paired_servers", design_doc):
        map_js = """function(doc) {
            if (doc.record_type == %r && ! doc.unpaired)  // unset or False
                if (doc.server) {
                    emit(doc.server, doc);
                } else if (doc.service_name) {
                    emit(doc.service_name, doc);
                }
            }
        """ % (PAIRED_SERVER_RECORD_TYPE,)
        db.add_view("paired_servers", map_js, design_doc=design_doc)

    return db.execute_view("paired_servers")


def remove_pairing(record_id, is_reconciled, uri=None,
                   ctx=local_files.DEFAULT_CONTEXT):
    """Remove a pairing record (or mark it as dead so it can be cleaned up
    properly later)."""
    db = _get_db("management", create=True, uri=uri, ctx=ctx)
    if is_reconciled:
        db.delete_record(record_id)
    else:
        db.update_fields(record_id, {"unpaired": True})


def expunge_pairing(host_id, uri=None):
    """Remove pairing record for host_id."""
    try:
        d = get_all_known_pairings(uri)
        record_id = d[host_id]["record_id"]
        remove_pairing(record_id, True, uri)
    except KeyError, e:
        logging.warn("no key.  %s", e)