Ejemplo n.º 1
0
def share(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Share a file on JottaCloud and get the public URI.',
        epilog='Note: This utility needs to find JOTTACLOUD_USERNAME'
        ' and JOTTACLOUD_PASSWORD in the running environment.')
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument('localfile',
                        help='The local file that you want to share',
                        type=argparse.FileType('r'))
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()
    jottadev = get_jfs_device(jfs)
    jottashare = jottadev.mountPoints['Shared']
    upload = jottashare.up(args.localfile)  # upload file
    public = upload.share()  # share file
    logging.debug('Shared %r and got: %r (%s)', args.localfile, public,
                  dir(public))
    for (filename, uuid, publicURI) in public.sharedFiles():
        print('%s is now available to the world at %s' % (filename, publicURI))
    return True  # TODO: check return value of command
Ejemplo n.º 2
0
def rm(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Delete an item from Jottacloud')
    parser.add_argument('file',
                        type=commandline_text,
                        help='The path to the item that you want to delete')
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument('-f',
                        '--force',
                        help='Completely deleted, no restore possiblity',
                        action='store_true')
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()
    root_dir = get_root_dir(jfs)
    item_path = posixpath.join(root_dir.path, args.file)
    item = jfs.getObject(item_path)
    if args.force:
        item.hard_delete()
    else:
        item.delete()
    print('%s deleted' % args.file)
    return True  # TODO: check return value of command
Ejemplo n.º 3
0
def upload(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Upload a file to JottaCloud.')
    parser.add_argument('localfile',
                        help='The local file that you want to upload',
                        type=argparse.FileType('r'))
    parser.add_argument('remote_dir',
                        help='The remote directory to upload the file to',
                        nargs='?',
                        type=commandline_text)
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    jfs = JFS.JFS()
    args = parse_args_and_apply_logging_level(parser, argv)
    decoded_filename = commandline_text(args.localfile.name)
    progress_bar = ProgressBar()
    callback = lambda monitor, size: progress_bar.show(monitor.bytes_read, size
                                                       )
    root_folder = get_root_dir(jfs)
    if args.remote_dir:
        target_dir_path = posixpath.join(root_folder.path, args.remote_dir)
        target_dir = jfs.getObject(target_dir_path)
    else:
        target_dir = root_folder
    upload = target_dir.up(args.localfile,
                           os.path.basename(decoded_filename),
                           upload_callback=callback)
    print('%s uploaded successfully' % decoded_filename)
    print(upload)
    return True  # TODO: check return value
Ejemplo n.º 4
0
    def test_xml(self):
        xml = b"""<?xml version="1.0" encoding="UTF-8"?>

<device time="2015-09-12-T23:14:25Z" host="dn-093.site-000.jotta.no">
  <name xml:space="preserve">Jotta</name>
  <type>JOTTA</type>
  <sid>ee93a510-907a-4d7c-bbb9-59df7894xxxx</sid>
  <size>58428516774</size>
  <modified>2015-09-12-T23:10:51Z</modified>
  <user>havardgulldahl</user>
  <mountPoints>
    <mountPoint>
      <name xml:space="preserve">Archive</name>
      <size>18577011381</size>
      <modified>2015-09-12-T23:10:51Z</modified>
    </mountPoint>
    <mountPoint>
      <name xml:space="preserve">Photos</name>
      <size>18577011381</size>
      <modified>2015-09-12-T23:10:51Z</modified>
    </mountPoint>
    <mountPoint>
      <name xml:space="preserve">Shared</name>
      <size>43777</size>
      <modified>2015-09-03-T21:12:55Z</modified>
    </mountPoint>
    <mountPoint>
      <name xml:space="preserve">Sync</name>
      <size>39851461616</size>
      <modified>2015-07-26-T22:26:54Z</modified>
    </mountPoint>
  </mountPoints>
  <metadata first="" max="" total="3" num_mountpoints="3"/>
</device>"""
        o = lxml.objectify.fromstring(xml)
        dev = JFS.JFSDevice(o, jfs, parentpath=jfs.rootpath)
        assert isinstance(o, lxml.objectify.ObjectifiedElement)
        # Test that mountpoints are populated correctly"
        assert sorted(dev.mountPoints.keys()) == ['Archive', 'Shared', 'Sync']
        assert all(isinstance(item, JFS.JFSMountPoint) for item in dev.mountPoints.values())
        # test "mountPoint" may be either an actual mountPoint element from JFSDevice.mountPoints{} or its .name. '
        assert all(isinstance(item, JFS.JFSFile) for item in dev.files('Archive'))
        # test "mountPoint" may be either an actual mountPoint element from JFSDevice.mountPoints{} or its .name. '
        mps = dev.mountpointobjects()
        assert all(isinstance(item, JFS.JFSFile) for item in dev.files(mps[2]))

        #test "mountPoint" may be either an actual mountPoint element from JFSDevice.mountPoints{} or its .name. '
        assert all(isinstance(item, JFS.JFSFolder) for item in dev.folders('Archive'))
        #test "mountPoint" may be either an actual mountPoint element from JFSDevice.mountPoints{} or its .name. '
        mps = dev.mountpointobjects()
        assert all(isinstance(item, JFS.JFSFolder) for item in dev.folders(mps[0]))

        # test_properties
        assert isinstance(dev.modified, datetime.datetime)
        assert dev.path == '%s/%s' % (jfs.rootpath, 'Jotta')
        assert dev.name == 'Jotta'
        assert dev.type == 'JOTTA'
        assert dev.size == 58428516774
        assert dev.sid == 'ee93a510-907a-4d7c-bbb9-59df7894xxxx'
Ejemplo n.º 5
0
def fuse(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    if not HAS_FUSE:
        message = [
            'jotta-fuse requires fusepy (pip install fusepy), install that and try again.'
        ]
        if os.name == 'nt':
            message.append(
                'Note: jotta-fuse is not supported on Windows, but Cygwin might work.'
            )
        print(' '.join(message))
        sys.exit(1)

    from .jottafuse import JottaFuse
    parser = argparse.ArgumentParser(
        description=__doc__,
        epilog=
        """The program expects to find an entry for "jottacloud.com" in your .netrc,
                                     or JOTTACLOUD_USERNAME and JOTTACLOUD_PASSWORD in the running environment.
                                     This is not an official JottaCloud project."""
    )
    parser.add_argument(
        '--debug',
        action='store_true',
        help=
        'Run fuse in the foreground and add a lot of messages to help debug')
    parser.add_argument('--debug-fuse',
                        action='store_true',
                        help='Show all low-level filesystem operations')
    parser.add_argument('--debug-http',
                        action='store_true',
                        help='Show all HTTP traffic')
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument(
        'mountpoint',
        type=is_dir,
        help=
        'A path to an existing directory where you want your JottaCloud tree mounted'
    )
    args = parser.parse_args(argv)
    if args.debug_http:
        http_client.HTTPConnection.debuglevel = 1
    if args.debug:
        requests_log = logging.getLogger("requests.packages.urllib3")
        requests_log.setLevel(logging.DEBUG)
        requests_log.propagate = True
        logging.basicConfig(level=logging.DEBUG)

    auth = JFS.get_auth_info()
    fuse = FUSE(JottaFuse(auth),
                args.mountpoint,
                debug=args.debug_fuse,
                sync_read=True,
                foreground=args.debug,
                raw_fi=False,
                fsname="JottaCloudFS",
                subtype="fuse")
Ejemplo n.º 6
0
 def test_errors(self):
     with pytest.raises(JFS.JFSCredentialsError): # HTTP 401
         JFS.JFS(auth=('PYTEST','PYTEST'))
     with pytest.raises(JFS.JFSNotFoundError): # HTTP 404
         jfs.get('/Jotta/Archive/FileNot.found')
     with pytest.raises(JFS.JFSRangeError): # HTTP 416
         p = "//Jotta/Archive/testfile_up_and_readpartial.txt"
         t = jfs.up(p, six.BytesIO(TESTFILEDATA))
         f = jfs.getObject(p)
         f.readpartial(10, 3) # Range start index larger than end index;
         f.delete()
Ejemplo n.º 7
0
def monitor(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    if not HAS_WATCHDOG:
        message = [
            'jotta-monitor requires watchdog (pip install watchdog), install that and try again.'
        ]
        print(' '.join(message))
        sys.exit(1)

    # Has watchdog, can safely import filemonitor
    from .monitor import filemonitor

    parser = argparse.ArgumentParser(
        description=__doc__,
        epilog=
        """The program expects to find an entry for "jottacloud.com" in your .netrc,
                                    or JOTTACLOUD_USERNAME and JOTTACLOUD_PASSWORD in the running environment.
                                    This is not an official JottaCloud project."""
    )
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument('--errorfile',
                        help='A file to write errors to',
                        default='./jottacloudclient.log')
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument(
        '--dry-run',
        action='store_true',
        help=
        "don't actually do any uploads or deletes, just show what would be done"
    )
    parser.add_argument('topdir',
                        type=is_dir,
                        help='Path to local dir that needs syncing')
    parser.add_argument(
        'mode',
        type=commandline_text,
        help='Mode of operation: ARCHIVE, SYNC or SHARE. See README.md',
        choices=('archive', 'sync', 'share'))
    args = parse_args_and_apply_logging_level(parser, argv)
    fh = logging.FileHandler(args.errorfile)
    fh.setLevel(logging.ERROR)
    logging.getLogger('').addHandler(fh)

    jfs = JFS.JFS()

    filemonitor(args.topdir, args.mode, jfs)
Ejemplo n.º 8
0
def cat(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Display contents of a file from Jottacloud')
    parser.add_argument('file',
                        type=commandline_text,
                        help='The path to the file that you want to show')
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()
    if args.file.startswith('//'):
        # break out of root_folder

        mountpoint = str.join(str('/'), args.file.split("/")[3:4])
        path = str.join(str('/'), args.file.split("/")[4:])
        logging.debug('mountpoint = ' + mountpoint)
        logging.debug('path = ' + path)

        root_folder = get_root_dir(jfs, mountpoint=mountpoint)
        item_path = posixpath.join(root_folder.path, path)


#        item_path = posixpath.join(jfs.rootpath, args.file[1:])
    else:
        root_dir = get_root_dir(jfs)
        item_path = posixpath.join(root_dir.path, args.file)
    item = jfs.getObject(item_path)
    if not isinstance(item, JFS.JFSFile):
        print("%r is not a file (it's a %s), so we can't show it" %
              (args.file, type(item)))
        sys.exit(1)
    s = ''
    for chunk in item.stream():
        if isinstance(chunk, six.text_type):
            print(chunk.encode(sys.getdefaultencoding()))
            s = s + chunk
        else:
            print(chunk)
            s = s + chunk.decode(sys.getdefaultencoding())
    return s
Ejemplo n.º 9
0
def mkdir(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Create a new folder in Jottacloud.')
    parser.add_argument('newdir',
                        type=commandline_text,
                        help='The path to the folder that you want to create')
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()
    root_folder = get_root_dir(jfs)
    root_folder.mkdir(args.newdir)
    return True  #  TODO: check return value of mkdir
Ejemplo n.º 10
0
 def download_jfsfile(remote_object, tofolder=None, checksum=False):
     'Helper function to get a jfsfile and store it in a local folder, optionally checksumming it. Returns boolean'
     if tofolder is None:
         tofolder = '.'  # with no arguments, store in current dir
     total_size = remote_object.size
     if remote_object.state in (JFS.ProtoFile.STATE_CORRUPT,
                                JFS.ProtoFile.STATE_INCOMPLETE):
         puts(
             colored.red(
                 '%s was NOT downloaded successfully - Incomplete file' %
                 remote_file.name))
         return False
     topath = os.path.join(tofolder, remote_object.name)
     with open(topath, 'wb') as fh:
         bytes_read = 0
         puts(
             colored.white('Downloading: %s, size: %s \t' %
                           (remote_object.name,
                            print_size(total_size, humanize=True))))
         with ProgressBar(expected_size=total_size) as bar:
             for chunk_num, chunk in enumerate(remote_object.stream()):
                 fh.write(chunk)
                 bytes_read += len(chunk)
                 bar.show(bytes_read)
     if checksum:
         md5_lf = JFS.calculate_md5(open(topath, 'rb'))
         md5_jf = remote_object.md5
         logging.info('%s - Checksum for downloaded file' % md5_lf)
         logging.info('%s - Checksum for server file' % md5_jf)
         if md5_lf != md5_jf:
             puts(colored.blue('%s - Checksum for downloaded file' %
                               md5_lf))
             puts(colored.blue('%s - Checksum for server file' % md5_jf))
             puts(
                 colored.red(
                     '%s was NOT downloaded successfully - cheksum mismatch'
                     % remote_object.name))
             return False
         puts(
             colored.green(
                 '%s was downloaded successfully - checksum  matched' %
                 remote_object.name))
     return True
Ejemplo n.º 11
0
    def __init__(self, parsed_url):
        duplicity.backend.Backend.__init__(self, parsed_url)

        # Import JottaCloud libraries.
        try:
            from jottalib_ng import JFS
        except ImportError:
            raise BackendException(
                'JottaCloud backend requires jottalib_ng'
                ' (see https://pypi.python.org/pypi/jottalib_ng).')

        # Set jottalib_ng loggers to the same verbosity as duplicity
        duplicity_log_level = get_duplicity_log_level()
        set_jottalib_ng_logging_level(duplicity_log_level)

        # Ensure jottalib_ng and duplicity log to the same handlers
        set_jottalib_ng_log_handlers(log._logger.handlers)

        # Will fetch jottacloud auth from environment or .netrc
        self.client = JFS.JFS()

        self.folder = self.get_or_create_directory(parsed_url.path.lstrip('/'))
        log.Debug("Jottacloud folder for duplicity: %r" % self.folder.path)
Ejemplo n.º 12
0
def scanner(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    parser = argparse.ArgumentParser(
        description=__doc__,
        epilog=
        """The program expects to find an entry for "jottacloud.com" in your .netrc,
                                    or JOTTACLOUD_USERNAME and JOTTACLOUD_PASSWORD in the running environment.
                                    This is not an official JottaCloud project."""
    )
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument('--errorfile',
                        type=commandline_text,
                        help='A file to write errors to',
                        default='./jottacloudclient.log')
    parser.add_argument(
        '--exclude',
        type=re.compile,
        action='append',
        help='Exclude paths matched by this pattern (can be repeated)')
    parser.add_argument('--prune-files',
                        dest='prune_files',
                        help='Delete files that does not exist locally',
                        action='store_true')
    parser.add_argument('--prune-folders',
                        dest='prune_folders',
                        help='Delete folders that does not exist locally',
                        action='store_true')
    parser.add_argument('--prune-all',
                        dest='prune_all',
                        help='Combines --prune-files  and --prune-folders',
                        action='store_true')
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument(
        '--dry-run',
        action='store_true',
        help=
        "don't actually do any uploads or deletes, just show what would be done"
    )
    parser.add_argument('topdir',
                        type=is_dir,
                        help='Path to local dir that needs syncing')
    parser.add_argument(
        'jottapath',
        type=commandline_text,
        help=
        'The path at JottaCloud where the tree shall be synced (must exist)')
    args = parse_args_and_apply_logging_level(parser, argv)
    if args.prune_all:
        args.prune_files = True
        args.prune_folders = True

    fh = logging.FileHandler(args.errorfile)
    fh.setLevel(logging.ERROR)
    logging.getLogger('').addHandler(fh)

    jfs = JFS.JFS()

    logging.info('args: topdir %r, jottapath %r', args.topdir, args.jottapath)
    filescanner(args.topdir, args.jottapath, jfs, args.errorfile, args.exclude,
                args.dry_run, args.prune_files, args.prune_folders)
Ejemplo n.º 13
0
import six
from six import StringIO


# import dependencies
import lxml
import dateutil
import requests

# import py.test
import pytest # pip install pytest

# import jotta
from jottalib_ng import JFS, __version__

jfs = JFS.JFS() # get username and password from environment or .netrc


TESTFILEDATA=b"""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
Nulla est dolor, convallis fermentum sapien in, fringilla congue ligula. 
Fusce at justo ac felis vulputate laoreet vel at metus. 
Aenean justo lacus, porttitor dignissim imperdiet a, elementum cursus ligula. 
Vivamus eu est viverra, pretium arcu eget, imperdiet eros. 
Curabitur in bibendum."""

TESTFILETEXT=u"""
Lørem ipsum dolor sit amet, consectetur adipiscing elit. 
Nulla est dolor, convallis fermentum sapien in, fringilla congue ligula. 
Fusce at justo ac felis vulputate laoreet vel at metus. 
Aenean justo lacus, porttitor dignissim imperdiet a, elementum cursus ligula. 
Ejemplo n.º 14
0
def ls(argv=None):
    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(description='List files in Jotta folder.',
                                     add_help=False)
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument(
        '-h',
        '--humanize',  # this matches ls(1)
        help='Print human-readable file sizes.',
        action='store_true')
    parser.add_argument(
        '-a',
        '--all',
        action='store_true',
        help='Include deleted and incomplete files (otherwise ignored)')
    parser.add_argument(
        'item',
        nargs='?',
        help='The file or directory to list. Defaults to the root dir',
        type=commandline_text)
    parser.add_argument(
        '-H',  # because -h means --humanize
        '--help',
        help='Print this help',
        action='help')
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()
    root_folder = get_root_dir(jfs)
    if args.item:
        if args.item.startswith('//'):
            # break out of root_folder
            mountpoint = str.join(str('/'), args.item.split("/")[3:4])
            path = str.join(str('/'), args.item.split("/")[4:])

            logging.debug('len: ' + str(len(args.item.split("/"))))

            if len(args.item.split("/")) == 4:
                path += u'/'

            logging.debug('mountpoint = ' + mountpoint)
            logging.debug('path = ' + path)

            root_folder = get_root_dir(jfs, mountpoint=mountpoint)
            item_path = posixpath.join(root_folder.path, path)
        else:
            item_path = posixpath.join(root_folder.path, args.item)

        logging.debug('item_path = ' + item_path)
        item = jfs.getObject(item_path)
    else:
        item = root_folder

    timestamp_width = 25
    logging.debug('about to ls %r', item)

    logging.debug('item.instance: ' + str(item))

    if isinstance(item, JFS.JFSFolder) or isinstance(item, JFS.JFSMountPoint):
        logging.debug('JFSMountPoint')

        logging.debug('item.files: ' + str(item.files()))

        files = []
        for f in item.files():
            logging.debug('f: ' + str(f))
            if not f is None:
                files.append([
                    f.created,
                    print_size(f.size, humanize=args.humanize)
                    if f.size else u'', u'D' if f.deleted else
                    u'I' if f.state == 'INCOMPLETE' else u' ', f.name
                ])
#        files = [(
#            f.created,
#            print_size(f.size, humanize=args.humanize) if f.size else u'',
#            u'D' if f.deleted else u'I' if f.state == 'INCOMPLETE' else u' ',
#            f.name) for f in item.files() if not f is None or (f.deleted and f.state != 'INCOMPLETE' or args.all)]

        folders = [(u' ' * timestamp_width, u'', u'D' if f.deleted else u' ',
                    unicode(f.name)) for f in item.folders()
                   if not f is None or (not f.deleted or args.all)]
        widest_size = 0
        for f in files:
            logging.debug('f in files: ' + str(f))
            if len(f[1]) > widest_size:
                widest_size = len(f[1])
        for item in sorted(files + folders, key=lambda t: t[3]):
            if args.all:
                print(u'%s %s %s %s' %
                      (item[0], item[1].rjust(widest_size), item[2], item[3]))
            else:
                print(u'%s %s %s' %
                      (item[0], item[1].rjust(widest_size), item[3]))


#    elif isinstance(item, JFS.JFSFolder):
#        files = [(
#            f.created,
#            print_size(f.size, humanize=args.humanize) if f.size else u'',
#            u'D' if f.deleted else u'I' if f.state == 'INCOMPLETE' else u' ',
#            f.name) for f in item.files() if not f.deleted and f.state != 'INCOMPLETE' or args.all]
#
#
#        folders = [(u' '*timestamp_width, u'', u'D' if f.deleted else u' ', unicode(f.name))
#                   for f in item.folders() if not f.deleted or args.all]
#        widest_size = 0
#        for f in files:
#            if len(f[1]) > widest_size:
#                widest_size = len(f[1])
#        for item in sorted(files + folders, key=lambda t: t[3]):
#            if args.all:
#                print(u'%s %s %s %s' % (item[0], item[1].rjust(widest_size), item[2], item[3]))
#            else:
#                print(u'%s %s %s' % (item[0], item[1].rjust(widest_size), item[3]))
    else:
        print(' '.join([
            str(item.created),
            print_size(item.size, humanize=args.humanize), item.name
        ]))
    return True  # TODO: check return value of command
Ejemplo n.º 15
0
def download(argv=None):
    def download_jfsfile(remote_object, tofolder=None, checksum=False):
        'Helper function to get a jfsfile and store it in a local folder, optionally checksumming it. Returns boolean'
        if tofolder is None:
            tofolder = '.'  # with no arguments, store in current dir
        total_size = remote_object.size
        if remote_object.state in (JFS.ProtoFile.STATE_CORRUPT,
                                   JFS.ProtoFile.STATE_INCOMPLETE):
            puts(
                colored.red(
                    '%s was NOT downloaded successfully - Incomplete file' %
                    remote_file.name))
            return False
        topath = os.path.join(tofolder, remote_object.name)
        with open(topath, 'wb') as fh:
            bytes_read = 0
            puts(
                colored.white('Downloading: %s, size: %s \t' %
                              (remote_object.name,
                               print_size(total_size, humanize=True))))
            with ProgressBar(expected_size=total_size) as bar:
                for chunk_num, chunk in enumerate(remote_object.stream()):
                    fh.write(chunk)
                    bytes_read += len(chunk)
                    bar.show(bytes_read)
        if checksum:
            md5_lf = JFS.calculate_md5(open(topath, 'rb'))
            md5_jf = remote_object.md5
            logging.info('%s - Checksum for downloaded file' % md5_lf)
            logging.info('%s - Checksum for server file' % md5_jf)
            if md5_lf != md5_jf:
                puts(colored.blue('%s - Checksum for downloaded file' %
                                  md5_lf))
                puts(colored.blue('%s - Checksum for server file' % md5_jf))
                puts(
                    colored.red(
                        '%s was NOT downloaded successfully - cheksum mismatch'
                        % remote_object.name))
                return False
            puts(
                colored.green(
                    '%s was downloaded successfully - checksum  matched' %
                    remote_object.name))
        return True

    if argv is None:
        argv = sys.argv[1:]
    parser = argparse.ArgumentParser(
        description='Download a file or folder from Jottacloud.')
    parser.add_argument(
        'remoteobject',
        help='The path to the file or folder that you want to download',
        type=commandline_text)
    parser.add_argument('-l',
                        '--loglevel',
                        help='Logging level. Default: %(default)s.',
                        choices=('debug', 'info', 'warning', 'error'),
                        default='warning')
    parser.add_argument('-c',
                        '--checksum',
                        help='Verify checksum of file after download',
                        action='store_true')
    #parser.add_argument('-r', '--resume',
    #                    help='Will not download the files again if it exist in path',
    #                    action='store_true' )
    args = parse_args_and_apply_logging_level(parser, argv)
    jfs = JFS.JFS()

    if args.remoteobject.startswith('//'):
        # break out of root_folder
        root_folder = jfs.rootpath
        item_path = posixpath.join(root_folder, args.remoteobject[2:])
    else:
        root_folder = get_root_dir(jfs).path
        item_path = posixpath.join(root_folder, args.remoteobject)

    logging.info('Root folder path: %s' % root_folder)
    logging.info('Command line path to object: %s' % args.remoteobject)
    logging.info('Jotta path to object: %s' % item_path)
    remote_object = jfs.getObject(item_path)
    if isinstance(remote_object, JFS.JFSFile):
        if download_jfsfile(remote_object, checksum=args.checksum):
            logging.info('%r downloaded successfully', remote_object.path)
            return True
        else:
            puts(colored.red('%r download failed' % remote_object.path))
            return False

    else:  #if it's not a file it has to be a folder
        incomplete_files = [
        ]  #Create an list where we can store incomplete files
        checksum_error_files = [
        ]  #Create an list where we can store checksum error files
        zero_files = []  #Create an list where we can store zero files
        long_path = [
        ]  #Create an list where we can store skipped files and folders because of long path
        puts(colored.blue("Getting index for folder: %s" % remote_object.name))
        fileTree = remote_object.filedirlist().tree  #Download the folder tree
        puts(
            colored.blue('Total number of folders to download: %d' %
                         len(fileTree)))
        topdir = os.path.dirname(item_path)
        logging.info("topdir: %r", topdir)

        #Iterate through each folder
        for folder in fileTree:
            #We need to strip the path to the folder path from account,device and mountpoint details
            logging.debug("folder: %r", folder)

            _abs_folder_path = posixpath.join(JFS.JFS_ROOT, folder[1:])
            logging.debug("absolute folder path  : %r", _abs_folder_path)
            _rel_folder_path = str.join(str('/'), folder.split('/')[4:])
            logging.info('relative folder path: %r', _rel_folder_path)

            if len(_rel_folder_path
                   ) > 250:  #Windows has a limit of 250 characters in path
                puts(
                    colored.red(
                        '%s was NOT downloaded successfully - path too long' %
                        _rel_folder_path))
                long_path.append(_rel_folder_path)
            else:
                logging.info('Entering a new folder: %s' % _rel_folder_path)
                if not os.path.exists(
                        _rel_folder_path
                ):  #Create the folder locally if it doesn't exist
                    os.makedirs(_rel_folder_path)
                for _file in fileTree[
                        folder]:  #Enter the folder and download the files within
                    logging.info("file: %r", _file)
                    #This is the absolute path to the file that is going to be downloaded
                    abs_path_to_object = posixpath.join(
                        topdir, _rel_folder_path, _file.name)
                    logging.info('Downloading the file from: %s' %
                                 abs_path_to_object)
                    if _file.state in (JFS.ProtoFile.STATE_CORRUPT,
                                       JFS.ProtoFile.STATE_INCOMPLETE):
                        #Corrupt and incomplete files will be skipped
                        puts(
                            colored.red(
                                '%s was NOT downloaded successfully - Incomplete or corrupt file'
                                % _file.name))
                        incomplete_files.append(
                            posixpath.join(_rel_folder_path, _file.name))
                        continue
                    remote_object = jfs.getObject(abs_path_to_object)
                    remote_file = remote_object
                    total_size = remote_file.size
                    if total_size == 0:  # Indicates an zero file
                        puts(
                            colored.red(
                                '%s was NOT downloaded successfully - zero file'
                                % remote_file.name))
                        zero_files.append(
                            posixpath.join(_rel_folder_path, remote_file.name))
                        continue
                    if len(
                            posixpath.join(_rel_folder_path, remote_file.name)
                    ) > 250:  #Windows has a limit of 250 characters in path
                        puts(
                            colored.red(
                                '%s was NOT downloaded successfully - path too long'
                                % remote_file.name))
                        long_path.append(
                            posixpath.join(_rel_folder_path, remote_file.name))
                        continue
                    #TODO: implement args.resume:
                    if not download_jfsfile(remote_file,
                                            tofolder=_rel_folder_path,
                                            checksum=args.checksum):
                        # download failed
                        puts(
                            colored.red("Download failed: %r" %
                                        remote_file.path))
        #Incomplete files
        if len(incomplete_files) > 0:
            with codecs.open("incomplete_files.txt", "w",
                             "utf-8") as text_file:
                for item in incomplete_files:
                    text_file.write("%s\n" % item)
        print('Incomplete files (not downloaded): %d' % len(incomplete_files))
        for _files in incomplete_files:
            logging.info("Incomplete: %r", _files)

        #Checksum error files
        if len(checksum_error_files) > 0:
            with codecs.open("checksum_error_files.txt", "w",
                             "utf-8") as text_file:
                for item in checksum_error_files:
                    text_file.write("%s\n" % item)
        print('Files with checksum error (not downloaded): %d' %
              len(checksum_error_files))
        for _files in checksum_error_files:
            logging.info("Checksum error: %r", _files)

        #zero files
        if len(zero_files) > 0:
            with codecs.open("zero_files.txt", "w", "utf-8") as text_file:
                for item in zero_files:
                    text_file.write("%s\n" % item)
        print('Files with zero size (not downloaded): %d' % len(zero_files))
        for _files in zero_files:
            logging.info("Zero sized files: %r", _files)

        #long path
        if len(long_path) > 0:
            with codecs.open("long_path.txt", "w", "utf-8") as text_file:
                for item in long_path:
                    text_file.write("%s\n" % item)
        print('Folder and files not downloaded because of path too long: %d' %
              len(long_path))
        for _files in long_path:
            logging.info("Path too long: %r", _files)
        return True
Ejemplo n.º 16
0
    def test_xml(self):
        xml = b"""<?xml version="1.0" encoding="UTF-8"?>

<mountPoint time="2015-09-13-T00:16:31Z" host="dn-097.site-000.jotta.no">
  <name xml:space="preserve">Sync</name>
  <path xml:space="preserve">/havardgulldahl//Jotta</path>
  <abspath xml:space="preserve">/havardgulldahl//Jotta</abspath>
  <size>39851461616</size>
  <modified>2015-07-26-T22:26:54Z</modified>
  <device>Jotta</device>
  <user>havardgulldahl</user>
  <folders>
    <folder name="folder1"/>
    <folder name="folder2"/>
    <folder name="folder3"/>
    <folder name="folder4"/>
    <folder name="folder5"/>
    <folder name="folder6"/>
    <folder name="folder7"/>
  </folders>
  <files>
    <file name="bigfile" uuid="7a36a217-88d5-4804-99df-bbc42eb4a9f2">
      <latestRevision>
        <number>1</number>
        <state>INCOMPLETE</state>
        <created>2015-05-29-T09:02:53Z</created>
        <modified>2015-05-29-T09:02:53Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <md5>4d710d3a12699730976216836a5217a8</md5>
        <updated>2015-05-29-T09:02:53Z</updated>
      </latestRevision>
    </file>
    <file name="test.pdf" uuid="1caec763-2ed0-4e88-9d3c-650f3babecc4">
      <currentRevision>
        <number>3</number>
        <state>COMPLETED</state>
        <created>2015-07-26-T22:26:54Z</created>
        <modified>2015-07-26-T22:26:54Z</modified>
        <mime>application/pdf</mime>
        <mstyle>APPLICATION_PDF</mstyle>
        <size>116153</size>
        <md5>138396327a51ea6bf20caa72cf6d6667</md5>
        <updated>2015-07-26-T22:26:54Z</updated>
      </currentRevision>
    </file>
  </files>
  <metadata first="" max="" total="9" num_folders="7" num_files="2"/>
</mountPoint>"""
        o = lxml.objectify.fromstring(xml)
        dev = JFS.JFSMountPoint(o, jfs, parentpath=jfs.rootpath + '/Jotta')

        #test native properties
        assert dev.name == 'Sync'
        assert dev.size == 39851461616
        assert isinstance(dev.modified, datetime.datetime)
        assert dev.modified == datetime.datetime(2015, 7, 26, 22, 26, 54).replace(tzinfo=dateutil.tz.tzutc())

        with pytest.raises(JFS.JFSError):
            dev.delete()
            dev.rename('sillywalkministry')

        # test JFSFolder inheritance
        assert dev.path == jfs.rootpath + '/Jotta/Sync'
        assert dev.deleted == None
        assert dev.is_deleted() == False
        assert all(isinstance(item, (JFS.JFSFile, JFS.JFSIncompleteFile)) for item in dev.files())
        assert all(isinstance(item, JFS.JFSFolder) for item in dev.folders())
        newf = dev.mkdir('testdir')
        assert isinstance(newf, JFS.JFSFolder)
        newf.delete()

        _f = tempfile.NamedTemporaryFile()
        _f.write(b'123test')

        newfile = dev.up(_f)
        assert isinstance(newfile, JFS.JFSFile)
        newfile.delete()
        newfile = dev.up(_f, filename='heyhei123.txt')
        assert isinstance(newfile, JFS.JFSFile)
        assert newfile.name == 'heyhei123.txt'
        newfile.delete()
        assert isinstance(dev.filedirlist(), JFS.JFSFileDirList)
Ejemplo n.º 17
0
 def __init__(self, auth, path='.'):
     self.client = JFS.JFS(auth)
     self.__newfiles = {}  # a dict of stringio objects
     self.__newfolders = []
     self.ino = 0
Ejemplo n.º 18
0
    def test_xml(self):
        xml = b"""<?xml version="1.0" encoding="UTF-8"?>

<file name="testimage.jpg" uuid="9ebcfe1a-98b1-4e38-a73e-f498555da865" time="2015-09-13-T21:22:46Z" host="dn-094.site-000.jotta.no">
  <path xml:space="preserve">/havardgulldahl/Jotta/Archive</path>
  <abspath xml:space="preserve">/havardgulldahl/Jotta/Archive</abspath>
  <currentRevision>
    <number>5</number>
    <state>COMPLETED</state>
    <created>2015-07-25-T21:18:49Z</created>
    <modified>2015-07-25-T21:18:49Z</modified>
    <mime>image/jpeg</mime>
    <mstyle>IMAGE_JPEG</mstyle>
    <size>1816221</size>
    <md5>125073533339a616b99bc53efc509561</md5>
    <updated>2015-07-25-T21:18:50Z</updated>
  </currentRevision>
  <revisions>
    <revision>
      <number>4</number>
      <state>COMPLETED</state>
      <created>2015-07-25-T21:18:05Z</created>
      <modified>2015-07-25-T21:18:05Z</modified>
      <mime>image/jpeg</mime>
      <mstyle>IMAGE_JPEG</mstyle>
      <size>1816221</size>
      <md5>125073533339a616b99bc53efc509561</md5>
      <updated>2015-07-25-T21:18:07Z</updated>
    </revision>
    <revision>
      <number>3</number>
      <state>COMPLETED</state>
      <created>2015-07-25-T21:17:49Z</created>
      <modified>2015-07-25-T21:17:49Z</modified>
      <mime>image/jpeg</mime>
      <mstyle>IMAGE_JPEG</mstyle>
      <size>1816221</size>
      <md5>125073533339a616b99bc53efc509561</md5>
      <updated>2015-07-25-T21:17:50Z</updated>
    </revision>
    <revision>
      <number>2</number>
      <state>COMPLETED</state>
      <created>2015-07-25-T21:01:45Z</created>
      <modified>2015-07-25-T21:01:45Z</modified>
      <mime>image/jpeg</mime>
      <mstyle>IMAGE_JPEG</mstyle>
      <size>1816221</size>
      <md5>125073533339a616b99bc53efc509561</md5>
      <updated>2015-07-25-T21:01:46Z</updated>
    </revision>
    <revision>
      <number>1</number>
      <state>COMPLETED</state>
      <created>2015-07-25-T21:00:02Z</created>
      <modified>2015-07-25-T21:00:02Z</modified>
      <mime>image/jpeg</mime>
      <mstyle>IMAGE_JPEG</mstyle>
      <size>1816221</size>
      <md5>125073533339a616b99bc53efc509561</md5>
      <updated>2015-07-25-T21:00:03Z</updated>
    </revision>
  </revisions>
</file>"""

        o = lxml.objectify.fromstring(xml)
        dev = JFS.JFSFile(o, jfs, parentpath=jfs.rootpath + '/Jotta/Archive')

        #test ProtoFile properties
        assert dev.path == jfs.rootpath + '/Jotta/Archive/testimage.jpg'
        assert dev.name == 'testimage.jpg'
        assert dev.uuid == '9ebcfe1a-98b1-4e38-a73e-f498555da865'
        assert dev.deleted == None
        assert dev.is_deleted() == False

        #test native properties
        assert dev.revisionNumber == 5
        assert dev.created == datetime.datetime(2015, 7, 25, 21, 18, 49).replace(tzinfo=dateutil.tz.tzutc())
        assert dev.modified == datetime.datetime(2015, 7, 25, 21, 18, 49).replace(tzinfo=dateutil.tz.tzutc())
        assert dev.updated == datetime.datetime(2015, 7, 25, 21, 18, 50).replace(tzinfo=dateutil.tz.tzutc())
        assert dev.size == 1816221
        assert dev.md5 == '125073533339a616b99bc53efc509561'
        assert dev.mime == 'image/jpeg'
        assert dev.state == 'COMPLETED'
Ejemplo n.º 19
0
    units = ['B','KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']
    p = math.floor(math.log(size, 2)/10)
    return "%.3f%s" % (size/math.pow(1024,p),units[int(p)])

if __name__ == '__main__':
    # we need an active login to test
    import netrc
    try:
        n = netrc.netrc()
        username, account, password = n.authenticators('jottacloud.com') # read .netrc entry for 'machine jottacloud'
    except Exception as e:
        log.exception(e)
        username = os.environ['JOTTACLOUD_USERNAME']
        password = os.environ['JOTTACLOUD_PASSWORD']

    jfs = JFS.JFS(auth=(username, password))
    lite = LiteJFS(username, password)

    filesize = 1024*10*10

    data = os.urandom(filesize)
    testfile = tempfile.NamedTemporaryFile()
    puts(colored.blue('Creating test file.'))
    for i in progress.bar(range(0, 1000)):
        testfile.write(data)
    filesize = os.path.getsize(testfile.name)

    p = '/Jotta/Archive/test/%s.data' % os.path.basename(testfile.name)

    # UPLOAD TEST
    puts(colored.green('Test 1 | requests | Upload | File size: %s' % humanizeFileSize(filesize)))
Ejemplo n.º 20
0
    def test_xml(self):
        xml = b"""<?xml version="1.0" encoding="UTF-8"?>

<folder name="test2" time="2015-09-28-T13:49:05Z" host="dn-091.site-000.jotta.no">
  <path xml:space="preserve">/havardgulldahl/Jotta/Archive</path>
  <abspath xml:space="preserve">/havardgulldahl/Jotta/Archive</abspath>
  <folders>
    <folder name="Documents"/>
  </folders>
  <files>
    <file name="boink.txt~" uuid="c6684726-a842-4536-95b9-140584515dd1">
      <currentRevision>
        <number>1</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>40</size>
        <md5>b924ebbc79ad414ded4af442ac7080d3</md5>
        <updated>2014-11-23-T21:12:47Z</updated>
      </currentRevision>
    </file>
    <file name="boink.txx~" uuid="1b243d8e-d6df-412c-a2ce-926ebaa73f47">
      <currentRevision>
        <number>1</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>40</size>
        <md5>0c7652132733ead903dbdb942577fed7</md5>
        <updated>2014-11-23-T21:27:21Z</updated>
      </currentRevision>
    </file>
    <file name="boink.txy~" uuid="ba1ec941-8901-4eec-b797-bde9b6b1958c">
      <currentRevision>
        <number>1</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>40</size>
        <md5>d32f37bf39041d9bb92ad45012e32cb9</md5>
        <updated>2014-11-23-T21:05:11Z</updated>
      </currentRevision>
    </file>
    <file name="boink.txz~" uuid="6724af96-e462-4862-b82a-00b7836a0681">
      <currentRevision>
        <number>1</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>9</size>
        <md5>e3a2cba90ec7630bdf1d0566c8abb93e</md5>
        <updated>2014-11-23-T21:03:25Z</updated>
      </currentRevision>
    </file>
    <file name="dingdong" uuid="95c4bbcc-9a59-4669-b4cb-ee49c186ae1b">
      <currentRevision>
        <number>127</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>20480</size>
        <md5>daa100df6e6711906b61c9ab5aa16032</md5>
        <updated>2014-11-23-T22:56:30Z</updated>
      </currentRevision>
    </file>
    <file name="fisk.txt" uuid="8b6db048-c44b-4656-8024-737a0c38c7ad">
      <currentRevision>
        <number>1</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>text/plain</mime>
        <mstyle>TEXT_PLAIN</mstyle>
        <size>18</size>
        <md5>308285a59ae0a4a5ede4f7a0c08d2390</md5>
        <updated>2014-11-23-T19:56:51Z</updated>
      </currentRevision>
    </file>
    <file name="nyboink.txt" uuid="3d0a0a28-4c68-4e6a-8414-fa5c37ec45cb">
      <currentRevision>
        <number>26</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>text/plain</mime>
        <mstyle>TEXT_PLAIN</mstyle>
        <size>6</size>
        <md5>6882bf1ef7f4d938a4cf2931d6953fa1</md5>
        <updated>2014-11-23-T21:28:55Z</updated>
      </currentRevision>
    </file>
    <file name="oo.txt" uuid="691aa9b6-eec1-4564-803e-6288dc57aa37">
      <currentRevision>
        <number>3</number>
        <state>COMPLETED</state>
        <created>2014-12-18-T21:23:48Z</created>
        <modified>2014-12-18-T21:23:48Z</modified>
        <mime>text/plain</mime>
        <mstyle>TEXT_PLAIN</mstyle>
        <size>4</size>
        <md5>aa3f5bb8c988fa9b75a1cdb1dc4d93fc</md5>
        <updated>2014-12-18-T21:23:48Z</updated>
      </currentRevision>
    </file>
    <file name="pingpong.data" uuid="f02f1ca1-c6a4-403d-b344-f4e3417d92fd">
      <currentRevision>
        <number>165</number>
        <state>COMPLETED</state>
        <created>2014-12-18-T21:20:32Z</created>
        <modified>2014-12-18-T21:20:32Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>9216</size>
        <md5>ec041186ebff92a26ab3ef2dd34dd0e7</md5>
        <updated>2014-12-18-T21:20:32Z</updated>
      </currentRevision>
    </file>
    <file name="testfs" uuid="a00034dd-971c-4699-8ee0-e7514045770e">
      <currentRevision>
        <number>2</number>
        <state>COMPLETED</state>
        <created>2014-10-05-T10:23:18Z</created>
        <modified>2014-10-05-T10:23:18Z</modified>
        <mime>application/octet-stream</mime>
        <mstyle>APPLICATION_OCTET_STREAM</mstyle>
        <size>0</size>
        <md5>d41d8cd98f00b204e9800998ecf8427e</md5>
        <updated>2014-11-23-T19:41:03Z</updated>
      </currentRevision>
    </file>
  </files>
  <metadata first="" max="" total="11" num_folders="1" num_files="10"/>
</folder>
"""
        o = lxml.objectify.fromstring(xml)
        dev = JFS.JFSFolder(o, jfs, parentpath=jfs.rootpath + '/Jotta/Archive')
        dev.synced = True # make sure our tests are based on the xml above, and not live

        #test native properties
        assert dev.path == jfs.rootpath + '/Jotta/Archive/test2'
        assert dev.name == 'test2'
        assert dev.deleted == None
        assert dev.is_deleted() == False

        #test convenience methods
        assert len(list(dev.folders())) == 1
        assert len(list(dev.files())) == 10
        assert all(isinstance(item, JFS.JFSFile) for item in dev.files())
        assert all(isinstance(item, JFS.JFSFolder) for item in dev.folders())