Files
stacks-puppet-node/blockstack_client/backend/drivers/dropbox.py
Aaron Blankstein 93d07d68b3 This reverts us *all* the way back to 523cf405d7 -- this removes all changes to support the token file from this branch.
Revert "added example request and response for PUT /v1/wallet/keys/owner to api docs"

This reverts commit d52ee4b31e.

Revert "cutting down on the verbosity of logging outputs -- registrar now only prints 1 line on wakeups. storage drivers are concatenated into 1 line"

This reverts commit 87e3e7ab0d.

Revert "adding dropbox as a default storage driver to load, and switched default 'required' drivers to 'disk,dropbox'"

This reverts commit 9471b0a20a.

Revert "adding test case for issue 483, which *also* required fixing the app session schema to handle empty string methods a little bit more gracefully"

This reverts commit 32efc99d62.

Revert "bugfix for the address reencoding in get_zonefile -- checks to see if the address is an address before trying to reencode"

This reverts commit 1488013b93.

Revert "Merge branch 'rc-0.14.3' of github.com:blockstack/blockstack-core into rc-0.14.3"

This reverts commit f75ab67960, reversing
changes made to fe863bcd3c.

Revert "don't create the metadata dir"

This reverts commit fe863bcd3c.

Revert "make all metadata directories inside the critical section"

This reverts commit e66236abd2.

Revert "don't cast 'None' to string by accident"

This reverts commit c6250d5349.

Revert "force string"

This reverts commit e72d43d0be.

Revert "add unbound proxy variable"

This reverts commit 7f1f7e9731.

Revert "return raw zonefile"

This reverts commit 51e858428d.

Revert "force string"

This reverts commit 1ce371644f.

Revert "force string"

This reverts commit 5353cb1015.

Revert "require virtualchain rc-0.14.3 and jsontokens-py 0.0.4"

This reverts commit 346f042db7.

Revert "Merge branch 'rc-0.14.3' of https://github.com/blockstack/blockstack-core into rc-0.14.3"

This reverts commit 1fa1de3e54, reversing
changes made to 523cf405d7.
2017-07-10 14:59:23 -04:00

412 lines
12 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Blockstack-client
~~~~~
copyright: (c) 2014-2015 by Halfmoon Labs, Inc.
copyright: (c) 2016-2017 by Blockstack.org
This file is part of Blockstack-client.
Blockstack-client is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Blockstack-client 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Blockstack-client. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
import requests
import urlparse
import zlib
import logging
import posixpath
import urllib
import hashlib
import threading
from ConfigParser import SafeConfigParser
from common import *
def import_non_local(name, custom_name=None):
import imp, sys
custom_name = custom_name or name
f, pathname, desc = imp.find_module(name, sys.path[1:])
module = imp.load_module(custom_name, f, pathname, desc)
if f:
f.close()
return module
dropbox = import_non_local("dropbox")
log = get_logger("blockstack-storage-drivers-dropbox")
log.setLevel( logging.DEBUG if DEBUG else logging.INFO )
DROPBOX_TOKEN = None
INDEX_DIRNAME = "index"
DVCONF = None
BLOCKSTACK_DEBUG = (os.environ.get("BLOCKSTACK_DEBUG") == "1")
def dropbox_url_reformat(url):
"""
Dropbox URLs end in ?dl=0 sometimes.
Switch this to ?dl=1
"""
if url.endswith("?dl=0"):
url = url[:len(url) - len("?dl=0")] + "?dl=1"
return url
def dropbox_put_chunk( dvconf, chunk_buf, name ):
"""
Driver-level call to put data.
Returns the URL to the data stored on success.
Returns None on error
"""
driver_info = dvconf['driver_info']
dropbox_token = driver_info['dropbox_token']
if dropbox_token is None:
log.warn("No dropbox token set")
return None
dbx = dropbox.Dropbox(dropbox_token)
chunk_buf = str(chunk_buf)
try:
file_info = dbx.files_upload(chunk_buf, name, mode=dropbox.files.WriteMode('overwrite'))
# share it
link_info = dbx.sharing_create_shared_link(name, short_url=False)
url = dropbox_url_reformat(link_info.url)
log.debug("{} available at {}".format(name, url))
return url
except Exception as e:
if DEBUG:
log.exception(e)
log.error("Failed to save {} bytes to {} in Dropbox".format(len(chunk_buf), name))
return None
def dropbox_delete_chunk(dvconf, name):
"""
Delete a chunk from dropbox
Return True on success
Return False on error
"""
driver_info = dvconf['driver_info']
dropbox_token = driver_info['dropbox_token']
if dropbox_token is None:
log.warn("No dropbox token set")
return None
dbx = dropbox.Dropbox(dropbox_token)
try:
dbx.files_delete(name)
return True
except Exception, e:
log.exception(e)
return False
def dropbox_get_chunk(dvconf, name):
"""
Get a chunk via dropbox
Return the data on success
Return None on error
"""
driver_info = dvconf['driver_info']
dropbox_token = driver_info['dropbox_token']
if dropbox_token is None:
log.warn("No dropbox token set")
return None
dbx = dropbox.Dropbox(dropbox_token)
try:
metadata, req = dbx.files_download(name)
if req.status_code != 200:
log.debug("Dropbox files_download {} status code {}".format(name, req.status_code))
return None
return req.text
except Exception, e:
log.error("Failed to load {}".format(name))
return None
def storage_init(conf, index=False, force_index=False):
"""
Initialize dropbox storage driver
"""
global DROPBOX_TOKEN, DVCONF
compress = False
config_path = conf['path']
if os.path.exists( config_path ):
parser = SafeConfigParser()
try:
parser.read(config_path)
except Exception, e:
log.exception(e)
return False
if parser.has_section('dropbox'):
if parser.has_option('dropbox', 'token'):
DROPBOX_TOKEN = parser.get('dropbox', 'token')
if parser.has_option('dropbox', 'compress'):
compress = (parser.get('dropbox', 'compress').lower() in ['1', 'true', 'yes'])
# need the token
if DROPBOX_TOKEN is None:
log.warn("Config file '{}': section 'dropbox' is missing 'token'. Write access will be disabled".format(config_path))
# set up driver
DVCONF = driver_config("dropbox", config_path, dropbox_get_chunk, dropbox_put_chunk, dropbox_delete_chunk, driver_info={'dropbox_token': DROPBOX_TOKEN}, index_stem=INDEX_DIRNAME, compress=compress)
if index:
# instantiate the index
url = index_setup(DVCONF, force=force_index)
if not url:
log.error("Failed to set up index")
return False
return True
def handles_url( url ):
"""
Do we handle this URL?
Must point to a dropbox link
"""
urltype, urlres = get_url_type(url)
if urltype is None and urlres is None:
# can't handle this
return False
urlparts = urlparse.urlparse(url)
return urlparts.netloc.endswith(".dropbox.com")
def make_mutable_url( data_id ):
"""
The URL here is a misnomer, since only Dropbox.com
can create public URLs.
This URL here will instruct get_chunk() to go and search through
the index for the target data.
"""
return index_make_mutable_url('www.dropbox.com', data_id)
def get_immutable_handler( key, **kw ):
"""
Get data by hash
"""
return index_get_immutable_handler(DVCONF, key, **kw)
def get_mutable_handler( url, **kw ):
"""
Get data by URL
"""
return index_get_mutable_handler(DVCONF, url, **kw)
def put_immutable_handler( key, data, txid, **kw ):
"""
Put data by hash and txid
"""
return index_put_immutable_handler(DVCONF, key, data, txid, **kw)
def put_mutable_handler( data_id, data_bin, **kw ):
"""
Put data by file ID
"""
return index_put_mutable_handler(DVCONF, data_id, data_bin, **kw)
def delete_immutable_handler( key, txid, sig_key_txid, **kw ):
"""
Delete by hash
"""
return index_delete_immutable_handler(DVCONF, key, txid, sig_key_txid, **kw)
def delete_mutable_handler( data_id, signature, **kw ):
"""
Delete by data ID
"""
return index_delete_mutable_handler(DVCONF, data_id, signature, **kw)
def get_classes():
return ['read_public', 'write_private']
if __name__ == "__main__":
import keylib
import json
import virtualchain
from virtualchain.lib.hashing import hex_hash160
# hack around absolute paths
current_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, current_dir)
current_dir = os.path.abspath(os.path.join( os.path.dirname(__file__), "..") )
sys.path.insert(0, current_dir)
from blockstack_client.storage import parse_mutable_data, serialize_mutable_data
from blockstack_client.config import log, get_config
CONFIG_PATH = os.environ.get('BLOCKSTACK_CONFIG_PATH', None)
assert CONFIG_PATH, "Missing BLOCKSTACK_CONFIG_PATH from environment"
conf = get_config(CONFIG_PATH)
print json.dumps(conf, indent=4, sort_keys=True)
pk = keylib.ECPrivateKey()
data_privkey = pk.to_hex()
data_pubkey = pk.public_key().to_hex()
test_data = [
["my_first_datum", "hello world", 1, "unused", None],
["/my/second/datum", "hello world 2", 2, "unused", None],
["user\"_profile", '{"name":{"formatted":"judecn"},"v":"2"}', 3, "unused", None],
["empty_string", "", 4, "unused", None],
]
def hash_data( d ):
return hex_hash160( d )
rc = storage_init(conf)
if not rc:
raise Exception("Failed to initialize")
index_manifest_url = index_setup(DVCONF)
assert index_manifest_url
if len(sys.argv) > 1:
# try to get these profiles
for name in sys.argv[1:]:
prof = get_mutable_handler( make_mutable_url( name ), index_manifest_url=index_manifest_url, blockchain_id='test.id' )
if prof is None:
raise Exception("Failed to get %s" % name)
print json.dumps(prof, indent=4, sort_keys=True)
sys.exit(0)
# put_immutable_handler
print "put_immutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
print "store {} ({})".format(d_id, hash_data(d))
rc = put_immutable_handler( hash_data( d ), d, "unused" )
if not rc:
raise Exception("put_immutable_handler('%s') failed" % d)
# put_mutable_handler
print "put_mutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
data_url = make_mutable_url( d_id )
print 'store {} with {}'.format(d_id, data_privkey)
data_json = serialize_mutable_data( json.dumps({"id": d_id, "nonce": n, "data": d}), data_privkey)
rc = put_mutable_handler( d_id, data_json )
if not rc:
raise Exception("put_mutable_handler('%s', '%s') failed" % (d_id, d))
test_data[i][4] = data_url
# get_immutable_handler
print "get_immutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
print "get {}".format(hash_data(d))
rd = get_immutable_handler( hash_data( d ), index_manifest_url=index_manifest_url, fqu='test.id' )
if rd != d:
raise Exception("get_immutable_handler('%s'): '%s' != '%s'" % (hash_data(d), d, rd))
# get_mutable_handler
print "get_mutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
print "get {}".format(d_id)
rd_json = get_mutable_handler( url, index_manifest_url=index_manifest_url, fqu='test.id' )
if rd_json is None:
raise Exception("Failed to get data {}".format(d_id))
rd = parse_mutable_data( rd_json, data_pubkey )
if rd is None:
raise Exception("Failed to parse mutable data '%s'" % rd_json)
rd = json.loads(rd)
if rd['id'] != d_id:
raise Exception("Data ID mismatch: '%s' != '%s'" % (rd['id'], d_id))
if rd['nonce'] != n:
raise Exception("Nonce mismatch: '%s' != '%s'" % (rd['nonce'], n))
if rd['data'] != d:
raise Exception("Data mismatch: '%s' != '%s'" % (rd['data'], d))
# delete_immutable_handler
print "delete_immutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
print "delete {}".format(hash_data(d))
rc = delete_immutable_handler( hash_data(d), "unused", "unused" )
if not rc:
raise Exception("delete_immutable_handler('%s' (%s)) failed" % (hash_data(d), d))
# delete_mutable_handler
print "delete_mutable_handler"
for i in xrange(0, len(test_data)):
d_id, d, n, s, url = test_data[i]
print "delete {}".format(d_id)
rc = delete_mutable_handler( d_id, "unused" )
if not rc:
raise Exception("delete_mutable_handler('%s') failed" % d_id)