summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatarLibravatar Biswakalyan Bhuyan <biswa@surgot.in> 2023-04-06 16:39:52 +0530
committerLibravatarLibravatar Biswakalyan Bhuyan <biswa@surgot.in> 2023-04-06 16:39:52 +0530
commit6fa71c7efadb990aad14d1c6a7833e9cae88436d (patch)
tree5601e8bdd31c113b2dccdb01002daab6dd244eca
downloadcgit-6fa71c7efadb990aad14d1c6a7833e9cae88436d.tar.gz
cgit-6fa71c7efadb990aad14d1c6a7833e9cae88436d.tar.bz2
cgit-6fa71c7efadb990aad14d1c6a7833e9cae88436d.zip
cgit
-rw-r--r--about/_index.md4
-rw-r--r--about/about.md14
-rwxr-xr-xcgit.cgibin0 -> 14992368 bytes
-rw-r--r--cgit.css1045
-rw-r--r--cgit.js68
-rw-r--r--cgit.pngbin0 -> 30541 bytes
-rw-r--r--favicon.icobin0 -> 15406 bytes
-rwxr-xr-xfilters/about-formatting.sh27
-rwxr-xr-xfilters/commit-links.sh28
-rw-r--r--filters/email-gravatar.lua35
-rwxr-xr-xfilters/email-gravatar.py39
-rw-r--r--filters/email-libravatar.lua36
-rw-r--r--filters/file-authentication.lua359
-rw-r--r--filters/gentoo-ldap-authentication.lua360
-rwxr-xr-xfilters/html-converters/man2html4
-rwxr-xr-xfilters/html-converters/md2html330
-rwxr-xr-xfilters/html-converters/rst2html2
-rwxr-xr-xfilters/html-converters/txt2html4
-rw-r--r--filters/owner-example.lua17
-rw-r--r--filters/simple-authentication.lua314
-rwxr-xr-xfilters/syntax-highlighting.py63
-rwxr-xr-xfilters/syntax-highlighting.sh147
-rw-r--r--robots.txt4
23 files changed, 2900 insertions, 0 deletions
diff --git a/about/_index.md b/about/_index.md
new file mode 100644
index 0000000..30dda98
--- /dev/null
+++ b/about/_index.md
@@ -0,0 +1,4 @@
+---
+title: "about
+date: 22.10.2022
+---
diff --git a/about/about.md b/about/about.md
new file mode 100644
index 0000000..7405a75
--- /dev/null
+++ b/about/about.md
@@ -0,0 +1,14 @@
+<h1 style="text-align:center"><u>welcome to my git server</u></h1>
+## Note
+- Here you can find all my personal projects, cofnigs and dotflies.
+- You are free to clone/download change, modify, share my code.
+- This git server is a part of [https://xgenos.me/](https://xgenos.me/)
+- you can send patches, ideas, issues ot me [via email](mailto:biswa@xgenos.me)
+
+## Server Backstory
+Back then i was a newbie guy idk anything about tech i use to host my site at github pages i was curious to know how nginx works i always wished to watch the backend of the server i'd contacted with my collage teachers and wished them to let me look into the backedn of the server/website unfortunately my teachers where so dumb about that and they where using cpanel. (typical indian education system)
+
+Any of my teachers and my friends aren't enthusiastic about tech, well i have to do things by myself so i thought to host everything myself.
+
+<br><br>
+<p style="text-align:center"><u><i>Happy Coding</i><img src="https://media.giphy.com/media/VgCDAzcKvsR6OM0uWg/giphy.gif" width="40"></u></p>
diff --git a/cgit.cgi b/cgit.cgi
new file mode 100755
index 0000000..3175f66
--- /dev/null
+++ b/cgit.cgi
Binary files differ
diff --git a/cgit.css b/cgit.css
new file mode 100644
index 0000000..76eb3f4
--- /dev/null
+++ b/cgit.css
@@ -0,0 +1,1045 @@
+@import url(source-code-pro/source-code-pro.css);
+
+div#cgit {
+ padding: 0em;
+ margin: 0em;
+ font-family: sans-serif;
+ font-size: 10pt;
+ color: #333;
+ background: white;
+ padding: 4px;
+}
+
+div#cgit a {
+ color: blue;
+ text-decoration: none;
+}
+
+div#cgit a:hover {
+ text-decoration: underline;
+}
+
+div#cgit table {
+ border-collapse: collapse;
+}
+
+div#cgit table#header {
+ width: 100%;
+ margin-bottom: 1em;
+}
+
+div#cgit table#header td.logo {
+ width: 96px;
+ vertical-align: top;
+}
+
+div#cgit table#header td.main {
+ font-size: 250%;
+ padding-left: 10px;
+ white-space: nowrap;
+}
+
+div#cgit table#header td.main a {
+ color: #000;
+}
+
+div#cgit table#header td.form {
+ text-align: right;
+ vertical-align: bottom;
+ padding-right: 1em;
+ padding-bottom: 2px;
+ white-space: nowrap;
+}
+
+div#cgit table#header td.form form,
+div#cgit table#header td.form input,
+div#cgit table#header td.form select {
+ font-size: 90%;
+}
+
+div#cgit table#header td.sub {
+ color: #777;
+ border-top: solid 1px #ccc;
+ padding-left: 10px;
+}
+
+div#cgit table.tabs {
+ border-bottom: solid 3px #ccc;
+ border-collapse: collapse;
+ margin-top: 2em;
+ margin-bottom: 0px;
+ width: 100%;
+}
+
+div#cgit table.tabs td {
+ padding: 0px 1em;
+ vertical-align: bottom;
+}
+
+div#cgit table.tabs td a {
+ padding: 2px 0.75em;
+ color: #777;
+ font-size: 110%;
+}
+
+div#cgit table.tabs td a.active {
+ color: #000;
+ background-color: #ccc;
+}
+
+div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
+ content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC);
+ opacity: 0.5;
+ margin: 0 0 0 5px;
+}
+
+div#cgit table.tabs td.form {
+ text-align: right;
+}
+
+div#cgit table.tabs td.form form {
+ padding-bottom: 2px;
+ font-size: 90%;
+ white-space: nowrap;
+}
+
+div#cgit table.tabs td.form input,
+div#cgit table.tabs td.form select {
+ font-size: 90%;
+}
+
+div#cgit div.path {
+ margin: 0px;
+ padding: 5px 2em 2px 2em;
+ color: #000;
+ background-color: #eee;
+}
+
+div#cgit div.content {
+ margin: 0px;
+ padding: 2em;
+ border-bottom: solid 3px #ccc;
+}
+
+
+div#cgit table.list {
+ width: 100%;
+ border: none;
+ border-collapse: collapse;
+}
+
+div#cgit table.list tr {
+ background: white;
+}
+
+div#cgit table.list tr.logheader {
+ background: #eee;
+}
+
+div#cgit table.list tr:nth-child(even) {
+ background: #f7f7f7;
+}
+
+div#cgit table.list tr:nth-child(odd) {
+ background: white;
+}
+
+div#cgit table.list tr:hover {
+ background: #eee;
+}
+
+div#cgit table.list tr.nohover {
+ background: white;
+}
+
+div#cgit table.list tr.nohover:hover {
+ background: white;
+}
+
+div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
+ background: #f7f7f7;
+}
+
+div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
+ background: white;
+}
+
+div#cgit table.list th {
+ font-weight: bold;
+ /* color: #888;
+ border-top: dashed 1px #888;
+ border-bottom: dashed 1px #888;
+ */
+ padding: 0.1em 0.5em 0.05em 0.5em;
+ vertical-align: baseline;
+}
+
+div#cgit table.list td {
+ border: none;
+ padding: 0.1em 0.5em 0.1em 0.5em;
+}
+
+div#cgit table.list td.commitgraph {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+}
+div#cgit pre {
+ font-family: "Source Code Pro", "Courier New", monospace;
+}
+
+div#cgit table.list td.commitgraph .column1 {
+ color: #a00;
+}
+
+div#cgit table.list td.commitgraph .column2 {
+ color: #0a0;
+}
+
+div#cgit table.list td.commitgraph .column3 {
+ color: #aa0;
+}
+
+div#cgit table.list td.commitgraph .column4 {
+ color: #00a;
+}
+
+div#cgit table.list td.commitgraph .column5 {
+ color: #a0a;
+}
+
+div#cgit table.list td.commitgraph .column6 {
+ color: #0aa;
+}
+
+div#cgit table.list td.logsubject {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ font-weight: bold;
+}
+
+div#cgit table.list td.logmsg {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+ padding: 0 0.5em;
+}
+
+div#cgit table.list td a {
+ color: black;
+}
+
+div#cgit table.list td a.ls-dir {
+ font-weight: bold;
+ color: #00f;
+}
+
+div#cgit table.list td a:hover {
+ color: #00f;
+}
+
+div#cgit img {
+ border: none;
+}
+
+div#cgit input#switch-btn {
+ margin: 2px 0px 0px 0px;
+}
+
+div#cgit td#sidebar input.txt {
+ width: 100%;
+ margin: 2px 0px 0px 0px;
+}
+
+div#cgit table#grid {
+ margin: 0px;
+}
+
+div#cgit td#content {
+ vertical-align: top;
+ padding: 1em 2em 1em 1em;
+ border: none;
+}
+
+div#cgit div#summary {
+ vertical-align: top;
+ margin-bottom: 1em;
+}
+
+div#cgit table#downloads {
+ float: right;
+ border-collapse: collapse;
+ border: solid 1px #777;
+ margin-left: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+div#cgit table#downloads th {
+ background-color: #ccc;
+}
+
+div#cgit div#blob {
+ border: solid 1px black;
+}
+
+div#cgit div.error {
+ color: red;
+ font-weight: bold;
+ margin: 1em 2em;
+}
+
+div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod {
+ font-family: "Source Code Pro", "Courier New", monospace;
+}
+
+div#cgit td.ls-size {
+ text-align: right;
+ font-family: "Source Code Pro", "Courier New", monospace;
+ width: 10em;
+}
+
+div#cgit td.ls-mode {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ width: 10em;
+}
+
+div#cgit table.blob {
+ margin-top: 0.5em;
+ border-top: solid 1px black;
+}
+
+div#cgit table.blob td.hashes,
+div#cgit table.blob td.lines {
+ margin: 0; padding: 0 0 0 0.5em;
+ vertical-align: top;
+ color: black;
+}
+
+div#cgit table.blob td.linenumbers {
+ margin: 0; padding: 0 0.5em 0 0.5em;
+ vertical-align: top;
+ text-align: right;
+ border-right: 1px solid gray;
+}
+
+div#cgit table.blob pre {
+ padding: 0; margin: 0;
+}
+
+div#cgit table.blob td.linenumbers a,
+div#cgit table.ssdiff td.lineno a {
+ color: gray;
+ text-align: right;
+ text-decoration: none;
+}
+
+div#cgit table.blob td.linenumbers a:hover,
+div#cgit table.ssdiff td.lineno a:hover {
+ color: black;
+}
+
+div#cgit table.blame td.hashes,
+div#cgit table.blame td.lines,
+div#cgit table.blame td.linenumbers {
+ padding: 0;
+}
+
+div#cgit table.blame td.hashes div.alt,
+div#cgit table.blame td.lines div.alt {
+ padding: 0 0.5em 0 0.5em;
+}
+
+div#cgit table.blame td.linenumbers div.alt {
+ padding: 0 0.5em 0 0;
+}
+
+div#cgit table.blame div.alt:nth-child(even) {
+ background: #eee;
+}
+
+div#cgit table.blame div.alt:nth-child(odd) {
+ background: white;
+}
+
+div#cgit table.blame td.lines > div {
+ position: relative;
+}
+
+div#cgit table.blame td.lines > div > pre {
+ padding: 0 0 0 0.5em;
+ position: absolute;
+ top: 0;
+}
+
+div#cgit table.blame .oid {
+ font-size: 100%;
+}
+
+div#cgit table.bin-blob {
+ margin-top: 0.5em;
+ border: solid 1px black;
+}
+
+div#cgit table.bin-blob th {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+ border: solid 1px #777;
+ padding: 0.5em 1em;
+}
+
+div#cgit table.bin-blob td {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+ border-left: solid 1px #777;
+ padding: 0em 1em;
+}
+
+div#cgit table.nowrap td {
+ white-space: nowrap;
+}
+
+div#cgit table.commit-info {
+ border-collapse: collapse;
+ margin-top: 1.5em;
+}
+
+div#cgit div.cgit-panel {
+ float: right;
+ margin-top: 1.5em;
+}
+
+div#cgit div.cgit-panel table {
+ border-collapse: collapse;
+ border: solid 1px #aaa;
+ background-color: #eee;
+}
+
+div#cgit div.cgit-panel th {
+ text-align: center;
+}
+
+div#cgit div.cgit-panel td {
+ padding: 0.25em 0.5em;
+}
+
+div#cgit div.cgit-panel td.label {
+ padding-right: 0.5em;
+}
+
+div#cgit div.cgit-panel td.ctrl {
+ padding-left: 0.5em;
+}
+
+div#cgit table.commit-info th {
+ text-align: left;
+ font-weight: normal;
+ padding: 0.1em 1em 0.1em 0.1em;
+ vertical-align: top;
+}
+
+div#cgit table.commit-info td {
+ font-weight: normal;
+ padding: 0.1em 1em 0.1em 0.1em;
+}
+
+div#cgit div.commit-subject {
+ font-weight: bold;
+ font-size: 125%;
+ margin: 1.5em 0em 0.5em 0em;
+ padding: 0em;
+}
+
+div#cgit div.commit-msg {
+ white-space: pre;
+ font-family: "Source Code Pro", "Courier New", monospace;
+}
+
+div#cgit div.notes-header {
+ font-weight: bold;
+ padding-top: 1.5em;
+}
+
+div#cgit div.notes {
+ white-space: pre;
+ font-family: "Source Code Pro", "Courier New", monospace;
+ border: solid 1px #ee9;
+ background-color: #ffd;
+ padding: 0.3em 2em 0.3em 1em;
+ float: left;
+}
+
+div#cgit div.notes-footer {
+ clear: left;
+}
+
+div#cgit div.diffstat-header {
+ font-weight: bold;
+ padding-top: 1.5em;
+}
+
+div#cgit table.diffstat {
+ border-collapse: collapse;
+ border: solid 1px #aaa;
+ background-color: #eee;
+}
+
+div#cgit table.diffstat th {
+ font-weight: normal;
+ text-align: left;
+ text-decoration: underline;
+ padding: 0.1em 1em 0.1em 0.1em;
+ font-size: 100%;
+}
+
+div#cgit table.diffstat td {
+ padding: 0.2em 0.2em 0.1em 0.1em;
+ font-size: 100%;
+ border: none;
+}
+
+div#cgit table.diffstat td.mode {
+ white-space: nowrap;
+}
+
+div#cgit table.diffstat td span.modechange {
+ padding-left: 1em;
+ color: red;
+}
+
+div#cgit table.diffstat td.add a {
+ color: green;
+}
+
+div#cgit table.diffstat td.del a {
+ color: red;
+}
+
+div#cgit table.diffstat td.upd a {
+ color: blue;
+}
+
+div#cgit table.diffstat td.graph {
+ width: 500px;
+ vertical-align: middle;
+}
+
+div#cgit table.diffstat td.graph table {
+ border: none;
+}
+
+div#cgit table.diffstat td.graph td {
+ padding: 0px;
+ border: 0px;
+ height: 7pt;
+}
+
+div#cgit table.diffstat td.graph td.add {
+ background-color: #5c5;
+}
+
+div#cgit table.diffstat td.graph td.rem {
+ background-color: #c55;
+}
+
+div#cgit div.diffstat-summary {
+ color: #888;
+ padding-top: 0.5em;
+}
+
+div#cgit table.diff {
+ width: 100%;
+}
+
+div#cgit table.diff td {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+}
+
+div#cgit table.diff td div.head {
+ font-weight: bold;
+ margin-top: 1em;
+ color: black;
+}
+
+div#cgit table.diff td div.hunk {
+ color: #009;
+}
+
+div#cgit table.diff td div.add {
+ color: green;
+}
+
+div#cgit table.diff td div.del {
+ color: red;
+}
+
+div#cgit .oid {
+ font-family: "Source Code Pro", "Courier New", monospace;
+ font-size: 90%;
+}
+
+div#cgit .left {
+ text-align: left;
+}
+
+div#cgit .right {
+ text-align: right;
+}
+
+div#cgit table.list td.reposection {
+ font-style: italic;
+ color: #888;
+}
+
+div#cgit a.button {
+ font-size: 80%;
+ padding: 0em 0.5em;
+}
+
+div#cgit a.primary {
+ font-size: 100%;
+}
+
+div#cgit a.secondary {
+ font-size: 90%;
+}
+
+div#cgit td.toplevel-repo {
+
+}
+
+div#cgit table.list td.sublevel-repo {
+ padding-left: 1.5em;
+}
+
+div#cgit ul.pager {
+ list-style-type: none;
+ text-align: center;
+ margin: 1em 0em 0em 0em;
+ padding: 0;
+}
+
+div#cgit ul.pager li {
+ display: inline-block;
+ margin: 0.25em 0.5em;
+}
+
+div#cgit ul.pager a {
+ color: #777;
+}
+
+div#cgit ul.pager .current {
+ font-weight: bold;
+}
+
+div#cgit span.age-mins {
+ font-weight: bold;
+ color: #080;
+}
+
+div#cgit span.age-hours {
+ color: #080;
+}
+
+div#cgit span.age-days {
+ color: #040;
+}
+
+div#cgit span.age-weeks {
+ color: #444;
+}
+
+div#cgit span.age-months {
+ color: #888;
+}
+
+div#cgit span.age-years {
+ color: #bbb;
+}
+
+div#cgit span.insertions {
+ color: #080;
+}
+
+div#cgit span.deletions {
+ color: #800;
+}
+
+div#cgit div.footer {
+ margin-top: 0.5em;
+ text-align: center;
+ font-size: 80%;
+ color: #ccc;
+}
+
+div#cgit div.footer a {
+ color: #ccc;
+ text-decoration: none;
+}
+
+div#cgit div.footer a:hover {
+ text-decoration: underline;
+}
+
+div#cgit a.branch-deco {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #88ff88;
+ border: solid 1px #007700;
+}
+
+div#cgit a.tag-deco {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #ffff88;
+ border: solid 1px #777700;
+}
+
+div#cgit a.tag-annotated-deco {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #ffcc88;
+ border: solid 1px #777700;
+}
+
+div#cgit a.remote-deco {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #ccccff;
+ border: solid 1px #000077;
+}
+
+div#cgit a.deco {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #ff8888;
+ border: solid 1px #770000;
+}
+
+div#cgit div.commit-subject a.branch-deco,
+div#cgit div.commit-subject a.tag-deco,
+div#cgit div.commit-subject a.tag-annotated-deco,
+div#cgit div.commit-subject a.remote-deco,
+div#cgit div.commit-subject a.deco {
+ margin-left: 1em;
+ font-size: 75%;
+}
+
+div#cgit table.stats {
+ border: solid 1px black;
+ border-collapse: collapse;
+}
+
+div#cgit table.stats th {
+ text-align: left;
+ padding: 1px 0.5em;
+ background-color: #eee;
+ border: solid 1px black;
+}
+
+div#cgit table.stats td {
+ text-align: right;
+ padding: 1px 0.5em;
+ border: solid 1px black;
+}
+
+div#cgit table.stats td.total {
+ font-weight: bold;
+ text-align: left;
+}
+
+div#cgit table.stats td.sum {
+ color: #c00;
+ font-weight: bold;
+/* background-color: #eee; */
+}
+
+div#cgit table.stats td.left {
+ text-align: left;
+}
+
+div#cgit table.vgraph {
+ border-collapse: separate;
+ border: solid 1px black;
+ height: 200px;
+}
+
+div#cgit table.vgraph th {
+ background-color: #eee;
+ font-weight: bold;
+ border: solid 1px white;
+ padding: 1px 0.5em;
+}
+
+div#cgit table.vgraph td {
+ vertical-align: bottom;
+ padding: 0px 10px;
+}
+
+div#cgit table.vgraph div.bar {
+ background-color: #eee;
+}
+
+div#cgit table.hgraph {
+ border: solid 1px black;
+ width: 800px;
+}
+
+div#cgit table.hgraph th {
+ background-color: #eee;
+ font-weight: bold;
+ border: solid 1px black;
+ padding: 1px 0.5em;
+}
+
+div#cgit table.hgraph td {
+ vertical-align: middle;
+ padding: 2px 2px;
+}
+
+div#cgit table.hgraph div.bar {
+ background-color: #eee;
+ height: 1em;
+}
+
+div#cgit table.ssdiff {
+ width: 100%;
+}
+
+div#cgit table.ssdiff td {
+ font-size: 75%;
+ font-family: "Source Code Pro", "Courier New", monospace;
+ white-space: pre;
+ padding: 1px 4px 1px 4px;
+ border-left: solid 1px #aaa;
+ border-right: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.add {
+ color: black;
+ background: #cfc;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff td.add_dark {
+ color: black;
+ background: #aca;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff span.add {
+ background: #cfc;
+ font-weight: bold;
+}
+
+div#cgit table.ssdiff td.del {
+ color: black;
+ background: #fcc;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff td.del_dark {
+ color: black;
+ background: #caa;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff span.del {
+ background: #fcc;
+ font-weight: bold;
+}
+
+div#cgit table.ssdiff td.changed {
+ color: black;
+ background: #ffc;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff td.changed_dark {
+ color: black;
+ background: #cca;
+ min-width: 50%;
+}
+
+div#cgit table.ssdiff td.lineno {
+ color: black;
+ background: #eee;
+ text-align: right;
+ width: 3em;
+ min-width: 3em;
+}
+
+div#cgit table.ssdiff td.hunk {
+ color: black;
+ background: #ccf;
+ border-top: solid 1px #aaa;
+ border-bottom: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.head {
+ border-top: solid 1px #aaa;
+ border-bottom: solid 1px #aaa;
+}
+
+div#cgit table.ssdiff td.head div.head {
+ font-weight: bold;
+ color: black;
+}
+
+div#cgit table.ssdiff td.foot {
+ border-top: solid 1px #aaa;
+ border-left: none;
+ border-right: none;
+ border-bottom: none;
+}
+
+div#cgit table.ssdiff td.space {
+ border: none;
+}
+
+div#cgit table.ssdiff td.space div {
+ min-height: 3em;
+}
+div#cgit span.libravatar img.onhover {
+ display: none;
+ border: 1px solid gray;
+ padding: 0px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ width: 128px;
+ height: 128px;
+}
+
+div#cgit span.libravatar img.inline {
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ width: 13px;
+ height: 13px;
+ margin-right: 0.2em;
+ opacity: 0.6;
+}
+
+div#cgit span.libravatar:hover > img.onhover {
+ display: block;
+ position: absolute;
+ margin-left: 1.5em;
+ background-color: #eeeeee;
+ box-shadow: 2px 2px 7px rgba(100,100,100,0.75);
+}
+
+/*
+ Color overrides for browsers running in dark mode.
+ "only all and ..." forces very old browsers to ignore the media query:
+ https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#improving_compatibility_with_older_browsers
+*/
+:root { color-scheme: light dark; }
+@media only all and (prefers-color-scheme: dark) {
+ html, div#cgit { color: #eee; background: #171717; }
+ div#cgit a { color: #3af; }
+ div#cgit .diffstat-header a { color: #28d; }
+ div#cgit table#header td.main a { color: #eee; }
+ div#cgit table#header td.sub { color: #999; }
+ div#cgit table.tabs { border-bottom-color: #444; }
+ div#cgit table.tabs td a { color: #888; }
+ div#cgit table.tabs td a.active { color: #fff; background-color: #444; }
+ div#cgit div.path { color: #eee; background-color: #333; }
+ div#cgit div.content { border-bottom-color: #bbb; }
+ div#cgit table.list tr { background: #171717; }
+ div#cgit table.list tr.logheader { background: #171717; }
+ div#cgit table.list tr:nth-child(even) { background: #171717; }
+ div#cgit table.list tr:nth-child(odd) { background: #1f1f1f; }
+ div#cgit table.list tr:hover { background: #333; }
+ div#cgit table.list tr.nohover { background: #171717; }
+ div#cgit table.list tr.nohover:hover { background: #171717; }
+ div#cgit table.list tr.nohover-highlight:hover:nth-child(even) { background: #171717; }
+ div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) { background: #1f1f1f; }
+ div#cgit table.list td.commitgraph .column1 { color: #f55; }
+ div#cgit table.list td.commitgraph .column2 { color: #5f5; }
+ div#cgit table.list td.commitgraph .column3 { color: #ff5; }
+ div#cgit table.list td.commitgraph .column4 { color: #55f; }
+ div#cgit table.list td.commitgraph .column5 { color: #f5f; }
+ div#cgit table.list td.commitgraph .column6 { color: #5ff; }
+ div#cgit table.list td a { color: #eee; }
+ div#cgit table.list td a.ls-dir { color: #28d; }
+ div#cgit table.list td a:hover { color: #3af; }
+ div#cgit table#downloads { border-color: #888; }
+ div#cgit table#downloads th { background-color: #333; }
+ div#cgit div#blob { border-color: #eee; }
+ div#cgit table.blob { border-top-color: #eee; }
+ div#cgit table.blob td.hashes,
+ div#cgit table.blob td.lines { color: #eee; }
+ div#cgit table.blob td.linenumbers { border-right-color: gray; }
+ div#cgit table.blob td.linenumbers a,
+ div#cgit table.ssdiff td.lineno a { color: gray; }
+ div#cgit table.blob td.linenumbers a:hover,
+ div#cgit table.ssdiff td.lineno a:hover { color: #eee; }
+ div#cgit table.blame div.alt:nth-child(even) { background: #171717; }
+ div#cgit table.blame div.alt:nth-child(odd) { background: #1f1f1f; }
+ div#cgit table.bin-blob { border-color: #eee; }
+ div#cgit table.bin-blob th { border-color: #888; }
+ div#cgit table.bin-blob td { border-left-color: #888; }
+ div#cgit div.cgit-panel table { border-color: #555; background-color: #282828; }
+ div#cgit div.notes { border-color: #661; background-color: #220; }
+ div#cgit table.diffstat { border-color: #555; background-color: #282828; }
+ div#cgit table.diffstat td span.modechange { color: #c66; }
+ div#cgit table.diffstat td.add a { color: #6c6; }
+ div#cgit table.diffstat td.del a { color: #c66; }
+ div#cgit table.diffstat td.upd a { color: #3af; }
+ div#cgit table.diffstat td.graph td.add { background-color: #3a3; }
+ div#cgit table.diffstat td.graph td.rem { background-color: #a33; }
+ div#cgit div.diffstat-summary { color: #777; }
+ div#cgit table.diff td div.head { color: #eee; }
+ div#cgit table.diff td div.hunk { color: #28d; }
+ div#cgit table.diff td div.add { color: #6c6; }
+ div#cgit table.diff td div.del { color: #c66; }
+ div#cgit table.list td.reposection { color: #777; }
+ div#cgit ul.pager a { color: #888; }
+ div#cgit span.age-mins { color: #7f7; }
+ div#cgit span.age-hours { color: #7f7; }
+ div#cgit span.age-days { color: #9d9; }
+ div#cgit span.age-weeks { color: #bbb; }
+ div#cgit span.age-months { color: #888; }
+ div#cgit span.age-years { color: #666; }
+ div#cgit span.insertions { color: #7f7; }
+ div#cgit span.deletions { color: #e33; }
+ div#cgit div.footer { color: #555; }
+ div#cgit div.footer a { color: #555; }
+ div#cgit a.branch-deco { color: #fff; background-color: #361; border-color: #3b2; }
+ div#cgit a.tag-deco { color: #fff; background-color: #650; border-color: #ba3; }
+ div#cgit a.tag-annotated-deco { color: #fff; background-color: #840; border-color: #b96; }
+ div#cgit a.remote-deco { color: #fff; background-color: #348; border-color: #67c; }
+ div#cgit a.deco { color: #fff; background-color: #922; border-color: #d55; }
+ div#cgit a.branch-deco:hover, div#cgit table.list td a.branch-deco:hover,
+ div#cgit a.tag-deco:hover, div#cgit table.list td a.tag-deco:hover,
+ div#cgit a.tag-annotated-deco:hover, div#cgit table.list td a.tag-annotated-deco:hover,
+ div#cgit a.remote-deco:hover, div#cgit table.list td a.remote-deco:hover,
+ div#cgit a.deco:hover, div#cgit table.list td a.deco:hover { color: #fff; }
+ div#cgit table.stats { border-color: #555; }
+ div#cgit table.stats th { background-color: #333; border-color: #555; }
+ div#cgit table.stats td { border-color: #555; }
+ div#cgit table.stats td.sum { color: #5C7E9B; }
+ div#cgit table.vgraph { border-color: #eee; }
+ div#cgit table.vgraph th { background-color: #1f1f1f; border-color: black; }
+ div#cgit table.vgraph div.bar { background-color: #1f1f1f; }
+ div#cgit table.hgraph { border-color: #eee; }
+ div#cgit table.hgraph th { background-color: #1f1f1f; border-color: #eee; }
+ div#cgit table.hgraph div.bar { background-color: #1f1f1f; }
+ div#cgit table.ssdiff td { border-left-color: #555; border-right-color: #555; }
+ div#cgit table.ssdiff td.add { color: #eee; background: #353; }
+ div#cgit table.ssdiff td.add_dark { color: #eee; background: #030; }
+ div#cgit table.ssdiff span.add { background: #030; }
+ div#cgit table.ssdiff td.del { color: #eee; background: #533; }
+ div#cgit table.ssdiff td.del_dark { color: #eee; background: #411; }
+ div#cgit table.ssdiff span.del { background: #300; }
+ div#cgit table.ssdiff td.changed { color: #eee; background: #553; }
+ div#cgit table.ssdiff td.changed_dark { color: #eee; background: #330; }
+ div#cgit table.ssdiff td.lineno { color: #eee; background: #1f1f1f; }
+ div#cgit table.ssdiff td.hunk { color: #eee; background: #0D3D64; border-top-color: #555; border-bottom-color: #555; }
+ div#cgit table.ssdiff td.head { border-top-color: #555; border-bottom-color: #555; }
+ div#cgit table.ssdiff td.head div.head { color: #eee; }
+ div#cgit table.ssdiff td.foot { border-top-color: #555; }
+}
diff --git a/cgit.js b/cgit.js
new file mode 100644
index 0000000..df3ad4e
--- /dev/null
+++ b/cgit.js
@@ -0,0 +1,68 @@
+/* cgit.js: javacript functions for cgit
+ *
+ * Copyright (C) 2006-2018 cgit Development Team <cgit@lists.zx2c4.com>
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+(function () {
+
+/* This follows the logic and suffixes used in ui-shared.c */
+
+var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", "age-months", "age-years" ];
+var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", "years" ];
+var age_next = [ 60, 3600, 24 * 3600, 7 * 24 * 3600, 30 * 24 * 3600, 365 * 24 * 3600, 365 * 24 * 3600 ];
+var age_limit = [ 7200, 24 * 7200, 7 * 24 * 7200, 30 * 24 * 7200, 365 * 25 * 7200, 365 * 25 * 7200 ];
+var update_next = [ 10, 5 * 60, 1800, 24 * 3600, 24 * 3600, 24 * 3600, 24 * 3600 ];
+
+function render_age(e, age) {
+ var t, n;
+
+ for (n = 0; n < age_classes.length; n++)
+ if (age < age_limit[n])
+ break;
+
+ t = Math.round(age / age_next[n]) + " " + age_suffix[n];
+
+ if (e.textContent != t) {
+ e.textContent = t;
+ if (n == age_classes.length)
+ n--;
+ if (e.className != age_classes[n])
+ e.className = age_classes[n];
+ }
+}
+
+function aging() {
+ var n, next = 24 * 3600,
+ now_ut = Math.round((new Date().getTime() / 1000));
+
+ for (n = 0; n < age_classes.length; n++) {
+ var m, elems = document.getElementsByClassName(age_classes[n]);
+
+ if (elems.length && update_next[n] < next)
+ next = update_next[n];
+
+ for (m = 0; m < elems.length; m++) {
+ var age = now_ut - elems[m].getAttribute("data-ut");
+
+ render_age(elems[m], age);
+ }
+ }
+
+ /*
+ * We only need to come back when the age might have changed.
+ * Eg, if everything is counted in hours already, once per
+ * 5 minutes is accurate enough.
+ */
+
+ window.setTimeout(aging, next * 1000);
+}
+
+document.addEventListener("DOMContentLoaded", function() {
+ /* we can do the aging on DOM content load since no layout dependency */
+ aging();
+}, false);
+
+})();
diff --git a/cgit.png b/cgit.png
new file mode 100644
index 0000000..55c660a
--- /dev/null
+++ b/cgit.png
Binary files differ
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..d37c021
--- /dev/null
+++ b/favicon.ico
Binary files differ
diff --git a/filters/about-formatting.sh b/filters/about-formatting.sh
new file mode 100755
index 0000000..85daf9c
--- /dev/null
+++ b/filters/about-formatting.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# This may be used with the about-filter or repo.about-filter setting in cgitrc.
+# It passes formatting of about pages to differing programs, depending on the usage.
+
+# Markdown support requires python and markdown-python.
+# RestructuredText support requires python and docutils.
+# Man page support requires groff.
+
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL ( = repo.url setting )
+# CGIT_REPO_NAME ( = repo.name setting )
+# CGIT_REPO_PATH ( = repo.path setting )
+# CGIT_REPO_OWNER ( = repo.owner setting )
+# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
+# CGIT_REPO_SECTION ( = section setting )
+# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
+
+cd "$(dirname $0)/html-converters/"
+case "$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" in
+ *.markdown|*.mdown|*.md|*.mkd) exec ./md2html; ;;
+ *.rst) exec ./rst2html; ;;
+ *.[1-9]) exec ./man2html; ;;
+ *.htm|*.html) exec cat; ;;
+ *.txt|*) exec ./txt2html; ;;
+esac
diff --git a/filters/commit-links.sh b/filters/commit-links.sh
new file mode 100755
index 0000000..796ac30
--- /dev/null
+++ b/filters/commit-links.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# This script can be used to generate links in commit messages.
+#
+# To use this script, refer to this file with either the commit-filter or the
+# repo.commit-filter options in cgitrc.
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL ( = repo.url setting )
+# CGIT_REPO_NAME ( = repo.name setting )
+# CGIT_REPO_PATH ( = repo.path setting )
+# CGIT_REPO_OWNER ( = repo.owner setting )
+# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
+# CGIT_REPO_SECTION ( = section setting )
+# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
+#
+
+regex=''
+
+# This expression generates links to commits referenced by their SHA1.
+regex=$regex'
+s|\b([0-9a-fA-F]{7,64})\b|<a href="./?id=\1">\1</a>|g'
+
+# This expression generates links to a fictional bugtracker.
+regex=$regex'
+s|#([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
+
+sed -re "$regex"
diff --git a/filters/email-gravatar.lua b/filters/email-gravatar.lua
new file mode 100644
index 0000000..c39b490
--- /dev/null
+++ b/filters/email-gravatar.lua
@@ -0,0 +1,35 @@
+-- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+-- It adds gravatar icons to author names. It is designed to be used with the lua:
+-- prefix in filters. It is much faster than the corresponding python script.
+--
+-- Requirements:
+-- luaossl
+-- <http://25thandclement.com/~william/projects/luaossl.html>
+--
+
+local digest = require("openssl.digest")
+
+function md5_hex(input)
+ local b = digest.new("md5"):final(input)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+function filter_open(email, page)
+ buffer = ""
+ md5 = md5_hex(email:sub(2, -2):lower())
+end
+
+function filter_close()
+ html("<img src='//www.gravatar.com/avatar/" .. md5 .. "?s=13&amp;d=retro' width='13' height='13' alt='Gravatar' /> " .. buffer)
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
+
+
diff --git a/filters/email-gravatar.py b/filters/email-gravatar.py
new file mode 100755
index 0000000..d70440e
--- /dev/null
+++ b/filters/email-gravatar.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+# Please prefer the email-gravatar.lua using lua: as a prefix over this script. This
+# script is very slow, in comparison.
+#
+# This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL ( = repo.url setting )
+# CGIT_REPO_NAME ( = repo.name setting )
+# CGIT_REPO_PATH ( = repo.path setting )
+# CGIT_REPO_OWNER ( = repo.owner setting )
+# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
+# CGIT_REPO_SECTION ( = section setting )
+# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
+#
+# It receives an email address on argv[1] and text on stdin. It prints
+# to stdout that text prepended by a gravatar at 10pt.
+
+import sys
+import hashlib
+import codecs
+
+email = sys.argv[1].lower().strip()
+if email[0] == '<':
+ email = email[1:]
+if email[-1] == '>':
+ email = email[0:-1]
+
+page = sys.argv[2]
+
+sys.stdin = codecs.getreader("utf-8")(sys.stdin.detach())
+sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
+
+md5 = hashlib.md5(email.encode()).hexdigest()
+text = sys.stdin.read().strip()
+
+print("<img src='//www.gravatar.com/avatar/" + md5 + "?s=13&amp;d=retro' width='13' height='13' alt='Gravatar' /> " + text)
diff --git a/filters/email-libravatar.lua b/filters/email-libravatar.lua
new file mode 100644
index 0000000..7336baf
--- /dev/null
+++ b/filters/email-libravatar.lua
@@ -0,0 +1,36 @@
+-- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+-- It adds libravatar icons to author names. It is designed to be used with the lua:
+-- prefix in filters.
+--
+-- Requirements:
+-- luaossl
+-- <http://25thandclement.com/~william/projects/luaossl.html>
+--
+
+local digest = require("openssl.digest")
+
+function md5_hex(input)
+ local b = digest.new("md5"):final(input)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+function filter_open(email, page)
+ buffer = ""
+ md5 = md5_hex(email:sub(2, -2):lower())
+end
+
+function filter_close()
+ baseurl = os.getenv("HTTPS") and "https://seccdn.libravatar.org/" or "http://cdn.libravatar.org/"
+ html("<img src='" .. baseurl .. "avatar/" .. md5 .. "?s=13&amp;d=retro' width='13' height='13' alt='Libravatar' /> " .. buffer)
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
+
+
diff --git a/filters/file-authentication.lua b/filters/file-authentication.lua
new file mode 100644
index 0000000..0248804
--- /dev/null
+++ b/filters/file-authentication.lua
@@ -0,0 +1,359 @@
+-- This script may be used with the auth-filter.
+--
+-- Requirements:
+-- luaossl
+-- <http://25thandclement.com/~william/projects/luaossl.html>
+-- luaposix
+-- <https://github.com/luaposix/luaposix>
+--
+local sysstat = require("posix.sys.stat")
+local unistd = require("posix.unistd")
+local rand = require("openssl.rand")
+local hmac = require("openssl.hmac")
+
+-- This file should contain a series of lines in the form of:
+-- username1:hash1
+-- username2:hash2
+-- username3:hash3
+-- ...
+-- Hashes can be generated using something like `mkpasswd -m sha-512 -R 300000`.
+-- This file should not be world-readable.
+local users_filename = "/etc/cgit-auth/users"
+
+-- This file should contain a series of lines in the form of:
+-- groupname1:username1,username2,username3,...
+-- ...
+local groups_filename = "/etc/cgit-auth/groups"
+
+-- This file should contain a series of lines in the form of:
+-- reponame1:groupname1,groupname2,groupname3,...
+-- ...
+local repos_filename = "/etc/cgit-auth/repos"
+
+-- Set this to a path this script can write to for storing a persistent
+-- cookie secret, which should not be world-readable.
+local secret_filename = "/var/cache/cgit/auth-secret"
+
+--
+--
+-- Authentication functions follow below. Swap these out if you want different authentication semantics.
+--
+--
+
+-- Looks up a hash for a given user.
+function lookup_hash(user)
+ local line
+ for line in io.lines(users_filename) do
+ local u, h = string.match(line, "(.-):(.+)")
+ if u:lower() == user:lower() then
+ return h
+ end
+ end
+ return nil
+end
+
+-- Looks up users for a given repo.
+function lookup_users(repo)
+ local users = nil
+ local groups = nil
+ local line, group, user
+ for line in io.lines(repos_filename) do
+ local r, g = string.match(line, "(.-):(.+)")
+ if r == repo then
+ groups = { }
+ for group in string.gmatch(g, "([^,]+)") do
+ groups[group:lower()] = true
+ end
+ break
+ end
+ end
+ if groups == nil then
+ return nil
+ end
+ for line in io.lines(groups_filename) do
+ local g, u = string.match(line, "(.-):(.+)")
+ if groups[g:lower()] then
+ if users == nil then
+ users = { }
+ end
+ for user in string.gmatch(u, "([^,]+)") do
+ users[user:lower()] = true
+ end
+ end
+ end
+ return users
+end
+
+
+-- Sets HTTP cookie headers based on post and sets up redirection.
+function authenticate_post()
+ local hash = lookup_hash(post["username"])
+ local redirect = validate_value("redirect", post["redirect"])
+
+ if redirect == nil then
+ not_found()
+ return 0
+ end
+
+ redirect_to(redirect)
+
+ if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
+ set_cookie("cgitauth", "")
+ else
+ -- One week expiration time
+ local username = secure_value("username", post["username"], os.time() + 604800)
+ set_cookie("cgitauth", username)
+ end
+
+ html("\n")
+ return 0
+end
+
+
+-- Returns 1 if the cookie is valid and 0 if it is not.
+function authenticate_cookie()
+ accepted_users = lookup_users(cgit["repo"])
+ if accepted_users == nil then
+ -- We return as valid if the repo is not protected.
+ return 1
+ end
+
+ local username = validate_value("username", get_cookie(http["cookie"], "cgitauth"))
+ if username == nil or not accepted_users[username:lower()] then
+ return 0
+ else
+ return 1
+ end
+end
+
+-- Prints the html for the login form.
+function body()
+ html("<h2>Authentication Required</h2>")
+ html("<form method='post' action='")
+ html_attr(cgit["login"])
+ html("'>")
+ html("<input type='hidden' name='redirect' value='")
+ html_attr(secure_value("redirect", cgit["url"], 0))
+ html("' />")
+ html("<table>")
+ html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
+ html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
+ html("</table></form>")
+
+ return 0
+end
+
+
+
+--
+--
+-- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
+--
+--
+
+local actions = {}
+actions["authenticate-post"] = authenticate_post
+actions["authenticate-cookie"] = authenticate_cookie
+actions["body"] = body
+
+function filter_open(...)
+ action = actions[select(1, ...)]
+
+ http = {}
+ http["cookie"] = select(2, ...)
+ http["method"] = select(3, ...)
+ http["query"] = select(4, ...)
+ http["referer"] = select(5, ...)
+ http["path"] = select(6, ...)
+ http["host"] = select(7, ...)
+ http["https"] = select(8, ...)
+
+ cgit = {}
+ cgit["repo"] = select(9, ...)
+ cgit["page"] = select(10, ...)
+ cgit["url"] = select(11, ...)
+ cgit["login"] = select(12, ...)
+
+end
+
+function filter_close()
+ return action()
+end
+
+function filter_write(str)
+ post = parse_qs(str)
+end
+
+
+--
+--
+-- Utility functions based on keplerproject/wsapi.
+--
+--
+
+function url_decode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "+", " ")
+ str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
+ str = string.gsub(str, "\r\n", "\n")
+ return str
+end
+
+function url_encode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "\n", "\r\n")
+ str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
+ str = string.gsub(str, " ", "+")
+ return str
+end
+
+function parse_qs(qs)
+ local tab = {}
+ for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
+ tab[url_decode(key)] = url_decode(val)
+ end
+ return tab
+end
+
+function get_cookie(cookies, name)
+ cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
+ return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
+end
+
+function tohex(b)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+--
+--
+-- Cookie construction and validation helpers.
+--
+--
+
+local secret = nil
+
+-- Loads a secret from a file, creates a secret, or returns one from memory.
+function get_secret()
+ if secret ~= nil then
+ return secret
+ end
+ local secret_file = io.open(secret_filename, "r")
+ if secret_file == nil then
+ local old_umask = sysstat.umask(63)
+ local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
+ local temporary_file = io.open(temporary_filename, "w")
+ if temporary_file == nil then
+ os.exit(177)
+ end
+ temporary_file:write(tohex(rand.bytes(32)))
+ temporary_file:close()
+ unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
+ unistd.unlink(temporary_filename)
+ sysstat.umask(old_umask)
+ secret_file = io.open(secret_filename, "r")
+ end
+ if secret_file == nil then
+ os.exit(177)
+ end
+ secret = secret_file:read()
+ secret_file:close()
+ if secret:len() ~= 64 then
+ os.exit(177)
+ end
+ return secret
+end
+
+-- Returns value of cookie if cookie is valid. Otherwise returns nil.
+function validate_value(expected_field, cookie)
+ local i = 0
+ local value = ""
+ local field = ""
+ local expiration = 0
+ local salt = ""
+ local chmac = ""
+
+ if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
+ return nil
+ end
+
+ for component in string.gmatch(cookie, "[^|]+") do
+ if i == 0 then
+ field = component
+ elseif i == 1 then
+ value = component
+ elseif i == 2 then
+ expiration = tonumber(component)
+ if expiration == nil then
+ expiration = -1
+ end
+ elseif i == 3 then
+ salt = component
+ elseif i == 4 then
+ chmac = component
+ else
+ break
+ end
+ i = i + 1
+ end
+
+ if chmac == nil or chmac:len() == 0 then
+ return nil
+ end
+
+ -- Lua hashes strings, so these comparisons are time invariant.
+ if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
+ return nil
+ end
+
+ if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
+ return nil
+ end
+
+ if url_decode(field) ~= expected_field then
+ return nil
+ end
+
+ return url_decode(value)
+end
+
+function secure_value(field, value, expiration)
+ if value == nil or value:len() <= 0 then
+ return ""
+ end
+
+ local authstr = ""
+ local salt = tohex(rand.bytes(16))
+ value = url_encode(value)
+ field = url_encode(field)
+ authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
+ authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
+ return authstr
+end
+
+function set_cookie(cookie, value)
+ html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
+ if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
+ html("; secure")
+ end
+ html("\n")
+end
+
+function redirect_to(url)
+ html("Status: 302 Redirect\n")
+ html("Cache-Control: no-cache, no-store\n")
+ html("Location: " .. url .. "\n")
+end
+
+function not_found()
+ html("Status: 404 Not Found\n")
+ html("Cache-Control: no-cache, no-store\n\n")
+end
diff --git a/filters/gentoo-ldap-authentication.lua b/filters/gentoo-ldap-authentication.lua
new file mode 100644
index 0000000..673c88d
--- /dev/null
+++ b/filters/gentoo-ldap-authentication.lua
@@ -0,0 +1,360 @@
+-- This script may be used with the auth-filter. Be sure to configure it as you wish.
+--
+-- Requirements:
+-- luaossl
+-- <http://25thandclement.com/~william/projects/luaossl.html>
+-- lualdap >= 1.2
+-- <https://git.zx2c4.com/lualdap/about/>
+-- luaposix
+-- <https://github.com/luaposix/luaposix>
+--
+local sysstat = require("posix.sys.stat")
+local unistd = require("posix.unistd")
+local lualdap = require("lualdap")
+local rand = require("openssl.rand")
+local hmac = require("openssl.hmac")
+
+--
+--
+-- Configure these variables for your settings.
+--
+--
+
+-- A list of password protected repositories, with which gentooAccess
+-- group is allowed to access each one.
+local protected_repos = {
+ glouglou = "infra",
+ portage = "dev"
+}
+
+-- Set this to a path this script can write to for storing a persistent
+-- cookie secret, which should be guarded.
+local secret_filename = "/var/cache/cgit/auth-secret"
+
+
+--
+--
+-- Authentication functions follow below. Swap these out if you want different authentication semantics.
+--
+--
+
+-- Sets HTTP cookie headers based on post and sets up redirection.
+function authenticate_post()
+ local redirect = validate_value("redirect", post["redirect"])
+
+ if redirect == nil then
+ not_found()
+ return 0
+ end
+
+ redirect_to(redirect)
+
+ local groups = gentoo_ldap_user_groups(post["username"], post["password"])
+ if groups == nil then
+ set_cookie("cgitauth", "")
+ else
+ -- One week expiration time
+ set_cookie("cgitauth", secure_value("gentoogroups", table.concat(groups, ","), os.time() + 604800))
+ end
+
+ html("\n")
+ return 0
+end
+
+
+-- Returns 1 if the cookie is valid and 0 if it is not.
+function authenticate_cookie()
+ local required_group = protected_repos[cgit["repo"]]
+ if required_group == nil then
+ -- We return as valid if the repo is not protected.
+ return 1
+ end
+
+ local user_groups = validate_value("gentoogroups", get_cookie(http["cookie"], "cgitauth"))
+ if user_groups == nil or user_groups == "" then
+ return 0
+ end
+ for group in string.gmatch(user_groups, "[^,]+") do
+ if group == required_group then
+ return 1
+ end
+ end
+ return 0
+end
+
+-- Prints the html for the login form.
+function body()
+ html("<h2>Gentoo LDAP Authentication Required</h2>")
+ html("<form method='post' action='")
+ html_attr(cgit["login"])
+ html("'>")
+ html("<input type='hidden' name='redirect' value='")
+ html_attr(secure_value("redirect", cgit["url"], 0))
+ html("' />")
+ html("<table>")
+ html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
+ html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
+ html("</table></form>")
+
+ return 0
+end
+
+--
+--
+-- Gentoo LDAP support.
+--
+--
+
+function gentoo_ldap_user_groups(username, password)
+ -- Ensure the user is alphanumeric
+ if username == nil or username:match("%W") then
+ return nil
+ end
+
+ local who = "uid=" .. username .. ",ou=devs,dc=gentoo,dc=org"
+
+ local ldap, err = lualdap.open_simple {
+ uri = "ldap://ldap1.gentoo.org",
+ who = who,
+ password = password,
+ starttls = true,
+ certfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.crt",
+ keyfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.key",
+ cacertfile = "/var/www/uwsgi/cgit/gentoo-ldap/ca.pem"
+ }
+ if ldap == nil then
+ return nil
+ end
+
+ local group_suffix = ".group"
+ local group_suffix_len = group_suffix:len()
+ local groups = {}
+ for dn, attribs in ldap:search { base = who, scope = "subtree" } do
+ local access = attribs["gentooAccess"]
+ if dn == who and access ~= nil then
+ for i, v in ipairs(access) do
+ local vlen = v:len()
+ if vlen > group_suffix_len and v:sub(-group_suffix_len) == group_suffix then
+ table.insert(groups, v:sub(1, vlen - group_suffix_len))
+ end
+ end
+ end
+ end
+
+ ldap:close()
+
+ return groups
+end
+
+--
+--
+-- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
+--
+--
+
+local actions = {}
+actions["authenticate-post"] = authenticate_post
+actions["authenticate-cookie"] = authenticate_cookie
+actions["body"] = body
+
+function filter_open(...)
+ action = actions[select(1, ...)]
+
+ http = {}
+ http["cookie"] = select(2, ...)
+ http["method"] = select(3, ...)
+ http["query"] = select(4, ...)
+ http["referer"] = select(5, ...)
+ http["path"] = select(6, ...)
+ http["host"] = select(7, ...)
+ http["https"] = select(8, ...)
+
+ cgit = {}
+ cgit["repo"] = select(9, ...)
+ cgit["page"] = select(10, ...)
+ cgit["url"] = select(11, ...)
+ cgit["login"] = select(12, ...)
+
+end
+
+function filter_close()
+ return action()
+end
+
+function filter_write(str)
+ post = parse_qs(str)
+end
+
+
+--
+--
+-- Utility functions based on keplerproject/wsapi.
+--
+--
+
+function url_decode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "+", " ")
+ str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
+ str = string.gsub(str, "\r\n", "\n")
+ return str
+end
+
+function url_encode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "\n", "\r\n")
+ str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
+ str = string.gsub(str, " ", "+")
+ return str
+end
+
+function parse_qs(qs)
+ local tab = {}
+ for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
+ tab[url_decode(key)] = url_decode(val)
+ end
+ return tab
+end
+
+function get_cookie(cookies, name)
+ cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
+ return string.match(cookies, ";" .. name .. "=(.-);")
+end
+
+function tohex(b)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+--
+--
+-- Cookie construction and validation helpers.
+--
+--
+
+local secret = nil
+
+-- Loads a secret from a file, creates a secret, or returns one from memory.
+function get_secret()
+ if secret ~= nil then
+ return secret
+ end
+ local secret_file = io.open(secret_filename, "r")
+ if secret_file == nil then
+ local old_umask = sysstat.umask(63)
+ local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
+ local temporary_file = io.open(temporary_filename, "w")
+ if temporary_file == nil then
+ os.exit(177)
+ end
+ temporary_file:write(tohex(rand.bytes(32)))
+ temporary_file:close()
+ unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
+ unistd.unlink(temporary_filename)
+ sysstat.umask(old_umask)
+ secret_file = io.open(secret_filename, "r")
+ end
+ if secret_file == nil then
+ os.exit(177)
+ end
+ secret = secret_file:read()
+ secret_file:close()
+ if secret:len() ~= 64 then
+ os.exit(177)
+ end
+ return secret
+end
+
+-- Returns value of cookie if cookie is valid. Otherwise returns nil.
+function validate_value(expected_field, cookie)
+ local i = 0
+ local value = ""
+ local field = ""
+ local expiration = 0
+ local salt = ""
+ local chmac = ""
+
+ if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
+ return nil
+ end
+
+ for component in string.gmatch(cookie, "[^|]+") do
+ if i == 0 then
+ field = component
+ elseif i == 1 then
+ value = component
+ elseif i == 2 then
+ expiration = tonumber(component)
+ if expiration == nil then
+ expiration = -1
+ end
+ elseif i == 3 then
+ salt = component
+ elseif i == 4 then
+ chmac = component
+ else
+ break
+ end
+ i = i + 1
+ end
+
+ if chmac == nil or chmac:len() == 0 then
+ return nil
+ end
+
+ -- Lua hashes strings, so these comparisons are time invariant.
+ if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
+ return nil
+ end
+
+ if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
+ return nil
+ end
+
+ if url_decode(field) ~= expected_field then
+ return nil
+ end
+
+ return url_decode(value)
+end
+
+function secure_value(field, value, expiration)
+ if value == nil or value:len() <= 0 then
+ return ""
+ end
+
+ local authstr = ""
+ local salt = tohex(rand.bytes(16))
+ value = url_encode(value)
+ field = url_encode(field)
+ authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
+ authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
+ return authstr
+end
+
+function set_cookie(cookie, value)
+ html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
+ if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
+ html("; secure")
+ end
+ html("\n")
+end
+
+function redirect_to(url)
+ html("Status: 302 Redirect\n")
+ html("Cache-Control: no-cache, no-store\n")
+ html("Location: " .. url .. "\n")
+end
+
+function not_found()
+ html("Status: 404 Not Found\n")
+ html("Cache-Control: no-cache, no-store\n\n")
+end
diff --git a/filters/html-converters/man2html b/filters/html-converters/man2html
new file mode 100755
index 0000000..0ef7884
--- /dev/null
+++ b/filters/html-converters/man2html
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "<div style=\"font-family: monospace\">"
+groff -mandoc -T html -P -r -P -l | egrep -v '(<html>|<head>|<meta|<title>|</title>|</head>|<body>|</body>|</html>|<!DOCTYPE|"http://www.w3.org)'
+echo "</div>"
diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html
new file mode 100755
index 0000000..3f5aed7
--- /dev/null
+++ b/filters/html-converters/md2html
@@ -0,0 +1,330 @@
+#!/usr/bin/env python3
+import markdown
+import sys
+import io
+from pygments.formatters import HtmlFormatter
+from markdown.extensions.toc import TocExtension
+
+# The dark style is automatically selected if the browser is in dark mode
+light_style='pastie'
+dark_style='monokai'
+
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
+sys.stdout.write('''
+<style>
+.markdown-body {
+ font-size: 14px;
+ line-height: 1.6;
+ overflow: hidden;
+}
+.markdown-body>*:first-child {
+ margin-top: 0 !important;
+}
+.markdown-body>*:last-child {
+ margin-bottom: 0 !important;
+}
+.markdown-body a.absent {
+ color: #c00;
+}
+.markdown-body a.anchor {
+ display: block;
+ padding-left: 30px;
+ margin-left: -30px;
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+}
+.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
+ margin: 20px 0 10px;
+ padding: 0;
+ font-weight: bold;
+ -webkit-font-smoothing: antialiased;
+ cursor: text;
+ position: relative;
+}
+.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
+ display: none;
+ color: #000;
+}
+.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
+ text-decoration: none;
+ line-height: 1;
+ padding-left: 0;
+ margin-left: -22px;
+ top: 15%;
+}
+.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
+ display: inline-block;
+}
+div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
+ color: black;
+}
+.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
+ font-size: inherit;
+}
+.markdown-body h1 {
+ font-size: 28px;
+ color: #000;
+}
+.markdown-body h2 {
+ font-size: 24px;
+ border-bottom: 1px solid #ccc;
+ color: #000;
+}
+.markdown-body h3 {
+ font-size: 18px;
+}
+.markdown-body h4 {
+ font-size: 16px;
+}
+.markdown-body h5 {
+ font-size: 14px;
+}
+.markdown-body h6 {
+ color: #777;
+ font-size: 14px;
+}
+.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
+ margin: 15px 0;
+}
+.markdown-body hr {
+ border: 2px solid #ccc;
+}
+.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
+ margin-top: 0;
+ padding-top: 0;
+}
+.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
+ margin-top: 0;
+ padding-top: 0;
+}
+.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
+ margin-top: 0;
+}
+.markdown-body li p.first {
+ display: inline-block;
+}
+.markdown-body ul, .markdown-body ol {
+ padding-left: 30px;
+}
+.markdown-body ul.no-list, .markdown-body ol.no-list {
+ list-style-type: none;
+ padding: 0;
+}
+.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
+ margin-top: 0px;
+}
+.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
+ margin-bottom: 0;
+}
+.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
+ margin-bottom: 0;
+}
+.markdown-body dl {
+ padding: 0;
+}
+.markdown-body dl dt {
+ font-size: 14px;
+ font-weight: bold;
+ font-style: italic;
+ padding: 0;
+ margin: 15px 0 5px;
+}
+.markdown-body dl dt:first-child {
+ padding: 0;
+}
+.markdown-body dl dt>:first-child {
+ margin-top: 0px;
+}
+.markdown-body dl dt>:last-child {
+ margin-bottom: 0px;
+}
+.markdown-body dl dd {
+ margin: 0 0 15px;
+ padding: 0 15px;
+}
+.markdown-body dl dd>:first-child {
+ margin-top: 0px;
+}
+.markdown-body dl dd>:last-child {
+ margin-bottom: 0px;
+}
+.markdown-body blockquote {
+ border-left: 4px solid #DDD;
+ padding: 0 15px;
+ color: #777;
+}
+.markdown-body blockquote>:first-child {
+ margin-top: 0px;
+}
+.markdown-body blockquote>:last-child {
+ margin-bottom: 0px;
+}
+.markdown-body table th {
+ font-weight: bold;
+}
+.markdown-body table th, .markdown-body table td {
+ border: 1px solid #ccc;
+ padding: 6px 13px;
+}
+.markdown-body table tr {
+ border-top: 1px solid #ccc;
+ background-color: #fff;
+}
+.markdown-body table tr:nth-child(2n) {
+ background-color: #f8f8f8;
+}
+.markdown-body img {
+ max-width: 100%;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.markdown-body span.frame {
+ display: block;
+ overflow: hidden;
+}
+.markdown-body span.frame>span {
+ border: 1px solid #ddd;
+ display: block;
+ float: left;
+ overflow: hidden;
+ margin: 13px 0 0;
+ padding: 7px;
+ width: auto;
+}
+.markdown-body span.frame span img {
+ display: block;
+ float: left;
+}
+.markdown-body span.frame span span {
+ clear: both;
+ color: #333;
+ display: block;
+ padding: 5px 0 0;
+}
+.markdown-body span.align-center {
+ display: block;
+ overflow: hidden;
+ clear: both;
+}
+.markdown-body span.align-center>span {
+ display: block;
+ overflow: hidden;
+ margin: 13px auto 0;
+ text-align: center;
+}
+.markdown-body span.align-center span img {
+ margin: 0 auto;
+ text-align: center;
+}
+.markdown-body span.align-right {
+ display: block;
+ overflow: hidden;
+ clear: both;
+}
+.markdown-body span.align-right>span {
+ display: block;
+ overflow: hidden;
+ margin: 13px 0 0;
+ text-align: right;
+}
+.markdown-body span.align-right span img {
+ margin: 0;
+ text-align: right;
+}
+.markdown-body span.float-left {
+ display: block;
+ margin-right: 13px;
+ overflow: hidden;
+ float: left;
+}
+.markdown-body span.float-left span {
+ margin: 13px 0 0;
+}
+.markdown-body span.float-right {
+ display: block;
+ margin-left: 13px;
+ overflow: hidden;
+ float: right;
+}
+.markdown-body span.float-right>span {
+ display: block;
+ overflow: hidden;
+ margin: 13px auto 0;
+ text-align: right;
+}
+.markdown-body code, .markdown-body tt {
+ margin: 0 2px;
+ padding: 0px 5px;
+ border: 1px solid #eaeaea;
+ background-color: #f8f8f8;
+ border-radius: 3px;
+}
+.markdown-body code {
+ white-space: nowrap;
+}
+.markdown-body pre>code {
+ margin: 0;
+ padding: 0;
+ white-space: pre;
+ border: none;
+ background: transparent;
+}
+.markdown-body .highlight pre, .markdown-body pre {
+ background-color: #f8f8f8;
+ border: 1px solid #ccc;
+ font-size: 13px;
+ line-height: 19px;
+ overflow: auto;
+ padding: 6px 10px;
+ border-radius: 3px;
+}
+.markdown-body pre code, .markdown-body pre tt {
+ margin: 0;
+ padding: 0;
+ background-color: transparent;
+ border: none;
+}
+@media only all and (prefers-color-scheme: dark) {
+.markdown-body a.absent { color: #f33; }
+.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link { color: #eee; }
+div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink { color: #eee; }
+.markdown-body h1 { color: #eee; }
+.markdown-body h2 { border-bottom-color: #333; color: #eee; }
+.markdown-body h6 { color: #888; }
+.markdown-body hr { border-color: #333; }
+.markdown-body blockquote { border-left-color: #222; color: #888; }
+.markdown-body table th, .markdown-body table td { border-color: #333; }
+.markdown-body table tr { border-top-color: #333; background-color: #111; }
+.markdown-body table tr:nth-child(2n) { background-color: #070707; }
+.markdown-body span.frame span span { color: #ccc; }
+.markdown-body code, .markdown-body tt { border-color: #151515; background-color: #070707; }
+.markdown-body .highlight pre, .markdown-body pre { background-color: #070707; border-color: #333; }
+''')
+sys.stdout.write(HtmlFormatter(style=dark_style).get_style_defs('.highlight'))
+sys.stdout.write('''
+}
+ at media (prefers-color-scheme: light) {
+''')
+sys.stdout.write(HtmlFormatter(style=light_style).get_style_defs('.highlight'))
+sys.stdout.write('''
+}
+</style>
+''')
+sys.stdout.write("<div class='markdown-body'>")
+sys.stdout.flush()
+# Note: you may want to run this through bleach for sanitization
+markdown.markdownFromFile(
+ output_format="html5",
+ extensions=[
+ "markdown.extensions.fenced_code",
+ "markdown.extensions.codehilite",
+ "markdown.extensions.tables",
+ "markdown.extensions.sane_lists",
+ TocExtension(anchorlink=True)],
+ extension_configs={
+ "markdown.extensions.codehilite":{"css_class":"highlight"}})
+sys.stdout.write("</div>")
diff --git a/filters/html-converters/rst2html b/filters/html-converters/rst2html
new file mode 100755
index 0000000..02d90f8
--- /dev/null
+++ b/filters/html-converters/rst2html
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec rst2html.py --template <(echo -e "%(stylesheet)s\n%(body_pre_docinfo)s\n%(docinfo)s\n%(body)s")
diff --git a/filters/html-converters/txt2html b/filters/html-converters/txt2html
new file mode 100755
index 0000000..495eece
--- /dev/null
+++ b/filters/html-converters/txt2html
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "<pre>"
+sed "s|&|\\&amp;|g;s|'|\\&apos;|g;s|\"|\\&quot;|g;s|<|\\&lt;|g;s|>|\\&gt;|g"
+echo "</pre>"
diff --git a/filters/owner-example.lua b/filters/owner-example.lua
new file mode 100644
index 0000000..50fc25a
--- /dev/null
+++ b/filters/owner-example.lua
@@ -0,0 +1,17 @@
+-- This script is an example of an owner-filter. It replaces the
+-- usual query link with one to a fictional homepage. This script may
+-- be used with the owner-filter or repo.owner-filter settings in
+-- cgitrc with the `lua:` prefix.
+
+function filter_open()
+ buffer = ""
+end
+
+function filter_close()
+ html(string.format("<a href=\"%s\">%s</a>", "http://wiki.example.com/about/" .. buffer, buffer))
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
diff --git a/filters/simple-authentication.lua b/filters/simple-authentication.lua
new file mode 100644
index 0000000..23d3457
--- /dev/null
+++ b/filters/simple-authentication.lua
@@ -0,0 +1,314 @@
+-- This script may be used with the auth-filter. Be sure to configure it as you wish.
+--
+-- Requirements:
+-- luaossl
+-- <http://25thandclement.com/~william/projects/luaossl.html>
+-- luaposix
+-- <https://github.com/luaposix/luaposix>
+--
+local sysstat = require("posix.sys.stat")
+local unistd = require("posix.unistd")
+local rand = require("openssl.rand")
+local hmac = require("openssl.hmac")
+
+--
+--
+-- Configure these variables for your settings.
+--
+--
+
+-- A list of password protected repositories along with the users who can access them.
+local protected_repos = {
+ glouglou = { laurent = true, jason = true },
+ qt = { jason = true, bob = true }
+}
+
+-- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`.
+local users = {
+ jason = "$6$rounds=300000$YYJct3n/o.ruYK$HhpSeuCuW1fJkpvMZOZzVizeLsBKcGA/aF2UPuV5v60JyH2MVSG6P511UMTj2F3H75.IT2HIlnvXzNb60FcZH1",
+ laurent = "$6$rounds=300000$dP0KNHwYb3JKigT$pN/LG7rWxQ4HniFtx5wKyJXBJUKP7R01zTNZ0qSK/aivw8ywGAOdfYiIQFqFhZFtVGvr11/7an.nesvm8iJUi.",
+ bob = "$6$rounds=300000$jCLCCt6LUpTz$PI1vvd1yaVYcCzqH8QAJFcJ60b6W/6sjcOsU7mAkNo7IE8FRGW1vkjF8I/T5jt/auv5ODLb1L4S2s.CAyZyUC"
+}
+
+-- Set this to a path this script can write to for storing a persistent
+-- cookie secret, which should be guarded.
+local secret_filename = "/var/cache/cgit/auth-secret"
+
+--
+--
+-- Authentication functions follow below. Swap these out if you want different authentication semantics.
+--
+--
+
+-- Sets HTTP cookie headers based on post and sets up redirection.
+function authenticate_post()
+ local hash = users[post["username"]]
+ local redirect = validate_value("redirect", post["redirect"])
+
+ if redirect == nil then
+ not_found()
+ return 0
+ end
+
+ redirect_to(redirect)
+
+ if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
+ set_cookie("cgitauth", "")
+ else
+ -- One week expiration time
+ local username = secure_value("username", post["username"], os.time() + 604800)
+ set_cookie("cgitauth", username)
+ end
+
+ html("\n")
+ return 0
+end
+
+
+-- Returns 1 if the cookie is valid and 0 if it is not.
+function authenticate_cookie()
+ accepted_users = protected_repos[cgit["repo"]]
+ if accepted_users == nil then
+ -- We return as valid if the repo is not protected.
+ return 1
+ end
+
+ local username = validate_value("username", get_cookie(http["cookie"], "cgitauth"))
+ if username == nil or not accepted_users[username:lower()] then
+ return 0
+ else
+ return 1
+ end
+end
+
+-- Prints the html for the login form.
+function body()
+ html("<h2>Authentication Required</h2>")
+ html("<form method='post' action='")
+ html_attr(cgit["login"])
+ html("'>")
+ html("<input type='hidden' name='redirect' value='")
+ html_attr(secure_value("redirect", cgit["url"], 0))
+ html("' />")
+ html("<table>")
+ html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
+ html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
+ html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
+ html("</table></form>")
+
+ return 0
+end
+
+
+
+--
+--
+-- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
+--
+--
+
+local actions = {}
+actions["authenticate-post"] = authenticate_post
+actions["authenticate-cookie"] = authenticate_cookie
+actions["body"] = body
+
+function filter_open(...)
+ action = actions[select(1, ...)]
+
+ http = {}
+ http["cookie"] = select(2, ...)
+ http["method"] = select(3, ...)
+ http["query"] = select(4, ...)
+ http["referer"] = select(5, ...)
+ http["path"] = select(6, ...)
+ http["host"] = select(7, ...)
+ http["https"] = select(8, ...)
+
+ cgit = {}
+ cgit["repo"] = select(9, ...)
+ cgit["page"] = select(10, ...)
+ cgit["url"] = select(11, ...)
+ cgit["login"] = select(12, ...)
+
+end
+
+function filter_close()
+ return action()
+end
+
+function filter_write(str)
+ post = parse_qs(str)
+end
+
+
+--
+--
+-- Utility functions based on keplerproject/wsapi.
+--
+--
+
+function url_decode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "+", " ")
+ str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
+ str = string.gsub(str, "\r\n", "\n")
+ return str
+end
+
+function url_encode(str)
+ if not str then
+ return ""
+ end
+ str = string.gsub(str, "\n", "\r\n")
+ str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
+ str = string.gsub(str, " ", "+")
+ return str
+end
+
+function parse_qs(qs)
+ local tab = {}
+ for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
+ tab[url_decode(key)] = url_decode(val)
+ end
+ return tab
+end
+
+function get_cookie(cookies, name)
+ cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
+ return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
+end
+
+function tohex(b)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+--
+--
+-- Cookie construction and validation helpers.
+--
+--
+
+local secret = nil
+
+-- Loads a secret from a file, creates a secret, or returns one from memory.
+function get_secret()
+ if secret ~= nil then
+ return secret
+ end
+ local secret_file = io.open(secret_filename, "r")
+ if secret_file == nil then
+ local old_umask = sysstat.umask(63)
+ local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
+ local temporary_file = io.open(temporary_filename, "w")
+ if temporary_file == nil then
+ os.exit(177)
+ end
+ temporary_file:write(tohex(rand.bytes(32)))
+ temporary_file:close()
+ unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
+ unistd.unlink(temporary_filename)
+ sysstat.umask(old_umask)
+ secret_file = io.open(secret_filename, "r")
+ end
+ if secret_file == nil then
+ os.exit(177)
+ end
+ secret = secret_file:read()
+ secret_file:close()
+ if secret:len() ~= 64 then
+ os.exit(177)
+ end
+ return secret
+end
+
+-- Returns value of cookie if cookie is valid. Otherwise returns nil.
+function validate_value(expected_field, cookie)
+ local i = 0
+ local value = ""
+ local field = ""
+ local expiration = 0
+ local salt = ""
+ local chmac = ""
+
+ if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
+ return nil
+ end
+
+ for component in string.gmatch(cookie, "[^|]+") do
+ if i == 0 then
+ field = component
+ elseif i == 1 then
+ value = component
+ elseif i == 2 then
+ expiration = tonumber(component)
+ if expiration == nil then
+ expiration = -1
+ end
+ elseif i == 3 then
+ salt = component
+ elseif i == 4 then
+ chmac = component
+ else
+ break
+ end
+ i = i + 1
+ end
+
+ if chmac == nil or chmac:len() == 0 then
+ return nil
+ end
+
+ -- Lua hashes strings, so these comparisons are time invariant.
+ if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
+ return nil
+ end
+
+ if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
+ return nil
+ end
+
+ if url_decode(field) ~= expected_field then
+ return nil
+ end
+
+ return url_decode(value)
+end
+
+function secure_value(field, value, expiration)
+ if value == nil or value:len() <= 0 then
+ return ""
+ end
+
+ local authstr = ""
+ local salt = tohex(rand.bytes(16))
+ value = url_encode(value)
+ field = url_encode(field)
+ authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
+ authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
+ return authstr
+end
+
+function set_cookie(cookie, value)
+ html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
+ if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
+ html("; secure")
+ end
+ html("\n")
+end
+
+function redirect_to(url)
+ html("Status: 302 Redirect\n")
+ html("Cache-Control: no-cache, no-store\n")
+ html("Location: " .. url .. "\n")
+end
+
+function not_found()
+ html("Status: 404 Not Found\n")
+ html("Cache-Control: no-cache, no-store\n\n")
+end
diff --git a/filters/syntax-highlighting.py b/filters/syntax-highlighting.py
new file mode 100755
index 0000000..099e67b
--- /dev/null
+++ b/filters/syntax-highlighting.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+
+# This script uses Pygments and Python3. You must have both installed
+# for this to work.
+#
+# http://pygments.org/
+# http://python.org/
+#
+# It may be used with the source-filter or repo.source-filter settings
+# in cgitrc.
+#
+# The following environment variables can be used to retrieve the
+# configuration of the repository for which this script is called:
+# CGIT_REPO_URL ( = repo.url setting )
+# CGIT_REPO_NAME ( = repo.name setting )
+# CGIT_REPO_PATH ( = repo.path setting )
+# CGIT_REPO_OWNER ( = repo.owner setting )
+# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
+# CGIT_REPO_SECTION ( = section setting )
+# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
+
+
+import sys
+import io
+from pygments import highlight
+from pygments.util import ClassNotFound
+from pygments.lexers import TextLexer
+from pygments.lexers import guess_lexer
+from pygments.lexers import guess_lexer_for_filename
+from pygments.formatters import HtmlFormatter
+
+# The dark style is automatically selected if the browser is in dark mode
+light_style='pastie'
+dark_style='monokai'
+
+sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
+sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
+data = sys.stdin.read()
+filename = sys.argv[1]
+light_formatter = HtmlFormatter(style=light_style, nobackground=True)
+dark_formatter = HtmlFormatter(style=dark_style, nobackground=True)
+
+try:
+ lexer = guess_lexer_for_filename(filename, data)
+except ClassNotFound:
+ # check if there is any shebang
+ if data[0:2] == '#!':
+ lexer = guess_lexer(data)
+ else:
+ lexer = TextLexer()
+except TypeError:
+ lexer = TextLexer()
+
+# highlight! :-)
+# printout pygments' css definitions as well
+sys.stdout.write('<style>')
+sys.stdout.write('\n at media only all and (prefers-color-scheme: dark) {\n')
+sys.stdout.write(dark_formatter.get_style_defs('.highlight'))
+sys.stdout.write('\n}\n at media (prefers-color-scheme: light) {\n')
+sys.stdout.write(light_formatter.get_style_defs('.highlight'))
+sys.stdout.write('\n}\n')
+sys.stdout.write('</style>')
+sys.stdout.write(highlight(data, lexer, light_formatter, outfile=None))
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
new file mode 100755
index 0000000..0429b0f
--- /dev/null
+++ b/filters/syntax-highlighting.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+# This script can be used to implement syntax highlighting in the cgit
+# tree-view by referring to this file with the source-filter or repo.source-
+# filter options in cgitrc.
+#
+# This script requires a shell supporting the ${var##pattern} syntax.
+# It is supported by at least dash and bash, however busybox environments
+# might have to use an external call to sed instead.
+#
+# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
+# highlighting, so you'll probably want something like the following included
+# in your css file:
+#
+# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/
+#
+# table.blob .num { color:#2928ff; }
+# table.blob .esc { color:#ff00ff; }
+# table.blob .str { color:#ff0000; }
+# table.blob .dstr { color:#818100; }
+# table.blob .slc { color:#838183; font-style:italic; }
+# table.blob .com { color:#838183; font-style:italic; }
+# table.blob .dir { color:#008200; }
+# table.blob .sym { color:#000000; }
+# table.blob .kwa { color:#000000; font-weight:bold; }
+# table.blob .kwb { color:#830000; }
+# table.blob .kwc { color:#000000; font-weight:bold; }
+# table.blob .kwd { color:#010181; }
+# @media only all and (prefers-color-scheme: dark) { /* cgit addition for dark mode */
+# table.blob .num { color:#ff814f; }
+# table.blob .esc { color:#f60; }
+# table.blob .str { color:#cc0; }
+# table.blob .dstr { color:#ffff7e; }
+# table.blob .slc { color:#7f7c7f; }
+# table.blob .com { color:#7e7c7e; }
+# table.blob .dir { color:#7dff7d; }
+# table.blob .sym { color:#eeeeee; }
+# table.blob .kwa { color:#bb0; }
+# table.blob .kwb { color:#0a0; }
+# table.blob .kwc { color:#eeeeee; }
+# table.blob .kwd { color:#7e7efe; }
+# }
+#
+#
+# Style definition file generated by highlight 2.6.14, http://www.andre-simon.de/
+#
+# body.hl { background-color:#ffffff; }
+# pre.hl { color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';}
+# .hl.num { color:#2928ff; }
+# .hl.esc { color:#ff00ff; }
+# .hl.str { color:#ff0000; }
+# .hl.dstr { color:#818100; }
+# .hl.slc { color:#838183; font-style:italic; }
+# .hl.com { color:#838183; font-style:italic; }
+# .hl.dir { color:#008200; }
+# .hl.sym { color:#000000; }
+# .hl.line { color:#555555; }
+# .hl.mark { background-color:#ffffbb;}
+# .hl.kwa { color:#000000; font-weight:bold; }
+# .hl.kwb { color:#830000; }
+# .hl.kwc { color:#000000; font-weight:bold; }
+# .hl.kwd { color:#010181; }
+# @media only all and (prefers-color-scheme: dark) { /* cgit addition for dark mode */
+# pre.hl { color:#eeeeee; background-color:#111; }
+# .hl.num { color:#ff814f; }
+# .hl.esc { color:#f60; }
+# .hl.str { color:#cc0; }
+# .hl.dstr { color:#ffff7e; }
+# .hl.slc { color:#7f7c7f; }
+# .hl.com { color:#7e7c7e; }
+# .hl.dir { color:#7dff7d; }
+# .hl.sym { color:#eeeeee; }
+# .hl.line { color:#aaaaaa; }
+# .hl.mark { background-color:#444400; }
+# .hl.kwa { color:#bb0; }
+# .hl.kwb { color:#a0a; }
+# .hl.kwc { color:#eeeeee; }
+# .hl.kwd { color:#7e7efe; }
+# }
+#
+#
+# Style definition file generated by highlight 3.8 / 3.13 / 3.41, http://www.andre-simon.de/
+#
+# table.blob .num { color:#b07e00; }
+# table.blob .esc { color:#ff00ff; }
+# table.blob .str { color:#bf0303; }
+# table.blob .pps { color:#818100; }
+# table.blob .slc { color:#838183; font-style:italic; }
+# table.blob .com { color:#838183; font-style:italic; }
+# table.blob .ppc { color:#008200; }
+# table.blob .opt { color:#000000; }
+# table.blob .ipl { color:#0057ae; }
+# table.blob .lin { color:#555555; }
+# table.blob .kwa { color:#000000; font-weight:bold; }
+# table.blob .kwb { color:#0057ae; }
+# table.blob .kwc { color:#000000; font-weight:bold; }
+# table.blob .kwd { color:#010181; }
+# @media only all and (prefers-color-scheme: dark) { /* cgit addition for dark mode */
+# table.blob .num { color:#ff814f; }
+# table.blob .esc { color:#f60; }
+# table.blob .str { color:#cc0; }
+# table.blob .pps { color:#ffff7f; }
+# table.blob .slc { color:#7f7c7f; }
+# table.blob .com { color:#7e7c7e; }
+# table.blob .ppc { color:#8dd; }
+# table.blob .opt { color:#eeeeee; }
+# table.blob .ipl { color:#51a8ff; }
+# table.blob .lin { color:#aaaaaa; }
+# table.blob .kwa { color:#bb0; }
+# table.blob .kwb { color:#0a0; }
+# table.blob .kwc { color:#eeeeee; }
+# table.blob .kwd { color:#7e7efe; }
+# }
+#
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL ( = repo.url setting )
+# CGIT_REPO_NAME ( = repo.name setting )
+# CGIT_REPO_PATH ( = repo.path setting )
+# CGIT_REPO_OWNER ( = repo.owner setting )
+# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
+# CGIT_REPO_SECTION ( = section setting )
+# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
+#
+
+# store filename and extension in local vars
+BASENAME="$1"
+EXTENSION="${BASENAME##*.}"
+
+[ "${BASENAME}" = "${EXTENSION}" ] && EXTENSION=txt
+[ -z "${EXTENSION}" ] && EXTENSION=txt
+
+# map Makefile and Makefile.* to .mk
+[ "${BASENAME%%.*}" = "Makefile" ] && EXTENSION=mk
+
+# highlight versions 2 and 3 have different commandline options. Specifically,
+# the -X option that is used for version 2 is replaced by the -O xhtml option
+# for version 3.
+#
+# Version 2 can be found (for example) on EPEL 5, while version 3 can be
+# found (for example) on EPEL 6.
+#
+# This is for version 2
+exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null
+
+# This is for version 3
+#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..1b33266
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Disallow: /*/snapshot/*
+Disallow: /*/blame/*
+Allow: /