--- desktopcouch-0.6.2.orig/desktopcouch/replication.py +++ desktopcouch-0.6.2/desktopcouch/replication.py @@ -70,7 +70,6 @@ def do_all_replication(local_port): log.debug("started replicating") - local_uri = couchdb_io.mkuri("localhost", local_port) try: global already_replicating # Fuzzy, as not really critical, already_replicating = True # just trying to be polite. @@ -82,7 +81,7 @@ # all those. for remote_hostid, addr, port, is_unpaired, remote_oauth in \ - dbus_io.get_seen_paired_hosts(uri=local_uri): + dbus_io.get_seen_paired_hosts(): if is_unpaired: # The far end doesn't know want to break up. @@ -104,7 +103,7 @@ # push caught exception back... except: # ... so that we log it here. - log.exception( + logging.exception( "failed to unpair from other end.") continue else: @@ -116,12 +115,12 @@ # Ah, good, this is an active relationship. Be a giver. log.debug("want to replipush to discovered host %r @ %s", remote_hostid, addr) - for db_name in couchdb_io.get_database_names_replicatable(local_uri): + for db_name in couchdb_io.get_database_names_replicatable( + couchdb_io.mkuri("localhost", local_port)): if not is_running: return couchdb_io.replicate(db_name, db_name, target_host=addr, target_port=port, - source_port=local_port, target_oauth=remote_oauth, - local_uri=local_uri) + source_port=local_port, target_oauth=remote_oauth) log.debug("replication of discovered hosts finished") except Exception, e: log.exception("replication of discovered hosts aborted") @@ -132,7 +131,7 @@ # to look up what the service needs from us. for remote_hostid, sn, to_pull, to_push in \ - couchdb_io.get_static_paired_hosts(port=local_port): + couchdb_io.get_static_paired_hosts(): if not sn in dir(replication_services): if not is_running: @@ -158,7 +157,8 @@ remote_db_name_prefix = urlinfo.path.strip("/") # ^ if to_push: - for db_name in couchdb_io.get_database_names_replicatable(local_uri): + for db_name in couchdb_io.get_database_names_replicatable( + couchdb_io.mkuri("localhost", int(local_port))): if not is_running: return remote_db_name = remote_db_name_prefix + "/" + db_name @@ -169,8 +169,7 @@ couchdb_io.replicate(db_name, remote_db_name, target_host=addr, target_port=port, source_port=local_port, target_ssl=True, - target_oauth=remote_oauth_data, - local_uri=local_uri) + target_oauth=remote_oauth_data) if to_pull: for remote_db_name in \ couchdb_io.get_database_names_replicatable( @@ -196,8 +195,7 @@ couchdb_io.replicate(remote_db_name, db_name, source_host=addr, source_port=port, target_port=local_port, source_ssl=True, - source_oauth=remote_oauth_data, - local_uri=local_uri) + source_oauth=remote_oauth_data) except Exception, e: log.exception("replication of services aborted") @@ -231,7 +229,7 @@ dbus_io.maintain_discovered_servers() - t = task.LoopingCall(replicate_local_databases_to_paired_hosts, int(port)) + t = task.LoopingCall(replicate_local_databases_to_paired_hosts, port) t.start(600) # TODO: port may change, so every so often, check it and --- desktopcouch-0.6.2.orig/desktopcouch/records/server_base.py +++ desktopcouch-0.6.2/desktopcouch/records/server_base.py @@ -21,42 +21,18 @@ """The Desktop Couch Records API.""" -import httplib2, urlparse, cgi, copy -from time import time - -# please keep desktopcouch python 2.5 compatible for now - -# pylint can't deal with failing imports even when they're handled -# pylint: disable-msg=F0401 -try: - # Python 2.5 - import simplejson as json -except ImportError: - # Python 2.6+ - import json -# pylint: enable-msg=F0401 - -from oauth import oauth - from couchdb import Server -from couchdb.client import ResourceNotFound, ResourceConflict, uri as couchdburi +from couchdb.client import ResourceNotFound, ResourceConflict from couchdb.design import ViewDefinition from record import Record +import httplib2 +from oauth import oauth +import urlparse +import cgi #DEFAULT_DESIGN_DOCUMENT = "design" DEFAULT_DESIGN_DOCUMENT = None # each view in its own eponymous design doc. -class FieldsConflict(Exception): - """Raised in case of an unrecoverable couchdb conflict.""" - - #pylint: disable-msg=W0231 - def __init__(self, conflicts): - self.conflicts = conflicts - #pylint: enable-msg=W0231 - - def __str__(self): - return "" % self.conflicts - class NoSuchDatabase(Exception): "Exception for trying to use a non-existent database" @@ -69,22 +45,21 @@ return ("Database %s does not exist on this server. (Create it by " "passing create=True)") % self.database - class OAuthAuthentication(httplib2.Authentication): """An httplib2.Authentication subclass for OAuth""" - def __init__(self, oauth_data, host, request_uri, headers, response, + def __init__(self, oauth_data, host, request_uri, headers, response, content, http, scheme): self.oauth_data = oauth_data self.scheme = scheme - httplib2.Authentication.__init__(self, None, host, request_uri, + httplib2.Authentication.__init__(self, None, host, request_uri, headers, response, content, http) def request(self, method, request_uri, headers, content): """Modify the request headers to add the appropriate Authorization header.""" - consumer = oauth.OAuthConsumer(self.oauth_data['consumer_key'], + consumer = oauth.OAuthConsumer(self.oauth_data['consumer_key'], self.oauth_data['consumer_secret']) - access_token = oauth.OAuthToken(self.oauth_data['token'], + access_token = oauth.OAuthToken(self.oauth_data['token'], self.oauth_data['token_secret']) sig_method = oauth.OAuthSignatureMethod_HMAC_SHA1 full_http_url = "%s://%s%s" % (self.scheme, self.host, request_uri) @@ -101,15 +76,14 @@ req.sign_request(sig_method(), consumer, access_token) headers.update(httplib2._normalize_headers(req.to_header())) - class OAuthCapableHttp(httplib2.Http): - """Subclass of httplib2.Http which specifically uses our OAuth + """Subclass of httplib2.Http which specifically uses our OAuth Authentication subclass (because httplib2 doesn't know about it)""" def __init__(self, scheme="http", cache=None, timeout=None, proxy_info=None): self.__scheme = scheme super(OAuthCapableHttp, self).__init__(cache, timeout, proxy_info) - def add_oauth_tokens(self, consumer_key, consumer_secret, + def add_oauth_tokens(self, consumer_key, consumer_secret, token, token_secret): self.oauth_data = { "consumer_key": consumer_key, @@ -123,7 +97,7 @@ """Since we know we're talking to desktopcouch, and we know that it requires OAuth, just return the OAuthAuthentication here rather than checking to see which supported auth method is required.""" - yield OAuthAuthentication(self.oauth_data, host, request_uri, headers, + yield OAuthAuthentication(self.oauth_data, host, request_uri, headers, response, content, self, self.__scheme) def row_is_deleted(row): @@ -151,14 +125,6 @@ raise NoSuchDatabase(database) self.db = self._server[database] self.record_factory = record_factory or Record - self._changes_since = 0 - self._changes_last_used = 0 - try: - self.report_changes(lambda **_: None) # Update _changes_since. - except IOError: - pass - self._changes_last_used = 0 # Immediate run works. - def _temporary_query(self, map_fun, reduce_fun=None, language='javascript', wrapper=None, **options): @@ -173,140 +139,42 @@ except ResourceNotFound: return None data = {} - if row_is_deleted(couch_record): + if 'deleted' in couch_record.get('application_annotations', {}).get( + 'Ubuntu One', {}).get('private_application_annotations', {}): return None data.update(couch_record) record = self.record_factory(data=data) - - def make_getter(source_db, document_id, attachment_name, content_type): - """Closure storing the database for lower levels to use when needed.""" - def getter(): - return source_db.get_attachment(document_id, attachment_name), \ - content_type - return getter - if "_attachments" in data: - for att_name, att_attributes in data["_attachments"].iteritems(): - record.attach_by_reference(att_name, - make_getter(self.db, record_id, att_name, - att_attributes["content_type"])) + record.record_id = record_id return record def put_record(self, record): """Put a record in back end storage.""" - if not record.record_id: - # Do not rely on couchdb to create an ID for us. - from uuid import uuid4 - record.record_id = uuid4().hex - self.db[record.record_id] = record._data - - for attachment_name in record.list_attachments(): - data, content_type = record.attachment_data(attachment_name) - self.db.put_attachment( - record._data, data, attachment_name, content_type) - - return record.record_id - - def put_records_batch(self, batch): - """Put a batch of records in back end storage.""" - # used to access fast the record - records_hash = {} - for record in batch: - if not record.record_id: - from uuid import uuid4 # Do not rely on couchdb to create an ID for us. - record.record_id = uuid4().hex - records_hash[record.record_id] = record - # although with a single record we need to test for the revisison, with - # a batch we do not, but we have to make sure that we did not get an error - batch_put_result = self.db.update(batch) - for current_tuple in batch_put_result: - success, docid, rev_or_exc = current_tuple - if success: - record = records_hash[docid] - # set the new rev - record._data["_rev"] = rev_or_exc - for attachment_name in record.list_attachments(): - data, content_type = record.attachment_data(attachment_name) - self.db.put_attachment( - {"_id":record.record_id, "_rev":record["_rev"]}, - data, attachment_name, content_type) - # all success record have the blobs added we return result of update - return batch_put_result - - def update_fields(self, record_id, fields, cached_record=None): - """Safely update a number of fields. 'fields' being a - dictionary with path_tuple: new_value for only the fields we - want to change the value of, where path_tuple is a tuple of - fieldnames indicating the path to the possibly nested field - we're interested in. old_record a the copy of the record we - most recently read from the database. - - In the case the underlying document was changed, we try to - merge, but only if none of the old values have changed. (i.e., - do not overwrite changes originating elsewhere.) + record_id = record.record_id or record._data.get('_id', '') + record_data = record._data + if record_id: + self.db[record_id] = record_data + else: + record_id = self._add_record(record_data) + return record_id - This is slightly hairy, so that other code won't have to be. + def update_fields(self, record_id, fields): + """Safely update a number of fields. 'fields' being a + dictionary with fieldname: value for only the fields we want + to change the value of. """ - # Initially, the record in memory and in the db are the same - # as far as we know. (If they're not, we'll get a - # ResourceConflict later on, from which we can recover.) - if cached_record is None: - cached_record = self.db[record_id] - if isinstance(cached_record, Record): - cached_record = cached_record._data - record = copy.deepcopy(cached_record) - # Loop until either failure or success has been determined while True: - modified = False - conflicts = {} - # loop through all the fields that need to be modified - for path, new_value in fields.items(): - if not isinstance(path, tuple): - path = (path,) - # Walk down in both copies of the record to the leaf - # node that represents the field, creating the path in - # the in memory record if necessary. - db_parent = record - cached_parent = cached_record - for field in path[:-1]: - db_parent = db_parent.setdefault(field, {}) - cached_parent = cached_parent.get(field, {}) - # Get the values of the fields in the two copies. - db_value = db_parent.get(path[-1]) - cached_value = cached_parent.get(path[-1]) - # If the value we intend to store is already in the - # database, we need do nothing, which is our favorite. - if db_value == new_value: - continue - # If the value in the db is different than the value - # our copy holds, we have a conflict. We could bail - # here, but we can give better feedback if we gather - # all the conflicts, so we continue the for loop - if db_value != cached_value: - conflicts[path] = (db_value, new_value) - continue - # Otherwise, it is safe to update the field with the - # new value. - modified = True - db_parent[path[-1]] = new_value - # If we had conflicts, we can bail. - if conflicts: - raise FieldsConflict(conflicts) - # If we made changes to the document, we'll need to save - # it. - if modified: - try: - self.db[record_id] = record - except ResourceConflict: - # We got a conflict, meaning the record has - # changed in the database since we last loaded it - # into memory. Let's get a fresh copy and try - # again. - record = self.db[record_id] - continue - # If we get here, nothing remains to be done, and we can - # take a well deserved break. + record = self.db[record_id] + record.update(fields) + try: + self.db[record_id] = record + except ResourceConflict: + continue break + def _add_record(self, data): + """Add a new record to the storage backend.""" + return self.db.create(data) + def delete_record(self, record_id): """Delete record with given id""" record = self.db[record_id] @@ -320,7 +188,8 @@ if record_id not in self.db: return False record = self.db[record_id] - return not row_is_deleted(record) + return 'deleted' not in record.get('application_annotations', {}).get( + 'Ubuntu One', {}).get('private_application_annotations', {}) def delete_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT): """Remove a view, given its name. Raises a KeyError on a unknown @@ -365,14 +234,13 @@ return deleted_data - def execute_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT, - **params): + def execute_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT): """Execute view and return results.""" if design_doc is None: design_doc = view_name view_id_fmt = "_design/%(design_doc)s/_view/%(view_name)s" - return self.db.view(view_id_fmt % locals(), **params) + return self.db.view(view_id_fmt % locals()) def add_view(self, view_name, map_js, reduce_js, design_doc=DEFAULT_DESIGN_DOCUMENT): @@ -464,56 +332,3 @@ return viewdata else: return viewdata[record_type] - - def get_changes(self, niceness=10): - """Get a list of database changes. This is the sister function of - report_changes that returns a list instead of calling a function for - each.""" - l = list() - self.report_changes(lambda **k: l.append(k), niceness=niceness) - return l - - def report_changes(self, function_to_send_changes_to, niceness=10): - """Check to see if there are any changes on this database since last - call (or since this object was instantiated), call a function for each, - and return the number of changes reported. - - The callback function is called for every single change, with the - keyword parameters of the dictionary of values returned from couchdb. - - >>> def f(seq=None, id=None, changes=None): - ... pass - - >>> db_foo.report_changes(f) - >>> time.sleep(30) - >>> db_foo.report_changes(f) - - or - - >>> # Make function that returns true, to signal never to remove. - >>> report_forever = lambda **kw: db_foo.report_changes(**kw) or True - >>> cb_id = glib.mainloop.idle_add(report_forever, f) - - or - - >>> task_id = twisted.task.looping_call(db_foo.report_changes, f) - """ - now = time() - call_count = 0 - if not niceness or now > self._changes_last_used + niceness: - - # Can't use self._server.resource.get() directly because it encodes "/". - uri = couchdburi(self._server.resource.uri, self.db.name, "_changes", - since=self._changes_since) - resp, data = self._server.resource.http.request(uri, "GET", "", {}) - if resp["status"] != '200': - raise IOError("HTTP response code %s.\n%s" % (resp["status"], data)) - structure = json.loads(data) - for change in structure.get("results"): - # kw-args can't have unicode keys - change_encoded_keys = dict((k.encode("utf8"), v) for k, v in change.iteritems()) - function_to_send_changes_to(**change_encoded_keys) - self._changes_since = change["seq"] # Not s.last_seq. Exceptions! - call_count += 1 - self._changes_last_used = now # If exception in cb, we never update governor. - return call_count --- desktopcouch-0.6.2.orig/desktopcouch/pair/tests/test_couchdb_io.py +++ desktopcouch-0.6.2/desktopcouch/pair/tests/test_couchdb_io.py @@ -18,26 +18,23 @@ import pygtk pygtk.require('2.0') -import desktopcouch.tests as test_environment +import desktopcouch.tests as dctests from desktopcouch.pair.couchdb_pairing import couchdb_io from desktopcouch.records.server import CouchDatabase from desktopcouch.records.record import Record -from twisted.trial import unittest +import unittest import uuid import os import httplib2 -import socket URI = None # use autodiscovery that desktopcouch.tests permits. class TestCouchdbIo(unittest.TestCase): def setUp(self): """setup each test""" - self.mgt_database = CouchDatabase('management', create=True, uri=URI, - ctx=test_environment.test_context) - self.foo_database = CouchDatabase('foo', create=True, uri=URI, - ctx=test_environment.test_context) + self.mgt_database = CouchDatabase('management', create=True, uri=URI) + self.foo_database = CouchDatabase('foo', create=True, uri=URI) #create some records to pull out and test self.foo_database.put_record(Record({ "key1_1": "val1_1", "key1_2": "val1_2", "key1_3": "val1_3", @@ -54,18 +51,6 @@ del self.mgt_database._server['management'] del self.mgt_database._server['foo'] - - def test_obsfuscation(self): - t = {'url': 'https://couchdb.one.ubuntu.com/u%2Fb2%2Fc8%2F276%2Ftest', 'auth': {'oauth': {'consumer_secret': 'SeCrEtSe', 'token': '3XRjQrWX92TTTJFDTWJJ', 'consumer_key': 'ubuntuone', 'token_secret': 'jBmSeCrEtawkefwklefliwuregqwlkeh347wq87w4fiuq4fyu3q4fiqwu4fqwfiqufM6xjsPwSeCrEt4'}}} - cleaned_t = couchdb_io.obsfuscate(t) - self.failIf("SeCrEt" in str(cleaned_t), {'url': 'https://couchdb.one.ubuntu.com/u%2Fb2%2Fc8%2F276%2Ftest', 'auth': {'oauth': {'consumer_secret': 'HiddenHidd', 'token': '3XRjQrWX92TTTJFDTWJJ', 'consumer_key': 'ubuntuone', 'token_secret': 'HiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHiddenHi'}}}) - - self.assertEqual(couchdb_io.obsfuscate(""), "") - self.assertEqual(couchdb_io.obsfuscate({}), {}) - self.assertEqual(couchdb_io.obsfuscate({1:{}}), {1:{}}) - self.assertEqual(couchdb_io.obsfuscate({1:1}), {1:1}) - - def test_put_static_paired_service(self): service_name = "dummyfortest" oauth_data = { @@ -74,8 +59,8 @@ "token": str("opqrst"), "token_secret": str("uvwxyz"), } - couchdb_io.put_static_paired_service(oauth_data, service_name, uri=URI, ctx=test_environment.test_context) - pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context)) + couchdb_io.put_static_paired_service(oauth_data, service_name, uri=URI) + pairings = list(couchdb_io.get_pairings()) def test_put_dynamic_paired_host(self): hostname = "host%d" % (os.getpid(),) @@ -88,48 +73,43 @@ } couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data, - uri=URI, ctx=test_environment.test_context) + uri=URI) couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data, - uri=URI, ctx=test_environment.test_context) + uri=URI) couchdb_io.put_dynamic_paired_host(hostname, remote_uuid, oauth_data, - uri=URI, ctx=test_environment.test_context) + uri=URI) - pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context)) + pairings = list(couchdb_io.get_pairings()) self.assertEqual(3, len(pairings)) self.assertEqual(pairings[0].value["oauth"], oauth_data) self.assertEqual(pairings[0].value["server"], hostname) self.assertEqual(pairings[0].value["pairing_identifier"], remote_uuid) for i, row in enumerate(pairings): - couchdb_io.remove_pairing(row.id, i == 1, ctx=test_environment.test_context) + couchdb_io.remove_pairing(row.id, i == 1) - pairings = list(couchdb_io.get_pairings(ctx=test_environment.test_context)) + pairings = list(couchdb_io.get_pairings()) self.assertEqual(0, len(pairings)) def test_get_database_names_replicatable_bad_server(self): - hostname = "test.desktopcouch.example.com" - try: - socket.gethostbyname(hostname) - raise unittest.SkipTest("nxdomain hijacked") - except socket.gaierror: - pass + # If this resolves, FIRE YOUR DNS PROVIDER. try: names = couchdb_io.get_database_names_replicatable( - uri='http://' + hostname + ':9/') + uri='http://test.desktopcouch.example.com:9/') self.assertEqual(set(), names) except httplib2.ServerNotFoundError: pass def test_get_database_names_replicatable(self): - names = couchdb_io.get_database_names_replicatable(uri=URI, ctx=test_environment.test_context) + names = couchdb_io.get_database_names_replicatable(uri=URI) self.assertFalse('management' in names) self.assertTrue('foo' in names) def test_get_my_host_unique_id(self): - got = couchdb_io.get_my_host_unique_id(uri=URI, ctx=test_environment.test_context) - again = couchdb_io.get_my_host_unique_id(uri=URI, ctx=test_environment.test_context) + got = couchdb_io.get_my_host_unique_id(uri=URI) + again = couchdb_io.get_my_host_unique_id(uri=URI) self.assertEquals(len(got), 1) self.assertEquals(got, again) --- desktopcouch-0.6.2.orig/debian/compat +++ desktopcouch-0.6.2/debian/compat @@ -0,0 +1 @@ +6 --- desktopcouch-0.6.2.orig/debian/watch +++ desktopcouch-0.6.2/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://launchpad.net/desktopcouch/+download .*/desktopcouch-([0-9.]+)\.tar\.gz --- desktopcouch-0.6.2.orig/debian/python-desktopcouch.install +++ desktopcouch-0.6.2/debian/python-desktopcouch.install @@ -0,0 +1,2 @@ +debian/tmp/usr/lib/*/*/desktopcouch/*.py +debian/tmp/usr/lib/*/*/desktopcouch/pair/ --- desktopcouch-0.6.2.orig/debian/control +++ desktopcouch-0.6.2/debian/control @@ -0,0 +1,76 @@ +Source: desktopcouch +Section: python +Priority: optional +Build-Depends: cdbs (>= 0.4.43), + debhelper (>= 6), + python, + python-central (>= 0.6.11), + python-distutils-extra (>= 2.8), + python-setuptools +Maintainer: Ubuntu Developers +Standards-Version: 3.8.4 +XS-Python-Version: current +Homepage: http://launchpad.net/desktopcouch +Vcs-Bzr: https://code.launchpad.net/~ubuntuone-control-tower/ubuntu/karmic/desktopcouch/spb +Vcs-Browser: http://bazaar.launchpad.net/~ubuntuone-control-tower/ubuntu/karmic/desktopcouch/spb + +Package: desktopcouch +Architecture: all +XB-Python-Version: ${python:Versions} +Depends: ${misc:Depends}, + ${python:Depends}, + couchdb-bin (>= 0.10.0-0ubuntu3), + python-dbus, + python-couchdb (>= 0.6), + python-twisted-core, + python-gobject, + python-avahi, + python-desktopcouch, + python-desktopcouch-records, + python-gtk2 +Description: A Desktop CouchDB instance + Runs an instance of CouchDB with the users session. + +Package: desktopcouch-tools +Architecture: all +XB-Python-Version: ${python:Versions} +Depends: ${misc:Depends}, + ${python:Depends}, + python-dbus, + python-twisted-core, + python-gobject, + python-avahi, + python-desktopcouch, + python-desktopcouch-records, + python-gtk2 +Description: Desktop CouchDB tools + Tools used to work with Desktop CouchDB. + +Package: python-desktopcouch +Architecture: all +XB-Python-Version: ${python:Versions} +Depends: ${misc:Depends}, + ${python:Depends}, + desktopcouch, + python-dbus, + python-couchdb, + python-xdg, + python-twisted-core, + python-gtk2, + python-gnomekeyring +Description: Python Desktop CouchDB + A Python library for Desktop CouchDB. + +Package: python-desktopcouch-records +Architecture: all +XB-Python-Version: ${python:Versions} +Depends: ${misc:Depends}, + ${python:Depends}, + python-dbus, + python-couchdb, + python-desktopcouch, + python-gtk2, + python-gnomekeyring, + python-oauth +Description: Desktop CouchDB Records API + A Python library for the Desktop CouchDB Records API. --- desktopcouch-0.6.2.orig/debian/changelog +++ desktopcouch-0.6.2/debian/changelog @@ -0,0 +1,224 @@ +desktopcouch (0.6.2-0ubuntu1~lucid2) lucid; urgency=low + + * New upstream bugfix release. + - Include a new program bin/desktopcouch-get-port . + - Use DBus to get port, and thereby start the service daemon, on normal + Python access to the database. (LP: #519444) + - Update docs to be explicit about put_record(r) mutating r . + - Add new method put_records_batch(iterable_of_records) . + - Fix up apport collection. + - Fix a problem with couchdb mutating its INI file to store a hashed + password, which is useless for HTTP Basic auth in the bookmark file. + - Fix obscure bugs in couchdb startup regarding port availability. + - Update execute_view() ti take a dict of additional parameters for + execution. + - Add has_key() method to desktopcouch.record.RecordDict so that it + behaves more like a dictionary. + + -- Elliot Murphy Mon, 01 Mar 2010 11:47:53 -0500 + +desktopcouch (0.6.1-0ubuntu1) lucid; urgency=low + + * New upstream release. + - When logging replication events, replace all oauth secrets with a + string of the same length. + - Add CouchDatabase.report_changes() method. + - Don't remove the HTML file that bookmarks point to. + - Improve test coverage. + - Use an explicit test context for testing. + - For startup, loop over finding the port in case the port is not + available when the PID is. + - Ensure that the method that tests for the presence of a record does + not just check if the field is present but ensures that the value + is correct. + - Do not read from the keyring if we have a configuration file that + already has the info we need. + - Move record_id to be a property rather than an attribute. + - Improved pairing tool reliability by fixing race conditions. + - Make the database generate it's own record IDs at "put" time, avoiding + issues with non-idempotency of POST for doc creation. + - Add Record.record_revision attribute to make it easier to get a records + revision ID. + * debian/control + - bumped standards version to 3.8.3 + + -- Elliot Murphy Wed, 03 Feb 2010 14:09:55 -0500 + +desktopcouch (0.5-0ubuntu1) karmic; urgency=low + + * New upstream release. + + Fix remote-db-listing bug for SSL/HTTPS services. (LP: #458540) + + Fix OAuth signatures for SSL urls. + + Avoid couchdb bug where users' PID files are incorrectly interpreted as + valid by couchdb, and so after slightly different boot, desktopcouch can + never start couchdb, causing desktopcouch to quit. (LP: #442120) + + For dbus, do not resolve data for events signaling a service disappears, + and use explicit veriable-passing / references. (LP: #440072) + + Flip push/pull logic in replication information. + + When pulling from remote DB, do not try to access remote management DB. + + Use meaningful names for keyring access. (LP: #451333) + + -- Chad MILLER Thu, 22 Oct 2009 17:15:57 -0400 + +desktopcouch (0.4.4-0ubuntu1) karmic; urgency=low + + * New upstream release. + + Include doc "txt" and translation files in sources. + + couchgrid does not correctly retrieve record id (LP: #447512) + + couchgrid selected_records property is buggy and should be removed for + karmic if possible (LP: #448357) + + -- Chad MILLER Mon, 12 Oct 2009 10:17:50 -0400 + +desktopcouch (0.4.3-0ubuntu1) karmic; urgency=low + + * Include compulsory-auth INI file to be secure by default. + (LP: #438800) + * Make debhelper warn about files not installed to some package. + * Shorten debhelper install paths using dh_install exlusions. + * New upstream release: + + couchgrid did not correctly retrieve record id (LP: #447512) + + HTTP 401 for valid auth information when talking to couchdb over SSL + (LP: #446516) + + Support headless apps. (LP: #428681) + + desktopcouch-service "ValueError: dictionary update sequence..." on + stdout(LP: #446511) + + -- Chad Miller Mon, 12 Oct 2009 07:02:07 -0400 + +desktopcouch (0.4.2-0ubuntu1) karmic; urgency=low + + * Include missing 0.4.0 changelog entry. + * New upstream release. + + Log to correct place in service, ~/.cache/desktop-couch/ . + + Abandon service modules giving host and port info separate from the + db prefixes, which now give a URI. + + Let the replicator gather its own OAuth data to use to connect to far + end. This *should* fix repliction to cloud. + + Make service more resilient to errors, and make breakable parts in + smaller granules. + + Support Python 2.5. + + Added basic notes record API to include NOTE_RECORD_TYPE. + + Make create-contacts script agnostic of desktop vs cloud and oauth + vs noauth. + * Depend on a more recent version of couchdb, which provides OAuth + support. + + -- Chad Miller Mon, 28 Sep 2009 12:06:08 -0400 + +desktopcouch (0.4.1-0ubuntu1) karmic; urgency=low + + * New upstream release (LP: #435429) + * debian/python-desktopcouch-records.install + Added notes, contacts, and replication_services + + -- Ken VanDine Wed, 23 Sep 2009 14:22:38 -0400 + +desktopcouch (0.4-0ubuntu1) karmic; urgency=low + + * Packaging: desktopcouch-tools installed by default in Karmic (LP: #427421) + * Forcing desktopcouch auth on. (LP: #427446) + * Requiring new version of couchdb that supports authentication properly. + * Pairing updates couchdb replication system. (LP: #397663) + * Added pairing of desktop Couches to desktopcouch-tools (LP: #404087) + * Admin users in the system couchdb are no longer inherited by desktopcouch + couchdbs (LP: #424330) + * Fixed failing tests in desktopcouch (LP: #405612) + * Creating login details on initial desktopcouch setup (LP: #416413) + * Starting replication to paired servers on desktopcouch startup. + (LP: #416581) + * Unpaired couchdb peers are reconciled with replication. (LP: #424386) + * At pairing time, changing couchdb pairing address to public. (LP: #419969) + * In replication daemon, verifying local couchdb bind address is not 127/8 . + (LP: #419973) + * getPort no longer suceeds when desktopcouch isn't running. (LP: #422127) + + -- Chad Miller Mon, 14 Sep 2009 19:24:08 -0400 + +desktopcouch (0.3.1-0ubuntu2) karmic; urgency=low + + * Depend on couchdb-bin instead of couchdb (LP: #427036) + + -- Elliot Murphy Wed, 16 Sep 2009 17:32:51 -0400 + +desktopcouch (0.3.1-0ubuntu1) karmic; urgency=low + + [Ken VanDine] + * debian/control + - Added depends on python-desktopcouch-records. (LP: #422179) + [Chad Miller] + * New upstream release. + * Changed Vcs-Bzr links in control file. + * Changed version dependency on couchdb. + * Converting to a full-blown source-package branch. + * Fix getPort failure. (LP: #420911, LP: #422127) + * Check couchdb bind-port in replicator. (LP: #419973) + * Change couchdb bind-port in pairing tool. (LP: #419969) + + -- Chad Miller Tue, 01 Sep 2009 11:57:25 -0400 + +desktopcouch (0.3-0ubuntu1) karmic; urgency=low + + [ Ken VanDine ] + * New upstream release (LP: #416591) + - added unit tests for couchwidget, and then fixed bug #412266 + - Change to freedesktop URL for record type spec. + - First version of the contacts picker, based on CouchWidget + - Adding the desktopcouch.contacts module. + - Use subprocess.Popen and ourselves to the wait()ing, + since subprocess.call() is buggy. There's still an EINTR bug + in subprocess, though. + - Occasionally stop couchdb in tests, so we exercise the automatic + starting code. This will lead to spurious errors because of the + aforementioned subprocess bug, but it's the right thing to do. + - Abstract away some of the linuxisms and complain if we're run on + an unsupported OS. + - Fix a race condition in the process-testing code. + - Replace the TestCase module with one that doesn't complain of dirty + twisted reactors. + - Add a means of stopping the desktop couchdb daemon. + - Add an additional check that a found PID and process named correctly + is indeed a process that this user started, so we don't try to talk + to other local users' desktop couchdbs. + - Get the port at function-call time, instead of storing a port at + start-time and giving that back. The info can be stale, the old + way. + - Don't create a view per record-type; instead, call the standard + return-all-records-keyed-by-record-type and use slice notation on + the viewresults to only get back the records with that type, + which does the same thing but more elegantly. + - Remove the unused/invalid "utils" import from test_server + - Change the name of a function tested to be what actually exists in + the code. + - Refactored server.py by renaming server.get_records_and_type to + server.get_records. Also modified the function to take a record + type param if desired to only create records of that type and + optionally create a view name "get_"+ record_type. (LP: #411475) + * debian/control + - Make python-desktopcouch depend on python-gtk2 and python-gnomekeyring + - Make python-desktopcouch-records depend on python-gtk2, + python-gnomekeyring, and python-oauth. + - Remove depends for python-distutils-extra + - Fixed Vcs-Browser tag + + [Elliot Murphy] + * debian/control: added build-dep on python-setuptools + + + [ Martin Pitt ] + * debian/control: Fix Vcs-* links. + + -- Ken VanDine Thu, 27 Aug 2009 15:32:11 +0200 + +desktopcouch (0.2-0ubuntu1) karmic; urgency=low + + * New upstream release + * Handle the case where the pid file is empty or doesn't exist (LP: #408796) + + -- Ken VanDine Wed, 05 Aug 2009 02:17:47 +0100 + +desktopcouch (0.1-0ubuntu1) karmic; urgency=low + + * Initial release (LP: #397662) + + -- Ken VanDine Fri, 31 Jul 2009 13:44:45 -0400 --- desktopcouch-0.6.2.orig/debian/copyright +++ desktopcouch-0.6.2/debian/copyright @@ -0,0 +1,11 @@ +Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat +Upstream-Name: desktopcouch +Upstream-Maintainer: Stuart Langridge +Upstream-Source: https://launchpad.net/desktopcouch + +Files: * +Copyright: (C) 2009 Canonical Ltd. + +License: LGPL-3 + The full text of the LGPL is distributed in + /usr/share/common-licenses/LGPL-3 on Debian systems. --- desktopcouch-0.6.2.orig/debian/rules +++ desktopcouch-0.6.2/debian/rules @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +DEB_PYTHON_SYSTEM := pycentral +DEB_DH_INSTALL_ARGS := --list-missing --exclude=/tests/ --exclude=egg-info/ + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/python-distutils.mk +include /usr/share/cdbs/1/rules/langpack.mk --- desktopcouch-0.6.2.orig/debian/desktopcouch-tools.install +++ desktopcouch-0.6.2/debian/desktopcouch-tools.install @@ -0,0 +1,3 @@ +debian/tmp/usr/share/applications/desktopcouch-pair.desktop +debian/tmp/usr/bin/desktopcouch-pair +debian/tmp/usr/share/man/man1/desktopcouch-pair.1 --- desktopcouch-0.6.2.orig/debian/desktopcouch.install +++ desktopcouch-0.6.2/debian/desktopcouch.install @@ -0,0 +1,6 @@ +debian/tmp/etc/xdg/desktop-couch/ +debian/tmp/usr/share/desktopcouch/ +debian/tmp/usr/lib/desktopcouch/desktopcouch-service +debian/tmp/usr/lib/desktopcouch/desktopcouch-stop +debian/tmp/usr/lib/desktopcouch/desktopcouch-get-port +debian/tmp/usr/share/dbus-1/services/org.desktopcouch.CouchDB.service --- desktopcouch-0.6.2.orig/debian/python-desktopcouch-records.install +++ desktopcouch-0.6.2/debian/python-desktopcouch-records.install @@ -0,0 +1,5 @@ +debian/tmp/usr/share/doc/python-desktopcouch-records/api/ +debian/tmp/usr/lib/*/*/desktopcouch/records/ +debian/tmp/usr/lib/*/*/desktopcouch/contacts/ +debian/tmp/usr/lib/*/*/desktopcouch/notes/ +debian/tmp/usr/lib/*/*/desktopcouch/replication_services/ --- desktopcouch-0.6.2.orig/debian/pycompat +++ desktopcouch-0.6.2/debian/pycompat @@ -0,0 +1 @@ +2