def download(auth, label, scan_ids=None, project=None, aid=None,
             out_dir='.', in_mem=True, progress=False, attempts=1):
    '''
    Download scan data from XNAT.
    
    Example:
        >>> import yaxil
        >>> auth = yaxil.XnatAuth(url='...', username='...', password='...')
        >>> yaxil.download(auth, 'AB1234C', ['1', '2'], out_dir='./data')

    :param auth: XNAT authentication object
    :type auth: :mod:`yaxil.XnatAuth`
    :param label: XNAT MR Session label
    :type label: str
    :param scan_ids: Scan numbers to return; use None for all
    :type scan_ids: list
    :param project: XNAT MR Session project
    :type project: str
    :param aid: XNAT Accession ID
    :type aid: str
    :param out_dir: Output directory
    :type out_dir: str
    :param in_mem: Keep download content in memory; faster but uses more memory
    :type in_mem: bool
    :param progress: Show download progress every N bytes
    :type progress: int
    :param attempts: Number of download attempts
    :type attempts: int
    ''' 
    if not scan_ids:
        scan_ids = ['ALL']
    if not aid:
        aid = accession(auth, label, project)
    # build the url
    url = "%s/data/experiments/%s/scans/%s/files?format=zip" % (auth.url.rstrip('/'), 
            aid, ','.join([str(x) for x in scan_ids]))
    # issue the http request, with exponential backoff retry behavior
    backoff = 10
    for _ in range(attempts): 
        logger.debug("issuing http request %s", url)
        r = requests.get(url, stream=True, auth=(auth.username, auth.password), verify=CHECK_CERTIFICATE)
        logger.debug("response headers %s", r.headers)
        if r.status_code == requests.codes.ok:
            break
        fuzz = random.randint(0, 10)
        logger.warn("download unsuccessful (%s), retrying in %s seconds", r.status_code, 
                    backoff + fuzz)
        time.sleep(backoff + fuzz)
        backoff *= 2
    # if we still have a not-ok status at this point, the download failed
    if r.status_code != requests.codes.ok:
        raise DownloadError("response not ok (%s) from %s" % (r.status_code, r.url))
    # create output directory
    if not os.path.exists(out_dir):
        os.makedirs(out_dir)
    # keep response content in memory or write to a file (memory is obviously faster, but limited)
    if in_mem:
        content = io.BytesIO()
        logger.debug("response content will be read into memory")
    else:
        content = tf.NamedTemporaryFile(dir=out_dir, prefix="xnat", suffix=".zip")
        logger.debug("response content will be stored on disk %s", content.name)
    # progress indicator setup
    if progress:
        sys.stdout.write("reading response data: ")
        sys.stdout.flush()
    # read response content in chunks
    meter = 0
    chunk_size = 1024
    for chunk in r.iter_content(chunk_size=chunk_size):
        if progress and meter >= progress:
            sys.stdout.write(next(commons.spinner)); sys.stdout.flush()
            sys.stdout.write('\b')
            meter = 0
        content.write(chunk)
        meter += chunk_size
    # progress indicator shut down
    if progress:
        sys.stdout.write('done.\n'); sys.stdout.flush()
    # load reponse content into a zipfile object
    try:
        zf = zipfile.ZipFile(content, allowZip64=True)
    except zipfile.BadZipfile:
        with tf.NamedTemporaryFile(dir=out_dir, prefix="xnat",
                                   suffix=".zip", delete=False) as fo:
            content.seek(0)
            fo.write(content.read())
        raise DownloadError("bad zip file, written to %s" % fo.name)
    # finally extract the zipfile (with various nasty edge cases handled)
    logger.debug("extracting zip archive to %s", out_dir)
    extract(zf, content, out_dir)
Download scan data from XNAT.
    
    Example:
        >>> import yaxil
        >>> auth = yaxil.XnatAuth(url='...', username='...', password='...')
        >>> yaxil.download(auth, 'AB1234C', ['1', '2'], out_dir='./data')

    :param auth: XNAT authentication object
    :type auth: :mod:`yaxil.XnatAuth`
    :param label: XNAT MR Session label
    :type label: str
    :param scan_ids: Scan numbers to return; use None for all
    :type scan_ids: list
    :param project: XNAT MR Session project
    :type project: str
    :param aid: XNAT Accession ID
    :type aid: str
    :param out_dir: Output directory
    :type out_dir: str
    :param in_mem: Keep download content in memory; faster but uses more memory
    :type in_mem: bool
    :param progress: Show download progress every N bytes
    :type progress: int
    :param attempts: Number of download attempts
    :type attempts: int