(tor_protocol, onion, progress, await_all_uploads)
Internal helper.

    :param tor_protocol: ITorControlProtocol instance

    :param onion: IOnionService instance

    :param progress: a progess callback, or None

    :returns: a Deferred that fires once we've detected at least one
        descriptor upload for the service (as detected by listening for
        HS_DESC events)
Internal helper.
Internal helper .
def _await_descriptor_upload(tor_protocol, onion, progress, await_all_uploads):
    """
    Internal helper.

    :param tor_protocol: ITorControlProtocol instance

    :param onion: IOnionService instance

    :param progress: a progess callback, or None

    :returns: a Deferred that fires once we've detected at least one
        descriptor upload for the service (as detected by listening for
        HS_DESC events)
    """
    # For v3 services, Tor attempts to upload to 16 services; we'll
    # assume that for now but also cap it (we want to show some
    # progress for "attempting uploads" but we need to decide how
    # much) .. so we leave 50% of the "progress" for attempts, and the
    # other 50% for "are we done" (which is either "one thing
    # uploaded" or "all the things uploaded")
    attempted_uploads = set()
    confirmed_uploads = set()
    failed_uploads = set()
    uploaded = defer.Deferred()
    await_all = False if await_all_uploads is None else await_all_uploads

    def translate_progress(tag, description):
        if progress:
            done = len(confirmed_uploads) + len(failed_uploads)
            done_endpoint = float(len(attempted_uploads)) if await_all else 1.0
            done_pct = 0 if not attempted_uploads else float(done) / done_endpoint
            started_pct = float(min(16, len(attempted_uploads))) / 16.0
            try:
                progress(
                    (done_pct * 50.0) + (started_pct * 50.0),
                    tag,
                    description,
                )
            except Exception:
                log.err()

    def hostname_matches(hostname):
        if IAuthenticatedOnionClients.providedBy(onion):
            return hostname[:-6] == onion.get_permanent_id()
        else:
            # provides IOnionService
            return onion.hostname == hostname

    def hs_desc(evt):
        """
        From control-spec:
        "650" SP "HS_DESC" SP Action SP HSAddress SP AuthType SP HsDir
        [SP DescriptorID] [SP "REASON=" Reason] [SP "REPLICA=" Replica]
        """
        args = evt.split()
        subtype = args[0]
        if subtype == 'UPLOAD':
            if hostname_matches('{}.onion'.format(args[1])):
                attempted_uploads.add(args[3])
                translate_progress(
                    "wait_descriptor",
                    "Upload to {} started".format(args[3])
                )

        elif subtype == 'UPLOADED':
            # we only need ONE successful upload to happen for the
            # HS to be reachable.
            # unused? addr = args[1]

            # XXX FIXME I think tor is sending the onion-address
            # properly with these now, so we can use those
            # (i.e. instead of matching to "attempted_uploads")
            if args[3] in attempted_uploads:
                confirmed_uploads.add(args[3])
                log.msg("Uploaded '{}' to '{}'".format(args[1], args[3]))
                translate_progress(
                    "wait_descriptor",
                    "Successful upload to {}".format(args[3])
                )
                if not uploaded.called:
                    if await_all:
                        if (len(failed_uploads) + len(confirmed_uploads)) == len(attempted_uploads):
                            uploaded.callback(onion)
                    else:
                        uploaded.callback(onion)

        elif subtype == 'FAILED':
            if hostname_matches('{}.onion'.format(args[1])):
                failed_uploads.add(args[3])
                translate_progress(
                    "wait_descriptor",
                    "Failed upload to {}".format(args[3])
                )
                if failed_uploads == attempted_uploads:
                    msg = "Failed to upload '{}' to: {}".format(
                        args[1],
                        ', '.join(failed_uploads),
                    )
                    uploaded.errback(RuntimeError(msg))

    # the first 'yield' should be the add_event_listener so that a
    # caller can do "d = _await_descriptor_upload()", then add the
    # service.
    yield tor_protocol.add_event_listener('HS_DESC', hs_desc)
    yield uploaded
    yield tor_protocol.remove_event_listener('HS_DESC', hs_desc)
    # ensure we show "100%" at the end
    if progress:
        if await_all_uploads:
            msg = "Completed descriptor uploads"
        else:
            msg = "At least one descriptor uploaded"
        try:
            progress(100.0, "wait_descriptor", msg)
        except Exception:
            log.err()
def _await_descriptor_upload ( tor_protocol , onion , progress , await_all_uploads ) : # For v3 services, Tor attempts to upload to 16 services; we'll # assume that for now but also cap it (we want to show some # progress for "attempting uploads" but we need to decide how # much) .. so we leave 50% of the "progress" for attempts, and the # other 50% for "are we done" (which is either "one thing # uploaded" or "all the things uploaded") attempted_uploads = set ( ) confirmed_uploads = set ( ) failed_uploads = set ( ) uploaded = defer . Deferred ( ) await_all = False if await_all_uploads is None else await_all_uploads def translate_progress ( tag , description ) : if progress : done = len ( confirmed_uploads ) + len ( failed_uploads ) done_endpoint = float ( len ( attempted_uploads ) ) if await_all else 1.0 done_pct = 0 if not attempted_uploads else float ( done ) / done_endpoint started_pct = float ( min ( 16 , len ( attempted_uploads ) ) ) / 16.0 try : progress ( ( done_pct * 50.0 ) + ( started_pct * 50.0 ) , tag , description , ) except Exception : log . err ( ) def hostname_matches ( hostname ) : if IAuthenticatedOnionClients . providedBy ( onion ) : return hostname [ : - 6 ] == onion . get_permanent_id ( ) else : # provides IOnionService return onion . hostname == hostname def hs_desc ( evt ) : """
        From control-spec:
        "650" SP "HS_DESC" SP Action SP HSAddress SP AuthType SP HsDir
        [SP DescriptorID] [SP "REASON=" Reason] [SP "REPLICA=" Replica]
        """ args = evt . split ( ) subtype = args [ 0 ] if subtype == 'UPLOAD' : if hostname_matches ( '{}.onion' . format ( args [ 1 ] ) ) : attempted_uploads . add ( args [ 3 ] ) translate_progress ( "wait_descriptor" , "Upload to {} started" . format ( args [ 3 ] ) ) elif subtype == 'UPLOADED' : # we only need ONE successful upload to happen for the # HS to be reachable. # unused? addr = args[1] # XXX FIXME I think tor is sending the onion-address # properly with these now, so we can use those # (i.e. instead of matching to "attempted_uploads") if args [ 3 ] in attempted_uploads : confirmed_uploads . add ( args [ 3 ] ) log . msg ( "Uploaded '{}' to '{}'" . format ( args [ 1 ] , args [ 3 ] ) ) translate_progress ( "wait_descriptor" , "Successful upload to {}" . format ( args [ 3 ] ) ) if not uploaded . called : if await_all : if ( len ( failed_uploads ) + len ( confirmed_uploads ) ) == len ( attempted_uploads ) : uploaded . callback ( onion ) else : uploaded . callback ( onion ) elif subtype == 'FAILED' : if hostname_matches ( '{}.onion' . format ( args [ 1 ] ) ) : failed_uploads . add ( args [ 3 ] ) translate_progress ( "wait_descriptor" , "Failed upload to {}" . format ( args [ 3 ] ) ) if failed_uploads == attempted_uploads : msg = "Failed to upload '{}' to: {}" . format ( args [ 1 ] , ', ' . join ( failed_uploads ) , ) uploaded . errback ( RuntimeError ( msg ) ) # the first 'yield' should be the add_event_listener so that a # caller can do "d = _await_descriptor_upload()", then add the # service. yield tor_protocol . add_event_listener ( 'HS_DESC' , hs_desc ) yield uploaded yield tor_protocol . remove_event_listener ( 'HS_DESC' , hs_desc ) # ensure we show "100%" at the end if progress : if await_all_uploads : msg = "Completed descriptor uploads" else : msg = "At least one descriptor uploaded" try : progress ( 100.0 , "wait_descriptor" , msg ) except Exception : log . err ( )