From c1491b044ba48a0aeab0a5970703b4f9e13fbc1b Mon Sep 17 00:00:00 2001 From: Brad Arant Date: Sun, 6 Sep 2026 04:14:00 +0000 Subject: [PATCH] Add IMAP webmail tag, admin/opensource subpage tiers, store/cart, htmleditor, uploads New __imap*/__smtpsend/__eval Jet tags give templates real IMAP (list/message/flag/delete/move/search/folders/accounts) and SMTP-send capability, backing the new /email webmail client (email.jet/ email.menu, assets/images/email.js + newemail.js) and its mainpagetemplate.jet-based page shell. Most of this tag family's source was already being built into the live site (Tag.cpp already registered __imap/__imaplist/__imapmessage/__imapflag/__imapdelete/ __eval) but had never actually been committed; this also registers and commits the remaining five (__imapmove/__imapfolders/ __imapsearch/__smtpsend/__imapaccounts). admin.page/admin.menu gain a real subpage tier (admin-email, admin-newemail, admin-invoices, admin-products, admin-users) replacing several previously top-level pages, mirrored by a similar opensource.page subpage tier (opensource-overview/asm1802/barantmail/ jet/serverframework/synthmax) - old top-level pages retired via git rename (asm1802.page, jet.page, serverframework.page, admin_invoices.page); barantmail.page is dropped outright, folded into opensource-barantmail.subpage. New storefront (store.page, store-createstore.subpage, barantstore.page) and cart flow (cart.jet, updatecartqty.proc, removecartitem.proc, editproduct.page/selectproduct.proc/ update_product.proc for inventory maintenance against the barant database). New document upload/download manager (documents.jet, documents_api.jet, documents_upload.proc, documents_delete.proc, assets/images/documents.js + documents-page.js) with a persistent floating panel, and a rich-text page editor (htmleditor.jet/.page, createhtmleditor.proc, savehtmleditor.proc, assets/images/htmleditor.js + its icon/logo assets). SESSIONCTL.tag: login now redirects back to the originally-requested page instead of always landing on /account ($[start:0] vs $[start] variable-name mismatch). main.css/menu.bar/invoices.menu/jet_overview.section/login.menu: styling and navigation updates for the above; new site assets (fonts, images, audio) referenced by them. jetc/build-assets.sh, list_jet_files.sh, lowercase_jet.py, strip_fragments.py: small dev tooling used while building the above. --- .gitignore | 2 + Tag.cpp | 20 + __eval.cpp | 43 + __eval.h | 21 + __imap.cpp | 1100 +++++++++++++++++ __imap.h | 112 ++ __imapaccounts.cpp | 139 +++ __imapaccounts.h | 59 + __imapdelete.cpp | 25 + __imapdelete.h | 25 + __imapflag.cpp | 32 + __imapflag.h | 26 + __imapfolders.cpp | 16 + __imapfolders.h | 27 + __imaplist.cpp | 26 + __imaplist.h | 30 + __imapmessage.cpp | 25 + __imapmessage.h | 25 + __imapmove.cpp | 29 + __imapmove.h | 29 + __imapsearch.cpp | 22 + __imapsearch.h | 27 + __smtpsend.cpp | 220 ++++ __smtpsend.h | 69 ++ .../assets/images/AvenirLTStd-Light.ttf | Bin 0 -> 27272 bytes .../assets/images/Exo2-RegularCondensed.otf | Bin 0 -> 65404 bytes .../assets/images/ServerCoreMap.png | Bin 0 -> 24477 bytes jetc/barant-site/assets/images/arant2.jpg | Bin 0 -> 13319 bytes jetc/barant-site/assets/images/back.png | Bin 0 -> 3945 bytes .../assets/images/barant_web_logo.png | Bin 0 -> 49080 bytes jetc/barant-site/assets/images/beep-07a.wav | Bin 0 -> 9702 bytes jetc/barant-site/assets/images/beep-08b.wav | Bin 0 -> 11968 bytes .../assets/images/blank_collection_cover.png | Bin 0 -> 11767 bytes .../assets/images/documents-page.js | 96 ++ jetc/barant-site/assets/images/documents.js | 178 +++ jetc/barant-site/assets/images/edit.png | Bin 0 -> 4977 bytes jetc/barant-site/assets/images/email.js | 778 ++++++++++++ jetc/barant-site/assets/images/emu1802a.png | Bin 0 -> 135489 bytes jetc/barant-site/assets/images/htmleditor.js | 678 ++++++++++ .../assets/images/htmleditor_bold.png | Bin 0 -> 1235 bytes .../images/htmleditor_drinkwise_logo.png | Bin 0 -> 24355 bytes .../assets/images/htmleditor_dwbackground.png | Bin 0 -> 245730 bytes .../assets/images/htmleditor_jc.png | Bin 0 -> 488 bytes .../assets/images/htmleditor_jj.png | Bin 0 -> 457 bytes .../assets/images/htmleditor_jl.png | Bin 0 -> 470 bytes .../assets/images/htmleditor_jr.png | Bin 0 -> 492 bytes .../assets/images/htmleditor_redo.png | Bin 0 -> 1083 bytes .../assets/images/htmleditor_underline.png | Bin 0 -> 1092 bytes .../assets/images/htmleditor_undo.png | Bin 0 -> 1177 bytes .../assets/images/invoice_logo.png | Bin 0 -> 16079 bytes jetc/barant-site/assets/images/jackinput.TTF | Bin 0 -> 54264 bytes jetc/barant-site/assets/images/main.css | 157 ++- jetc/barant-site/assets/images/modular1_0.png | Bin 0 -> 834318 bytes jetc/barant-site/assets/images/newemail.js | 76 ++ jetc/barant-site/assets/images/organ1.png | Bin 0 -> 6991 bytes jetc/barant-site/assets/images/power_on.png | Bin 0 -> 7380 bytes .../assets/images/rca1802chip.jpeg | Bin 0 -> 2201 bytes jetc/barant-site/assets/images/sawtooth.png | Bin 0 -> 100890 bytes jetc/barant-site/assets/images/spa.js | 322 +++++ jetc/barant-site/assets/images/spinning.png | Bin 0 -> 176678 bytes jetc/barant-site/assets/images/square.png | Bin 0 -> 53409 bytes jetc/barant-site/assets/images/triangle.png | Bin 0 -> 56796 bytes .../barant-site/assets/images/trytryagain.mp3 | Bin 0 -> 4628160 bytes .../assets/images/trytryagain6.mp3 | Bin 0 -> 7681920 bytes jetc/barant-site/list_jet_files.sh | 20 + jetc/barant-site/src/SESSIONCTL.tag | 8 +- jetc/barant-site/src/account.jet | 16 + jetc/barant-site/src/admin-email.subpage | 138 +++ ...n_invoices.page => admin-invoices.subpage} | 9 - jetc/barant-site/src/admin-newemail.subpage | 81 ++ jetc/barant-site/src/admin-products.subpage | 4 + jetc/barant-site/src/admin-users.subpage | 4 + jetc/barant-site/src/admin.jet | 16 + jetc/barant-site/src/admin.menu | 9 + jetc/barant-site/src/admin.page | 48 +- jetc/barant-site/src/barantmail.page | 23 - jetc/barant-site/src/barantstore.page | 42 + jetc/barant-site/src/cart.jet | 16 + jetc/barant-site/src/createhtmleditor.proc | 8 + jetc/barant-site/src/documents.jet | 2 + jetc/barant-site/src/documents.page | 35 +- jetc/barant-site/src/documents_api.jet | 52 + jetc/barant-site/src/documents_delete.proc | 11 + jetc/barant-site/src/documents_upload.proc | 10 + jetc/barant-site/src/editproduct.page | 31 + jetc/barant-site/src/email.jet | 178 +++ jetc/barant-site/src/email.menu | 17 + jetc/barant-site/src/htmleditor.jet | 16 + jetc/barant-site/src/htmleditor.page | 154 +++ jetc/barant-site/src/invoices.menu | 2 +- jetc/barant-site/src/jet_overview.section | 60 +- jetc/barant-site/src/login.jet | 16 + jetc/barant-site/src/login.menu | 2 +- jetc/barant-site/src/main.menu | 7 + jetc/barant-site/src/mainpagetemplate.jet | 149 +++ jetc/barant-site/src/menu.bar | 6 +- jetc/barant-site/src/modules.page | 27 + ...sm1802.page => opensource-asm1802.subpage} | 8 +- .../src/opensource-barantmail.subpage | 53 + .../src/{jet.page => opensource-jet.subpage} | 7 - .../src/opensource-overview.subpage | 49 + ...age => opensource-serverframework.subpage} | 7 +- .../src/opensource-synthmax.subpage | 5 + jetc/barant-site/src/opensource.menu | 11 +- jetc/barant-site/src/opensource.page | 60 +- jetc/barant-site/src/removecartitem.proc | 1 + jetc/barant-site/src/savehtmleditor.proc | 15 + jetc/barant-site/src/selectproduct.proc | 3 + .../barant-site/src/store-createstore.subpage | 12 + jetc/barant-site/src/store.page | 10 + jetc/barant-site/src/update_product.proc | 5 + jetc/barant-site/src/updatecartqty.proc | 5 + jetc/build-assets.sh | 52 + jetc/lowercase_jet.py | 261 ++++ jetc/strip_fragments.py | 96 ++ 115 files changed, 6174 insertions(+), 137 deletions(-) create mode 100644 __eval.cpp create mode 100644 __eval.h create mode 100644 __imap.cpp create mode 100644 __imap.h create mode 100644 __imapaccounts.cpp create mode 100644 __imapaccounts.h create mode 100644 __imapdelete.cpp create mode 100644 __imapdelete.h create mode 100644 __imapflag.cpp create mode 100644 __imapflag.h create mode 100644 __imapfolders.cpp create mode 100644 __imapfolders.h create mode 100644 __imaplist.cpp create mode 100644 __imaplist.h create mode 100644 __imapmessage.cpp create mode 100644 __imapmessage.h create mode 100644 __imapmove.cpp create mode 100644 __imapmove.h create mode 100644 __imapsearch.cpp create mode 100644 __imapsearch.h create mode 100644 __smtpsend.cpp create mode 100644 __smtpsend.h create mode 100644 jetc/barant-site/assets/images/AvenirLTStd-Light.ttf create mode 100755 jetc/barant-site/assets/images/Exo2-RegularCondensed.otf create mode 100644 jetc/barant-site/assets/images/ServerCoreMap.png create mode 100644 jetc/barant-site/assets/images/arant2.jpg create mode 100644 jetc/barant-site/assets/images/back.png create mode 100644 jetc/barant-site/assets/images/barant_web_logo.png create mode 100644 jetc/barant-site/assets/images/beep-07a.wav create mode 100644 jetc/barant-site/assets/images/beep-08b.wav create mode 100644 jetc/barant-site/assets/images/blank_collection_cover.png create mode 100644 jetc/barant-site/assets/images/documents-page.js create mode 100644 jetc/barant-site/assets/images/documents.js create mode 100644 jetc/barant-site/assets/images/edit.png create mode 100644 jetc/barant-site/assets/images/email.js create mode 100644 jetc/barant-site/assets/images/emu1802a.png create mode 100644 jetc/barant-site/assets/images/htmleditor.js create mode 100644 jetc/barant-site/assets/images/htmleditor_bold.png create mode 100644 jetc/barant-site/assets/images/htmleditor_drinkwise_logo.png create mode 100644 jetc/barant-site/assets/images/htmleditor_dwbackground.png create mode 100644 jetc/barant-site/assets/images/htmleditor_jc.png create mode 100644 jetc/barant-site/assets/images/htmleditor_jj.png create mode 100644 jetc/barant-site/assets/images/htmleditor_jl.png create mode 100644 jetc/barant-site/assets/images/htmleditor_jr.png create mode 100644 jetc/barant-site/assets/images/htmleditor_redo.png create mode 100644 jetc/barant-site/assets/images/htmleditor_underline.png create mode 100644 jetc/barant-site/assets/images/htmleditor_undo.png create mode 100644 jetc/barant-site/assets/images/invoice_logo.png create mode 100644 jetc/barant-site/assets/images/jackinput.TTF create mode 100644 jetc/barant-site/assets/images/modular1_0.png create mode 100644 jetc/barant-site/assets/images/newemail.js create mode 100644 jetc/barant-site/assets/images/organ1.png create mode 100644 jetc/barant-site/assets/images/power_on.png create mode 100644 jetc/barant-site/assets/images/rca1802chip.jpeg create mode 100644 jetc/barant-site/assets/images/sawtooth.png create mode 100644 jetc/barant-site/assets/images/spa.js create mode 100644 jetc/barant-site/assets/images/spinning.png create mode 100644 jetc/barant-site/assets/images/square.png create mode 100644 jetc/barant-site/assets/images/triangle.png create mode 100644 jetc/barant-site/assets/images/trytryagain.mp3 create mode 100644 jetc/barant-site/assets/images/trytryagain6.mp3 create mode 100755 jetc/barant-site/list_jet_files.sh create mode 100644 jetc/barant-site/src/account.jet create mode 100644 jetc/barant-site/src/admin-email.subpage rename jetc/barant-site/src/{admin_invoices.page => admin-invoices.subpage} (61%) mode change 100755 => 100644 create mode 100644 jetc/barant-site/src/admin-newemail.subpage create mode 100644 jetc/barant-site/src/admin-products.subpage create mode 100644 jetc/barant-site/src/admin-users.subpage create mode 100644 jetc/barant-site/src/admin.jet create mode 100755 jetc/barant-site/src/admin.menu delete mode 100755 jetc/barant-site/src/barantmail.page create mode 100644 jetc/barant-site/src/barantstore.page create mode 100644 jetc/barant-site/src/cart.jet create mode 100644 jetc/barant-site/src/createhtmleditor.proc create mode 100644 jetc/barant-site/src/documents.jet create mode 100644 jetc/barant-site/src/documents_api.jet create mode 100644 jetc/barant-site/src/documents_delete.proc create mode 100644 jetc/barant-site/src/documents_upload.proc create mode 100644 jetc/barant-site/src/editproduct.page create mode 100644 jetc/barant-site/src/email.jet create mode 100644 jetc/barant-site/src/email.menu create mode 100644 jetc/barant-site/src/htmleditor.jet create mode 100644 jetc/barant-site/src/htmleditor.page create mode 100644 jetc/barant-site/src/login.jet create mode 100644 jetc/barant-site/src/main.menu create mode 100644 jetc/barant-site/src/mainpagetemplate.jet create mode 100644 jetc/barant-site/src/modules.page rename jetc/barant-site/src/{asm1802.page => opensource-asm1802.subpage} (95%) mode change 100755 => 100644 create mode 100644 jetc/barant-site/src/opensource-barantmail.subpage rename jetc/barant-site/src/{jet.page => opensource-jet.subpage} (68%) mode change 100755 => 100644 create mode 100644 jetc/barant-site/src/opensource-overview.subpage rename jetc/barant-site/src/{serverframework.page => opensource-serverframework.subpage} (90%) mode change 100755 => 100644 create mode 100644 jetc/barant-site/src/opensource-synthmax.subpage create mode 100644 jetc/barant-site/src/removecartitem.proc create mode 100644 jetc/barant-site/src/savehtmleditor.proc create mode 100644 jetc/barant-site/src/selectproduct.proc create mode 100644 jetc/barant-site/src/store-createstore.subpage create mode 100644 jetc/barant-site/src/store.page create mode 100644 jetc/barant-site/src/update_product.proc create mode 100644 jetc/barant-site/src/updatecartqty.proc create mode 100755 jetc/build-assets.sh create mode 100644 jetc/lowercase_jet.py create mode 100755 jetc/strip_fragments.py diff --git a/.gitignore b/.gitignore index a7aef4a..7f9872d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ jetc/libCoreUtils_pic.a jetc/barant-site/jet/vst.jet jetc/barant-site/jet/modular1.jet jetc/barant-site/jet/modular1_patches.jet +jetc/**/*.jtc +jetc/__pycache__/ diff --git a/Tag.cpp b/Tag.cpp index 2e9c2ef..a32c3eb 100644 --- a/Tag.cpp +++ b/Tag.cpp @@ -11,6 +11,11 @@ #include "__imapmessage.h" #include "__imapflag.h" #include "__imapdelete.h" +#include "__imapmove.h" +#include "__imapfolders.h" +#include "__imapsearch.h" +#include "__smtpsend.h" +#include "__imapaccounts.h" #include "__whilerow.h" #include "__comment.h" #include "__eval.h" @@ -472,6 +477,21 @@ namespace jet { } else if(ifTagName(in, "imapdelete")) { __imapdelete _imapdelete(in, out, global, this, local); continue; + } else if(ifTagName(in, "imapmove")) { + __imapmove _imapmove(in, out, global, this, local); + continue; + } else if(ifTagName(in, "imapfolders")) { + __imapfolders _imapfolders(in, out, global, this, local); + continue; + } else if(ifTagName(in, "imapsearch")) { + __imapsearch _imapsearch(in, out, global, this, local); + continue; + } else if(ifTagName(in, "smtpsend")) { + __smtpsend _smtpsend(in, out, global, this, local); + continue; + } else if(ifTagName(in, "imapaccounts")) { + __imapaccounts _imapaccounts(in, out, global, this, local); + continue; } else if(ifTagName(in, "whilerow")) { __whilerow _whilerow(in, out, global, this, local); continue; diff --git a/__eval.cpp b/__eval.cpp new file mode 100644 index 0000000..66328ec --- /dev/null +++ b/__eval.cpp @@ -0,0 +1,43 @@ +#include "__eval.h" +#include "Exception.h" + +namespace jet { + + // is not a jet-2.0 addition - it's a genuine jet-1.4 tag that was + // simply never implemented in 2.0 at all (no class, no dispatch entry + // anywhere). SESSIONCTL.tag's own definition depends on it + // throughout - capturing a tag's own invocation content into a named + // variable (, often with + // scope="parent" so a nested custom tag can write back into its + // caller's scope: ...), and also + // using it bare (no name=) to render an 'd file's content + // inline (). Without this tag, + // all of that content was silently falling through as undefined-tag + // literal text, which is why a real, unmodified SESSIONCTL.tag + // never produced its actual rendered body. + // + // content - same + // capture semantics as content (Tag::storeVariable + // already implements exactly this: interpret the container, then store + // the result - raw if the container itself specified eval="no", already + // evaluated otherwise - into variable X at the requested scope, + // defaulting to global like does). Reused directly rather than + // reimplemented, since it's the identical contract under a different + // tag name. + // + // content (no name=) - content is interpreted and flows to + // normal output, exactly like any ordinary container-having tag; no + // special-casing needed since output/evaluate already default to true + // on the base Tag class and the destructor's copyContainer(out, + // parentOut) handles the rest once processContainer(container) below + // has populated `out`. + __eval::__eval(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + if(keywordDefined("name")) { + storeVariable(resolveKeyword("name")); + output = false; + } else if(hasContainer) { + processContainer(container); + } + } + +} diff --git a/__eval.h b/__eval.h new file mode 100644 index 0000000..aa0976f --- /dev/null +++ b/__eval.h @@ -0,0 +1,21 @@ +#ifndef ____eval_h__ +#define ____eval_h__ + +#include "Tag.h" +#include "ZString.h" +#include "MString.h" + +namespace jet { + + class __eval : public Tag { + + public: + __eval(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + protected: + + }; + +} + +#endif diff --git a/__imap.cpp b/__imap.cpp new file mode 100644 index 0000000..2b5edb4 --- /dev/null +++ b/__imap.cpp @@ -0,0 +1,1100 @@ +#include "__imap.h" +#include "Base64.h" +#include "Exception.h" +#include "JFile.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace jet { + + namespace { + + struct PooledImapConnection { + SSL_CTX *ctx; + SSL *ssl; + int fd; + std::string host, user; + time_t lastUsed; + }; + // thread_local, keyed by "sessionid|user" - same reasoning as + // __mysql.cpp's own connectionPool: this APIServer's EPoll worker + // pool is a fixed set of OS threads for the process's whole life, + // so a thread_local pool here is only ever touched by the one + // thread that created each entry, never concurrently. + // + // That thread-locality is exactly why an explicit disconnect + // (see the disconnect="yes" branch in __imap's constructor, and + // email.jet's /email/disconnect route) can't be relied on to + // reliably reach a given connection at all: EPoll's fixed worker + // pool is 4 OS threads, and there is no guarantee two requests + // from the same site session - a /email/connect and a later + // /email/disconnect - land on the SAME one. A disconnect that + // happens to run on a different thread than the one holding the + // live connection finds nothing in ITS OWN, empty-for-this-key + // pool and silently does nothing, leaving the real connection on + // the other thread open and unread indefinitely (confirmed live - + // see [[barantcom-email-page-fixes-20260904]]). The idle-sweep + // below is the actual guarantee: it runs at the top of EVERY + // construction, on whichever thread that happens to be, + // so any thread that ever handles IMAP traffic again eventually + // cleans out its own stale entries regardless of which thread + // any particular disconnect call landed on. The explicit + // disconnect="yes" close is kept too, as a same-thread fast path + // when it does happen to line up - just not the only mechanism. + thread_local std::unordered_map imapPool; + + const time_t IMAP_POOL_IDLE_TIMEOUT_SECONDS = 120; + + void closePooledConnection(PooledImapConnection &c) { + if (c.ssl) { SSL_shutdown(c.ssl); SSL_free(c.ssl); } + if (c.fd >= 0) ::close(c.fd); + if (c.ctx) SSL_CTX_free(c.ctx); + } + + // Called at the top of every construction (connect or + // disconnect alike) - closes and evicts any pooled connection on + // THIS thread that's sat unused past the idle timeout, regardless + // of which user/session it belongs to. This is what actually + // bounds how long a leaked connection (an explicit disconnect + // that missed it, a browser tab closed mid-session, a crashed + // client) can stay open, independent of whether any future + // request ever happens to target that exact user again. + void sweepIdleImapConnections() { + time_t now = time(nullptr); + for (auto it = imapPool.begin(); it != imapPool.end(); ) { + if (now - it->second.lastUsed > IMAP_POOL_IDLE_TIMEOUT_SECONDS) { + closePooledConnection(it->second); + it = imapPool.erase(it); + } else { + ++it; + } + } + } + + std::string imapQuote(const std::string &s) { + std::string out = "\""; + for (char c : s) { + if (c == '"' || c == '\\') + out += '\\'; + out += c; + } + out += "\""; + return out; + } + + std::string trimWS(std::string s) { + size_t a = s.find_first_not_of(" \t"); + size_t b = s.find_last_not_of(" \t"); + return (a == std::string::npos) ? std::string() : s.substr(a, b - a + 1); + } + + bool taggedOk(const std::vector &lines, const std::string &tag) { + return !lines.empty() && lines.back().rfind(tag + " OK", 0) == 0; + } + + // Given `line` (a literal-rejoined response string - see + // __imap::readResponseLine()'s own doc comment) and the index of + // the '}' that closes a "{n}" literal-length marker somewhere in + // it, returns the n bytes immediately following (skipping the + // "\r\n" joiner readResponseLine() inserted there). + std::string extractLiteralAt(const std::string &line, size_t braceEndPos) { + size_t braceStart = line.rfind('{', braceEndPos); + if (braceStart == std::string::npos) + return ""; + std::string numStr = line.substr(braceStart + 1, braceEndPos - braceStart - 1); + if (numStr.empty() || numStr.find_first_not_of("0123456789") != std::string::npos) + return ""; + long n = strtol(numStr.c_str(), nullptr, 10); + size_t contentStart = braceEndPos + 1; + if (contentStart + 2 <= line.size() && line[contentStart] == '\r' && line[contentStart + 1] == '\n') + contentStart += 2; + if (contentStart > line.size()) + return ""; + if (contentStart + (size_t)n > line.size()) + n = (long)(line.size() - contentStart); + return line.substr(contentStart, n); + } + + // Q-encoding (RFC 2047's variant of quoted-printable used inside + // "=?charset?Q?...?=" encoded-words) - '_' stands for a literal + // space (plain quoted-printable would need "=20" for that) and + // "=XX" is a hex-escaped byte, same as body quoted-printable. + std::string decodeQEncoding(const std::string &in) { + std::string out; + out.reserve(in.size()); + for (size_t i = 0; i < in.size(); ++i) { + char c = in[i]; + if (c == '_') { + out += ' '; + } else if (c == '=' && i + 2 < in.size() && + isxdigit((unsigned char)in[i + 1]) && isxdigit((unsigned char)in[i + 2])) { + out += (char)strtol(in.substr(i + 1, 2).c_str(), nullptr, 16); + i += 2; + } else { + out += c; + } + } + return out; + } + + // Real-world mail overwhelmingly declares UTF-8 (every encoded + // subject seen from actual traffic to this mailbox does), so that + // case is a pure passthrough; anything else goes through iconv, + // the standard POSIX charset-conversion API, falling back to the + // raw bytes if the declared charset is one iconv doesn't + // recognize rather than throwing - a garbled subject is a far + // better failure mode than a 500 on the whole message list. + std::string convertToUtf8(const std::string &bytes, const std::string &charset) { + std::string cs = charset; + std::transform(cs.begin(), cs.end(), cs.begin(), [](unsigned char c) { return std::tolower(c); }); + if (cs.empty() || cs == "utf-8" || cs == "us-ascii") + return bytes; + iconv_t cd = iconv_open("UTF-8//IGNORE", charset.c_str()); + if (cd == (iconv_t)-1) + return bytes; + std::string result(bytes.size() * 4 + 16, '\0'); + char *inBuf = const_cast(bytes.data()); + size_t inBytesLeft = bytes.size(); + char *outStart = &result[0]; + char *outBuf = outStart; + size_t outBytesLeft = result.size(); + iconv(cd, &inBuf, &inBytesLeft, &outBuf, &outBytesLeft); + iconv_close(cd); + if (outBuf == outStart) + return bytes; + result.resize(outBuf - outStart); + return result; + } + + // Decodes RFC 2047 MIME encoded-words ("=?charset?B|Q?text?=") + // that show up verbatim in raw header field values - IMAP hands + // Subject/From/To back exactly as the sender wrote them, with no + // decoding of its own, so without this every non-ASCII subject + // (extremely common in real mail - every Japanese-language + // message seen in this mailbox uses it) rendered as raw + // "=?UTF-8?B?...?=" garbage in the webmail UI instead of the + // actual text. Per RFC 2047 section 2, whitespace that sits + // ONLY between two consecutive encoded-words is itself part of + // the encoding (a folding artifact) and gets dropped; whitespace + // next to plain text is preserved as-is. + std::string decodeMimeHeader(const std::string &raw) { + std::string result; + size_t pos = 0; + bool lastWasEncoded = false; + while (pos < raw.size()) { + size_t start = raw.find("=?", pos); + if (start == std::string::npos) { + result += raw.substr(pos); + break; + } + + std::string gap = raw.substr(pos, start - pos); + bool gapIsWhitespaceOnly = gap.find_first_not_of(" \t\r\n") == std::string::npos; + if (!(lastWasEncoded && gapIsWhitespaceOnly)) + result += gap; + + size_t q1 = raw.find('?', start + 2); + bool valid = (q1 != std::string::npos) && (q1 - start - 2 <= 40) && (q1 - start - 2 >= 1); + size_t q2 = valid ? raw.find('?', q1 + 1) : std::string::npos; + valid = valid && (q2 == q1 + 2); + char encLetter = valid ? (char)toupper((unsigned char)raw[q1 + 1]) : 0; + valid = valid && (encLetter == 'B' || encLetter == 'Q'); + size_t end = valid ? raw.find("?=", q2 + 1) : std::string::npos; + valid = valid && (end != std::string::npos); + + if (!valid) { + result += raw.substr(start, 2); + pos = start + 2; + lastWasEncoded = false; + continue; + } + + std::string charset = raw.substr(start + 2, q1 - start - 2); + std::string encodedText = raw.substr(q2 + 1, end - q2 - 1); + + std::string decodedBytes; + if (encLetter == 'B') { + coreutils::Base64 b64; + decodedBytes = b64.decode(coreutils::ZString(encodedText.c_str(), encodedText.length())).str(); + } else { + decodedBytes = decodeQEncoding(encodedText); + } + result += convertToUtf8(decodedBytes, charset); + + pos = end + 2; + lastWasEncoded = true; + } + return result; + } + + void parseHeaderFields(const std::string &headerBlock, std::string &from, std::string &to, + std::string &subject, std::string &date) { + size_t pos = 0; + while (pos < headerBlock.size()) { + size_t nl = headerBlock.find("\r\n", pos); + std::string hline = (nl == std::string::npos) ? headerBlock.substr(pos) : headerBlock.substr(pos, nl - pos); + if (hline.rfind("From:", 0) == 0) from = hline.substr(5); + else if (hline.rfind("To:", 0) == 0) to = hline.substr(3); + else if (hline.rfind("Subject:", 0) == 0) subject = hline.substr(8); + else if (hline.rfind("Date:", 0) == 0) date = hline.substr(5); + if (nl == std::string::npos) + break; + pos = nl + 2; + } + } + + // RFC 2822 header unfolding - a continuation line (one starting + // with a space or tab) is really a soft-wrapped part of the + // PREVIOUS field's value, not a new one. Real mail wraps long + // Content-Type lines this way constantly (a multipart boundary + // string alone is often 40+ characters), so without unfolding + // first, a plain per-line scan for "boundary=" would miss it + // whenever it landed on its own continuation line. + std::string unfoldHeaders(const std::string &s) { + std::string out; + size_t i = 0; + while (i < s.size()) { + if (s[i] == '\r' && i + 2 < s.size() && s[i + 1] == '\n' && (s[i + 2] == ' ' || s[i + 2] == '\t')) { + out += ' '; + i += 3; + while (i < s.size() && (s[i] == ' ' || s[i] == '\t')) + ++i; + } else { + out += s[i]; + ++i; + } + } + return out; + } + + // Case-insensitive header lookup against an already-unfolded + // header block - Content-Type/Content-Transfer-Encoding/Content- + // Disposition all show up in whatever case the sending client + // happened to use, unlike From/To/Subject/Date above which real + // mail overwhelmingly capitalizes consistently. + std::string getHeaderValueCI(const std::string &unfolded, const std::string &name) { + size_t pos = 0; + while (pos < unfolded.size()) { + size_t nl = unfolded.find("\r\n", pos); + std::string line = (nl == std::string::npos) ? unfolded.substr(pos) : unfolded.substr(pos, nl - pos); + size_t colon = line.find(':'); + if (colon != std::string::npos && colon == name.size()) { + bool match = true; + for (size_t i = 0; i < name.size() && match; ++i) + match = tolower((unsigned char)line[i]) == tolower((unsigned char)name[i]); + if (match) + return trimWS(line.substr(colon + 1)); + } + if (nl == std::string::npos) + break; + pos = nl + 2; + } + return ""; + } + + // The bare "type/subtype" portion of a header value like + // "multipart/alternative; boundary=\"...\"" or "text/html; + // charset=UTF-8" - lowercased, since MIME type/subtype names are + // case-insensitive by spec and real senders vary in practice. + std::string getMainValue(const std::string &headerValue) { + size_t semi = headerValue.find(';'); + std::string main = (semi == std::string::npos) ? headerValue : headerValue.substr(0, semi); + main = trimWS(main); + std::transform(main.begin(), main.end(), main.begin(), [](unsigned char c) { return std::tolower(c); }); + return main; + } + + // A parameter off a header value, e.g. boundary= or charset= - + // quoted ("...") or bare, matched case-insensitively by name but + // returned with its original case intact (a charset name or a + // boundary string both matter verbatim). + std::string getParam(const std::string &headerValue, const std::string ¶mName) { + std::string lowerVal = headerValue; + std::transform(lowerVal.begin(), lowerVal.end(), lowerVal.begin(), [](unsigned char c) { return std::tolower(c); }); + std::string needle = paramName + "="; + size_t pos = lowerVal.find(needle); + if (pos == std::string::npos) + return ""; + size_t valStart = pos + needle.size(); + if (valStart >= headerValue.size()) + return ""; + if (headerValue[valStart] == '"') { + size_t endQuote = headerValue.find('"', valStart + 1); + if (endQuote == std::string::npos) + return ""; + return headerValue.substr(valStart + 1, endQuote - valStart - 1); + } + size_t end = headerValue.find_first_of("; \t\r\n", valStart); + return (end == std::string::npos) ? headerValue.substr(valStart) : headerValue.substr(valStart, end - valStart); + } + + // Body quoted-printable - distinct from decodeQEncoding above + // (RFC 2047's own "=XX plus '_' for space" variant used only + // inside header encoded-words): a message BODY uses plain RFC + // 2045 quoted-printable, where "=\r\n" (or a bare "=\n") is a + // soft line break to be removed entirely, not a literal + // character, and there is no '_'-for-space special case at all. + std::string decodeQuotedPrintableBody(const std::string &in) { + std::string out; + out.reserve(in.size()); + for (size_t i = 0; i < in.size(); ++i) { + char c = in[i]; + if (c != '=') { + out += c; + continue; + } + if (i + 2 < in.size() && in[i + 1] == '\r' && in[i + 2] == '\n') { + i += 2; + } else if (i + 1 < in.size() && in[i + 1] == '\n') { + i += 1; + } else if (i + 2 < in.size() && isxdigit((unsigned char)in[i + 1]) && isxdigit((unsigned char)in[i + 2])) { + out += (char)strtol(in.substr(i + 1, 2).c_str(), nullptr, 16); + i += 2; + } else { + out += c; + } + } + return out; + } + + struct MimeTextPart { + std::string decodedBody; + }; + + // Walks a MIME part (given as separate header/body blocks - the + // top-level message the first time, one boundary-delimited + // sub-part on every recursive call after that) looking for the + // best text/html and text/plain content to show as a message + // body. Recurses into any multipart/* container uniformly + // (alternative, mixed, related - all handled the same way, + // which correctly falls through nested wrappers like a + // multipart/mixed enclosing a multipart/alternative without + // needing to special-case either), skips anything marked + // Content-Disposition: attachment, and keeps only the FIRST + // text/html and FIRST text/plain part found (document order) - + // deliberately not the most sophisticated possible MIME walker, + // but real mail practically never has more than one of each at + // the "body" level, and this matches the same pragmatic, + // purpose-built philosophy as the rest of this class (see + // __imap.h's own doc comment on readResponseLine()). + void collectBestTextParts(const std::string &headerBlock, const std::string &bodyBlock, + MimeTextPart &htmlOut, bool &haveHtml, MimeTextPart &plainOut, bool &havePlain) { + std::string unfolded = unfoldHeaders(headerBlock); + std::string contentTypeHeader = getHeaderValueCI(unfolded, "Content-Type"); + std::string mainType = contentTypeHeader.empty() ? "text/plain" : getMainValue(contentTypeHeader); + + if (mainType.rfind("multipart/", 0) == 0) { + std::string boundary = getParam(contentTypeHeader, "boundary"); + if (boundary.empty()) + return; + std::string delim = "--" + boundary; + size_t pos = bodyBlock.find(delim); + while (pos != std::string::npos) { + size_t partStart = pos + delim.size(); + if (partStart + 1 < bodyBlock.size() && bodyBlock[partStart] == '-' && bodyBlock[partStart + 1] == '-') + break; + if (partStart < bodyBlock.size() && bodyBlock[partStart] == '\r') + partStart += 2; + else if (partStart < bodyBlock.size() && bodyBlock[partStart] == '\n') + partStart += 1; + + size_t nextPos = bodyBlock.find(delim, partStart); + std::string partRaw = (nextPos == std::string::npos) + ? bodyBlock.substr(partStart) + : bodyBlock.substr(partStart, nextPos - partStart); + + size_t hEnd = partRaw.find("\r\n\r\n"); + std::string partHeader = (hEnd == std::string::npos) ? partRaw : partRaw.substr(0, hEnd); + std::string partBody = (hEnd == std::string::npos) ? "" : partRaw.substr(hEnd + 4); + + collectBestTextParts(partHeader, partBody, htmlOut, haveHtml, plainOut, havePlain); + + if (nextPos == std::string::npos) + break; + pos = nextPos; + } + return; + } + + std::string disposition = getMainValue(getHeaderValueCI(unfolded, "Content-Disposition")); + if (disposition == "attachment") + return; + if (mainType != "text/html" && mainType != "text/plain") + return; + if ((mainType == "text/html" && haveHtml) || (mainType == "text/plain" && havePlain)) + return; + + std::string transferEncoding = getMainValue(getHeaderValueCI(unfolded, "Content-Transfer-Encoding")); + std::string decoded; + if (transferEncoding == "base64") { + std::string stripped; + stripped.reserve(bodyBlock.size()); + for (char c : bodyBlock) + if (!isspace((unsigned char)c)) + stripped += c; + coreutils::Base64 b64; + decoded = b64.decode(coreutils::ZString(stripped.c_str(), stripped.length())).str(); + } else if (transferEncoding == "quoted-printable") { + decoded = decodeQuotedPrintableBody(bodyBlock); + } else { + decoded = bodyBlock; + } + + decoded = convertToUtf8(decoded, getParam(contentTypeHeader, "charset")); + + if (mainType == "text/html") { + htmlOut.decodedBody = decoded; + haveHtml = true; + } else { + plainOut.decodedBody = decoded; + havePlain = true; + } + } + + } + + // Shared by listMessages() and searchMessages() - both issue the + // identical FETCH field list (UID FLAGS RFC822.SIZE + // BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)]), just against a + // different sequence set (1:* vs whatever UIDs a SEARCH turned + // up), so this is the one place that turns those response lines + // into the JSON summary objects the webmail client's message list + // already knows how to render. A private static member (not a + // plain anonymous-namespace function) specifically so it can call + // findUidField() and friends directly - those are private statics + // of this same class. + std::vector __imap::parseSummaryLines(const std::vector &lines) { + std::vector items; + for (auto &line : lines) { + if (line.rfind("* ", 0) != 0 || line.find("FETCH") == std::string::npos) + continue; + + uint32_t uid = findUidField(line); + if (uid == 0) + continue; + std::vector flags = findFlagsField(line); + long size = findSizeField(line); + std::string headerBlock = findBodyLiteral(line); + + std::string from, to, subject, date; + parseHeaderFields(headerBlock, from, to, subject, date); + from = decodeMimeHeader(from); + subject = decodeMimeHeader(subject); + + bool seen = std::find(flags.begin(), flags.end(), "\\Seen") != flags.end(); + bool flagged = std::find(flags.begin(), flags.end(), "\\Flagged") != flags.end(); + + std::ostringstream item; + item << "{\"uid\":" << uid + << ",\"from\":\"" << jsonEscape(trimWS(from)) << "\"" + << ",\"subject\":\"" << jsonEscape(trimWS(subject)) << "\"" + << ",\"date\":\"" << jsonEscape(trimWS(date)) << "\"" + << ",\"size\":" << size + << ",\"seen\":" << (seen ? "true" : "false") + << ",\"flagged\":" << (flagged ? "true" : "false") + << "}"; + items.push_back(item.str()); + } + return items; + } + + __imap::__imap(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, this) { + + if (!keywordDefined("config")) + throw coreutils::Exception("config must be specified for imap tag.", __FILE__, __LINE__, 500); + if (!keywordDefined("user")) + throw coreutils::Exception("user must be specified for imap tag.", __FILE__, __LINE__, 401); + + sweepIdleImapConnections(); + + sessionId = resolveKeyword("sessionid"); + coreutils::MString userVal = resolveKeyword("user"); + + // A disconnect="yes" invocation only tears down this user's own + // pooled connection (see the reuse-pooled-connection branch just + // below, and email.jet's /email/disconnect route) - real IMAP + // servers keep pushing protocol state at an open connection for + // as long as it stays open, whether or not this process is still + // reading it. /email/disconnect used to just clear the site + // session's own _imap_user/_imap_password variables and leave + // the real, pooled TCP+TLS socket sitting open and completely + // unread forever (the destructor deliberately never closes it - + // see its own doc comment - by design, for reuse across requests + // on the same worker thread, but nothing ever ended that reuse + // window on an explicit disconnect). BARANTMail eventually tries + // to write something to one of these abandoned sockets and hits + // exactly the "stuck write retry loop" pattern + // [[barantmail-ssl-busyloop-diskfull]]'s own fix already catches + // and shuts down correctly - but repeatedly, once per leaked + // connection, which is what was actually producing the ongoing + // log growth (and quite possibly feeding whatever's triggering + // the separate double-free crashes seen the same day - a shutdown + // path getting exercised this often, on connections nothing else + // in this process still references, is exactly the kind of + // pattern that turns a latent use-after-free into a frequent one). + if (keywordDefined("disconnect") && resolveKeyword("disconnect") == "yes") { + std::string poolKey = sessionId.str() + "|" + userVal.str(); + auto pooled = imapPool.find(poolKey); + if (pooled != imapPool.end()) { + closePooledConnection(pooled->second); + imapPool.erase(pooled); + } + return; + } + + if (!keywordDefined("password")) + throw coreutils::Exception("password must be specified for imap tag.", __FILE__, __LINE__, 401); + + coreutils::MString configPath = resolveKeyword("config"); + coreutils::MString passwordVal = resolveKeyword("password"); + + coreutils::JFile config(configPath); + std::string host = config.getString("host"); + long port = (long)config.getNumber("port"); + if (port <= 0) + port = 993; + + std::string poolKey = sessionId.str() + "|" + userVal.str(); + auto pooled = imapPool.find(poolKey); + if (pooled != imapPool.end() && pooled->second.host == host && pooled->second.user == userVal.str()) { + ctx = pooled->second.ctx; + ssl = pooled->second.ssl; + fd = pooled->second.fd; + imapPool.erase(pooled); + + bool alive = true; + try { + std::string tag = nextTag(); + sendLine(tag + " NOOP"); + auto lines = readUntilTagged(tag); + alive = taggedOk(lines, tag); + } catch (...) { + alive = false; + } + if (!alive) { + if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); } + if (fd >= 0) ::close(fd); + if (ctx) SSL_CTX_free(ctx); + ssl = nullptr; + fd = -1; + ctx = nullptr; + recvBuffer.clear(); + recvPos = 0; + } + } + + if (!ssl) { + struct addrinfo hints {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + struct addrinfo *result = nullptr; + std::string portStr = std::to_string(port); + if (getaddrinfo(host.c_str(), portStr.c_str(), &hints, &result) != 0 || !result) + throw coreutils::Exception("imap: could not resolve " + host, __FILE__, __LINE__, 502); + + int newFd = -1; + for (struct addrinfo *rp = result; rp != nullptr; rp = rp->ai_next) { + newFd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (newFd < 0) + continue; + if (::connect(newFd, rp->ai_addr, rp->ai_addrlen) == 0) + break; + ::close(newFd); + newFd = -1; + } + freeaddrinfo(result); + if (newFd < 0) + throw coreutils::Exception("imap: could not connect to " + host, __FILE__, __LINE__, 502); + + struct timeval timeout { 30, 0 }; + setsockopt(newFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(newFd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + SSL_CTX *newCtx = SSL_CTX_new(SSLv23_client_method()); + if (!newCtx) { + ::close(newFd); + throw coreutils::Exception("imap: SSL_CTX_new failed", __FILE__, __LINE__, 502); + } + SSL *newSsl = SSL_new(newCtx); + SSL_set_fd(newSsl, newFd); + if (SSL_connect(newSsl) != 1) { + SSL_free(newSsl); + SSL_CTX_free(newCtx); + ::close(newFd); + throw coreutils::Exception("imap: TLS handshake failed", __FILE__, __LINE__, 502); + } + + ctx = newCtx; + ssl = newSsl; + fd = newFd; + + // Consume the server's own greeting line ("* OK ...") before + // sending anything. + readResponseLine(); + + std::string tag = nextTag(); + sendLine(tag + " LOGIN " + imapQuote(userVal.str()) + " " + imapQuote(passwordVal.str())); + auto lines = readUntilTagged(tag); + if (!taggedOk(lines, tag)) { + SSL_shutdown(ssl); + SSL_free(ssl); + SSL_CTX_free(ctx); + ::close(fd); + ssl = nullptr; + ctx = nullptr; + fd = -1; + throw coreutils::Exception("Invalid mailbox email or password.", __FILE__, __LINE__, 401); + } + } + + imapPool[poolKey] = PooledImapConnection{ctx, ssl, fd, host, userVal.str(), time(nullptr)}; + + global.addImapSession(sessionId, this); + processContainer(container); + } + + __imap::~__imap() { + global.removeImapSession(sessionId); + // ssl/fd/ctx deliberately NOT closed here - owned by this worker + // thread's persistent pool now, exactly like __mysql's own + // destructor leaves `mysql` alone. See the constructor's pooling + // logic above. + } + + std::string __imap::nextTag() { + return "a" + std::to_string(++tagCounter); + } + + void __imap::sendLine(const std::string &line) { + std::string data = line + "\r\n"; + size_t sent = 0; + while (sent < data.size()) { + int n = SSL_write(ssl, data.data() + sent, (int)(data.size() - sent)); + if (n <= 0) + throw coreutils::Exception("imap: connection lost while writing.", __FILE__, __LINE__, 502); + sent += n; + } + } + + char __imap::readByte() { + while (recvPos >= recvBuffer.size()) { + char buf[8192]; + int n = SSL_read(ssl, buf, sizeof(buf)); + if (n <= 0) + throw coreutils::Exception("imap: connection lost while reading.", __FILE__, __LINE__, 502); + recvBuffer.append(buf, n); + } + return recvBuffer[recvPos++]; + } + + std::string __imap::readResponseLine() { + std::string line; + char c; + while (true) { + c = readByte(); + if (c == '\r') + continue; + if (c == '\n') + break; + line += c; + } + + if (!line.empty() && line.back() == '}') { + size_t brace = line.rfind('{'); + if (brace != std::string::npos) { + std::string numStr = line.substr(brace + 1, line.size() - brace - 2); + bool allDigits = !numStr.empty() && numStr.find_first_not_of("0123456789") == std::string::npos; + if (allDigits) { + long n = strtol(numStr.c_str(), nullptr, 10); + std::string literalData; + literalData.reserve(n); + for (long i = 0; i < n; ++i) + literalData += readByte(); + // The literal is always followed by the rest of the SAME + // logical response line (more items, then the real + // CRLF) - recursing into this same function reads that + // remainder, correctly handling a second literal on the + // same line (e.g. FETCH ... BODY[HEADER...] {n1} ... + // BODY[TEXT] {n2} ...) via a further recursion. + std::string rest = readResponseLine(); + recvBuffer.erase(0, recvPos); + recvPos = 0; + return line + "\r\n" + literalData + "\r\n" + rest; + } + } + } + + recvBuffer.erase(0, recvPos); + recvPos = 0; + return line; + } + + std::vector __imap::readUntilTagged(const std::string &tag) { + std::vector lines; + std::string prefix = tag + " "; + while (true) { + std::string line = readResponseLine(); + lines.push_back(line); + if (line.rfind(prefix, 0) == 0) + break; + } + return lines; + } + + bool __imap::selectFolderIfNeeded(const std::string &folder) { + if (selectedFolder == folder) + return true; + std::string tag = nextTag(); + sendLine(tag + " SELECT " + folder); + auto lines = readUntilTagged(tag); + if (!taggedOk(lines, tag)) + return false; + selectedFolder = folder; + return true; + } + + std::string __imap::jsonEscape(const std::string &s) { + std::string out; + out.reserve(s.size() + 8); + for (unsigned char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': continue; + case '\t': out += "\\t"; break; + default: + if (c < 0x20) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", c); + out += buf; + } else { + out += (char)c; + } + } + } + return out; + } + + uint32_t __imap::findUidField(const std::string &fetchLine) { + size_t pos = fetchLine.find("UID "); + if (pos == std::string::npos) + return 0; + return (uint32_t)strtoul(fetchLine.c_str() + pos + 4, nullptr, 10); + } + + long __imap::findSizeField(const std::string &fetchLine) { + size_t pos = fetchLine.find("RFC822.SIZE "); + if (pos == std::string::npos) + return 0; + return strtol(fetchLine.c_str() + pos + 12, nullptr, 10); + } + + std::vector __imap::findFlagsField(const std::string &fetchLine) { + std::vector flags; + size_t pos = fetchLine.find("FLAGS ("); + if (pos == std::string::npos) + return flags; + size_t start = pos + 7; + size_t end = fetchLine.find(')', start); + if (end == std::string::npos) + return flags; + std::istringstream ss(fetchLine.substr(start, end - start)); + std::string tok; + while (ss >> tok) + flags.push_back(tok); + return flags; + } + + std::string __imap::findBodyLiteral(const std::string &fetchLine) { + size_t bodyPos = fetchLine.find("BODY["); + if (bodyPos == std::string::npos) + return ""; + size_t bracketEnd = fetchLine.find(']', bodyPos); + if (bracketEnd == std::string::npos) + return ""; + size_t braceStart = fetchLine.find('{', bracketEnd); + if (braceStart == std::string::npos) + return ""; + size_t braceEnd = fetchLine.find('}', braceStart); + if (braceEnd == std::string::npos) + return ""; + return extractLiteralAt(fetchLine, braceEnd); + } + + coreutils::MString __imap::listMessages(const std::string &folder, int limit) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + + std::string tag = nextTag(); + sendLine(tag + " FETCH 1:* (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)])"); + auto lines = readUntilTagged(tag); + if (!taggedOk(lines, tag)) + throw coreutils::Exception("imap: FETCH failed", __FILE__, __LINE__, 502); + + std::vector items = parseSummaryLines(lines); + + // Keep only the newest `limit` - UIDs are assigned in strictly + // ascending delivery order (see MailFolder.h, BARANTMail, built + // earlier this session), so the tail of this UID-ascending list + // is the newest mail. + if ((int)items.size() > limit) + items.erase(items.begin(), items.end() - limit); + std::reverse(items.begin(), items.end()); + + std::string json = "["; + for (size_t i = 0; i < items.size(); ++i) { + if (i) + json += ","; + json += items[i]; + } + json += "]"; + return coreutils::MString(json); + } + + // BARANTMail's own __IMAP_SEARCH.cpp supports FROM/SUBJECT/BODY and + // a chained OR, which is exactly enough to build "one query box + // matching sender, subject, or body content" without needing to run + // three separate searches and merge results by hand - OR takes + // exactly two search keys per IMAP4rev1 grammar, so a 3-way match + // is the same OR nested once: OR FROM OR SUBJECT BODY reads as + // FROM-matches OR (SUBJECT-matches OR BODY-matches). + coreutils::MString __imap::searchMessages(const std::string &folder, const std::string &query) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + + std::string escaped; + escaped.reserve(query.size()); + for (char c : query) { + if (c == '"' || c == '\\') + escaped += '\\'; + escaped += c; + } + + std::string searchTag = nextTag(); + sendLine(searchTag + " UID SEARCH OR FROM \"" + escaped + "\" OR SUBJECT \"" + escaped + "\" BODY \"" + escaped + "\""); + auto searchLines = readUntilTagged(searchTag); + if (!taggedOk(searchLines, searchTag)) + throw coreutils::Exception("imap: SEARCH failed", __FILE__, __LINE__, 502); + + std::vector uidStrings; + for (auto &line : searchLines) { + if (line.rfind("* SEARCH", 0) != 0) + continue; + std::istringstream iss(line.substr(8)); + std::string tok; + while (iss >> tok) + uidStrings.push_back(tok); + } + + if (uidStrings.empty()) + return coreutils::MString("[]"); + + std::string seqSet; + for (size_t i = 0; i < uidStrings.size(); ++i) { + if (i) + seqSet += ","; + seqSet += uidStrings[i]; + } + + std::string fetchTag = nextTag(); + sendLine(fetchTag + " UID FETCH " + seqSet + " (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE)])"); + auto fetchLines = readUntilTagged(fetchTag); + if (!taggedOk(fetchLines, fetchTag)) + throw coreutils::Exception("imap: FETCH failed", __FILE__, __LINE__, 502); + + std::vector items = parseSummaryLines(fetchLines); + std::reverse(items.begin(), items.end()); + + std::string json = "["; + for (size_t i = 0; i < items.size(); ++i) { + if (i) + json += ","; + json += items[i]; + } + json += "]"; + return coreutils::MString(json); + } + + coreutils::MString __imap::fetchMessage(const std::string &folder, uint32_t uid) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + + // A single BODY[] pulls the whole message (headers + every MIME + // part) in one literal - needed now to actually see Content-Type/ + // boundary/Content-Transfer-Encoding, none of which the old + // HEADER.FIELDS-subset + BODY[TEXT] split ever exposed, so the + // raw MIME envelope (boundaries, base64/quoted-printable + // encoded parts, nested multipart wrappers) was always what + // showed up as the "body" rather than the actual message + // content. Still marks \Seen exactly like before (any non-.PEEK + // BODY fetch does). + std::string tag = nextTag(); + sendLine(tag + " UID FETCH " + std::to_string(uid) + " (FLAGS BODY[])"); + auto lines = readUntilTagged(tag); + if (!taggedOk(lines, tag)) + throw coreutils::Exception("imap: FETCH failed", __FILE__, __LINE__, 502); + + std::string rawMessage; + bool found = false; + for (auto &line : lines) { + if (line.rfind("* ", 0) != 0 || line.find("FETCH") == std::string::npos) + continue; + found = true; + rawMessage = findBodyLiteral(line); + } + + if (!found) + throw coreutils::Exception("Message not found.", __FILE__, __LINE__, 404); + + size_t headerEnd = rawMessage.find("\r\n\r\n"); + std::string headerBlock = (headerEnd == std::string::npos) ? rawMessage : rawMessage.substr(0, headerEnd); + std::string bodyBlock = (headerEnd == std::string::npos) ? "" : rawMessage.substr(headerEnd + 4); + + std::string from, to, subject, date; + parseHeaderFields(headerBlock, from, to, subject, date); + from = decodeMimeHeader(from); + to = decodeMimeHeader(to); + subject = decodeMimeHeader(subject); + + MimeTextPart htmlPart, plainPart; + bool haveHtml = false, havePlain = false; + collectBestTextParts(headerBlock, bodyBlock, htmlPart, haveHtml, plainPart, havePlain); + + std::string bodyText; + bool isHtml = false; + if (haveHtml) { + bodyText = htmlPart.decodedBody; + isHtml = true; + } else if (havePlain) { + bodyText = plainPart.decodedBody; + } else { + // Neither a text/html nor a text/plain part was found anywhere + // (an unusual message - e.g. one made up entirely of + // attachments) - fall back to the raw body rather than + // showing nothing at all. + bodyText = bodyBlock; + } + + std::ostringstream json; + json << "{\"uid\":" << uid + << ",\"from\":\"" << jsonEscape(trimWS(from)) << "\"" + << ",\"to\":\"" << jsonEscape(trimWS(to)) << "\"" + << ",\"subject\":\"" << jsonEscape(trimWS(subject)) << "\"" + << ",\"date\":\"" << jsonEscape(trimWS(date)) << "\"" + << ",\"body\":\"" << jsonEscape(bodyText) << "\"" + << ",\"isHtml\":" << (isHtml ? "true" : "false") + << "}"; + return coreutils::MString(json.str()); + } + + coreutils::MString __imap::setFlag(const std::string &folder, uint32_t uid, const std::string &flag, bool add) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + std::string tag = nextTag(); + sendLine(tag + " UID STORE " + std::to_string(uid) + " " + (add ? "+FLAGS" : "-FLAGS") + " (" + flag + ")"); + auto lines = readUntilTagged(tag); + bool ok = taggedOk(lines, tag); + return coreutils::MString(std::string("{\"ok\":") + (ok ? "true" : "false") + "}"); + } + + coreutils::MString __imap::deleteMessage(const std::string &folder, uint32_t uid) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + + std::string tag = nextTag(); + sendLine(tag + " UID STORE " + std::to_string(uid) + " +FLAGS (\\Deleted)"); + auto lines = readUntilTagged(tag); + bool ok1 = taggedOk(lines, tag); + + std::string tag2 = nextTag(); + sendLine(tag2 + " EXPUNGE"); + auto lines2 = readUntilTagged(tag2); + bool ok2 = taggedOk(lines2, tag2); + + bool ok = ok1 && ok2; + return coreutils::MString(std::string("{\"ok\":") + (ok ? "true" : "false") + "}"); + } + + // BARANTMail (see __IMAP_COPY.cpp) implements COPY but not the + // RFC 6851 MOVE extension - not surprising, since the whole point + // of that extension was standardizing what every real-world client + // already did by hand: COPY into the destination, then \Deleted + + // EXPUNGE from the source. Same two IMAP round trips deleteMessage() + // above already does, with a COPY in front. + coreutils::MString __imap::moveMessage(const std::string &folder, uint32_t uid, const std::string &destFolder) { + if (!selectFolderIfNeeded(folder)) + throw coreutils::Exception("imap: could not select folder " + folder, __FILE__, __LINE__, 502); + + std::string copyTag = nextTag(); + sendLine(copyTag + " UID COPY " + std::to_string(uid) + " " + destFolder); + auto copyLines = readUntilTagged(copyTag); + if (!taggedOk(copyLines, copyTag)) + return coreutils::MString("{\"ok\":false}"); + + std::string tag = nextTag(); + sendLine(tag + " UID STORE " + std::to_string(uid) + " +FLAGS (\\Deleted)"); + auto lines = readUntilTagged(tag); + bool ok1 = taggedOk(lines, tag); + + std::string tag2 = nextTag(); + sendLine(tag2 + " EXPUNGE"); + auto lines2 = readUntilTagged(tag2); + bool ok2 = taggedOk(lines2, tag2); + + bool ok = ok1 && ok2; + return coreutils::MString(std::string("{\"ok\":") + (ok ? "true" : "false") + "}"); + } + + // Message/unseen counts for the site's fixed 3-folder set (see + // email.menu - Inbox/Spam/Trash are the only folders this webmail + // client ever shows). Uses IMAP STATUS rather than a real FETCH, + // since it's just two integers per folder, not the full message + // data listMessages() pulls - BARANTMail's own __IMAP_STATUS.cpp + // doesn't require the folder to be unselected first, so this works + // regardless of whichever folder is currently SELECTed for other + // operations on this same pooled connection. + coreutils::MString __imap::folderCounts() { + static const char *folders[] = {"Inbox", "Spam", "Trash"}; + std::ostringstream json; + json << "{"; + bool first = true; + for (const char *folder : folders) { + std::string tag = nextTag(); + sendLine(tag + " STATUS " + folder + " (MESSAGES UNSEEN)"); + auto lines = readUntilTagged(tag); + + int messages = 0, unseen = 0; + if (taggedOk(lines, tag)) { + for (auto &line : lines) { + if (line.rfind("* STATUS", 0) != 0) + continue; + size_t mPos = line.find("MESSAGES "); + if (mPos != std::string::npos) + messages = atoi(line.c_str() + mPos + 9); + size_t uPos = line.find("UNSEEN "); + if (uPos != std::string::npos) + unseen = atoi(line.c_str() + uPos + 7); + } + } + + if (!first) + json << ","; + first = false; + json << "\"" << folder << "\":{\"messages\":" << messages << ",\"unseen\":" << unseen << "}"; + } + json << "}"; + return coreutils::MString(json.str()); + } + +} diff --git a/__imap.h b/__imap.h new file mode 100644 index 0000000..b04d44b --- /dev/null +++ b/__imap.h @@ -0,0 +1,112 @@ +#ifndef ____imap_h__ +# define ____imap_h__ + +# include "Tag.h" +# include "ZString.h" +# include "MString.h" +# include +# include +# include +# include + +namespace jet { + + /// + /// + /// + /// Connects to the IMAP host named in the JSON file `config` (keys: + /// host, port, caCertificate - see email.cfg), TLS-handshakes + /// (implicit TLS, matching IMAP's standard port 993 - no STARTTLS + /// negotiation), and LOGINs with user/password. Throws + /// coreutils::Exception (with errorNumber set to a real HTTP status - + /// 401 for a bad login, 502 for a connection/protocol failure) on any + /// failure, exactly like __mysql throws on a failed + /// mysql_real_connect() - see ApiSession.cpp's exception->HTTP-status + /// mapping, which both this tag and __mysql rely on identically. + /// + /// Registers itself into Global::imapSessions under sessionid (a + /// SEPARATE map from __mysql's Global::sessions - the two backends + /// aren't interchangeable and Global::sessions is concretely typed + /// __mysql*, not something a second backend could share without + /// widening every existing __mysql-only call site), the same way + /// __mysql registers into Global::sessions - later __imap* -family + /// tags (__imaplist, __imapmessage, __imapflag, __imapdelete) look + /// the live connection up by sessionid, mirroring how finds its + /// by sessionid. + /// + /// Connection pooling: a thread_local pool keyed by sessionid+user, + /// reused across requests on the same APIServer worker thread (an + /// IMAP TCP+TLS+LOGIN round trip is too expensive to redo on every + /// AJAX call the webmail UI makes) - directly mirrors __mysql.cpp's + /// own thread_local connectionPool, including using a cheap + /// keepalive probe (NOOP, IMAP's equivalent of mysql_ping()) to + /// detect and discard a connection the server itself already dropped + /// rather than handing a dead socket to the first real command. + /// + + class __imap : public Tag { + + public: + __imap(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + ~__imap(); + + // Each returns a complete, ready-to-emit JSON value (object or + // array) - the __imap*-family wrapper tags (__imaplist.cpp etc.) + // just write whatever this returns straight into their own `out`, + // the same one-call-does-everything shape __sql's query() call + // has relative to . + coreutils::MString listMessages(const std::string &folder, int limit); + coreutils::MString searchMessages(const std::string &folder, const std::string &query); + coreutils::MString fetchMessage(const std::string &folder, uint32_t uid); + coreutils::MString setFlag(const std::string &folder, uint32_t uid, const std::string &flag, bool add); + coreutils::MString deleteMessage(const std::string &folder, uint32_t uid); + coreutils::MString moveMessage(const std::string &folder, uint32_t uid, const std::string &destFolder); + coreutils::MString folderCounts(); + + coreutils::MString sessionId; + + private: + SSL_CTX *ctx = nullptr; + SSL *ssl = nullptr; + int fd = -1; + int tagCounter = 0; + std::string recvBuffer; + size_t recvPos = 0; + std::string selectedFolder; + + std::string nextTag(); + void sendLine(const std::string &line); + char readByte(); + // Reads one IMAP response line, INCLUDING any literal {n} content + // that line announces (rejoined into the same returned string, + // separated by "\r\n" markers) - see the .cpp for the full + // rationale; this is a pragmatic, purpose-built reader for the + // specific, known response shapes this client's own requests + // provoke from BARANTMail (whose own FETCH implementation - built + // earlier this same session - always uses literals for content, + // never quoted strings), not a fully general IMAP parser. + std::string readResponseLine(); + // Reads response lines until the tagged completion line for `tag` + // (built earlier via nextTag()) appears; returns every line + // (untagged and the final tagged one) collected along the way. + // Throws if the tagged line's status isn't OK. + std::vector readUntilTagged(const std::string &tag); + + bool selectFolderIfNeeded(const std::string &folder); + static std::string jsonEscape(const std::string &s); + static uint32_t findUidField(const std::string &fetchLine); + static std::vector findFlagsField(const std::string &fetchLine); + static long findSizeField(const std::string &fetchLine); + // Extracts the literal content immediately following a + // "BODY[...]" (or "BODY[...]") marker in an already- + // literal-rejoined response line - see readResponseLine()'s own + // doc comment for the "\r\n"-separated rejoining this depends on. + static std::string findBodyLiteral(const std::string &fetchLine); + // Shared by listMessages() and searchMessages() - see its own + // doc comment in the .cpp. + static std::vector parseSummaryLines(const std::vector &lines); + }; + +} + +#endif diff --git a/__imapaccounts.cpp b/__imapaccounts.cpp new file mode 100644 index 0000000..cbb16e6 --- /dev/null +++ b/__imapaccounts.cpp @@ -0,0 +1,139 @@ +#include "__imapaccounts.h" +#include "Base64.h" +#include "Exception.h" + +#include + +namespace jet { + + std::string __imapaccounts::jsonEscape(const std::string &s) { + std::string out; + out.reserve(s.size() + 8); + for (unsigned char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': continue; + case '\t': out += "\\t"; break; + default: + if (c < 0x20) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", c); + out += buf; + } else { + out += (char)c; + } + } + } + return out; + } + + static std::string trimWhitespace(const std::string &s) { + size_t start = s.find_first_not_of(" \t\r\n"); + if (start == std::string::npos) + return std::string(); + size_t end = s.find_last_not_of(" \t\r\n"); + return s.substr(start, end - start + 1); + } + + std::vector> __imapaccounts::decodeList(const std::string &encodedRaw) { + std::vector> entries; + coreutils::Base64 base64; + // The list value round-trips through / template + // captures, which include whatever whitespace/newlines surround + // the tag call in the .jet source - trimming here (rather than + // insisting every template call site stays whitespace-free) is + // the robust fix, since a base64 decoder that stops at the first + // invalid character would otherwise silently truncate to nothing + // when a stray leading newline precedes real, valid content. + std::string encoded = trimWhitespace(encodedRaw); + size_t pos = 0; + while (pos < encoded.size()) { + size_t semi = encoded.find(';', pos); + std::string entry = (semi == std::string::npos) ? encoded.substr(pos) : encoded.substr(pos, semi - pos); + pos = (semi == std::string::npos) ? encoded.size() : semi + 1; + entry = trimWhitespace(entry); + if (entry.empty()) + continue; + size_t colon = entry.find(':'); + if (colon == std::string::npos) + continue; + std::string userB64 = trimWhitespace(entry.substr(0, colon)); + std::string passB64 = trimWhitespace(entry.substr(colon + 1)); + std::string user = base64.decode(userB64).str(); + std::string password = base64.decode(passB64).str(); + entries.push_back(std::make_pair(user, password)); + } + return entries; + } + + std::string __imapaccounts::encodeList(const std::vector> &entries) { + coreutils::Base64 base64; + std::string result; + for (size_t i = 0; i < entries.size(); i++) { + if (i > 0) + result += ";"; + std::string user = entries[i].first; + std::string password = entries[i].second; + result += base64.encode(user).str(); + result += ":"; + result += base64.encode(password).str(); + } + return result; + } + + __imapaccounts::__imapaccounts(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + std::string action = trimWhitespace(resolveKeyword("action").str()); + std::string listStr = resolveKeyword("list").str(); + auto entries = decodeList(listStr); + + if (action == "add") { + std::string user = trimWhitespace(resolveKeyword("user").str()); + std::string password = trimWhitespace(resolveKeyword("password").str()); + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].first == user) { + entries.erase(entries.begin() + (long)i); + break; + } + } + entries.push_back(std::make_pair(user, password)); + out = coreutils::MString(encodeList(entries)); + } else if (action == "remove") { + std::string user = trimWhitespace(resolveKeyword("user").str()); + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].first == user) { + entries.erase(entries.begin() + (long)i); + break; + } + } + out = coreutils::MString(encodeList(entries)); + } else if (action == "getpassword") { + std::string user = trimWhitespace(resolveKeyword("user").str()); + std::string password; + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].first == user) { + password = entries[i].second; + break; + } + } + out = coreutils::MString(password); + } else if (action == "firstuser") { + out = coreutils::MString(entries.empty() ? std::string() : entries[0].first); + } else if (action == "list") { + std::string active = resolveKeyword("active").str(); + std::string json = "{\"accounts\":["; + for (size_t i = 0; i < entries.size(); i++) { + if (i > 0) + json += ","; + json += "\"" + jsonEscape(entries[i].first) + "\""; + } + json += "],\"active\":\"" + jsonEscape(active) + "\"}"; + out = coreutils::MString(json); + } else { + throw coreutils::Exception("imapaccounts: unknown action \"" + action + "\"", __FILE__, __LINE__, 500); + } + } + +} diff --git a/__imapaccounts.h b/__imapaccounts.h new file mode 100644 index 0000000..1261a87 --- /dev/null +++ b/__imapaccounts.h @@ -0,0 +1,59 @@ +#ifndef ____imapaccounts_h__ +# define ____imapaccounts_h__ + +# include "Tag.h" +# include "ZString.h" +# include "MString.h" +# include +# include +# include + +namespace jet { + + /// + /// + /// + /// Pure list-transform tag (no session-variable access of its own, + /// no IMAP connection) that manages the multi-account list webmail + /// keeps in the single session variable "_imap_accounts" - the .jet + /// route wraps each call in itself and + /// s it back, the same separation-of-concerns every other + /// session variable in this codebase already uses (this tag never + /// touches Global's session-variable map directly). + /// + /// The list's own on-the-wire encoding (semicolon-separated + /// "base64(user):base64(password)" entries) is private to this tag - + /// callers only ever pass the whole `list` value back in verbatim + /// and use the returned value the same way, so the choice of + /// delimiter can't collide with any real username or password + /// character. + /// + /// action="add": upserts user/password (removing any existing entry + /// for the same user first, so reconnecting with a changed password + /// updates it) - returns the updated encoded list. + /// action="remove": drops the entry for user, if present - returns + /// the updated encoded list. + /// action="getpassword": returns the stored password for user, or + /// an empty string if not present. + /// action="firstuser": returns the first entry's username, or an + /// empty string if the list is empty. + /// action="list": returns JSON ({"accounts":[...],"active":"..."}) + /// - the one client-facing action; "active" echoes the `active` + /// keyword (typically $[@_imap_user]) back verbatim so the sidebar + /// can highlight the current one, and never includes any password. + /// + class __imapaccounts : public Tag { + + public: + __imapaccounts(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + private: + static std::vector> decodeList(const std::string &encoded); + static std::string encodeList(const std::vector> &entries); + static std::string jsonEscape(const std::string &s); + }; + +} + +#endif diff --git a/__imapdelete.cpp b/__imapdelete.cpp new file mode 100644 index 0000000..e8ab3a7 --- /dev/null +++ b/__imapdelete.cpp @@ -0,0 +1,25 @@ +#include "__imapdelete.h" +#include "Exception.h" +#include "__imap.h" +#include + +namespace jet { + + __imapdelete::__imapdelete(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + if (!keywordDefined("uid")) + throw coreutils::Exception("imapdelete tag must have uid defined.", __FILE__, __LINE__, 400); + uint32_t uid = (uint32_t)strtoul(resolveKeyword("uid").str().c_str(), nullptr, 10); + + out = global.getImapSession(sessionId)->deleteMessage(folder, uid); + } + +} diff --git a/__imapdelete.h b/__imapdelete.h new file mode 100644 index 0000000..2897e77 --- /dev/null +++ b/__imapdelete.h @@ -0,0 +1,25 @@ +#ifndef ____imapdelete_h__ +# define ____imapdelete_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - marks \Deleted and EXPUNGEs one message, + /// writes {"ok":true|false} into `out`. See __imaplist.h's doc + /// comment for how `out` reaches the real response body. + /// + + class __imapdelete : public Tag { + + public: + __imapdelete(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imapflag.cpp b/__imapflag.cpp new file mode 100644 index 0000000..ab3a247 --- /dev/null +++ b/__imapflag.cpp @@ -0,0 +1,32 @@ +#include "__imapflag.h" +#include "Exception.h" +#include "__imap.h" +#include + +namespace jet { + + __imapflag::__imapflag(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + if (!keywordDefined("uid")) + throw coreutils::Exception("imapflag tag must have uid defined.", __FILE__, __LINE__, 400); + uint32_t uid = (uint32_t)strtoul(resolveKeyword("uid").str().c_str(), nullptr, 10); + + if (!keywordDefined("flag")) + throw coreutils::Exception("imapflag tag must have flag defined.", __FILE__, __LINE__, 400); + std::string flag = resolveKeyword("flag").str(); + + std::string setStr = resolveKeyword("set").str(); + bool add = setStr.empty() || setStr == "yes" || setStr == "true"; + + out = global.getImapSession(sessionId)->setFlag(folder, uid, flag, add); + } + +} diff --git a/__imapflag.h b/__imapflag.h new file mode 100644 index 0000000..37febd2 --- /dev/null +++ b/__imapflag.h @@ -0,0 +1,26 @@ +#ifndef ____imapflag_h__ +# define ____imapflag_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - STOREs (adds, or removes when set="no") a + /// single IMAP flag on one message, writes {"ok":true|false} into + /// `out`. See __imaplist.h's doc comment for how `out` reaches the + /// real response body. + /// + + class __imapflag : public Tag { + + public: + __imapflag(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imapfolders.cpp b/__imapfolders.cpp new file mode 100644 index 0000000..86106e7 --- /dev/null +++ b/__imapfolders.cpp @@ -0,0 +1,16 @@ +#include "__imapfolders.h" +#include "Exception.h" +#include "__imap.h" + +namespace jet { + + __imapfolders::__imapfolders(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + out = global.getImapSession(sessionId)->folderCounts(); + } + +} diff --git a/__imapfolders.h b/__imapfolders.h new file mode 100644 index 0000000..21c1395 --- /dev/null +++ b/__imapfolders.h @@ -0,0 +1,27 @@ +#ifndef ____imapfolders_h__ +# define ____imapfolders_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - writes {"Inbox":{"messages":n,"unseen":n}, + /// "Spam":{...},"Trash":{...}} into `out` (see + /// __imap::folderCounts()'s own doc comment for why it's this fixed + /// 3-folder set specifically). See __imaplist.h's doc comment for + /// how `out` reaches the real response body. + /// + + class __imapfolders : public Tag { + + public: + __imapfolders(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imaplist.cpp b/__imaplist.cpp new file mode 100644 index 0000000..46acb69 --- /dev/null +++ b/__imaplist.cpp @@ -0,0 +1,26 @@ +#include "__imaplist.h" +#include "Exception.h" +#include "__imap.h" +#include + +namespace jet { + + __imaplist::__imaplist(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + std::string limitStr = resolveKeyword("limit").str(); + int limit = limitStr.empty() ? 50 : atoi(limitStr.c_str()); + if (limit <= 0) + limit = 50; + + out = global.getImapSession(sessionId)->listMessages(folder, limit); + } + +} diff --git a/__imaplist.h b/__imaplist.h new file mode 100644 index 0000000..a59a9a2 --- /dev/null +++ b/__imaplist.h @@ -0,0 +1,30 @@ +#ifndef ____imaplist_h__ +# define ____imaplist_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag (no container) - writes a JSON array of message + /// summaries for `folder`, newest first, straight into `out`, the + /// same way any other self-closing value tag with default + /// output=true/evaluate=true does: Tag::~Tag() copies `out` into the + /// caller's parentOut verbatim (Tag::copyContainer() is a raw byte + /// copy, no re-evaluation - see Tag.cpp - so literal "$"/"["/"<" in + /// real email subjects pass through unmolested; __imap::jsonEscape() + /// already handled quote/backslash/control-char JSON-escaping). + /// + + class __imaplist : public Tag { + + public: + __imaplist(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imapmessage.cpp b/__imapmessage.cpp new file mode 100644 index 0000000..6a2c66c --- /dev/null +++ b/__imapmessage.cpp @@ -0,0 +1,25 @@ +#include "__imapmessage.h" +#include "Exception.h" +#include "__imap.h" +#include + +namespace jet { + + __imapmessage::__imapmessage(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + if (!keywordDefined("uid")) + throw coreutils::Exception("imapmessage tag must have uid defined.", __FILE__, __LINE__, 400); + uint32_t uid = (uint32_t)strtoul(resolveKeyword("uid").str().c_str(), nullptr, 10); + + out = global.getImapSession(sessionId)->fetchMessage(folder, uid); + } + +} diff --git a/__imapmessage.h b/__imapmessage.h new file mode 100644 index 0000000..485ba94 --- /dev/null +++ b/__imapmessage.h @@ -0,0 +1,25 @@ +#ifndef ____imapmessage_h__ +# define ____imapmessage_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - writes a single JSON message object (headers + + /// text body) into `out`. See __imaplist.h's doc comment for how + /// `out` reaches the real response body. + /// + + class __imapmessage : public Tag { + + public: + __imapmessage(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imapmove.cpp b/__imapmove.cpp new file mode 100644 index 0000000..c2ce20e --- /dev/null +++ b/__imapmove.cpp @@ -0,0 +1,29 @@ +#include "__imapmove.h" +#include "Exception.h" +#include "__imap.h" +#include + +namespace jet { + + __imapmove::__imapmove(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + if (!keywordDefined("uid")) + throw coreutils::Exception("imapmove tag must have uid defined.", __FILE__, __LINE__, 400); + uint32_t uid = (uint32_t)strtoul(resolveKeyword("uid").str().c_str(), nullptr, 10); + + if (!keywordDefined("destfolder")) + throw coreutils::Exception("imapmove tag must have destfolder defined.", __FILE__, __LINE__, 400); + std::string destFolder = resolveKeyword("destfolder").str(); + + out = global.getImapSession(sessionId)->moveMessage(folder, uid, destFolder); + } + +} diff --git a/__imapmove.h b/__imapmove.h new file mode 100644 index 0000000..7051ede --- /dev/null +++ b/__imapmove.h @@ -0,0 +1,29 @@ +#ifndef ____imapmove_h__ +# define ____imapmove_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - moves one message into destfolder (COPY there, + /// then \Deleted + EXPUNGE from the original folder, the standard + /// pre-RFC6851 two-step "move" every IMAP client used before the + /// MOVE extension existed - see __imap::moveMessage()'s own doc + /// comment for why this server doesn't just speak that extension + /// directly). Writes {"ok":true|false} into `out`, same convention + /// as __imapdelete/__imapflag. + /// + + class __imapmove : public Tag { + + public: + __imapmove(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__imapsearch.cpp b/__imapsearch.cpp new file mode 100644 index 0000000..877a0c4 --- /dev/null +++ b/__imapsearch.cpp @@ -0,0 +1,22 @@ +#include "__imapsearch.h" +#include "Exception.h" +#include "__imap.h" + +namespace jet { + + __imapsearch::__imapsearch(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + coreutils::MString sessionId = resolveKeyword("sessionid"); + if (!global.imapSessionExists(sessionId)) + throw coreutils::Exception("imap sessionid does not exist.", __FILE__, __LINE__, 502); + + std::string folder = resolveKeyword("folder").str(); + if (folder.empty()) + folder = "INBOX"; + + std::string query = resolveKeyword("query").str(); + + out = global.getImapSession(sessionId)->searchMessages(folder, query); + } + +} diff --git a/__imapsearch.h b/__imapsearch.h new file mode 100644 index 0000000..aac4120 --- /dev/null +++ b/__imapsearch.h @@ -0,0 +1,27 @@ +#ifndef ____imapsearch_h__ +# define ____imapsearch_h__ + +# include "Tag.h" + +namespace jet { + + /// + /// + /// + /// Self-closing tag - writes a JSON array of message summaries + /// (same shape as ) for every message in `folder` whose + /// From, Subject, or body matches `query` (see + /// __imap::searchMessages()'s own doc comment), newest first, + /// straight into `out`. + /// + + class __imapsearch : public Tag { + + public: + __imapsearch(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + }; + +} + +#endif diff --git a/__smtpsend.cpp b/__smtpsend.cpp new file mode 100644 index 0000000..de96709 --- /dev/null +++ b/__smtpsend.cpp @@ -0,0 +1,220 @@ +#include "__smtpsend.h" +#include "Exception.h" +#include "JFile.h" +#include "Base64.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace jet { + + void __smtpsend::sendLine(const std::string &line) { + std::string data = line + "\r\n"; + size_t sent = 0; + while (sent < data.size()) { + int n = SSL_write(ssl, data.data() + sent, (int)(data.size() - sent)); + if (n <= 0) + throw coreutils::Exception("smtpsend: write failed", __FILE__, __LINE__, 502); + sent += (size_t)n; + } + } + + char __smtpsend::readByte() { + if (recvPos >= recvBuffer.size()) { + char buf[4096]; + int n = SSL_read(ssl, buf, sizeof(buf)); + if (n <= 0) + throw coreutils::Exception("smtpsend: read failed", __FILE__, __LINE__, 502); + recvBuffer.assign(buf, buf + n); + recvPos = 0; + } + return recvBuffer[recvPos++]; + } + + std::string __smtpsend::readLine() { + std::string line; + char c; + for (;;) { + c = readByte(); + if (c == '\n') + break; + if (c != '\r') + line += c; + } + return line; + } + + int __smtpsend::readReply(int expectedCode) { + int code = 0; + std::string line; + for (;;) { + line = readLine(); + if (line.size() < 4) + throw coreutils::Exception("smtpsend: malformed reply: " + line, __FILE__, __LINE__, 502); + code = atoi(line.substr(0, 3).c_str()); + if (line[3] == ' ') + break; + // else line[3] == '-': a continuation line, same code - keep reading. + } + if (code / 100 != expectedCode / 100) + throw coreutils::Exception("smtpsend: unexpected reply " + std::to_string(code) + ": " + line, __FILE__, __LINE__, 502); + return code; + } + + void __smtpsend::closeConnection() { + if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = nullptr; } + if (fd >= 0) { ::close(fd); fd = -1; } + if (ctx) { SSL_CTX_free(ctx); ctx = nullptr; } + } + + std::string __smtpsend::base64Encode(const std::string &s) { + coreutils::Base64 base64; + std::string copy = s; + coreutils::ZString z(copy); + return base64.encode(z).str(); + } + + std::string __smtpsend::rfc2822Date() { + time_t now = time(nullptr); + struct tm tmResult{}; + gmtime_r(&now, &tmResult); + char buf[64]; + strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S +0000", &tmResult); + return std::string(buf); + } + + std::string __smtpsend::buildMessage(const std::string &from, const std::string &to, + const std::string &subject, const std::string &html) { + std::string msg; + msg += "Date: " + rfc2822Date() + "\r\n"; + msg += "From: " + from + "\r\n"; + msg += "To: " + to + "\r\n"; + msg += "Subject: " + subject + "\r\n"; + msg += "MIME-Version: 1.0\r\n"; + msg += "Content-Type: text/html; charset=UTF-8\r\n"; + msg += "Content-Transfer-Encoding: 8bit\r\n"; + msg += "\r\n"; + // SMTP DATA's dot-stuffing rule: any body line starting with '.' + // needs a second '.' prepended, or the server treats it as the + // end-of-data marker prematurely and truncates the message. + std::string stuffed; + stuffed.reserve(html.size()); + bool atLineStart = true; + for (size_t i = 0; i < html.size(); i++) { + char c = html[i]; + if (atLineStart && c == '.') + stuffed += '.'; + stuffed += c; + atLineStart = (c == '\n'); + } + msg += stuffed; + return msg; + } + + __smtpsend::__smtpsend(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { + + std::string user = resolveKeyword("user").str(); + std::string password = resolveKeyword("password").str(); + std::string to = resolveKeyword("to").str(); + std::string subject = resolveKeyword("subject").str(); + std::string html = resolveKeyword("html").str(); + coreutils::MString configPath = resolveKeyword("config"); + + if (user.empty() || password.empty()) + throw coreutils::Exception("smtpsend: user/password must be specified.", __FILE__, __LINE__, 401); + if (to.empty()) + throw coreutils::Exception("smtpsend: to address must be specified.", __FILE__, __LINE__, 400); + + coreutils::JFile config(configPath); + std::string host = config.getString("host"); + long port = (long)config.getNumber("smtpPort"); + if (port <= 0) + port = 465; + + struct addrinfo hints {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + struct addrinfo *result = nullptr; + std::string portStr = std::to_string(port); + if (getaddrinfo(host.c_str(), portStr.c_str(), &hints, &result) != 0 || !result) + throw coreutils::Exception("smtpsend: could not resolve " + host, __FILE__, __LINE__, 502); + + int newFd = -1; + for (struct addrinfo *rp = result; rp != nullptr; rp = rp->ai_next) { + newFd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (newFd < 0) + continue; + if (::connect(newFd, rp->ai_addr, rp->ai_addrlen) == 0) + break; + ::close(newFd); + newFd = -1; + } + freeaddrinfo(result); + if (newFd < 0) + throw coreutils::Exception("smtpsend: could not connect to " + host, __FILE__, __LINE__, 502); + + struct timeval timeout { 30, 0 }; + setsockopt(newFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(newFd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + SSL_CTX *newCtx = SSL_CTX_new(SSLv23_client_method()); + if (!newCtx) { + ::close(newFd); + throw coreutils::Exception("smtpsend: SSL_CTX_new failed", __FILE__, __LINE__, 502); + } + SSL *newSsl = SSL_new(newCtx); + SSL_set_fd(newSsl, newFd); + if (SSL_connect(newSsl) != 1) { + SSL_free(newSsl); + SSL_CTX_free(newCtx); + ::close(newFd); + throw coreutils::Exception("smtpsend: TLS handshake failed", __FILE__, __LINE__, 502); + } + + ctx = newCtx; + ssl = newSsl; + fd = newFd; + + try { + readReply(220); + + sendLine("EHLO " + host); + readReply(250); + + sendLine("AUTH LOGIN"); + readReply(334); + sendLine(base64Encode(user)); + readReply(334); + sendLine(base64Encode(password)); + readReply(235); + + sendLine("MAIL FROM:<" + user + ">"); + readReply(250); + sendLine("RCPT TO:<" + to + ">"); + readReply(250); + sendLine("DATA"); + readReply(354); + + std::string message = buildMessage(user, to, subject, html); + sendLine(message); + sendLine("."); + readReply(250); + + sendLine("QUIT"); + readReply(221); + } catch (...) { + closeConnection(); + throw; + } + + closeConnection(); + + out = coreutils::MString(std::string("{\"ok\":true}")); + } + +} diff --git a/__smtpsend.h b/__smtpsend.h new file mode 100644 index 0000000..6438a22 --- /dev/null +++ b/__smtpsend.h @@ -0,0 +1,69 @@ +#ifndef ____smtpsend_h__ +# define ____smtpsend_h__ + +# include "Tag.h" +# include "ZString.h" +# include "MString.h" +# include +# include + +namespace jet { + + /// + /// + /// + /// One-shot authenticated SMTP submission to BARANTMAIL's own + /// implicit-TLS submission port (config key "smtpPort" - matches + /// barantmail.cfg's smtpTLS.port, :465, the same implicit-TLS style + /// as IMAP's :993 - no STARTTLS negotiation). Connects, TLS- + /// handshakes, AUTH LOGIN with the SAME mailbox credentials already + /// used by (so the message goes out authenticated as the + /// actual connected mailbox, not the site's own transactional-mail + /// identity - see barant.tag's separate tag for that), then + /// MAIL FROM/RCPT TO/DATAs a freshly-built RFC 5322 message + /// (Date/From/To/Subject/MIME headers, HTML body), then QUITs. The + /// authenticated user IS the From address - SMTP submission + /// requires MAIL FROM to match the authenticated identity anyway, so + /// there's no separate "from" keyword. + /// + /// No connection pooling, unlike __imap - composing mail is rare + /// enough that a fresh connect/auth/send/close per message is fine. + /// + /// Throws coreutils::Exception (401 for a bad login, 502 for any + /// other connect/protocol/rejected-recipient failure) on any + /// failure - same convention as __imap. + /// + class __smtpsend : public Tag { + + public: + __smtpsend(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local); + + private: + SSL_CTX *ctx = nullptr; + SSL *ssl = nullptr; + int fd = -1; + std::string recvBuffer; + size_t recvPos = 0; + + void sendLine(const std::string &line); + char readByte(); + std::string readLine(); + // Reads one (possibly multi-line, "250-text"/"250 text" + // continuation-marked) SMTP reply and returns its 3-digit code; + // throws if the code's first digit doesn't match expectedCode's. + int readReply(int expectedCode); + + void closeConnection(); + + static std::string base64Encode(const std::string &s); + static std::string rfc2822Date(); + // Builds the full RFC 5322 message (headers + dot-stuffed body) + // handed to SMTP's DATA command. + static std::string buildMessage(const std::string &from, const std::string &to, + const std::string &subject, const std::string &html); + }; + +} + +#endif diff --git a/jetc/barant-site/assets/images/AvenirLTStd-Light.ttf b/jetc/barant-site/assets/images/AvenirLTStd-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8e717ce2843203697697f3619840a05b7c5eac22 GIT binary patch literal 27272 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 UfWu&@i8p+C=AtZHhO@5ze|YUJN&o-= literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/Exo2-RegularCondensed.otf b/jetc/barant-site/assets/images/Exo2-RegularCondensed.otf new file mode 100755 index 0000000000000000000000000000000000000000..491a432f4546ae0e24a76ddd06e7987ee08e4d7d GIT binary patch literal 65404 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�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 L`Zl{N+z8<}x|C4m literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/ServerCoreMap.png b/jetc/barant-site/assets/images/ServerCoreMap.png new file mode 100644 index 0000000000000000000000000000000000000000..35cbedaf5137987f73666199a199ee313a4f95a5 GIT binary patch literal 24477 zcmb@ubyQrJAVP2~6F$hU3G{={Lj|k0_)EtFvtgMWz9f4a2 z2w{68Jx3!$Viz+`lc-%#Yd~& zLttm=hWo~0^(NI}G$`9F89cN&4zv*X=b!K3*GTZBAc$eWlM+OJ2fX?czyhxj+^FF9 z!3HGnAc&3a9URcn(ccH{%PA;KOiq?;e{N}QB_kz8{hH9 z1vO}&JgTv=aauq|L`1~UkbL9LKQ}iwG7`1>GsZXI>D`Kh@eJ<+ihqWOmlp{;?M*mIb8_P271gS8 z>%R3rI9Q}TxL9Uqm6|P8fhLn3PNZnVvrIWS!E-CDjU^qLo~!Ns9gxhY;l%3u2{I^- z@8n@#Qp#`;Yf0B;*=+(!xmg*K^F&vm#y)rd$IrCV*;S^(wKICw`eAG1QuoEBQ;X9@ zR|sF5B%$NJCA9b8=lmuOZwlwoBu$CCu`hTKd-l^z*h!LnF9geMHeahN`{d1Me1hlg z5MOEXG3>qxXL@HlP}k3n7$Wkk<9689HQ|-=7G~{NtN2vuZR`wZi*2lQ{NtISBS&VE z$5NOF<4YTZ%>3N(+|4nHf)%g3Kwanfzl%p1gk@aapQl%DmO`E>%eI#{BBO6ic!TO@ zRh5-&%(uKRMM+2kGMv5A{M`(kprDXqbXcFghVSej*HhjY-dyLmHICbwY+h{6S)YoF8pBi?*tn`HRx>ejV-k;KRZx&$yg1;lBd0Y! z`+%SeHBxOoFDEKmA2-~Rsrmzi)&;K-bh9ra-n6+o*+wq-Mj?Namrx-hsBQg;K5z?x zR(FSGIILc!l?Mp|A~>O@rp98n{ORv!3YTqmEB~XeXuT6vH%%Zf}c`7&>3FZWsUsfc2k+mp{*=EPhbU_s@n-gr>+t;$;Vw0jB*Y^9; z(8CA~_gu^s<~%PlpZm)i!;BV(`7zUCF}yrAO2iZyF()16 zD_cwT3+_zN`AvF=h*lFBR`rhO14h-EgL-k<9h+~rAnAu!(hCftR|NQ~DoczHdR{P} z<;vHb=Rf&u-(BpCW(ZVx3(w7iFXiXN#4By2vRQ4nm`9Gz)v{XsAokHlHtES86y8P7 z8ZEbDBlG=|ixPVLx|avNGg$+1)iEB?f;4=@vzJSQ@y7@lXwFny%KCF0`vFYH&F^D8 zFZKa%-@ZLtrz8D{4Gn=7Lh2~N98s3L=o~vLtjQQEExv~Xchh!K7cl-nG;~1n1(pQK z`0(myC_&;^Sdy>8gLZ+sCSTiDwpHD0Dp1lZ^}6P;zIOi9vK4pVcjH9hgC76T=zWxj z&ML#J)2!d|!z9!(#dHv};J}<_yVr2iZ$0E#5o>;zPR{V}x9X%M|M&&qeYdr^$4w+b z$J@l=d{>4Kh6ALAx5*6hCSo62>vKK$%zwOvK}da0voY%?72AB_ruJcxiboaVk`Ia) zFaIoMnfx2JO6QP$ufd@G=?+Wa=lV;NM&*xM-72wBP&IZc$WOmzjK zrSVwdfL)OM_?+C#ACpZTl5KeZK$(|kG8C^o#I)9yVyNN{b`fzr8&#{y-g4b?mcCo-@Rh7F&Wh*yFQxGEv}jR zeQy}3g;B)--M@6o@p)ALPemqTygcjETw%};fhFx*YTB91M&^i} zbnkjKt@DM2EGFjkuuavccR4P{WpD_o&F*32Oq-Fx$Lm-6!}0VY6Z>Y1H*Q7VqVH$e ztUFI3*!2QW@%2#0Po_oWeN^Y~?leZyv_o&iFo6jtFBG5Xf*#JB8qRFclxx^<1VZ&h z1>6ic2>FGc<|H%be$&NE>D-ngPSoLB50z2}Vq#p5`J~oW^1KBcv#)#f+tyEq9L7CK z3Ai5ww*F#ZNJ&V^`=Qi6wsr6UxCXUm>` z^K>*2alM%-UvL(#uDRs0?3ofz;NYE>(-X-sdgmyw$L!xj-r{$o%5lF!jdd-Oo!T*vsP-tDq3&;keyBc-Z1n(DL-bL8OVWH)8!nd(G*{%3Nq_oZ>iL zm31o*j^t2gQ|vwH?nTQpEuF~4F0GH>{gTV!dQ@Lc{s7hZLVa;Z5x?|)c-i9^G&frn zWpXogv}nTMavfhJog`%Nr@SFE&EDY0Ok?&ow0t0iJOuGii)=HU^TwX16th2rw0m{+ znm%MPvXg9_?tN>$#TtxyqHM#1!ApA!1wYDv`vuzV$m28K{h|jo+=3nWQ4mx$RE6KR zdi|7qv?}X9f5(nDaz?(C4gt=pjFiN5yZ%Ob`<4G?jk{$?KeomU3SC-|74P9nb5VYX z^F^D3zb-c5_%K>s9PyLYRU_b_6wBA!2P0+ASHacu{!_s z)hRa4N#ioZ;n1j~qPsGCCd({2KNNkW6wkzHMos&(XoXS4IGt?k0G*dwBTl@R^_^e9 z%$cnrOL35(Mm6dc5qe0~G<_ZG`7=nJM>E%ZPYl#zxJI4yJ zfr-M&2oMmbNY!B`BH2_M#Bd*9gOUFkq@aFKLq~$xpyA_dO`mz3tAcrYNGUGre=35< zz{Zx6njPxpl8})2{Q0w!+)g&Bj-jD@g$@#R2-?|oSa5LrB03OP2r`8H{8?1ME-Nif z$a|ZdlT#EIN8*2G!avT=&TjFSD=I1~JUqNsTT^|0TT+q_0Y2Ny^Eh*-tD=j490DSn zf{qSkwYbGg1Tq>7OyLMKUsOS1E)sXV3=$0$BI5r=6|mG)+aQ6?(EZMva0ZC4g0a}y z-RMfN5TUO&MEBdjr@Y+LaZ$lwQ@+T{$aGBP^_7*irt^8o%I9i-@*)=m2Vj{i)%7Rh zlWY-v1!niV<1a(Qsxa?FzRl_Q=I^bod0=5#Ab$v7yD&ozl9m5uw@HtRd#jy@@D>7< zn*g~&xx^?#0NaaOQ4xcrd+4iA7xWiJMM8=2p=uKe1Z-#lk9hIPRsOcz+(g-oautPW z(b4a#;HU&5s>_%)2!+$Yr`4QFdXi0Az%6>NVzt+3Q0U@3gyJLR{5;fP{10TDHA2Ft z@1rmA5LNR-9sY|XkZ-^~O}}%boGhq|!2=~>aOYxAILKO4trQNqjfy*K?E);dc2g<8 z<1TWBtV3hVZ=Wq&*Bmh?v7qm?U<2``&Ngdu*kPBD$bh{&qaXf;%Ac`6-euSY4fFP` z(^+^|C^n5^0g?L?PuM8-v)Od!_w%O*pO+%(cbPg}T&NJf;@-k}h2uG&s^^79COrA| zSc;aGgw)k(sv`Wevcx9y7cigVhK+yWPC~(w0?xy7+Dbq$RW z{SWVOaqB87E;f4nsU`nljcGAjnasY|h60Dl!Es_?O2b2lIEZ5Nhd~{gSWHAU9`!N5 z5H*E11m&lG;i@#dd}2=eN_o+2nwWx*hi9Hy@*2R}v6?FtOf1r4#q=Bg`BV9DV?*&h zYive^G$1<8sCs^A;!|IEPu=Uf3lbpVPUw6*@6cz)=TK^M9nX=*Z#~<*F*e$gQX)V@ zuh!B=x!gChzdL^;ER2K|`WZGV-WUmQ3M=j1tB52N6u1@#GXar|+P@feQddip!Ye9R z69!W_j!+W@6C#QKZecbz98S)tth6qw>*_uo&NuRTaF7MeEz~8%#0XJ+r!^>7t9m03 z0fDN{!eR*pwU_p(4}t$=Yb&qWiS_KdEix`{wIHHs4&+G`Az&2p((HmuLc%&VjR{Ml zpirvUH3W|!6^O(-Ijt;;@}0Kp8GP7{&R5G)$?`XNx3JIZ0pF1TG==(ySfgWF#%@`q=4xiWbSI=5MEK4Dj$QSy`+U>+Zy-1>g7pU%@w1 z7*(9H8X%RB-P+0&(nq&p>uPQNe0N6ygCZmJ<{j8WWeTP7;9AYCtsQxh&>vRev=GRS zqN5OXf8gaR`ux_2jd6+=3&^?TR=Ihc-Ov9%rE-5kLYiKz87~1b8IGpoLqfjy_C{Z< zn-;DXvg&dNcA{PT)_LErdBP7FZr6vrNJu~KZ_&!NJ=Xu$xSVZ#o-EMJ{`@-G$Ku07 zzwd83EzD`5VPq4ci%q&7D|GS}7OZQl%vy~Gao8xPkV?Vkj!sK!|NLCeq?=E#fuRL5 zPmhZ`+1QwEaqB|DCm0KV&HB7ke*U~J*Onn6xj`XlP}bcaT0fP$CRQ(<$yAnr9}BzP>(5q^DF}rLOMCcmy#xVie=7 z*m}{Ko9%LaByBT=bM|xBXTq*zQwRuVC9NSsX8nhqQPBd~QDueb_rsFud=FE@M{C8( zT~soM-zoi@g21kcnVeyG0pz2gqjNS@^vp}7`_AI_@eao2hAcchEeZk>oG2DbEG$R@ zs}l0dv)cPfUA{z9L!&%&u&vJO>iMn@nE~o`tpaP!a_aT`yGGJfWm8>=d1`5CH@>Fs zQqj@X`n-s6r2I7Y6MzBtG!I z`!@j|>@zXgGJ_k91}#0VEji34o?~MV1$2_a@JBQF9vnxB5_-ef$Jt)BVtyeqn(2H@ zP?1f#v$3J7rgjcvfkyP~dN3npG4pG8W%=FFn^zfUXsE>>?xa0m_$}5u1)OoisASQ0 zhVEPjVs$qATtq`(O}LOuOf0#rO{PGGZ>jdcd^sUK_Oid<%qD?ROez8Sv!-wGw|}>C z*YcT^fDegP65!|`1lu%WUs$}Fx1sK@ZN~L183gk!6}**<2lVk ziFUVJgHug3QKJPWL$KmfgWRsR(SMHBkiZfOK0clJkZIPM|2=GB0?tF^5Y1Me!`?7# zz{COr@nHGEF4k8daM<|%o(FLEW|PQvjkO-QVeRdOd>+=83xDUb{7Fbrz|UGknVeF( zym9e5CH9^!R;*PW@Of#~uAz1XyaCx4XOW>-OAii+XK<>w!avw?p(7^F<_Ldze)^(1 z`_}8BE>U0=dK`LOWSlLG8o)R5k?=>3mfW`Hs?4ykq|Hw&fx~cq!P=-giAuI+j^hgSTWM@7sZ}xnsAFdBeHW%@ObJZPYDi zPxodh+E5V8&0M%gmctAG#gV{xF$v$Sh`NN+eB5KAb38eUQ{S~# ziKu|M9)jleVD=qAkG&KJ<&`7X2_I6@&pGiG{XKZfv z<2Dq%^?||ul~Q>?lVCeZcEgl*$Yq#$bnCWQ(ZYn4`+-vhS>4V5;>jv7q-mpB(}XA& z!wdH=3-8w{4^KT=iJu>BaWq8Q!ylLAWu=UV`OAd+gfcOmfLYgy<~x_xUJp@Xl-JNk z4pK+xPjMNO=E$7;x6$HpBqB>KLX1(W095Oxaph0C_33R}r|S%pnyo8U`>R}xBmoz@ zbNz2DB8;x3C4^4*mnG76ECoDW2Mu!)%LY+Iu+fDQhb@<_QkdarJW}R|ysuGv8$oG@ zy(5>GJhuBYgAsXq5_f(&ZJ0U~La-!{OJ6*&pHA$WDGmypm9k*hpVY!2@{)r1ng(31 zxDNHc#|gjcIAF7l*Qc%F1*7kpXP<}RzVWHnG_i<{B*4~yNhc3R`om&TRIxePMb#wC zS%sE+KSjX&xAv(buSL#r!&V-b?^d6j#HAQ8opRC@DX$n?94^=sP{sVOpB6Y#Jy|@P zrHg7@G+b^));J>8tuPeJj}!TvHw0!U&9b1hk?(I(D{0>g?WXp5hw=*V>iI}>DCWPr z!q!3Z<@vt)130`j9aY5YRCb{K?C>SWb9a21ZcKazrdh_{FUcnIG%aN0@Q(+4#A8Dv z=WXjduI|Cqe4BD9h6kBFi@&xyp(uxT&6ho+va z1s%hAG?>agmnby6`a|RarwTEw7)o(8La_e&o?h8veQugFDDpY5B#ximh2oXELblUg z`>_22AgpIM6)}E2pGW%rJX^WO@LbydqypAwcOTYUeSp)ihg41YU*7G0$B65j`un zq$_*6me8P3+CM`l=X7llP~`EB(rA~Z??)$e&}=H zchV*or*hK9pxz0;Z*elFsul-J5?fQl*29LwKdE!v8Q#U*gQ@DzNKfBs zi#qj;4@&}UY0!>sOqGx9&0>?)%sN}S#sz;h_98wDEJ?t$wz2k|tQTs$g1j9ZB`uhD;ePpboK~ zrd5=X$Qig|{whrofPsUrdIvi&FjOF08xop?Au1(xfq@~3fx&ftPQc9_9*c3}5;0Sv zT0t8G>-#MO92=ePDL!B9d`K5~K_z@z?~l1TJ4@mv8a8ABfU2M_iU2u`T7$lZ^{(M! zt?r$_hO07Zup}{Lq_J0*#h$6yObUUB^9piuLX!a<#E8%_gIn)~V&jZ7YsRcrbwHw_ zfeadKY#cMjRh1fV!D{pUSXI^k^01mzAdAk*!jCRAXGxhMd*LcD!pw{l8x%CW)c91Q zs4`WqT|Zkv72v2_Q#g6KH_bg&rdiSZ>x@BHHY6MPN>EphS#pm)9=^qJsu{b!2n-wB z6q?~YkOah4RXJ;EX&4))uYrIh)l(nHam0S@el(E*na5)iVu(02dJ-`;EzIuhkLT>= zONV2KKG_OLPf}25b#OW`CfsaGyYGyMnHs&8DFpPZlYp41(2y&mN59U-hL~;rmO;Hj z*XLJg4jc*F)7f;%Py$nly2F<~8HeplgO7zF79)x5B^Bl6v>Ldt6GjC>hu!Itq_I3! zdMsg8)vYlSRhpvx=SPa|ZaChJ-9#?!}wx zzKVgV@!Qb$E{Yi1pM;9)=>()07q?ZcD>8Jp>B*>So6@(=ivmwNts^BVA6tJ@D3kU} zjo$bU)YN)inXW4tMEdY(_+&+Udw;=TGXEkz9>25eL(VdI9=?BpWb5I2`EdRsgHBbS(}su; zjU@-LT|a>^+{vCfwG>nqbEO?z)>8B0?i-h53}d4>5a@KWpgTA?nKlnVQ*?DrLP#-0 zEz#cUup_J#3CO)77rDpH$5m^!zmLl{BzYos1qH-WtT*?l+tX{(g*xp{r&SVj}F_nXE4i6Nu^rcLsLpH zgwU@ssQ+TUE(4G!4yQO$6<8x+Bg-kpzW<@|LpDhW7I))-j#S?L_T%~qNH~L6f`GmF zqVvaFb~cPLhqZdJLl+Ea@kn4c3IX~1N2I4O45x&uRuD0g!bQTeu(HA;q#SLr?{D<% z@VS?E$q0dsGgD_^laTkj+KizyK}TXZ7tWH zy}w)5sI&YtT@reKX%=4j$a6^kn$7QL1fif?TGhL|5>(e&J3U;Zsm`XyJ#xU)j#p+4 z)L9vHb(vseVQFgGXf%AJW@Saudp!t9sVFEGm!JMbl4x%I&1KR_1?l-0AQSSZ+@9^UG(q3DQD!%KR4mLJdAiK?nPjc0#P434$M0hdu35*S5q_OC{poJ>+vo0ePd5?Wi`o}_)d z-(tamQ*hsft9z&W20M?`w40E1-g`S}EFXz;)C%U#&p!}Ib9Gce4W7!Aa1+ zfHydh-uTj! z03I@1j>N>c4V-Cv+cPP{#vxB7ShH7xm=q0a)HJ*rt;cRDkF&y??}!P*v)lZ@iGy z#!z%FwuFW@>ggRX)`d+^D2S-}0^{Ix0MKl^3l~8;=p{`$Wu(7ff=P$>4}kMBUNibO zR(?3WyFA-mtrJh=ZA^&82!x9QMx2tZw{AVZMD%)j-prNYGZ{TD$n*_uC4;$#qem+S zfzdueSQ49++=vgLzVY$CcEP9nD^UQl5PS0u^yVLBO{sEoLs`tG*VQaV#>abXbbkd> zaxX8Z04Q$+EV$_}U{BXip*C9O?t~NZWi)Nkr!(NVVa|i%Blh({8vH*G2`>NSqQj(1 zbbs%Z9HUqdKvvW=>-r~uzMCTg&p^2CeG7p`#mxzdF*|GU9SjcfH;fARI&q11)1QQwrJnruJQEfC#~MCj_|^K#5pxcA>baEy%~Z zz3q;VJ+-pLb2?e^?Jv#I_PM;gq^GAZc8d%S4({&m7Qk;G@`r&V!DD3fsl&TsPG^&T zeqcH}oEKqCaCWjXHq0gOHz9$6fsrqQM}mkZ2Tpgt(N;uJQ3Pn=x^Su5>gp2jE}+u< zbaqO^f)nyfK)^%JXH!C6AtXebT2itT^z-M)9ct(_B>5#l0Pz$OfDGV09-<&4i}Ai6 zy{Dt5{xd!@3I0dGiv{r2Da75{|LQ#jUBy2JBR5pxU)e1Fk|b`OI*Oprg? zEC@=-S69uCiGF9+y;h<@wu^ka{{}6kGnkulG1Hu+JL59xHs1G)XKlSCD4`=-uRbeJ zC)11hXj~g2OrS(46f({aTD6V(4y&ykdtz^Na#!9ZRRSC=oO+XCjzk@c;4^6mFt#MB9npZetlclO3iU(+KmtTkQT3nWvw@-@O0Q-aO&% zel0ejV5b$@$UP$cL%JxskPk1P$8AVG-)z8&M+~aRvQPMUS&}f8c7Ivkhjz@MM!e=g z%$)T$4z1?+u5DzC5s}$)vphn6=Iw>JJaX5lOIk6jQ_VffFIRB=q=XL~*qM)TM^8yc!i#%FMf-KX)3GsB zFRz|ERli@9YH}#ak6RhBGVLm7eXCjezpzEJ);?|A7(B#9lwWLgc3ylL{ylwL7gX(7 z0J7;Ns8mIFZ_@vW%gdNR8Bmm$bN|@gHh6Ll@|-rp8EJa#-5JCMc@ERLhrPM%vk2wc zOCANqcD$fP{;r^VsL^DtKV7n|H?}Sa9k*Q-$OLFBPPh5K^IpkjMeO@r>w;6#OkY^} zlqrttay&Dz6J{=gHoOZ_Hq_rs_SpaSpNd0Z)@mUHoTuRMX0J6a-$mPxo3P!WK5RT4 zXTl@QVX}b8=%53!VULO7gZsgR+14h$q)+BgIcP%S{k37A1U{W`vyA@gwj`?lXx(1E zSfQh>Im=_e9lYg&07p_9u2?U7M2+Lf=%MMlZllx6X>B#|d|bm+P*hNIeU+WxlI{6D z^|ImM(~h zXSqJ|Nm*FZtbHU3Ks=`R8YOJbbH=g2=Y@6+&I2$a{cdS z&07)N09k@f;NbJ0XDOQoHtB?=WP=IoJF<=^ww)-G#r%t7Pvkn)!%E6zI@gUytoX&1 zi+3p0O5Le?I z9gWk!;W;TSu z%Y665?&6id=0eNzu?p)wFz<;7H#%`IuEt9f;_0%7mrSW-qFF0NLGD#>NyRo_VHZc9!OAU2T*gNdmHp%I=@9nwbVs|BH`d#J_}t zr_uD}Qg8}NjdnmwU%uIYX*Sp#+OG4@4R_5Df4MG_d*r%p?~$d29*EP~PP@6Ddo*i{ zkcy$ANs}SGyl~*36tY;!{601-4jV;OdDf6{GeU8nb)lj)oM*A`^wX>BNrmqRcHg&; z~OtVKl9Ekw+7<; zd8#bzdXFwr10EAsM|`VxhoBt(%@tYtXrLErv4hLF!!VWWH7)p;# zy8sad4@ou79iBmkWffKdi%~{&Wpn99*Xy40`D#F_; zuQFpc?lV%A0JSwkhtUF`Y_hEIMM7t|@Znv?1NUP_g$-)m#`gr=#>q09#oqmMcnxVD z#3wrHYgW+4*85c~+wKf?_LYGlacv#>!9Ske8*A85n6p~D7W5vEkDm}UX`Ix~D#dpX zHpEVFmeiC7DGp66i1)3Z1~RM~egoAD{*uF^^B&BfEBD1bp(bY!p$EOr7R5Q_1*H}X zgFOceFyWC*3)c zByMY48B73kCcb9}t@dY2^z zE>*yZD7(w|CNqZi7+X}$^C&1F!DdxgGmSZhX*qnSrn$`Vrrii%!8-dV-Q<(U`99?UArr2pdNoF(9Eg7YV7`e5wt?v90pHO(syuv7K*NqIhQ zBt=HnyIrtK4F=ZLL0{EC5fTsphmP;gAD>ht3m9YL zcG#BC(TU1f6``R?ofJ$x0|(tGuq3#+89CW2&U+K@n2V4-t={++PfjR!K5z&}LcAA} zlx*;Ox7%Ee=+EqC&-AG|oL#3WJ-ulSI$;oGx0wf&MSrVf``$Ksa2{{=@e09)_8=m8 z1^WVZJD_ZrO>J``|JC@12^v=Dc%D=YKy;ZB+mn=J0g8xuW@AT-b}}*^p&?wR*r=*h zGQ0p57l6u`fQ@$_*|knW2^96#BA8hwQaQ>Z#%j6fu@t4G%tncJ7Bq_{3!^0WtQZqM zygys$=%8Y%z&U!e5!AJw#{X2vcG5pc*Jig#(dh@IZ_l~h4)e z5D@B?;Hu5rH)=h{P@Q}UJHLBuggttrzdaC_`1rWNvdWADNF%5QEMEHubnFBU??)X` zQS}a#wZZcy2jlTUxSv8 zmezjo`^Da=~uXV(8>Y)piQ(A|D_G1{>7p_xZC zDiT$T^DQ`5j;pC507~G%Vx+KJ*d8sA0!W45*}0bVzIk@L^&&C}Owz=l*YR^KSf5sI_iSesAffCWfriGbbb(bd4UIqZ{yq8xpEzn6 zZ%z`T^yugY;o}8ZlMv0!>iqmJ+q}?uoY_QTzR$s;_zZ2rfhsXGWP<{hgprllfgI2` zeSXCLOdfOJsae`y-d*s0?yt+37XK~Eu-y`qnEXg)%|^V08A7q z`Od}dc1}Wqsl5~v9^#%5AMa1C3|XE84ZTZuIoota<~mcaU;PU`gSh z=}_}Yw&nU0N_6xi0PYzJ20$v>?~NZJZlk^daIF`Iq0!clAHypEgz6>X3rm6_I-E3l za=F)6RYhYe`C7LE6+iY0Tpd#&5-ruKv!<14GG(ztf_WH^TB7(bQ=f3)-+=k?r&0}C z&uw0u9+zq^FW!&iIf{=@+D?v2`&V?Ga;RW}zi)=?J)7O1NQM&CAns2tAZIK2yEK#o zGDIjyup=*w1SA5OB!h!eFOHiT?l8iiA>_CZ(UFL=BsrhwTij|)tsfPo{jxHFT7FOO zb(aqCdmk1O&A&4Blg8t}C_x7Lf!h&$bE%upM&sxZ zdMr0HXxo8F#Rffnhv*KQvjM(8M+XZr01&}^ZzEH~e2O6e=>c5}B1$au3Ml(3|8ut_ zz74+nM>Jwea<_jU1Zb!5rt(a7`WrPYxP~-k00=nnuYmg$fP7wH`5;sIAor1gJF-eu zR0!B_1hAPz)XTShz4=$eDN>OT%IN<}TKJ&1nE}vhfDM5fZ{At_stV!>V1%QNe-;B( z6d0jS4;ao?1qzh`xETSD4J>OgJTO24Fg6&^eCD-$y!ZnAMlgDv9N}~ac&q@F2Na|@ z3jpmrO3dBvM0Cla`u_Ds$Tq{1hj_npbVT_6J&AWkXnqk43SQ-W3#Bwa4YjTL!c=<6~1ax*>qEU9K(`A45_J- zy2{E#P8*$JBJ@o`IRP05fEuZPiy4ruSvz1lN!iMQmKhlt3BVf3JR17?`r_ij4bC5t z!aALgCL$rGEi;PnHReWV0#oog<)T%`t!A92LSpQo*zJ}!P+^pV zBK6l?u$w3f=+|pqM@j)P#)soH?)Db5w-lnoy~e`WP`!WS0#&aM{Bh~s7-FnP zG*65tY8Ufs7edGMM5OPqXgo$;_~bNdtJFboDb~3cu=iB<#J=}2kY4{sG~rpcIB7U& zm~bZbC+f?)LCg6xlp^p2=V29Rbwl|IW_UN;sp4a$IXLM=KsJX!S0FZ=QPX-}s5PGU z`!98|w6TQ`&B7Fr^YWdrQ%`zaKAzGyaV*vxdqkbe=6qB&nEhxd<)dYW^7U7vvuzUl z$(VTPPZAnWzvsIz4^-axD5ryxq!AhH=dLV$8lj{CrsiCwXHucR;qOKVi!+qiSA)xX z>1}>Mya;9fKY%c_Evz(dS_}Qo?FF{uGj|@{^7!O5HR~ms)8no3U6PM5&6D3B4RbJygh(}UZa@3q3LU326#wn=1;j7OENtC$N zbLIYh!X3>zx>ClRPSI%m;3ZFe>wNyiRky~WDD~y)?x$D6>Xy%)O!voBr@OGx1V#VI z&9NlTnMIc=)i~8DmRfatjwD&yhH6W@PCCo_gTK+w`3uEJL#fDZ)KPPkg|JMF^t?V( z^qOHd_)Fq0_YcNHkRI$=RNT-p9h>%ht2UDp$@m9sV+t&14G(z?=c8XO3qPHGU6D;U znlN9{o*s~-|4=A zd%|4I2hDgI70u_AcUA!)O^Y66i*yGtDf@$KosDVZ`O@IjlDHxKSB zO8;#rEw9u>RkSDBdaQ7M7xamkjDR5yRrWNW0^j`q-4S!?Qh&Fns*1#-60N}T8)tjF z@uIu+Z}tQ{EVoR5nwxZrEEmQVBm3DIC`L1Js8w5NQs{&pzD~6Yg|RrdvzTz%f7AZA zK1vwtM4Elk?apVi0DwL=ql6B;K3_V@Q%Rpr*9XHrdn}0dUCTK`vC{$H#kIRN(0RR_ z7fLJ^#qjocp!3gsK+>S;$8kl0Pe?I$E>fn+`3|z&>>rbzT2Bi`-BT0$N*cP)t3urA z*^2!VbR*rIlD@A-|sM8jq2WF z6vF=%B*x;Fljwdk={)$s0eHHZ_l$q!Q^Ja^Su$fIdtdJI`Il6tonK$ltx`ssFSzG) zssPqu`P$D5O*Lze(FgmPS13f-cBT*&M7*_^N465$do`92W3f+=*j}>Bb?3sH%z@v` zNm}G9JGqqoNXiJ5qdSl3`Q9rj>E&7L*ti1t3)FXKkn=#{13}?&vY+Be|Cp8;Balj&^6Yf09mPu>Cp->R)6!LGmEyS^83QOW(8M$l z&${ib5P#fIcUg&v$#BLkr+qm@`&V$@$etLmqS&{3jELQD1g?Yw+kR$!^TN+F=dc&j zC8GqIU83UiY?f{Zn>hb%N8tzhe`0#ScA&?s>N?Dtz%3x=m18XLFxLmYq#Eo{hS zB8M$4x6VT%yeVcUYXC|i|I2?(wBTjD`z0Aw$WU^$+EeHA!bMDa{KBu|8ngLix*M0n z40P`ed5PpeKq&|A8bf97%( zr=&~B|816voMbDyTp(NGWLnw7*@a6+e?kXJG{xgl>@`Z!o9Iv=_B@BBVqetG zu_+a=m;2=e8W-!w6cJ1YBmm{<_y&-AtY4OC9+?U$X_>cwOw1pW)E${M zQC87!R4b}A&7!0)4nW<5#E}%#NYh~K* zDa&TR$Z9=Ws$E=_4x0lS9tt>RZe(gdEGifKZJw?YI^$Pw3=RH@$N&m?A$hrZ)ItF( z>iPTvMU}86p_GNL#D!rG_wPqd)Of6rLxbI}4vkOUckDC;!qm7+_ztM}c5W6fA57@f zc#t1GEwA?%w`H#He*s#lh`Ti?O|7$Xz`{}%f~BCNlB)V@Vx3mvXgabG>=zQ6e{pec zCc)1)B{~}W+t!ZuoM@-*Wq*?Mtm1?2dD`(tS2|bKVEtPxlY%_(MFH}1r<+>L+et*w zR`GC*BHhjKsqxV-gA49SHce*yg>YSIU6hlXsW)NL8#UKiaf97*>*6e+NN8k~hV`^V z;Gr&OZQZt}JO&(8Jd)?;V#`#wOQY+m90|H;KE~6obgX1fZlVli7w<#;bupCe3I0Pn zQ0ED~rpOlN^A<%w?#S%hX?gT9%-;Wq1HEATU29t1Z)U+W%9wRe;1Hhwd^z$J@Os@F zabtz?eBYkaew4-Xl)9>s1EKh3ct@CWtroc(pB&-U(8dk+Y}nC^(iaIC_1T()(AioP z0us6jpdi$fnU;6adtkOC$-{bYvRn$2ZJOLm{3e*|UzF#g(AfcX2s|R9qpfXWR1Opr z6gLmghU0gBGV?LkGa6qYR{^fqNbEm!4FB)X*Ag%=Vu@k(fxOy|i?4-G?_!kG!4O%$9 zKh^~NR-IDuXMbz~H7>L;z>eF&Ha1FIvt=tc)RHj$A(<@rSXhWi z9AIdYQ8M4fk|b$v-lay*-r;lih$bX?`$$Su5j@J2xkt(mN*ra%-qYs?0r&ZPq{vh; zsE3fj=`?$Xhll2mi2#vfzka?tLZ{)Bp0mDi$OO^9-*Aj=-QF#4IVGE#o&9dJ&xFlt zAro3EgZFMNiBZkfnh-?6$2~2#w(!j+C?w9ct62I8yQLSJU35A$~E`{;CFSA+2GD0U7f{o~7qFJ}+TAFwl+;3=Z zMiUq`T0Z}dnyKP;BF|Ll&rfueF;xBSjh@|TG{&H=W>4izDK1d7Jze zoX4Slr8Ky|e0g|>y01Y?sOejYyPam9|+^|CLx?FaRhaF{76Z6W;4g~k80PAme2m?s;nMCLB@Wu8yKeC{}iyA4@VIHrN_r|_itkh_bw1_m+X zjNlCgOG3_b?+!G&GeVAc!8oe-{8LEiao? z)#5|U%^)%+pjX#hgPKqrfSKRuBal8?AcekESa>@%m8!uoHQ$($vHFf9MZSqQxNoye zo%OuKAG1US$>;WC$AEcGSeT3LDPM<%88}Cerl#F5tG;OSe&XoqCiid)3klIUUe}v! zs0#EKrs7DU5%+C!W=Dok&d(&)mU0r88S%cP&bl;+M(x}j|NQx8j7OiC?Q$Fi!7=e` zZ;@hzd9xON$_yl=^~3~y$LMC*FScf9D^eyVKI_z;=@JBhl4SHP^LX8EXvs4#VfL=H z)UyTyFt$hx0%Rtx+w{DxK!CYsVuB4&cXXQAR##TEB5zQI@@0;Ox6Wxo)HITb_zx)^ zf5=ILQJk2&3f)K7Qa87xaKsg1HudCx3J2Aph-KY{Y2o(9kFc zM_GYvz2%~c>@Hba+6_Q~uv+Cm4zf};*E(4_*xZ=#c0H+_|B8Zw?{d>-`um=apTF8- zwpQrH5nv9Hkqz?{b}iQ9D(2s2@NDUfGj)c5Sr!q_Cv8~xDHHi&D*#0T=$E2LdV3OyL}DvM%meKlP?4_!;63G)^2WH5LFM*k-SK zLxW>Qp6=@(eklCJ%-rAXqWj5dKdQKR>Gn*UO7=J}mz<8&X|dsK>H0|ZT{Bs{%hrGp z&_r1Cjc>Ya@nxkI-P?}w?b|DWbwhfmOhQFWtpD=7cz4$`GgG#-hsfeNYDgK3f_`rixlcZ6jBgJg0+DGHBg}hm(zJUC)SX47@H6)uU@`RVh#!Y&&wC z4#cw6@1oI;0c!#jMTDR73yuZY-f|hXTrC$n8Al6FSN$;}4i~+wCK^{qi+TQ zrg8bqaXe${Gi(Od>}`vKjY03k9j&u#$*P@s>D zrRDF4(FBUo7^)jbr=y}$`k+dPDS8^3cmSYmZM~Z;_z8fTTEEE+H+vnBk<%z;4$E_L z{B8lr&yPphxZlCQ*5b>CCXFDW>7RVTXvS$M{t3_s(A^4!Zir6lSmBAH0QS%z$;!WOt8JQE zj5bq+BZ~Q#)>E)j>5w9#^KifYVNPHv|MZ4c163Cqzqf#UET-n**lPc-+0k(WfN9#= z9smxlC<+Ds!uSokAt%uMr%`Xs=IyBpR5)tg*Y$uQh^-|Pc=7Sf>9u(bA8*UmOdyEw z2V^6y=4@xit5_-jfIYh`KwOHWW7e$G^1K8Ozt0TF`rD|kqeI@?yB@GPV>7e;{RMP% zLjZ$X{$De|rG(km-G(nW2G$B$H2_n5HB@mh@y%#Y+AVEP96=~85s7Mc0dR36#q?Zta z^cIj3LX{o9?|f(X?C#lrcK7{b&dGc7CUa-*-22Qs&vXAC9KWl4QyZU@Q2YBX?zgG| zM>`$3PkEt0{l9pm!THg`3FEtW0ab9b|@ zMgDs(va-9y4rxo%{m#=ZOhrWxUo!)*UgI}ful8C>1N_F0PUz}5`KFz&3GH!UKfm@4 zeUX%7J(=8Dc+FFbg$i>Zi-6}dUz~K zKXYRLtN)qU$tg+j0H+M=r;j|h?H`7Pu%R=v+xSEvx)HtfMYi3MOx(45fO_W6_pbdU``XqQ+5&V}^Qvx2f zZj?I5a>=WT!CaT8rf#}Df(k@EZ-LxBXJ>bLPTXvI zT1=4^Ov+7wYm;uRJ)*8S0{T60^!oZSxo1xA3LtkW;_>e=2gt3Zp>;s93uYpIKfwV; z?g1|tWH62U`;Scs&ww6IE5VKzSTuz!=_M9?w*-7j5Li&ZCvad0y_Reyftw-DodD5o z2>2~{{jQTV>|YFnmF&2e`6B0vHSs(pN~> zq4dyXz)3&D_t&iME54uydj3}@Xo!TbbGtpM5r6-D6gWo&;c2v#ybfOT1N*gc9xzyQ zq%DNyoVt1upj-l#CT|>%r~4WZNydqN+MDme0kQBIZV*ol*4Cbke-A7JVp3cz!pV`a z^6k0W1zTE3kG`zp)~;7&-68PS)+jXKit^n7-NQtK?t%XXcrH~1x`F^_2r;9zf#m zA2hsg1i8x!a9tRe7JAcecgwj6;ds9>{7@+7ER@1H;7ko+vlMDU2k?qIsC-;(?~&c^G~IFNU8pmv@8a1 z6v4SrT3Oko6Ct$aX)zlUOGbQf%r$Y4VR$2KU*!;oc&(~Ma~4h$4T-)tBQ55M!7_N< zmV9yfc*ESmkav=2^4tz4W$yP(QbR{e#2Am#lv<}ot|8lo%HBb9C)ofoWKSmS(SIuV z(#m$}(%2hpaKVa|tHKup9gz}bXJyPlt$+R&8ErgXE+RhlW{d1E>ypF`i0+on{Nay<5)u+Mt;V(oK?Fiy3n~PNhl`u`M_S*TgU4J`sv{DQt`e`$eLr%ILHVqT3fTMW z*xABEUF{L-Ort2(;Ld15`2nTAH6j-KReCG917Ey)$70&k?~g`%ZH+L!=T>&8PDn;{ zXglbF>oNTc+-v*WnZ4{o%eQ<51YP<%-n(NHSZY_(t&+#(3tIzJZbc!&UzC6DzTj9-c0eS}aVD}MP zty1=})8R^27uWG7<$D>i7xGSIm-?}BOv894y(@m&tXjy}hsVBEnj24TsH--5Y{Vdy zcV2qB&GI0y-|31u=cL5W0q1G#P;+k7TsWfi3MYZ(;?IW0!lkc$rKZ<%M(!DFzSeN- zA3vvfP+9r(blgV%TO@DL&pH3H8&;VpTQnL`N2*;L=$5ZaZRwt3rD9Kdx|0#a|Aw(foYl ziG^uTF%6qpj)Hm(v0$M2VOf@}v6PTYky-?SxS5q&81>0vqIQKicIhA{DO>Y#&mV=S z)a`~pd&~-ooT9cP(#=Kw`Vi-3rRBry%s?qF2)iN!yokziL z;B_u2>(rd~nBC4}h;kLwF;l3X8}=gE%$jO%%P-ppe|HqsSB>P4J}CFlr9N=1t@x0b z_$LK*-;29=_o;E#{eeQyeEKtTlilW3`C2}xAnbf|M$x(_=8f^K{xni=RAVH+f$7e( zy)uLle^!@c;KR^1<21os7qeL&DU%A$+{Fnor-kQuG>tvM7EKstH233KGFMz=|LRv6 z%qf!N{_SCCc4~XLo7R0x#ZhoqewI@xil4mJIL=9XV;|KSF7Axl_C0__e1msu=V@<6WYovVw~Bm)NQL9cX`!i zg`t`aVsBj=MWM6eo1G0XO1i8FTe!-dt0wPb5XbG=#+PAk10x;{)gs&Cvnjz6!u)Ez zTo%*dX8d}av5(En*BU;(lQR8KD%aM=!SPdL)0=O;LBRk59;E zLhMi0MCX{uW93eR8|IvHTgf?Hy&OwMJ*`)2_BzHA+-i(O)y}0Ur=5%Bx1W(Vl(H0g zN2wb1aNya{re~XD8qq9C=9k{k`7x{d*nTX@C8`vf^ZX*$-^1m>K6T) zxLX~c8U3aHwq(_oMFift#oYP&)GV@8D_U^p{L;%7^fsME#iJNtL+ZGZd`A{Byv~qw z(5SEft_O0wM$CUDG|?P$n~_;a(DYyFwGvG#fbk8Am-n#3xo8VJMbUe=@r0^u|sk3YM`w8d=RK4$-P z)zX{xdt3JUXmx$w6j8|$j$ymCo;IZLr}m>ZbrZ%)P3{=5@U@FEF4MPwCvRDUr%DqO%-G%hA~CMhG1~tC@6|m9m8)axM-8N9QD}{JdJHZ+>c! z-3F&K&eET^mfOyKue8bM+RM*f-JjR>9MiGAn8Mllkie=`45_Y*Z=tfiDyFt}#%X|K zY|wxBQU2$8bY#T)k+HVgzPYdmhgvl;>jNDx>aqrtuCc>K%L;u1n(B@6b2RDo!p1Ll zgB0{xCNgtb&iw_sD-^w+b6B3fjJF|tx4@-%JiK|?rDzRSS+#~i!aOAUWe0A(W8y@? zT)HD}Q40TjwcRwl^PEk8sT(XZ@|10x{#y#Xzm-%MWh(ZPh1^tEhHhvIGxTn}f3JxY zooolmdY%y2Sui5GX+j!voAz)q?2Q$!M>LU&9Iplya7a5QY&$Jfoy2x^ci$>^Y0c=} zG*)tUSiOKOkda>;o~cgiKg6GA^t0cfsbPVmq%bP`1`X3$JEKNRMJMfxn)B5)+VJ>j zI3nqQj0WrZ98omVq4W-){#xsD1#~L1H_ytNAA2yK0-MdYVJegA6+eh3TS{K0T2%V% z4b|6AN)Vovp5nEF4cbleS~@-Nkh>DZ@_AX?oN?!+wSc5DtF@E{Yi?YgV4x*qr#OSRKy zQ>@1{+qc#vt&18fc@s9ipY)u0AsD3lbbeUTlIZ$}#4d*^oX@9kY)XuAI4oDx@E1Tv zs!QC|Xxc)dicyq3B2=zVoy$qIc6*?neYZkRreg2p^--^^@^LNFnA)!Q9Q*oVe#*}V@(xw+twXgQ z+Urp6tIEmkr=^~58yvDT*E<^@v!(XU_-z>KawDpk-g5=M7~+rw6WC;F0>5m zl3JDQ0Z}{cqQTy%V|vZ69pA__n6wLxp?O_?1&CtEp z@8G4XO1G5oF3P?5yQc&R5V|3wmmPS3qJ|NaQ2j4WkgSBn`cd83>+vPVNGZSlfHWad zM0y5lk#v|&6SlTXS<><@{_*2pdoru@2{rzy0!TS>LbfVkAu)VjLEDQqt?Dpt?#EEap{KGj5-?+NtMYHGd3AbwNL9+r;CX6m|CAnJOM0o_$bXQAW-ocE*ex&_+W*IP6#+M*J`J?*F!>b<7Idd z%gl_dOYkvpvtQsxQ*vqH1H%JJ;iavYr(txUz+3;_7|qBpadFe@9%cjQ|Nf%YPGnU6 z?Cjv~w|DCvI3(`N(8j`XpvFCJoL7T(B+^Ni`|n{M_FBIHH>JX@von7-Hznu!@O69} zMRGvf$>4-4^SMZW2-^Zs0Jjg^mmL`e>Ob)Ki{W8zXQ$~vpF_+@PiidPnZN!&9gm;< z_IzVz#+UCXi@ButGBnyi1()};DC0dcmx-R{-m2tZB{M36-sHG*-NW_1yJsc1fFp-iZ0HyyGb_#fWUtLC7Tx=e3uh`% z`!4i#*ERow8h$fQ@lq_x);8?e)aB288VoRBeUsV{hIJ^5d$p^g=`&@<>pJWmAad^$*bC*g`%DQ!VH;Dh5-Cz1_ z2!v)PDnmK1yeo!f$|=yY$-D2zjSx!=1=Ne%~7dIINWx@U?*&KF#GUE)iw(99Z z6(b)8Derv1k;?){PuGO4bRIN!y@o_o+BaDM6oymPQ7Bqj__3pBhyyiVjF^4Mhvv}7 z<+BEq%FB|s3DgCQv=r3v>2hyFsvcU;xzSaQm4$N97)0Go@k4Kj=d#SuFV+8XBR1k>mb0E?VX0_}DLR zdOttZGs_)o^j4rW)+NamdrlP@ccZ(WTa$&RR#oHO;a)X_@br zx%x}^C*5nKC!H&jV^MmpU(WplDa|%B|ACZOQk{(}sABTMR@MeS%$)*mlv?U>jOoFP ze8ji@=md>MLxIZOM*$KC`0|Kys#R&8QdvfGhtdAZ))S5ex%+^#uK2hjf+{>S8co*p zdIXif27#2b+q~DVsFayAtx1nM8P5}6OYKVG537tS;7i8 zGB&yD3-t{h+PwLR^mM^&Cv$@_~6)k1PPBhP*#<}#AbHW9L^ zTE@F3BdFbd@x{8Vp@+2z#=l_$32ZY0l@8S@gR!XV{{HPF*$Ey04mYWUmJ(q?{Nguh zZ{4Gi9{>FR7Un5w861r6#{Z`1-KZ9K=t}pe&svxZvmflnQK%Lfcw@1a%45z3Lw6CS z!JV(iunV(fi_@pywmT6r6&9S_Fv@W#$B-8wz7+PM_ygATV149!Jef@%MY9Tt>wHF-Sk9xcu<*<#Y+#} zLxatPNa0){wA59lnq5ta$oES!&5Zprx|(%>A?E%Ft8bEDB93&8=MGJ%wWs#2EIAA|s96QrbT#=M%_u5B)5F1n578`TrF4|M_U=gYWEh z(9~og>C!+T9W4wgY`E}nAWV>glSUc2?~L{JMcq0|*C1yi8IzC1$NzKcyiPnk?!8Ym U>ckbAfM+0T%G&qx;E!JX8*oNMJpcdz literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/arant2.jpg b/jetc/barant-site/assets/images/arant2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c4434bb1ab8fbd1448242b20a55677dc78a274eb GIT binary patch literal 13319 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

)X+^nr0e3!VFo(EFAs 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 RRZ!qVMnX}%T+}e|{{THRlA{0s literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/beep-07a.wav b/jetc/barant-site/assets/images/beep-07a.wav new file mode 100644 index 0000000000000000000000000000000000000000..015e1f642e91b72578bca1c318f7a7c0d515d843 GIT binary patch literal 9702 zcmZ{qRdZa~l7%yK?<14Uj54!jCYhO;@7s--n74VGA2EOAwn=7cQ@hP9vn?~KZJ8OW z&d!{*PnBf%otSW_qivU^QYmxgx7MoV^JdR}(1&QvwAHiz^S}PPdkZ3B{F+%w^gnBv zgj&#l{PVy6c>s?gvQp`+{bj7q*&#k})%@H( zi_;cTrh{cdnCsW1i<4>j5z>d+QzOdL8+#yE^ce54-*^`v4OQ-bc&b2LjDP4$xvy1xly=!~vPX~66@Ex> z@Le{tcHGBC$TVHdYiOQUxFMmNYpFtC=sDlEb8=X>(q`UIr|AYgr4OdkR6EH49V4@J zIbP{mHU_`zgg-U@NYCgdouLC%Nn7a0KX3ALe~JWMCa!m!RQ)JZ&ydRlAz`FE@3dsJmdWT&p@ zy?jEi$Rm9tsWy@h*2hM=>FE-`I?Uk;9&BAPB*LGyMymBJAHwsh#Pd6Y=lP62Vr-gG z7aB<8Xf~~+*Mo7!0e$>}S8K$rEX-<|#oA4lC~yHBqvM-8bR9y5Zb(qdZ6^TULsJYVKp1;=0c zh27>0b{Jn{6aQ`}`8qwZccP(*>lFIAF*<{m%gQjzjSB;$i!~Jr9|W(np7sawoBh^s z(47mn{9pNx3Fn(7UDJVXyw2uTw2WrZXzR-zsR;$jriM=1Ud-lpIieT%uD#%I=D9WX zuyUS63v8_{!dwrhUeuNv&<}cL_xKVOrzRY8SM&kBk$hvM45l(} zX(C_ruQ)Y)n6_cp_G9OYQ^QRyn;M>Fm>QeO*iPO$yaXQevSxX;Smg1Xrt{$)G0kMEFgRFdZ2B#+W7?nKz*HiRAS&v4P* z3$^my5^iHXc$iJ%g|-eKzig-0;xQNT*d6#cd*p;&p-24I^4!?kQ(qos(|D<^qPbEL z2DxsbxfA`wf7vZQM~7?+SK5BYcG^?=$k}$<03K(vWTh=DY$tcJCY-&_c8yQjzQT55 zgKpDvsFq_)jSSHWnP;nQv745T@O?r%Zp3+eO%Le`9j86K-gaWHFVTH^g*k74dGAHT zX$mc(b-W-AF`=+Esci??Ng~w1exHj!I_amL{uowvBUx zFcZy<_>(>3TbPxDyu~W<-?MsCYM}JY&Gm0n!=tI6b--sz3z|+Rsb15mxSiCRv7LnN zq;+_9Q}8V7w-e9)5+Bu_ZhhFz$FUFCPJEXQt)1%=py{@lpy@op26LIVa4!9vJWs3T zJRP>JvRQwZ(|Vnu>15QDI^$Z74Kv*esQN4(i%Z&BOT{d^o#9lnKes8_77o#Qxov;N z+ZkH8G8;@2XdbPx#ke3N7@7_ZPW25xqN{X*_R0M`$t_IR z`GsMU46`28hD-DtH2*GF@t?XwHrO6J?yiQ1f~ld#)Is{DBmMNSL{{4zs-VH#O`8j* zMldyY7L&ZiRi^v>nQ%j%*#|S%EOeHEHul%4iJHzD-X%|R*Gf+1_9dHin;n)5dYfM` zwzE#t-F$_nhbewUzE{#NG@zWk4)+~2op;;6@Ntx(>HONV?R33$I8Wup_&4))f|P5S zv{EP7PPwD!1=}f`bf28EYq%xvaZ^gE6Za3J{mgWkUztMFbpUtaX2JZ&^r?fU%K_cY zxSjaEs;$O8F(Ajfy20sqH#e+uOLdxz((HD|*BN*33N~yHZQz~Zh_4EFq*lLjLeO*? zY7@DBYOJSgo0j+=0VH5mbd+}~NWd}@(ANwSp!STZNv8c++;Gj0xd}8~%B%&sNCI@5 z&NE1WH&aFe{4~7x7D~A@XCz?D3&h0(tP^I*FjF9|aLVsbH@R(!sbOrVeP)NxSjI!F zg0Y={*K~Ok9^k@2(`h|85tl&I z`6Pk3ZgE)SKmx8jU~0(Q7kPncya3WDPK{pU;?x*4T}R7IT_!7a zmSAe6i-H6cB%n9sjP938EowSQC)0Fn$snoFdg7wK)B)QB;({Juwd1^3L0sX8s|p}4 z194ev>Pf?RvMr#0&_bFFeeYqdrG7iH$vbVm?A7CZl^)^qqy$ZuzFH)%bdHCnOE+sC ziOXuJn$PJ$w>4~*19qCB>HJZ%?eqilWBu%~LO@(P+V#iHDz!lG`N>aBkGGTW*mL?y zo?BTNmD5C;&$yj3RfkJ&Ys(FBBVS`1@jA!tq}_5Xa=N~xAJQPSalOJYHw8ZzkO25* zsCDPo;GJ(&t9RVR@CSaD8)zRx(-|bd!5ZU+_pKuVn#1FDpz9i%G0Ug)S#m3X#(~of zB%o)Y@Xz>DLDLJIPOEGw2p^hG;B*)CEkCAfdcy6)SJ@#)(hJGm^o4w*#ImW0#6{~2 zQ)9!Vm$tz({_b9Bk+^8RLDLDEE^jo)*iPz0ATC-`&~$~S>8{aD(mQ2Yn^y|WWnQTKj<8=E5&A(?C`A^#cecFqO zyJ`>VEvMFqI&l5eggH`SgQy#|;G(9(Hyq-vwwW;Gvuy=0w;4Rf z`V~lk-rHlo4sHgg*AbUKXH1Q?l<=JMGqL7F4@%3G5r61_c}n+ zF;%?{noiJkKR<=0Q<1prIp5(6dYHHKrm)YSO0PL^dO_2zpMbbzxvjv>0jK*e0o#dN z^i-hf?m)UZ*_uD-!RfeJ&~$6gATFxlxwZ;4G>t}qjBq<$8ldTR#g5BfTTeT24=>6+ zt>y2yk*%et4b@4>!aQzgI?2`FPR8pTF8V)H5SQ+e<9b;h)@eFr?k+5bznv=+Xoz(a zObvgrBBxuX=><-=_kyV@xac|(ps@sZ=b8p}AM^>`h@1{~*h+`xyjBaQhQaAnhR^(O znr;m#A32?_$nmfnjIz`H;-VRvPQw|*rEB3=;i9RBLes-nfuwBMesx5JNO~44PS9pu$?sY--*jg+>i97zaKIejSIC09GPur$hk(L9dd(_a41V` zH8g6X4dL$AlA-DJ0vdK6TDFZhQReP2t?xl1aCh2|$MFBrbo)Thtf=X>#o+EzxVwO< z;TGCe2J3hM3HYVyG&d^rf#ZdXwl`7JwdkS?n(pADr6ja=J$0x|)&=s9u)twE!R>9R z#4_TdD#?foQv(+bYRfd8;G#hp;B*3U;f6rde-c+=I~AG^TDhrD1)9$FF4~~!VFp}l zKadVIowMzPKlxoZa^|A>4!yvw^V*WTQ@Kq5Ib|+dMuc9zUE09sQuraegssfBlaJ{o z_aMB&)@HX8kIQbS%(n?#Ze_R)j^Lv8w!?N3wli{iB!OSE7yRs(L7`{!cpJ!F4cUu+ z+?ml1#k-b30$0Btpx@hoMC(ekApXznNcJgLig2R!x>@WJl^%+;xbX<>V zxFUbcxZvxd>6{Ul-gB?QcWGd4s25mfGE$_q0^-tgml0QCJCQj3!ML4nU%++-5ZBL) zi)J9LS_M`eFUVe`MPT_%nY*)tQPbr>I1SFNCjmE5$I@(uyW^QQn)_QvDn<4I7fsjW z)P#+0dpPVagxm6hzM5mWXb=;o#@6s+L-rDh3zGp&=LdR8ju~7uK91QXWHI1$&NQ7y z;IYv3pTvdF^)+g`ou@x|yKcnS%3QR(2Z@y8wqQFA*$b`Y**ew@gr{hxhG_?4rw1hDxKz+hE+0G$MfKLJCU)0xVSoQC)_>U9V4+bkbwRzv6B_ZN1^FR zV~U!Ngr=zJV2E>8?VhI~0c(Lvk!3GowObNE0=OjY2rgP*yN7z2j>lw_kc?`Tz(pH4 zy)ZR4nHC91z)cF^bZbQkQYN_Qy6goBRFSwOgvPETa?R2Dx9o+R6OvJ`v74N6x>mY_ z>6zqK?pg9F5I2YFV>@|HSm}|B@@VcCI`}4O>ffc0{IxVAE&->zi}^bxwYje)N!kj} zq}+1A#3jK9;Xy6ptl>?veijARtd1LCSnMtNSS@I%rp8I2iN9dU8yqLE%b zg-^DaKpTh)=@zC2iQ+iRI2}0#lF>R%FK{}3QuWEGfz!hrw_2BQmW+aq8Y3?Or|U1- zi(Ujx)e=a6>a&-~=>`|AJ>aYpPy$@EtI8iqc7}}8&e%w&%W}jOj>USJPa> zcH)+_K=y;}L`qwjno7;G7kU;s9n;oE}{?@{gI6N;b;ZWTfliB8I68O zvy7`Q83hX!GcJ0E%(w}6)V|0z>s>UQPIf!tm(g3;8L(E7xERT39dYSE8ehlhF;ACH zpdDzsdjdyW%+u{KoGwTpy1P0~M=ri5S)7}afCRW5bo&RgbL8of1Qgi|9}Aa~2f0^1 z2Zx>I=~2^h2QW2MF5T0XiO+p0d65>U#x}-0-LACWGlFxE~XV|VXU`|#OGh4NT8|6D9lJqkiEnfCf%BB zNe?(=i7|mTi+Oq^0h|^5zhNUZ^5obK$2A8 zN2Cu^9BwDlhz68Kt)c8f!QbzOFM=Q9*jveO_mbUibBRk&G4LYp<)Xohr$-ORS)txE+o(yKNfOckiZg}j+D4h zmR6vJVN8u6PnS)$1D#LS55Ze{k(T3TB1wt~G`o4Y0h_BIO!qhnMBiFf(Vs8<*lbPV|;)RMWOl%c7OQ7{59BC>2 zOzf6LvW27Sh};7?Z{$aJI>6mgc01EM9)3uQiCr;IFRCCse}gpVk=VtK6pdnbJEfRF zYl+J`(oAbsBk8N)A;{#>4@DB7x;%X|SoL(_cH-+7vlmSAvasCE3S$^)1)eYRbbSUD zhr82CL38~veSooKzVL=mu#11n#7p^PIxVX>{!QIKD z01~i0NbInkbQyioE0@EpBTq*!io}lB(l369vyQZoy`YEMhn@skqP=5R1`5AX_BRpVJy&onkv_YoQ-1G74H4?t;W|oX+BvTqIkh@{L3A@6*Q)oKC;vt7NY; z_~T{xCeU>1j-Pskn+JYGs~CF=c>a>u!Z7$zj@xcp4~EPVX#80oe}_zf(cPh0$JE3w zI_>6T7yXTq*cF;}LIPcw8jWd1WBGZZ&|AdLpF2}iGx-!SHH8a>i+jl7cB0Y7)KGKj z8nTX*R!LFQU3+wzA-qK+con^UY+<13u^-|{wn*%(*sP~(eYTyk^T(fm$KNBdgZ}SI zHzfP=C;T;iMDLI#f*-ZFADN%(q3JY7kR=v;XZY-&=c|+R=^-TJU=+(3MLvOE(8ACF zBrda%rf2PK%o5Swq9fHO1x|OH)9o&6Z>g5PnYfnuZY4u<6&@XFY;Oy`6Fq1?eeJXE zPWSKz+D*rBM>9W!JU2^{z>U!KLT_PZ*3$X(TkWePAkl5Ks zo933LXcco~OVBEMObxQ;8dn{%snIR|Q2wm1)*4V{F|mu?UDR}$9!9#ppwPyoV7MX) z;6fMsA$0oR3!0AX1c{xk!Luv2ig?DzdG$W75)!+x-tSG1`>W|g_cr7m_z`=OxjR~| zNTBnBO1kBmCz5_~;KxFq&h^_V=={;sX4y;Kc53E3kyd~okx^%f9Wv|f=;SZN1UhOu zqlLk{3C5$hTpbpnksa=OhqlNI>p4B$<2UFoLwlRMU-CNt!y|i%u7f9`hh9euxagt< zQ-jv=ew-Tkr>u)kulmOU2{dP#&L9E5BxEE&ATEl2=rep-wJ%Oh?4k{=V)*DOE%luP zy1V$kmpT%N?-b#zg@Fe=mA&r;WE(DcB+%{~^1mWKhPmkPq3Jq|dI|cW1XB~;9b-Fr zgY5=KUdHcZKLmHj=tz-lEP}tscA~}U7CZl#r^gm1W&%{N>9JLenvMw`%`Zq^qCr-v?nPzvJG6}*JeDoXwBEWGapiTx&Yo>`U{lm57! z0^*9Vv#gCte<=4zDquv~Wx5m;Iod!1X%kQ313!yXgG8(nzB6kT(dB%J-5tC+ z-uD8>wG!JoMY6O4^a|n%4>7|?>~sSfuWUQ}@ma%;-qL>B||!Wh3Dakyga)rNHS~l7s}>E*9EbIwX9z&+Cc1>DRU Ay#N3J literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/beep-08b.wav b/jetc/barant-site/assets/images/beep-08b.wav new file mode 100644 index 0000000000000000000000000000000000000000..24a13d40e4907753f41626b309e06c826ad0f4c8 GIT binary patch literal 11968 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≶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

JshlTVuP3Av=8$4B1fiNb5)>-ssV4i+@ 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 Ny7FVCIt9zn{{aZXyVU>y literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/documents-page.js b/jetc/barant-site/assets/images/documents-page.js new file mode 100644 index 0000000..b5ec9ee --- /dev/null +++ b/jetc/barant-site/assets/images/documents-page.js @@ -0,0 +1,96 @@ +(function() { + var dropzone = document.getElementById('documents-dropzone'); + var fileInput = document.getElementById('documents-file-input'); + var listBody = document.getElementById('documents-list-body'); + if (!dropzone || !listBody) + return; + + function escapeHtml(s) { + var div = document.createElement('div'); + div.textContent = s == null ? '' : String(s); + return div.innerHTML; + } + + function post(path, params) { + return fetch(path, { + method: 'POST', + credentials: 'same-origin', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams(params || {}).toString() + }).then(function(r) { return r.json(); }); + } + + function loadList() { + post('/documents/api/list', {}).then(function(res) { + if (!res.ok || !res.documents.length) { + listBody.innerHTML = '

No documents yet - upload your first one above.
'; + return; + } + var html = ''; + for (var i = 0; i < res.documents.length; i++) { + var doc = res.documents[i]; + html += '' + + '' + + '' + + '' + + '' + + ''; + } + html += '
NameUploaded
' + escapeHtml(doc.name) + '' + escapeHtml(doc.created) + 'DownloadDelete
'; + listBody.innerHTML = html; + + var actions = listBody.querySelectorAll('.documents-action'); + for (var j = 0; j < actions.length; j++) { + actions[j].addEventListener('click', function() { + var id = this.getAttribute('data-id'); + if (this.getAttribute('data-action') === 'download') { + window.fileManagerDownload && window.fileManagerDownload(id, this.getAttribute('data-name')); + } else { + post('/documents/api/delete', { id: id }).then(function(res) { + if (res.ok) + loadList(); + }); + } + }); + } + }).catch(function() { + listBody.innerHTML = '
Could not load your documents.
'; + }); + } + + function uploadFiles(files) { + if (!files || !files.length) + return; + if (window.fileManagerUpload) + window.fileManagerUpload(files); + } + + dropzone.addEventListener('click', function() { + fileInput.click(); + }); + fileInput.addEventListener('change', function() { + uploadFiles(fileInput.files); + fileInput.value = ''; + }); + dropzone.addEventListener('dragover', function(e) { + e.preventDefault(); + dropzone.classList.add('documents-dropzone-active'); + }); + dropzone.addEventListener('dragleave', function() { + dropzone.classList.remove('documents-dropzone-active'); + }); + dropzone.addEventListener('drop', function(e) { + e.preventDefault(); + dropzone.classList.remove('documents-dropzone-active'); + uploadFiles(e.dataTransfer.files); + }); + + // The site-wide file manager (documents.js, loaded once via + // mainpagetemplate.jet) dispatches this after a successful upload - + // listening here (rather than only refreshing right after our own + // uploadFiles() calls) also picks up uploads that were still running + // when this page was navigated to via spa.js. + document.addEventListener('documents:changed', loadList); + + loadList(); +})(); diff --git a/jetc/barant-site/assets/images/documents.js b/jetc/barant-site/assets/images/documents.js new file mode 100644 index 0000000..a2f70e8 --- /dev/null +++ b/jetc/barant-site/assets/images/documents.js @@ -0,0 +1,178 @@ +// Site-wide file transfer manager - loaded once via mainpagetemplate.jet +// (see its own script tag, outside #content) so the floating panel and +// any in-progress transfer survive spa.js's own #content-only swaps +// when navigating to a different page mid-upload/download. +(function() { + var panel = document.getElementById('filemgr-panel'); + if (!panel) + return; + + var toggle = document.getElementById('filemgr-toggle'); + var badge = document.getElementById('filemgr-badge'); + var list = document.getElementById('filemgr-list'); + var clearBtn = document.getElementById('filemgr-clear'); + + var transfers = {}; + var nextId = 1; + + function escapeHtml(s) { + var div = document.createElement('div'); + div.textContent = s == null ? '' : String(s); + return div.innerHTML; + } + + function updateBadge() { + var activeCount = 0; + for (var k in transfers) { + if (transfers[k].status === 'active') + activeCount++; + } + badge.textContent = activeCount > 0 ? String(activeCount) : ''; + } + + function removeEmptyPlaceholder() { + var empty = document.getElementById('filemgr-empty'); + if (empty) + empty.remove(); + } + + function showEmptyPlaceholderIfNeeded() { + if (Object.keys(transfers).length === 0 && !document.getElementById('filemgr-empty')) + list.innerHTML = '
No transfers yet.
'; + } + + var UPLOAD_ICON = ''; + var DOWNLOAD_ICON = ''; + + function addTransfer(name, direction) { + var id = 'xfer' + (nextId++); + removeEmptyPlaceholder(); + + var el = document.createElement('div'); + el.className = 'filemgr-item'; + el.innerHTML = + '
' + + '' + (direction === 'upload' ? UPLOAD_ICON : DOWNLOAD_ICON) + '' + + '' + escapeHtml(name) + '' + + '0%' + + '
' + + '
'; + list.insertBefore(el, list.firstChild); + + transfers[id] = { el: el, status: 'active' }; + panel.classList.add('filemgr-expanded'); + updateBadge(); + return id; + } + + function updateProgress(id, percent) { + var t = transfers[id]; + if (!t) + return; + t.el.querySelector('.filemgr-progress-fill').style.width = percent + '%'; + t.el.querySelector('.filemgr-item-status').textContent = percent + '%'; + } + + function markDone(id, statusText) { + var t = transfers[id]; + if (!t) + return; + t.status = 'done'; + t.el.classList.add('filemgr-done'); + t.el.querySelector('.filemgr-progress-fill').style.width = '100%'; + t.el.querySelector('.filemgr-item-status').textContent = statusText || 'Done'; + updateBadge(); + } + + function markFailed(id, statusText) { + var t = transfers[id]; + if (!t) + return; + t.status = 'failed'; + t.el.classList.add('filemgr-failed'); + t.el.querySelector('.filemgr-item-status').textContent = statusText || 'Failed'; + updateBadge(); + } + + // Uploads a FileList/array of File objects, each as its own + // concurrent XHR request - the browser's own HTTP connection pooling + // runs several at once, tracked here as separate panel entries with + // independent progress via xhr.upload's own progress event (fetch() + // has no equivalent for the request/upload side, only the response). + window.fileManagerUpload = function(files) { + for (var i = 0; i < files.length; i++) { + (function(file) { + var id = addTransfer(file.name, 'upload'); + var xhr = new XMLHttpRequest(); + transfers[id].xhr = xhr; + xhr.open('POST', '/documents/api/upload'); + xhr.upload.addEventListener('progress', function(e) { + if (e.lengthComputable) + updateProgress(id, Math.round((e.loaded / e.total) * 100)); + }); + xhr.addEventListener('load', function() { + var res = null; + try { res = JSON.parse(xhr.responseText); } catch (e) {} + if (xhr.status === 200 && res && res.ok) { + markDone(id); + document.dispatchEvent(new CustomEvent('documents:changed')); + } else { + markFailed(id, (res && res.error) || 'Failed'); + } + }); + xhr.addEventListener('error', function() { markFailed(id); }); + var fd = new FormData(); + fd.append('name', file.name); + fd.append('filedata', file); + xhr.send(fd); + })(files[i]); + } + }; + + // Downloads a document by id via XHR (responseType 'blob') instead + // of a plain so it gets real tracked progress in the same + // panel as uploads, then hands the fetched bytes to the browser's + // normal save flow via a Blob URL + synthetic click. + window.fileManagerDownload = function(id, name) { + var xferId = addTransfer(name, 'download'); + var xhr = new XMLHttpRequest(); + transfers[xferId].xhr = xhr; + xhr.open('GET', '/documents/download?id=' + encodeURIComponent(id)); + xhr.responseType = 'blob'; + xhr.addEventListener('progress', function(e) { + if (e.lengthComputable) + updateProgress(xferId, Math.round((e.loaded / e.total) * 100)); + }); + xhr.addEventListener('load', function() { + if (xhr.status !== 200) { + markFailed(xferId); + return; + } + var blobUrl = URL.createObjectURL(xhr.response); + var a = document.createElement('a'); + a.href = blobUrl; + a.download = name; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(function() { URL.revokeObjectURL(blobUrl); }, 30000); + markDone(xferId); + }); + xhr.addEventListener('error', function() { markFailed(xferId); }); + xhr.send(); + }; + + toggle.addEventListener('click', function() { + panel.classList.toggle('filemgr-expanded'); + }); + + clearBtn.addEventListener('click', function() { + for (var k in transfers) { + if (transfers[k].status !== 'active') { + transfers[k].el.remove(); + delete transfers[k]; + } + } + showEmptyPlaceholderIfNeeded(); + }); +})(); diff --git a/jetc/barant-site/assets/images/edit.png b/jetc/barant-site/assets/images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..787c82748e0f9ceb595b866f8f322995a825aae3 GIT binary patch literal 4977 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 literal 0 HcmV?d00001 diff --git a/jetc/barant-site/assets/images/email.js b/jetc/barant-site/assets/images/email.js new file mode 100644 index 0000000..8350cc3 --- /dev/null +++ b/jetc/barant-site/assets/images/email.js @@ -0,0 +1,778 @@ +(function() { + var app = document.getElementById('email-app'); + if (!app) return; + + // Small inline-SVG icon set for the toolbar/action buttons below - + // stroke="currentColor" so each one just inherits its +
+ + +
+
+ $[@_imap_user] + + + + + + + + + +
+
+
+
Select a message to read it.
+
+
+ + + diff --git a/jetc/barant-site/src/admin_invoices.page b/jetc/barant-site/src/admin-invoices.subpage old mode 100755 new mode 100644 similarity index 61% rename from jetc/barant-site/src/admin_invoices.page rename to jetc/barant-site/src/admin-invoices.subpage index 1b867e0..d11d89b --- a/jetc/barant-site/src/admin_invoices.page +++ b/jetc/barant-site/src/admin-invoices.subpage @@ -1,14 +1,5 @@ - -ADMIN INVOICES - - - -

Invoices

select invoices.id as X,invoices.id,user_id,name,invoices.created,amount,invoices.status from invoices join users on invoices.user_id=users.id -
-
-
diff --git a/jetc/barant-site/src/admin-newemail.subpage b/jetc/barant-site/src/admin-newemail.subpage new file mode 100644 index 0000000..8f9268a --- /dev/null +++ b/jetc/barant-site/src/admin-newemail.subpage @@ -0,0 +1,81 @@ + +
+ +
Connect to your mailbox first (see the Email section) before composing a message.
+ +

New Message

+
+
$[@_imap_user]
+
+
+
+ + + + + +
+
+
+
+ + +
+
+
+
+ diff --git a/jetc/barant-site/src/admin-products.subpage b/jetc/barant-site/src/admin-products.subpage new file mode 100644 index 0000000..bb15fd7 --- /dev/null +++ b/jetc/barant-site/src/admin-products.subpage @@ -0,0 +1,4 @@ +

Products

+ +select sku as X,sku,type,description from products + diff --git a/jetc/barant-site/src/admin-users.subpage b/jetc/barant-site/src/admin-users.subpage new file mode 100644 index 0000000..39d9f07 --- /dev/null +++ b/jetc/barant-site/src/admin-users.subpage @@ -0,0 +1,4 @@ +

Users

+ +select id as X,email,name,type,created,status from users + diff --git a/jetc/barant-site/src/admin.jet b/jetc/barant-site/src/admin.jet new file mode 100644 index 0000000..c623bd3 --- /dev/null +++ b/jetc/barant-site/src/admin.jet @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/admin.menu b/jetc/barant-site/src/admin.menu new file mode 100755 index 0000000..332172d --- /dev/null +++ b/jetc/barant-site/src/admin.menu @@ -0,0 +1,9 @@ + + EMAIL
+ INVOICES
+ PRODUCTS
+ USERS
+
+ + + diff --git a/jetc/barant-site/src/admin.page b/jetc/barant-site/src/admin.page index bbcac46..ec239ab 100755 --- a/jetc/barant-site/src/admin.page +++ b/jetc/barant-site/src/admin.page @@ -1,17 +1,45 @@ - + ADMINISTRATION + + + + + + + + +

Requested page is not found.

+ + +
+ + + +

Requested page is not found.

+ + +
+ + + +

Requested page is not found.

+ + +
+ + +

Requested page is not found.

+ -

Users

- -select id as X,email,name,type,created,status from users - -

Products

- -select sku as X,sku,type,description from products - -
+

Select an item from the menu.

+
+
+
+
+
+
diff --git a/jetc/barant-site/src/barantmail.page b/jetc/barant-site/src/barantmail.page deleted file mode 100755 index 5a8b5b1..0000000 --- a/jetc/barant-site/src/barantmail.page +++ /dev/null @@ -1,23 +0,0 @@ - -BARANTMAIL OPEN SOURCE PROJECT - - -

In the year 2000 I was extremely frustrated with the state of e-amil -systems available to Linux. Sendmail and q-mail were pretty much the two -mainstays. sendmail was prohibitively complex for the newly emerging -Internet method of using e-mail and q-mail was poorly maintained.

-

As a result, I embarked on a mission to write my own e-mail system that used -mysql as it's storage medium. I wanted to keep it simple and support -multiple users and domains with the simplest of configurations imaginable. -I also wanted a scalable multi-server capability.

-

The result is BARANTMAIL.

-

Features

-

Features of BARANTMAIL are:

-
    -
  • Fully compliant e-mail system.
  • -
  • Support for multiple domains.
  • -
-

-
-
-
diff --git a/jetc/barant-site/src/barantstore.page b/jetc/barant-site/src/barantstore.page new file mode 100644 index 0000000..f6b4edd --- /dev/null +++ b/jetc/barant-site/src/barantstore.page @@ -0,0 +1,42 @@ + +BARANTSTORE + + + +

BarantStore

+

A demonstration storefront built on BARANT's own e-commerce +platform - browse and purchase real products below.

+ + +select product_list.sku as sku, product_list.sku_prefix as sku_prefix, + product_list.description as description, product_list.price as price, + products.image as image + from product_list + join products on products.sku=product_list.sku_prefix + order by product_list.category, product_list.description +$[sqlerror] + +
+ +
+ + + +

$[.description]

+
$$[.price]
+
Add to Cart
+
+
+
+ +

No products available right now.

+
+
+
+
diff --git a/jetc/barant-site/src/cart.jet b/jetc/barant-site/src/cart.jet new file mode 100644 index 0000000..b2c5910 --- /dev/null +++ b/jetc/barant-site/src/cart.jet @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/createhtmleditor.proc b/jetc/barant-site/src/createhtmleditor.proc new file mode 100644 index 0000000..7bcd608 --- /dev/null +++ b/jetc/barant-site/src/createhtmleditor.proc @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/jetc/barant-site/src/documents.jet b/jetc/barant-site/src/documents.jet new file mode 100644 index 0000000..bb2edd2 --- /dev/null +++ b/jetc/barant-site/src/documents.jet @@ -0,0 +1,2 @@ +#api GET /documents/download +Not authorized.select id,name from documents where id=$[id] and user_id=$[@_login_id]
$[_filedata]Document not found.Unknown route. \ No newline at end of file diff --git a/jetc/barant-site/src/documents.page b/jetc/barant-site/src/documents.page index bdce1cb..f8b7f58 100755 --- a/jetc/barant-site/src/documents.page +++ b/jetc/barant-site/src/documents.page @@ -2,15 +2,35 @@ Document Management @@ -23,5 +43,14 @@ upon department for internal documents use. Or if you are a service group you can establish accounts and store correspondence and documents for any activity with that account.

+ + + + +
Drop files here, or click to choose files to upload
+
Loading…
+
+ +
- \ No newline at end of file + diff --git a/jetc/barant-site/src/documents_api.jet b/jetc/barant-site/src/documents_api.jet new file mode 100644 index 0000000..4eb30ba --- /dev/null +++ b/jetc/barant-site/src/documents_api.jet @@ -0,0 +1,52 @@ +#api POST /documents/api/upload +#api POST /documents/api/delete +#api POST /documents/api/list + + + + + + +
+ +{"ok":false,"error":"Not authorized."} + + +{"ok":false,"error":"Name is required."} + + insert into documents set user_id=$[@_login_id], name="$[name]", created=now() + select last_insert_id() as document_id + +{"ok":true,"id":$[document_id],"name":"$[name]"} + + + + +
+ +{"ok":false,"error":"Not authorized."} + + select id from documents where id=$[id] and user_id=$[@_login_id] + + delete from documents where id=$[id] and user_id=$[@_login_id] +{"ok":true} + +{"ok":false,"error":"Document not found."} + + + + +
+ +{"ok":false,"error":"Not authorized."} + + select id,name,created from documents where user_id=$[@_login_id] order by created desc + +{"ok":true,"documents":[,{"id":$[.id],"name":"$[.name]","created":"$[.created]"}]} + + +{"ok":false,"error":"Unknown route."} + + + + diff --git a/jetc/barant-site/src/documents_delete.proc b/jetc/barant-site/src/documents_delete.proc new file mode 100644 index 0000000..a268a15 --- /dev/null +++ b/jetc/barant-site/src/documents_delete.proc @@ -0,0 +1,11 @@ + + + + select id from documents where id=$[delete_id] and user_id=$[@_login_id]$[sqlerror] + + delete from documents where id=$[delete_id] and user_id=$[@_login_id]$[sqlerror] + + + + + diff --git a/jetc/barant-site/src/documents_upload.proc b/jetc/barant-site/src/documents_upload.proc new file mode 100644 index 0000000..2ada2f6 --- /dev/null +++ b/jetc/barant-site/src/documents_upload.proc @@ -0,0 +1,10 @@ + + + + insert into documents set user_id=$[@_login_id], name="$[name]", created=now()$[sqlerror] + select last_insert_id() as document_id + + + + + diff --git a/jetc/barant-site/src/editproduct.page b/jetc/barant-site/src/editproduct.page new file mode 100644 index 0000000..325c618 --- /dev/null +++ b/jetc/barant-site/src/editproduct.page @@ -0,0 +1,31 @@ + +EDIT PRODUCT + + + + + +select * from products where sku='$[_product_sku]' and sku_suffix=''$[sqlerror] + +

Product $[1.sku]

+ + Type: + + + + + + + +
Update Product
+
+
+
+
+
+
diff --git a/jetc/barant-site/src/email.jet b/jetc/barant-site/src/email.jet new file mode 100644 index 0000000..1fa0525 --- /dev/null +++ b/jetc/barant-site/src/email.jet @@ -0,0 +1,178 @@ +#api POST /email/connect +#api POST /email/disconnect +#api POST /email/list +#api POST /email/message +#api POST /email/flag +#api POST /email/delete +#api POST /email/trash +#api POST /email/folders +#api POST /email/search +#api POST /email/send +#api POST /email/accounts +#api POST /email/switchaccount +
+ + + + + + + + + +{"ok":true} + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + + + + + + +{"ok":true,"active":""} + + + + + + +{"ok":true,"active":"$[_next_user]"} + + + + +{"ok":true,"active":""} + + + + + +{"ok":false,"error":"Not connected to a mailbox."} + + + + + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + + + +{"ok":false,"error":"$[_imaperr]"} + + + + + + + +{"ok":false,"error":"$[_smtperr]"} + + + + + + + + + + +{"ok":false,"error":"That account is not connected."} + + + + +{"ok":true} + + + +{"ok":false,"error":"$[_switcherr]"} + + + +{"ok":false,"error":"Unknown route."} + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/email.menu b/jetc/barant-site/src/email.menu new file mode 100644 index 0000000..a48c183 --- /dev/null +++ b/jetc/barant-site/src/email.menu @@ -0,0 +1,17 @@ + + + + +
+ ++ Add Account +
diff --git a/jetc/barant-site/src/htmleditor.jet b/jetc/barant-site/src/htmleditor.jet new file mode 100644 index 0000000..6c9697f --- /dev/null +++ b/jetc/barant-site/src/htmleditor.jet @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/htmleditor.page b/jetc/barant-site/src/htmleditor.page new file mode 100644 index 0000000..4923ed5 --- /dev/null +++ b/jetc/barant-site/src/htmleditor.page @@ -0,0 +1,154 @@ + +HTML EDITOR + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

SITE .JET FILES

+

These are the real source files compiled into this site's own routes. Editing and saving one overwrites it directly - a timestamped backup is made automatically before each save.

+ +$[filelist] +
+ + + + + + +
+ + + + + + +
+ +
+
+
$[letter_dir]/$[letter_file]
+
+
+
+
+
+
+
+   +
+ +
+
+
+
+
+
+
+
+
+ +
+ $[mhtml] +
+ +
+ + +
+ + Options + +
+ +
+ Show Grid
+ Snap To Grid
+ Grid Size:
+
+ +
+ +
+

NO DATA

+
+ +
+
+ + + +
+ Toolbar +
+
+
+ + + +
+ + Images + +
+
+
+
+
drinkwise_logo.png
+
+
+
+
dwbackground.png
+
+
+
+
+
+
+
+ + + +
+ +
+
diff --git a/jetc/barant-site/src/invoices.menu b/jetc/barant-site/src/invoices.menu index 5a07f12..7a635b5 100755 --- a/jetc/barant-site/src/invoices.menu +++ b/jetc/barant-site/src/invoices.menu @@ -1,6 +1,6 @@ select * from invoices where user_id=$[_user_id]$[sqlerror] - |$[created]|
+ |$[.created]|
diff --git a/jetc/barant-site/src/jet_overview.section b/jetc/barant-site/src/jet_overview.section index e32b8a5..07e158e 100755 --- a/jetc/barant-site/src/jet_overview.section +++ b/jetc/barant-site/src/jet_overview.section @@ -27,17 +27,57 @@ to.

many expression handling functions are dependant on the mysql library functions. Any expression that works in mysql will work in JET.

-

JET Libraries

-

You can make JET scripts and compile them into super fast -libraries. Scripts can be anything from web page functions and -concealers to session control, credit card processing and just about -anything. Libraries add some performance enhancements by pre parsing -many of the elements in the script.

+

One Language, Two Speeds: The JET 2.0 Interpreter and Compiler

-

Libraries allow you to make your own tags and implement them in -the scripts. JET does not have JSON or XML parsing but using a -libraries you can develop an XML parser operation that usually -suffices for most script processing. +

Every JET script runs two ways, and you never have to choose in +advance. Drop a file on disk and JET interprets it on the spot — +edit it, refresh, see the change. No build step, no deploy pipeline, +no waiting. That's the interpreter, and it's what makes JET feel less +like "compiling a website" and more like sketching one.

+ +

Then, when a page is ready to carry real traffic, the exact same +source compiles straight down to native machine code with the JET +compiler, jetc. Not bytecode. Not a virtual machine sitting +in the middle slowing things down. Real, compiled C++ — the +tags you wrote resolve once, at build time, instead of being +re-parsed on every single request.

+ +

That's the pitch in one line: write it fast, then make it fast.

+ +

What jetc gives you:

+
    +
  • Genuine compiled-code speed. A compiled JET module isn't +an interpreted script pretending to be quick — it's a shared +library. Tag logic that would normally be walked and evaluated on +every hit is already resolved into executable code before the first +request ever arrives.
  • + +
  • Zero-downtime deploys, by design. Compiled modules are +loaded live and swapped out while the server keeps running. A +request already in flight finishes on the version it started with; +the very next request lands on the new one. No restart, no dropped +connections, no maintenance window.
  • + +
  • Routes that declare themselves. A compiled module carries +its own API routes right inside the source file. Drop it in the +plugin directory and it's live — there's no central routing +table to hand-edit and no separate registration step to forget.
  • + +
  • Builds that only do the work that changed. The compiler +toolchain tracks what's actually been touched and skips +recompiling everything else, so a large site with dozens of modules +still builds in seconds, not minutes.
  • + +
  • Shared code costs nothing at runtime. Common tag +definitions and includes get inlined directly into the compiled +output at build time — you write clean, reusable JET source, +and the compiler makes sure reusing it is free.
  • +
+ +

The interpreter and the compiler aren't two different products +you have to pick between — they're two gears on the same +engine. Prototype in the interpreter, ship in the compiler, and +never rewrite a single tag to get there.

Session Control (SESSIONCTL) Library

For web sites, a session control library is provided with JET and diff --git a/jetc/barant-site/src/login.jet b/jetc/barant-site/src/login.jet new file mode 100644 index 0000000..63b20d6 --- /dev/null +++ b/jetc/barant-site/src/login.jet @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/login.menu b/jetc/barant-site/src/login.menu index 8638fd2..dc3dbaf 100755 --- a/jetc/barant-site/src/login.menu +++ b/jetc/barant-site/src/login.menu @@ -1,5 +1,5 @@ -

Welcome $[_login_name]
+
Welcome $[_login_name]
Logout diff --git a/jetc/barant-site/src/main.menu b/jetc/barant-site/src/main.menu new file mode 100644 index 0000000..b2afa6e --- /dev/null +++ b/jetc/barant-site/src/main.menu @@ -0,0 +1,7 @@ + + SERVICES
+ HOSTING
+ SIP TRUNKING
+ OPEN SOURCE
+ CONTACT +
diff --git a/jetc/barant-site/src/mainpagetemplate.jet b/jetc/barant-site/src/mainpagetemplate.jet new file mode 100644 index 0000000..9998d10 --- /dev/null +++ b/jetc/barant-site/src/mainpagetemplate.jet @@ -0,0 +1,149 @@ +#api GET / +#api GET /{page} +#api GET /{page}/{subpage} +#api POST / +#api POST /{page} +#api POST /{page}/{subpage} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +

Page not found.

+ + + + + +
+
+ + +

Page not found.

+ +$[_BODY] +
+
+
+
+
+ + + + + +BARANT Technology Group + + + + +
/$[SUBPAGE]" enctype="multipart/form-data"> + + + + + + + + +
+
+
+ +
+
BARANT TECHNOLOGY GROUP
+
+
+
+
+ +
+
+
+ + +
+
+
+ + + +

Page not found.

+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+ Transfers + Clear +
+
No transfers yet.
+
+
+ + + + + +
+ + + + + + +

Page not found.

+
+
+
diff --git a/jetc/barant-site/src/menu.bar b/jetc/barant-site/src/menu.bar index 5ae43bf..59b768d 100755 --- a/jetc/barant-site/src/menu.bar +++ b/jetc/barant-site/src/menu.bar @@ -10,6 +10,7 @@ HOME SERVICES HOSTING + CREATE STORE DOCUMENT MGMT SIP TRUNKING VST @@ -26,10 +27,7 @@ ACCOUNT - EMAIL - - ADMIN - + ADMIN LOGIN diff --git a/jetc/barant-site/src/modules.page b/jetc/barant-site/src/modules.page new file mode 100644 index 0000000..ba20d3f --- /dev/null +++ b/jetc/barant-site/src/modules.page @@ -0,0 +1,27 @@ + +LOADED MODULES + + + +

This APIServer process is running as a small core loader with the +real functionality split into separately compiled .jtc modules. +Each one is dlopen()'d straight off disk by ApiModuleWatcher, and +watched live — drop a new build into the plugin directory and it's +serving requests within moments, with zero downtime and no restart.

+ +

Below is the actual, live list of every compiled module currently +attached to this process, read directly off disk at the moment this +page rendered.

+ + + + + + + + +
ModuleFile
$[modname]$[filename]
+ +
+
+
diff --git a/jetc/barant-site/src/asm1802.page b/jetc/barant-site/src/opensource-asm1802.subpage old mode 100755 new mode 100644 similarity index 95% rename from jetc/barant-site/src/asm1802.page rename to jetc/barant-site/src/opensource-asm1802.subpage index 530997b..7e4a890 --- a/jetc/barant-site/src/asm1802.page +++ b/jetc/barant-site/src/opensource-asm1802.subpage @@ -1,6 +1,3 @@ - -RCA1802 Micro-Controller Assembler/Emulator -

The RCA1802 Micro-Controller

@@ -42,11 +39,8 @@ visibility and manipulation.

-We are working to provide a hardware kit for the controller device. +We are working to provide a hardware kit for the controller device. Current schematics are being layed out using Kicad. I have a full git change environment going for the Kicad project as well as the source code for the assembler and emulator components. - -
-
\ No newline at end of file diff --git a/jetc/barant-site/src/opensource-barantmail.subpage b/jetc/barant-site/src/opensource-barantmail.subpage new file mode 100644 index 0000000..d6bfe0b --- /dev/null +++ b/jetc/barant-site/src/opensource-barantmail.subpage @@ -0,0 +1,53 @@ + +

In the year 2000 I was extremely frustrated with the state of e-amil +systems available to Linux. Sendmail and q-mail were pretty much the two +mainstays. sendmail was prohibitively complex for the newly emerging +Internet method of using e-mail and q-mail was poorly maintained.

+

As a result, I embarked on a mission to write my own e-mail system that used +mysql as it's storage medium. I wanted to keep it simple and support +multiple users and domains with the simplest of configurations imaginable. +I also wanted a scalable multi-server capability.

+

The result is BARANTMAIL.

+

Features

+

BARANTMAIL is a complete mail server implementation, written from +scratch in C++ - no dependency on Sendmail, Postfix, Dovecot, or any +other existing mail server software.

+ +

Protocols

+
    +
  • SMTP (port 25) plus authenticated SMTP-over-TLS submission (port 465).
  • +
  • IMAP4rev1 over TLS (port 993) - LOGIN, SELECT, FETCH/UID FETCH, STORE/UID STORE, COPY/UID COPY, EXPUNGE, SEARCH/UID SEARCH, SUBSCRIBE, NOOP and more.
  • +
  • POP3 over TLS (port 995) - USER/PASS, STAT, LIST, RETR, DELE, TOP, UIDL.
  • +
+ +

Security

+
    +
  • TLS/SSL on every protocol, backed by OpenSSL and real CA-issued certificates.
  • +
  • DKIM signing on outbound mail.
  • +
  • SPF verification on inbound mail.
  • +
+ +

Spam Filtering

+
    +
  • Every inbound message is scanned by SpamAssassin (spoken directly over the spamd protocol, not shelled out to a binary) on the way into a mailbox.
  • +
  • Messages scoring above the configured threshold are automatically filed into a dedicated Spam folder rather than the Inbox - not just tagged and left mixed in with everything else.
  • +
+ +

Multi-Tenancy

+
    +
  • Multiple domains on one server (virtual hosting).
  • +
  • Multiple independent mailboxes per domain, each with its own Inbox, Spam, and Trash folders, plus support for any additional folder a client creates.
  • +
+ +

Architecture

+
    +
  • Asynchronous, EPoll-based I/O - handles many concurrent connections without a thread per connection.
  • +
  • Queue-based SMTP delivery - inbound mail is queued to disk, then either filed locally or relayed onward over SMTP on a background thread, so a slow remote host never blocks the server.
  • +
+ +

Companion Webmail Client

+

A full browser-based email client (see Email +under the Admin section) is built directly against BARANTMAIL's own IMAP +implementation - real proof the protocol support is complete enough to +drive a real application, not just a demonstration.

+
diff --git a/jetc/barant-site/src/jet.page b/jetc/barant-site/src/opensource-jet.subpage old mode 100755 new mode 100644 similarity index 68% rename from jetc/barant-site/src/jet.page rename to jetc/barant-site/src/opensource-jet.subpage index 9ca78f2..469adc7 --- a/jetc/barant-site/src/jet.page +++ b/jetc/barant-site/src/opensource-jet.subpage @@ -1,13 +1,6 @@ - -barant.com - JET Extension Tags - - - - - diff --git a/jetc/barant-site/src/opensource-overview.subpage b/jetc/barant-site/src/opensource-overview.subpage new file mode 100644 index 0000000..b53a09f --- /dev/null +++ b/jetc/barant-site/src/opensource-overview.subpage @@ -0,0 +1,49 @@ + +

Open Source Projects

+

We have several open source projects that we have created.

+ +
    + +
  • JET - JET Extension Tags provides an amazingly versatile and +simple mechanism for generating output for muliple uses. Originally +designed in the early days of the Internet for generating web pages +containing dynamic data it has been adapted to many uses from Asterisk +dial plans to other generated output that requires templated protocols +with dynamic data from a mysql database. Jeff Gonzalez added the +ability to manage web site sessions and pretty URLs to JET with his +session manager library.

  • + +
  • BARANTMAIL - Designed out of frustration with qmail a decade ago +BARANTMAIL was developed on top of C++ and mySQL to provide an +extremely super simple e-mail server for the open-source +community.

  • + +
  • SYNTHMAX - I have been developing a fully modular synthesizer for +use in Linux. This is not your usual plaything and maybe someday will +be used professionally in my keyboard products.

  • + +
  • C++ SERVER FRAMEWORK - A core epoll server code for developing +all sorts of high performance TCP and UDP servers. Generic socket +objects allow tying into epoll handling for any socket based +communications including timers, files and network sockets. +Basic functional server frameworks are provided for: +

    +
      +
    • HTTP(S) Server
    • +
    • Stream Server
    • +
    • E-Mail Server
    • +
    • Chat Server
    • +
    + +

  • + +
  • RCA1802 Micro-Controller +Emulator - A fully operational assembler +and emulation environment for the development and deployment of +firmware code utilizing the RCA1802 micro-controller chip.
  • + +
+ +Check out our git server at git.barant.com. + +
diff --git a/jetc/barant-site/src/serverframework.page b/jetc/barant-site/src/opensource-serverframework.subpage old mode 100755 new mode 100644 similarity index 90% rename from jetc/barant-site/src/serverframework.page rename to jetc/barant-site/src/opensource-serverframework.subpage index df9b6a9..f3b5d7a --- a/jetc/barant-site/src/serverframework.page +++ b/jetc/barant-site/src/opensource-serverframework.subpage @@ -1,6 +1,3 @@ - -Server Core Framework -

It is finally here! The Server Core Framework is now available. The framework offers support for just about any server you can devise.

@@ -8,7 +5,7 @@ framework offers support for just about any server you can devise.

Now why would I want to go and build a server when there are all sorts of frameworks out there for supporting server activity? All I can say is top notch performance. The Server Core is all C++ and is -built on the epoll structure of the Linux operating system.

+built on the epoll structure of the Linux operating system.

  • TCP and/or UDP server capabilities.
  • @@ -40,5 +37,3 @@ git clone https://barant.com/git@ServerCore.git - - \ No newline at end of file diff --git a/jetc/barant-site/src/opensource-synthmax.subpage b/jetc/barant-site/src/opensource-synthmax.subpage new file mode 100644 index 0000000..ccfe054 --- /dev/null +++ b/jetc/barant-site/src/opensource-synthmax.subpage @@ -0,0 +1,5 @@ + +

    I have been developing a fully modular synthesizer for +use in Linux. This is not your usual plaything and maybe someday will +be used professionally in my keyboard products.

    +
    diff --git a/jetc/barant-site/src/opensource.menu b/jetc/barant-site/src/opensource.menu index c03e4b8..db7b8ba 100755 --- a/jetc/barant-site/src/opensource.menu +++ b/jetc/barant-site/src/opensource.menu @@ -1,7 +1,8 @@ - BARANTMAIL
    - JET
    - SYNTHMAX
    - SERVER CORE FRAMEWORK - ASM/MCH 1802 SIMULATOR
    + OVERVIEW
    + BARANTMAIL
    + JET
    + SYNTHMAX
    + SERVER CORE FRAMEWORK + ASM/MCH 1802 SIMULATOR
    diff --git a/jetc/barant-site/src/opensource.page b/jetc/barant-site/src/opensource.page index eccfcf2..6e53b72 100755 --- a/jetc/barant-site/src/opensource.page +++ b/jetc/barant-site/src/opensource.page @@ -1,54 +1,16 @@ OPEN SOURCE - -

    Open Source Projects

    -

    We have several open source projects that we have created.

    - -
      - -
    • JET - JET Extension Tags provides an amazingly versatile and -simple mechanism for generating output for muliple uses. Originally -designed in the early days of the Internet for generating web pages -containing dynamic data it has been adapted to many uses from Asterisk -dial plans to other generated output that requires templated protocols -with dynamic data from a mysql database. Jeff Gonzalez added the -ability to manage web site sessions and pretty URLs to JET with his -session manager library.

    • - -
    • BARANTMAIL - Designed out of frustration with qmail a decade ago -BARANTMAIL was developed on top of C++ and mySQL to provide an -extremely super simple e-mail server for the open-source -community.

    • - -
    • SYNTHMAX - I have been developing a fully modular synthesizer for -use in Linux. This is not your usual plaything and maybe someday will -be used professionally in my keyboard products.

    • - -
    • C++ SERVER FRAMEWORK - A core epoll server code for developing -all sorts of high performance TCP and UDP servers. Generic socket -objects allow tying into epoll handling for any socket based -communications including timers, files and network sockets. -Basic functional server frameworks are provided for: -

      -
        -
      • HTTP(S) Server
      • -
      • Stream Server
      • -
      • E-Mail Server
      • -
      • Chat Server
      • -
      - -

    • - -
    • RCA1802 Micro-Controller -Emulator - A fully operational assembler -and emulation environment for the development and deployment of -firmware code utilizing the RCA1802 micro-controller chip.
    • - -
    - - Check out our git server at git.barant.com. - -
    +
    + + + + + + +

    That open source category doesn't exist.

    +
    +
    +
    diff --git a/jetc/barant-site/src/removecartitem.proc b/jetc/barant-site/src/removecartitem.proc new file mode 100644 index 0000000..63fe5f2 --- /dev/null +++ b/jetc/barant-site/src/removecartitem.proc @@ -0,0 +1 @@ +delete from cart_item where session_uuid='$[_session_id]' and sku='$[sku]'$[sqlerror] diff --git a/jetc/barant-site/src/savehtmleditor.proc b/jetc/barant-site/src/savehtmleditor.proc new file mode 100644 index 0000000..fc32e8a --- /dev/null +++ b/jetc/barant-site/src/savehtmleditor.proc @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jetc/barant-site/src/selectproduct.proc b/jetc/barant-site/src/selectproduct.proc new file mode 100644 index 0000000..a52583e --- /dev/null +++ b/jetc/barant-site/src/selectproduct.proc @@ -0,0 +1,3 @@ + + $[sku] + diff --git a/jetc/barant-site/src/store-createstore.subpage b/jetc/barant-site/src/store-createstore.subpage new file mode 100644 index 0000000..137d138 --- /dev/null +++ b/jetc/barant-site/src/store-createstore.subpage @@ -0,0 +1,12 @@ + +

    Create Your Own Store

    +

    BARANT Technology Group's e-commerce platform lets you build and run +your own online store right here on this site - list physical +products, digital downloads, subscriptions, or streaming content, and +we handle checkout, payment processing, and order management for you.

    +

    Whether you're selling a handful of items or running a full catalog, +your store lives alongside everything else here - the same account, +document storage, and support tools you already use.

    +

    Want to see it in action? Take a look at BarantStore, +our own storefront, built on the same platform we're offering you.

    +
    diff --git a/jetc/barant-site/src/store.page b/jetc/barant-site/src/store.page new file mode 100644 index 0000000..8f368c4 --- /dev/null +++ b/jetc/barant-site/src/store.page @@ -0,0 +1,10 @@ + +STORE + + + + +

    Page not found.

    +
    +
    +
    diff --git a/jetc/barant-site/src/update_product.proc b/jetc/barant-site/src/update_product.proc new file mode 100644 index 0000000..d737a09 --- /dev/null +++ b/jetc/barant-site/src/update_product.proc @@ -0,0 +1,5 @@ + +update products set type='$[type]', description='$[description]', text='$[text]', price=$[price], image='$[image]' where sku='$[_product_sku]' and sku_suffix=''$[sqlerror] + + + diff --git a/jetc/barant-site/src/updatecartqty.proc b/jetc/barant-site/src/updatecartqty.proc new file mode 100644 index 0000000..163f2dd --- /dev/null +++ b/jetc/barant-site/src/updatecartqty.proc @@ -0,0 +1,5 @@ + +delete from cart_item where session_uuid='$[_session_id]' and sku='$[sku]'$[sqlerror] + +update cart_item set quantity=$[qty] where session_uuid='$[_session_id]' and sku='$[sku]'$[sqlerror] + diff --git a/jetc/build-assets.sh b/jetc/build-assets.sh new file mode 100755 index 0000000..e6c6795 --- /dev/null +++ b/jetc/build-assets.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Builds the hand-written assets.compiled.cpp module into assets.jtc. +# Mirrors build-shared.sh's compile/link tail exactly (same object cache, +# same libCoreUtils_pic.a, same link line) but skips the jetc --shared +# codegen step since there is no .jet source for this module. +set -e +JC="$(cd "/home/barant/servercore-analysis/JetCore" && pwd)" +CU="$JC/../CoreUtils" +HERE="$JC/jetc" +STEM="assets" +OUTPUT="$1" + +if [ -z "$OUTPUT" ]; then + echo "usage: $0 " + exit 1 +fi + +echo "Building JetCore runtime objects as position-independent code..." +list="" +for f in "$JC"/*.cpp; do + base=$(basename "$f") + if [ "$base" = "jet-2.0.cpp" ]; then continue; fi + obj="$HERE/${base%.cpp}.pic.o" + if [ ! -f "$obj" ] || [ "$f" -nt "$obj" ]; then + g++ -g -fPIC -c -std=c++23 -I"$CU" -I"$JC" "$f" -o "$obj" & + fi + list="$list $obj" +done + +cu_pic_list="" +cu_rebuilt=false +for f in "$CU"/*.cpp; do + base=$(basename "$f") + obj="$HERE/cu_${base%.cpp}.pic.o" + if [ ! -f "$obj" ] || [ "$f" -nt "$obj" ]; then + g++ -g -fPIC -c -std=c++23 -I"$CU" "$f" -o "$obj" & + cu_rebuilt=true + fi + cu_pic_list="$cu_pic_list $obj" +done +wait +if [ "$cu_rebuilt" = true ] || [ ! -f "$HERE/libCoreUtils_pic.a" ]; then + ar rcs "$HERE/libCoreUtils_pic.a" $cu_pic_list +fi + +g++ -g -fPIC -c -std=c++23 -I"$CU" -I"$JC" "$HERE/$STEM.compiled.cpp" -o "$HERE/$STEM.compiled.pic.o" + +echo "Linking $OUTPUT..." +g++ -shared -fPIC -o "$OUTPUT" "$HERE/$STEM.compiled.pic.o" $list -std=c++23 \ + -L"$HERE" -lCoreUtils_pic -lmysqlclient -lcrypto + +echo "Built $OUTPUT" diff --git a/jetc/lowercase_jet.py b/jetc/lowercase_jet.py new file mode 100644 index 0000000..11ccf19 --- /dev/null +++ b/jetc/lowercase_jet.py @@ -0,0 +1,261 @@ +""" +Lowercases jet tag NAMES ( / ) and attribute NAMES +(ATTR=) throughout a jet source file, WITHOUT touching: + - attribute VALUES (quoted or unquoted) + - $[...]/#[...] variable references anywhere (including nested ones, + and ones that appear inside attribute values) + - literal HTML/text content outside of tags + +Handles the same real-content wrinkles this session's own JetParser.cpp +fixes had to: tags embedded inside a quoted attribute value (e.g. +linkbutton's own HREF="javascript: ........."), and +both single and double quoted attribute values. + +This is a straightforward character-by-character scanner, not a regex - +regex can't reliably track the nested quote/tag structure real content +uses. +""" +import sys + + +IDENT_CHARS = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") + + +def skip_variable_ref(s, i, out): + """s[i] is '$' or '#', s[i+1] is '['. Copies the whole $[...]/#[...] + reference verbatim (including nested [...] refs inside it) to out, + returns the index just past the closing ']'.""" + out.append(s[i]) + out.append(s[i + 1]) + i += 2 + depth = 1 + while i < len(s) and depth > 0: + if s[i] == '[': + depth += 1 + out.append(s[i]) + i += 1 + elif s[i] == ']': + depth -= 1 + out.append(s[i]) + i += 1 + else: + out.append(s[i]) + i += 1 + return i + + +def is_var_start(s, i): + return i + 1 < len(s) and s[i] in '$#' and s[i + 1] == '[' + + +def looks_like_tag(s, i): + """s[i] is '<'. Peeks (without consuming) to decide whether this + really starts a jet tag, using the same rule Tag.cpp's own + constructor uses at runtime (Tag.cpp:50): read an identifier-char + run right after '<' (or after ''. A literal '<' embedded in JS/text (e.g. + console.log("<")) fails this and must be left untouched - real + content relies on exactly this rule to disambiguate.""" + n = len(s) + j = i + 1 + if j < n and s[j] == '/': + k = j + 1 + while k < n and s[k] in IDENT_CHARS: + k += 1 + if k == j + 1: + return False + return k < n and s[k] == '>' + if j >= n or not s[j].isalpha(): + return False + k = j + while k < n and s[k] in IDENT_CHARS: + k += 1 + return k < n and s[k] in ' \t\r\n/>' + + +def skip_quoted_value(s, i, quote_char, out): + """s[i] is the opening quote character (already consumed by caller, + i.e. this points at the FIRST character of the value). Copies the + value verbatim, including any embedded ... constructs and + variable refs, stopping at (and consuming) the matching closing + quote. Returns the index just past the closing quote.""" + while i < len(s): + if s[i] == quote_char: + out.append(s[i]) + return i + 1 + if is_var_start(s, i): + i = skip_variable_ref(s, i, out) + continue + if s[i] == '<' and looks_like_tag(s, i): + i = skip_embedded_tag(s, i, out) + continue + out.append(s[i]) + i += 1 + return i + + +def skip_embedded_tag(s, i, out): + """s[i] is '<', starting a tag embedded inside a quoted attribute + value (or, via lowercase_source's own top-level loop, an ordinary + top-level tag). Lowercases its name and attribute names the same + way the top-level scanner does, then either stops after a + self-closing '/>' or, if it looks like it has its own body, copies + through to its own matching close tag verbatim (rare inside a + quoted value in real content; the void-tag list below covers the + common built-ins that never do). Returns the index just past this + tag's own closing '>' (and, for a real container tag, past its + matching close tag too).""" + out.append(s[i]) # '<' + i += 1 + closing = False + if i < len(s) and s[i] == '/': + closing = True + out.append(s[i]) + i += 1 + name_start = i + while i < len(s) and s[i] in IDENT_CHARS: + i += 1 + name = s[name_start:i] + out.append(name.lower()) + + if closing: + # just the name, then skip to '>' + while i < len(s) and s[i] != '>': + out.append(s[i]) + i += 1 + if i < len(s): + out.append(s[i]) + i += 1 + return i + + # attribute region + self_closed = False + while i < len(s): + if s[i] in ' \t\r\n': + out.append(s[i]) + i += 1 + continue + if s[i] == '/' and i + 1 < len(s) and s[i + 1] == '>': + out.append('/>') + i += 2 + self_closed = True + break + if s[i] == '>': + out.append('>') + i += 1 + break + if s[i] in IDENT_CHARS: + attr_start = i + while i < len(s) and s[i] in IDENT_CHARS: + i += 1 + attr_name = s[attr_start:i] + out.append(attr_name.lower()) + # optional whitespace before '=' + j = i + while j < len(s) and s[j] in ' \t\r\n': + j += 1 + if j < len(s) and s[j] == '=': + out.append(s[i:j]) + i = j + out.append('=') + i += 1 + # optional whitespace after '=' + while i < len(s) and s[i] in ' \t\r\n': + out.append(s[i]) + i += 1 + if i < len(s) and s[i] in ('"', "'"): + q = s[i] + out.append(q) + i += 1 + i = skip_quoted_value(s, i, q, out) + else: + # unquoted value - copy verbatim to next + # whitespace/'>'//' + while i < len(s) and s[i] not in ' \t\r\n>/': + if is_var_start(s, i): + i = skip_variable_ref(s, i, out) + else: + out.append(s[i]) + i += 1 + continue + # anything unexpected - copy through defensively + out.append(s[i]) + i += 1 + + void_tags = { + "set", "include", "cookie", "header", "read", "write", "call", + "system", "dump", "br", "img", "input", "hr", "meta", "link", + "area", "base", "col", "embed", "source", "track", "wbr", + "else", + } + if self_closed or name.lower() in void_tags: + return i + + # real container tag embedded in a quoted value - copy through to + # its own matching close, tracking nesting of the same tag name. + depth = 1 + nlower = name.lower() + while i < len(s) and depth > 0: + if is_var_start(s, i): + i = skip_variable_ref(s, i, out) + continue + if s[i] == '<': + # peek for open/close of the same tag name + j = i + 1 + is_close = False + if j < len(s) and s[j] == '/': + is_close = True + j += 1 + k = j + while k < len(s) and s[k] in IDENT_CHARS: + k += 1 + token = s[j:k] + if not looks_like_tag(s, i): + out.append(s[i]) + i += 1 + continue + if token.lower() == nlower: + if is_close: + depth -= 1 + else: + depth += 1 + i = skip_embedded_tag(s, i, out) + continue + i = skip_embedded_tag(s, i, out) + continue + out.append(s[i]) + i += 1 + return i + + +def lowercase_source(s): + out = [] + i = 0 + n = len(s) + while i < n: + if is_var_start(s, i): + i = skip_variable_ref(s, i, out) + continue + if s[i] == '<' and looks_like_tag(s, i): + i = skip_embedded_tag(s, i, out) + continue + out.append(s[i]) + i += 1 + return ''.join(out) + + +def main(): + if len(sys.argv) != 3: + print("usage: lowercase_jet.py ") + sys.exit(1) + with open(sys.argv[1], encoding="utf-8", errors="surrogateescape", newline="") as f: + content = f.read() + result = lowercase_source(content) + with open(sys.argv[2], "w", encoding="utf-8", errors="surrogateescape", newline="") as f: + f.write(result) + print("wrote", sys.argv[2], "(%d bytes -> %d bytes)" % (len(content), len(result))) + + +if __name__ == "__main__": + main() diff --git a/jetc/strip_fragments.py b/jetc/strip_fragments.py new file mode 100755 index 0000000..e5b6379 --- /dev/null +++ b/jetc/strip_fragments.py @@ -0,0 +1,96 @@ +""" +Strips the dead SPA-fragment dual-render wrapper from a barant-site/jet/*.jet +route file, leaving just the real page content (the former "_full_NAME" tag +body), unwrapped, with no fragment-flag try/catch, no _frag_NAME tag +definition, no dispatch , and no dead client-side SPA navigation script. + +Structure being removed (present in all 20 files, verified consistent): + + #api GET /NAME + ...... + ... fragment-only content, NEVER actually invoked any more since the + client-side SPA click handlers were already disabled earlier this + session ... + + ... the REAL page content - full doctype/head/style/body wrapper ... + + <_frag_NAME /><_full_NAME /> + +Kept: "#api GET /NAME\n" plus everything between "" +and its own matching close, MINUS the dead ", + re.DOTALL, + ) + new_content, n = pattern.subn("", content, count=1) + return new_content, n + + +def strip_fragment_wrapper(content, page_name): + lines = content.split("\n", 1) + assert lines[0].startswith("#api GET /"), f"unexpected first line: {lines[0]!r}" + api_line = lines[0] + rest = lines[1] if len(lines) > 1 else "" + + start_marker = ( + '' + '' + f'' + ) + if start_marker not in rest: + return None, f"start marker not found" + idx = rest.index(start_marker) + prefix_extra = rest[:idx] # should just be leading whitespace/newline, kept as-is + rest = rest[idx + len(start_marker):] + + mid_marker = f'' + if mid_marker not in rest: + return None, f"mid marker not found" + mid_idx = rest.index(mid_marker) + rest = rest[mid_idx + len(mid_marker):] + + end_pattern = re.compile( + r'' + rf'<_frag_{re.escape(page_name)} /><_full_{re.escape(page_name)} />\s*$' + ) + m = end_pattern.search(rest) + if not m: + return None, f"end marker not found" + full_content = rest[:m.start()] + + full_content, nscript = strip_dead_spa_script(full_content) + + result = api_line + "\n" + prefix_extra + full_content + return result, f"ok (removed {nscript} dead script block(s))" + + +def main(): + if len(sys.argv) != 4: + print("usage: strip_fragments.py ") + sys.exit(1) + with open(sys.argv[1], encoding="utf-8", newline="") as f: + content = f.read() + result, status = strip_fragment_wrapper(content, sys.argv[3]) + if result is None: + print(f"FAILED for {sys.argv[3]}: {status}") + sys.exit(1) + with open(sys.argv[2], "w", encoding="utf-8", newline="") as f: + f.write(result) + print(f"{sys.argv[3]}: {status} ({len(content)} -> {len(result)} bytes)") + + +if __name__ == "__main__": + main()