示例#1
0
    def wrapped(request: Request, **kwargs) -> Response:
        if "REMOTE_USER" not in request.environ:
            msg = "Attach your client certificate to continue."
            return Response(Status.CLIENT_CERTIFICATE_REQUIRED, msg)

        if request.environ["TLS_CLIENT_AUTHORISED"]:
            # Old-style verified certificate
            serial_number = request.environ["TLS_CLIENT_SERIAL_NUMBER"]
            fingerprint = f"{serial_number:032X}"  # Convert to hex
        else:
            # New-style self signed certificate
            fingerprint = typing.cast(str, request.environ["TLS_CLIENT_HASH_B64"])

        cert = User.login(fingerprint)
        if cert is None:
            body = render_template(
                "register.gmi",
                request=request,
                fingerprint=fingerprint,
                cert=request.environ["client_certificate"],
            )
            return Response(Status.SUCCESS, "text/gemini", body)

        request = AuthenticatedRequest(request.environ, cert)
        response = func(request, **kwargs)
        return response
示例#2
0
def docs(request, module, page=1):
    page = int(page)

    if module not in modules:
        return Response(Status.NOT_FOUND, f"Invalid module {module}")

    help_text_lines = modules[module].splitlines()

    page_count = math.ceil(len(help_text_lines) / paginate_by)
    page_count = max(page_count, 1)
    if page > page_count:
        return Response(Status.NOT_FOUND, "Invalid page number")

    offset = (page - 1) * paginate_by
    items = help_text_lines[offset:offset + paginate_by]

    lines = [f"[{module}]", f"(page {page} of {page_count})", ""]
    lines.extend(items)
    lines.append("")
    if page < page_count:
        lines.append(f"=>/docs/{module}/p/{page+1} Next {paginate_by} lines")
    if page > 1:
        lines.append(f"=>/docs/{module} Back to the beginning")

    return Response(Status.SUCCESS, "text/gemini", "\n".join(lines))
示例#3
0
def pond_tribute_view(request, color: str):
    pond = Pond(request.user)
    if color not in pond.petal_map:
        return Response(Status.BAD_REQUEST, "Not Found")

    request.session["alert"] = pond.tribute(color)
    return Response(Status.REDIRECT_TEMPORARY, "/app/pond/")
示例#4
0
    def callback(request: AstrobotanyRequest, **kwargs):

        if "REMOTE_USER" not in request.environ:
            if request.path != "/app":
                # Redirect the user to the correct "path scope" first
                return Response(Status.REDIRECT_TEMPORARY, "/app")
            else:
                msg = "Attach your client certificate to continue."
                return Response(Status.CLIENT_CERTIFICATE_REQUIRED, msg)

        if request.environ["TLS_CLIENT_AUTHORISED"]:
            # Old-style verified certificate
            serial_number = request.environ["TLS_CLIENT_SERIAL_NUMBER"]
            fingerprint = f"{serial_number:032X}"  # Convert to hex
        else:
            # New-style self signed certificate
            fingerprint = typing.cast(str,
                                      request.environ["TLS_CLIENT_HASH_B64"])

        cert = User.login(fingerprint)
        if cert is None:
            body = render_template(
                "register.gmi",
                request=request,
                fingerprint=fingerprint,
                cert=request.environ["client_certificate"],
            )
            return Response(Status.SUCCESS, "text/gemini", body)

        request = AstrobotanyRequest(request.environ, cert)
        request.plant.refresh()
        response = func(request, **kwargs)
        request.plant.save()
        return response
示例#5
0
def synth_listen_view(request):
    song = request.user.get_song()
    if not song:
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    synthesizer = Synthesizer.from_song(song)
    data = synthesizer.get_raw_data()
    return Response(Status.SUCCESS, "audio/ogg", data)
示例#6
0
def mailbox_compose(request, postcard_id):
    postcard = items.Postcard.lookup(postcard_id)
    if postcard is None:
        return Response(Status.NOT_FOUND, "Postcard was not found")

    data = PostcardData.from_request(request)
    body = render_template("mailbox_compose.gmi", request=request, postcard=postcard, data=data)
    return Response(Status.SUCCESS, "text/gemini", body)
示例#7
0
def mailbox_compose_subject(request, postcard_id):
    subject = request.query
    if not subject:
        return Response(Status.INPUT, f"Enter subject:")

    data = PostcardData.from_request(request)
    data.subject = subject
    return Response(Status.REDIRECT_TEMPORARY, f"/app/mailbox/outgoing/{postcard_id}")
示例#8
0
def name(request):
    if not request.query:
        return Response(Status.INPUT, "Enter a new nickname for your plant:")

    request.plant.name = request.query[:40]
    msg = f'Your plant shall henceforth be known as "{request.plant.name}".'
    request.session["alert"] = msg
    return Response(Status.REDIRECT_TEMPORARY, "/app/plant")
示例#9
0
def proxy_request(request):
    command = [b"w3m", b"-dump", request.url.encode()]
    try:
        out = subprocess.run(command, stdout=subprocess.PIPE)
        out.check_returncode()
    except Exception:
        return Response(Status.CGI_ERROR, "Failed to load URL")
    else:
        return Response(Status.SUCCESS, "text/plain", out.stdout)
示例#10
0
def submit(request):
    if request.query:
        message = request.query[:256]
        created = datetime.now()
        ip_address = request.environ["REMOTE_HOST"]
        values = (ip_address, created, message)
        db.execute("INSERT INTO guestbook VALUES (?, ?, ?)", values)
        return Response(Status.REDIRECT_TEMPORARY, "")
    else:
        return Response(Status.INPUT, "Enter your message (max 256 characters)")
示例#11
0
def submit(request):
    if request.query:
        message = request.query[:256]
        created = datetime.utcnow()
        with guestbook.open("a") as fp:
            fp.write(f"\n[{created:%Y-%m-%d %I:%M %p}]\n{message}\n")

        return Response(Status.REDIRECT_TEMPORARY, "")
    else:
        return Response(Status.INPUT, "Enter your message (max 256 characters)")
示例#12
0
def synth_beat_view(request, beat_str: str):
    beat = int(beat_str)

    song = request.user.get_song()
    if not song:
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    synthesizer = Synthesizer.from_song(song)
    body = request.render_template("synth_note.gmi", synthesizer=synthesizer, beat=beat)
    return Response(Status.SUCCESS, "text/gemini", body)
示例#13
0
def mailbox_compose_line(request, postcard_id, line_number):
    line = request.query
    if not line:
        return Response(
            Status.INPUT, f"Enter message line {line_number} (or submit a blank space to erase):"
        )

    data = PostcardData.from_request(request)
    data.lines[int(line_number) - 1] = line.rstrip()
    return Response(Status.REDIRECT_TEMPORARY, f"/app/mailbox/outgoing/{postcard_id}")
示例#14
0
def mailbox_view(request, message_id):
    message = Inbox.get_or_none(id=message_id, user_to=request.user)
    if message is None:
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    message.is_seen = True
    message.save()

    body = render_template("mailbox_view.gmi", request=request, message=message)
    return Response(Status.SUCCESS, "text/gemini", body)
示例#15
0
def xmas_view(request):
    plant = request.user.plant
    plant.refresh()

    if not plant.can_use_christmas_cheer():
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    request.session["alert"] = plant.use_christmas_cheer()
    plant.save()

    return Response(Status.REDIRECT_TEMPORARY, "/app/plant")
示例#16
0
def search_view(request):
    plant = request.user.plant
    plant.refresh()

    if not plant.can_search():
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    request.session["alert"] = plant.pick_petal()
    plant.save()

    return Response(Status.REDIRECT_TEMPORARY, "/app/plant")
示例#17
0
def message_board_submit(request):
    if not request.query:
        return Response(Status.INPUT, "What would you like to say? ")

    rate_limit_resp = message_rate_limiter.check(request)
    if rate_limit_resp:
        return rate_limit_resp

    message = Message(user=request.user, text=request.query)
    message.save()
    return Response(Status.REDIRECT_TEMPORARY, "/app/message-board")
示例#18
0
def visit_plant_water(request, user_id):
    user = User.get_or_none(user_id=user_id)
    if user is None:
        return Response(Status.NOT_FOUND, "User not found")
    elif request.user == user:
        return Response(Status.REDIRECT_TEMPORARY, "/app/plant")

    user.plant.refresh()
    request.session["alert"] = user.plant.water(request.user)
    user.plant.save()

    return Response(Status.REDIRECT_TEMPORARY, f"/app/visit/{user_id}")
示例#19
0
def song_view(request):
    plant = request.user.plant
    plant.refresh()

    if not plant.can_play_song():
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    text = "You play the tune that you wrote for your plant."
    link = "=> /app/plant/song/audio.ogg Listen (download audio)"
    request.session["alert"] = f"{text}\n{link}"

    return Response(Status.REDIRECT_TEMPORARY, "/app/plant")
示例#20
0
def visit_plant(request, user_id):
    user = User.get_or_none(user_id=user_id)
    if user is None:
        return Response(Status.NOT_FOUND, "User not found")
    elif request.user == user:
        return Response(Status.REDIRECT_TEMPORARY, "/app/plant")

    user.plant.refresh()
    user.plant.save()

    alert = request.session.pop("alert", None)
    body = render_template("visit_plant.gmi", request=request, plant=user.plant, alert=alert)
    return Response(Status.SUCCESS, "text/gemini", body)
示例#21
0
def mailbox_preview(request, postcard_id):
    postcard = items.Postcard.lookup(postcard_id)
    if postcard is None:
        return Response(Status.NOT_FOUND, "Postcard was not found")

    data = PostcardData.from_request(request)
    if data.user is None:
        return Response(Status.BAD_REQUEST, "Cannot proceed without a user defined")
    if not data.subject:
        return Response(Status.BAD_REQUEST, "Cannot proceed without a subject defined")

    body = render_template("mailbox_preview.gmi", request=request, postcard=postcard, data=data)
    return Response(Status.SUCCESS, "text/gemini", body)
示例#22
0
def plant_rename_view(request):
    plant = request.user.plant
    plant.refresh()

    if not request.query:
        return Response(Status.INPUT, "Enter a new nickname for your plant:")

    plant.name = request.query[:40]
    msg = f'Your plant shall henceforth be known as "{plant.name}".'
    request.session["alert"] = msg
    plant.save()

    return Response(Status.REDIRECT_TEMPORARY, "/app/plant")
示例#23
0
def message_board(request, page=1):
    page = int(page)
    paginate_by = 10
    page_count = int(math.ceil(Message.select().count() / paginate_by))
    page_count = max(page_count, 1)
    if page > page_count:
        return Response(Status.NOT_FOUND, "Invalid page number")

    items = Message.by_date().paginate(page, paginate_by)

    body = render_template(
        "message_board.gmi", request=request, items=items, page=page, page_count=page_count,
    )
    return Response(Status.SUCCESS, "text/gemini", body)
示例#24
0
def visit_song_audio_view(request, user_id: str):
    user = User.get_or_none(User.user_id == user_id)
    if user is None:
        return Response(Status.NOT_FOUND, "Not Found")
    elif user == request.user:
        return Response(Status.REDIRECT_TEMPORARY, "/app/plant")

    song = user.get_song()
    if not song:
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    synthesizer = Synthesizer.from_song(song)
    data = synthesizer.get_raw_data()
    return Response(Status.SUCCESS, "audio/ogg", data)
示例#25
0
def synth_note_view(request, beat_str: str, note_str: str):
    beat = int(beat_str)
    note = int(note_str)

    song = request.user.get_song()
    if not song:
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    data = song.get_data()
    data["notes"][beat] = Synthesizer.note_char_map[note]
    song.set_data(data)
    song.save()

    return Response(Status.REDIRECT_TEMPORARY, "/app/synth")
示例#26
0
def settings_emoji_mode_view(request):
    if not request.query:
        prompt = f"Set emoji display mode (0/1/2): "
        return Response(Status.INPUT, prompt)

    answer = request.query.strip()

    if answer in ("0", "1", "2"):
        request.cert.emoji_mode = int(answer)
        request.cert.save()
    else:
        return Response(Status.BAD_REQUEST, f"Invalid query value: {request.query}")

    return Response(Status.REDIRECT_TEMPORARY, "/app/settings")
示例#27
0
def mailbox_compose_item_view(request, postcard_id, item_id=None):
    postcard = items.Postcard.lookup(postcard_id)
    if postcard is None:
        return Response(Status.NOT_FOUND, "Postcard was not found")

    if item_id is None:
        item_slots = [slot for slot in request.user.inventory if slot.item.can_gift(request.user)]
        item_slots.sort(key=lambda x: x.item.name)
        body = request.render_template("mailbox_item.gmi", postcard=postcard, item_slots=item_slots)
        return Response(Status.SUCCESS, "text/gemini", body)

    data = PostcardData.from_request(request)
    data.item = items.Item.lookup(item_id)
    return Response(Status.REDIRECT_TEMPORARY, f"/app/mailbox/outgoing/{postcard_id}")
示例#28
0
def register_existing(request, user_id=None):
    if "REMOTE_USER" not in request.environ:
        msg = "Attach your client certificate to continue."
        return Response(Status.CLIENT_CERTIFICATE_REQUIRED, msg)

    fingerprint = request.environ["TLS_CLIENT_HASH_B64"]
    if Certificate.select().where(
            Certificate.fingerprint == fingerprint).exists():
        msg = "This certificate has already been linked to an account."
        return Response(Status.CERTIFICATE_NOT_AUTHORISED, msg)

    if user_id is None:
        username = request.query
        if not username:
            msg = "Enter your existing username"
            return Response(Status.INPUT, msg)

        try:
            user = User.select().where(User.username == username).get()
        except User.DoesNotExist:
            msg = f"No existing user was found with the name '{username}'."
            return Response(Status.BAD_REQUEST, msg)

        return Response(Status.REDIRECT_TEMPORARY,
                        f"/app/register-existing/{user.id}")

    user = User.get_by_id(int(user_id))
    if not user.password:
        msg = "Unable to add a certificate because this account does not have a password set."
        return Response(Status.BAD_REQUEST, msg)

    password = request.query
    if not password:
        msg = "Enter your password"
        return Response(Status.SENSITIVE_INPUT, msg)

    rate_limit_resp = password_failed_rate_limiter.check(request)
    if rate_limit_resp:
        return rate_limit_resp

    if not user.check_password(password):
        msg = "Invalid password, try again"
        return Response(Status.SENSITIVE_INPUT, msg)

    cert = request.environ["client_certificate"]
    Certificate.create(
        user=user,
        fingerprint=fingerprint,
        subject=cert.subject.rfc4514_string(),
        not_valid_before_utc=cert.not_valid_before,
        not_valid_after_utc=cert.not_valid_after,
    )

    return Response(Status.REDIRECT_TEMPORARY, "/app")
示例#29
0
def inventory_view(request, item_slot_id):
    item_slot_id = int(item_slot_id)
    try:
        item_slot = ItemSlot.get_by_id(item_slot_id)
    except ItemSlot.DoesNotExist:
        return Response(Status.NOT_FOUND, "Not Found")

    if not item_slot.user == request.user:
        return Response(Status.NOT_FOUND, "Not Found")

    description = item_slot.item.get_inventory_description(request.user)
    body = render_template(
        "inventory_view.gmi", request=request, item_slot=item_slot, description=description
    )
    return Response(Status.SUCCESS, "text/gemini", body)
示例#30
0
def visit_plant_search(request, user_id):
    user = User.get_or_none(user_id=user_id)
    if user is None:
        return Response(Status.NOT_FOUND, "User not found")
    elif request.user == user:
        return Response(Status.REDIRECT_TEMPORARY, "/app/plant")

    if user.plant.dead or user.plant.stage_str != "flowering":
        return Response(Status.BAD_REQUEST, "You shouldn't be here!")

    user.plant.refresh()
    request.session["alert"] = user.plant.pick_petal(request.user)
    user.plant.save()

    return Response(Status.REDIRECT_TEMPORARY, f"/app/visit/{user_id}")