Exemplo n.º 1
0
def access_message(
    user_profile: UserProfile,
    message_id: int,
    lock_message: bool = False,
) -> Tuple[Message, Optional[UserMessage]]:
    """You can access a message by ID in our APIs that either:
    (1) You received or have previously accessed via starring
        (aka have a UserMessage row for).
    (2) Was sent to a public stream in your realm.

    We produce consistent, boring error messages to avoid leaking any
    information from a security perspective.

    The lock_message parameter should be passed by callers that are
    planning to modify the Message object. This will use the SQL
    `SELECT FOR UPDATE` feature to ensure that other processes cannot
    delete the message during the current transaction, which is
    important to prevent rare race conditions. Callers must only
    pass lock_message when inside a @transaction.atomic block.
    """
    try:
        base_query = Message.objects.select_related()
        if lock_message:
            # We want to lock only the `Message` row, and not the related fields
            # because the `Message` row only has a possibility of races.
            base_query = base_query.select_for_update(of=("self",))
        message = base_query.get(id=message_id)
    except Message.DoesNotExist:
        raise JsonableError(_("Invalid message(s)"))

    user_message = get_usermessage_by_message_id(user_profile, message_id)

    if has_message_access(user_profile, message, has_user_message=user_message is not None):
        return (message, user_message)
    raise JsonableError(_("Invalid message(s)"))
Exemplo n.º 2
0
def bulk_access_messages(user_profile: UserProfile, messages: Sequence[Message]) -> List[Message]:
    filtered_messages = []

    for message in messages:
        user_message = get_usermessage_by_message_id(user_profile, message.id)
        if has_message_access(user_profile, message, user_message):
            filtered_messages.append(message)
    return filtered_messages
Exemplo n.º 3
0
def bulk_access_messages(user_profile: UserProfile, messages: Sequence[Message]) -> List[Message]:
    filtered_messages = []

    for message in messages:
        user_message = get_usermessage_by_message_id(user_profile, message.id)
        if has_message_access(user_profile, message, user_message):
            filtered_messages.append(message)
    return filtered_messages
Exemplo n.º 4
0
def update_pointer_backend(request: HttpRequest, user_profile: UserProfile,
                           pointer: int=REQ(converter=to_non_negative_int)) -> HttpResponse:
    if pointer <= user_profile.pointer:
        return json_success()

    if get_usermessage_by_message_id(user_profile, pointer) is None:
        raise JsonableError(_("Invalid message ID"))

    request._log_data["extra"] = "[%s]" % (pointer,)
    update_flags = (request.client.name.lower() in ['android', "zulipandroid"])
    do_update_pointer(user_profile, request.client, pointer, update_flags=update_flags)

    return json_success()
Exemplo n.º 5
0
def access_message(user_profile: UserProfile, message_id: int) -> Tuple[Message, Optional[UserMessage]]:
    """You can access a message by ID in our APIs that either:
    (1) You received or have previously accessed via starring
        (aka have a UserMessage row for).
    (2) Was sent to a public stream in your realm.

    We produce consistent, boring error messages to avoid leaking any
    information from a security perspective.
    """
    try:
        message = Message.objects.select_related().get(id=message_id)
    except Message.DoesNotExist:
        raise JsonableError(_("Invalid message(s)"))

    user_message = get_usermessage_by_message_id(user_profile, message_id)

    if has_message_access(user_profile, message, user_message):
        return (message, user_message)
    raise JsonableError(_("Invalid message(s)"))
Exemplo n.º 6
0
def access_message(user_profile: UserProfile, message_id: int) -> Tuple[Message, Optional[UserMessage]]:
    """You can access a message by ID in our APIs that either:
    (1) You received or have previously accessed via starring
        (aka have a UserMessage row for).
    (2) Was sent to a public stream in your realm.

    We produce consistent, boring error messages to avoid leaking any
    information from a security perspective.
    """
    try:
        message = Message.objects.select_related().get(id=message_id)
    except Message.DoesNotExist:
        raise JsonableError(_("Invalid message(s)"))

    user_message = get_usermessage_by_message_id(user_profile, message_id)

    if has_message_access(user_profile, message, user_message):
        return (message, user_message)
    raise JsonableError(_("Invalid message(s)"))
Exemplo n.º 7
0
def home_real(request: HttpRequest) -> HttpResponse:
    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    user_profile = request.user

    # If a user hasn't signed the current Terms of Service, send them there
    if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \
       int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version():
        return accounts_accept_terms(request)

    narrow = []  # type: List[List[str]]
    narrow_stream = None
    narrow_topic = request.GET.get("topic")
    if request.GET.get("stream"):
        try:
            narrow_stream_name = request.GET.get("stream")
            (narrow_stream, ignored_rec,
             ignored_sub) = access_stream_by_name(user_profile,
                                                  narrow_stream_name)
            narrow = [["stream", narrow_stream.name]]
        except Exception:
            logging.exception("Narrow parsing exception",
                              extra=dict(request=request))
        if narrow_stream is not None and narrow_topic is not None:
            narrow.append(["topic", narrow_topic])

    register_ret = do_events_register(user_profile,
                                      request.client,
                                      apply_markdown=True,
                                      client_gravatar=True,
                                      narrow=narrow)
    user_has_messages = (register_ret['max_message_id'] != -1)

    # Reset our don't-spam-users-with-email counter since the
    # user has since logged in
    if user_profile.last_reminder is not None:  # nocoverage
        # TODO: Look into the history of last_reminder; we may have
        # eliminated that as a useful concept for non-bot users.
        user_profile.last_reminder = None
        user_profile.save(update_fields=["last_reminder"])

    # Brand new users get narrowed to PM with welcome-bot
    needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING

    first_in_realm = realm_user_count(user_profile.realm) == 1
    # If you are the only person in the realm and you didn't invite
    # anyone, we'll continue to encourage you to do so on the frontend.
    prompt_for_invites = first_in_realm and \
        not PreregistrationUser.objects.filter(referred_by=user_profile).count()

    if user_profile.pointer == -1 and user_has_messages:
        # Put the new user's pointer at the bottom
        #
        # This improves performance, because we limit backfilling of messages
        # before the pointer.  It's also likely that someone joining an
        # organization is interested in recent messages more than the very
        # first messages on the system.

        register_ret['pointer'] = register_ret['max_message_id']
        user_profile.last_pointer_updater = request.session.session_key

    if user_profile.pointer == -1:
        latest_read = None
    else:
        latest_read = get_usermessage_by_message_id(user_profile,
                                                    user_profile.pointer)
        if latest_read is None:
            # Don't completely fail if your saved pointer ID is invalid
            logging.warning("%s has invalid pointer %s" %
                            (user_profile.email, user_profile.pointer))

    # We pick a language for the user as follows:
    # * First priority is the language in the URL, for debugging.
    # * If not in the URL, we use the language from the user's settings.
    request_language = translation.get_language_from_path(request.path_info)
    if request_language is None:
        request_language = register_ret['default_language']
    translation.activate(request_language)
    # We also save the language to the user's session, so that
    # something reasonable will happen in logged-in portico pages.
    request.session[
        translation.LANGUAGE_SESSION_KEY] = translation.get_language()

    two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED
    # Pass parameters to the client-side JavaScript code.
    # These end up in a global JavaScript Object named 'page_params'.
    page_params = dict(
        # Server settings.
        development_environment=settings.DEVELOPMENT,
        debug_mode=settings.DEBUG,
        test_suite=settings.TEST_SUITE,
        poll_timeout=settings.POLL_TIMEOUT,
        login_page=settings.HOME_NOT_LOGGED_IN,
        root_domain_uri=settings.ROOT_DOMAIN_URI,
        maxfilesize=settings.MAX_FILE_UPLOAD_SIZE,
        max_avatar_file_size=settings.MAX_AVATAR_FILE_SIZE,
        server_generation=settings.SERVER_GENERATION,
        use_websockets=settings.USE_WEBSOCKETS,
        save_stacktraces=settings.SAVE_FRONTEND_STACKTRACES,
        warn_no_email=settings.WARN_NO_EMAIL,
        server_inline_image_preview=settings.INLINE_IMAGE_PREVIEW,
        server_inline_url_embed_preview=settings.INLINE_URL_EMBED_PREVIEW,
        password_min_length=settings.PASSWORD_MIN_LENGTH,
        password_min_guesses=settings.PASSWORD_MIN_GUESSES,
        jitsi_server_url=settings.JITSI_SERVER_URL,
        search_pills_enabled=settings.SEARCH_PILLS_ENABLED,

        # Misc. extra data.
        have_initial_messages=user_has_messages,
        initial_servertime=time.time(
        ),  # Used for calculating relative presence age
        default_language_name=get_language_name(
            register_ret['default_language']),
        language_list_dbl_col=get_language_list_for_templates(
            register_ret['default_language']),
        language_list=get_language_list(),
        needs_tutorial=needs_tutorial,
        first_in_realm=first_in_realm,
        prompt_for_invites=prompt_for_invites,
        furthest_read_time=sent_time_in_epoch_seconds(latest_read),
        has_mobile_devices=num_push_devices_for_user(user_profile) > 0,
        bot_types=get_bot_types(user_profile),
        two_fa_enabled=two_fa_enabled,
        # Adding two_fa_enabled as condition saves us 3 queries when
        # 2FA is not enabled.
        two_fa_enabled_user=two_fa_enabled
        and bool(default_device(user_profile)),
    )

    undesired_register_ret_fields = [
        'streams',
    ]
    for field_name in set(
            register_ret.keys()) - set(undesired_register_ret_fields):
        page_params[field_name] = register_ret[field_name]

    if narrow_stream is not None:
        # In narrow_stream context, initial pointer is just latest message
        recipient = get_stream_recipient(narrow_stream.id)
        try:
            initial_pointer = Message.objects.filter(
                recipient=recipient).order_by('id').reverse()[0].id
        except IndexError:
            initial_pointer = -1
        page_params["narrow_stream"] = narrow_stream.name
        if narrow_topic is not None:
            page_params["narrow_topic"] = narrow_topic
        page_params["narrow"] = [
            dict(operator=term[0], operand=term[1]) for term in narrow
        ]
        page_params["max_message_id"] = initial_pointer
        page_params["pointer"] = initial_pointer
        page_params["have_initial_messages"] = (initial_pointer != -1)
        page_params["enable_desktop_notifications"] = False

    statsd.incr('views.home')
    show_invites = True

    # Some realms only allow admins to invite users
    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
        show_invites = False
    if user_profile.is_guest:
        show_invites = False

    show_billing = False
    show_plans = False
    if settings.CORPORATE_ENABLED:
        from corporate.models import Customer
        if user_profile.is_billing_admin or user_profile.is_realm_admin:
            customer = Customer.objects.filter(
                realm=user_profile.realm).first()
            if customer is not None and customer.has_billing_relationship:
                show_billing = True
        if user_profile.realm.plan_type == Realm.LIMITED:
            show_plans = True

    request._log_data['extra'] = "[%s]" % (register_ret["queue_id"], )

    page_params['translation_data'] = {}
    if request_language != 'en':
        page_params['translation_data'] = get_language_translation_data(
            request_language)

    csp_nonce = generate_random_token(48)
    emojiset = user_profile.emojiset
    if emojiset == UserProfile.TEXT_EMOJISET:
        # If current emojiset is `TEXT_EMOJISET`, then fallback to
        # GOOGLE_EMOJISET for picking which spritesheet's CSS to
        # include (and thus how to display emojis in the emoji picker
        # and composebox typeahead).
        emojiset = UserProfile.GOOGLE_BLOB_EMOJISET
    response = render(
        request,
        'zerver/app/index.html',
        context={
            'user_profile': user_profile,
            'emojiset': emojiset,
            'page_params': JSONEncoderForHTML().encode(page_params),
            'csp_nonce': csp_nonce,
            'avatar_url': avatar_url(user_profile),
            'show_debug': settings.DEBUG and ('show_debug' in request.GET),
            'pipeline': settings.PIPELINE_ENABLED,
            'search_pills_enabled': settings.SEARCH_PILLS_ENABLED,
            'show_invites': show_invites,
            'show_billing': show_billing,
            'show_plans': show_plans,
            'is_admin': user_profile.is_realm_admin,
            'is_guest': user_profile.is_guest,
            'show_webathena': user_profile.realm.webathena_enabled,
            'enable_feedback': settings.ENABLE_FEEDBACK,
            'embedded': narrow_stream is not None,
        },
    )
    patch_cache_control(response,
                        no_cache=True,
                        no_store=True,
                        must_revalidate=True)
    return response
Exemplo n.º 8
0
def home_real(request: HttpRequest) -> HttpResponse:
    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    user_profile = request.user

    # If a user hasn't signed the current Terms of Service, send them there
    if settings.TERMS_OF_SERVICE is not None and settings.TOS_VERSION is not None and \
       int(settings.TOS_VERSION.split('.')[0]) > user_profile.major_tos_version():
        return accounts_accept_terms(request)

    narrow = []  # type: List[List[str]]
    narrow_stream = None
    narrow_topic = request.GET.get("topic")
    if request.GET.get("stream"):
        try:
            narrow_stream_name = request.GET.get("stream")
            (narrow_stream, ignored_rec, ignored_sub) = access_stream_by_name(
                user_profile, narrow_stream_name)
            narrow = [["stream", narrow_stream.name]]
        except Exception:
            logging.exception("Narrow parsing exception", extra=dict(request=request))
        if narrow_stream is not None and narrow_topic is not None:
            narrow.append(["topic", narrow_topic])

    register_ret = do_events_register(user_profile, request.client,
                                      apply_markdown=True, client_gravatar=True,
                                      narrow=narrow)
    user_has_messages = (register_ret['max_message_id'] != -1)

    # Reset our don't-spam-users-with-email counter since the
    # user has since logged in
    if user_profile.last_reminder is not None:  # nocoverage
        # TODO: Look into the history of last_reminder; we may have
        # eliminated that as a useful concept for non-bot users.
        user_profile.last_reminder = None
        user_profile.save(update_fields=["last_reminder"])

    # Brand new users get narrowed to PM with welcome-bot
    needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING

    first_in_realm = realm_user_count(user_profile.realm) == 1
    # If you are the only person in the realm and you didn't invite
    # anyone, we'll continue to encourage you to do so on the frontend.
    prompt_for_invites = first_in_realm and \
        not PreregistrationUser.objects.filter(referred_by=user_profile).count()

    if user_profile.pointer == -1 and user_has_messages:
        # Put the new user's pointer at the bottom
        #
        # This improves performance, because we limit backfilling of messages
        # before the pointer.  It's also likely that someone joining an
        # organization is interested in recent messages more than the very
        # first messages on the system.

        register_ret['pointer'] = register_ret['max_message_id']
        user_profile.last_pointer_updater = request.session.session_key

    if user_profile.pointer == -1:
        latest_read = None
    else:
        latest_read = get_usermessage_by_message_id(user_profile, user_profile.pointer)
        if latest_read is None:
            # Don't completely fail if your saved pointer ID is invalid
            logging.warning("%s has invalid pointer %s" % (user_profile.email, user_profile.pointer))

    # We pick a language for the user as follows:
    # * First priority is the language in the URL, for debugging.
    # * If not in the URL, we use the language from the user's settings.
    request_language = translation.get_language_from_path(request.path_info)
    if request_language is None:
        request_language = register_ret['default_language']
    translation.activate(request_language)
    # We also save the language to the user's session, so that
    # something reasonable will happen in logged-in portico pages.
    request.session[translation.LANGUAGE_SESSION_KEY] = translation.get_language()

    two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED
    # Pass parameters to the client-side JavaScript code.
    # These end up in a global JavaScript Object named 'page_params'.
    page_params = dict(
        # Server settings.
        development_environment = settings.DEVELOPMENT,
        debug_mode            = settings.DEBUG,
        test_suite            = settings.TEST_SUITE,
        poll_timeout          = settings.POLL_TIMEOUT,
        login_page            = settings.HOME_NOT_LOGGED_IN,
        root_domain_uri       = settings.ROOT_DOMAIN_URI,
        maxfilesize           = settings.MAX_FILE_UPLOAD_SIZE,
        max_avatar_file_size  = settings.MAX_AVATAR_FILE_SIZE,
        server_generation     = settings.SERVER_GENERATION,
        use_websockets        = settings.USE_WEBSOCKETS,
        save_stacktraces      = settings.SAVE_FRONTEND_STACKTRACES,
        warn_no_email         = settings.WARN_NO_EMAIL,
        server_inline_image_preview = settings.INLINE_IMAGE_PREVIEW,
        server_inline_url_embed_preview = settings.INLINE_URL_EMBED_PREVIEW,
        password_min_length = settings.PASSWORD_MIN_LENGTH,
        password_min_guesses  = settings.PASSWORD_MIN_GUESSES,
        jitsi_server_url      = settings.JITSI_SERVER_URL,
        search_pills_enabled  = settings.SEARCH_PILLS_ENABLED,

        # Misc. extra data.
        have_initial_messages = user_has_messages,
        initial_servertime    = time.time(),  # Used for calculating relative presence age
        default_language_name = get_language_name(register_ret['default_language']),
        language_list_dbl_col = get_language_list_for_templates(register_ret['default_language']),
        language_list         = get_language_list(),
        needs_tutorial        = needs_tutorial,
        first_in_realm        = first_in_realm,
        prompt_for_invites    = prompt_for_invites,
        furthest_read_time    = sent_time_in_epoch_seconds(latest_read),
        has_mobile_devices    = num_push_devices_for_user(user_profile) > 0,
        bot_types             = get_bot_types(user_profile),
        two_fa_enabled        = two_fa_enabled,
        # Adding two_fa_enabled as condition saves us 3 queries when
        # 2FA is not enabled.
        two_fa_enabled_user   = two_fa_enabled and bool(default_device(user_profile)),
    )

    undesired_register_ret_fields = [
        'streams',
    ]
    for field_name in set(register_ret.keys()) - set(undesired_register_ret_fields):
        page_params[field_name] = register_ret[field_name]

    if narrow_stream is not None:
        # In narrow_stream context, initial pointer is just latest message
        recipient = get_stream_recipient(narrow_stream.id)
        try:
            initial_pointer = Message.objects.filter(recipient=recipient).order_by('id').reverse()[0].id
        except IndexError:
            initial_pointer = -1
        page_params["narrow_stream"] = narrow_stream.name
        if narrow_topic is not None:
            page_params["narrow_topic"] = narrow_topic
        page_params["narrow"] = [dict(operator=term[0], operand=term[1]) for term in narrow]
        page_params["max_message_id"] = initial_pointer
        page_params["pointer"] = initial_pointer
        page_params["have_initial_messages"] = (initial_pointer != -1)
        page_params["enable_desktop_notifications"] = False

    statsd.incr('views.home')
    show_invites = True

    # Some realms only allow admins to invite users
    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
        show_invites = False
    if user_profile.is_guest:
        show_invites = False

    show_billing = False
    show_plans = False
    if settings.CORPORATE_ENABLED:
        from corporate.models import Customer
        if user_profile.is_billing_admin or user_profile.is_realm_admin:
            customer = Customer.objects.filter(realm=user_profile.realm).first()
            if customer is not None and customer.has_billing_relationship:
                show_billing = True
        if user_profile.realm.plan_type == Realm.LIMITED:
            show_plans = True

    request._log_data['extra'] = "[%s]" % (register_ret["queue_id"],)

    page_params['translation_data'] = {}
    if request_language != 'en':
        page_params['translation_data'] = get_language_translation_data(request_language)

    csp_nonce = generate_random_token(48)
    emojiset = user_profile.emojiset
    if emojiset == UserProfile.TEXT_EMOJISET:
        # If current emojiset is `TEXT_EMOJISET`, then fallback to
        # GOOGLE_EMOJISET for picking which spritesheet's CSS to
        # include (and thus how to display emojis in the emoji picker
        # and composebox typeahead).
        emojiset = UserProfile.GOOGLE_BLOB_EMOJISET
    response = render(request, 'zerver/app/index.html',
                      context={'user_profile': user_profile,
                               'emojiset': emojiset,
                               'page_params': JSONEncoderForHTML().encode(page_params),
                               'csp_nonce': csp_nonce,
                               'avatar_url': avatar_url(user_profile),
                               'show_debug':
                               settings.DEBUG and ('show_debug' in request.GET),
                               'pipeline': settings.PIPELINE_ENABLED,
                               'search_pills_enabled': settings.SEARCH_PILLS_ENABLED,
                               'show_invites': show_invites,
                               'show_billing': show_billing,
                               'show_plans': show_plans,
                               'is_admin': user_profile.is_realm_admin,
                               'is_guest': user_profile.is_guest,
                               'show_webathena': user_profile.realm.webathena_enabled,
                               'enable_feedback': settings.ENABLE_FEEDBACK,
                               'embedded': narrow_stream is not None,
                               'invite_as': PreregistrationUser.INVITE_AS,
                               },)
    patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True)
    return response
Exemplo n.º 9
0
def home_real(request: HttpRequest) -> HttpResponse:
    # Before we do any real work, check if the app is banned.
    (insecure_desktop_app, banned_desktop_app,
     auto_update_broken) = is_outdated_desktop_app(
         request.META.get("HTTP_USER_AGENT", ""))
    if banned_desktop_app:
        return render(request,
                      'zerver/insecure_desktop_app.html',
                      context={
                          "auto_update_broken": auto_update_broken,
                      })

    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    if request.user.is_authenticated:
        user_profile = request.user
    else:  # nocoverage
        # This code path should not be reachable because of zulip_login_required above.
        user_profile = None

    # If a user hasn't signed the current Terms of Service, send them there
    if need_accept_tos(user_profile):
        return accounts_accept_terms(request)

    narrow, narrow_stream, narrow_topic = detect_narrowed_window(
        request, user_profile)

    register_ret = do_events_register(user_profile,
                                      request.client,
                                      apply_markdown=True,
                                      client_gravatar=True,
                                      slim_presence=True,
                                      notification_settings_null=True,
                                      narrow=narrow)
    user_has_messages = (register_ret['max_message_id'] != -1)
    update_last_reminder(user_profile)

    if user_profile is not None:
        first_in_realm = realm_user_count(user_profile.realm) == 1
        # If you are the only person in the realm and you didn't invite
        # anyone, we'll continue to encourage you to do so on the frontend.
        prompt_for_invites = (first_in_realm
                              and not PreregistrationUser.objects.filter(
                                  referred_by=user_profile).count())
        needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING
    else:  # nocoverage
        first_in_realm = False
        prompt_for_invites = False
        # The current tutorial doesn't super make sense for logged-out users.
        needs_tutorial = False

    if user_profile is None:  # nocoverage
        furthest_read_time = time.time()  # type: Optional[float]
    elif user_profile.pointer == -1:
        if user_has_messages:
            # Put the new user's pointer at the bottom
            #
            # This improves performance, because we limit backfilling of messages
            # before the pointer.  It's also likely that someone joining an
            # organization is interested in recent messages more than the very
            # first messages on the system.

            register_ret['pointer'] = register_ret['max_message_id']
            user_profile.last_pointer_updater = request.session.session_key
        furthest_read_time = None
    else:
        latest_read = get_usermessage_by_message_id(user_profile,
                                                    user_profile.pointer)
        if latest_read is None:
            # Don't completely fail if your saved pointer ID is invalid
            logging.warning("User %s has invalid pointer %s" %
                            (user_profile.id, user_profile.pointer))
        furthest_read_time = sent_time_in_epoch_seconds(latest_read)

    # We pick a language for the user as follows:
    # * First priority is the language in the URL, for debugging.
    # * If not in the URL, we use the language from the user's settings.
    request_language = translation.get_language_from_path(request.path_info)
    if request_language is None:
        request_language = register_ret['default_language']
    translation.activate(request_language)
    # We also save the language to the user's session, so that
    # something reasonable will happen in logged-in portico pages.
    request.session[
        translation.LANGUAGE_SESSION_KEY] = translation.get_language()

    two_fa_enabled = settings.TWO_FACTOR_AUTHENTICATION_ENABLED and user_profile is not None

    # Pass parameters to the client-side JavaScript code.
    # These end up in a global JavaScript Object named 'page_params'.
    page_params = dict(
        # Server settings.
        development_environment=settings.DEVELOPMENT,
        debug_mode=settings.DEBUG,
        test_suite=settings.TEST_SUITE,
        poll_timeout=settings.POLL_TIMEOUT,
        insecure_desktop_app=insecure_desktop_app,
        login_page=settings.HOME_NOT_LOGGED_IN,
        root_domain_uri=settings.ROOT_DOMAIN_URI,
        max_file_upload_size=settings.MAX_FILE_UPLOAD_SIZE,
        max_avatar_file_size=settings.MAX_AVATAR_FILE_SIZE,
        server_generation=settings.SERVER_GENERATION,
        save_stacktraces=settings.SAVE_FRONTEND_STACKTRACES,
        warn_no_email=settings.WARN_NO_EMAIL,
        server_inline_image_preview=settings.INLINE_IMAGE_PREVIEW,
        server_inline_url_embed_preview=settings.INLINE_URL_EMBED_PREVIEW,
        password_min_length=settings.PASSWORD_MIN_LENGTH,
        password_min_guesses=settings.PASSWORD_MIN_GUESSES,
        jitsi_server_url=settings.JITSI_SERVER_URL,
        search_pills_enabled=settings.SEARCH_PILLS_ENABLED,
        server_avatar_changes_disabled=settings.AVATAR_CHANGES_DISABLED,
        server_name_changes_disabled=settings.NAME_CHANGES_DISABLED,

        # Misc. extra data.
        have_initial_messages=user_has_messages,
        initial_servertime=time.time(
        ),  # Used for calculating relative presence age
        default_language_name=get_language_name(
            register_ret['default_language']),
        language_list_dbl_col=get_language_list_for_templates(
            register_ret['default_language']),
        language_list=get_language_list(),
        needs_tutorial=needs_tutorial,
        first_in_realm=first_in_realm,
        prompt_for_invites=prompt_for_invites,
        furthest_read_time=furthest_read_time,
        has_mobile_devices=user_profile is not None
        and num_push_devices_for_user(user_profile) > 0,
        bot_types=get_bot_types(user_profile),
        two_fa_enabled=two_fa_enabled,
        # Adding two_fa_enabled as condition saves us 3 queries when
        # 2FA is not enabled.
        two_fa_enabled_user=two_fa_enabled
        and bool(default_device(user_profile)),
    )

    undesired_register_ret_fields = [
        'streams',
    ]
    for field_name in set(
            register_ret.keys()) - set(undesired_register_ret_fields):
        page_params[field_name] = register_ret[field_name]

    if narrow_stream is not None:
        # In narrow_stream context, initial pointer is just latest message
        recipient = narrow_stream.recipient
        try:
            initial_pointer = Message.objects.filter(
                recipient=recipient).order_by('id').reverse()[0].id
        except IndexError:
            initial_pointer = -1
        page_params["narrow_stream"] = narrow_stream.name
        if narrow_topic is not None:
            page_params["narrow_topic"] = narrow_topic
        page_params["narrow"] = [
            dict(operator=term[0], operand=term[1]) for term in narrow
        ]
        page_params["max_message_id"] = initial_pointer
        page_params["pointer"] = initial_pointer
        page_params["have_initial_messages"] = (initial_pointer != -1)
        page_params["enable_desktop_notifications"] = False

    statsd.incr('views.home')
    show_invites, show_add_streams = compute_show_invites_and_add_streams(
        user_profile)

    show_billing = False
    show_plans = False
    if settings.CORPORATE_ENABLED and user_profile is not None:
        from corporate.models import Customer, CustomerPlan
        if user_profile.is_billing_admin or user_profile.is_realm_admin:
            customer = Customer.objects.filter(
                realm=user_profile.realm).first()
            if customer is not None and CustomerPlan.objects.filter(
                    customer=customer).exists():
                show_billing = True
        if user_profile.realm.plan_type == Realm.LIMITED:
            show_plans = True

    request._log_data['extra'] = "[%s]" % (register_ret["queue_id"], )

    page_params['translation_data'] = {}
    if request_language != 'en':
        page_params['translation_data'] = get_language_translation_data(
            request_language)

    csp_nonce = generate_random_token(48)
    if user_profile is not None:
        night_mode = user_profile.night_mode
        is_guest = user_profile.is_guest
        is_realm_admin = user_profile.is_realm_admin
        show_webathena = user_profile.realm.webathena_enabled
    else:  # nocoverage
        night_mode = False
        is_guest = False
        is_realm_admin = False
        show_webathena = False

    navbar_logo_url = compute_navbar_logo_url(page_params)

    response = render(
        request,
        'zerver/app/index.html',
        context={
            'user_profile': user_profile,
            'page_params': page_params,
            'csp_nonce': csp_nonce,
            'search_pills_enabled': settings.SEARCH_PILLS_ENABLED,
            'show_invites': show_invites,
            'show_add_streams': show_add_streams,
            'show_billing': show_billing,
            'show_plans': show_plans,
            'is_admin': is_realm_admin,
            'is_guest': is_guest,
            'night_mode': night_mode,
            'navbar_logo_url': navbar_logo_url,
            'show_webathena': show_webathena,
            'embedded': narrow_stream is not None,
            'invite_as': PreregistrationUser.INVITE_AS,
            'max_file_upload_size': settings.MAX_FILE_UPLOAD_SIZE,
        },
    )
    patch_cache_control(response,
                        no_cache=True,
                        no_store=True,
                        must_revalidate=True)
    return response