1
0
Fork 0

Move to hugo

This commit is contained in:
Daniel Kempkens 2021-08-28 21:50:49 +02:00
parent 7e9da6e61a
commit 3b4f2f6312
55 changed files with 277 additions and 1569 deletions

6
.gitignore vendored
View file

@ -1,7 +1,7 @@
public/
resources/
.DS_Store
_site
.jekyll-assets-cache
_drafts
*~
*.swp
*.swo

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "themes/terminal"]
path = themes/terminal
url = https://github.com/panr/hugo-theme-terminal.git

View file

@ -1,9 +0,0 @@
---
layout: default
title: "404: Page not found"
---
<div class="page">
<h1 class="page-title">404: Page not found</h1>
<p class="lead">Sorry, we've misplaced that URL or it's pointing to something that doesn't exist. <a href="{{ site.baseurl }}">Head back home</a> to try finding it again.</p>
</div>

View file

@ -1,8 +0,0 @@
source 'https://rubygems.org'
gem 'jekyll', '~> 2.5.3'
group :extensions do
gem 'jekyll-assets', '~> 1.0.0'
gem 'yui-compressor', '~> 0.12.0'
end

View file

@ -1,96 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.3.8)
blankslate (2.1.2.4)
classifier-reborn (2.0.4)
fast-stemmer (~> 1.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.9.1.1)
colorator (0.1)
execjs (2.6.0)
fast-stemmer (1.0.2)
fastimage (1.7.0)
addressable (~> 2.3, >= 2.3.5)
ffi (1.9.10)
hike (1.2.3)
jekyll (2.5.3)
classifier-reborn (~> 2.0)
colorator (~> 0.1)
jekyll-coffeescript (~> 1.0)
jekyll-gist (~> 1.0)
jekyll-paginate (~> 1.0)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.3)
liquid (~> 2.6.1)
mercenary (~> 0.3.3)
pygments.rb (~> 0.6.0)
redcarpet (~> 3.1)
safe_yaml (~> 1.0)
toml (~> 0.1.0)
jekyll-assets (1.0.0)
fastimage (~> 1.6)
jekyll (>= 2)
mini_magick (~> 4.1)
sass (~> 3.2)
sprockets (~> 2.10)
sprockets-helpers
sprockets-sass
jekyll-coffeescript (1.0.1)
coffee-script (~> 2.2)
jekyll-gist (1.3.5)
jekyll-paginate (1.1.0)
jekyll-sass-converter (1.3.0)
sass (~> 3.2)
jekyll-watch (1.3.0)
listen (~> 3.0)
kramdown (1.9.0)
liquid (2.6.3)
listen (3.0.4)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
mercenary (0.3.5)
mini_magick (4.3.6)
multi_json (1.11.2)
parslet (1.5.0)
blankslate (~> 2.0)
posix-spawn (0.3.11)
pygments.rb (0.6.3)
posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.2.0)
rack (1.6.4)
rb-fsevent (0.9.6)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
redcarpet (3.3.3)
safe_yaml (1.0.4)
sass (3.4.19)
sprockets (2.12.4)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-helpers (1.2.1)
sprockets (>= 2.2)
sprockets-sass (1.3.1)
sprockets (~> 2.0)
tilt (~> 1.1)
tilt (1.4.1)
toml (0.1.2)
parslet (~> 1.5.0)
yajl-ruby (1.2.3)
yui-compressor (0.12.0)
PLATFORMS
ruby
DEPENDENCIES
jekyll (~> 2.5.3)
jekyll-assets (~> 1.0.0)
yui-compressor (~> 0.12.0)
BUNDLED WITH
1.16.1

View file

@ -1,30 +0,0 @@
all: compile compress upload superfeedr
compile: clean
@echo "=== Generating static files"
@bundle exec jekyll build --lsi
@echo "Done."
compress: compile
@echo "=== Compressing generated files"
@find ./_site -type f | xargs zopfli --gzip --i30
@find ./_site -type f ! -name "*.gz" | xargs -I {} brotli --quality=11 --output={}.br {}
@find ./_site -type f -name "*.br" | xargs chmod 644
@echo "Done."
upload:
@echo "=== Syncing files"
@rsync --rsync-path="sudo rsync" -avz --no-o --no-g -e ssh --chmod=og=r -p --delete _site/ kempkens.io:/iocage/jails/webserver/root/var/www/blog
@echo "Done."
@echo "=== Changing permissions"
@ssh kempkens.io sudo /usr/home/daniel/bin/chmodweb /iocage/jails/webserver/root/var/www/blog
@echo "Done."
superfeedr:
@echo "=== Notifying Superfeedr"
@curl -X POST https://kempkens.superfeedr.com -d "hub.mode=publish" -d "hub.url=https://blog.kempkens.io/feed.xml"
@curl -X POST https://kempkens.superfeedr.com -d "hub.mode=publish" -d "hub.url=https://blog.kempkens.io/feed-with-links.xml"
@echo "Done."
clean:
@rm -rf ./_site

View file

@ -1,5 +0,0 @@
/*
*= require poole
*= require syntax
*= require hyde
*/

View file

@ -1,250 +0,0 @@
/*
* __ __
* /\ \ /\ \
* \ \ \___ __ __ \_\ \ __
* \ \ _ `\/\ \/\ \ /'_` \ /'__`\
* \ \ \ \ \ \ \_\ \/\ \_\ \/\ __/
* \ \_\ \_\/`____ \ \___,_\ \____\
* \/_/\/_/`/___/> \/__,_ /\/____/
* /\___/
* \/__/
*
* Designed, built, and released under MIT license by @mdo. Learn more at
* https://github.com/poole/hyde.
*/
/*
* Contents
*
* Global resets
* Sidebar
* Container
* Reverse layout
* Themes
*/
/*
* Global resets
*
* Update the foundational and global aspects of the page.
*/
html {
font-family: "PT Sans", Helvetica, Arial, sans-serif;
}
@media (min-width: 48em) {
html {
font-size: 16px;
}
}
@media (min-width: 58em) {
html {
font-size: 20px;
}
}
/*
* Sidebar
*
* Flexible banner for housing site name, intro, and "footer" content. Starts
* out above content in mobile and later moves to the side with wider viewports.
*/
.sidebar {
text-align: center;
padding: 2rem 1rem;
color: rgba(255,255,255,.5);
background-color: #202020;
}
@media (min-width: 48em) {
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 18rem;
text-align: left;
}
}
/* Sidebar links */
.sidebar a {
color: #fff;
}
/* About section */
.sidebar-about h1 {
color: #fff;
margin-top: 0;
font-family: "Abril Fatface", serif;
font-size: 3.25rem;
}
/* Sidebar nav */
.sidebar-nav {
margin-bottom: 1rem;
}
.sidebar-nav-item {
display: block;
line-height: 1.75;
}
a.sidebar-nav-item:hover,
a.sidebar-nav-item:focus {
text-decoration: underline;
}
.sidebar-nav-item.active {
font-weight: bold;
}
/* Sticky sidebar
*
* Add the `sidebar-sticky` class to the sidebar's container to affix it the
* contents to the bottom of the sidebar in tablets and up.
*/
@media (min-width: 48em) {
.sidebar-sticky {
position: absolute;
right: 1rem;
bottom: 1rem;
left: 1rem;
}
}
/* Container
*
* Align the contents of the site above the proper threshold with some margin-fu
* with a 25%-wide `.sidebar`.
*/
.content {
padding-top: 4rem;
padding-bottom: 4rem;
}
@media (min-width: 48em) {
.content {
max-width: 38rem;
margin-left: 20rem;
margin-right: 2rem;
}
}
@media (min-width: 64em) {
.content {
margin-left: 22rem;
margin-right: 4rem;
}
}
/*
* Reverse layout
*
* Flip the orientation of the page by placing the `.sidebar` on the right.
*/
@media (min-width: 48em) {
.layout-reverse .sidebar {
left: auto;
right: 0;
}
.layout-reverse .content {
margin-left: 2rem;
margin-right: 20rem;
}
}
@media (min-width: 64em) {
.layout-reverse .content {
margin-left: 4rem;
margin-right: 22rem;
}
}
/*
* Themes
*
* As of v1.1, Hyde includes optional themes to color the sidebar and links
* within blog posts. To use, add the class of your choosing to the `body`.
*/
/* Base16 (http://chriskempson.github.io/base16/#default) */
/* Red */
.theme-base-08 .sidebar {
background-color: #ac4142;
}
.theme-base-08 .content a,
.theme-base-08 .related-posts li a:hover {
color: #ac4142;
}
/* Orange */
.theme-base-09 .sidebar {
background-color: #d28445;
}
.theme-base-09 .content a,
.theme-base-09 .related-posts li a:hover {
color: #d28445;
}
/* Yellow */
.theme-base-0a .sidebar {
background-color: #f4bf75;
}
.theme-base-0a .content a,
.theme-base-0a .related-posts li a:hover {
color: #f4bf75;
}
/* Green */
.theme-base-0b .sidebar {
background-color: #90a959;
}
.theme-base-0b .content a,
.theme-base-0b .related-posts li a:hover {
color: #90a959;
}
/* Cyan */
.theme-base-0c .sidebar {
background-color: #75b5aa;
}
.theme-base-0c .content a,
.theme-base-0c .related-posts li a:hover {
color: #75b5aa;
}
/* Blue */
.theme-base-0d .sidebar {
background-color: #6a9fb5;
}
.theme-base-0d .content a,
.theme-base-0d .related-posts li a:hover {
color: #6a9fb5;
}
/* Magenta */
.theme-base-0e .sidebar {
background-color: #aa759f;
}
.theme-base-0e .content a,
.theme-base-0e .related-posts li a:hover {
color: #aa759f;
}
/* Brown */
.theme-base-0f .sidebar {
background-color: #8f5536;
}
.theme-base-0f .content a,
.theme-base-0f .related-posts li a:hover {
color: #8f5536;
}

View file

@ -1,465 +0,0 @@
/*
* ___
* /\_ \
* _____ ___ ___\//\ \ __
* /\ '__`\ / __`\ / __`\\ \ \ /'__`\
* \ \ \_\ \/\ \_\ \/\ \_\ \\_\ \_/\ __/
* \ \ ,__/\ \____/\ \____//\____\ \____\
* \ \ \/ \/___/ \/___/ \/____/\/____/
* \ \_\
* \/_/
*
* Designed, built, and released under MIT license by @mdo. Learn more at
* https://github.com/poole/poole.
*/
/*
* Contents
*
* Body resets
* Custom type
* Messages
* Container
* Masthead
* Posts and pages
* Pagination
* Reverse layout
* Themes
*/
/*
* Body resets
*
* Update the foundational and global aspects of the page.
*/
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
}
@media (min-width: 38em) {
html {
font-size: 20px;
}
}
body {
color: #515151;
background-color: #fff;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
/* No `:visited` state is required by default (browsers will use `a`) */
a {
color: #268bd2;
text-decoration: none;
}
a strong {
color: inherit;
}
/* `:focus` is linked to `:hover` for basic accessibility */
a:hover,
a:focus {
text-decoration: underline;
}
/* Headings */
h1, h2, h3, h4, h5, h6 {
margin-bottom: .5rem;
font-weight: bold;
line-height: 1.25;
color: #313131;
text-rendering: optimizeLegibility;
}
h1 {
font-size: 2rem;
}
h2 {
margin-top: 1rem;
font-size: 1.5rem;
}
h3 {
margin-top: 1.5rem;
font-size: 1.25rem;
}
h4, h5, h6 {
margin-top: 1rem;
font-size: 1rem;
}
/* Body text */
p {
margin-top: 0;
margin-bottom: 1rem;
}
strong {
color: #303030;
}
/* Lists */
ul, ol, dl {
margin-top: 0;
margin-bottom: 1rem;
}
dt {
font-weight: bold;
}
dd {
margin-bottom: .5rem;
}
/* Misc */
hr {
position: relative;
margin: 1.5rem 0;
border: 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #fff;
}
abbr {
font-size: 85%;
font-weight: bold;
color: #555;
text-transform: uppercase;
}
abbr[title] {
cursor: help;
border-bottom: 1px dotted #e5e5e5;
}
/* Code */
code,
pre {
font-family: Menlo, Monaco, "Courier New", monospace;
}
code {
padding: .25em .5em;
font-size: 85%;
color: #bf616a;
background-color: #f9f9f9;
border-radius: 3px;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
padding: 1rem;
font-size: .8rem;
line-height: 1.4;
white-space: pre;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
background-color: #f9f9f9;
}
pre code {
padding: 0;
font-size: 100%;
color: inherit;
background-color: transparent;
}
/* Pygments via Jekyll */
.highlight {
margin-bottom: 1rem;
border-radius: 4px;
}
.highlight pre {
margin-bottom: 0;
}
/* Gist via GitHub Pages */
.gist .gist-file {
font-family: Menlo, Monaco, "Courier New", monospace !important;
}
.gist .markdown-body {
padding: 15px;
}
.gist pre {
padding: 0;
background-color: transparent;
}
.gist .gist-file .gist-data {
font-size: .8rem !important;
line-height: 1.4;
}
.gist code {
padding: 0;
color: inherit;
background-color: transparent;
border-radius: 0;
}
/* Quotes */
blockquote {
padding: .5rem 1rem;
margin: .8rem 0;
color: #7a7a7a;
border-left: .25rem solid #e5e5e5;
}
blockquote p:last-child {
margin-bottom: 0;
}
@media (min-width: 30em) {
blockquote {
padding-right: 5rem;
padding-left: 1.25rem;
}
}
img {
display: block;
max-width: 100%;
margin: 0 0 1rem;
border-radius: 5px;
}
/* Tables */
table {
margin-bottom: 1rem;
width: 100%;
border: 1px solid #e5e5e5;
border-collapse: collapse;
}
td,
th {
padding: .25rem .5rem;
border: 1px solid #e5e5e5;
}
tbody tr:nth-child(odd) td,
tbody tr:nth-child(odd) th {
background-color: #f9f9f9;
}
/*
* Custom type
*
* Extend paragraphs with `.lead` for larger introductory text.
*/
.lead {
font-size: 1.25rem;
font-weight: 300;
}
/*
* Messages
*
* Show alert messages to users. You may add it to single elements like a `<p>`,
* or to a parent if there are multiple elements to show.
*/
.message {
margin-bottom: 1rem;
padding: 1rem;
color: #717171;
background-color: #f9f9f9;
}
/*
* Container
*
* Center the page content.
*/
.container {
max-width: 38rem;
padding-left: 1rem;
padding-right: 1rem;
margin-left: auto;
margin-right: auto;
}
/*
* Masthead
*
* Super small header above the content for site name and short description.
*/
.masthead {
padding-top: 1rem;
padding-bottom: 1rem;
margin-bottom: 3rem;
}
.masthead-title {
margin-top: 0;
margin-bottom: 0;
color: #505050;
}
.masthead-title a {
color: #505050;
}
.masthead-title small {
font-size: 75%;
font-weight: 400;
color: #c0c0c0;
letter-spacing: 0;
}
/*
* Posts and pages
*
* Each post is wrapped in `.post` and is used on default and post layouts. Each
* page is wrapped in `.page` and is only used on the page layout.
*/
.page,
.post {
margin-bottom: 4em;
}
/* Blog post or page title */
.page-title,
.post-title,
.post-title a {
color: #303030;
}
.page-title,
.post-title {
margin-top: 0;
}
/* Meta data line below post title */
.post-date {
display: block;
margin-top: -.5rem;
margin-bottom: 1rem;
color: #9a9a9a;
}
/* Related posts */
.related {
padding-top: 2rem;
padding-bottom: 2rem;
border-top: 1px solid #eee;
}
.related-posts {
padding-left: 0;
list-style: none;
}
.related-posts h3 {
margin-top: 0;
}
.related-posts li small {
font-size: 75%;
color: #999;
}
.related-posts li a:hover {
color: #268bd2;
text-decoration: none;
}
.related-posts li a:hover small {
color: inherit;
}
/*
* Pagination
*
* Super lightweight (HTML-wise) blog pagination. `span`s are provide for when
* there are no more previous or next posts to show.
*/
.pagination {
overflow: hidden; /* clearfix */
margin-left: -1rem;
margin-right: -1rem;
font-family: "PT Sans", Helvetica, Arial, sans-serif;
color: #ccc;
text-align: center;
}
/* Pagination items can be `span`s or `a`s */
.pagination-item {
display: block;
padding: 1rem;
border: 1px solid #eee;
}
.pagination-item:first-child {
margin-bottom: -1px;
}
/* Only provide a hover state for linked pagination items */
a.pagination-item:hover {
background-color: #f5f5f5;
}
/* Custom */
.post-date a {
color: #9a9a9a;
}
.post-date a:hover {
color: #303030;
text-decoration: none;
}
.post-link > span {
font-weight: bold;
}
.footer {
margin-top: 3rem;
font-size: 75%;
}
.footer-links {
margin: 0;
padding: 0;
list-style-type: none;
text-align: center;
}
.footer-links li {
display: inline;
}
.footer-links li.divider:after {
content: "|";
color: #ccc;
}
.footer-links li a {
color: #515151;
}
@media (min-width: 30em) {
.pagination {
margin: 3rem 0;
}
.pagination-item {
float: left;
width: 50%;
}
.pagination-item:first-child {
margin-bottom: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.pagination-item:last-child {
margin-left: -1px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
}

View file

@ -1,65 +0,0 @@
.highlight .hll { background-color: #ffc; }
.highlight .c { color: #999; } /* Comment */
.highlight .err { color: #a00; background-color: #faa } /* Error */
.highlight .k { color: #069; } /* Keyword */
.highlight .o { color: #555 } /* Operator */
.highlight .cm { color: #09f; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #099 } /* Comment.Preproc */
.highlight .c1 { color: #999; } /* Comment.Single */
.highlight .cs { color: #999; } /* Comment.Special */
.highlight .gd { background-color: #fcc; border: 1px solid #c00 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #f00 } /* Generic.Error */
.highlight .gh { color: #030; } /* Generic.Heading */
.highlight .gi { background-color: #cfc; border: 1px solid #0c0 } /* Generic.Inserted */
.highlight .go { color: #aaa } /* Generic.Output */
.highlight .gp { color: #009; } /* Generic.Prompt */
.highlight .gs { } /* Generic.Strong */
.highlight .gu { color: #030; } /* Generic.Subheading */
.highlight .gt { color: #9c6 } /* Generic.Traceback */
.highlight .kc { color: #069; } /* Keyword.Constant */
.highlight .kd { color: #069; } /* Keyword.Declaration */
.highlight .kn { color: #069; } /* Keyword.Namespace */
.highlight .kp { color: #069 } /* Keyword.Pseudo */
.highlight .kr { color: #069; } /* Keyword.Reserved */
.highlight .kt { color: #078; } /* Keyword.Type */
.highlight .m { color: #f60 } /* Literal.Number */
.highlight .s { color: #d44950 } /* Literal.String */
.highlight .na { color: #4f9fcf } /* Name.Attribute */
.highlight .nb { color: #366 } /* Name.Builtin */
.highlight .nc { color: #0a8; } /* Name.Class */
.highlight .no { color: #360 } /* Name.Constant */
.highlight .nd { color: #99f } /* Name.Decorator */
.highlight .ni { color: #999; } /* Name.Entity */
.highlight .ne { color: #c00; } /* Name.Exception */
.highlight .nf { color: #c0f } /* Name.Function */
.highlight .nl { color: #99f } /* Name.Label */
.highlight .nn { color: #0cf; } /* Name.Namespace */
.highlight .nt { color: #2f6f9f; } /* Name.Tag */
.highlight .nv { color: #033 } /* Name.Variable */
.highlight .ow { color: #000; } /* Operator.Word */
.highlight .w { color: #bbb } /* Text.Whitespace */
.highlight .mf { color: #f60 } /* Literal.Number.Float */
.highlight .mh { color: #f60 } /* Literal.Number.Hex */
.highlight .mi { color: #f60 } /* Literal.Number.Integer */
.highlight .mo { color: #f60 } /* Literal.Number.Oct */
.highlight .sb { color: #c30 } /* Literal.String.Backtick */
.highlight .sc { color: #c30 } /* Literal.String.Char */
.highlight .sd { color: #c30; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #c30 } /* Literal.String.Double */
.highlight .se { color: #c30; } /* Literal.String.Escape */
.highlight .sh { color: #c30 } /* Literal.String.Heredoc */
.highlight .si { color: #a00 } /* Literal.String.Interpol */
.highlight .sx { color: #c30 } /* Literal.String.Other */
.highlight .sr { color: #3aa } /* Literal.String.Regex */
.highlight .s1 { color: #c30 } /* Literal.String.Single */
.highlight .ss { color: #fc3 } /* Literal.String.Symbol */
.highlight .bp { color: #366 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #033 } /* Name.Variable.Class */
.highlight .vg { color: #033 } /* Name.Variable.Global */
.highlight .vi { color: #033 } /* Name.Variable.Instance */
.highlight .il { color: #f60 } /* Literal.Number.Integer.Long */
.css .o,
.css .o + .nt,
.css .nt + .nt { color: #999; }

View file

@ -1,38 +0,0 @@
# Dependencies
markdown: kramdown
highlighter: pygments
# Permalinks
permalink: /:categories/:title/
relative_permalinks: true
# Setup
title: tail call
tagline: Ramblings in software development
description: The ramblings of a 28-year-old software developer from Germany.
url: https://blog.kempkens.io
hub_url: https://kempkens.superfeedr.com
baseurl: /
author:
name: Daniel Kempkens
location: Wassenberg, North Rhine-Westphalia, Germany
url: https://kempkens.io
email: daniel+blog@kempkens.io
twitter: '@nifoc_'
paginate: 5
include: [".htaccess"]
exclude: ["tmp", "Gemfile", "Gemfile.lock", "Makefile"]
# Custom
assets:
gzip: []
css_compressor: yui
disqus_shortname: blog-kempkens-io
footer:
github: nifoc
ipv6_check: kempkens.io

View file

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{{ site.title | append: include.title }}</title>
<link rel="self" href="{{ include.path | prepend: site.url }}" />
<link href="{{ site.url }}/" />
<link rel="hub" href="{{ site.hub_url }}/" />
<updated>{{ site.time | date_to_xmlschema }}</updated>
<id>{{ site.url | append: include.id }}</id>
<author>
<name>{{ site.author.name }}</name>
<email>{{ site.author.email }}</email>
</author>
{% for post in include.posts %}
<entry>
<id>{{ site.url }}{{ post.id }}</id>
<title>{% if post.category == "links" %}Link: {% endif %}{{ post.title }}</title>
<link href="{{ post.url | prepend: site.url }}" />
<published>{{ post.date | date_to_xmlschema }}</published>
{% if post.modified != nil %}
<updated>{{ post.modified | date_to_xmlschema }}</updated>
{% else %}
<updated>{{ post.date | date_to_xmlschema }}</updated>
{% endif %}
{% if post.category == "links" %}
<content type="html">
{% capture linktext %}<p><strong>Link:</strong> <a href="{{ post.link }}">{{ post.link | hostname }}</a></p>{% endcapture %}
{{ linktext | xml_escape }}
{{ post.content | xml_escape }}
</content>
{% else %}
<content type="html">{{ post.content | xml_escape }}</content>
{% endif %}
</entry>
{% endfor %}
</feed>

View file

@ -1,13 +0,0 @@
<div class="footer">
<ul class="footer-links">
<li><a href="{{ "feed.xml" | prepend: site.baseurl }}">Feed</a></li>
<li class="divider"></li>
<li><a href="{{ "feed-with-links.xml" | prepend: site.baseurl }}">Feed (with links)</a></li>
<li class="divider"></li>
<li><a href="{{ site.footer.github | prepend: "https://github.com/" }}">GitHub</a></li>
<li class="divider"></li>
<li><a href="{{ site.footer.ipv6_check | prepend: "http://ip6.nl/#!" }}">IPv6</a></li>
<li class="divider"></li>
<li><a href="https://www.freebsd.org">FreeBSD</a></li>
</ul>
</div>

View file

@ -1,51 +0,0 @@
<head>
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- Enable responsiveness on mobile devices-->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<title>
{% if page.title == "Home" %}
{{ site.title }} &middot; {{ site.tagline }}
{% else %}
{{ page.title }} &middot; {{ site.title }}
{% endif %}
</title>
<!-- CSS -->
{% stylesheet app %}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface">
<!-- Icons -->
<link rel="shortcut icon" href="{{ "favicon.ico" | prepend: site.baseurl }}">
<link rel="icon" type="image/png" href="{{ "favicon.png" | prepend: site.baseurl }}" />
<!-- RSS -->
<link rel="alternate" type="application/atom+xml" title="{{ site.title }} Feed" href="{{ "/feed.xml" | prepend: site.url }}">
<link rel="alternate" type="application/atom+xml" title="{{ site.title }} Feed (with links)" href="{{ "/feed-with-links.xml" | prepend: site.url }}">
<!-- humans.txt -->
<link rel="author" href="{{ "humans.txt" | prepend: site.baseurl }}">
{% if page.category == "posts" %}
<!-- Twitter -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@nifoc_">
<meta name="twitter:title" content="{{ page.title }}">
<meta name="twitter:description" content="{{ page.description }}">
<meta name="twitter:url" content="{{ site.url }}{{ page.url }}">
<!-- Facebook -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="{{ site.title }}">
<meta property="og:title" content="{{ page.title }}">
<meta property="og:description" content="{{ page.description }}">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
<!-- Schema.org -->
<meta itemprop="name" content="{{ page.title }}">
<meta itemprop="description" content="{{ page.description }}">
{% endif %}
</head>

View file

@ -1,32 +0,0 @@
<div class="sidebar">
<div class="container sidebar-sticky">
<div class="sidebar-about">
<h1>
<a href="{{ site.baseurl }}">
{{ site.title }}
</a>
</h1>
<p class="lead">{{ site.description }}</p>
</div>
<nav class="sidebar-nav">
<a class="sidebar-nav-item{% if page.url == site.baseurl %} active{% endif %}" href="{{ site.baseurl }}">Home</a>
{% comment %}
The code below dynamically generates a sidebar nav of pages with
`layout: page` in the front-matter. See readme for usage.
{% endcomment %}
{% assign pages_list = site.pages %}
{% for node in pages_list %}
{% if node.title != null %}
{% if node.layout == "page" %}
<a class="sidebar-nav-item{% if page.url == node.url %} active{% endif %}" href="{{ node.url }}">{{ node.title }}</a>
{% endif %}
{% endif %}
{% endfor %}
</nav>
<p>&copy; {{ site.time | date: '%Y' }}. All rights reserved.</p>
</div>
</div>

View file

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
{% include head.html %}
<body>
{% include sidebar.html %}
<div class="content container">
{{ content }}
{% include foot.html %}
</div>
</body>
</html>

View file

@ -1,12 +0,0 @@
---
layout: default
---
<div class="post">
<h1 class="post-title">&#9875; {{ page.title }}</h1>
<span class="post-date">{{ page.date | date_to_string }}</span>
<p class="post-link"><span>Link:</span> <a href="{{ page.link }}" rel="external">{{ page.link | hostname }}</a></p>
{{ content }}
</div>

View file

@ -1,8 +0,0 @@
---
layout: default
---
<div class="page">
<h1 class="page-title">{{ page.title }}</h1>
{{ content }}
</div>

View file

@ -1,45 +0,0 @@
---
layout: default
---
<div class="post">
<h1 class="post-title">{{ page.title }}</h1>
<span class="post-date">{{ page.date | date_to_string }}</span>
{{ content }}
</div>
<div class="related">
<h2>Related Posts</h2>
<ul class="related-posts">
{% for post in site.related_posts limit:3 %}
<li>
<h3>
<a href="{{ post.url }}">
{% if post.category == "links" %}&#9875;{% endif %}
{{ post.title }}
<small>{{ post.date | date_to_string }}</small>
</a>
</h3>
</li>
{% endfor %}
</ul>
</div>
{% if false %}
<div class="comments">
<h2>Comments</h2>
<div id="disqus_thread"></div>
</div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = '{{ site.disqus_shortname }}';
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
{% endif %}

View file

@ -1,2 +0,0 @@
require 'bundler'
Bundler.require(:extensions)

View file

@ -1,12 +0,0 @@
require 'uri'
module Jekyll
module HostnameFilter
def hostname(input)
parsed_uri = URI.parse(input)
parsed_uri.host
end
end
end
Liquid::Template.register_filter(Jekyll::HostnameFilter)

View file

@ -1,23 +0,0 @@
---
layout: page
title: Post Archive
---
{% for post in site.posts %}
{% capture month %}{{ post.date | date: '%m%Y' }}{% endcapture %}
{% capture nmonth %}{{ post.next.date | date: '%m%Y' }}{% endcapture %}
{% if month != nmonth %}
{% if forloop.index != 1 %}
</ul>
{% endif %}
<h3>{{ post.date | date: '%B %Y' }}</h3>
<ul>
{% endif %}
<li>
{% if post.category == "links" %}&#9875;{% endif %}
<a href="{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}

View file

@ -1,20 +0,0 @@
---
layout: page
title: Tag Archive
---
{% capture site_tags %}{% for tag in site.tags %}{{ tag | first }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign tags_list = site_tags | split:',' | sort %}
{% for tag in tags_list %}
<h2 id="tag-{{ tag }}">{{ tag }}</h2>
<ul>
{% for post in site.tags[tag] %}
{% if post.title != null %}
<li>
<a href="{{ post.url }}">{{ post.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}

38
config.toml Normal file
View file

@ -0,0 +1,38 @@
baseURL = "https://blog.kempkens.io"
languageCode = "en-en"
title = "tail call"
theme = "terminal"
paginate = 5
[params]
contentTypeName = "posts"
themeColor = "orange"
showMenuItems = 2
showLanguageSelector = false
fullWidthTheme = false
centerTheme = true
showLastUpdated = false
[languages]
[languages.en]
languageName = "English"
title = "tail call"
subtitle = "The ramblings of a software developer from Germany"
owner = "Daniel Kempkens"
keywords = ""
copyright = ""
menuMore = "Show more"
readMore = "Read more"
readOtherPosts = "Read other posts"
newerPosts = "Newer posts"
olderPosts = "Older posts"
missingContentMessage = "Page not found..."
missingBackButtonLabel = "Back to home page"
[languages.en.params.logo]
logoText = "tail call"
logoHomeLink = "/"
[outputFormats]
[outputFormats.RSS]
baseName = "feed"

View file

@ -1,18 +1,19 @@
---
layout: post
date: "2014-02-16T15:30:00Z"
description: Description and implementation of a function that joins a list of binaries.
tags:
- erlang
- programming
- english
slug: joining-a-list-of-binaries-in-erlang
title: Joining a List of Binaries in Erlang
description: "Description and implementation of a function that joins a list of binaries."
date: 2014-02-16 15:30:00 CET
category: posts
tags: [erlang, programming, english]
comments: true
---
The binary module in Erlang provides an easy way to split binaries using `split/2,3`, but what if you want to join a list of binaries back together?
There is no built-in function to do this, so I've decided to write my own.
{% highlight erlang %}
{{< highlight erlang >}}
-spec binary_join([binary()], binary()) -> binary().
binary_join([], _Sep) ->
<<>>;
@ -20,13 +21,13 @@ binary_join([Part], _Sep) ->
Part;
binary_join([Head|Tail], Sep) ->
lists:foldl(fun (Value, Acc) -> <<Acc/binary, Sep/binary, Value/binary>> end, Head, Tail).
{% endhighlight %}
{{< / highlight >}}
It works just like you would expect:
{% highlight erlang %}
{{< highlight erlang >}}
binary_join([<<"Hello">>, <<"World">>], <<", ">>) % => <<"Hello, World">>
binary_join([<<"Hello">>], <<"...">>) % => <<"Hello">>
{% endhighlight %}
{{< / highlight >}}
Hope you find this useful!

View file

@ -1,24 +1,26 @@
---
layout: post
date: "2014-02-17T21:45:00Z"
description: Simple nginx configuration that allows you to proxy most of Microsoft Exchange.
tags:
- nginx
- exchange
- ops
- english
slug: exchange-reverse-proxy-using-nginx
title: Exchange Reverse Proxy Using nginx
description: "Simple nginx configuration that allows you to proxy most of Microsoft Exchange."
date: 2014-02-17 21:45:00 CET
category: posts
tags: [nginx, exchange, ops, english]
comments: true
---
As it turns out, setting up [nginx](http://nginx.org) as a reverse proxy for Microsoft Exchange is not as easy as [some](http://blog.friedlandreas.net/2013/07/reverseproxy-fur-eas-exchange-activesync-und-owa-outlookwebapp-mit-nginx/) [posts](http://www.administrator.de/wissen/ngnix-als-reverse-proxy-für-exchange-2010-192711.html) suggest.
The issue that for some calls (Autodiscovery, RPC, …) IIS asks for an `Authorization` header, which nginx can pass through by doing:
{% highlight nginx %}
{{< highlight nginx >}}
proxy_pass_header Authorization;
{% endhighlight %}
{{< / highlight >}}
Only problem is: It doesn't work. Thankfully someone on StackOverflow already had a [solution](http://stackoverflow.com/a/19714696) for this:
{% highlight nginx %}
{{< highlight nginx >}}
proxy_http_version 1.1;
proxy_pass_request_headers on;
proxy_pass_header Date;
@ -31,12 +33,12 @@ more_set_input_headers 'Authorization: $http_authorization';
proxy_set_header Accept-Encoding "";
more_set_headers -s 401 'WWW-Authenticate: Basic realm="your.mail.host"';
proxy_pass https://your.mail.host;
{% endhighlight %}
{{< / highlight >}}
You'll need the [HttpHeadersMore](http://wiki.nginx.org/HttpHeadersMoreModule) module for this to work. On Ubuntu 12.04 (using the [nginx/stable PPA](https://launchpad.net/~nginx/+archive/stable)) all you need to install is:
{% highlight bash %}
{{< highlight bash >}}
apt-get install nginx-extras
{% endhighlight %}
{{< / highlight >}}
And you're good to go!

View file

@ -1,16 +1,18 @@
---
layout: post
date: "2014-02-24T22:00:00Z"
description: Some links regarding Telegram and its security model.
tags:
- telegram
- whatsapp
- app
- english
slug: telegram-and-security
title: Telegram and Security
description: "Some links regarding Telegram and its security model."
date: 2014-02-24 22:00:00 CET
category: posts
tags: [telegram, whatsapp, app, english]
comments: true
---
Ever since [Facebook bought WhatsApp](http://techcrunch.com/2014/02/19/facebook-buying-whatsapp-for-16b-in-cash-and-stock-plus-3b-in-rsus/) more and more of the people I know switch to [Telegram](https://telegram.org). One of the main reasons they choose Telegram is because it's "secure" and Telegram itself seems really keen on being seen as a secure, privacy-focused WhatsApp alternative.
People that are way smarter than I am when it comes to cryptography have a sligtly different opinion on the "secure, privacy-focused" part …
People that are way smarter than I am when it comes to cryptography have a slightly different opinion on the "secure, privacy-focused" part …
- [Telegram, AKA "Stand back, we have Math PhDs!"](http://unhandledexpression.com/2013/12/17/telegram-stand-back-we-know-maths/)
- [A Crypto Challenge For The Telegram Developers](http://thoughtcrime.org/blog/telegram-crypto-challenge/)

View file

@ -1,18 +1,19 @@
---
layout: post
date: "2014-03-23T20:00:00Z"
description: Blow up a polyline to search inside the generated polygon.
tags:
- javascript
- programming
- english
slug: buffered-polyline
title: Buffered Polyline
description: "Blow up a polyline to search inside the generated polygon."
date: 2014-03-23 20:00:00 CET
category: posts
tags: [javascript, programming, english]
comments: true
---
At work, we needed a simple way to buffer a polyline in order to search for stuff along the route from A to B. I'll explain how we used Google's Maps API and [JSTS](https://github.com/bjornharrtell/jsts) in order to achieve this easily.
The first thing we had to do was to "transform" each element in the `overview_path` (which the `DirectionsService` returns) to GeoJSON, because that's what JSTS understands.
{% highlight javascript linenos %}
{{< highlight javascript "linenos=table" >}}
var overviewPath = response.routes[0].overview_path,
overviewPathGeo = [];
for(var i = 0; i < overviewPath.length; i++) {
@ -20,11 +21,11 @@ for(var i = 0; i < overviewPath.length; i++) {
[overviewPath[i].lng(), overviewPath[i].lat()]
);
}
{% endhighlight %}
{{< / highlight >}}
The next step was getting `overviewPathGeo` into JSTS and letting it do the work of buffering the line.
{% highlight javascript linenos %}
{{< highlight javascript "linenos=table" >}}
var distance = 10/111.12, // Roughly 10km
geoInput = {
type: "LineString",
@ -34,7 +35,7 @@ var geoReader = new jsts.io.GeoJSONReader(),
geoWriter = new jsts.io.GeoJSONWriter();
var geometry = geoReader.read(geoInput).buffer(distance);
var polygon = geoWriter.write(geometry);
{% endhighlight %}
{{< / highlight >}}
The `polygon` variable now contains a polygon that neatly fits around the `overviewPath`, with a *distance* (buffer size) of roughly 10km.
@ -42,4 +43,4 @@ Since it is nested, you need to call `polygon.coordinates[0]` in order to get ba
You could then use the the coordinates to draw the generated polygon on the map (along with the route), in order to produce something like this:
{% image buffered-polyline-1.png %}
![Example image of a buffered polyline](/posts/buffered-polyline-1.png)

View file

@ -1,11 +1,12 @@
---
layout: post
date: "2014-03-26T13:55:00Z"
description: Collection of some interesting DEF CON videos.
tags:
- defcon
- videos
- english
slug: some-defcon-videos
title: Some DEF CON Videos
description: "Collection of some interesting DEF CON videos."
date: 2014-03-26 13:55:00 CET
category: posts
tags: [defcon, videos, english]
comments: true
---
Over the weekend I watched some [DEF CON](https://www.defcon.org) videos on [YouTube](https://www.youtube.com/user/DEFCONConference/videos) and thought I should share some of the interesting ones on here. The two talks on Bitsquatting (by Stucke and Schultz) are especially entertaining and scary.

View file

@ -1,12 +1,15 @@
---
layout: post
title: OCSP Stapling with nginx
description: "A general explanation of how to set up OCSP stapling with nginx."
date: 2014-03-30 21:30:00 CEST
date: "2014-03-30T21:30:00Z"
description: A general explanation of how to set up OCSP stapling with nginx.
modified: 2015-08-03 20:55:00 CEST
category: posts
tags: [nginx, ocsp, ssl, ops, english]
comments: true
tags:
- nginx
- ocsp
- ssl
- ops
- english
slug: ocsp-stapling-with-nginx
title: OCSP Stapling with nginx
---
Setting up [OCSP stapling](https://en.wikipedia.org/wiki/OCSP_stapling) with [nginx](http://nginx.org/) is more or less straightforward, but depending on what's in your `ssl_certificate` you might run into some issues with it silently failing. So I've decided to write about how I set up OCSP stapling with certificates from [StartSSL](https://www.startssl.com/) and [CAcert](http://www.cacert.org/).
@ -15,14 +18,14 @@ I have only tested this using nginx version 1.5.12, but the configuration option
To enable OCSP stapling, you simply have to add the following lines to your (already SSL enabled) site:
{% highlight nginx %}
{{< highlight nginx >}}
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s;
resolver_timeout 10s;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/certs/startssl.stapling.crt;
{% endhighlight %}
{{< / highlight >}}
The important thing here is the usage of `ssl_trusted_certificate`. The [nginx documentation](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_stapling_verify) states that:
@ -34,25 +37,25 @@ You have to include the root certificate (and intermediate certificates) for OCS
For CAcert (and unless you have a Class 3 certificate) you only have to include the Class 1 certificate in your stapling file.
{% highlight text %}
{{< highlight text >}}
03/08/2015 Removed. Get the Class 1 certificate here: http://www.cacert.org/index.php?id=3
{% endhighlight %}
{{< / highlight >}}
## StartSSL
StartSSL - using a Class 1 certificate again - has an intermediate certificate in their chain, so you have to include this one in your stapling file, too.
{% highlight text %}
{{< highlight text >}}
03/08/2015: Removed. Get the root certificate and the intermediate certificate here: https://www.startssl.com/certs/
{% endhighlight %}
{{< / highlight >}}
After you've done all that, you can restart nginx or reload the configuration and you should be good to go!
You can verify that everything works using the `openssl` command line tool.
{% highlight bash %}
{{< highlight bash >}}
openssl s_client -servername blog.kempkens.io -connect blog.kempkens.io:443 -tls1 -tlsextdebug -status
{% endhighlight %}
{{< / highlight >}}
Both of those should include a section (with data) named "OCSP Response Data".

View file

@ -1,11 +1,14 @@
---
layout: post
date: "2014-04-06T20:15:00Z"
description: Property-based testing in Erlang, with some basic usage examples.
tags:
- erlang
- quickcheck
- testing
- programming
- english
slug: property-based-testing-with-proper
title: Property-based Testing with PropEr
description: "Property-based testing in Erlang, with some basic usage examples."
date: 2014-04-06 20:15:00 CEST
category: posts
tags: [erlang, quickcheck, testing, programming, english]
comments: true
---
[PropEr](http://proper.softlab.ntua.gr/) is a [QuickCheck](https://en.wikipedia.org/wiki/QuickCheck)-inspired property-based testing tool for Erlang. In contrast to traditional testing methodologies, the tester only has to provide the generic structure of valid inputs and some properties, which specify the relation between input and output. The testing tool will then produce progressively more complex valid inputs and compare the results it gets to what it expected (based on the defined properties).
@ -13,28 +16,28 @@ PropEr is one such testing tool for Erlang. It is [open-source](https://github.c
Assuming you already have your `$ERL_LIBS` set up, you can simply run the following commands to install PropEr and make it available *globally*.
{% highlight bash linenos %}
{{< highlight bash "linenos=table" >}}
cd $ERL_LIBS
git clone git://github.com/manopapad/proper.git
cd proper
make fast
{% endhighlight %}
{{< / highlight >}}
After that, you can use PropEr in *all* your Erlang projects.
I like using [EUnit](http://www.erlang.org/doc/apps/eunit/chapter.html) as the runner for my property based tests. There are [some caveats](https://github.com/manopapad/proper#using-proper-in-conjunction-with-eunit), but all in all it works without any issues.
What I don't like is defining my property-based tests in the same module as my unit tests, because it feels wrong to do so. I generally define one `_test_` function that calls `proper:module/2`.
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
proper_module_test_() ->
{timeout, 180, ?_assertEqual([], proper:module(example_utils_prop, [long_result, {to_file, user}]))}.
{% endhighlight %}
{{< / highlight >}}
This allows me to have all of the unit tests in `example_utils_test` and all of the property-based tests in `example_utils_prop`.
Let's write a simple property-based test for the `binary_join` function I've [introduced in February]({{ site.url }}/posts/joining-a-list-of-binaries-in-erlang/).
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
prop_binary_join() ->
?FORALL({Bs, S}, {list(binary()), binary()}, begin
ExpectedLength = if
@ -44,7 +47,7 @@ prop_binary_join() ->
end,
ExpectedLength =:= byte_size(example_utils:binary_join(Bs, S))
end).
{% endhighlight %}
{{< / highlight >}}
This will test if the size of the output of `example_utils:binary_join/2` is what we would expect:
@ -60,11 +63,11 @@ The `?FORALL` macro takes three aguments:
If you actually run the test, the output will look something like this:
{% highlight text %}
{{< highlight text >}}
Testing example_utils_prop:prop_binary_join/0
....................................................................................................
OK: Passed 100 test(s).
{% endhighlight %}
{{< / highlight >}}
By default, PropEr will generate 100 random valid inputs and compare the output to the (length) properties we defined above.

View file

@ -1,11 +1,12 @@
---
layout: post
date: "2014-04-12T21:25:00Z"
description: Short description of how to install Erlang/OTP 17.0 using kerl on Mac OS X.
tags:
- erlang
- programming
- english
slug: installing-erlang-17-0-using-kerl
title: Installing Erlang 17.0 On Mac OS X
description: "Short description of how to install Erlang/OTP 17.0 using kerl on Mac OS X."
date: 2014-04-12 21:25:00 CEST
category: posts
tags: [erlang, programming, english]
comments: true
---
It's been a few days since [Erlang/OTP 17.0](http://www.erlang.org/news/73) has been released. Installing 64 Bit Erlang (with [Observer](http://www.erlang.org/doc/man/observer.html) support) on Mac OS X has always been a bit tricky, but with 17.0 it has gotten significantly easier.
@ -18,36 +19,36 @@ First you need to install/get some prerequisites:
After you've gotten these three, we start by installing *wxWidgets*. It's required in order to use the Observer GUI.
{% highlight bash %}
{{< highlight bash >}}
brew install wxmac
{% endhighlight %}
{{< / highlight >}}
Compiling *wxWidgets* might take some time. After it's done, you should create a `~/.kerlrc` file. It's basically a configuration file for *kerl*, which will be used for every Erlang version you compile. You can find the list of available options in the [*kerl* readme](https://github.com/spawngrid/kerl#tuning). Mine looks like this:
{% highlight bash linenos %}
{{< highlight bash "linenos=table" >}}
CPPFLAGS="-march=native -mtune=native -O3 -g"
KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --enable-shared-zlib --enable-dynamic-ssl-lib --enable-smp-support --enable-threads --enable-hipe --enable-kernel-poll --enable-darwin-64bit --with-wx"
KERL_DEFAULT_INSTALL_DIR="$KERL_BASE_DIR/installs"
{% endhighlight %}
{{< / highlight >}}
We can now move on to actually installing Erlang/OTP 17.0.
{% highlight bash %}
{{< highlight bash >}}
kerl update releases
kerl build 17.0 17.0
kerl install 17.0 17.0
{% endhighlight %}
{{< / highlight >}}
After the installation is finished, *kerl* will output instructions on how to enable a specific Erlang version globally. In general, you will put something like the following in your `~/.zshrc` or `~/.bash_profile`:
{% highlight bash %}
{{< highlight bash >}}
source $HOME/.kerl/installs/17.0/activate
{% endhighlight %}
{{< / highlight >}}
The following `alias` makes it easier to start the Observer from a terminal. The only command you have to remember is `erlobserver`.
{% highlight bash %}
{{< highlight bash >}}
alias erlobserver='erl -sname observer -run observer -detached'
{% endhighlight %}
{{< / highlight >}}
{% image installing-erlang-17-0-using-kerl-1.png %}
![Image of a running Observer instance](/posts/installing-erlang-17-0-using-kerl-1.png)

View file

@ -1,11 +1,12 @@
---
layout: post
date: "2014-04-18T20:25:00Z"
description: Simple workaround for supporting deprecated types and warnings_as_errors.
tags:
- erlang
- programming
- english
slug: erlang-17-0-supporting-deprecated-types-without-removing-warnings_as_errors
title: 'Erlang 17.0: Supporting Deprecated Types Without Removing warnings_as_errors'
description: "Simple workaround for supporting deprecated types and warnings_as_errors."
date: 2014-04-18 20:25:00 CEST
category: posts
tags: [erlang, programming, english]
comments: true
---
Erlang 17.0 [deprecated some pre-defined types](http://www.erlang.org/download/otp_src_17.0.readme) like `dict()` and `gb_tree()` in favor of `dict:dict()` and `gb_tree:tree()`. The workaround they suggest (`nowarn_deprecated_type`) works in 17.0, but would break once the deprecated types are removed.
@ -25,22 +26,22 @@ A rather nice solution to this issue an option that [rebar](https://github.com/r
This allows us to set a `namespaced_types` option on 17.0+ only.
{% highlight erlang %}
{{< highlight erlang >}}
{erl_opts, [
{platform_define, "^[0-9]+", namespaced_types},
debug_info,
warnings_as_errors
]}.
{% endhighlight %}
{{< / highlight >}}
In your source files you can then check if the option is set and define an (internal) type accordingly.
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
-ifdef(namespaced_types).
-type xxx_dict() :: dict:dict().
-else.
-type xxx_dict() :: dict().
-endif.
{% endhighlight %}
{{< / highlight >}}
I learned about this trick by looking at how [meck](https://github.com/eproxus/meck) handles Erlang 17.0 support.

View file

@ -1,11 +1,14 @@
---
layout: post
date: "2014-05-04T21:55:00Z"
description: Introductory post about the Riemann client I've written.
tags:
- erlang
- programming
- riemann
- katja
- english
slug: katja-riemann-client-written-in-erlang
title: 'Katja: Riemann Client Written In Erlang'
description: "Introductory post about the Riemann client I've written."
date: 2014-05-04 21:55:00 CEST
category: posts
tags: [erlang, programming, riemann, katja, english]
comments: true
---
[Riemann](http://riemann.io) is a network monitoring system written in Clojure, it offers a rather simple [protobuf](https://de.wikipedia.org/wiki/Protocol_Buffers)-based API. I have just tagged [Katja](https://github.com/nifoc/katja) [version 0.1](https://github.com/nifoc/katja/tree/v0.1), my Riemann client written in Erlang.
@ -22,12 +25,12 @@ Katja only supports Erlang/OTP R16B01+. This is mostly because in releases befor
## Sending Events
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
Event = [{service, "katja demo"}, {metric, 9000.1}],
ok = katja:send_event(Event),
Event2 = [{service, <<"katja demo">>}, {metric, 9000.1}, {tags, ["demo"]}],
ok = katja:send_events([Event, Event2]).
{% endhighlight %}
{{< / highlight >}}
Katja allows you to send either a single event or multiple ones. Events are simple property lists with the following (possible) keys: time, state, service, host, description, tags, ttl, attributes, metric.
@ -35,12 +38,12 @@ The entire `katja:event()` type definition can be found on [GitHub](https://gith
## Sending States
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
State = [{service, "katja demo"}, {state, "testing"}],
ok = katja:send_state(State),
State2 = [{service, "katja demo"}, {state, "testing"}, {tags, ["demo"]}],
ok = katja:send_states([State, State2]).
{% endhighlight %}
{{< / highlight >}}
States and events are very similar, so much so that the (possible) keys of a state property list are almost identical to the keys of an event property list: time, state, service, host, description, tags, ttl, once.
@ -48,9 +51,9 @@ Once again, the entire `katja:state()` type definition can be found on [GitHub](
## Querying Events
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
{ok, Events} = katja:query("service = \"katja demo\"").
{% endhighlight %}
{{< / highlight >}}
`katja:query/1` will return a list of events. Events are a property list of type `katja:event()`, so what you send to Riemann is also what you get back when querying. There is one important thing to keep in mind: All `undefined` or `[]` values will be removed from the returned property list(s).
@ -58,11 +61,11 @@ You can find example queries in the [Riemann test suite](https://github.com/aphy
## Sending Entities
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
Event = [{service, "katja demo"}, {metric, 9000.1}],
State = [{service, "katja demo"}, {state, "testing"}],
ok = katja:send_entities([{events, [Event]}, {states, [State]}]).
{% endhighlight %}
{{< / highlight >}}
Katja also allows you to send mutiple events and/or states in a single request via `katja:send_entities/1`.

View file

@ -1,11 +1,14 @@
---
layout: post
date: "2014-12-07T22:59:00Z"
description: Example GitLab CI setup that can run tests for Erlang projects.
tags:
- erlang
- testing
- programming
- gitlabci
- english
slug: gitlab-ci-for-erlang-projects
title: GitLab CI for Erlang Projects
description: "Example GitLab CI setup that can run tests for Erlang projects."
date: 2014-12-07 22:59:00 CET
category: posts
tags: [erlang, testing, programming, gitlabci, english]
comments: true
---
[GitLab CI](https://about.gitlab.com/gitlab-ci/) is GitLab's continuous integration software. It integrates with [GitLab](https://about.gitlab.com) and runs your tests every time a commit is pushed to the remote repository. Getting it to run tests for projects written in Erlang can be a bit of hassle, so in this post I will share and explain my setup.
@ -16,7 +19,7 @@ The CI runner (v5.0.0) is installed on an Ubuntu 14.04 machine and it talks to G
The build script that is used to run Erlang tests looks like this:
{% highlight bash %}
{{< highlight bash >}}
# Setup
source "$HOME/.kerl/installs/17.3/activate"
gitlabdir=$(basename "$(pwd)")
@ -39,7 +42,7 @@ ls -rt $(find ./logs -name "cover.html") | tail -n 1 | xargs cat | grep -F 'Tota
cd ..
mv "$appdir" "$gitlabdir"
cd "$gitlabdir"
{% endhighlight %}
{{< / highlight >}}
**Setup**
@ -59,9 +62,9 @@ My projects use [erlang.mk](https://github.com/ninenines/erlang.mk), which is wh
The test coverage is extracted from the latest `cover.html` file. It will be printed to *stdout* like this:
{% highlight text %}
{{< highlight text >}}
Total97 %742
{% endhighlight %}
{{< / highlight >}}
Since we're only interest in the `97`, we can set the "Test coverage parsing" option to this regular expression: `Total(\d+)\s\%`.

View file

@ -1,28 +1,29 @@
---
layout: post
date: "2014-12-22T19:45:00Z"
description: Choosing a small number of newer ciphers prevents SSH bots from connecting.
tags:
- ssh
- ops
- english
slug: ssh-bots-do-not-know-about-my-ciphers
title: SSH Bots Don't Know about My Ciphers
description: "Choosing a small number of newer ciphers prevents SSH bots from connecting."
date: 2014-12-22 19:45:00 CET
category: posts
tags: [ssh, ops, english]
comments: true
---
Last weekend I decided to update my `sshd_config` to include a very limited set of ciphers, MACs and key exchange algorithms. I did this to tighten the security of my `sshd` and not because I wanted to prevent bots from trying (and failing) to log in to my servers. I'm already using [fail2ban](http://www.fail2ban.org) for that.
However, after I updated my configuration I noticed failed login attempts basically dropped to zero, because all these bots do not support my very restrictive set of ciphers.
{% highlight text %}
{{< highlight text >}}
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
{% endhighlight %}
{{< / highlight >}}
In order to use these settings, you need a recent version of [OpenSSH](http://www.openssh.com). I'm running 6.6 locally and on my servers, the minimum version that supports these settings is 6.4.
After these changes your `auth.log` will probably contain this line rather often:
{% highlight text %}
{{< highlight text >}}
fatal: no matching cipher found: client aes128-ctr,aes192-ctr,aes256-ctr,aes256-cbc,rijndael-cbc@lysator.liu.se,aes192-cbc,aes128-cbc,blowfish-cbc,arcfour128,arcfour,cast128-cbc,3des-cbc server chacha20-poly1305@openssh.com,aes256-gcm@openssh.com [preauth]
{% endhighlight %}
{{< / highlight >}}
Please keep in mind that this will not prevent bots from attacking you (in the future) and that you have to mitigate these attacks by other means. I only wrote this post because I thought it was kind of interesting that SSH bots do not support these settings *right now*.

View file

@ -1,39 +1,40 @@
---
layout: post
date: "2014-12-24T13:25:00Z"
description: Simple workaround for supporting deprecated types and warnings_as_errors with erlang.mk.
tags:
- erlang
- programming
- english
slug: supporting-deprecated-types-with-erlang-mk
title: Supporting Deprecated Types with erlang.mk
description: "Simple workaround for supporting deprecated types and warnings_as_errors with erlang.mk."
date: 2014-12-24 13:25:00 CET
category: posts
tags: [erlang, programming, english]
comments: true
---
In April I wrote about how I use the `platform_define` feature of [rebar](https://github.com/rebar/rebar) to make deprecated types work without removing `warnings_as_errors`. I have switched to [erlang.mk](https://github.com/ninenines/erlang.mk) since then, but needed a similar feature for [one of my libraries](https://github.com/nifoc/noesis).
Since erlang.mk only uses [make](http://en.wikipedia.org/wiki/Make_(software)), adding support for a `platform_define`-like feature is very straightforward.
{% highlight makefile %}
{{< highlight makefile >}}
otp_release = $(shell erl -noshell -eval 'io:format("~s", [erlang:system_info(otp_release)]), init:stop()')
otp_17plus = $(shell echo $(otp_release) | grep -q -E "^[[:digit:]]+$$" ; echo $$?)
{% endhighlight %}
{{< / highlight >}}
The first variable (`otp_release`) will contain the OTP version (e.g. `17`). The second variable (`otp_17plus`) will be either `0` or `1`, depending on wether or not `otp_release` matches a regular expression. The regular expression checks if `otp_release` is just a number (e.g. `17`) or not (e.g. `R16B03-1`).
That's enough to conditionally add options to `erlc`.
{% highlight makefile %}
{{< highlight makefile >}}
ifeq ($(otp_17plus),0)
ERLC_OPTS += -Dnamespaced_types=1
TEST_ERLC_OPTS += -Dnamespaced_types=1
endif
{% endhighlight %}
{{< / highlight >}}
This will define `namespaced_types` only on Erlang 17+, allowing us to use the same `ifdef`-switch from April.
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
-ifdef(namespaced_types).
-type xxx_dict() :: dict:dict().
-else.
-type xxx_dict() :: dict().
-endif.
{% endhighlight %}
{{< / highlight >}}

View file

@ -1,11 +1,14 @@
---
layout: link
date: "2014-12-31T16:33:00Z"
description: 'Link: [media.ccc.de](http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video)'
tags:
- links
url: links/a-gentle-introduction-to-elliptic-curve-cryptography
title: A Gentle Introduction to Elliptic-Curve Cryptography
date: 2014-12-31 16:33:00 CET
category: links
link: http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video
---
Link: [media.ccc.de](http://media.ccc.de/browse/congress/2014/31c3_-_6369_-_en_-_saal_1_-_201412272145_-_ecchacks_-_djb_-_tanja_lange.html#video)
A talk by djb and Tanja Lange.
> This talk will explain how to work with elliptic curves constructively to obtain secure and efficient implementations, and will highlight pitfalls that must be avoided when implementing elliptic-curve crypto (ECC). The talk will also explain what all the buzz in curve choices for TLS is about. This talk does not require any prior exposure to ECC.

View file

@ -1,11 +1,14 @@
---
layout: link
date: "2015-01-05T17:39:00Z"
description: 'Link: [www.marco.org](http://www.marco.org/2015/01/04/apple-lost-functional-high-ground)'
tags:
- links
url: links/apple-has-lost-the-functional-high-ground
title: Apple Has Lost the Functional High Ground
date: 2015-01-05 17:39:00 CET
category: links
link: http://www.marco.org/2015/01/04/apple-lost-functional-high-ground
---
Link: [www.marco.org](http://www.marco.org/2015/01/04/apple-lost-functional-high-ground)
Marco Arment's view on the rapid decline in Apple's software quality.
> I fear that Apples leadership doesnt realize quite how badly and deeply their software flaws have damaged their reputation, because if they realized it, theyd make serious changes that dont appear to be happening. Instead, the opposite appears to be happening: the pace of rapid updates on multiple product lines seems to be expanding and accelerating.

View file

@ -1,11 +1,13 @@
---
layout: post
date: "2015-02-01T20:59:00Z"
description: Explanation of how to use spiped to encrypt HTTP traffic to backend systems.
tags:
- spiped
- nginx
- ops
- english
slug: securing-http-traffic-with-spiped
title: Securing HTTP Traffic with spiped
description: "Explanation of how to use spiped to encrypt HTTP traffic to backend systems."
date: 2015-02-01 20:59:00 CET
category: posts
tags: [spiped, nginx, ops, english]
comments: true
---
[spiped](http://www.tarsnap.com/spiped.html) - the secure pipe daemon - is a utility for creating symmetrically encrypted and authenticated pipes between socket addresses. I recently used it to encrypt HTTP traffic from my main webserver to a Raspberry Pi that I run at home.
@ -16,25 +18,25 @@ I'm using [nginx](http://nginx.org) on the main webserver and the Raspberry Pi,
After installing spiped, the first thing you have to do is to create a (private) key. I ran the following command on my webserver:
{% highlight bash %}
{{< highlight bash >}}
dd if=/dev/urandom bs=32 count=1 of=/usr/local/etc/nginx/certs/spiped.key
{% endhighlight %}
{{< / highlight >}}
After that, the key has to be securely copied to the backend machine. You can - for example - do this using `scp`. I copied it to `/etc/nginx/certs/spiped.key`, but your paths may vary.
That's it. You can now start spiped.
{% highlight bash %}
{{< highlight bash >}}
spiped -D -e -s [127.0.0.1]:9080 -t rpi.kempkens.io:9080 -k /usr/local/etc/nginx/certs/spiped.key
{% endhighlight %}
{{< / highlight >}}
That command tells spiped to listen on `127.0.0.1:9080`, encrypt the incoming data and send it to `rpi.kempkens.io:9080`.
To set up the receiving end of the pipe, you have to run something like this:
{% highlight bash %}
{{< highlight bash >}}
spiped -D -d -s [0.0.0.0]:9080 -t [127.0.0.1]:80 -k /etc/nginx/certs/spiped.key
{% endhighlight %}
{{< / highlight >}}
spiped will listen for incoming connections on port 9080, decrypt the data it receives and forward it to `127.0.0.1:80`.
@ -42,11 +44,11 @@ And that's it.
On the main webserver I have defined an `upstream` that forwards traffic to `127.0.0.1:9080`.
{% highlight nginx %}
{{< highlight nginx >}}
upstream rpi {
server 127.0.0.1:9080;
}
{% endhighlight %}
{{< / highlight >}}
The nginx installation on the Raspberry Pi listens on port 80, so there is nothing special you have to set up there. It will simply receive the decrypted HTTP traffic from spiped. The only thing that you have to keep in mind is that the server name (`Host`-header) has to be the same on the main webserver and the Raspberry Pi.

View file

@ -1,11 +1,12 @@
---
layout: post
date: "2015-03-12T22:03:00Z"
description: Description and implementation of functions that encode and decode polylines.
tags:
- erlang
- programming
- english
slug: encoding-and-decoding-polylines-with-erlang
title: Encoding and Decoding Polylines with Erlang
description: "Description and implementation of functions that encode and decode polylines."
date: 2015-03-12 22:03:00 CET
category: posts
tags: [erlang, programming, english]
comments: true
---
If you have ever worked with the [Google Directions API](https://developers.google.com/maps/documentation/directions/?csw=1) you probably came across [encoded polylines](https://developers.google.com/maps/documentation/utilities/polylinealgorithm). I wanted to decode and encode these using Erlang but was unable to find an existing implementation. So I decided to write my own.
@ -14,14 +15,14 @@ If you have ever worked with the [Google Directions API](https://developers.goog
* A path, i.e. a decoded polyline, is represented as a list of point tuples
* An encoded polyline is represented as a simple binary string
{% highlight erlang %}
{{< highlight erlang >}}
Point = {Lng, Lat},
Path = [{Lng, Lat}, {Lng2, Lat2}].
{% endhighlight %}
{{< / highlight >}}
The `encode/1` function takes a path and returns an encoded polyline.
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
encode(Path) -> encode_acc(Path, 0, 0, <<>>).
% Private
@ -41,11 +42,11 @@ encode_part(Num, Result) when Num < 32 -> <<Result/binary, (Num + 63)>>;
encode_part(Num, Result) ->
Value = (32 bor (Num band 31)) + 63,
encode_part(Num bsr 5, <<Result/binary, Value>>).
{% endhighlight %}
{{< / highlight >}}
The `decode/1` function takes an encoded polyline and returns a path.
{% highlight erlang linenos %}
{{< highlight erlang "linenos=table" >}}
decode(Line) -> decode_acc(Line, 0, 0, []).
% Private
@ -68,7 +69,7 @@ decode_part(<<C:8, Rest/binary>>, _OldB, Shift, Result) ->
B = C - 63,
Result2 = Result bor ((B band 31) bsl Shift),
decode_part(Rest, B, Shift + 5, Result2).
{% endhighlight %}
{{< / highlight >}}
I have written these functions for [noesis](https://github.com/nifoc/noesis), which is a library that contains useful utility functions. Right now the implementation is only available in the [development branch](https://github.com/nifoc/noesis/blob/development/src/noesis_polyline.erl). It is tested using [EUnit](https://github.com/nifoc/noesis/blob/f3e9ae21d53e09bea9ca48fe4a56ddd006952e0a/test/noesis_polyline_test.erl) and [QuickCheck](https://github.com/nifoc/noesis/blob/f3e9ae21d53e09bea9ca48fe4a56ddd006952e0a/test/noesis_polyline_triq.erl).

View file

@ -1,18 +1,19 @@
---
layout: post
date: "2015-07-12T18:46:00Z"
description: Introducing my port of the pcsensor utility to FreeBSD.
tags:
- pcsensor
- freebsd
- english
slug: porting-pcsensor-to-freebsd
title: Porting pcsensor to FreeBSD
description: "Introducing my port of the pcsensor utility to FreeBSD."
date: 2015-07-12 18:46:00 CEST
category: posts
tags: [pcsensor, freebsd, english]
comments: true
---
Last week, it got rather hot where I live and so I got interested in measuring the temperature of the room where I keep my NAS and various other devices. I started looking for cheap USB thermometers and quickly found [this one](http://www.amazon.de/gp/product/B009RETJIO). It has some decent reviews and costs only around 16€, which seemed perfect to simply play around with.
The device only comes bundled with Windows software, but there is an open source utility called `pcsensor` which allows you to use it via the command line on Linux. I don't have any Linux devices in the room that I wanted to measure. Since the source code was pretty straightforward and only minimal changes were required to port the utility to FreeBSD, I did just that! You can find the ported source code on [GitHub](https://github.com/nifoc/pcsensor-freebsd).
{% highlight text %}
{{< highlight text >}}
$ pcsensor -h
pcsensor version 1.0.3
Aviable options:
@ -26,6 +27,6 @@ pcsensor version 1.0.3
-d output with Bus and Device number
-D display device list
-D[n] specific device number
{% endhighlight %}
{{< / highlight >}}
If you want to read the temperature from the device, you might have to prefix the call to `pcsensor` with `sudo`.

View file

@ -1,20 +1,21 @@
---
layout: post
date: "2016-01-31T17:46:00Z"
description: Description of how to easily anonymize IPs using HAProxy.
tags:
- haproxy
- ops
- english
slug: anonymizing-ips-using-haproxy
title: Anonymizing IPs Using HAProxy
description: "Description of how to easily anonymize IPs using HAProxy."
date: 2016-01-31 17:46:00 CET
category: posts
tags: [haproxy, ops, english]
comments: true
---
At work, I had to come up with an easy way to anonymize the last octet of a logged IP address in order to comply with German data protection laws. If you're using [HAProxy](http://www.haproxy.org) (1.5+), you can do this in one line.
If you want to forward the source IP address to a backend server, you would usually use `option forwardfor`. Sadly you can't set or change the forwarded IP using that option, so instead you have to set the `X-Forwarded-For` header manually.
{% highlight text %}
{{< highlight text >}}
http-request set-header X-Forwarded-For %[src,ipmask(24)]
{% endhighlight %}
{{< / highlight >}}
This will set the last octet of the source IP address to zero.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,4 +0,0 @@
---
layout: null
---
{% include feed.xml id="/feed-with-links.xml" path="/feed-with-links.xml" title=" (with links)" posts=site.posts %}

View file

@ -1,4 +0,0 @@
---
layout: null
---
{% include feed.xml path="/feed.xml" posts=site.categories.posts %}

View file

@ -1,12 +0,0 @@
---
layout: null
---
/* TEAM */
Author: {{ site.author.name }}
Contact: {{ site.author.email }}
Twitter: {{ site.author.twitter }}
From: {{ site.author.location }}
/* SITE */
Last update: {{ site.time | date: '%Y/%m/%d' }}
Software: Jekyll, MacVim

View file

@ -1,30 +0,0 @@
---
layout: page
permalink: /impressum/
title: Impressum
---
Daniel Kempkens
Kugelsberger Weg 6
41849 Wassenberg
Email: {{ site.author.email }}
## Domains
* [blog.kempkens.io](https://blog.kempkens.io)
* [daniel.xxx](https://daniel.xxx)
## Security
### HTTP
This blog makes use of [HSTS](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security), [strong ciphers](https://www.ssllabs.com/ssltest/analyze.html?d={{ site.url | hostname }}&hideResults=on) and various other security-related technologies.
_German law demands the following statements, too._
## Datenschutzerklärung
Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder eMail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis.
Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.

View file

@ -1,47 +0,0 @@
---
layout: default
title: Home
---
<div class="posts">
{% for post in paginator.posts %}
<div class="post">
<h1 class="post-title">
{% if post.category == "links" %}
&#9875; <a href="{{ post.link }}" rel="external">{{ post.title }}</a>
{% else %}
<a href="{{ post.url }}">{{ post.title }}</a>
{% endif %}
</h1>
<span class="post-date">
{{ post.date | date_to_string }}
&middot;
<a href="{{ post.url }}" title="Permalink">&#8734;</a>
</span>
{% if post.category == "links" %}
{{ post.content }}
{% else %}
{{ post.excerpt }}
{% endif %}
</div>
{% endfor %}
</div>
<div class="pagination">
{% if paginator.next_page %}
<a class="pagination-item older" href="{{ site.baseurl }}page{{paginator.next_page}}">Older</a>
{% else %}
<span class="pagination-item older">Older</span>
{% endif %}
{% if paginator.previous_page %}
{% if paginator.page == 2 %}
<a class="pagination-item newer" href="{{ site.baseurl }}">Newer</a>
{% else %}
<a class="pagination-item newer" href="{{ site.baseurl }}page{{paginator.previous_page}}">Newer</a>
{% endif %}
{% else %}
<span class="pagination-item newer">Newer</span>
{% endif %}
</div>

View file

@ -1,6 +0,0 @@
---
layout: null
---
User-agent: *
Allow: /
Sitemap: {{ "/sitemap.xml" | prepend: site.url }}

View file

@ -1,29 +0,0 @@
---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>{{ site.url }}</loc>
<lastmod>{{ site.time | date_to_xmlschema }}</lastmod>
</url>
{% for page in site.pages %}
{% if page.title != null and page.url != '/404.html' %}
<url>
<loc>{{ site.url }}{{ page.url }}</loc>
</url>
{% endif %}
{% endfor %}
{% for post in site.posts %}
<url>
<loc>{{ site.url }}{{ post.url }}</loc>
{% if post.modified != nil %}
<lastmod>{{ post.modified | date_to_xmlschema }}</lastmod>
{% else %}
<lastmod>{{ post.date | date_to_xmlschema }}</lastmod>
{% endif %}
</url>
{% endfor %}
</urlset>

View file

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 228 KiB

View file

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

1
themes/terminal Submodule

@ -0,0 +1 @@
Subproject commit cd36a63fd00bbc2f155ac950a945f81131ae3531