Skip to content

Commit

Permalink
nevent/nprofile
Browse files Browse the repository at this point in the history
  • Loading branch information
hoytech committed Dec 20, 2024
1 parent 9bca396 commit 863edcf
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
40 changes: 38 additions & 2 deletions src/apps/web/Bech32Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,51 @@ inline std::string encodeBech32Simple(const std::string &hrp, std::string_view v
return bech32::encode(hrp, values5, bech32::Encoding::BECH32);
}

inline std::string decodeBech32Simple(std::string_view v) {
inline std::string decodeBech32Raw(std::string_view v) {
auto res = bech32::decode(std::string(v));

if (res.encoding == bech32::Encoding::INVALID) throw herr("invalid bech32");
else if (res.encoding == bech32::Encoding::BECH32M) throw herr("got bech32m");

std::vector<uint8_t> out;
if (!convertbits<5, 8, false>(out, res.data)) throw herr("convertbits failed");
if (out.size() != 32) throw herr("unexpected size from bech32");

return std::string((char*)out.data(), out.size());
}

inline std::string decodeBech32Simple(std::string_view v) {
auto out = decodeBech32Raw(v);
if (out.size() != 32) throw herr("unexpected size from bech32");
return out;
}

inline uint8_t getByte(std::string_view &encoded) {
if (encoded.size() < 1) throw herr("parse ends prematurely");
uint8_t output = encoded[0];
encoded = encoded.substr(1);
return output;
}

inline std::string getBytes(std::string_view &encoded, size_t n) {
if (encoded.size() < n) throw herr("parse ends prematurely");
auto res = encoded.substr(0, n);
encoded = encoded.substr(n);
return std::string(res);
};

inline std::string decodeBech32GetSpecial(std::string_view origStr) {
auto decoded = decodeBech32Raw(origStr);
std::string_view s(decoded);

while (s.size()) {
auto tag = getByte(s);
auto len = getByte(s);
auto val = getBytes(s, len);
if (tag == 0) {
if (len != 32) throw herr("unexpected size from bech32 special");
return std::string(val);
}
}

throw herr("no special tag found");
}
3 changes: 0 additions & 3 deletions src/apps/web/TODO
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
read
* support nprofile/nevent/etc links
example nevent: https://oddbean.com/e/note1qmye0at28we63aze93xjr92nzw725td0a5ncz3htwlc3wg78kp6q7802ad
example nprofile: https://oddbean.com/e/note1ykjalrpaj6jvxeuc434yd7ksrj8yd2vte478700ta8np250l3clsyjvh4q
* non-500 error pages when bech32 fails to parse, for example
* search field: enter anything, pubkey (hex or npub), eventId, etc. maybe even full-text search?
* rss
Expand Down
30 changes: 26 additions & 4 deletions src/apps/web/WebData.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ struct Event {
}

void preprocessEventContent(lmdb::txn &txn, Decompressor &decomp, UserCache &userCache, std::string &content, bool withLinks = true) const {
static RE2 matcher(R"((?is)(.*?)(https?://\S+|#\[\d+\]|nostr:(?:note|npub)1\w+))");
static RE2 matcher(R"((?is)(.*?)(https?://\S+|#\[\d+\]|nostr:(?:note|npub|nevent|nprofile)1\w+))");

std::string output;

Expand Down Expand Up @@ -367,16 +367,38 @@ struct Event {
std::string path = "/e/";
path += sv(match).substr(6);
appendLink(path, sv(match));
} else if (match.starts_with("nostr:nevent1")) {
bool didTransform = false;

try {
auto id = decodeBech32GetSpecial(sv(match).substr(6));
std::string path = "/e/";
path += encodeBech32Simple("note", id);

appendLink(path, sv(match));
didTransform = true;
} catch(...) {}

if (!didTransform) output += sv(match);
} else if (match.starts_with("nostr:npub1")) {
bool didTransform = false;

try {
const auto *u = userCache.getUser(txn, decomp, decodeBech32Simple(sv(match).substr(6)));
appendLink(std::string("/u/") + u->npubId, std::string("@") + u->username);
didTransform = true;
} catch(std::exception &e) {
//LW << "tag parse error: " << e.what();
}
} catch(...) {}

if (!didTransform) output += sv(match);
} else if (match.starts_with("nostr:nprofile")) {
bool didTransform = false;

try {
auto pubkey = decodeBech32GetSpecial(sv(match).substr(6));
const auto *u = userCache.getUser(txn, decomp, pubkey);
appendLink(std::string("/u/") + u->npubId, std::string("@") + u->username);
didTransform = true;
} catch(...) {}

if (!didTransform) output += sv(match);
} else if (match.starts_with("#[")) {
Expand Down
4 changes: 3 additions & 1 deletion src/apps/web/bech32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace bech32
namespace
{

const size_t BECH32_MAX_SIZE = 5000;

typedef std::vector<uint8_t> data;

/** The Bech32 character set for encoding. */
Expand Down Expand Up @@ -204,7 +206,7 @@ DecodeResult decode(const std::string& str) {
}
if (lower && upper) return {};
size_t pos = str.rfind('1');
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
if (str.size() > BECH32_MAX_SIZE || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
return {};
}
data values(str.size() - 1 - pos);
Expand Down

0 comments on commit 863edcf

Please sign in to comment.