Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 55 additions & 14 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from cryptography.fernet import Fernet
import base64
import ipaddress
import random

logging.config.dictConfig(settings.log_config)
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -129,7 +130,7 @@ def decrypt(message, salt=settings.salt):
f = Fernet(key)
except ValueError:
return False
return f.decrypt(message.decode())
return f.decrypt(message)


def connect(db=settings.db):
Expand Down Expand Up @@ -271,6 +272,7 @@ def add_message():
return {"status": "Failure", "error": ["Bad characters in payload"]}

display_salt = False
pin = None
salt = settings.salt
if "salt" in request_body:
if request_body["salt"]:
Expand All @@ -290,7 +292,11 @@ def add_message():
return {"status": "Failure", "error": ["TTL must be a positive integer"]}
if ttl > 604800:
ttl = 604800

require_pin = request_body.get("requirePin", False)
if require_pin:
pin = f"{random.randint(0, 9999):04d}"
logger.info(f"PIN: {pin}")
logger.info(request_body)

try:
r = connect()
Expand All @@ -303,9 +309,12 @@ def add_message():
}

key = generate_key()
encrypted_message = encrypt(message, salt)
encrypted_message = encrypt(message, salt).decode("utf-8")
logger.info(f"Encrypted message: {encrypted_message}")
db_message = json.dumps({"message": encrypted_message, "pin": pin})
logger.info(f"json message: {json.dumps(db_message)}")

if update_redis(key, encrypted_message, ttl):
if update_redis(key, db_message, ttl):
logger.info(f"Message created successfully.")
bottle.response.status = 200
if display_salt:
Expand All @@ -315,6 +324,7 @@ def add_message():
return {
"status": "Success",
"message": {
"pin": pin,
"link": link,
"key": key,
**({"salt": salt} if display_salt else {}),
Expand Down Expand Up @@ -367,14 +377,17 @@ def display_message():
)

link = bottle.request.query.link
try:
salt = bottle.request.query.salt
if not salt:
# Check if salt is empty string
salt = settings.salt
except:
if "salt" in bottle.request.query:
salt = bottle.request.query.get("salt", settings.salt)
else:
salt = settings.salt

if "pin" in bottle.request.query:
pin = bottle.request.query.get("pin", None)
else:
pin = None


if not link:
"""
If no link is provided, display the welcome message.
Expand Down Expand Up @@ -425,10 +438,8 @@ def display_message():
_help=False
)

message = r.get(link)
ttl = r.ttl(link)

if not message:
db_message = r.get(link)
if not db_message:
"""
If the message is not found, display an error message.
"""
Expand Down Expand Up @@ -458,6 +469,36 @@ def display_message():
_help=False
)

logger.info(json.loads(db_message.decode("utf-8")))
json_message = json.loads(db_message.decode("utf-8"))
message = json_message["message"]
require_pin = json_message["pin"]
if require_pin:
if not pin:
return bottle.template(
"index.html",
generic_message="PIN required",
salt=salt,
link=link,
pin=True,
message="Please provide a PIN to access this message.",
company=settings.company,
_help=False
)

if require_pin != pin:
bottle.response.status = 401
generic_message = "Wrong PIN"
return bottle.template(
"index.html",
generic_message=generic_message,
message="Provide a valid PIN to access this message, go back and try again",
company=settings.company,
_help=False
)

ttl = r.ttl(link)

if salt:
try:
decrypted_message = decrypt(message, salt=salt)
Expand Down
16 changes: 15 additions & 1 deletion static/script/script.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
document.addEventListener("DOMContentLoaded", function () {
const submitButton = document.getElementById("submit-button");
const submitPinButton = document.getElementById("pin-button");
const submitMessage = document.getElementById("submit-message");
const deniedMessage = document.getElementById("denied-message");
const copyButton = document.getElementById("copy-button");
const messageInput = document.getElementById("message-input");
const saltInput = document.getElementById("salt-input");
const ttlInput = document.getElementById("ttl-input");
const linkElement = document.getElementById("link");
const pinElement = document.getElementById("pin");
const pinElementCheck = document.getElementById("pin-check");
const techHelpToggler = document.getElementById("tht");
const shortMain = document.getElementById("main-short");
const shortTech = document.getElementById("tech-short");
Expand All @@ -15,11 +18,13 @@ document.addEventListener("DOMContentLoaded", function () {
const initialMessageValue = messageInput.value;
const initialButtonText = copyButton.innerHTML;

pinElementCheck.checked = false;
techHelpToggler.checked = false
helpTech.style.display = "none";
deniedMessage.style.display = "none";
shortTech.style.display = "none";


techHelpToggler.addEventListener("change", function() {
if(techHelpToggler.checked) {
shortTech.style.display = "";
Expand All @@ -35,28 +40,37 @@ document.addEventListener("DOMContentLoaded", function () {

});


submitButton.addEventListener("click", async () => {
try {
const message = messageInput.value;
const salt = saltInput.value;
const ttl = ttlInput.value;
const newSalt = await generateSalt();
const requirePin = pinElementCheck.checked;

const response = await fetch(document.documentURI, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ message, salt, ttl })
body: JSON.stringify({ message, salt, ttl, requirePin })
});

if (!response.ok) {
throw new Error("Network response was not ok");
}

const data = await response.json();
console.log("Response:", data);
linkElement.innerHTML = data.message.link;
linkElement.href = data.message.link;
if (data.message.pin) {
pinElement.innerHTML = "PIN: " + data.message.pin;
}
else {
pinElement.innerHTML = "";
}
copyButton.hidden = false;
messageInput.value = initialMessageValue;
saltInput.value = newSalt;
Expand Down
40 changes: 40 additions & 0 deletions static/style/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,46 @@ a {
position: absolute;
top: 250px;
}
.require-pin {
display: none;
}
.pin-input-container {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin: 10px 0;
margin-bottom: 25px;
}
.pin-input {
width: 40px;
height: 40px;
text-align: center;
font-size: 18px;
font-family: monospace;
border: 2px solid #ccc;
border-radius: 5px;
outline: none;
transition: border-color 0.3s, box-shadow 0.3s;
}

.pin-input:focus {
border-color: #007bff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}

.pin-input:hover {
border-color: #999;
}

.pin-input:invalid {
border-color: #ff4d4d;
}

.pin-message {
text-align: center;
margin-top: 75px;
}
.message {
max-width: 750px;
min-height: 300px;
Expand Down
68 changes: 67 additions & 1 deletion views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
% setdefault('submit', False)
% setdefault('_help', True)
% setdefault('ttl', 3600)
% setdefault('pin', False)
<!-- <a class="github-fork-ribbon" href="https://github.com/axians/otm" data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a> -->
<div class="top-container">
<div class="brand-logo left"></div>
Expand Down Expand Up @@ -50,7 +51,66 @@ <h3 class="generic-message">{{generic_message}}</h3>
<pre>{{message}}</pre>
</div>
% else:
<pre>{{message}}</pre>
% if pin:
<div id="pin-message" class="pin-message">
<div class="input-header optional">
<span>Enter your PIN Code</span>
</div>
<div class="pin-outer-container">
<div class="pin-input-container">
<input class="pin-input" type="text" maxlength="1" pattern="[0-9]" id="pin1" autofocus />
<input class="pin-input" type="text" maxlength="1" pattern="[0-9]" id="pin2" />
<input class="pin-input" type="text" maxlength="1" pattern="[0-9]" id="pin3" />
<input class="pin-input" type="text" maxlength="1" pattern="[0-9]" id="pin4" />
</div>
<div class="pin-submit-container">
<button class="input-button" id="pin-button" type="button">Submit</button>
</div>
<script>
console.log("Pin button script loaded");
// Handle auto-focus for PIN inputs
const pinInputs = document.querySelectorAll(".pin-input");
pinInputs.forEach((input, index) => {
input.addEventListener("input", () => {
if (input.value.match(/^[0-9]$/)) {
if (index < pinInputs.length - 1) {
pinInputs[index + 1].focus();
}
} else {
input.value = ""; // Clear non-digit input
}
});
input.addEventListener("keydown", (e) => {
if (e.key === "Backspace" && !input.value && index > 0) {
pinInputs[index - 1].focus();
}
});
});
// Handle pin-button click
document.getElementById("pin-button").addEventListener("click", function() {
const pin = Array.from(pinInputs).map(input => input.value).join("");
if (pin.length === 4 && pin.match(/^[0-9]{4}$/)) {
const path = window.location.pathname;
const params = new URLSearchParams(window.location.search);
params.append("pin", pin);
window.location.assign(`${path}?${params.toString()}`);
} else {
const deniedMessage = document.getElementById("denied-message");
if (deniedMessage) {
deniedMessage.style.display = "block";
deniedMessage.innerHTML = "Please enter a valid 4-digit PIN.";
setTimeout(() => {
deniedMessage.style.display = "none";
}, 3000);
}
}
});
</script>
</div>
</div>
% else:
<pre>{{message}}</pre>
% end
% end
% if submit:
<div id="denied-message" class="denied-message" >
Expand All @@ -66,6 +126,9 @@ <h3 class="generic-message">{{generic_message}}</h3>
</div>
<div class="input-container">
<textarea class="input-field" id="message-input" type="text" placeholder="Enter your message here"></textarea>
<div class="require-pin">
<span><input id="pin-check" type="checkbox" name="option" value="option1"> Pin?</span>
</div>
</div>
<div class="input-header optional">
<span>Salt <i>(Optional)</i></span>
Expand All @@ -88,6 +151,9 @@ <h3 class="generic-message">{{generic_message}}</h3>
<a href="#" id="link" style="pointer-events:none;"></a>
<button id="copy-button" class="clip-button" hidden="true"><i class="fas fa-clipboard"></i></button>
</div>
<div class="link-container">
<a href="#" id="pin" style="pointer-events:none;"></a>
</div>
</div>
% end

Expand Down