From 04a30160b71744f3af8fb6a77dda332706173784 Mon Sep 17 00:00:00 2001 From: andryyy Date: Mon, 1 May 2017 21:38:12 +0200 Subject: [PATCH] Add gitignore for site data --- .gitignore | 1 + site/404.html | 283 +++++ site/assets/images/favicon.ico | Bin 0 -> 1150 bytes .../images/icons/bitbucket-670608a71a.svg | 1 + .../assets/images/icons/github-1da075986e.svg | 1 + .../assets/images/icons/gitlab-5ad3f9f9e5.svg | 1 + .../javascripts/application-30ac6a1727.js | 3 + .../javascripts/modernizr-56ade86843.js | 1 + .../application-892b79c5c5.palette.css | 1 + .../stylesheets/application-e17eeafcbc.css | 1 + site/first_steps/index.html | 874 ++++++++++++++ site/images/logo.svg | 179 +++ site/index.html | 428 +++++++ site/install/index.html | 512 ++++++++ site/mkdocs/js/lunr.min.js | 7 + site/mkdocs/js/mustache.min.js | 1 + site/mkdocs/js/require.js | 36 + .../js/search-results-template.mustache | 4 + site/mkdocs/js/search.js | 88 ++ site/mkdocs/js/text.js | 390 ++++++ site/mkdocs/search_index.json | 309 +++++ site/sitemap.xml | 36 + site/u_and_e/index.html | 1058 +++++++++++++++++ 23 files changed, 4215 insertions(+) create mode 100644 .gitignore create mode 100644 site/404.html create mode 100644 site/assets/images/favicon.ico create mode 100644 site/assets/images/icons/bitbucket-670608a71a.svg create mode 100644 site/assets/images/icons/github-1da075986e.svg create mode 100644 site/assets/images/icons/gitlab-5ad3f9f9e5.svg create mode 100644 site/assets/javascripts/application-30ac6a1727.js create mode 100644 site/assets/javascripts/modernizr-56ade86843.js create mode 100644 site/assets/stylesheets/application-892b79c5c5.palette.css create mode 100644 site/assets/stylesheets/application-e17eeafcbc.css create mode 100644 site/first_steps/index.html create mode 100644 site/images/logo.svg create mode 100644 site/index.html create mode 100644 site/install/index.html create mode 100644 site/mkdocs/js/lunr.min.js create mode 100644 site/mkdocs/js/mustache.min.js create mode 100644 site/mkdocs/js/require.js create mode 100644 site/mkdocs/js/search-results-template.mustache create mode 100644 site/mkdocs/js/search.js create mode 100644 site/mkdocs/js/text.js create mode 100644 site/mkdocs/search_index.json create mode 100644 site/sitemap.xml create mode 100644 site/u_and_e/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1320f90e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +site diff --git a/site/404.html b/site/404.html new file mode 100644 index 000000000..4d208f333 --- /dev/null +++ b/site/404.html @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + mailcow: dockerized + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + +
+
+ + edit + + +

404 - Not found

+ + +
+
+
+
+ + + + +
+ + + + + + + + + + \ No newline at end of file diff --git a/site/assets/images/favicon.ico b/site/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e85006a3ce1c6fd81faa6d5a13095519c4a6fc96 GIT binary patch literal 1150 zcmd6lF-yZh9L1kl>(HSEK`2y^4yB6->f+$wD)=oNY!UheIt03Q=;qj=;8*Bap_4*& za8yAl;wmmx5Yyi^7dXN-WYdJ-{qNqpcez|5t#Fr0qTSYcPTG`I2PBk8r$~4kg^0zN zCJe(rhix3do!L$bZ+IuZ{i08x=JR3=e+M4pv0KsKA??{u_*EFfo|`p&t`Vf=jn{)F z1fKk9hWsmYwqWAP^JO*5u*R;*L&dX3H$%S7oB$f0{ISh{QVXuncnzN67WQH2`lip7 zhX+VI$6x$1+$8gMjh4+1l0N#8_0Fh=N#EwpKk{SeE!)SHFB@xQFX3y+8sF#_@!bDW eIdI-IC`$c%>bk?KbPeN9RHtL<1^)v~#xMt8oB^@` literal 0 HcmV?d00001 diff --git a/site/assets/images/icons/bitbucket-670608a71a.svg b/site/assets/images/icons/bitbucket-670608a71a.svg new file mode 100644 index 000000000..7d95cb22d --- /dev/null +++ b/site/assets/images/icons/bitbucket-670608a71a.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/images/icons/github-1da075986e.svg b/site/assets/images/icons/github-1da075986e.svg new file mode 100644 index 000000000..3cacb2e0f --- /dev/null +++ b/site/assets/images/icons/github-1da075986e.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/images/icons/gitlab-5ad3f9f9e5.svg b/site/assets/images/icons/gitlab-5ad3f9f9e5.svg new file mode 100644 index 000000000..b036a9b52 --- /dev/null +++ b/site/assets/images/icons/gitlab-5ad3f9f9e5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/assets/javascripts/application-30ac6a1727.js b/site/assets/javascripts/application-30ac6a1727.js new file mode 100644 index 000000000..ef6ceffd3 --- /dev/null +++ b/site/assets/javascripts/application-30ac6a1727.js @@ -0,0 +1,3 @@ +window.app=function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=94)}([function(t,e,n){"use strict";var r=n(30)("wks"),o=n(22),i=n(1).Symbol,a="function"==typeof i,s=t.exports=function(t){return r[t]||(r[t]=a&&i[t]||(a?i:o)("Symbol."+t))};s.store=r},function(t,e,n){"use strict";var r=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=r)},function(t,e,n){"use strict";var r=n(11);t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},function(t,e,n){"use strict";var r=n(12),o=n(29);t.exports=n(5)?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e,n){"use strict";var r=t.exports={version:"2.4.0"};"number"==typeof __e&&(__e=r)},function(t,e,n){"use strict";t.exports=!n(25)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,e,n){"use strict";var r={}.hasOwnProperty;t.exports=function(t,e){return r.call(t,e)}},function(t,e,n){"use strict";t.exports={}},function(t,e,n){"use strict";var r=n(1),o=n(3),i=n(6),a=n(22)("src"),s="toString",c=Function[s],u=(""+c).split(s);n(4).inspectSource=function(t){return c.call(t)},(t.exports=function(t,e,n,s){var c="function"==typeof n;c&&(i(n,"name")||o(n,"name",e)),t[e]!==n&&(c&&(i(n,a)||o(n,a,t[e]?""+t[e]:u.join(String(e)))),t===r?t[e]=n:s?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n)))})(Function.prototype,s,function(){return"function"==typeof this&&this[a]||c.call(this)})},function(t,e,n){"use strict";var r={}.toString;t.exports=function(t){return r.call(t).slice(8,-1)}},function(t,e,n){"use strict";var r=n(14);t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};t.exports=function(t){return"object"===("undefined"==typeof t?"undefined":r(t))?null!==t:"function"==typeof t}},function(t,e,n){"use strict";var r=n(2),o=n(43),i=n(63),a=Object.defineProperty;e.f=n(5)?Object.defineProperty:function(t,e,n){if(r(t),e=i(e,!0),r(n),o)try{return a(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default={createElement:function(t,e){var n=document.createElement(t);e&&Array.prototype.forEach.call(Object.keys(e),function(t){n.setAttribute(t,e[t])});for(var r=function t(e){Array.prototype.forEach.call(e,function(e){"string"==typeof e||"number"==typeof e?n.textContent+=e:Array.isArray(e)?t(e):n.appendChild(e)})},o=arguments.length,i=Array(o>2?o-2:0),a=2;a0?o:r)(t)}},function(t,e,n){"use strict";var r=n(45),o=n(16);t.exports=function(t){return r(o(t))}},function(t,e,n){"use strict";var r=0,o=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++r+o).toString(36))}},function(t,e,n){"use strict";t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){"use strict";var r=n(1),o=n(4),i=n(3),a=n(8),s=n(10),c="prototype",u=function t(e,n,u){var l,f,h,d,p=e&t.F,v=e&t.G,m=e&t.S,y=e&t.P,g=e&t.B,w=v?r:m?r[n]||(r[n]={}):(r[n]||{})[c],b=v?o:o[n]||(o[n]={}),_=b[c]||(b[c]={});v&&(u=n);for(l in u)f=!p&&w&&void 0!==w[l],h=(f?w:u)[l],d=g&&f?s(h,r):y&&"function"==typeof h?s(Function.call,h):h,w&&a(w,l,h,e&t.U),b[l]!=h&&i(b,l,d),y&&_[l]!=h&&(_[l]=h)};r.core=o,u.F=1,u.G=2,u.S=4,u.P=8,u.B=16,u.W=32,u.U=64,u.R=128,t.exports=u},function(t,e,n){"use strict";t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,e,n){"use strict";t.exports=n(1).document&&document.documentElement},function(t,e,n){"use strict";var r=n(28),o=n(24),i=n(8),a=n(3),s=n(6),c=n(7),u=n(48),l=n(18),f=n(54),h=n(0)("iterator"),d=!([].keys&&"next"in[].keys()),p="@@iterator",v="keys",m="values",y=function(){return this};t.exports=function(t,e,n,g,w,b,_){u(n,e,g);var E,S,x,k=function(t){if(!d&&t in A)return A[t];switch(t){case v:return function(){return new n(this,t)};case m:return function(){return new n(this,t)}}return function(){return new n(this,t)}},T=e+" Iterator",O=w==m,C=!1,A=t.prototype,M=A[h]||A[p]||w&&A[w],L=M||k(w),P=w?O?k("entries"):L:void 0,j="Array"==e?A.entries||M:M;if(j&&(x=f(j.call(new t)),x!==Object.prototype&&(l(x,T,!0),r||s(x,h)||a(x,h,y))),O&&M&&M.name!==m&&(C=!0,L=function(){return M.call(this)}),r&&!_||!d&&!C&&A[h]||a(A,h,L),c[e]=L,c[T]=y,w)if(E={values:O?L:k(m),keys:b?L:k(v),entries:P},_)for(S in E)S in A||i(A,S,E[S]);else o(o.P+o.F*(d||C),e,E);return E}},function(t,e,n){"use strict";t.exports=!1},function(t,e,n){"use strict";t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){"use strict";var r=n(1),o="__core-js_shared__",i=r[o]||(r[o]={});t.exports=function(t){return i[t]||(i[t]={})}},function(t,e,n){"use strict";var r,o,i,a=n(10),s=n(44),c=n(26),u=n(17),l=n(1),f=l.process,h=l.setImmediate,d=l.clearImmediate,p=l.MessageChannel,v=0,m={},y="onreadystatechange",g=function(){var t=+this;if(m.hasOwnProperty(t)){var e=m[t];delete m[t],e()}},w=function(t){g.call(t.data)};h&&d||(h=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return m[++v]=function(){s("function"==typeof t?t:Function(t),e)},r(v),v},d=function(t){delete m[t]},"process"==n(9)(f)?r=function(t){f.nextTick(a(g,t,1))}:p?(o=new p,i=o.port2,o.port1.onmessage=w,r=a(i.postMessage,i,1)):l.addEventListener&&"function"==typeof postMessage&&!l.importScripts?(r=function(t){l.postMessage(t+"","*")},l.addEventListener("message",w,!1)):r=y in u("script")?function(t){c.appendChild(u("script"))[y]=function(){c.removeChild(this),g.call(t)}}:function(t){setTimeout(a(g,t,1),0)}),t.exports={set:h,clear:d}},function(t,e,n){"use strict";var r=n(20),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(){function t(t,e){for(var n=0;n-1?e:t}function d(t,e){e=e||{};var n=e.body;if(t instanceof d){if(t.bodyUsed)throw new TypeError("Already read");this.url=t.url,this.credentials=t.credentials,e.headers||(this.headers=new o(t.headers)),this.method=t.method,this.mode=t.mode,n||null==t._bodyInit||(n=t._bodyInit,t.bodyUsed=!0)}else this.url=String(t);if(this.credentials=e.credentials||this.credentials||"omit",!e.headers&&this.headers||(this.headers=new o(e.headers)),this.method=h(e.method||this.method||"GET"),this.mode=e.mode||this.mode||null,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&n)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(n)}function p(t){var e=new FormData;return t.trim().split("&").forEach(function(t){if(t){var n=t.split("="),r=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");e.append(decodeURIComponent(r),decodeURIComponent(o))}}),e}function v(t){var e=new o;return t.split(/\r?\n/).forEach(function(t){var n=t.split(":"),r=n.shift().trim();if(r){var o=n.join(":").trim();e.append(r,o)}}),e}function m(t,e){e||(e={}),this.type="default",this.status="status"in e?e.status:200,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in e?e.statusText:"OK",this.headers=new o(e.headers),this.url=e.url||"",this._initBody(t)}if(!t.fetch){var y={searchParams:"URLSearchParams"in t,iterable:"Symbol"in t&&"iterator"in Symbol,blob:"FileReader"in t&&"Blob"in t&&function(){try{return new Blob,!0}catch(t){return!1}}(),formData:"FormData"in t,arrayBuffer:"ArrayBuffer"in t};if(y.arrayBuffer)var g=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],w=function(t){return t&&DataView.prototype.isPrototypeOf(t)},b=ArrayBuffer.isView||function(t){return t&&g.indexOf(Object.prototype.toString.call(t))>-1};o.prototype.append=function(t,r){t=e(t),r=n(r);var o=this.map[t];this.map[t]=o?o+","+r:r},o.prototype.delete=function(t){delete this.map[e(t)]},o.prototype.get=function(t){return t=e(t),this.has(t)?this.map[t]:null},o.prototype.has=function(t){return this.map.hasOwnProperty(e(t))},o.prototype.set=function(t,r){this.map[e(t)]=n(r)},o.prototype.forEach=function(t,e){for(var n in this.map)this.map.hasOwnProperty(n)&&t.call(e,this.map[n],n,this)},o.prototype.keys=function(){var t=[];return this.forEach(function(e,n){t.push(n)}),r(t)},o.prototype.values=function(){var t=[];return this.forEach(function(e){t.push(e)}),r(t)},o.prototype.entries=function(){var t=[];return this.forEach(function(e,n){t.push([n,e])}),r(t)},y.iterable&&(o.prototype[Symbol.iterator]=o.prototype.entries);var _=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];d.prototype.clone=function(){return new d(this,{body:this._bodyInit})},f.call(d.prototype),f.call(m.prototype),m.prototype.clone=function(){return new m(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new o(this.headers),url:this.url})},m.error=function(){var t=new m(null,{status:0,statusText:""});return t.type="error",t};var E=[301,302,303,307,308];m.redirect=function(t,e){if(E.indexOf(e)===-1)throw new RangeError("Invalid status code");return new m(null,{status:e,headers:{location:t}})},t.Headers=o,t.Request=d,t.Response=m,t.fetch=function(t,e){return new Promise(function(n,r){var o=new d(t,e),i=new XMLHttpRequest;i.onload=function(){var t={status:i.status,statusText:i.statusText,headers:v(i.getAllResponseHeaders()||"")};t.url="responseURL"in i?i.responseURL:t.headers.get("X-Request-URL");var e="response"in i?i.response:i.responseText;n(new m(e,t))},i.onerror=function(){r(new TypeError("Network request failed"))},i.ontimeout=function(){r(new TypeError("Network request failed"))},i.open(o.method,o.url,!0),"include"===o.credentials&&(i.withCredentials=!0),"responseType"in i&&y.blob&&(i.responseType="blob"),o.headers.forEach(function(t,e){i.setRequestHeader(e,t)}),i.send("undefined"==typeof o._bodyInit?null:o._bodyInit)})},t.fetch.polyfill=!0}}("undefined"!=typeof self?self:void 0)},function(t,e,n){"use strict";(function(t){function r(e){new a.a.Event.Listener(document,"DOMContentLoaded",function(){if(!(document.body instanceof HTMLElement))throw new ReferenceError;i.a.attach(document.body),Modernizr.addTest("ios",function(){return!!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)});var e=document.querySelectorAll("table:not([class])");if(Array.prototype.forEach.call(e,function(e){var n=t.createElement("div",{class:"md-typeset__scrollwrap"},t.createElement("div",{class:"md-typeset__table"}));e.nextSibling?e.parentNode.insertBefore(n,e.nextSibling):e.parentNode.appendChild(n),n.children[0].appendChild(e)}),Modernizr.ios){var n=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(n,function(t){t.addEventListener("touchstart",function(){var e=t.scrollTop;0===e?t.scrollTop=1:e+t.offsetHeight===t.scrollHeight&&(t.scrollTop=e-1)})})}}).listen(),new a.a.Event.MatchMedia("(min-width: 1220px)",new a.a.Event.Listener(window,["scroll","resize","orientationchange"],new a.a.Header.Shadow("[data-md-component=container]"))),document.querySelector("[data-md-component=tabs]")&&new a.a.Event.Listener(window,["scroll","resize","orientationchange"],new a.a.Tabs.Toggle("[data-md-component=tabs]")).listen(),new a.a.Event.MatchMedia("(min-width: 1220px)",new a.a.Event.Listener(window,["scroll","resize","orientationchange"],new a.a.Sidebar.Position("[data-md-component=navigation]"))),new a.a.Event.MatchMedia("(min-width: 960px) and (max-width: 1219px)",new a.a.Event.Listener(window,["scroll","resize","orientationchange"],new a.a.Sidebar.Position("[data-md-component=toc]"))),new a.a.Event.MatchMedia("(min-width: 1220px)",new a.a.Event.Listener(window,["scroll","resize","orientationchange"],new a.a.Sidebar.Position("[data-md-component=toc]"))),new a.a.Event.MatchMedia("(min-width: 960px)",new a.a.Event.Listener(window,"scroll",new a.a.Nav.Blur("[data-md-component=toc] [href]")));var n=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(n,function(t){new a.a.Event.MatchMedia("(min-width: 1220px)",new a.a.Event.Listener(t.previousElementSibling,"click",new a.a.Nav.Collapse(t)))}),new a.a.Event.MatchMedia("(max-width: 1219px)",new a.a.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new a.a.Nav.Scrolling("[data-md-component=navigation] nav"))),new a.a.Event.MatchMedia("(max-width: 959px)",new a.a.Event.Listener("[data-md-toggle=search]","change",new a.a.Search.Lock("[data-md-toggle=search]"))),new a.a.Event.Listener("[data-md-component=query]",["focus","keyup"],new a.a.Search.Result("[data-md-component=result]",function(){return fetch(e.url.base+"/mkdocs/search_index.json",{credentials:"same-origin"}).then(function(t){return t.json()}).then(function(t){return t.docs.map(function(t){return t.location=e.url.base+t.location,t})})})).listen(),new a.a.Event.MatchMedia("(max-width: 1219px)",new a.a.Event.Listener("[data-md-component=overlay]","touchstart",function(t){return t.preventDefault()})),new a.a.Event.MatchMedia("(max-width: 959px)",new a.a.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var t=document.querySelector("[data-md-toggle=drawer]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.checked&&(t.checked=!1,t.dispatchEvent(new CustomEvent("change")))})),new a.a.Event.Listener("[data-md-toggle=search]","change",function(t){setTimeout(function(t){if(!(t instanceof HTMLInputElement))throw new ReferenceError;if(t.checked){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()}},400,t.target)}).listen(),new a.a.Event.MatchMedia("(min-width: 960px)",new a.a.Event.Listener("[data-md-component=query]","focus",function(){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.checked||(t.checked=!0,t.dispatchEvent(new CustomEvent("change")))})),new a.a.Event.MatchMedia("(min-width: 960px)",new a.a.Event.Listener(document.body,"click",function(){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.checked&&(t.checked=!1,t.dispatchEvent(new CustomEvent("change")))})),new a.a.Event.Listener(window,"keyup",function(t){var e=t.keyCode||t.which;if(27===e){var n=document.querySelector("[data-md-toggle=search]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(n.checked){n.checked=!1,n.dispatchEvent(new CustomEvent("change"));var r=document.querySelector("[data-md-component=query]");if(!(r instanceof HTMLInputElement))throw new ReferenceError;r.focus()}}}).listen(),new a.a.Event.MatchMedia("(min-width: 960px)",new a.a.Event.Listener("[data-md-toggle=search]","click",function(t){return t.stopPropagation()})),new a.a.Event.MatchMedia("(min-width: 960px)",new a.a.Event.Listener("[data-md-component=search]","click",function(t){return t.stopPropagation()})),function(){var t=document.querySelector("[data-md-source]");if(!t)return Promise.resolve([]);if(!(t instanceof HTMLAnchorElement))throw new ReferenceError;switch(t.dataset.mdSource){case"github":return new a.a.Source.Adapter.GitHub(t).fetch();default:return Promise.resolve([])}}().then(function(t){var e=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(e,function(e){new a.a.Source.Repository(e).initialize(t)})})}Object.defineProperty(e,"__esModule",{value:!0});var o=n(70),i=n.n(o),a=n(73);n.d(e,"initialize",function(){return r})}).call(e,n(13))},function(t,e,n){"use strict";var r=n(0)("unscopables"),o=Array.prototype;void 0==o[r]&&n(3)(o,r,{}),t.exports=function(t){o[r][t]=!0}},function(t,e,n){"use strict";t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},function(t,e,n){"use strict";var r=n(21),o=n(32),i=n(61);t.exports=function(t){return function(e,n,a){var s,c=r(e),u=o(c.length),l=i(a,u);if(t&&n!=n){for(;u>l;)if(s=c[l++],s!=s)return!0}else for(;u>l;l++)if((t||l in c)&&c[l]===n)return t||l||0;return!t&&-1}}},function(t,e,n){"use strict";var r=n(10),o=n(47),i=n(46),a=n(2),s=n(32),c=n(64),u={},l={},f=t.exports=function(t,e,n,f,h){var d,p,v,m,y=h?function(){return t}:c(t),g=r(n,f,e?2:1),w=0;if("function"!=typeof y)throw TypeError(t+" is not iterable!");if(i(y)){for(d=s(t.length);d>w;w++)if(m=e?g(a(p=t[w])[0],p[1]):g(t[w]),m===u||m===l)return m}else for(v=y.call(t);!(p=v.next()).done;)if(m=o(v,g,p.value,e),m===u||m===l)return m};f.BREAK=u,f.RETURN=l},function(t,e,n){"use strict";t.exports=!n(5)&&!n(25)(function(){return 7!=Object.defineProperty(n(17)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){"use strict";t.exports=function(t,e,n){var r=void 0===n;switch(e.length){case 0:return r?t():t.call(n);case 1:return r?t(e[0]):t.call(n,e[0]);case 2:return r?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return r?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return r?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3])}return t.apply(n,e)}},function(t,e,n){"use strict";var r=n(9);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e,n){"use strict";var r=n(7),o=n(0)("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||i[o]===t)}},function(t,e,n){"use strict";var r=n(2);t.exports=function(t,e,n,o){try{return o?e(r(n)[0],n[1]):e(n)}catch(e){var i=t.return;throw void 0!==i&&r(i.call(t)),e}}},function(t,e,n){"use strict";var r=n(52),o=n(29),i=n(18),a={};n(3)(a,n(0)("iterator"),function(){return this}),t.exports=function(t,e,n){t.prototype=r(a,{next:o(1,n)}),i(t,e+" Iterator")}},function(t,e,n){"use strict";var r=n(0)("iterator"),o=!1;try{var i=[7][r]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(t){}t.exports=function(t,e){if(!e&&!o)return!1;var n=!1;try{var i=[7],a=i[r]();a.next=function(){return{done:n=!0}},i[r]=function(){return a},t(i)}catch(t){}return n}},function(t,e,n){"use strict";t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e,n){"use strict";var r=n(1),o=n(31).set,i=r.MutationObserver||r.WebKitMutationObserver,a=r.process,s=r.Promise,c="process"==n(9)(a);t.exports=function(){var t,e,n,u=function(){var r,o;for(c&&(r=a.domain)&&r.exit();t;){o=t.fn,t=t.next;try{o()}catch(r){throw t?n():e=void 0,r}}e=void 0,r&&r.enter()};if(c)n=function(){a.nextTick(u)};else if(i){var l=!0,f=document.createTextNode("");new i(u).observe(f,{characterData:!0}),n=function(){f.data=l=!l}}else if(s&&s.resolve){var h=s.resolve();n=function(){h.then(u)}}else n=function(){o.call(r,u)};return function(r){var o={fn:r,next:void 0};e&&(e.next=o),t||(t=o,n()),e=o}}},function(t,e,n){"use strict";var r=n(2),o=n(53),i=n(23),a=n(19)("IE_PROTO"),s=function(){},c="prototype",u=function(){var t,e=n(17)("iframe"),r=i.length,o="<",a=">";for(e.style.display="none",n(26).appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(o+"script"+a+"document.F=Object"+o+"/script"+a),t.close(),u=t.F;r--;)delete u[c][i[r]];return u()};t.exports=Object.create||function(t,e){var n;return null!==t?(s[c]=r(t),n=new s,s[c]=null,n[a]=t):n=u(),void 0===e?n:o(n,e)}},function(t,e,n){"use strict";var r=n(12),o=n(2),i=n(56);t.exports=n(5)?Object.defineProperties:function(t,e){o(t);for(var n,a=i(e),s=a.length,c=0;s>c;)r.f(t,n=a[c++],e[n]);return t}},function(t,e,n){"use strict";var r=n(6),o=n(62),i=n(19)("IE_PROTO"),a=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=o(t),r(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?a:null}},function(t,e,n){"use strict";var r=n(6),o=n(21),i=n(41)(!1),a=n(19)("IE_PROTO");t.exports=function(t,e){var n,s=o(t),c=0,u=[];for(n in s)n!=a&&r(s,n)&&u.push(n);for(;e.length>c;)r(s,n=e[c++])&&(~i(u,n)||u.push(n));return u}},function(t,e,n){"use strict";var r=n(55),o=n(23);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e,n){"use strict";var r=n(8);t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},function(t,e,n){"use strict";var r=n(1),o=n(12),i=n(5),a=n(0)("species");t.exports=function(t){var e=r[t];i&&e&&!e[a]&&o.f(e,a,{configurable:!0,get:function(){return this}})}},function(t,e,n){"use strict";var r=n(2),o=n(14),i=n(0)("species");t.exports=function(t,e){var n,a=r(t).constructor;return void 0===a||void 0==(n=r(a)[i])?e:o(n)}},function(t,e,n){"use strict";var r=n(20),o=n(16);t.exports=function(t){return function(e,n){var i,a,s=String(o(e)),c=r(n),u=s.length;return c<0||c>=u?t?"":void 0:(i=s.charCodeAt(c),i<55296||i>56319||c+1===u||(a=s.charCodeAt(c+1))<56320||a>57343?t?s.charAt(c):i:t?s.slice(c,c+2):(i-55296<<10)+(a-56320)+65536)}}},function(t,e,n){"use strict";var r=n(20),o=Math.max,i=Math.min;t.exports=function(t,e){return t=r(t),t<0?o(t+e,0):i(t,e)}},function(t,e,n){"use strict";var r=n(16);t.exports=function(t){return Object(r(t))}},function(t,e,n){"use strict";var r=n(11);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){"use strict";var r=n(15),o=n(0)("iterator"),i=n(7);t.exports=n(4).getIteratorMethod=function(t){if(void 0!=t)return t[o]||t["@@iterator"]||i[r(t)]}},function(t,e,n){"use strict";var r=n(39),o=n(50),i=n(7),a=n(21);t.exports=n(27)(Array,"Array",function(t,e){this._t=a(t),this._i=0,this._k=e},function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,o(1)):"keys"==e?o(0,n):"values"==e?o(0,t[n]):o(0,[n,t[n]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(t,e,n){"use strict";var r=n(15),o={};o[n(0)("toStringTag")]="z",o+""!="[object z]"&&n(8)(Object.prototype,"toString",function(){return"[object "+r(this)+"]"},!0)},function(t,e,n){"use strict";var r,o,i,a=n(28),s=n(1),c=n(10),u=n(15),l=n(24),f=n(11),h=n(14),d=n(40),p=n(42),v=n(59),m=n(31).set,y=n(51)(),g="Promise",w=s.TypeError,b=s.process,_=s[g],b=s.process,E="process"==u(b),S=function(){},x=!!function(){try{var t=_.resolve(1),e=(t.constructor={})[n(0)("species")]=function(t){t(S,S)};return(E||"function"==typeof PromiseRejectionEvent)&&t.then(S)instanceof e}catch(t){}}(),k=function(t,e){return t===e||t===_&&e===i},T=function(t){var e;return!(!f(t)||"function"!=typeof(e=t.then))&&e},O=function(t){return k(_,t)?new C(t):new o(t)},C=o=function(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw w("Bad Promise constructor");e=t,n=r}),this.resolve=h(e),this.reject=h(n)},A=function(t){try{t()}catch(t){return{error:t}}},M=function(t,e){if(!t._n){t._n=!0;var n=t._c;y(function(){for(var r=t._v,o=1==t._s,i=0,a=function(e){var n,i,a=o?e.ok:e.fail,s=e.resolve,c=e.reject,u=e.domain;try{a?(o||(2==t._h&&j(t),t._h=1),a===!0?n=r:(u&&u.enter(),n=a(r),u&&u.exit()),n===e.promise?c(w("Promise-chain cycle")):(i=T(n))?i.call(n,s,c):s(n)):c(r)}catch(t){c(t)}};n.length>i;)a(n[i++]);t._c=[],t._n=!1,e&&!t._h&&L(t)})}},L=function(t){m.call(s,function(){var e,n,r,o=t._v;if(P(t)&&(e=A(function(){E?b.emit("unhandledRejection",o,t):(n=s.onunhandledrejection)?n({promise:t,reason:o}):(r=s.console)&&r.error&&r.error("Unhandled promise rejection",o)}),t._h=E||P(t)?2:1),t._a=void 0,e)throw e.error})},P=function t(e){if(1==e._h)return!1;for(var n,r=e._a||e._c,o=0;r.length>o;)if(n=r[o++],n.fail||!t(n.promise))return!1;return!0},j=function(t){m.call(s,function(){var e;E?b.emit("rejectionHandled",t):(e=s.onrejectionhandled)&&e({promise:t,reason:t._v})})},N=function(t){var e=this;e._d||(e._d=!0,e=e._w||e,e._v=t,e._s=2,e._a||(e._a=e._c.slice()),M(e,!0))},R=function t(e){var n,r=this;if(!r._d){r._d=!0,r=r._w||r;try{if(r===e)throw w("Promise can't be resolved itself");(n=T(e))?y(function(){var o={_w:r,_d:!1};try{n.call(e,c(t,o,1),c(N,o,1))}catch(t){N.call(o,t)}}):(r._v=e,r._s=1,M(r,!1))}catch(t){N.call({_w:r,_d:!1},t)}}};x||(_=function(t){d(this,_,g,"_h"),h(t),r.call(this);try{t(c(R,this,1),c(N,this,1))}catch(t){N.call(this,t)}},r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1},r.prototype=n(57)(_.prototype,{then:function(t,e){var n=O(v(this,_));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=E?b.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&M(this,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),C=function(){var t=new r;this.promise=t,this.resolve=c(R,t,1),this.reject=c(N,t,1)}),l(l.G+l.W+l.F*!x,{Promise:_}),n(18)(_,g),n(58)(g),i=n(4)[g],l(l.S+l.F*!x,g,{reject:function(t){var e=O(this),n=e.reject;return n(t),e.promise}}),l(l.S+l.F*(a||!x),g,{resolve:function(t){if(t instanceof _&&k(t.constructor,this))return t;var e=O(this),n=e.resolve;return n(t),e.promise}}),l(l.S+l.F*!(x&&n(49)(function(t){_.all(t).catch(S)})),g,{all:function(t){var e=this,n=O(e),r=n.resolve,o=n.reject,i=A(function(){var n=[],i=0,a=1;p(t,!1,function(t){var s=i++,c=!1;n.push(void 0),a++,e.resolve(t).then(function(t){ +c||(c=!0,n[s]=t,--a||r(n))},o)}),--a||r(n)});return i&&o(i.error),n.promise},race:function(t){var e=this,n=O(e),r=n.reject,o=A(function(){p(t,!1,function(t){e.resolve(t).then(n.resolve,r)})});return o&&r(o.error),n.promise}})},function(t,e,n){"use strict";var r=n(60)(!0);n(27)(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,e=this._t,n=this._i;return n>=e.length?{value:void 0,done:!0}:(t=r(e,n),this._i+=t.length,{value:t,done:!1})})},function(t,e,n){"use strict";for(var r=n(65),o=n(8),i=n(1),a=n(3),s=n(7),c=n(0),u=c("iterator"),l=c("toStringTag"),f=s.Array,h=["NodeList","DOMTokenList","MediaList","StyleSheetList","CSSRuleList"],d=0;d<5;d++){var p,v=h[d],m=i[v],y=m&&m.prototype;if(y){y[u]||a(y,u,f),y[l]||a(y,l,v),s[v]=f;for(p in r)y[p]||o(y,p,r[p],!0)}}},function(t,e,n){"use strict";var r,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(){function i(t,e){function n(t,e){return function(){return t.apply(e,arguments)}}var r;if(e=e||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=e.touchBoundary||10,this.layer=t,this.tapDelay=e.tapDelay||200,this.tapTimeout=e.tapTimeout||700,!i.notNeeded(t)){for(var o=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],a=this,c=0,u=o.length;c=0,s=navigator.userAgent.indexOf("Android")>0&&!a,c=/iP(ad|hone|od)/.test(navigator.userAgent)&&!a,u=c&&/OS 4_\d(_\d)?/.test(navigator.userAgent),l=c&&/OS [6-7]_\d/.test(navigator.userAgent),f=navigator.userAgent.indexOf("BB10")>0;i.prototype.needsClick=function(t){switch(t.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(t.disabled)return!0;break;case"input":if(c&&"file"===t.type||t.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(t.className)},i.prototype.needsFocus=function(t){switch(t.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!s;case"input":switch(t.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!t.disabled&&!t.readOnly;default:return/\bneedsfocus\b/.test(t.className)}},i.prototype.sendClick=function(t,e){var n,r;document.activeElement&&document.activeElement!==t&&document.activeElement.blur(),r=e.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(t),!0,!0,window,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,t.dispatchEvent(n)},i.prototype.determineEventType=function(t){return s&&"select"===t.tagName.toLowerCase()?"mousedown":"click"},i.prototype.focus=function(t){var e;c&&t.setSelectionRange&&0!==t.type.indexOf("date")&&"time"!==t.type&&"month"!==t.type?(e=t.value.length,t.setSelectionRange(e,e)):t.focus()},i.prototype.updateScrollParent=function(t){var e,n;if(e=t.fastClickScrollParent,!e||!e.contains(t)){n=t;do{if(n.scrollHeight>n.offsetHeight){e=n,t.fastClickScrollParent=n;break}n=n.parentElement}while(n)}e&&(e.fastClickLastScrollTop=e.scrollTop)},i.prototype.getTargetElementFromEventTarget=function(t){return t.nodeType===Node.TEXT_NODE?t.parentNode:t},i.prototype.onTouchStart=function(t){var e,n,r;if(t.targetTouches.length>1)return!0;if(e=this.getTargetElementFromEventTarget(t.target),n=t.targetTouches[0],c){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!u){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return t.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(e)}}return this.trackingClick=!0,this.trackingClickStart=t.timeStamp,this.targetElement=e,this.touchStartX=n.pageX,this.touchStartY=n.pageY,t.timeStamp-this.lastClickTimen||Math.abs(e.pageY-this.touchStartY)>n},i.prototype.onTouchMove=function(t){return!this.trackingClick||((this.targetElement!==this.getTargetElementFromEventTarget(t.target)||this.touchHasMoved(t))&&(this.trackingClick=!1,this.targetElement=null),!0)},i.prototype.findControl=function(t){return void 0!==t.control?t.control:t.htmlFor?document.getElementById(t.htmlFor):t.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},i.prototype.onTouchEnd=function(t){var e,n,r,o,i,a=this.targetElement;if(!this.trackingClick)return!0;if(t.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=t.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,l&&(i=t.changedTouches[0],a=document.elementFromPoint(i.pageX-window.pageXOffset,i.pageY-window.pageYOffset)||a,a.fastClickScrollParent=this.targetElement.fastClickScrollParent),r=a.tagName.toLowerCase(),"label"===r){if(e=this.findControl(a)){if(this.focus(a),s)return!1;a=e}}else if(this.needsFocus(a))return t.timeStamp-n>100||c&&window.top!==window&&"input"===r?(this.targetElement=null,!1):(this.focus(a),this.sendClick(a,t),c&&"select"===r||(this.targetElement=null,t.preventDefault()),!1);return!(!c||u||(o=a.fastClickScrollParent,!o||o.fastClickLastScrollTop===o.scrollTop))||(this.needsClick(a)||(t.preventDefault(),this.sendClick(a,t)),!1)},i.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},i.prototype.onMouse=function(t){return!this.targetElement||(!!t.forwardedTouchEvent||(!t.cancelable||(!(!this.needsClick(this.targetElement)||this.cancelNextClick)||(t.stopImmediatePropagation?t.stopImmediatePropagation():t.propagationStopped=!0,t.stopPropagation(),t.preventDefault(),!1))))},i.prototype.onClick=function(t){var e;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===t.target.type&&0===t.detail||(e=this.onMouse(t),e||(this.targetElement=null),e)},i.prototype.destroy=function(){var t=this.layer;s&&(t.removeEventListener("mouseover",this.onMouse,!0),t.removeEventListener("mousedown",this.onMouse,!0),t.removeEventListener("mouseup",this.onMouse,!0)),t.removeEventListener("click",this.onClick,!0),t.removeEventListener("touchstart",this.onTouchStart,!1),t.removeEventListener("touchmove",this.onTouchMove,!1),t.removeEventListener("touchend",this.onTouchEnd,!1),t.removeEventListener("touchcancel",this.onTouchCancel,!1)},i.notNeeded=function(t){var e,n,r,o;if("undefined"==typeof window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!s)return!0;if(e=document.querySelector("meta[name=viewport]")){if(e.content.indexOf("user-scalable=no")!==-1)return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(f&&(r=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),r[1]>=10&&r[2]>=3&&(e=document.querySelector("meta[name=viewport]")))){if(e.content.indexOf("user-scalable=no")!==-1)return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===t.style.msTouchAction||"manipulation"===t.style.touchAction||(o=+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1],!!(o>=27&&(e=document.querySelector("meta[name=viewport]"),e&&(e.content.indexOf("user-scalable=no")!==-1||document.documentElement.scrollWidth<=window.outerWidth)))||("none"===t.style.touchAction||"manipulation"===t.style.touchAction))},i.attach=function(t,e){return new i(t,e)},"object"===o(n(34))&&n(34)?(r=function(){return i}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))):"undefined"!=typeof t&&t.exports?(t.exports=i.attach,t.exports.FastClick=i):window.FastClick=i}()},function(t,e,n){"use strict";var r,o,i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};!function(a){var s=!1;if(r=a,o="function"==typeof r?r.call(e,n,e,t):r,!(void 0!==o&&(t.exports=o)),s=!0,"object"===i(e)&&(t.exports=a(),s=!0),!s){var c=window.Cookies,u=window.Cookies=a();u.noConflict=function(){return window.Cookies=c,u}}}(function(){function t(){for(var t=0,e={};t1){if(i=t({path:"/"},r.defaults,i),"number"==typeof i.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*i.expires),i.expires=s}try{a=JSON.stringify(o),/^[\{\[]/.test(a)&&(o=a)}catch(t){}return o=n.write?n.write(o,e):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),e=encodeURIComponent(String(e)),e=e.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),e=e.replace(/[\(\)]/g,escape),document.cookie=[e,"=",o,i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}e||(a={});for(var c=document.cookie?document.cookie.split("; "):[],u=/(%[0-9A-Z]{2})+/g,l=0;ln.idx?n=n.next:(r+=e.val*n.val,e=e.next,n=n.next);return r},i.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},i.SortedSet=function(){this.length=0,this.elements=[]},i.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},i.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(i===t)return o;it&&(n=o),r=n-e,o=e+Math.floor(r/2),i=this.elements[o]}return i===t?o:-1},i.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,r=n-e,o=e+Math.floor(r/2),i=this.elements[o];r>1;)it&&(n=o),r=n-e,o=e+Math.floor(r/2),i=this.elements[o];return i>t?o:io-1||r>a-1)break;s[n]!==c[r]?s[n]c[r]&&r++:(e.add(s[n]),n++,r++)}return e},i.SortedSet.prototype.clone=function(){var t=new i.SortedSet;return t.elements=this.toArray(),t.length=t.elements.length,t},i.SortedSet.prototype.union=function(t){var e,n,r;this.length>=t.length?(e=this,n=t):(e=t,n=this),r=e.clone();for(var o=0,i=n.toArray();o0&&(r=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=r},i.Index.prototype.search=function(t){var e=this.pipeline.run(this.tokenizerFn(t)),n=new i.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),a=e.some(function(t){return this.tokenStore.has(t)},this);if(!a)return[];e.forEach(function(t,e,a){var s=1/a.length*this._fields.length*o,c=this,u=this.tokenStore.expand(t).reduce(function(e,r){var o=c.corpusTokens.indexOf(r),a=c.idf(r),u=1,l=new i.SortedSet;if(r!==t){var f=Math.max(3,r.length-t.length);u=1/Math.log(f)}o>-1&&n.insert(o,s*a*u);for(var h=c.tokenStore.get(r),d=Object.keys(h),p=d.length,v=0;v=this.height_;t!==this.active_&&(this.header_.dataset.mdState=(this.active_=t)?"shadow":"")}},{key:"reset",value:function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1}}]),t}();e.a=i},function(t,e,n){"use strict";var r=n(79),o=n(80),i=n(81);e.a={Blur:r.a,Collapse:o.a,Scrolling:i.a}},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(){function t(t,e){for(var n=0;n0&&(this.els_[n-1].dataset.mdState="blur"),this.index_=n;else for(var r=this.index_;r>=0;r--){if(!(this.anchors_[r].offsetTop-80>t)){this.index_=r;break}r>0&&(this.els_[r-1].dataset.mdState="")}this.offset_=t,this.dir_=e}}},{key:"reset",value:function(){Array.prototype.forEach.call(this.els_,function(t){t.dataset.mdState=""}),this.index_=0,this.offset_=window.pageYOffset}}]),t}();e.a=i},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(){function t(t,e){for(var n=0;nn){for(;" "!==t[n]&&--n>0;);return t.substring(0,n)+"..."}return t}},{key:"update",value:function(e){var n=this;if("focus"!==e.type||this.index_){if("keyup"===e.type){var r=e.target;if(!(r instanceof HTMLInputElement))throw new ReferenceError;for(;this.list_.firstChild;)this.list_.removeChild(this.list_.firstChild);var o=this.index_.search(r.value);o.forEach(function(e){var r=n.docs_[e.ref],o=r.location.split("#"),i=a(o,1),s=i[0];s=s.replace(/^(\/?\.{2})+/g,""),n.list_.appendChild(t.createElement("li",{class:"md-search-result__item"},t.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link","data-md-rel":s===document.location.pathname?"anchor":""},t.createElement("article",{class:"md-search-result__article"},t.createElement("h1",{class:"md-search-result__title"},r.title),t.createElement("p",{class:"md-search-result__teaser"},n.truncate_(r.text,140))))))});var s=this.list_.querySelectorAll("[data-md-rel=anchor]");Array.prototype.forEach.call(s,function(t){t.addEventListener("click",function(e){var n=document.querySelector("[data-md-toggle=search]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;n.checked&&(n.checked=!1,n.dispatchEvent(new CustomEvent("change"))),e.preventDefault(),setTimeout(function(){document.location.href=t.href},100)})}),this.meta_.textContent=o.length+" search result"+(1!==o.length?"s":"")}}else{var c=function(t){n.index_=i()(function(){this.field("title",{boost:10}),this.field("text"),this.ref("location")}),n.docs_=t.reduce(function(t,e){return n.index_.add(e),t[e.location]=e,t},{})};setTimeout(function(){return"function"==typeof n.data_?n.data_().then(c):c(n.data_)},250)}}}]),e}();e.a=c}).call(e,n(13))},function(t,e,n){"use strict";var r=n(86);e.a={Position:r.a}},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(){function t(t,e){for(var n=0;n=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")}},{key:"reset",value:function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0}}]),t}();e.a=i},function(t,e,n){"use strict";var r=n(88),o=n(91);e.a={Adapter:r.a,Repository:o.a}},function(t,e,n){"use strict";var r=n(90);e.a={GitHub:r.a}},function(t,e,n){"use strict";function r(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=n(71),i=n.n(o),a=function(){function t(t,e){for(var n=0;n1e4?(t/1e3).toFixed(0)+"k":t>1e3?(t/1e3).toFixed(1)+"k":""+t}},{key:"hash_",value:function(t){var e=0;if(0===t.length)return e;for(var n=0,r=t.length;n=this.offset_;t!==this.active_&&(this.el_.dataset.mdState=(this.active_=t)?"hidden":"")}},{key:"reset",value:function(){this.el_.dataset.mdState="",this.active_=!1}}]),t}();e.a=i},function(t,e,n){n(35),n(36),n(37),t.exports=n(38)}]); \ No newline at end of file diff --git a/site/assets/javascripts/modernizr-56ade86843.js b/site/assets/javascripts/modernizr-56ade86843.js new file mode 100644 index 000000000..cffa58352 --- /dev/null +++ b/site/assets/javascripts/modernizr-56ade86843.js @@ -0,0 +1 @@ +!function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,i,s,f;for(var a in w)if(w.hasOwnProperty(a)){if(e=[],n=w[a],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t.md-nav__link,[data-md-color-primary=red] .md-nav__link:active{color:#ef5350}[data-md-color-primary=red] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=pink]{background-color:#e91e63}[data-md-color-primary=pink] .md-typeset a{color:#e91e63}[data-md-color-primary=pink] .md-header{background-color:#e91e63}[data-md-color-primary=pink] .md-nav__item--active>.md-nav__link,[data-md-color-primary=pink] .md-nav__link:active{color:#e91e63}[data-md-color-primary=pink] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=purple]{background-color:#ab47bc}[data-md-color-primary=purple] .md-typeset a{color:#ab47bc}[data-md-color-primary=purple] .md-header{background-color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--active>.md-nav__link,[data-md-color-primary=purple] .md-nav__link:active{color:#ab47bc}[data-md-color-primary=purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-purple]{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-typeset a{color:#7e57c2}[data-md-color-primary=deep-purple] .md-header{background-color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--active>.md-nav__link,[data-md-color-primary=deep-purple] .md-nav__link:active{color:#7e57c2}[data-md-color-primary=deep-purple] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=indigo]{background-color:#3f51b5}[data-md-color-primary=indigo] .md-typeset a{color:#3f51b5}[data-md-color-primary=indigo] .md-header{background-color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--active>.md-nav__link,[data-md-color-primary=indigo] .md-nav__link:active{color:#3f51b5}[data-md-color-primary=indigo] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue]{background-color:#2196f3}[data-md-color-primary=blue] .md-typeset a{color:#2196f3}[data-md-color-primary=blue] .md-header{background-color:#2196f3}[data-md-color-primary=blue] .md-nav__item--active>.md-nav__link,[data-md-color-primary=blue] .md-nav__link:active{color:#2196f3}[data-md-color-primary=blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-blue]{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-typeset a{color:#03a9f4}[data-md-color-primary=light-blue] .md-header{background-color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--active>.md-nav__link,[data-md-color-primary=light-blue] .md-nav__link:active{color:#03a9f4}[data-md-color-primary=light-blue] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=cyan]{background-color:#00bcd4}[data-md-color-primary=cyan] .md-typeset a{color:#00bcd4}[data-md-color-primary=cyan] .md-header{background-color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--active>.md-nav__link,[data-md-color-primary=cyan] .md-nav__link:active{color:#00bcd4}[data-md-color-primary=cyan] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=teal]{background-color:#009688}[data-md-color-primary=teal] .md-typeset a{color:#009688}[data-md-color-primary=teal] .md-header{background-color:#009688}[data-md-color-primary=teal] .md-nav__item--active>.md-nav__link,[data-md-color-primary=teal] .md-nav__link:active{color:#009688}[data-md-color-primary=teal] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=green]{background-color:#4caf50}[data-md-color-primary=green] .md-typeset a{color:#4caf50}[data-md-color-primary=green] .md-header{background-color:#4caf50}[data-md-color-primary=green] .md-nav__item--active>.md-nav__link,[data-md-color-primary=green] .md-nav__link:active{color:#4caf50}[data-md-color-primary=green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=light-green]{background-color:#7cb342}[data-md-color-primary=light-green] .md-typeset a{color:#7cb342}[data-md-color-primary=light-green] .md-header{background-color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--active>.md-nav__link,[data-md-color-primary=light-green] .md-nav__link:active{color:#7cb342}[data-md-color-primary=light-green] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=lime]{background-color:#c0ca33}[data-md-color-primary=lime] .md-typeset a{color:#c0ca33}[data-md-color-primary=lime] .md-header{background-color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--active>.md-nav__link,[data-md-color-primary=lime] .md-nav__link:active{color:#c0ca33}[data-md-color-primary=lime] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=yellow]{background-color:#f9a825}[data-md-color-primary=yellow] .md-typeset a{color:#f9a825}[data-md-color-primary=yellow] .md-header{background-color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--active>.md-nav__link,[data-md-color-primary=yellow] .md-nav__link:active{color:#f9a825}[data-md-color-primary=yellow] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=amber]{background-color:#ffb300}[data-md-color-primary=amber] .md-typeset a{color:#ffb300}[data-md-color-primary=amber] .md-header{background-color:#ffb300}[data-md-color-primary=amber] .md-nav__item--active>.md-nav__link,[data-md-color-primary=amber] .md-nav__link:active{color:#ffb300}[data-md-color-primary=amber] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=orange]{background-color:#fb8c00}[data-md-color-primary=orange] .md-typeset a{color:#fb8c00}[data-md-color-primary=orange] .md-header{background-color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--active>.md-nav__link,[data-md-color-primary=orange] .md-nav__link:active{color:#fb8c00}[data-md-color-primary=orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=deep-orange]{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-typeset a{color:#ff7043}[data-md-color-primary=deep-orange] .md-header{background-color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--active>.md-nav__link,[data-md-color-primary=deep-orange] .md-nav__link:active{color:#ff7043}[data-md-color-primary=deep-orange] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=brown]{background-color:#795548}[data-md-color-primary=brown] .md-typeset a{color:#795548}[data-md-color-primary=brown] .md-header{background-color:#795548}[data-md-color-primary=brown] .md-nav__item--active>.md-nav__link,[data-md-color-primary=brown] .md-nav__link:active{color:#795548}[data-md-color-primary=brown] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=grey]{background-color:#757575}[data-md-color-primary=grey] .md-typeset a{color:#757575}[data-md-color-primary=grey] .md-header{background-color:#757575}[data-md-color-primary=grey] .md-nav__item--active>.md-nav__link,[data-md-color-primary=grey] .md-nav__link:active{color:#757575}[data-md-color-primary=grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-primary=blue-grey]{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-typeset a{color:#546e7a}[data-md-color-primary=blue-grey] .md-header{background-color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--active>.md-nav__link,[data-md-color-primary=blue-grey] .md-nav__link:active{color:#546e7a}[data-md-color-primary=blue-grey] .md-nav__item--nested>.md-nav__link{color:inherit}button[data-md-color-accent=red]{background-color:#ff1744}[data-md-color-accent=red] .md-typeset a:active,[data-md-color-accent=red] .md-typeset a:hover{color:#ff1744}[data-md-color-accent=red] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=red] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-nav__link:hover,[data-md-color-accent=red] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=red] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=red] .md-typeset [id] .headerlink:focus,[data-md-color-accent=red] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=red] .md-typeset [id]:target .headerlink{color:#ff1744}[data-md-color-accent=red] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}[data-md-color-accent=red] .md-search-result__link:hover{background-color:rgba(255,23,68,.1)}[data-md-color-accent=red] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff1744}button[data-md-color-accent=pink]{background-color:#f50057}[data-md-color-accent=pink] .md-typeset a:active,[data-md-color-accent=pink] .md-typeset a:hover{color:#f50057}[data-md-color-accent=pink] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=pink] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-nav__link:hover,[data-md-color-accent=pink] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=pink] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=pink] .md-typeset [id] .headerlink:focus,[data-md-color-accent=pink] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=pink] .md-typeset [id]:target .headerlink{color:#f50057}[data-md-color-accent=pink] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}[data-md-color-accent=pink] .md-search-result__link:hover{background-color:rgba(245,0,87,.1)}[data-md-color-accent=pink] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#f50057}button[data-md-color-accent=purple]{background-color:#e040fb}[data-md-color-accent=purple] .md-typeset a:active,[data-md-color-accent=purple] .md-typeset a:hover{color:#e040fb}[data-md-color-accent=purple] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=purple] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-nav__link:hover,[data-md-color-accent=purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=purple] .md-typeset [id]:target .headerlink{color:#e040fb}[data-md-color-accent=purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}[data-md-color-accent=purple] .md-search-result__link:hover{background-color:rgba(224,64,251,.1)}[data-md-color-accent=purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#e040fb}button[data-md-color-accent=deep-purple]{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset a:active,[data-md-color-accent=deep-purple] .md-typeset a:hover{color:#7c4dff}[data-md-color-accent=deep-purple] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-purple] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-nav__link:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-purple] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-purple] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-purple] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-purple] .md-typeset [id]:target .headerlink{color:#7c4dff}[data-md-color-accent=deep-purple] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}[data-md-color-accent=deep-purple] .md-search-result__link:hover{background-color:rgba(124,77,255,.1)}[data-md-color-accent=deep-purple] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#7c4dff}button[data-md-color-accent=indigo]{background-color:#536dfe}[data-md-color-accent=indigo] .md-typeset a:active,[data-md-color-accent=indigo] .md-typeset a:hover{color:#536dfe}[data-md-color-accent=indigo] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=indigo] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-nav__link:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=indigo] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=indigo] .md-typeset [id] .headerlink:focus,[data-md-color-accent=indigo] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=indigo] .md-typeset [id]:target .headerlink{color:#536dfe}[data-md-color-accent=indigo] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}[data-md-color-accent=indigo] .md-search-result__link:hover{background-color:rgba(83,109,254,.1)}[data-md-color-accent=indigo] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}button[data-md-color-accent=blue]{background-color:#448aff}[data-md-color-accent=blue] .md-typeset a:active,[data-md-color-accent=blue] .md-typeset a:hover{color:#448aff}[data-md-color-accent=blue] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=blue] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-nav__link:hover,[data-md-color-accent=blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=blue] .md-typeset [id]:target .headerlink{color:#448aff}[data-md-color-accent=blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}[data-md-color-accent=blue] .md-search-result__link:hover{background-color:rgba(68,138,255,.1)}[data-md-color-accent=blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#448aff}button[data-md-color-accent=light-blue]{background-color:#0091ea}[data-md-color-accent=light-blue] .md-typeset a:active,[data-md-color-accent=light-blue] .md-typeset a:hover{color:#0091ea}[data-md-color-accent=light-blue] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-blue] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-nav__link:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-blue] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-blue] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-blue] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-blue] .md-typeset [id]:target .headerlink{color:#0091ea}[data-md-color-accent=light-blue] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}[data-md-color-accent=light-blue] .md-search-result__link:hover{background-color:rgba(0,145,234,.1)}[data-md-color-accent=light-blue] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#0091ea}button[data-md-color-accent=cyan]{background-color:#00b8d4}[data-md-color-accent=cyan] .md-typeset a:active,[data-md-color-accent=cyan] .md-typeset a:hover{color:#00b8d4}[data-md-color-accent=cyan] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=cyan] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-nav__link:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=cyan] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=cyan] .md-typeset [id] .headerlink:focus,[data-md-color-accent=cyan] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=cyan] .md-typeset [id]:target .headerlink{color:#00b8d4}[data-md-color-accent=cyan] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}[data-md-color-accent=cyan] .md-search-result__link:hover{background-color:rgba(0,184,212,.1)}[data-md-color-accent=cyan] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00b8d4}button[data-md-color-accent=teal]{background-color:#00bfa5}[data-md-color-accent=teal] .md-typeset a:active,[data-md-color-accent=teal] .md-typeset a:hover{color:#00bfa5}[data-md-color-accent=teal] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=teal] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-nav__link:hover,[data-md-color-accent=teal] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=teal] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=teal] .md-typeset [id] .headerlink:focus,[data-md-color-accent=teal] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=teal] .md-typeset [id]:target .headerlink{color:#00bfa5}[data-md-color-accent=teal] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}[data-md-color-accent=teal] .md-search-result__link:hover{background-color:rgba(0,191,165,.1)}[data-md-color-accent=teal] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00bfa5}button[data-md-color-accent=green]{background-color:#00c853}[data-md-color-accent=green] .md-typeset a:active,[data-md-color-accent=green] .md-typeset a:hover{color:#00c853}[data-md-color-accent=green] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=green] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-nav__link:hover,[data-md-color-accent=green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=green] .md-typeset [id]:target .headerlink{color:#00c853}[data-md-color-accent=green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}[data-md-color-accent=green] .md-search-result__link:hover{background-color:rgba(0,200,83,.1)}[data-md-color-accent=green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#00c853}button[data-md-color-accent=light-green]{background-color:#64dd17}[data-md-color-accent=light-green] .md-typeset a:active,[data-md-color-accent=light-green] .md-typeset a:hover{color:#64dd17}[data-md-color-accent=light-green] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=light-green] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-nav__link:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=light-green] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=light-green] .md-typeset [id] .headerlink:focus,[data-md-color-accent=light-green] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=light-green] .md-typeset [id]:target .headerlink{color:#64dd17}[data-md-color-accent=light-green] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}[data-md-color-accent=light-green] .md-search-result__link:hover{background-color:rgba(100,221,23,.1)}[data-md-color-accent=light-green] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#64dd17}button[data-md-color-accent=lime]{background-color:#aeea00}[data-md-color-accent=lime] .md-typeset a:active,[data-md-color-accent=lime] .md-typeset a:hover{color:#aeea00}[data-md-color-accent=lime] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=lime] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-nav__link:hover,[data-md-color-accent=lime] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=lime] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=lime] .md-typeset [id] .headerlink:focus,[data-md-color-accent=lime] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=lime] .md-typeset [id]:target .headerlink{color:#aeea00}[data-md-color-accent=lime] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}[data-md-color-accent=lime] .md-search-result__link:hover{background-color:rgba(174,234,0,.1)}[data-md-color-accent=lime] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#aeea00}button[data-md-color-accent=yellow]{background-color:#ffd600}[data-md-color-accent=yellow] .md-typeset a:active,[data-md-color-accent=yellow] .md-typeset a:hover{color:#ffd600}[data-md-color-accent=yellow] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=yellow] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-nav__link:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=yellow] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=yellow] .md-typeset [id] .headerlink:focus,[data-md-color-accent=yellow] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=yellow] .md-typeset [id]:target .headerlink{color:#ffd600}[data-md-color-accent=yellow] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}[data-md-color-accent=yellow] .md-search-result__link:hover{background-color:rgba(255,214,0,.1)}[data-md-color-accent=yellow] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffd600}button[data-md-color-accent=amber]{background-color:#ffab00}[data-md-color-accent=amber] .md-typeset a:active,[data-md-color-accent=amber] .md-typeset a:hover{color:#ffab00}[data-md-color-accent=amber] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=amber] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-nav__link:hover,[data-md-color-accent=amber] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=amber] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=amber] .md-typeset [id] .headerlink:focus,[data-md-color-accent=amber] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=amber] .md-typeset [id]:target .headerlink{color:#ffab00}[data-md-color-accent=amber] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}[data-md-color-accent=amber] .md-search-result__link:hover{background-color:rgba(255,171,0,.1)}[data-md-color-accent=amber] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ffab00}button[data-md-color-accent=orange]{background-color:#ff9100}[data-md-color-accent=orange] .md-typeset a:active,[data-md-color-accent=orange] .md-typeset a:hover{color:#ff9100}[data-md-color-accent=orange] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=orange] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-nav__link:hover,[data-md-color-accent=orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=orange] .md-typeset [id]:target .headerlink{color:#ff9100}[data-md-color-accent=orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}[data-md-color-accent=orange] .md-search-result__link:hover{background-color:rgba(255,145,0,.1)}[data-md-color-accent=orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff9100}button[data-md-color-accent=deep-orange]{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset a:active,[data-md-color-accent=deep-orange] .md-typeset a:hover{color:#ff6e40}[data-md-color-accent=deep-orange] .md-typeset .codehilite::-webkit-scrollbar-thumb:hover,[data-md-color-accent=deep-orange] .md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-nav__link:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:hover .footnote-backref:hover,[data-md-color-accent=deep-orange] .md-typeset .footnote li:target .footnote-backref,[data-md-color-accent=deep-orange] .md-typeset [id] .headerlink:focus,[data-md-color-accent=deep-orange] .md-typeset [id]:hover .headerlink:hover,[data-md-color-accent=deep-orange] .md-typeset [id]:target .headerlink{color:#ff6e40}[data-md-color-accent=deep-orange] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}[data-md-color-accent=deep-orange] .md-search-result__link:hover{background-color:rgba(255,110,64,.1)}[data-md-color-accent=deep-orange] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#ff6e40}@media only screen and (max-width:59.9375em){[data-md-color-primary=red] .md-nav__source{background-color:rgba(190,66,64,.9675)}[data-md-color-primary=pink] .md-nav__source{background-color:rgba(185,24,79,.9675)}[data-md-color-primary=purple] .md-nav__source{background-color:rgba(136,57,150,.9675)}[data-md-color-primary=deep-purple] .md-nav__source{background-color:rgba(100,69,154,.9675)}[data-md-color-primary=indigo] .md-nav__source{background-color:rgba(50,64,144,.9675)}[data-md-color-primary=blue] .md-nav__source{background-color:rgba(26,119,193,.9675)}[data-md-color-primary=light-blue] .md-nav__source{background-color:rgba(2,134,194,.9675)}[data-md-color-primary=cyan] .md-nav__source{background-color:rgba(0,150,169,.9675)}[data-md-color-primary=teal] .md-nav__source{background-color:rgba(0,119,108,.9675)}[data-md-color-primary=green] .md-nav__source{background-color:rgba(60,139,64,.9675)}[data-md-color-primary=light-green] .md-nav__source{background-color:rgba(99,142,53,.9675)}[data-md-color-primary=lime] .md-nav__source{background-color:rgba(153,161,41,.9675)}[data-md-color-primary=yellow] .md-nav__source{background-color:rgba(198,134,29,.9675)}[data-md-color-primary=amber] .md-nav__source{background-color:rgba(203,142,0,.9675)}[data-md-color-primary=orange] .md-nav__source{background-color:rgba(200,111,0,.9675)}[data-md-color-primary=deep-orange] .md-nav__source{background-color:rgba(203,89,53,.9675)}[data-md-color-primary=brown] .md-nav__source{background-color:rgba(96,68,57,.9675)}[data-md-color-primary=grey] .md-nav__source{background-color:rgba(93,93,93,.9675)}[data-md-color-primary=blue-grey] .md-nav__source{background-color:rgba(67,88,97,.9675)}}@media only screen and (max-width:76.1875em){html [data-md-color-primary=red] .md-nav--primary .md-nav__title--site{background-color:#ef5350}html [data-md-color-primary=pink] .md-nav--primary .md-nav__title--site{background-color:#e91e63}html [data-md-color-primary=purple] .md-nav--primary .md-nav__title--site{background-color:#ab47bc}html [data-md-color-primary=deep-purple] .md-nav--primary .md-nav__title--site{background-color:#7e57c2}html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site{background-color:#3f51b5}html [data-md-color-primary=blue] .md-nav--primary .md-nav__title--site{background-color:#2196f3}html [data-md-color-primary=light-blue] .md-nav--primary .md-nav__title--site{background-color:#03a9f4}html [data-md-color-primary=cyan] .md-nav--primary .md-nav__title--site{background-color:#00bcd4}html [data-md-color-primary=teal] .md-nav--primary .md-nav__title--site{background-color:#009688}html [data-md-color-primary=green] .md-nav--primary .md-nav__title--site{background-color:#4caf50}html [data-md-color-primary=light-green] .md-nav--primary .md-nav__title--site{background-color:#7cb342}html [data-md-color-primary=lime] .md-nav--primary .md-nav__title--site{background-color:#c0ca33}html [data-md-color-primary=yellow] .md-nav--primary .md-nav__title--site{background-color:#f9a825}html [data-md-color-primary=amber] .md-nav--primary .md-nav__title--site{background-color:#ffb300}html [data-md-color-primary=orange] .md-nav--primary .md-nav__title--site{background-color:#fb8c00}html [data-md-color-primary=deep-orange] .md-nav--primary .md-nav__title--site{background-color:#ff7043}html [data-md-color-primary=brown] .md-nav--primary .md-nav__title--site{background-color:#795548}html [data-md-color-primary=grey] .md-nav--primary .md-nav__title--site{background-color:#757575}html [data-md-color-primary=blue-grey] .md-nav--primary .md-nav__title--site{background-color:#546e7a}}@media only screen and (min-width:60em){[data-md-color-primary=red] .md-nav--secondary{border-left:.4rem solid #ef5350}[data-md-color-primary=pink] .md-nav--secondary{border-left:.4rem solid #e91e63}[data-md-color-primary=purple] .md-nav--secondary{border-left:.4rem solid #ab47bc}[data-md-color-primary=deep-purple] .md-nav--secondary{border-left:.4rem solid #7e57c2}[data-md-color-primary=indigo] .md-nav--secondary{border-left:.4rem solid #3f51b5}[data-md-color-primary=blue] .md-nav--secondary{border-left:.4rem solid #2196f3}[data-md-color-primary=light-blue] .md-nav--secondary{border-left:.4rem solid #03a9f4}[data-md-color-primary=cyan] .md-nav--secondary{border-left:.4rem solid #00bcd4}[data-md-color-primary=teal] .md-nav--secondary{border-left:.4rem solid #009688}[data-md-color-primary=green] .md-nav--secondary{border-left:.4rem solid #4caf50}[data-md-color-primary=light-green] .md-nav--secondary{border-left:.4rem solid #7cb342}[data-md-color-primary=lime] .md-nav--secondary{border-left:.4rem solid #c0ca33}[data-md-color-primary=yellow] .md-nav--secondary{border-left:.4rem solid #f9a825}[data-md-color-primary=amber] .md-nav--secondary{border-left:.4rem solid #ffb300}[data-md-color-primary=orange] .md-nav--secondary{border-left:.4rem solid #fb8c00}[data-md-color-primary=deep-orange] .md-nav--secondary{border-left:.4rem solid #ff7043}[data-md-color-primary=brown] .md-nav--secondary{border-left:.4rem solid #795548}[data-md-color-primary=grey] .md-nav--secondary{border-left:.4rem solid #757575}[data-md-color-primary=blue-grey] .md-nav--secondary{border-left:.4rem solid #546e7a}}@media only screen and (min-width:76.25em){[data-md-color-primary=red] .md-tabs{background:rgba(190,66,64,.9675)}[data-md-color-primary=red] .md-tabs[data-md-state=hidden]{background:#ef5350}[data-md-color-primary=pink] .md-tabs{background:rgba(185,24,79,.9675)}[data-md-color-primary=pink] .md-tabs[data-md-state=hidden]{background:#e91e63}[data-md-color-primary=purple] .md-tabs{background:rgba(136,57,150,.9675)}[data-md-color-primary=purple] .md-tabs[data-md-state=hidden]{background:#ab47bc}[data-md-color-primary=deep-purple] .md-tabs{background:rgba(100,69,154,.9675)}[data-md-color-primary=deep-purple] .md-tabs[data-md-state=hidden]{background:#7e57c2}[data-md-color-primary=indigo] .md-tabs{background:rgba(50,64,144,.9675)}[data-md-color-primary=indigo] .md-tabs[data-md-state=hidden]{background:#3f51b5}[data-md-color-primary=blue] .md-tabs{background:rgba(26,119,193,.9675)}[data-md-color-primary=blue] .md-tabs[data-md-state=hidden]{background:#2196f3}[data-md-color-primary=light-blue] .md-tabs{background:rgba(2,134,194,.9675)}[data-md-color-primary=light-blue] .md-tabs[data-md-state=hidden]{background:#03a9f4}[data-md-color-primary=cyan] .md-tabs{background:rgba(0,150,169,.9675)}[data-md-color-primary=cyan] .md-tabs[data-md-state=hidden]{background:#00bcd4}[data-md-color-primary=teal] .md-tabs{background:rgba(0,119,108,.9675)}[data-md-color-primary=teal] .md-tabs[data-md-state=hidden]{background:#009688}[data-md-color-primary=green] .md-tabs{background:rgba(60,139,64,.9675)}[data-md-color-primary=green] .md-tabs[data-md-state=hidden]{background:#4caf50}[data-md-color-primary=light-green] .md-tabs{background:rgba(99,142,53,.9675)}[data-md-color-primary=light-green] .md-tabs[data-md-state=hidden]{background:#7cb342}[data-md-color-primary=lime] .md-tabs{background:rgba(153,161,41,.9675)}[data-md-color-primary=lime] .md-tabs[data-md-state=hidden]{background:#c0ca33}[data-md-color-primary=yellow] .md-tabs{background:rgba(198,134,29,.9675)}[data-md-color-primary=yellow] .md-tabs[data-md-state=hidden]{background:#f9a825}[data-md-color-primary=amber] .md-tabs{background:rgba(203,142,0,.9675)}[data-md-color-primary=amber] .md-tabs[data-md-state=hidden]{background:#ffb300}[data-md-color-primary=orange] .md-tabs{background:rgba(200,111,0,.9675)}[data-md-color-primary=orange] .md-tabs[data-md-state=hidden]{background:#fb8c00}[data-md-color-primary=deep-orange] .md-tabs{background:rgba(203,89,53,.9675)}[data-md-color-primary=deep-orange] .md-tabs[data-md-state=hidden]{background:#ff7043}[data-md-color-primary=brown] .md-tabs{background:rgba(96,68,57,.9675)}[data-md-color-primary=brown] .md-tabs[data-md-state=hidden]{background:#795548}[data-md-color-primary=grey] .md-tabs{background:rgba(93,93,93,.9675)}[data-md-color-primary=grey] .md-tabs[data-md-state=hidden]{background:#757575}[data-md-color-primary=blue-grey] .md-tabs{background:rgba(67,88,97,.9675)}[data-md-color-primary=blue-grey] .md-tabs[data-md-state=hidden]{background:#546e7a}} \ No newline at end of file diff --git a/site/assets/stylesheets/application-e17eeafcbc.css b/site/assets/stylesheets/application-e17eeafcbc.css new file mode 100644 index 000000000..dfdd176fe --- /dev/null +++ b/site/assets/stylesheets/application-e17eeafcbc.css @@ -0,0 +1 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}html{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}body{margin:0}hr{overflow:visible;box-sizing:content-box}a{-webkit-text-decoration-skip:objects}a,button,input,label{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}a:active,a:hover{outline-width:0}small,sub,sup{font-size:80%}sub,sup{position:relative;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}table{border-collapse:collapse;border-spacing:0}td,th{font-weight:400;vertical-align:top}button{padding:0;background:transparent;font-size:inherit}button,input{border:0;outline:0}.admonition:before,.md-icon,.md-nav__button,.md-nav__link:after,.md-nav__title:before,.md-typeset .critic.comment:before,.md-typeset .footnote-backref,.md-typeset .task-list-control .task-list-indicator:before{font-family:Material Icons;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;white-space:nowrap;speak:none;word-wrap:normal;direction:ltr}.md-content__edit,.md-footer-nav__button,.md-header-nav__button,.md-nav__button,.md-nav__title:before{display:inline-block;margin:.4rem;padding:.8rem;font-size:2.4rem;cursor:pointer}.md-icon--arrow-back:before{content:"arrow_back"}.md-icon--arrow-forward:before{content:"arrow_forward"}.md-icon--menu:before{content:"menu"}.md-icon--search:before{content:"search"}.md-icon--home:before{content:"school"}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern","onum","liga";font-feature-settings:"kern","onum","liga";font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:400}code,kbd,pre{color:rgba(0,0,0,.87);-webkit-font-feature-settings:"kern","onum","liga";font-feature-settings:"kern","onum","liga";font-family:Courier New,Courier,monospace;font-weight:400}.md-typeset{font-size:1.6rem;line-height:1.6;-webkit-print-color-adjust:exact}.md-typeset blockquote,.md-typeset ol,.md-typeset p,.md-typeset ul{margin:1em 0}.md-typeset h1{margin:0 0 4rem;color:rgba(0,0,0,.54);font-size:3.125rem;line-height:1.3}.md-typeset h1,.md-typeset h2{font-weight:300;letter-spacing:-.01em}.md-typeset h2{margin:4rem 0 1.6rem;font-size:2.5rem;line-height:1.4}.md-typeset h3{margin:3.2rem 0 1.6rem;font-size:2rem;font-weight:400;letter-spacing:-.01em;line-height:1.5}.md-typeset h2+h3{margin-top:1.6rem}.md-typeset h4{font-size:1.6rem}.md-typeset h4,.md-typeset h5,.md-typeset h6{margin:1.6rem 0;font-weight:700;letter-spacing:-.01em}.md-typeset h5,.md-typeset h6{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.1rem dotted rgba(0,0,0,.26)}.md-typeset a{color:#3f51b5;word-break:break-word}.md-typeset a,.md-typeset a:before{-webkit-transition:color .125s;transition:color .125s}.md-typeset a:active,.md-typeset a:hover{color:#536dfe}.md-typeset code,.md-typeset pre{background-color:hsla(0,0%,93%,.5);color:#37474f;font-size:85%}.md-typeset code{margin:0 .29412em;padding:.07353em 0;border-radius:.2rem;box-shadow:.29412em 0 0 hsla(0,0%,93%,.5),-.29412em 0 0 hsla(0,0%,93%,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:0;background-color:transparent;box-shadow:none}.md-typeset a>code{margin:inherit;padding:inherit;border-radius:none;background-color:inherit;color:inherit;box-shadow:none}.md-typeset pre{margin:1em 0;padding:1rem 1.2rem;border-radius:.2rem;line-height:1.4;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset pre::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset pre::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset pre::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset pre>code{margin:0;background-color:transparent;font-size:inherit;box-shadow:none;-webkit-box-decoration-break:none;box-decoration-break:none}.md-typeset kbd{padding:0 .29412em;border:.1rem solid #c9c9c9;border-radius:.2rem;border-bottom-color:#bcbcbc;background-color:#fcfcfc;color:#555;font-size:85%;box-shadow:0 .1rem 0 #b0b0b0;word-break:break-word}.md-typeset mark{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;background-color:rgba(255,235,59,.5);box-shadow:.25em 0 0 rgba(255,235,59,.5),-.25em 0 0 rgba(255,235,59,.5);word-break:break-word;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{border-bottom:.1rem dotted rgba(0,0,0,.54);cursor:help}.md-typeset small{opacity:.75}.md-typeset sub,.md-typeset sup{margin-left:.07812em}.md-typeset blockquote{padding-left:1.2rem;border-left:.4rem solid rgba(0,0,0,.26);color:rgba(0,0,0,.54)}.md-typeset ul{list-style-type:disc}.md-typeset ol,.md-typeset ul{margin-left:.625em;padding:0}.md-typeset ol ol,.md-typeset ul ol{list-style-type:lower-alpha}.md-typeset ol ol ol,.md-typeset ul ol ol{list-style-type:lower-roman}.md-typeset ol li,.md-typeset ul li{margin-bottom:.5em;margin-left:1.25em}.md-typeset ol li blockquote,.md-typeset ol li p,.md-typeset ul li blockquote,.md-typeset ul li p{margin:.5em 0}.md-typeset ol li:last-child,.md-typeset ul li:last-child{margin-bottom:0}.md-typeset ol li ol,.md-typeset ol li ul,.md-typeset ul li ol,.md-typeset ul li ul{margin:.5em 0 .5em .625em}.md-typeset dd{margin:1em 0 1em 1.875em}.md-typeset iframe,.md-typeset img,.md-typeset svg{max-width:100%}.md-typeset table:not([class]){box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);display:inline-block;max-width:100%;border-radius:.2rem;font-size:1.28rem;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) td:not([align]),.md-typeset table:not([class]) th:not([align]){text-align:left}.md-typeset table:not([class]) th{min-width:10rem;padding:1.2rem 1.6rem;background-color:rgba(0,0,0,.54);color:#fff;vertical-align:top}.md-typeset table:not([class]) td{padding:1.2rem 1.6rem;border-top:.1rem solid rgba(0,0,0,.07);vertical-align:top}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset__scrollwrap{margin:1em -1.6rem;overflow-x:auto;-webkit-overflow-scrolling:touch}.md-typeset .md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 1.6rem}.md-typeset .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{font-size:62.5%}body,html{height:100%}body{position:relative}hr{display:block;height:.1rem;padding:0;border:0}.md-svg{display:none}.md-grid{max-width:122rem;margin-right:auto;margin-left:auto}.md-container,.md-main{overflow:auto}.md-container{display:table;width:100%;height:100%;padding-top:5.6rem;table-layout:fixed}.md-main{display:table-row;height:100%}.md-main__inner{min-height:100%;padding-top:3rem;overflow:auto}.md-toggle{display:none}.md-overlay{position:fixed;top:0;width:0;height:0;-webkit-transition:width 0s .25s,height 0s .25s,opacity .25s;transition:width 0s .25s,height 0s .25s,opacity .25s;background-color:rgba(0,0,0,.54);opacity:0;z-index:2}.md-flex{display:table}.md-flex__cell{display:table-cell;position:relative;vertical-align:top}.md-flex__cell--shrink{width:0}.md-flex__cell--stretch{display:table;width:100%;table-layout:fixed}.md-flex__ellipsis{display:table-cell;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}@page{margin:25mm}.md-content__inner{margin:2.4rem 1.6rem}.md-content__inner>:last-child{margin-bottom:0}.md-content__edit{float:right}.md-header{position:fixed;top:0;right:0;left:0;height:5.6rem;-webkit-transition:background-color .25s;transition:background-color .25s;background-color:#3f51b5;color:#fff;z-index:1}.md-header,.no-js .md-header{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)}.md-header-nav{padding:.4rem}.md-header-nav__button{position:relative;-webkit-transition:opacity .25s;transition:opacity .25s;z-index:1}.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo img{display:block}.no-js .md-header-nav__button.md-icon--search{display:none}.md-header-nav__title{padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-header-nav__parent{color:hsla(0,0%,100%,.7)}.md-header-nav__parent:after{display:inline;color:hsla(0,0%,100%,.3);content:"/"}.md-header-nav__source{display:none}.md-footer-nav{background-color:rgba(0,0,0,.87);color:#fff}.md-footer-nav__inner{padding:.4rem;overflow:auto}.md-footer-nav__link{padding-top:2.8rem;padding-bottom:.8rem;-webkit-transition:opacity .25s;transition:opacity .25s}.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{width:25%;float:left}.md-footer-nav__link--next{width:75%;float:right;text-align:right}.md-footer-nav__button{-webkit-transition:background .25s;transition:background .25s}.md-footer-nav__title{position:relative;padding:0 2rem;font-size:1.8rem;line-height:4.8rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-2rem;padding:0 2rem;color:hsla(0,0%,100%,.7);font-size:1.5rem}.md-footer-meta{background:rgba(0,0,0,.895)}.md-footer-meta__inner{padding:.4rem;overflow:auto}html .md-footer-meta.md-typeset a{color:hsla(0,0%,100%,.7)}.md-footer-copyright{margin:0 1.2rem;padding:.8rem 0;color:hsla(0,0%,100%,.3);font-size:1.28rem}.md-footer-copyright__highlight{color:hsla(0,0%,100%,.7)}.md-footer-social{margin:0 .8rem;padding:.4rem 0 1.2rem}.md-footer-social__link{display:inline-block;width:3.2rem;height:3.2rem;border:.1rem solid hsla(0,0%,100%,.12);border-radius:100%;color:hsla(0,0%,100%,.7);font-size:1.6rem;text-align:center}.md-footer-social__link:before{line-height:1.9}.md-nav{font-size:1.4rem;line-height:1.3}.md-nav--secondary{-webkit-transition:border-left .25s;transition:border-left .25s;border-left:.4rem solid #3f51b5}.md-nav__title{display:block;padding:1.2rem 1.2rem 0;font-weight:700;text-overflow:ellipsis;overflow:hidden}.md-nav__title:before{display:none;content:"arrow_back"}.md-nav__title .md-nav__button{display:none}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 1.2rem}.md-nav__item:last-child{padding-bottom:1.2rem}.md-nav__item .md-nav__item{padding-right:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__button img{width:100%;height:auto}.md-nav__link{display:block;margin-top:.625em;-webkit-transition:color .125s;transition:color .125s;text-overflow:ellipsis;cursor:pointer;overflow:hidden}.md-nav__item--nested>.md-nav__link:after{content:"keyboard_arrow_down"}html .md-nav__link[for=toc],html .md-nav__link[for=toc]+.md-nav__link:after,html .md-nav__link[for=toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:rgba(0,0,0,.54)}.md-nav__item--active>.md-nav__link,.md-nav__link:active{color:#3f51b5}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:#536dfe}.md-nav__source,.no-js .md-search{display:none}.md-search__overlay{display:none;pointer-events:none}.md-search__inner{width:100%}.md-search__form{position:relative}.md-search__input{position:relative;padding:0 1.6rem 0 7.2rem;text-overflow:ellipsis;z-index:1}.md-search__input+.md-search__icon,.md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}.md-search__input+.md-search__icon,.md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}.md-search__input+.md-search__icon,.md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}.md-search__input+.md-search__icon,.md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__input::-ms-clear{display:none}.md-search__icon{position:absolute;top:.8rem;left:1.2rem;-webkit-transition:color .25s;transition:color .25s;font-size:2.4rem;cursor:pointer;z-index:1}.md-search__icon:before{content:"search"}.md-search__output{position:absolute;width:100%;border-radius:0 0 .2rem .2rem;overflow:hidden}.md-search__scrollwrap{height:100%;background:-webkit-linear-gradient(top,#fff 10%,hsla(0,0%,100%,0)),-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background:linear-gradient(180deg,#fff 10%,hsla(0,0%,100%,0)),linear-gradient(180deg,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background-attachment:local,scroll;background-color:#fff;background-repeat:no-repeat;background-size:100% 2rem,100% 1rem;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07);overflow-y:auto;-webkit-overflow-scrolling:touch}.md-search-result__meta{padding:0 1.6rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-size:1.28rem;line-height:4rem}.md-search-result__list{margin:0;padding:0;border-top:.1rem solid rgba(0,0,0,.07);list-style:none}.md-search-result__item{box-shadow:0 -.1rem 0 rgba(0,0,0,.07)}.md-search-result__link{display:block;padding:0 1.6rem;-webkit-transition:background .25s;transition:background .25s;overflow:auto}.md-search-result__link:hover{background-color:rgba(83,109,254,.1)}.md-search-result__article{margin:1em 0}.md-search-result__title{margin-top:.5em;margin-bottom:0;color:rgba(0,0,0,.87);font-size:1.6rem;font-weight:400;line-height:1.4}.md-search-result__teaser{margin:.5em 0;color:rgba(0,0,0,.54);font-size:1.28rem;line-height:1.4;word-break:break-word}.md-sidebar{position:relative;width:24.2rem;padding:2.4rem 0;float:left;overflow:visible}.md-sidebar[data-md-state=lock]{position:fixed;top:5.6rem;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar--secondary{display:none}.md-sidebar__scrollwrap{max-height:100%;margin:0 .4rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}@-webkit-keyframes a{0%{height:0}to{height:1.3rem}}@keyframes a{0%{height:0}to{height:1.3rem}}@-webkit-keyframes b{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes b{0%{-webkit-transform:translateY(100%);transform:translateY(100%);opacity:0}50%{opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}.md-source{display:block;-webkit-transition:opacity .25s;transition:opacity .25s;font-size:1.3rem;line-height:1.2;white-space:nowrap}.md-source:hover{opacity:.7}.md-source:after,.md-source__icon{display:inline-block;height:4.8rem;content:"";vertical-align:middle}.md-source__icon{width:4.8rem}.md-source__icon svg{margin-top:1.2rem;margin-left:1.2rem}.md-source__icon+.md-source__repository{margin-left:-4.4rem;padding-left:4rem}.md-source__repository{display:inline-block;max-width:100%;margin-left:1.2rem;font-weight:700;text-overflow:ellipsis;overflow:hidden;vertical-align:middle}.md-source__facts{margin:0;padding:0;font-size:1.1rem;font-weight:700;list-style-type:none;opacity:.75;overflow:hidden}[data-md-state=done] .md-source__facts{-webkit-animation:a .25s ease-in;animation:a .25s ease-in}.md-source__fact{float:left}[data-md-state=done] .md-source__fact{-webkit-animation:b .4s ease-out;animation:b .4s ease-out}.md-source__fact:before{margin:0 .2rem;content:"\00B7"}.md-source__fact:first-child:before{display:none}.md-tabs{width:100%;-webkit-transition:background .25s;transition:background .25s;background:rgba(50,64,144,.9675);overflow:auto}.md-tabs__list{margin:0;margin-left:.4rem;padding:0;list-style:none;white-space:nowrap}.md-tabs__item{display:inline-block;height:4.8rem;padding-right:1.2rem;padding-left:1.2rem}.md-tabs__link{display:block;margin-top:1.6rem;-webkit-transition:color .25s,opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:color .25s,opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);transition:color .25s,transform .4s cubic-bezier(.1,.7,.1,1),opacity .1s;transition:color .25s,transform .4s cubic-bezier(.1,.7,.1,1),opacity .1s,-webkit-transform .4s cubic-bezier(.1,.7,.1,1);color:hsla(0,0%,100%,.7);font-size:1.4rem}.md-tabs__link--active,.md-tabs__link:hover{color:#fff}.md-tabs__item:nth-child(2) .md-tabs__link{-webkit-transition-delay:.02s;transition-delay:.02s}.md-tabs__item:nth-child(3) .md-tabs__link{-webkit-transition-delay:.04s;transition-delay:.04s}.md-tabs__item:nth-child(4) .md-tabs__link{-webkit-transition-delay:.06s;transition-delay:.06s}.md-tabs__item:nth-child(5) .md-tabs__link{-webkit-transition-delay:.08s;transition-delay:.08s}.md-tabs__item:nth-child(6) .md-tabs__link{-webkit-transition-delay:.1s;transition-delay:.1s}.md-tabs__item:nth-child(7) .md-tabs__link{-webkit-transition-delay:.12s;transition-delay:.12s}.md-tabs__item:nth-child(8) .md-tabs__link{-webkit-transition-delay:.14s;transition-delay:.14s}.md-tabs__item:nth-child(9) .md-tabs__link{-webkit-transition-delay:.16s;transition-delay:.16s}.md-tabs__item:nth-child(10) .md-tabs__link{-webkit-transition-delay:.18s;transition-delay:.18s}.md-tabs__item:nth-child(11) .md-tabs__link{-webkit-transition-delay:.2s;transition-delay:.2s}.md-tabs__item:nth-child(12) .md-tabs__link{-webkit-transition-delay:.22s;transition-delay:.22s}.md-tabs__item:nth-child(13) .md-tabs__link{-webkit-transition-delay:.24s;transition-delay:.24s}.md-tabs__item:nth-child(14) .md-tabs__link{-webkit-transition-delay:.26s;transition-delay:.26s}.md-tabs__item:nth-child(15) .md-tabs__link{-webkit-transition-delay:.28s;transition-delay:.28s}.md-tabs__item:nth-child(16) .md-tabs__link{-webkit-transition-delay:.3s;transition-delay:.3s}.md-tabs[data-md-state=hidden]{background:#3f51b5}.md-tabs[data-md-state=hidden] .md-tabs__link{-webkit-transform:translateY(50%);transform:translateY(50%);-webkit-transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,opacity .1s,-webkit-transform 0s .4s;transition:color .25s,transform 0s .4s,opacity .1s;transition:color .25s,transform 0s .4s,opacity .1s,-webkit-transform 0s .4s;opacity:0}.admonition{position:relative;margin:1.5625em 0;padding:.8rem 1.2rem;border-left:3.2rem solid rgba(68,138,255,.4);border-radius:.2rem;background-color:rgba(68,138,255,.15);font-size:1.28rem}.admonition:before{position:absolute;left:-2.6rem;color:#fff;font-size:2rem;content:"edit";vertical-align:-.25em}.admonition :first-child{margin-top:0}.admonition :last-child{margin-bottom:0}.admonition.summary,.admonition.tldr{border-color:rgba(0,176,255,.4);background-color:rgba(0,176,255,.15)}.admonition.summary:before,.admonition.tldr:before{content:"subject"}.admonition.hint,.admonition.important,.admonition.tip{border-color:rgba(0,191,165,.4);background-color:rgba(0,191,165,.15)}.admonition.hint:before,.admonition.important:before,.admonition.tip:before{content:"whatshot"}.admonition.check,.admonition.done,.admonition.success{border-color:rgba(0,230,118,.4);background-color:rgba(0,230,118,.15)}.admonition.check:before,.admonition.done:before,.admonition.success:before{content:"done"}.admonition.attention,.admonition.caution,.admonition.warning{border-color:rgba(255,145,0,.4);background-color:rgba(255,145,0,.15)}.admonition.attention:before,.admonition.caution:before,.admonition.warning:before{content:"warning"}.admonition.fail,.admonition.failure,.admonition.missing{border-color:rgba(255,82,82,.4);background-color:rgba(255,82,82,.15)}.admonition.fail:before,.admonition.failure:before,.admonition.missing:before{content:"clear"}.admonition.danger,.admonition.error{border-color:rgba(255,23,68,.4);background-color:rgba(255,23,68,.15)}.admonition.danger:before,.admonition.error:before{content:"flash_on"}.admonition.bug{border-color:rgba(245,0,87,.4);background-color:rgba(245,0,87,.15)}.admonition.bug:before{content:"bug_report"}.admonition.cite,.admonition.quote{border-color:hsla(0,0%,62%,.4);background-color:hsla(0,0%,62%,.15)}.admonition.cite:before,.admonition.quote:before{content:"format_quote"}.admonition-title{font-weight:700}html .admonition-title{margin-bottom:0}html .admonition-title+*{margin-top:0}.codehilite .o,.codehilite .ow{color:inherit}.codehilite .ge{color:#000}.codehilite .gr{color:#a00}.codehilite .gh{color:#999}.codehilite .go{color:#888}.codehilite .gp{color:#555}.codehilite .gs{color:inherit}.codehilite .gu{color:#aaa}.codehilite .gt{color:#a00}.codehilite .gd{background-color:#fdd}.codehilite .gi{background-color:#dfd}.codehilite .k{color:#3b78e7}.codehilite .kc{color:#a71d5d}.codehilite .kd,.codehilite .kn{color:#3b78e7}.codehilite .kp{color:#a71d5d}.codehilite .kr,.codehilite .kt{color:#3e61a2}.codehilite .c,.codehilite .cm{color:#999}.codehilite .cp{color:#666}.codehilite .c1,.codehilite .ch,.codehilite .cs{color:#999}.codehilite .na,.codehilite .nb{color:#c2185b}.codehilite .bp{color:#3e61a2}.codehilite .nc{color:#c2185b}.codehilite .no{color:#3e61a2}.codehilite .nd,.codehilite .ni{color:#666}.codehilite .ne,.codehilite .nf{color:#c2185b}.codehilite .nl{color:#3b5179}.codehilite .nn{color:#ec407a}.codehilite .nt{color:#3b78e7}.codehilite .nv,.codehilite .vc,.codehilite .vg,.codehilite .vi{color:#3e61a2}.codehilite .nx{color:#ec407a}.codehilite .il,.codehilite .m,.codehilite .mf,.codehilite .mh,.codehilite .mi,.codehilite .mo{color:#e74c3c}.codehilite .s,.codehilite .sb,.codehilite .sc{color:#0d904f}.codehilite .sd{color:#999}.codehilite .s2{color:#0d904f}.codehilite .se,.codehilite .sh,.codehilite .si,.codehilite .sx{color:#183691}.codehilite .sr{color:#009926}.codehilite .s1,.codehilite .ss{color:#0d904f}.codehilite .err{color:#a61717}.codehilite .w{color:transparent}.codehilite .hll{display:block;margin:0 -1.2rem;padding:0 1.2rem;background-color:rgba(255,235,59,.5)}.md-typeset .codehilite{margin:1em 0;padding:1rem 1.2rem .8rem;border-radius:.2rem;background-color:hsla(0,0%,93%,.5);color:#37474f;line-height:1.4;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .codehilite::-webkit-scrollbar{width:.4rem;height:.4rem}.md-typeset .codehilite::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-typeset .codehilite::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-typeset .codehilite pre{display:inline-block;min-width:100%;margin:0;padding:0;background-color:transparent;overflow:visible;vertical-align:top}.md-typeset .codehilitetable{display:block;margin:1em 0;border-radius:.2em;font-size:1.6rem;overflow:hidden}.md-typeset .codehilitetable tbody,.md-typeset .codehilitetable td{display:block;padding:0}.md-typeset .codehilitetable tr{display:-webkit-box;display:-ms-flexbox;display:flex}.md-typeset .codehilitetable .codehilite,.md-typeset .codehilitetable .linenodiv{margin:0;border-radius:0}.md-typeset .codehilitetable .linenodiv{padding:1rem 1.2rem .8rem}.md-typeset .codehilitetable .linenodiv,.md-typeset .codehilitetable .linenodiv>pre{height:100%}.md-typeset .codehilitetable .linenos{background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.26);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .codehilitetable .linenos pre{margin:0;padding:0;background-color:transparent;color:inherit;text-align:right}.md-typeset .codehilitetable .code{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden}.md-typeset>.codehilitetable{box-shadow:none}.md-typeset .footnote{color:rgba(0,0,0,.54);font-size:1.28rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{-webkit-transition:color .25s;transition:color .25s}.md-typeset .footnote li:before{display:block;height:0}.md-typeset .footnote li:target{color:rgba(0,0,0,.87)}.md-typeset .footnote li:target:before{margin-top:-9rem;padding-top:9rem;pointer-events:none}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{-webkit-transform:translateX(0);transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover,.md-typeset .footnote li:target .footnote-backref{color:#536dfe}.md-typeset .footnote-backref{display:inline-block;-webkit-transform:translateX(.5rem);transform:translateX(.5rem);-webkit-transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:color .25s,opacity .125s .125s,-webkit-transform .25s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s;transition:transform .25s .125s,color .25s,opacity .125s .125s,-webkit-transform .25s .125s;color:rgba(0,0,0,.26);font-size:0;opacity:0;vertical-align:text-bottom}.md-typeset .footnote-backref:before{font-size:1.6rem;content:"keyboard_return"}.md-typeset .headerlink{display:inline-block;margin-left:1rem;-webkit-transform:translateY(.5rem);transform:translateY(.5rem);-webkit-transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:color .25s,opacity .125s .25s,-webkit-transform .25s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s;transition:transform .25s .25s,color .25s,opacity .125s .25s,-webkit-transform .25s .25s;opacity:0}html body .md-typeset .headerlink{color:rgba(0,0,0,.26)}.md-typeset [id]:before{display:inline-block;content:""}.md-typeset [id]:target:before{margin-top:-9.8rem;padding-top:9.8rem}.md-typeset [id] .headerlink:focus,.md-typeset [id]:hover .headerlink,.md-typeset [id]:target .headerlink{-webkit-transform:translate(0);transform:translate(0);opacity:1}.md-typeset [id] .headerlink:focus,.md-typeset [id]:hover .headerlink:hover,.md-typeset [id]:target .headerlink{color:#536dfe}.md-typeset h1[id]{padding-top:.8rem}.md-typeset h1[id] .headerlink{display:none}.md-typeset h2[id]:before{display:block;margin-top:-.4rem;padding-top:.4rem}.md-typeset h2[id]:target:before{margin-top:-8.4rem;padding-top:8.4rem}.md-typeset h3[id]:before{display:block;margin-top:-.7rem;padding-top:.7rem}.md-typeset h3[id]:target:before{margin-top:-8.7rem;padding-top:8.7rem}.md-typeset h4[id]:before{display:block;margin-top:-.8rem;padding-top:.8rem}.md-typeset h4[id]:target:before{margin-top:-8.8rem;padding-top:8.8rem}.md-typeset h5[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem}.md-typeset h5[id]:target:before{margin-top:-9.1rem;padding-top:9.1rem}.md-typeset h6[id]:before{display:block;margin-top:-1.1rem;padding-top:1.1rem}.md-typeset h6[id]:target:before{margin-top:-9.1rem;padding-top:9.1rem}.md-typeset .MJXc-display{margin:.75em 0;padding:.25em 0;overflow:auto;-webkit-overflow-scrolling:touch}.md-typeset .MathJax_CHTML{outline:0}.md-typeset .comment.critic,.md-typeset del.critic,.md-typeset ins.critic{margin:0 .25em;padding:.0625em 0;border-radius:.2rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:#fdd;box-shadow:.25em 0 0 #fdd,-.25em 0 0 #fdd}.md-typeset ins.critic{background-color:#dfd;box-shadow:.25em 0 0 #dfd,-.25em 0 0 #dfd}.md-typeset .critic.comment{background-color:hsla(0,0%,93%,.5);color:#37474f;box-shadow:.25em 0 0 hsla(0,0%,93%,.5),-.25em 0 0 hsla(0,0%,93%,.5)}.md-typeset .critic.comment:before{padding-right:.125em;color:rgba(0,0,0,.26);content:"chat";vertical-align:-.125em}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:1.6rem;padding-left:1.6rem;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}.md-typeset .emojione{width:2rem;vertical-align:text-top}.md-typeset code.codehilite{margin:0 .29412em;padding:.07353em 0}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}.md-typeset .task-list-control .task-list-indicator:before{position:absolute;top:.05em;left:-1.25em;color:rgba(0,0,0,.26);font-size:1.5em;content:"check_box_outline_blank";vertical-align:-.25em}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator:before{content:"check_box"}.md-typeset .task-list-control [type=checkbox]{opacity:0;z-index:-1}@media print{.md-typeset a:after{color:rgba(0,0,0,.54);content:" [" attr(href) "]"}.md-typeset code{box-shadow:none;-webkit-box-decoration-break:initial;box-decoration-break:slice}.md-content__edit,.md-footer,.md-header,.md-sidebar,.md-typeset .headerlink{display:none}}@media only screen and (max-width:44.9375em){.md-typeset pre{margin:1em -1.6rem;padding:1rem 1.6rem;border-radius:0}.md-footer-nav__link--prev .md-footer-nav__title{display:none}.codehilite .hll{margin:0 -1.6rem;padding:0 1.6rem}.md-typeset>.codehilite{padding:1rem 1.6rem .8rem}.md-typeset>.codehilite,.md-typeset>.codehilitetable{margin:1em -1.6rem;border-radius:0}.md-typeset>.codehilitetable .codehilite,.md-typeset>.codehilitetable .linenodiv{padding:1rem 1.6rem}.md-typeset>p>.MJXc-display{margin:.75em -1.6rem;padding:.25em 1.6rem}}@media only screen and (min-width:100em){html{font-size:68.75%}}@media only screen and (min-width:125em){html{font-size:75%}}@media only screen and (max-width:59.9375em){body[data-md-state=lock]{overflow:hidden}.ios body[data-md-state=lock] .md-container{display:none}.md-content__edit{margin-right:-.8rem}.md-nav--secondary{border-left:0}html .md-nav__link[for=toc]{display:block;padding-right:4.8rem}html .md-nav__link[for=toc]:after{color:inherit;content:"toc"}html .md-nav__link[for=toc]+.md-nav__link{display:none}html .md-nav__link[for=toc]~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}.md-nav__source{display:block;padding:.4rem;background-color:rgba(50,64,144,.9675);color:#fff}.md-search__overlay{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;-webkit-transform-origin:center;transform-origin:center;-webkit-transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:opacity .2s .2s,-webkit-transform .3s .1s;transition:transform .3s .1s,opacity .2s .2s;transition:transform .3s .1s,opacity .2s .2s,-webkit-transform .3s .1s;border-radius:2rem;background-color:#fff;opacity:0;overflow:hidden;z-index:1}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transition:opacity .1s,-webkit-transform .4s;transition:opacity .1s,-webkit-transform .4s;transition:transform .4s,opacity .1s;transition:transform .4s,opacity .1s,-webkit-transform .4s;opacity:1}.md-search__inner{position:fixed;top:0;left:100%;height:100%;-webkit-transform:translateX(5%);transform:translateX(5%);-webkit-transition:left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:left 0s .3s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;transition:left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s;transition:left 0s .3s,transform .15s cubic-bezier(.4,0,.2,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.4,0,.2,1) .15s;opacity:0;z-index:2}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:left 0s 0s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;transition:left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s;transition:left 0s 0s,transform .15s cubic-bezier(.1,.7,.1,1) .15s,opacity .15s .15s,-webkit-transform .15s cubic-bezier(.1,.7,.1,1) .15s;opacity:1}.md-search__input{width:100%;height:5.6rem;font-size:1.8rem}.md-search__icon{top:1.6rem;left:1.6rem}.md-search__icon:before{content:"arrow_back"}.md-search__output{top:5.6rem;bottom:0}}@media only screen and (max-width:76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;-webkit-transition:width 0s,height 0s,opacity .25s;transition:width 0s,height 0s,opacity .25s;opacity:1}.md-header-nav__button.md-icon--home,.md-header-nav__button.md-logo{display:none}.md-nav{background-color:#fff}.md-nav--primary,.md-nav--primary .md-nav{display:-webkit-box;display:-ms-flexbox;display:flex;position:absolute;top:0;right:0;left:0;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%;z-index:1}.md-nav--primary .md-nav__item,.md-nav--primary .md-nav__title{font-size:1.6rem;line-height:1.5}html .md-nav--primary .md-nav__title{position:relative;height:11.2rem;padding:6rem 1.6rem .4rem;background-color:rgba(0,0,0,.07);color:rgba(0,0,0,.54);font-weight:400;line-height:4.8rem;white-space:nowrap;cursor:pointer}html .md-nav--primary .md-nav__title:before{display:block;position:absolute;top:.4rem;left:.4rem;width:4rem;height:4rem;color:rgba(0,0,0,.54)}html .md-nav--primary .md-nav__title~.md-nav__list{background:-webkit-linear-gradient(top,#fff 10%,hsla(0,0%,100%,0)),-webkit-linear-gradient(top,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background:linear-gradient(180deg,#fff 10%,hsla(0,0%,100%,0)),linear-gradient(180deg,rgba(0,0,0,.26),rgba(0,0,0,.07) 35%,transparent 60%);background-attachment:local,scroll;background-color:#fff;background-repeat:no-repeat;background-size:100% 2rem,100% 1rem;box-shadow:inset 0 .1rem 0 rgba(0,0,0,.07)}html .md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}html .md-nav--primary .md-nav__title--site{position:relative;background-color:#3f51b5;color:#fff}html .md-nav--primary .md-nav__title--site .md-nav__button{display:block;position:absolute;top:.4rem;left:.4rem;width:6.4rem;height:6.4rem;font-size:4.8rem}html .md-nav--primary .md-nav__title--site:before{display:none}.md-nav--primary .md-nav__list{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow-y:auto}.md-nav--primary .md-nav__item{padding:0;border-top:.1rem solid rgba(0,0,0,.07)}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:4.8rem}.md-nav--primary .md-nav__item--nested>.md-nav__link:after{content:"keyboard_arrow_right"}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:1.6rem}.md-nav--primary .md-nav__link:after{position:absolute;top:50%;right:1.2rem;margin-top:-1.2rem;color:rgba(0,0,0,.54);font-size:2.4rem}.md-nav--primary .md-nav__link:focus:after,.md-nav--primary .md-nav__link:hover:after{color:inherit}.md-nav--primary .md-nav--secondary .md-nav{position:static}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:2.8rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:4rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:5.2rem}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:6.4rem}.md-nav__toggle~.md-nav{display:none}.csstransforms3d .md-nav__toggle~.md-nav{-webkit-transform:translateX(100%);transform:translateX(100%);-webkit-transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s;transition:transform .25s cubic-bezier(.8,0,.6,1),opacity .125s .05s,-webkit-transform .25s cubic-bezier(.8,0,.6,1);opacity:0}.csstransforms3d .md-nav__toggle~.md-nav,.md-nav__toggle:checked~.md-nav{display:-webkit-box;display:-ms-flexbox;display:flex}.csstransforms3d .md-nav__toggle:checked~.md-nav{-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s;transition:transform .25s cubic-bezier(.4,0,.2,1),opacity .125s .125s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);opacity:1}.md-sidebar--primary{position:fixed;top:0;left:-24.2rem;width:24.2rem;height:100%;-webkit-transform:translateX(0);transform:translateX(0);-webkit-transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s;transition:transform .25s cubic-bezier(.4,0,.2,1),box-shadow .25s,-webkit-transform .25s cubic-bezier(.4,0,.2,1);background-color:#fff;z-index:2}.no-csstransforms3d .md-sidebar--primary{display:none}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);-webkit-transform:translateX(24.2rem);transform:translateX(24.2rem)}.no-csstransforms3d [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{display:block}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;margin:0}.md-tabs{display:none}}@media only screen and (min-width:60em){.md-content{margin-right:24.2rem}.md-header-nav__button.md-icon--search{display:none}.md-header-nav__source{display:block;width:23rem;max-width:23rem;padding-right:1.2rem}.md-search{margin-right:2.8rem;padding:.4rem}.md-search__inner{display:table;position:relative;clear:both}.md-search__form{width:23rem;float:right;-webkit-transition:width .25s cubic-bezier(.1,.7,.1,1);transition:width .25s cubic-bezier(.1,.7,.1,1);border-radius:.2rem}.md-search__input{width:100%;height:4rem;padding-left:4.8rem;-webkit-transition:background-color .25s,color .25s;transition:background-color .25s,color .25s;border-radius:.2rem;background-color:rgba(0,0,0,.26);color:#fff;font-size:1.6rem}.md-search__input+.md-search__icon,.md-search__input::-webkit-input-placeholder{-webkit-transition:color .25s;transition:color .25s;color:#fff}.md-search__input+.md-search__icon,.md-search__input::-moz-placeholder{-webkit-transition:color .25s;transition:color .25s;color:#fff}.md-search__input+.md-search__icon,.md-search__input:-ms-input-placeholder{-webkit-transition:color .25s;transition:color .25s;color:#fff}.md-search__input+.md-search__icon,.md-search__input::placeholder{-webkit-transition:color .25s;transition:color .25s;color:#fff}.md-search__input:hover{background-color:hsla(0,0%,100%,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{border-radius:.2rem .2rem 0 0;background-color:#fff;color:rgba(0,0,0,.87);text-overflow:none}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input:-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:rgba(0,0,0,.54)}.md-search__output{top:4rem;-webkit-transition:opacity .4s;transition:opacity .4s;opacity:0}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}.md-search__scrollwrap{max-height:0}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap::-webkit-scrollbar{width:.4rem;height:.4rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.26)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:#536dfe}.md-search-result__link,.md-search-result__meta{padding-left:4.8rem}.md-sidebar--secondary{display:block;float:right}.md-sidebar--secondary[data-md-state=lock]{margin-left:100%;-webkit-transform:translate(-100%);transform:translate(-100%)}}@media only screen and (min-width:76.25em){.md-content{margin-left:24.2rem;overflow:auto}.md-content__inner{margin:2.4rem}.md-header{box-shadow:none}.md-header[data-md-state=shadow]{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2)}.md-header-nav__button.md-icon--menu{display:none}.md-nav[data-md-state=animate]{-webkit-transition:max-height .25s cubic-bezier(.86,0,.07,1);transition:max-height .25s cubic-bezier(.86,0,.07,1)}.md-nav__toggle~.md-nav{max-height:0;overflow:hidden}.md-nav[data-md-state=expand],.md-nav__toggle:checked~.md-nav{max-height:100%}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__item--nested>.md-nav__link:after{display:inline-block;-webkit-transform-origin:.45em .45em;transform-origin:.45em .45em;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;vertical-align:-.125em}.js .md-nav__item--nested>.md-nav__link:after{-webkit-transition:-webkit-transform .4s;transition:-webkit-transform .4s;transition:transform .4s;transition:transform .4s,-webkit-transform .4s}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link:after{-webkit-transform:rotateX(180deg);transform:rotateX(180deg)}.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__form{width:68.8rem}.md-sidebar__inner{border-right:.1rem solid rgba(0,0,0,.07)}.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{font-size:0}.md-tabs--active~.md-main .md-nav--primary .md-nav__title--site{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{font-size:0}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none;font-size:1.4rem;overflow:auto}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link{margin-top:1.2rem;font-weight:700;pointer-events:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested>.md-nav__link:after{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{max-height:none}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding-left:0}}@media only screen and (max-width:29.9375em){.md-header-nav__parent{display:none}[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(45);transform:scale(45)}}@media only screen and (min-width:45em){.md-footer-nav__link{width:50%}.md-footer-copyright{max-width:75%;float:left}.md-footer-social{padding:1.2rem 0;float:right}}@media only screen and (min-width:30em) and (max-width:44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(60);transform:scale(60)}}@media only screen and (min-width:45em) and (max-width:59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{-webkit-transform:scale(75);transform:scale(75)}}@media only screen and (min-width:60em) and (max-width:76.1875em){.md-search__scrollwrap,[data-md-toggle=search]:checked~.md-header .md-search__form{width:46.8rem}}@media only screen and (min-width:60em) and (min-width:76.25em){.md-sidebar--secondary[data-md-state=lock]{margin-left:122rem}} \ No newline at end of file diff --git a/site/first_steps/index.html b/site/first_steps/index.html new file mode 100644 index 000000000..81f7718cd --- /dev/null +++ b/site/first_steps/index.html @@ -0,0 +1,874 @@ + + + + + + + + + + + + + + + + + + First Steps - mailcow: dockerized + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + +
+
+ + edit + + + +

First Steps

+ +

SSL (and: How to use Let's Encrypt)

+

mailcow dockerized comes with a snakeoil CA "mailcow" and a server certificate in data/assets/ssl. Please use your own trusted certificates.

+

mailcow uses 3 domain names that should be covered by your new certificate:

+
    +
  • ${MAILCOW_HOSTNAME}
  • +
  • autodiscover.example.org
  • +
  • autoconfig.example.org
  • +
+

Obtain multi-SAN certificate by Let's Encrypt

+

This is just an example of how to obtain certificates with certbot. There are several methods!

+

1. Get the certbot client:

+
wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot && chmod +x /usr/local/sbin/certbot
+
+ + +

2. Make sure you set HTTP_BIND=0.0.0.0 and HTTP_PORT=80 in mailcow.conf or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then rebuild Nginx:

+
docker-compose up -d
+
+ + +

3. Request the certificate with the webroot method:

+
cd /path/to/git/clone/mailcow-dockerized
+source mailcow.conf
+certbot certonly \
+    --webroot \
+    -w ${PWD}/data/web \
+    -d ${MAILCOW_HOSTNAME} \
+    -d autodiscover.example.org \
+    -d autoconfig.example.org \
+    --email you@example.org \
+    --agree-tos
+
+ + +

Remember to replace the example.org domain with your own domain, this command will not work if you dont.

+

4. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:

+
mv data/assets/ssl/cert.{pem,pem.backup}
+mv data/assets/ssl/key.{pem,pem.backup}
+ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/fullchain.pem) data/assets/ssl/cert.pem
+ln $(readlink -f /etc/letsencrypt/live/${MAILCOW_HOSTNAME}/privkey.pem) data/assets/ssl/key.pem
+
+ + +

5. Restart affected containers:

+
docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow
+
+ + +

When renewing certificates, run the last two steps (link + restart) as post-hook in a script.

+

Rspamd Web UI

+

At first you may want to setup Rspamds web interface which provides some useful features and information.

+

1. Generate a Rspamd controller password hash:

+
docker-compose exec rspamd-mailcow rspamadm pw
+
+ + +

2. Replace the default hash in data/conf/rspamd/override.d/worker-controller.inc by your newly generated:

+
enable_password = "myhash";
+
+ + +

You can use password = "myhash"; instead of enable_password to disable write-access in the web UI.

+

3. Restart rspamd:

+
docker-compose restart rspamd-mailcow
+
+ + +

Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!

+

Optional: Reverse proxy

+

You don't need to change the Nginx site that comes with mailcow: dockerized. +mailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. This is very important to control access to Rspamd's web UI.

+

1. Make sure you change HTTP_BIND and HTTPS_BIND in mailcow.conf to a local address and set the ports accordingly, for example:

+
HTTP_BIND=127.0.0.1
+HTTP_PORT=8080
+HTTPS_PORT=127.0.0.1
+HTTPS_PORT=8443
+
+ + +

IMPORTANT: Do not use port 8081

+

Recreate affected containers by running docker-compose up -d.

+

2. Configure your local webserver as reverse proxy:

+

Apache 2.4

+
<VirtualHost *:443>
+    ServerName mail.example.org
+    ServerAlias autodiscover.example.org
+    ServerAlias autoconfig.example.org
+
+    [...]
+    # You should proxy to a plain HTTP session to offload SSL processing
+    ProxyPass / http://127.0.0.1:8080/
+    ProxyPassReverse / http://127.0.0.1:8080/
+    ProxyPreserveHost Off
+    your-ssl-configuration-here
+    [...]
+
+    # If you plan to proxy to a HTTPS host:
+    #SSLProxyEngine On
+
+    # If you plan to proxy to an untrusted HTTPS host:
+    #SSLProxyVerify none
+    #SSLProxyCheckPeerCN off
+    #SSLProxyCheckPeerName off
+    #SSLProxyCheckPeerExpire off
+</VirtualHost>
+
+ + +

Nginx

+
server {
+    listen 443;
+    server_name mail.example.org autodiscover.example.org autoconfig.example.org;
+
+    [...]
+    your-ssl-configuration-here
+    location / {
+        proxy_pass http://127.0.0.1:8080/;
+        proxy_redirect http://127.0.0.1:8080/ $scheme://$host:$server_port/;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+    [...]
+}
+
+ + +

Optional: Setup a relayhost

+

Insert these lines to data/conf/postfix/main.cf. "relayhost" does already exist (empty), just change its value.

+
relayhost = [your-relayhost]:587
+smtp_sasl_password_maps = hash:/opt/postfix/conf/smarthost_passwd
+smtp_sasl_auth_enable = yes
+
+ + +

Create the credentials file:

+
echo "your-relayhost username:password" > data/conf/postfix/smarthost_passwd
+
+ + +

Run:

+
docker-compose exec postfix-mailcow postmap /opt/postfix/conf/smarthost_passwd
+docker-compose exec postfix-mailcow chown root:postfix /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db
+docker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db
+docker-compose exec postfix-mailcow postfix reload
+
+ + +

Helper script

+

There is a helper script mailcow-setup-relayhost.sh you can run to setup a relayhost.

+
Usage:
+
+Setup a relayhost:
+./mailcow-setup-relayhost.sh relayhost port (username) (password)
+Username and password are optional parameters.
+
+Reset to defaults:
+./mailcow-setup-relayhost.sh reset
+
+ + +

Optional: Log to Syslog

+

Enable Rsyslog to receive logs on 524/tcp:

+
# This setting depends on your Rsyslog version and configuration format.
+# For most Debian derivates it will work like this...
+$ModLoad imtcp
+$TCPServerAddress 127.0.0.1
+$InputTCPServerRun 524
+
+# ...while for Ubuntu 16.04 it looks like this:
+module(load="imtcp")
+input(type="imtcp" address="127.0.0.1" port="524")
+
+# No matter your Rsyslog version, you should set this option to off
+# if you plan to use Fail2ban
+$RepeatedMsgReduction off
+
+ + +

Restart rsyslog after enabling the TCP listener.

+

Now setup Docker daemon to start with the syslog driver. +This enables the syslog driver for all containers!

+

Debian users can change the startup configuration in /etc/default/docker while CentOS users find it in /etc/sysconfig/docker:

+
...
+DOCKER_OPTS="--log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524"
+...
+
+ + +

Caution: For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters.

+

Just run systemctl edit docker.service and add the following content to fix it.

+

Note: If "systemctl edit" is not available, just copy the content to /etc/systemd/system/docker.service.d/override.conf.

+

The first empty ExecStart parameter is not a mistake.

+
[Service]
+EnvironmentFile=/etc/default/docker
+ExecStart=
+ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS
+
+ + +

Restart the Docker daemon and run docker-compose down && docker-compose up -d to recreate the containers.

+

Use Fail2ban

+

This is a subsection of "Log to Syslog", which is required for Fail2ban to work.

+

Open /etc/fail2ban/filter.d/common.conf and search for the prefix_line parameter, change it to ".*":

+
__prefix_line = .*
+
+ + +

Create /etc/fail2ban/jail.d/dovecot.conf...

+
[dovecot]
+enabled = true
+filter  = dovecot
+logpath = /var/log/syslog
+chain = FORWARD
+
+ + +

and jail.d/postfix-sasl.conf:

+
[postfix-sasl]
+enabled = true
+filter  = postfix-sasl
+logpath = /var/log/syslog
+chain = FORWARD
+
+ + +

Restart Fail2ban.

+

Install a local MTA

+

The easiest option would be to disable the listener on port 25/tcp.

+

Postfix users disable the listener by commenting the following line (starting with smtp or 25) in /etc/postfix/master.cf:

+
#smtp      inet  n       -       -       -       -       smtpd
+
+ + +

Restart Postfix after applying your changes.

+

Sender and receiver model

+

When a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address.

+
Mailbox me@example.org is created. example.org is a primary domain. 
+Note: a mailbox cannot be created in an alias domain.
+
+me@example.org is only known as me@example.org.
+me@example.org is allowed to send as me@example.org.
+
+ + +

We can add an alias domain for example.org:

+
Alias domain alias.com is added and assigned to primary domain example.org.
+me@example.org is now known as me@example.org and me@alias.com.
+me@example.org is now allowed to send as me@example.org and me@alias.com.
+
+ + +

We can add aliases for a mailbox to receive mail for and to send from this new address.

+

It is important to know, that you are not able to receive mail for my-alias@my-alias-domain.tld. You would need to create this particular alias.

+
me@example.org is assigned the alias alias@example.org
+me@example.org is now known as alias@example.org, me@alias.com, alias@example.org
+
+me@example.org is NOT known as alias@alias.com.
+
+ + +

Administrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users ("delegate" them).

+

You can choose between mailbox users or completely disable the sender check for domains.

+

SOGo "mail from" addresses

+

Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains.

+

If you want to select another existing mailbox user as your "mail from" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator +needs to grant you access as described above.

+ + +
+
+
+
+ + + + +
+ + + + + + + + + + \ No newline at end of file diff --git a/site/images/logo.svg b/site/images/logo.svg new file mode 100644 index 000000000..ea3b2796b --- /dev/null +++ b/site/images/logo.svg @@ -0,0 +1,179 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/site/index.html b/site/index.html new file mode 100644 index 000000000..1a8693f95 --- /dev/null +++ b/site/index.html @@ -0,0 +1,428 @@ + + + + + + + + + + + + + + + + + + mailcow: dockerized + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + edit + + + +

mailcow: dockerized - 🐮 + 🐋 = 💕

+

Servercow

+

If you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow!

+

Screenshots

+

You can find screenshots on Imgur.

+

Overview

+

mailcow dockerized comes with 12 containers linked in one bridged network. +Each container represents a single application.

+
    +
  • Dovecot
  • +
  • ClamAV
  • +
  • Memcached
  • +
  • Redis
  • +
  • MySQL
  • +
  • Bind9 (Resolver) (formerly PDNS Recursor)
  • +
  • PHP-FPM
  • +
  • Postfix
  • +
  • Nginx
  • +
  • Rmilter
  • +
  • Rspamd
  • +
  • SOGo
  • +
+

7 volumes to keep dynamic data - take care of them!

+
    +
  • vmail-vol-1
  • +
  • dkim-vol-1
  • +
  • redis-vol-1
  • +
  • mysql-vol-1
  • +
  • rspamd-vol-1
  • +
  • postfix-vol-1
  • +
  • crypt-vol-1
  • +
+

The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:

+
    +
  • DKIM key management
  • +
  • Black- and whitelists per domain and per user
  • +
  • Spam score managment per-user (reject spam, mark spam, greylist)
  • +
  • Allow mailbox users to create temporary spam aliases
  • +
  • Prepend mail tags to subject or move mail to subfolder (per-user)
  • +
  • Allow mailbox users to toggle incoming and outgoing TLS enforcement
  • +
  • Allow users to reset SOGo ActiveSync device caches
  • +
  • imapsync to migrate or pull remote mailboxes regularly
  • +
  • TFA: Yubi OTP and U2F USB (Google Chrome and derivates only)
  • +
  • Add domains, mailboxes, aliases, domain aliases and SOGo resources
  • +
  • Add whitelisted hosts to forward mail to mailcow
  • +
+

Looking for a farm to host your cow?

+ + +
+
+
+
+ + + + +
+ + + + + + + + + + \ No newline at end of file diff --git a/site/install/index.html b/site/install/index.html new file mode 100644 index 000000000..5b9d64c21 --- /dev/null +++ b/site/install/index.html @@ -0,0 +1,512 @@ + + + + + + + + + + + + + + + + + + Installation - mailcow: dockerized + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + edit + + + +

Installation

+ +

Install mailcow

+

WARNING: Please use Ubuntu 16.04 instead of Debian 8 or switch to the kernel 4.9 from jessie backports because there is a bug (kernel panic) with the kernel 3.16 when running docker containers with healthchecks! Full details here: github.com/docker/docker/issues/30402 and forum.mailcow.email/t/solved-mailcow-docker-causes-kernel-panic-edit/448

+

You need Docker and Docker Compose.

+

1. Learn how to install Docker and Docker Compose.

+

Quick installation for most operation systems:

+
    +
  • Docker
  • +
+
curl -sSL https://get.docker.com/ | sh
+
+ + +
    +
  • Docker-Compose
  • +
+
curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
+chmod +x /usr/local/bin/docker-compose
+
+ + +

Please use the latest Docker engine available and do not use the engine that ships with your distros repository.

+

2. Clone the master branch of the repository

+
git clone https://github.com/andryyy/mailcow-dockerized && cd mailcow-dockerized
+
+ + +

3. Generate a configuration file. Use a FQDN (host.domain.tld) as hostname when asked.

+
./generate_config.sh
+
+ + +

4. Change configuration if you want or need to.

+
nano mailcow.conf
+
+ + +

If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080.

+

You may need to stop an existing pre-installed MTA which blocks port 25/tcp. See this chapter to learn how to reconfigure Postfix to run besides mailcow after a successful installation.

+

5. Pull the images and run the composer file. The paramter -d will start mailcow: dockerized detached:

+
docker-compose pull
+docker-compose up -d
+
+ + +

Done!

+

You can now access https://${MAILCOW_HOSTNAME} with the default credentials admin + password moohoo.

+

The database will be initialized right after a connection to MySQL can be established.

+

Update mailcow

+

There is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes.

+

Step 1, method 1

+

Stash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them:

+
# Stash local changes
+git stash
+# Re-pull master
+git pull
+# Apply stash and remove it
+git stash pop
+
+ + +

Step 1, method 2

+

Fetch new data from GitHub, commit changes and merge remote repository:

+
# Get updates/changes
+git fetch
+# Add all changed files to local clone
+git add -A
+# Commit changes, ignore git complaining about username and mail address
+git commit -m "Local config aat $(date)"
+# Merge changes
+git merge
+
+ + +

If git complains about conflicts, solve them! Example:

+
CONFLICT (content): Merge conflict in data/web/index.php
+
+ + +

Open data/web/index.php, solve the conflict, close the file and run git add -A + git commit -m "Solved conflict".

+

Step 1, method 3

+

Thanks to fabreg @ GitHub!

+

In case both methods do not work (for many reason like you're unable to fix the CONFLICTS or any other reasons) you can simply start all over again.

+

Keep in mind that all local changes to configuration files will be lost. However, your volumes will not be removed.

+
    +
  • Copy mailcow.conf somewhere outside the mailcow-dockerized directory
  • +
  • Stop and remove mailcow containers: docker-compose down
  • +
  • Delete the directory or rename it
  • +
  • Clone the remote repository again (git clone https://github.com/andryyy/mailcow-dockerized && cd mailcow-dockerized). Pay attention to this step - the folder must have the same name of the previous one!
  • +
  • Copy back your previous mailcow.conf into the mailcow-dockerizd folder
  • +
+

If you forgot to stop Docker before deleting the cloned directoy, you can use the following commands:

+
docker stop $(docker ps -a -q)
+docker rm $(docker ps -a -q)
+
+ + +

Step 2

+

Pull new images (if any) and recreate changed containers:

+
docker-compose pull
+docker-compose up -d --remove-orphans
+
+ + +

Step 3

+

Clean-up dangling (unused) images and volumes:

+
docker rmi -f $(docker images -f "dangling=true" -q)
+docker volume rm $(docker volume ls -qf dangling=true)
+
+ + +
+
+
+
+ + + + +
+ + + + + + + + + + \ No newline at end of file diff --git a/site/mkdocs/js/lunr.min.js b/site/mkdocs/js/lunr.min.js new file mode 100644 index 000000000..b0198dff9 --- /dev/null +++ b/site/mkdocs/js/lunr.min.js @@ -0,0 +1,7 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0 + * Copyright (C) 2016 Oliver Nightingale + * MIT Licensed + * @license + */ +!function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& +(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= +this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); +if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", +"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, +a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, +registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); +b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, +q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, +e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&& +!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), +s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== +b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); diff --git a/site/mkdocs/js/search-results-template.mustache b/site/mkdocs/js/search-results-template.mustache new file mode 100644 index 000000000..a8b3862f2 --- /dev/null +++ b/site/mkdocs/js/search-results-template.mustache @@ -0,0 +1,4 @@ + diff --git a/site/mkdocs/js/search.js b/site/mkdocs/js/search.js new file mode 100644 index 000000000..d5c866164 --- /dev/null +++ b/site/mkdocs/js/search.js @@ -0,0 +1,88 @@ +require([ + base_url + '/mkdocs/js/mustache.min.js', + base_url + '/mkdocs/js/lunr.min.js', + 'text!search-results-template.mustache', + 'text!../search_index.json', +], function (Mustache, lunr, results_template, data) { + "use strict"; + + function getSearchTerm() + { + var sPageURL = window.location.search.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) + { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == 'q') + { + return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); + } + } + } + + var index = lunr(function () { + this.field('title', {boost: 10}); + this.field('text'); + this.ref('location'); + }); + + data = JSON.parse(data); + var documents = {}; + + for (var i=0; i < data.docs.length; i++){ + var doc = data.docs[i]; + doc.location = base_url + doc.location; + index.add(doc); + documents[doc.location] = doc; + } + + var search = function(){ + + var query = document.getElementById('mkdocs-search-query').value; + var search_results = document.getElementById("mkdocs-search-results"); + while (search_results.firstChild) { + search_results.removeChild(search_results.firstChild); + } + + if(query === ''){ + return; + } + + var results = index.search(query); + + if (results.length > 0){ + for (var i=0; i < results.length; i++){ + var result = results[i]; + doc = documents[result.ref]; + doc.base_url = base_url; + doc.summary = doc.text.substring(0, 200); + var html = Mustache.to_html(results_template, doc); + search_results.insertAdjacentHTML('beforeend', html); + } + } else { + search_results.insertAdjacentHTML('beforeend', "

No results found

"); + } + + if(jQuery){ + /* + * We currently only automatically hide bootstrap models. This + * requires jQuery to work. + */ + jQuery('#mkdocs_search_modal a').click(function(){ + jQuery('#mkdocs_search_modal').modal('hide'); + }); + } + + }; + + var search_input = document.getElementById('mkdocs-search-query'); + + var term = getSearchTerm(); + if (term){ + search_input.value = term; + search(); + } + + search_input.addEventListener("keyup", search); + +}); diff --git a/site/mkdocs/js/text.js b/site/mkdocs/js/text.js new file mode 100644 index 000000000..17921b6e5 --- /dev/null +++ b/site/mkdocs/js/text.js @@ -0,0 +1,390 @@ +/** + * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/requirejs/text for details + */ +/*jslint regexp: true */ +/*global require, XMLHttpRequest, ActiveXObject, + define, window, process, Packages, + java, location, Components, FileUtils */ + +define(['module'], function (module) { + 'use strict'; + + var text, fs, Cc, Ci, xpcIsWindows, + progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], + xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, + bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, + hasLocation = typeof location !== 'undefined' && location.href, + defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), + defaultHostName = hasLocation && location.hostname, + defaultPort = hasLocation && (location.port || undefined), + buildMap = {}, + masterConfig = (module.config && module.config()) || {}; + + text = { + version: '2.0.12', + + strip: function (content) { + //Strips declarations so that external SVG and XML + //documents can be added to a document without worry. Also, if the string + //is an HTML document, only the part inside the body tag is returned. + if (content) { + content = content.replace(xmlRegExp, ""); + var matches = content.match(bodyRegExp); + if (matches) { + content = matches[1]; + } + } else { + content = ""; + } + return content; + }, + + jsEscape: function (content) { + return content.replace(/(['\\])/g, '\\$1') + .replace(/[\f]/g, "\\f") + .replace(/[\b]/g, "\\b") + .replace(/[\n]/g, "\\n") + .replace(/[\t]/g, "\\t") + .replace(/[\r]/g, "\\r") + .replace(/[\u2028]/g, "\\u2028") + .replace(/[\u2029]/g, "\\u2029"); + }, + + createXhr: masterConfig.createXhr || function () { + //Would love to dump the ActiveX crap in here. Need IE 6 to die first. + var xhr, i, progId; + if (typeof XMLHttpRequest !== "undefined") { + return new XMLHttpRequest(); + } else if (typeof ActiveXObject !== "undefined") { + for (i = 0; i < 3; i += 1) { + progId = progIds[i]; + try { + xhr = new ActiveXObject(progId); + } catch (e) {} + + if (xhr) { + progIds = [progId]; // so faster next time + break; + } + } + } + + return xhr; + }, + + /** + * Parses a resource name into its component parts. Resource names + * look like: module/name.ext!strip, where the !strip part is + * optional. + * @param {String} name the resource name + * @returns {Object} with properties "moduleName", "ext" and "strip" + * where strip is a boolean. + */ + parseName: function (name) { + var modName, ext, temp, + strip = false, + index = name.indexOf("."), + isRelative = name.indexOf('./') === 0 || + name.indexOf('../') === 0; + + if (index !== -1 && (!isRelative || index > 1)) { + modName = name.substring(0, index); + ext = name.substring(index + 1, name.length); + } else { + modName = name; + } + + temp = ext || modName; + index = temp.indexOf("!"); + if (index !== -1) { + //Pull off the strip arg. + strip = temp.substring(index + 1) === "strip"; + temp = temp.substring(0, index); + if (ext) { + ext = temp; + } else { + modName = temp; + } + } + + return { + moduleName: modName, + ext: ext, + strip: strip + }; + }, + + xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, + + /** + * Is an URL on another domain. Only works for browser use, returns + * false in non-browser environments. Only used to know if an + * optimized .js version of a text resource should be loaded + * instead. + * @param {String} url + * @returns Boolean + */ + useXhr: function (url, protocol, hostname, port) { + var uProtocol, uHostName, uPort, + match = text.xdRegExp.exec(url); + if (!match) { + return true; + } + uProtocol = match[2]; + uHostName = match[3]; + + uHostName = uHostName.split(':'); + uPort = uHostName[1]; + uHostName = uHostName[0]; + + return (!uProtocol || uProtocol === protocol) && + (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && + ((!uPort && !uHostName) || uPort === port); + }, + + finishLoad: function (name, strip, content, onLoad) { + content = strip ? text.strip(content) : content; + if (masterConfig.isBuild) { + buildMap[name] = content; + } + onLoad(content); + }, + + load: function (name, req, onLoad, config) { + //Name has format: some.module.filext!strip + //The strip part is optional. + //if strip is present, then that means only get the string contents + //inside a body tag in an HTML string. For XML/SVG content it means + //removing the declarations so the content can be inserted + //into the current doc without problems. + + // Do not bother with the work if a build and text will + // not be inlined. + if (config && config.isBuild && !config.inlineText) { + onLoad(); + return; + } + + masterConfig.isBuild = config && config.isBuild; + + var parsed = text.parseName(name), + nonStripName = parsed.moduleName + + (parsed.ext ? '.' + parsed.ext : ''), + url = req.toUrl(nonStripName), + useXhr = (masterConfig.useXhr) || + text.useXhr; + + // Do not load if it is an empty: url + if (url.indexOf('empty:') === 0) { + onLoad(); + return; + } + + //Load the text. Use XHR if possible and in a browser. + if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { + text.get(url, function (content) { + text.finishLoad(name, parsed.strip, content, onLoad); + }, function (err) { + if (onLoad.error) { + onLoad.error(err); + } + }); + } else { + //Need to fetch the resource across domains. Assume + //the resource has been optimized into a JS module. Fetch + //by the module name + extension, but do not include the + //!strip part to avoid file system issues. + req([nonStripName], function (content) { + text.finishLoad(parsed.moduleName + '.' + parsed.ext, + parsed.strip, content, onLoad); + }); + } + }, + + write: function (pluginName, moduleName, write, config) { + if (buildMap.hasOwnProperty(moduleName)) { + var content = text.jsEscape(buildMap[moduleName]); + write.asModule(pluginName + "!" + moduleName, + "define(function () { return '" + + content + + "';});\n"); + } + }, + + writeFile: function (pluginName, moduleName, req, write, config) { + var parsed = text.parseName(moduleName), + extPart = parsed.ext ? '.' + parsed.ext : '', + nonStripName = parsed.moduleName + extPart, + //Use a '.js' file name so that it indicates it is a + //script that can be loaded across domains. + fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; + + //Leverage own load() method to load plugin value, but only + //write out values that do not have the strip argument, + //to avoid any potential issues with ! in file names. + text.load(nonStripName, req, function (value) { + //Use own write() method to construct full module value. + //But need to create shell that translates writeFile's + //write() to the right interface. + var textWrite = function (contents) { + return write(fileName, contents); + }; + textWrite.asModule = function (moduleName, contents) { + return write.asModule(moduleName, fileName, contents); + }; + + text.write(pluginName, nonStripName, textWrite, config); + }, config); + } + }; + + if (masterConfig.env === 'node' || (!masterConfig.env && + typeof process !== "undefined" && + process.versions && + !!process.versions.node && + !process.versions['node-webkit'])) { + //Using special require.nodeRequire, something added by r.js. + fs = require.nodeRequire('fs'); + + text.get = function (url, callback, errback) { + try { + var file = fs.readFileSync(url, 'utf8'); + //Remove BOM (Byte Mark Order) from utf8 files if it is there. + if (file.indexOf('\uFEFF') === 0) { + file = file.substring(1); + } + callback(file); + } catch (e) { + if (errback) { + errback(e); + } + } + }; + } else if (masterConfig.env === 'xhr' || (!masterConfig.env && + text.createXhr())) { + text.get = function (url, callback, errback, headers) { + var xhr = text.createXhr(), header; + xhr.open('GET', url, true); + + //Allow plugins direct access to xhr headers + if (headers) { + for (header in headers) { + if (headers.hasOwnProperty(header)) { + xhr.setRequestHeader(header.toLowerCase(), headers[header]); + } + } + } + + //Allow overrides specified in config + if (masterConfig.onXhr) { + masterConfig.onXhr(xhr, url); + } + + xhr.onreadystatechange = function (evt) { + var status, err; + //Do not explicitly handle errors, those should be + //visible via console output in the browser. + if (xhr.readyState === 4) { + status = xhr.status || 0; + if (status > 399 && status < 600) { + //An http 4xx or 5xx error. Signal an error. + err = new Error(url + ' HTTP status: ' + status); + err.xhr = xhr; + if (errback) { + errback(err); + } + } else { + callback(xhr.responseText); + } + + if (masterConfig.onXhrComplete) { + masterConfig.onXhrComplete(xhr, url); + } + } + }; + xhr.send(null); + }; + } else if (masterConfig.env === 'rhino' || (!masterConfig.env && + typeof Packages !== 'undefined' && typeof java !== 'undefined')) { + //Why Java, why is this so awkward? + text.get = function (url, callback) { + var stringBuffer, line, + encoding = "utf-8", + file = new java.io.File(url), + lineSeparator = java.lang.System.getProperty("line.separator"), + input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), + content = ''; + try { + stringBuffer = new java.lang.StringBuffer(); + line = input.readLine(); + + // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 + // http://www.unicode.org/faq/utf_bom.html + + // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 + if (line && line.length() && line.charAt(0) === 0xfeff) { + // Eat the BOM, since we've already found the encoding on this file, + // and we plan to concatenating this buffer with others; the BOM should + // only appear at the top of a file. + line = line.substring(1); + } + + if (line !== null) { + stringBuffer.append(line); + } + + while ((line = input.readLine()) !== null) { + stringBuffer.append(lineSeparator); + stringBuffer.append(line); + } + //Make sure we return a JavaScript string and not a Java string. + content = String(stringBuffer.toString()); //String + } finally { + input.close(); + } + callback(content); + }; + } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && + typeof Components !== 'undefined' && Components.classes && + Components.interfaces)) { + //Avert your gaze! + Cc = Components.classes; + Ci = Components.interfaces; + Components.utils['import']('resource://gre/modules/FileUtils.jsm'); + xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); + + text.get = function (url, callback) { + var inStream, convertStream, fileObj, + readData = {}; + + if (xpcIsWindows) { + url = url.replace(/\//g, '\\'); + } + + fileObj = new FileUtils.File(url); + + //XPCOM, you so crazy + try { + inStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + inStream.init(fileObj, 1, 0, false); + + convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] + .createInstance(Ci.nsIConverterInputStream); + convertStream.init(inStream, "utf-8", inStream.available(), + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + convertStream.readString(inStream.available(), readData); + convertStream.close(); + inStream.close(); + callback(readData.value); + } catch (e) { + throw new Error((fileObj && fileObj.path || '') + ': ' + e); + } + }; + } + return text; +}); diff --git a/site/mkdocs/search_index.json b/site/mkdocs/search_index.json new file mode 100644 index 000000000..496502dd2 --- /dev/null +++ b/site/mkdocs/search_index.json @@ -0,0 +1,309 @@ +{ + "docs": [ + { + "location": "/", + "text": "mailcow: dockerized - \ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95\n\n\n\n\nIf you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow!\n\n\nScreenshots\n\n\nYou can find screenshots \non Imgur\n.\n\n\nOverview\n\n\nmailcow dockerized comes with \n12 containers\n linked in \none bridged network\n.\nEach container represents a single application.\n\n\n\n\nDovecot\n\n\nClamAV\n\n\nMemcached\n\n\nRedis\n\n\nMySQL\n\n\nBind9 (Resolver) (formerly PDNS Recursor)\n\n\nPHP-FPM\n\n\nPostfix\n\n\nNginx\n\n\nRmilter\n\n\nRspamd\n\n\nSOGo\n\n\n\n\n7 volumes\n to keep dynamic data - take care of them!\n\n\n\n\nvmail-vol-1\n\n\ndkim-vol-1\n\n\nredis-vol-1\n\n\nmysql-vol-1\n\n\nrspamd-vol-1\n\n\npostfix-vol-1\n\n\ncrypt-vol-1\n\n\n\n\nThe integrated \nmailcow UI\n allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access:\n\n\n\n\nDKIM key management\n\n\nBlack- and whitelists per domain and per user\n\n\nSpam score managment per-user (reject spam, mark spam, greylist)\n\n\nAllow mailbox users to create temporary spam aliases\n\n\nPrepend mail tags to subject or move mail to subfolder (per-user)\n\n\nAllow mailbox users to toggle incoming and outgoing TLS enforcement\n\n\nAllow users to reset SOGo ActiveSync device caches\n\n\nimapsync to migrate or pull remote mailboxes regularly\n\n\nTFA: Yubi OTP and U2F USB (Google Chrome and derivates only)\n\n\nAdd domains, mailboxes, aliases, domain aliases and SOGo resources\n\n\nAdd whitelisted hosts to forward mail to mailcow\n\n\n\n\nLooking for a farm to host your cow?", + "title": "This is mailcow" + }, + { + "location": "/#mailcow-dockerized-", + "text": "If you want to support mailcow, consider hosting mailcow on a Servercow virtual machine @ Servercow!", + "title": "mailcow: dockerized - \ud83d\udc2e + \ud83d\udc0b = \ud83d\udc95" + }, + { + "location": "/#screenshots", + "text": "You can find screenshots on Imgur .", + "title": "Screenshots" + }, + { + "location": "/#overview", + "text": "mailcow dockerized comes with 12 containers linked in one bridged network .\nEach container represents a single application. Dovecot ClamAV Memcached Redis MySQL Bind9 (Resolver) (formerly PDNS Recursor) PHP-FPM Postfix Nginx Rmilter Rspamd SOGo 7 volumes to keep dynamic data - take care of them! vmail-vol-1 dkim-vol-1 redis-vol-1 mysql-vol-1 rspamd-vol-1 postfix-vol-1 crypt-vol-1 The integrated mailcow UI allows administrative work on your mail server instance as well as separated domain administrator and mailbox user access: DKIM key management Black- and whitelists per domain and per user Spam score managment per-user (reject spam, mark spam, greylist) Allow mailbox users to create temporary spam aliases Prepend mail tags to subject or move mail to subfolder (per-user) Allow mailbox users to toggle incoming and outgoing TLS enforcement Allow users to reset SOGo ActiveSync device caches imapsync to migrate or pull remote mailboxes regularly TFA: Yubi OTP and U2F USB (Google Chrome and derivates only) Add domains, mailboxes, aliases, domain aliases and SOGo resources Add whitelisted hosts to forward mail to mailcow Looking for a farm to host your cow?", + "title": "Overview" + }, + { + "location": "/install/", + "text": "Install mailcow\n\n\nWARNING\n: Please use Ubuntu 16.04 instead of Debian 8 or \nswitch to the kernel 4.9 from jessie backports\n because there is a bug (kernel panic) with the kernel 3.16 when running docker containers with healthchecks! Full details here: \ngithub.com/docker/docker/issues/30402\n and \nforum.mailcow.email/t/solved-mailcow-docker-causes-kernel-panic-edit/448\n\n\nYou need Docker and Docker Compose.\n\n\n1. Learn how to install \nDocker\n and \nDocker Compose\n.\n\n\nQuick installation for most operation systems:\n\n\n\n\nDocker\n\n\n\n\ncurl -sSL https://get.docker.com/ | sh\n\n\n\n\n\n\n\nDocker-Compose\n\n\n\n\ncurl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) \n /usr/local/bin/docker-compose\nchmod +x /usr/local/bin/docker-compose\n\n\n\n\n\nPlease use the latest Docker engine available and do not use the engine that ships with your distros repository.\n\n\n2. Clone the master branch of the repository\n\n\ngit clone https://github.com/andryyy/mailcow-dockerized \n cd mailcow-dockerized\n\n\n\n\n\n3. Generate a configuration file. Use a FQDN (\nhost.domain.tld\n) as hostname when asked.\n\n\n./generate_config.sh\n\n\n\n\n\n4. Change configuration if you want or need to.\n\n\nnano mailcow.conf\n\n\n\n\n\nIf you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080.\n\n\nYou may need to stop an existing pre-installed MTA which blocks port 25/tcp. See \nthis chapter\n to learn how to reconfigure Postfix to run besides mailcow after a successful installation.\n\n\n5. Pull the images and run the composer file. The paramter \n-d\n will start mailcow: dockerized detached:\n\n\ndocker-compose pull\ndocker-compose up -d\n\n\n\n\n\nDone!\n\n\nYou can now access \nhttps://${MAILCOW_HOSTNAME}\n with the default credentials \nadmin\n + password \nmoohoo\n.\n\n\nThe database will be initialized right after a connection to MySQL can be established.\n\n\nUpdate mailcow\n\n\nThere is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes.\n\n\nStep 1, method 1\n\n\nStash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them:\n\n\n# Stash local changes\ngit stash\n# Re-pull master\ngit pull\n# Apply stash and remove it\ngit stash pop\n\n\n\n\n\nStep 1, method 2\n\n\nFetch new data from GitHub, commit changes and merge remote repository: \n\n\n# Get updates/changes\ngit fetch\n# Add all changed files to local clone\ngit add -A\n# Commit changes, ignore git complaining about username and mail address\ngit commit -m \nLocal config aat $(date)\n\n# Merge changes\ngit merge\n\n\n\n\n\nIf git complains about conflicts, solve them! Example:\n\n\nCONFLICT (content): Merge conflict in data/web/index.php\n\n\n\n\n\nOpen \ndata/web/index.php\n, solve the conflict, close the file and run \ngit add -A\n + \ngit commit -m \"Solved conflict\"\n.\n\n\nStep 1, method 3\n\n\nThanks to fabreg @ GitHub!\n\n\nIn case both methods do not work (for many reason like you're unable to fix the CONFLICTS or any other reasons) you can simply start all over again.\n\n\nKeep in mind that all local changes \nto configuration files\n will be lost. However, your volumes will not be removed.\n\n\n\n\nCopy mailcow.conf somewhere outside the mailcow-dockerized directory\n\n\nStop and remove mailcow containers: \ndocker-compose down\n\n\nDelete the directory or rename it\n\n\nClone the remote repository again (\ngit clone https://github.com/andryyy/mailcow-dockerized \n cd mailcow-dockerized\n). \nPay attention\n to this step - the folder must have the same name of the previous one!\n\n\nCopy back your previous \nmailcow.conf\n into the mailcow-dockerizd folder \n\n\n\n\nIf you forgot to stop Docker before deleting the cloned directoy, you can use the following commands:\n\n\ndocker stop $(docker ps -a -q)\ndocker rm $(docker ps -a -q)\n\n\n\n\n\nStep 2\n\n\nPull new images (if any) and recreate changed containers:\n\n\ndocker-compose pull\ndocker-compose up -d --remove-orphans\n\n\n\n\n\nStep 3\n\n\nClean-up dangling (unused) images and volumes:\n\n\ndocker rmi -f $(docker images -f \ndangling=true\n -q)\ndocker volume rm $(docker volume ls -qf dangling=true)", + "title": "Installation" + }, + { + "location": "/install/#install-mailcow", + "text": "WARNING : Please use Ubuntu 16.04 instead of Debian 8 or switch to the kernel 4.9 from jessie backports because there is a bug (kernel panic) with the kernel 3.16 when running docker containers with healthchecks! Full details here: github.com/docker/docker/issues/30402 and forum.mailcow.email/t/solved-mailcow-docker-causes-kernel-panic-edit/448 You need Docker and Docker Compose. 1. Learn how to install Docker and Docker Compose . Quick installation for most operation systems: Docker curl -sSL https://get.docker.com/ | sh Docker-Compose curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) /usr/local/bin/docker-compose\nchmod +x /usr/local/bin/docker-compose Please use the latest Docker engine available and do not use the engine that ships with your distros repository. 2. Clone the master branch of the repository git clone https://github.com/andryyy/mailcow-dockerized cd mailcow-dockerized 3. Generate a configuration file. Use a FQDN ( host.domain.tld ) as hostname when asked. ./generate_config.sh 4. Change configuration if you want or need to. nano mailcow.conf If you plan to use a reverse proxy, you can, for example, bind HTTPS to 127.0.0.1 on port 8443 and HTTP to 127.0.0.1 on port 8080. You may need to stop an existing pre-installed MTA which blocks port 25/tcp. See this chapter to learn how to reconfigure Postfix to run besides mailcow after a successful installation. 5. Pull the images and run the composer file. The paramter -d will start mailcow: dockerized detached: docker-compose pull\ndocker-compose up -d Done! You can now access https://${MAILCOW_HOSTNAME} with the default credentials admin + password moohoo . The database will be initialized right after a connection to MySQL can be established.", + "title": "Install mailcow" + }, + { + "location": "/install/#update-mailcow", + "text": "There is no update routine. You need to refresh your pulled repository clone and apply your local changes (if any). Actually there are many ways to merge local changes.", + "title": "Update mailcow" + }, + { + "location": "/install/#step-1-method-1", + "text": "Stash all local changes, pull changes from the remote master branch and apply your stash on top of it. You will most likely see warnings about non-commited changes; you can ignore them: # Stash local changes\ngit stash\n# Re-pull master\ngit pull\n# Apply stash and remove it\ngit stash pop", + "title": "Step 1, method 1" + }, + { + "location": "/install/#step-1-method-2", + "text": "Fetch new data from GitHub, commit changes and merge remote repository: # Get updates/changes\ngit fetch\n# Add all changed files to local clone\ngit add -A\n# Commit changes, ignore git complaining about username and mail address\ngit commit -m Local config aat $(date) \n# Merge changes\ngit merge If git complains about conflicts, solve them! Example: CONFLICT (content): Merge conflict in data/web/index.php Open data/web/index.php , solve the conflict, close the file and run git add -A + git commit -m \"Solved conflict\" .", + "title": "Step 1, method 2" + }, + { + "location": "/install/#step-1-method-3", + "text": "Thanks to fabreg @ GitHub! In case both methods do not work (for many reason like you're unable to fix the CONFLICTS or any other reasons) you can simply start all over again. Keep in mind that all local changes to configuration files will be lost. However, your volumes will not be removed. Copy mailcow.conf somewhere outside the mailcow-dockerized directory Stop and remove mailcow containers: docker-compose down Delete the directory or rename it Clone the remote repository again ( git clone https://github.com/andryyy/mailcow-dockerized cd mailcow-dockerized ). Pay attention to this step - the folder must have the same name of the previous one! Copy back your previous mailcow.conf into the mailcow-dockerizd folder If you forgot to stop Docker before deleting the cloned directoy, you can use the following commands: docker stop $(docker ps -a -q)\ndocker rm $(docker ps -a -q)", + "title": "Step 1, method 3" + }, + { + "location": "/install/#step-2", + "text": "Pull new images (if any) and recreate changed containers: docker-compose pull\ndocker-compose up -d --remove-orphans", + "title": "Step 2" + }, + { + "location": "/install/#step-3", + "text": "Clean-up dangling (unused) images and volumes: docker rmi -f $(docker images -f dangling=true -q)\ndocker volume rm $(docker volume ls -qf dangling=true)", + "title": "Step 3" + }, + { + "location": "/first_steps/", + "text": "SSL (and: How to use Let's Encrypt)\n\n\nmailcow dockerized comes with a snakeoil CA \"mailcow\" and a server certificate in \ndata/assets/ssl\n. Please use your own trusted certificates.\n\n\nmailcow uses 3 domain names that should be covered by your new certificate:\n\n\n\n\n${MAILCOW_HOSTNAME}\n\n\nautodiscover.\nexample.org\n\n\nautoconfig.\nexample.org\n\n\n\n\nObtain multi-SAN certificate by Let's Encrypt\n\n\nThis is just an example of how to obtain certificates with certbot. There are several methods!\n\n\n1. Get the certbot client:\n\n\nwget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot \n chmod +x /usr/local/sbin/certbot\n\n\n\n\n\n2. Make sure you set \nHTTP_BIND=0.0.0.0\n and \nHTTP_PORT=80\n in \nmailcow.conf\n or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then rebuild Nginx:\n\n\ndocker-compose up -d\n\n\n\n\n\n3. Request the certificate with the webroot method:\n\n\ncd\n /path/to/git/clone/mailcow-dockerized\n\nsource\n mailcow.conf\ncertbot certonly \n\\\n\n --webroot \n\\\n\n -w \n${\nPWD\n}\n/data/web \n\\\n\n -d \n${\nMAILCOW_HOSTNAME\n}\n \n\\\n\n -d autodiscover.example.org \n\\\n\n -d autoconfig.example.org \n\\\n\n --email you@example.org \n\\\n\n --agree-tos\n\n\n\n\n\nRemember to replace the example.org domain with your own domain, this command will not work if you dont.\n\n\n4. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder:\n\n\nmv data/assets/ssl/cert.\n{\npem,pem.backup\n}\n\nmv data/assets/ssl/key.\n{\npem,pem.backup\n}\n\nln \n$(\nreadlink -f /etc/letsencrypt/live/\n${\nMAILCOW_HOSTNAME\n}\n/fullchain.pem\n)\n data/assets/ssl/cert.pem\nln \n$(\nreadlink -f /etc/letsencrypt/live/\n${\nMAILCOW_HOSTNAME\n}\n/privkey.pem\n)\n data/assets/ssl/key.pem\n\n\n\n\n\n5. Restart affected containers:\n\n\ndocker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow\n\n\n\n\n\nWhen renewing certificates, run the last two steps (link + restart) as post-hook in a script.\n\n\nRspamd Web UI\n\n\nAt first you may want to setup Rspamds web interface which provides some useful features and information.\n\n\n1. Generate a Rspamd controller password hash:\n\n\ndocker-compose exec rspamd-mailcow rspamadm pw\n\n\n\n\n\n2. Replace the default hash in \ndata/conf/rspamd/override.d/worker-controller.inc\n by your newly generated:\n\n\nenable_password = \nmyhash\n;\n\n\n\n\n\nYou can use \npassword = \"myhash\";\n instead of \nenable_password\n to disable write-access in the web UI.\n\n\n3. Restart rspamd:\n\n\ndocker-compose restart rspamd-mailcow\n\n\n\n\n\nOpen https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!\n\n\nOptional: Reverse proxy\n\n\nYou don't need to change the Nginx site that comes with mailcow: dockerized.\nmailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. This is very important to control access to Rspamd's web UI.\n\n\n1. Make sure you change HTTP_BIND and HTTPS_BIND in \nmailcow.conf\n to a local address and set the ports accordingly, for example:\n\n\nHTTP_BIND\n=\n127\n.0.0.1\n\nHTTP_PORT\n=\n8080\n\n\nHTTPS_PORT\n=\n127\n.0.0.1\n\nHTTPS_PORT\n=\n8443\n\n\n\n\n\n\n IMPORTANT: Do not use port 8081 \n\n\nRecreate affected containers by running \ndocker-compose up -d\n.\n\n\n2. Configure your local webserver as reverse proxy:\n\n\nApache 2.4\n\n\nVirtualHost\n \n*:443\n\n \nServerName\n mail.example.org\n \nServerAlias\n autodiscover.example.org\n \nServerAlias\n autoconfig.example.org\n\n \n[\n...\n]\n\n \n# You should proxy to a plain HTTP session to offload SSL processing\n\n \nProxyPass\n / http://127.0.0.1:8080/\n \nProxyPassReverse\n / http://127.0.0.1:8080/\n \nProxyPreserveHost\n \nOff\n\n \nyour-ssl-configuration-\nhere\n\n [...]\n\n \n# If you plan to proxy to a HTTPS host:\n\n \n#SSLProxyEngine On\n\n\n \n# If you plan to proxy to an untrusted HTTPS host:\n\n \n#SSLProxyVerify none\n\n \n#SSLProxyCheckPeerCN off\n\n \n#SSLProxyCheckPeerName off\n\n \n#SSLProxyCheckPeerExpire off\n\n\n/VirtualHost\n\n\n\n\n\n\nNginx\n\n\nserver\n \n{\n\n \nlisten\n \n443\n;\n\n \nserver_name\n \nmail.example.org\n \nautodiscover.example.org\n \nautoconfig.example.org\n;\n\n\n \n[\n...\n]\n\n \nyour-ssl-configuration-here\n\n \nlocation\n \n/\n \n{\n\n \nproxy_pass\n \nhttp\n:\n//\n127.0.0.1\n:\n8080\n/\n;\n\n \nproxy_redirect\n \nhttp\n:\n//\n127.0.0.1\n:\n8080\n/\n \n$\nscheme\n://\n$\nhost\n:\n$\nserver_port\n/\n;\n\n \nproxy_set_header\n \nX-Real-IP\n \n$remote_addr\n;\n\n \nproxy_set_header\n \nX-Forwarded-For\n \n$proxy_add_x_forwarded_for\n;\n\n \nproxy_set_header\n \nX-Forwarded-Proto\n \n$scheme\n;\n\n \n}\n\n \n[\n...\n]\n\n\n}\n\n\n\n\n\n\nOptional: Setup a relayhost\n\n\nInsert these lines to \ndata/conf/postfix/main.cf\n. \"relayhost\" does already exist (empty), just change its value.\n\n\nrelayhost = [your-relayhost]:587\nsmtp_sasl_password_maps = hash:/opt/postfix/conf/smarthost_passwd\nsmtp_sasl_auth_enable = yes\n\n\n\n\n\nCreate the credentials file:\n\n\necho \nyour-relayhost username:password\n \n data/conf/postfix/smarthost_passwd\n\n\n\n\n\nRun:\n\n\ndocker-compose exec postfix-mailcow postmap /opt/postfix/conf/smarthost_passwd\ndocker-compose exec postfix-mailcow chown root:postfix /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db\ndocker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db\ndocker-compose exec postfix-mailcow postfix reload\n\n\n\n\n\nHelper script\n\n\nThere is a helper script \nmailcow-setup-relayhost.sh\n you can run to setup a relayhost.\n\n\nUsage:\n\nSetup a relayhost:\n./mailcow-setup-relayhost.sh relayhost port \n(\nusername\n)\n \n(\npassword\n)\n\nUsername and password are optional parameters.\n\nReset to defaults:\n./mailcow-setup-relayhost.sh reset\n\n\n\n\n\nOptional: Log to Syslog\n\n\nEnable Rsyslog to receive logs on 524/tcp:\n\n\n# This setting depends on your Rsyslog version and configuration format.\n# For most Debian derivates it will work like this...\n$ModLoad imtcp\n$TCPServerAddress 127.0.0.1\n$InputTCPServerRun 524\n\n# ...while for Ubuntu 16.04 it looks like this:\nmodule(load=\nimtcp\n)\ninput(type=\nimtcp\n address=\n127.0.0.1\n port=\n524\n)\n\n# No matter your Rsyslog version, you should set this option to off\n# if you plan to use Fail2ban\n$RepeatedMsgReduction off\n\n\n\n\n\nRestart rsyslog after enabling the TCP listener.\n\n\nNow setup Docker daemon to start with the syslog driver.\nThis enables the syslog driver for all containers!\n\n\nDebian users can change the startup configuration in \n/etc/default/docker\n while CentOS users find it in \n/etc/sysconfig/docker\n:\n\n\n...\nDOCKER_OPTS=\n--log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524\n\n...\n\n\n\n\n\nCaution:\n For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters.\n\n\nJust run \nsystemctl edit docker.service\n and add the following content to fix it.\n\n\nNote:\n If \"systemctl edit\" is not available, just copy the content to \n/etc/systemd/system/docker.service.d/override.conf\n.\n\n\nThe first empty ExecStart parameter is not a mistake.\n\n\n[Service]\n\n\nEnvironmentFile\n=\n/etc/default/docker\n\n\nExecStart\n=\n\n\nExecStart\n=\n/usr/bin/docker daemon -H fd:// $DOCKER_OPTS\n\n\n\n\n\n\nRestart the Docker daemon and run \ndocker-compose down \n docker-compose up -d\n to recreate the containers.\n\n\nUse Fail2ban\n\n\nThis is a subsection of \"Log to Syslog\", which is required for Fail2ban to work.\n\n\nOpen \n/etc/fail2ban/filter.d/common.conf\n and search for the prefix_line parameter, change it to \".*\":\n\n\n__prefix_line = .*\n\n\n\n\n\nCreate \n/etc/fail2ban/jail.d/dovecot.conf\n...\n\n\n[dovecot]\n\n\nenabled\n \n=\n \ntrue\n\n\nfilter\n \n=\n \ndovecot\n\n\nlogpath\n \n=\n \n/var/log/syslog\n\n\nchain\n \n=\n \nFORWARD\n\n\n\n\n\n\nand \njail.d/postfix-sasl.conf\n:\n\n\n[postfix-sasl]\n\n\nenabled\n \n=\n \ntrue\n\n\nfilter\n \n=\n \npostfix-sasl\n\n\nlogpath\n \n=\n \n/var/log/syslog\n\n\nchain\n \n=\n \nFORWARD\n\n\n\n\n\n\nRestart Fail2ban.\n\n\nInstall a local MTA\n\n\nThe easiest option would be to disable the listener on port 25/tcp.\n\n\nPostfix\n users disable the listener by commenting the following line (starting with \nsmtp\n or \n25\n) in \n/etc/postfix/master.cf\n:\n\n\n#smtp inet n - - - - smtpd\n\n\n\n\n\nRestart Postfix after applying your changes.\n\n\nSender and receiver model\n\n\nWhen a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address.\n\n\nMailbox me@example.org is created. example.org is a primary domain. \nNote: a mailbox cannot be created in an alias domain.\n\nme@example.org is only known as me@example.org.\nme@example.org is allowed to send as me@example.org.\n\n\n\n\n\nWe can add an alias domain for example.org:\n\n\nAlias domain alias.com is added and assigned to primary domain example.org.\nme@example.org is now known as me@example.org and me@alias.com.\nme@example.org is now allowed to send as me@example.org and me@alias.com.\n\n\n\n\n\nWe can add aliases for a mailbox to receive mail for and to send from this new address.\n\n\nIt is important to know, that you are not able to receive mail for \nmy-alias@my-alias-domain.tld\n. You would need to create this particular alias.\n\n\nme@example.org is assigned the alias alias@example.org\nme@example.org is now known as alias@example.org, me@alias.com, alias@example.org\n\nme@example.org is NOT known as alias@alias.com.\n\n\n\n\n\nAdministrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users (\"delegate\" them).\n\n\nYou can choose between mailbox users or completely disable the sender check for domains.\n\n\nSOGo \"mail from\" addresses\n\n\nMailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains.\n\n\nIf you want to select another \nexisting\n mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator\nneeds to grant you access as described above.", + "title": "First Steps" + }, + { + "location": "/first_steps/#ssl-and-how-to-use-lets-encrypt", + "text": "mailcow dockerized comes with a snakeoil CA \"mailcow\" and a server certificate in data/assets/ssl . Please use your own trusted certificates. mailcow uses 3 domain names that should be covered by your new certificate: ${MAILCOW_HOSTNAME} autodiscover. example.org autoconfig. example.org", + "title": "SSL (and: How to use Let's Encrypt)" + }, + { + "location": "/first_steps/#obtain-multi-san-certificate-by-lets-encrypt", + "text": "This is just an example of how to obtain certificates with certbot. There are several methods! 1. Get the certbot client: wget https://dl.eff.org/certbot-auto -O /usr/local/sbin/certbot chmod +x /usr/local/sbin/certbot 2. Make sure you set HTTP_BIND=0.0.0.0 and HTTP_PORT=80 in mailcow.conf or setup a reverse proxy to enable connections to port 80. If you changed HTTP_BIND, then rebuild Nginx: docker-compose up -d 3. Request the certificate with the webroot method: cd /path/to/git/clone/mailcow-dockerized source mailcow.conf\ncertbot certonly \\ \n --webroot \\ \n -w ${ PWD } /data/web \\ \n -d ${ MAILCOW_HOSTNAME } \\ \n -d autodiscover.example.org \\ \n -d autoconfig.example.org \\ \n --email you@example.org \\ \n --agree-tos Remember to replace the example.org domain with your own domain, this command will not work if you dont. 4. Create hard links to the full path of the new certificates. Assuming you are still in the mailcow root folder: mv data/assets/ssl/cert. { pem,pem.backup } \nmv data/assets/ssl/key. { pem,pem.backup } \nln $( readlink -f /etc/letsencrypt/live/ ${ MAILCOW_HOSTNAME } /fullchain.pem ) data/assets/ssl/cert.pem\nln $( readlink -f /etc/letsencrypt/live/ ${ MAILCOW_HOSTNAME } /privkey.pem ) data/assets/ssl/key.pem 5. Restart affected containers: docker-compose restart postfix-mailcow dovecot-mailcow nginx-mailcow When renewing certificates, run the last two steps (link + restart) as post-hook in a script.", + "title": "Obtain multi-SAN certificate by Let's Encrypt" + }, + { + "location": "/first_steps/#rspamd-web-ui", + "text": "At first you may want to setup Rspamds web interface which provides some useful features and information. 1. Generate a Rspamd controller password hash: docker-compose exec rspamd-mailcow rspamadm pw 2. Replace the default hash in data/conf/rspamd/override.d/worker-controller.inc by your newly generated: enable_password = myhash ; You can use password = \"myhash\"; instead of enable_password to disable write-access in the web UI. 3. Restart rspamd: docker-compose restart rspamd-mailcow Open https://${MAILCOW_HOSTNAME}/rspamd in a browser and login!", + "title": "Rspamd Web UI" + }, + { + "location": "/first_steps/#optional-reverse-proxy", + "text": "You don't need to change the Nginx site that comes with mailcow: dockerized.\nmailcow: dockerized trusts the default gateway IP 172.22.1.1 as proxy. This is very important to control access to Rspamd's web UI. 1. Make sure you change HTTP_BIND and HTTPS_BIND in mailcow.conf to a local address and set the ports accordingly, for example: HTTP_BIND = 127 .0.0.1 HTTP_PORT = 8080 HTTPS_PORT = 127 .0.0.1 HTTPS_PORT = 8443 IMPORTANT: Do not use port 8081 Recreate affected containers by running docker-compose up -d . 2. Configure your local webserver as reverse proxy:", + "title": "Optional: Reverse proxy" + }, + { + "location": "/first_steps/#apache-24", + "text": "VirtualHost *:443 \n ServerName mail.example.org\n ServerAlias autodiscover.example.org\n ServerAlias autoconfig.example.org\n\n [ ... ] \n # You should proxy to a plain HTTP session to offload SSL processing \n ProxyPass / http://127.0.0.1:8080/\n ProxyPassReverse / http://127.0.0.1:8080/\n ProxyPreserveHost Off \n your-ssl-configuration- here \n [...]\n\n # If you plan to proxy to a HTTPS host: \n #SSLProxyEngine On \n\n # If you plan to proxy to an untrusted HTTPS host: \n #SSLProxyVerify none \n #SSLProxyCheckPeerCN off \n #SSLProxyCheckPeerName off \n #SSLProxyCheckPeerExpire off /VirtualHost", + "title": "Apache 2.4" + }, + { + "location": "/first_steps/#nginx", + "text": "server { \n listen 443 ; \n server_name mail.example.org autodiscover.example.org autoconfig.example.org ; \n\n [ ... ] \n your-ssl-configuration-here \n location / { \n proxy_pass http : // 127.0.0.1 : 8080 / ; \n proxy_redirect http : // 127.0.0.1 : 8080 / $ scheme :// $ host : $ server_port / ; \n proxy_set_header X-Real-IP $remote_addr ; \n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; \n proxy_set_header X-Forwarded-Proto $scheme ; \n } \n [ ... ] }", + "title": "Nginx" + }, + { + "location": "/first_steps/#optional-setup-a-relayhost", + "text": "Insert these lines to data/conf/postfix/main.cf . \"relayhost\" does already exist (empty), just change its value. relayhost = [your-relayhost]:587\nsmtp_sasl_password_maps = hash:/opt/postfix/conf/smarthost_passwd\nsmtp_sasl_auth_enable = yes Create the credentials file: echo your-relayhost username:password data/conf/postfix/smarthost_passwd Run: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/smarthost_passwd\ndocker-compose exec postfix-mailcow chown root:postfix /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db\ndocker-compose exec postfix-mailcow chmod 660 /opt/postfix/conf/smarthost_passwd /opt/postfix/conf/smarthost_passwd.db\ndocker-compose exec postfix-mailcow postfix reload", + "title": "Optional: Setup a relayhost" + }, + { + "location": "/first_steps/#helper-script", + "text": "There is a helper script mailcow-setup-relayhost.sh you can run to setup a relayhost. Usage:\n\nSetup a relayhost:\n./mailcow-setup-relayhost.sh relayhost port ( username ) ( password ) \nUsername and password are optional parameters.\n\nReset to defaults:\n./mailcow-setup-relayhost.sh reset", + "title": "Helper script" + }, + { + "location": "/first_steps/#optional-log-to-syslog", + "text": "Enable Rsyslog to receive logs on 524/tcp: # This setting depends on your Rsyslog version and configuration format.\n# For most Debian derivates it will work like this...\n$ModLoad imtcp\n$TCPServerAddress 127.0.0.1\n$InputTCPServerRun 524\n\n# ...while for Ubuntu 16.04 it looks like this:\nmodule(load= imtcp )\ninput(type= imtcp address= 127.0.0.1 port= 524 )\n\n# No matter your Rsyslog version, you should set this option to off\n# if you plan to use Fail2ban\n$RepeatedMsgReduction off Restart rsyslog after enabling the TCP listener. Now setup Docker daemon to start with the syslog driver.\nThis enables the syslog driver for all containers! Debian users can change the startup configuration in /etc/default/docker while CentOS users find it in /etc/sysconfig/docker : ...\nDOCKER_OPTS= --log-driver=syslog --log-opt syslog-address=tcp://127.0.0.1:524 \n... Caution: For some reason Ubuntu 16.04 and some, but not all, systemd based distros do not read the defaults file parameters. Just run systemctl edit docker.service and add the following content to fix it. Note: If \"systemctl edit\" is not available, just copy the content to /etc/systemd/system/docker.service.d/override.conf . The first empty ExecStart parameter is not a mistake. [Service] EnvironmentFile = /etc/default/docker ExecStart = ExecStart = /usr/bin/docker daemon -H fd:// $DOCKER_OPTS Restart the Docker daemon and run docker-compose down docker-compose up -d to recreate the containers.", + "title": "Optional: Log to Syslog" + }, + { + "location": "/first_steps/#use-fail2ban", + "text": "This is a subsection of \"Log to Syslog\", which is required for Fail2ban to work. Open /etc/fail2ban/filter.d/common.conf and search for the prefix_line parameter, change it to \".*\": __prefix_line = .* Create /etc/fail2ban/jail.d/dovecot.conf ... [dovecot] enabled = true filter = dovecot logpath = /var/log/syslog chain = FORWARD and jail.d/postfix-sasl.conf : [postfix-sasl] enabled = true filter = postfix-sasl logpath = /var/log/syslog chain = FORWARD Restart Fail2ban.", + "title": "Use Fail2ban" + }, + { + "location": "/first_steps/#install-a-local-mta", + "text": "The easiest option would be to disable the listener on port 25/tcp. Postfix users disable the listener by commenting the following line (starting with smtp or 25 ) in /etc/postfix/master.cf : #smtp inet n - - - - smtpd Restart Postfix after applying your changes.", + "title": "Install a local MTA" + }, + { + "location": "/first_steps/#sender-and-receiver-model", + "text": "When a mailbox is created, a user is allowed to send mail from and receive mail for his own mailbox address. Mailbox me@example.org is created. example.org is a primary domain. \nNote: a mailbox cannot be created in an alias domain.\n\nme@example.org is only known as me@example.org.\nme@example.org is allowed to send as me@example.org. We can add an alias domain for example.org: Alias domain alias.com is added and assigned to primary domain example.org.\nme@example.org is now known as me@example.org and me@alias.com.\nme@example.org is now allowed to send as me@example.org and me@alias.com. We can add aliases for a mailbox to receive mail for and to send from this new address. It is important to know, that you are not able to receive mail for my-alias@my-alias-domain.tld . You would need to create this particular alias. me@example.org is assigned the alias alias@example.org\nme@example.org is now known as alias@example.org, me@alias.com, alias@example.org\n\nme@example.org is NOT known as alias@alias.com. Administrators and domain administrators can edit mailboxes to allow specific users to send as other mailbox users (\"delegate\" them). You can choose between mailbox users or completely disable the sender check for domains.", + "title": "Sender and receiver model" + }, + { + "location": "/first_steps/#sogo-mail-from-addresses", + "text": "Mailbox users can, obviously, select their own mailbox address, as well as all alias addresses and aliases that exist through alias domains. If you want to select another existing mailbox user as your \"mail from\" address, this user has to delegate you access through SOGo (see SOGo documentation). Moreover a mailcow (domain) administrator\nneeds to grant you access as described above.", + "title": "SOGo \"mail from\" addresses" + }, + { + "location": "/u_and_e/", + "text": "mailcow UI configuration\n\n\nSeveral configuration parameters of the mailcow UI can be changed by creating a file \ndata/web/inc/vars.local.inc.php\n which overrides defaults settings found in \ndata/web/inc/vars.inc.php\n.\n\n\nThe local configuration file is persistent over updates of mailcow. Try not to change values inside \ndata/web/inc/vars.inc.php\n, but use them as template for the local override.\n\n\nmailcow UI configuration parameters can be to...\n\n\n\n\n...change the default language*\n\n\n...change the default bootstrap theme\n\n\n...set a password complexity regex\n\n\n...add mailcow app buttons to the login screen\n\n\n...set a pagination trigger\n\n\n...set action after submitting forms (stay in form, return to previous page)\n\n\n\n\n* To change SOGos default language, you will need to edit \ndata/conf/sogo/sogo.conf\n and replace \"English\" by your preferred language.\n\n\nAnonymize headers\n\n\nSave as \ndata/conf/postfix/mailcow_anonymize_headers.pcre\n:\n\n\n/^\\s*Received:[^\\)]+\\)\\s+\\(Authenticated sender:(.+)/\n REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1\n/^\\s*User-Agent/ IGNORE\n/^\\s*X-Enigmail/ IGNORE\n/^\\s*X-Mailer/ IGNORE\n/^\\s*X-Originating-IP/ IGNORE\n/^\\s*X-Forward/ IGNORE\n\n\n\n\n\nAdd this to \ndata/conf/postfix/main.cf\n:\n\n\nsmtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre\n\n\n\n\n\nBackup and restore maildir (simple tar file)\n\n\nBackup\n\n\nThis line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker run --rm -it -v $(docker inspect --format \n{{ range .Mounts }}{{ if eq .Destination \n/var/vmail\n }}{{ .Name }}{{ end }}{{ end }}\n $(docker-compose ps -q dovecot-mailcow)):/vmail -v \n${\nPWD\n}\n:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail\n\n\n\n\n\nYou can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.\nSet the filename \nbackup_vmail.tar.gz\n to any custom name, but leave the path as it is. Example: \n[...] tar cvfz /backup/my_own_filename_.tar.gz\n\n\nRestore\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker run --rm -it -v $(docker inspect --format \n{{ range .Mounts }}{{ if eq .Destination \n/var/vmail\n }}{{ .Name }}{{ end }}{{ end }}\n $(docker-compose ps -q dovecot-mailcow)):/vmail -v \n${\nPWD\n}\n:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz\n\n\n\n\n\nDocker Compose Bash completion\n\n\nFor the tab-tab... :-)\n\n\ncurl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose\n\n\n\n\n\nBlack and Whitelist\n\n\nEdit a domain as (domain) administrator to add an item to the filter table.\n\n\nBeware that a mailbox user can login to mailcow and override a domain policy filter item. \n\n\nCustomize Dockerfiles\n\n\nMake your changes in \ndata/Dockerfiles/$service\n and build the image locally:\n\n\ndocker build data/Dockerfiles/service -t mailcow/$service\n\n\n\n\n\nNow auto-recreate modified containers:\n\n\ndocker-compose up -d\n\n\n\n\n\nDisable sender addresses verification\n\n\nThis option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.\n\n\nSimply create a file \ndata/conf/postfix/check_sasl_access\n and enter the following content. This user must exist in your installation and needs to authenticate before sending mail.\n\n\nuser-to-allow-everything@example.com OK\n\n\n\n\n\nOpen \ndata/conf/postfix/main.cf\n and find \nsmtpd_sender_restrictions\n. Prepend \ncheck_sasl_access hash:/opt/postfix/conf/check_sasl_access\n like this:\n\n\nsmtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...]\n\n\n\n\n\nRun postmap on check_sasl_access:\n\n\ndocker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access\n\n\n\n\n\nRestart the Postfix container.\n\n\nInstall Roundcube\n\n\nDownload Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here \nrc/\n):\n\n\ncd data/web/rc\nwget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -\n# Change folder name\nmv roundcubemail-1.3* rc\n# Change permissions\nchown -R root: rc/\n\n\n\n\n\nCreate a file \ndata/web/rc/config/config.inc.php\n with the following content.\n\n\nChange the \ndes_key\n parameter to a random value.\n It is used to temporarily store your IMAP password.\n\n\n?php\n\n\nerror_reporting\n(\n0\n);\n\n\nif\n \n(\n!\nfile_exists\n(\n/tmp/mime.types\n))\n \n{\n\n\nfile_put_contents\n(\n/tmp/mime.types\n,\n \nfopen\n(\nhttp://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types\n,\n \nr\n));\n\n\n}\n\n\n$config\n \n=\n \narray\n();\n\n\n$config\n[\ndb_dsnw\n]\n \n=\n \nmysql://\n \n.\n \ngetenv\n(\nDBUSER\n)\n \n.\n \n:\n \n.\n \ngetenv\n(\nDBPASS\n)\n \n.\n \n@mysql/\n \n.\n \ngetenv\n(\nDBNAME\n);\n\n\n$config\n[\ndefault_host\n]\n \n=\n \ntls://dovecot\n;\n\n\n$config\n[\ndefault_port\n]\n \n=\n \n143\n;\n\n\n$config\n[\nsmtp_server\n]\n \n=\n \ntls://postfix\n;\n\n\n$config\n[\nsmtp_port\n]\n \n=\n \n587\n;\n\n\n$config\n[\nsmtp_user\n]\n \n=\n \n%u\n;\n\n\n$config\n[\nsmtp_pass\n]\n \n=\n \n%p\n;\n\n\n$config\n[\nsupport_url\n]\n \n=\n \n;\n\n\n$config\n[\nproduct_name\n]\n \n=\n \nRoundcube Webmail\n;\n\n\n$config\n[\ndes_key\n]\n \n=\n \nrcmail-!24ByteDESkey*Str\n;\n\n\n$config\n[\nlog_dir\n]\n \n=\n \n/dev/null\n;\n\n\n$config\n[\ntemp_dir\n]\n \n=\n \n/tmp\n;\n\n\n$config\n[\nplugins\n]\n \n=\n \narray\n(\n\n \narchive\n,\n\n\n);\n\n\n$config\n[\nskin\n]\n \n=\n \nlarry\n;\n\n\n$config\n[\nmime_types\n]\n \n=\n \n/tmp/mime.types\n;\n\n\n$config\n[\nimap_conn_options\n]\n \n=\n \narray\n(\n\n\nssl\n \n=\n \narray\n(\nverify_peer\n \n=\n \nfalse\n,\n \nverify_peer_name\n \n=\n \nfalse\n,\n \nallow_self_signed\n \n=\n \ntrue\n)\n\n\n);\n\n\n$config\n[\nenable_installer\n]\n \n=\n \nfalse\n;\n\n\n$config\n[\nsmtp_conn_options\n]\n \n=\n \narray\n(\n\n\nssl\n \n=\n \narray\n(\nverify_peer\n \n=\n \nfalse\n,\n \nverify_peer_name\n \n=\n \nfalse\n,\n \nallow_self_signed\n \n=\n \ntrue\n)\n\n\n);\n\n\n\n\n\n\nPoint your browser to \nhttps://myserver/rc/installer\n and follow the instructions.\nInitialize the database and leave the installer.\n\n\nDelete the directory \ndata/web/rc/installer\n after a successful installation!\n\n\nEnable change password function in Roundcube\n\n\nOpen \ndata/web/rc/config/config.inc.php\n and enable the password plugin:\n\n\n...\n$config[\nplugins\n] = array(\n \narchive\n,\n \npassword\n,\n);\n...\n\n\n\n\n\nOpen \ndata/web/rc/plugins/password/password.php\n, search for \ncase 'ssha':\n and add above:\n\n\n \ncase\n \nssha256\n:\n\n \n$\nsalt\n \n=\n \nrcube_utils\n::\nrandom_bytes\n(\n8\n);\n\n \n$\ncrypted\n \n=\n \nbase64_encode\n(\n \nhash\n(\nsha256\n,\n \n$\npassword\n \n.\n \n$\nsalt\n,\n \nTRUE\n \n)\n \n.\n \n$\nsalt\n \n);\n\n \n$\nprefix\n \n=\n \n{SSHA256}\n;\n\n \nbreak\n;\n\n\n\n\n\n\nOpen \ndata/web/rc/plugins/password/config.inc.php\n and change the following parameters (or add them at the bottom of that file):\n\n\n$config[\npassword_driver\n] = \nsql\n;\n$config[\npassword_algorithm\n] = \nssha256\n;\n$config[\npassword_algorithm_prefix\n] = \n{SSHA256}\n;\n$config[\npassword_query\n] = \nUPDATE mailbox SET password = %P WHERE username = %u\n;\n\n\n\n\n\nMySQL\n\n\nConnect\n\n\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u\n${\nDBUSER\n}\n -p\n${\nDBPASS\n}\n \n${\nDBNAME\n}\n\n\n\n\n\n\nBackup\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date +\n%Y%m%d_%H%M%S\n)\ndocker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u\n${\nDBUSER\n}\n -p\n${\nDBPASS\n}\n \n${\nDBNAME\n}\n \n backup_\n${\nDBNAME\n}\n_\n${\nDATE\n}\n.sql\n\n\n\n\n\nRestore\n\n\ncd /path/to/mailcow-dockerized\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u\n${\nDBUSER\n}\n -p\n${\nDBPASS\n}\n \n${\nDBNAME\n}\n \n backup\n_file.sql\n\n\n\n\n\n\nReset MySQL passwords\n\n\nStop the stack by running \ndocker-compose stop\n.\n\n\nWhen the containers came to a stop, run this command:\n\n\ndocker-compose run --rm --entrypoint \n/bin/sh -c \ngosu mysql mysqld --skip-grant-tables \n sleep 10 \n mysql -hlocalhost -uroot \n exit 0\n mysql-mailcow\n\n\n\n\n\n1. Find database name\n\n\nMariaDB [(none)]\n show databases;\n+--------------------+\n| Database |\n+--------------------+\n| information_schema |\n| mailcow_database | \n=====\n| mysql |\n| performance_schema |\n+--------------------+\n4 rows in set (0.00 sec)\n\n\n\n\n\n2. Reset one or more users\n\n\nBoth \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both.\n\n\nMariaDB [(none)]\n SELECT user FROM mysql.user;\n+--------------+\n| user |\n+--------------+\n| mailcow_user | \n===== \n| root |\n+--------------+\n2 rows in set (0.00 sec)\n\nMariaDB [(none)]\n FLUSH PRIVILEGES;\nMariaDB [(none)]\n UPDATE mysql.user SET authentication_string = PASSWORD(\ngotr00t\n), password = PASSWORD(\ngotr00t\n) WHERE User = \nroot\n AND Host = \n%\n;\nMariaDB [(none)]\n UPDATE mysql.user SET authentication_string = PASSWORD(\nmookuh\n), password = PASSWORD(\nmookuh\n) WHERE User = \nmailcow\n AND Host = \n%\n;\nMariaDB [(none)]\n FLUSH PRIVILEGES;\n\n\n\n\n\nDebugging\n\n\nYou can use \ndocker-compose logs $service-name\n for all containers.\n\n\nRun \ndocker-compose logs\n for all logs at once.\n\n\nFollow the log output by running docker-compose with \nlogs -f\n.\n\n\nLimit the output by calling logs with \n--tail=300\n like \ndocker-compose logs --tail=300 mysql-mailcow\n.\n\n\nRedirect port 80 to 443\n\n\nSince February the 28th 2017 mailcow does come with port 80 and 443 enabled.\n\n\nOpen \nmailcow.conf\n and set \nHTTP_BIND=0.0.0.0\n.\n\n\nOpen \ndata/conf/nginx/site.conf\n and add a new \"catch-all\" site at the top of that file:\n\n\nserver\n \n{\n\n \nlisten\n \n80\n \ndefault_server\n;\n\n \ninclude\n \n/etc/nginx/conf.d/server_name.active\n;\n\n \nreturn\n \n301\n \nhttps\n:\n//\n$\nhost\n$\nrequest_uri\n;\n\n\n}\n\n\n\n\n\n\nRestart the stack, changed containers will be updated:\n\n\ndocker-compose up -d\n\n\nRedis\n\n\nClient\n\n\ndocker-compose exec redis-mailcow redis-cli\n\n\n\n\n\nRemove persistent data\n\n\n\n\nRemove volume \nmysql-vol-1\n to remove all MySQL data.\n\n\nRemove volume \nredis-vol-1\n to remove all Redis data.\n\n\nRemove volume \nvmail-vol-1\n to remove all contents of \n/var/vmail\n mounted to \ndovecot-mailcow\n.\n\n\nRemove volume \ndkim-vol-1\n to remove all DKIM keys.\n\n\nRemove volume \nrspamd-vol-1\n to remove all Rspamd data.\n\n\n\n\nRunning \ndocker-compose down -v\n will \ndestroy all mailcow: dockerized volumes\n and delete any related containers.\n\n\nReset admin password\n\n\nReset mailcow admin to \nadmin:moohoo\n:\n\n\ncd mailcow_path\nbash reset_admin.sh\n\n\n\n\n\nRspamd\n\n\nLearn spam and ham\n\n\nRspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.\nThis is archived by using the Dovecot plugin \"antispam\" and a simple parser script.\n\n\nRspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)\n\n\nThe bayes statistics are written to Redis as keys \nBAYES_HAM\n and \nBAYES_SPAM\n.\n\n\nYou can also use Rspamd's web ui to learn ham and/or spam.\n\n\nLearn ham or spam from existing directory\n\n\nYou can use a one-liner to learn mail in plain-text (uncompressed) format:\n\n\n# Ham\nfor file in /my/folder/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_ham \n $file; done\n# Spam\nfor file in /my/folder/.Junk/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_spam \n $file; done\n\n\n\n\n\nConsider attaching a local folder as new volume to \nrspamd-mailcow\n in \ndocker-compose.yml\n and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example:\n\n\nfor file in /data/old_mail/.Junk/cur/*; do rspamc learn_spam \n zcat $file; done\n\n\n\n\n\nCLI tools\n\n\ndocker-compose exec rspamd-mailcow rspamc --help\ndocker-compose exec rspamd-mailcow rspamadm --help\n\n\n\n\n\nSee \nRspamd documentation\n\n\nAdjust service configurations\n\n\nThe most important configuration files are mounted from the host into the related containers:\n\n\ndata/conf\n\u251c\u2500\u2500 bind9\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 named.conf\n\u251c\u2500\u2500 dovecot\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-master.passwd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 sieve_after\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-dict-sql.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 dovecot-mysql.conf\n\u251c\u2500\u2500 mysql\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 my.cnf\n\u251c\u2500\u2500 nginx\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 site.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 templates\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 listen_plain.template\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 listen_ssl.template\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server_name.template\n\u251c\u2500\u2500 pdns\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 pdns_custom.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 recursor.conf\n\u251c\u2500\u2500 postfix\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 main.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 master.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 postscreen_access.cidr\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 smtp_dsn_filter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_relay_recipient_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_tls_enforce_in_policy.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_tls_enforce_out_policy.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_domain_catchall_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_domain_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_domains_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_mailbox_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_relay_domain_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_sender_acl.cf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 mysql_virtual_spamalias_maps.cf\n\u251c\u2500\u2500 rmilter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 rmilter.conf\n\u251c\u2500\u2500 rspamd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 authoritative.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 tags.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 vars.inc.php -\n ../../../web/inc/vars.inc.php\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 local.d\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 dkim.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 metrics.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 options.inc\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 redis.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 rspamd.conf.local\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 statistic.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 lua\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 rspamd.local.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 override.d\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 logging.inc\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 worker-controller.inc\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 worker-normal.inc\n\u2514\u2500\u2500 sogo\n \u251c\u2500\u2500 sieve.creds\n \u2514\u2500\u2500 sogo.conf\n\n\n\n\n\nJust change the according configuration file on the host and restart the related service:\n\n\ndocker-compose restart service-mailcow\n\n\n\n\n\nTagging\n\n\nMailbox users can tag their mail address like in \nme+facebook@example.org\n and choose between to setups to handle this tag:\n\n\n1. Move this message to a subfolder \"facebook\" (will be created lower case if not existing)\n\n\n2. Prepend the tag to the subject: \"[facebook] Subject\"\n\n\nTwo-factor authentication\n\n\nSo far two methods for TFA are implemented. Both work with the fantastic \nYubikey\n.\n\n\nWhile Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS.\n\n\nBoth methods support multiple YubiKeys.\n\n\nAs administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in.\n\n\nThe key used to login will be displayed in green, while other keys remain grey.\n\n\nYubi OTP\n\n\nThe Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.\nThe API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.\n\n\nU2F\n\n\nOnly Google Chrome (+derivates) and Opera support U2F authentication to this day natively.\nFor Firefox you will need to install the \"U2F Support Add-on\" as provided on \nmozilla.org\n.\n\n\nU2F works without an internet connection.\n\n\nPortainer\n\n\nIn order to enable Portainer, the docker-compose.yml and site.conf for nginx must be modified.\n\n\n1. docker-compose.yml: Insert this block for portainer\n\n\n portainer-mailcow:\n image: portainer/portainer\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n restart: always\n dns:\n - 172.22.1.254\n dns_search: mailcow-network\n networks:\n mailcow-network:\n aliases:\n - portainer\n\n\n\n\n\n2a. data/conf/nginx/site.conf: Just beneath the opening line, at the same level as a server { block, add this:\n\n\nupstream\n \nportainer\n \n{\n\n \nserver\n \nportainer-mailcow\n:\n9000\n;\n\n\n}\n\n\n\nmap\n \n$\nhttp_upgrade\n \n$\nconnection_upgrade\n \n{\n\n \ndefault\n \nupgrade\n;\n\n \n \nclose\n;\n\n\n}\n\n\n\n\n\n\n2b. data/conf/nginx/site.conf: Then, inside \nboth\n (ssl and plain) server blocks, add this:\n\n\n \nlocation\n \n/\nportainer\n/\n \n{\n\n \nproxy_http_version\n \n1.1\n;\n\n \nproxy_set_header\n \nHost\n \n$http_host\n;\n \n#\n \nrequired\n \nfor\n \ndocker\n \nclient\ns\n \nsake\n\n \nproxy_set_header\n \nX-Real-IP\n \n$remote_addr\n;\n \n#\n \npass\n \non\n \nreal\n \nclient\ns\n \nIP\n\n \nproxy_set_header\n \nX-Forwarded-For\n \n$proxy_add_x_forwarded_for\n;\n\n \nproxy_set_header\n \nX-Forwarded-Proto\n \n$scheme\n;\n\n \nproxy_read_timeout\n \n900\n;\n\n\n \nproxy_set_header\n \nConnection\n \n;\n\n \nproxy_buffers\n \n32\n \n4k\n;\n\n \nproxy_pass\n \nhttp\n:\n//\nportainer\n/\n;\n\n \n}\n\n\n \nlocation\n \n/\nportainer\n/\napi\n/\nwebsocket\n/\n \n{\n\n \nproxy_http_version\n \n1.1\n;\n\n \nproxy_set_header\n \nUpgrade\n \n$http_upgrade\n;\n\n \nproxy_set_header\n \nConnection\n \n$connection_upgrade\n;\n\n \nproxy_pass\n \nhttp\n:\n//\nportainer\n/\napi\n/\nwebsocket\n/\n;\n\n \n}\n\n\n\n\n\n\nNow you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You\u2019ll then be prompted to specify a new password for the \nadmin\n account. After specifying your password, you\u2019ll then be able to connect to the Portainer UI.\n\n\nChange autodiscover setup type\n\n\nThis disables ActiveSync in the autodiscover service for Outlook and configures it with IMAP and SMTP instead:\n\n\nOpen \ndata/web/autodiscover.php\n and set \n'useEASforOutlook' =\n 'yes'\n to \n'useEASforOutlook' =\n 'no'\n.\n\n\nTo always use IMAP and SMTP instead of EAS, set \n'autodiscoverType' =\n 'imap'\n.\n\n\nWhy Bind?\n\n\nFor DNS blacklist lookups and DNSSEC.\n\n\nMost systems use either a public or a local caching DNS resolver.\nThat's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.\nMost if not all providers apply a rate limit based on the DNS resolver that is used to query their service.\nUsing a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.", + "title": "Usage & Examples" + }, + { + "location": "/u_and_e/#mailcow-ui-configuration", + "text": "Several configuration parameters of the mailcow UI can be changed by creating a file data/web/inc/vars.local.inc.php which overrides defaults settings found in data/web/inc/vars.inc.php . The local configuration file is persistent over updates of mailcow. Try not to change values inside data/web/inc/vars.inc.php , but use them as template for the local override. mailcow UI configuration parameters can be to... ...change the default language* ...change the default bootstrap theme ...set a password complexity regex ...add mailcow app buttons to the login screen ...set a pagination trigger ...set action after submitting forms (stay in form, return to previous page) * To change SOGos default language, you will need to edit data/conf/sogo/sogo.conf and replace \"English\" by your preferred language.", + "title": "mailcow UI configuration" + }, + { + "location": "/u_and_e/#anonymize-headers", + "text": "Save as data/conf/postfix/mailcow_anonymize_headers.pcre : /^\\s*Received:[^\\)]+\\)\\s+\\(Authenticated sender:(.+)/\n REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1\n/^\\s*User-Agent/ IGNORE\n/^\\s*X-Enigmail/ IGNORE\n/^\\s*X-Mailer/ IGNORE\n/^\\s*X-Originating-IP/ IGNORE\n/^\\s*X-Forward/ IGNORE Add this to data/conf/postfix/main.cf : smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre", + "title": "Anonymize headers" + }, + { + "location": "/u_and_e/#backup-and-restore-maildir-simple-tar-file", + "text": "", + "title": "Backup and restore maildir (simple tar file)" + }, + { + "location": "/u_and_e/#backup", + "text": "This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory: cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker run --rm -it -v $(docker inspect --format {{ range .Mounts }}{{ if eq .Destination /var/vmail }}{{ .Name }}{{ end }}{{ end }} $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${ PWD } :/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to.\nSet the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz", + "title": "Backup" + }, + { + "location": "/u_and_e/#restore", + "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker run --rm -it -v $(docker inspect --format {{ range .Mounts }}{{ if eq .Destination /var/vmail }}{{ .Name }}{{ end }}{{ end }} $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${ PWD } :/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz", + "title": "Restore" + }, + { + "location": "/u_and_e/#docker-compose-bash-completion", + "text": "For the tab-tab... :-) curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose", + "title": "Docker Compose Bash completion" + }, + { + "location": "/u_and_e/#black-and-whitelist", + "text": "Edit a domain as (domain) administrator to add an item to the filter table. Beware that a mailbox user can login to mailcow and override a domain policy filter item.", + "title": "Black and Whitelist" + }, + { + "location": "/u_and_e/#customize-dockerfiles", + "text": "Make your changes in data/Dockerfiles/$service and build the image locally: docker build data/Dockerfiles/service -t mailcow/$service Now auto-recreate modified containers: docker-compose up -d", + "title": "Customize Dockerfiles" + }, + { + "location": "/u_and_e/#disable-sender-addresses-verification", + "text": "This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do. Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail. user-to-allow-everything@example.com OK Open data/conf/postfix/main.cf and find smtpd_sender_restrictions . Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this: smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...] Run postmap on check_sasl_access: docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access Restart the Postfix container.", + "title": "Disable sender addresses verification" + }, + { + "location": "/u_and_e/#install-roundcube", + "text": "Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here rc/ ): cd data/web/rc\nwget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -\n# Change folder name\nmv roundcubemail-1.3* rc\n# Change permissions\nchown -R root: rc/ Create a file data/web/rc/config/config.inc.php with the following content. Change the des_key parameter to a random value. It is used to temporarily store your IMAP password. ?php error_reporting ( 0 ); if ( ! file_exists ( /tmp/mime.types )) { file_put_contents ( /tmp/mime.types , fopen ( http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types , r )); } $config = array (); $config [ db_dsnw ] = mysql:// . getenv ( DBUSER ) . : . getenv ( DBPASS ) . @mysql/ . getenv ( DBNAME ); $config [ default_host ] = tls://dovecot ; $config [ default_port ] = 143 ; $config [ smtp_server ] = tls://postfix ; $config [ smtp_port ] = 587 ; $config [ smtp_user ] = %u ; $config [ smtp_pass ] = %p ; $config [ support_url ] = ; $config [ product_name ] = Roundcube Webmail ; $config [ des_key ] = rcmail-!24ByteDESkey*Str ; $config [ log_dir ] = /dev/null ; $config [ temp_dir ] = /tmp ; $config [ plugins ] = array ( \n archive , ); $config [ skin ] = larry ; $config [ mime_types ] = /tmp/mime.types ; $config [ imap_conn_options ] = array ( ssl = array ( verify_peer = false , verify_peer_name = false , allow_self_signed = true ) ); $config [ enable_installer ] = false ; $config [ smtp_conn_options ] = array ( ssl = array ( verify_peer = false , verify_peer_name = false , allow_self_signed = true ) ); Point your browser to https://myserver/rc/installer and follow the instructions.\nInitialize the database and leave the installer. Delete the directory data/web/rc/installer after a successful installation!", + "title": "Install Roundcube" + }, + { + "location": "/u_and_e/#enable-change-password-function-in-roundcube", + "text": "Open data/web/rc/config/config.inc.php and enable the password plugin: ...\n$config[ plugins ] = array(\n archive ,\n password ,\n);\n... Open data/web/rc/plugins/password/password.php , search for case 'ssha': and add above: case ssha256 : \n $ salt = rcube_utils :: random_bytes ( 8 ); \n $ crypted = base64_encode ( hash ( sha256 , $ password . $ salt , TRUE ) . $ salt ); \n $ prefix = {SSHA256} ; \n break ; Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file): $config[ password_driver ] = sql ;\n$config[ password_algorithm ] = ssha256 ;\n$config[ password_algorithm_prefix ] = {SSHA256} ;\n$config[ password_query ] = UPDATE mailbox SET password = %P WHERE username = %u ;", + "title": "Enable change password function in Roundcube" + }, + { + "location": "/u_and_e/#mysql", + "text": "", + "title": "MySQL" + }, + { + "location": "/u_and_e/#connect", + "text": "source mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u ${ DBUSER } -p ${ DBPASS } ${ DBNAME }", + "title": "Connect" + }, + { + "location": "/u_and_e/#backup_1", + "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\nDATE=$(date + %Y%m%d_%H%M%S )\ndocker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u ${ DBUSER } -p ${ DBPASS } ${ DBNAME } backup_ ${ DBNAME } _ ${ DATE } .sql", + "title": "Backup" + }, + { + "location": "/u_and_e/#restore_1", + "text": "cd /path/to/mailcow-dockerized\nsource mailcow.conf\ndocker-compose exec mysql-mailcow mysql -u ${ DBUSER } -p ${ DBPASS } ${ DBNAME } backup _file.sql", + "title": "Restore" + }, + { + "location": "/u_and_e/#reset-mysql-passwords", + "text": "Stop the stack by running docker-compose stop . When the containers came to a stop, run this command: docker-compose run --rm --entrypoint /bin/sh -c gosu mysql mysqld --skip-grant-tables sleep 10 mysql -hlocalhost -uroot exit 0 mysql-mailcow 1. Find database name MariaDB [(none)] show databases;\n+--------------------+\n| Database |\n+--------------------+\n| information_schema |\n| mailcow_database | =====\n| mysql |\n| performance_schema |\n+--------------------+\n4 rows in set (0.00 sec) 2. Reset one or more users Both \"password\" and \"authentication_string\" exist. Currently \"password\" is used, but better set both. MariaDB [(none)] SELECT user FROM mysql.user;\n+--------------+\n| user |\n+--------------+\n| mailcow_user | ===== \n| root |\n+--------------+\n2 rows in set (0.00 sec)\n\nMariaDB [(none)] FLUSH PRIVILEGES;\nMariaDB [(none)] UPDATE mysql.user SET authentication_string = PASSWORD( gotr00t ), password = PASSWORD( gotr00t ) WHERE User = root AND Host = % ;\nMariaDB [(none)] UPDATE mysql.user SET authentication_string = PASSWORD( mookuh ), password = PASSWORD( mookuh ) WHERE User = mailcow AND Host = % ;\nMariaDB [(none)] FLUSH PRIVILEGES;", + "title": "Reset MySQL passwords" + }, + { + "location": "/u_and_e/#debugging", + "text": "You can use docker-compose logs $service-name for all containers. Run docker-compose logs for all logs at once. Follow the log output by running docker-compose with logs -f . Limit the output by calling logs with --tail=300 like docker-compose logs --tail=300 mysql-mailcow .", + "title": "Debugging" + }, + { + "location": "/u_and_e/#redirect-port-80-to-443", + "text": "Since February the 28th 2017 mailcow does come with port 80 and 443 enabled. Open mailcow.conf and set HTTP_BIND=0.0.0.0 . Open data/conf/nginx/site.conf and add a new \"catch-all\" site at the top of that file: server { \n listen 80 default_server ; \n include /etc/nginx/conf.d/server_name.active ; \n return 301 https : // $ host $ request_uri ; } Restart the stack, changed containers will be updated: docker-compose up -d", + "title": "Redirect port 80 to 443" + }, + { + "location": "/u_and_e/#redis", + "text": "", + "title": "Redis" + }, + { + "location": "/u_and_e/#client", + "text": "docker-compose exec redis-mailcow redis-cli", + "title": "Client" + }, + { + "location": "/u_and_e/#remove-persistent-data", + "text": "Remove volume mysql-vol-1 to remove all MySQL data. Remove volume redis-vol-1 to remove all Redis data. Remove volume vmail-vol-1 to remove all contents of /var/vmail mounted to dovecot-mailcow . Remove volume dkim-vol-1 to remove all DKIM keys. Remove volume rspamd-vol-1 to remove all Rspamd data. Running docker-compose down -v will destroy all mailcow: dockerized volumes and delete any related containers.", + "title": "Remove persistent data" + }, + { + "location": "/u_and_e/#reset-admin-password", + "text": "Reset mailcow admin to admin:moohoo : cd mailcow_path\nbash reset_admin.sh", + "title": "Reset admin password" + }, + { + "location": "/u_and_e/#rspamd", + "text": "", + "title": "Rspamd" + }, + { + "location": "/u_and_e/#learn-spam-and-ham", + "text": "Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash.\nThis is archived by using the Dovecot plugin \"antispam\" and a simple parser script. Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning) The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM . You can also use Rspamd's web ui to learn ham and/or spam.", + "title": "Learn spam and ham" + }, + { + "location": "/u_and_e/#learn-ham-or-spam-from-existing-directory", + "text": "You can use a one-liner to learn mail in plain-text (uncompressed) format: # Ham\nfor file in /my/folder/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_ham $file; done\n# Spam\nfor file in /my/folder/.Junk/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_spam $file; done Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example: for file in /data/old_mail/.Junk/cur/*; do rspamc learn_spam zcat $file; done", + "title": "Learn ham or spam from existing directory" + }, + { + "location": "/u_and_e/#cli-tools", + "text": "docker-compose exec rspamd-mailcow rspamc --help\ndocker-compose exec rspamd-mailcow rspamadm --help See Rspamd documentation", + "title": "CLI tools" + }, + { + "location": "/u_and_e/#adjust-service-configurations", + "text": "The most important configuration files are mounted from the host into the related containers: data/conf\n\u251c\u2500\u2500 bind9\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 named.conf\n\u251c\u2500\u2500 dovecot\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-master.passwd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 sieve_after\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dovecot-dict-sql.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 dovecot-mysql.conf\n\u251c\u2500\u2500 mysql\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 my.cnf\n\u251c\u2500\u2500 nginx\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 site.conf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 templates\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 listen_plain.template\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 listen_ssl.template\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server_name.template\n\u251c\u2500\u2500 pdns\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 pdns_custom.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 recursor.conf\n\u251c\u2500\u2500 postfix\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 main.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 master.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 postscreen_access.cidr\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 smtp_dsn_filter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 sql\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_relay_recipient_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_tls_enforce_in_policy.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_tls_enforce_out_policy.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_domain_catchall_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_domain_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_alias_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_domains_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_mailbox_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_relay_domain_maps.cf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 mysql_virtual_sender_acl.cf\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 mysql_virtual_spamalias_maps.cf\n\u251c\u2500\u2500 rmilter\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 rmilter.conf\n\u251c\u2500\u2500 rspamd\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dynmaps\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 authoritative.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 tags.php\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 vars.inc.php - ../../../web/inc/vars.inc.php\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 local.d\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 dkim.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 metrics.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 options.inc\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 redis.conf\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 rspamd.conf.local\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 statistic.conf\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 lua\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 rspamd.local.lua\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 override.d\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 logging.inc\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 worker-controller.inc\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 worker-normal.inc\n\u2514\u2500\u2500 sogo\n \u251c\u2500\u2500 sieve.creds\n \u2514\u2500\u2500 sogo.conf Just change the according configuration file on the host and restart the related service: docker-compose restart service-mailcow", + "title": "Adjust service configurations" + }, + { + "location": "/u_and_e/#tagging", + "text": "Mailbox users can tag their mail address like in me+facebook@example.org and choose between to setups to handle this tag: 1. Move this message to a subfolder \"facebook\" (will be created lower case if not existing) 2. Prepend the tag to the subject: \"[facebook] Subject\"", + "title": "Tagging" + }, + { + "location": "/u_and_e/#two-factor-authentication", + "text": "So far two methods for TFA are implemented. Both work with the fantastic Yubikey . While Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS. Both methods support multiple YubiKeys. As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in. The key used to login will be displayed in green, while other keys remain grey.", + "title": "Two-factor authentication" + }, + { + "location": "/u_and_e/#yubi-otp", + "text": "The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key.\nThe API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.", + "title": "Yubi OTP" + }, + { + "location": "/u_and_e/#u2f", + "text": "Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively.\nFor Firefox you will need to install the \"U2F Support Add-on\" as provided on mozilla.org . U2F works without an internet connection.", + "title": "U2F" + }, + { + "location": "/u_and_e/#portainer", + "text": "In order to enable Portainer, the docker-compose.yml and site.conf for nginx must be modified. 1. docker-compose.yml: Insert this block for portainer portainer-mailcow:\n image: portainer/portainer\n volumes:\n - /var/run/docker.sock:/var/run/docker.sock\n restart: always\n dns:\n - 172.22.1.254\n dns_search: mailcow-network\n networks:\n mailcow-network:\n aliases:\n - portainer 2a. data/conf/nginx/site.conf: Just beneath the opening line, at the same level as a server { block, add this: upstream portainer { \n server portainer-mailcow : 9000 ; } map $ http_upgrade $ connection_upgrade { \n default upgrade ; \n close ; } 2b. data/conf/nginx/site.conf: Then, inside both (ssl and plain) server blocks, add this: location / portainer / { \n proxy_http_version 1.1 ; \n proxy_set_header Host $http_host ; # required for docker client s sake \n proxy_set_header X-Real-IP $remote_addr ; # pass on real client s IP \n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; \n proxy_set_header X-Forwarded-Proto $scheme ; \n proxy_read_timeout 900 ; \n\n proxy_set_header Connection ; \n proxy_buffers 32 4k ; \n proxy_pass http : // portainer / ; \n } \n\n location / portainer / api / websocket / { \n proxy_http_version 1.1 ; \n proxy_set_header Upgrade $http_upgrade ; \n proxy_set_header Connection $connection_upgrade ; \n proxy_pass http : // portainer / api / websocket / ; \n } Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You\u2019ll then be prompted to specify a new password for the admin account. After specifying your password, you\u2019ll then be able to connect to the Portainer UI.", + "title": "Portainer" + }, + { + "location": "/u_and_e/#change-autodiscover-setup-type", + "text": "This disables ActiveSync in the autodiscover service for Outlook and configures it with IMAP and SMTP instead: Open data/web/autodiscover.php and set 'useEASforOutlook' = 'yes' to 'useEASforOutlook' = 'no' . To always use IMAP and SMTP instead of EAS, set 'autodiscoverType' = 'imap' .", + "title": "Change autodiscover setup type" + }, + { + "location": "/u_and_e/#why-bind", + "text": "For DNS blacklist lookups and DNSSEC. Most systems use either a public or a local caching DNS resolver.\nThat's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics.\nMost if not all providers apply a rate limit based on the DNS resolver that is used to query their service.\nUsing a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.", + "title": "Why Bind?" + } + ] +} \ No newline at end of file diff --git a/site/sitemap.xml b/site/sitemap.xml new file mode 100644 index 000000000..aafca5c27 --- /dev/null +++ b/site/sitemap.xml @@ -0,0 +1,36 @@ + + + + + + / + 2017-05-01 + daily + + + + + + /install/ + 2017-05-01 + daily + + + + + + /first_steps/ + 2017-05-01 + daily + + + + + + /u_and_e/ + 2017-05-01 + daily + + + + \ No newline at end of file diff --git a/site/u_and_e/index.html b/site/u_and_e/index.html new file mode 100644 index 000000000..f08c6e0e7 --- /dev/null +++ b/site/u_and_e/index.html @@ -0,0 +1,1058 @@ + + + + + + + + + + + + + + + + + + Usage & Examples - mailcow: dockerized + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + +
+
+ + edit + + + +

Usage & Examples

+ +

mailcow UI configuration

+

Several configuration parameters of the mailcow UI can be changed by creating a file data/web/inc/vars.local.inc.php which overrides defaults settings found in data/web/inc/vars.inc.php.

+

The local configuration file is persistent over updates of mailcow. Try not to change values inside data/web/inc/vars.inc.php, but use them as template for the local override.

+

mailcow UI configuration parameters can be to...

+
    +
  • ...change the default language*
  • +
  • ...change the default bootstrap theme
  • +
  • ...set a password complexity regex
  • +
  • ...add mailcow app buttons to the login screen
  • +
  • ...set a pagination trigger
  • +
  • ...set action after submitting forms (stay in form, return to previous page)
  • +
+

* To change SOGos default language, you will need to edit data/conf/sogo/sogo.conf and replace "English" by your preferred language.

+

Anonymize headers

+

Save as data/conf/postfix/mailcow_anonymize_headers.pcre:

+
/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/
+    REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1
+/^\s*User-Agent/        IGNORE
+/^\s*X-Enigmail/        IGNORE
+/^\s*X-Mailer/          IGNORE
+/^\s*X-Originating-IP/  IGNORE
+/^\s*X-Forward/         IGNORE
+
+ + +

Add this to data/conf/postfix/main.cf:

+
smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre
+
+ + +

Backup and restore maildir (simple tar file)

+

Backup

+

This line backups the vmail directory to a file backup_vmail.tar.gz in the mailcow root directory:

+
cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar cvfz /backup/backup_vmail.tar.gz /vmail
+
+ + +

You can change the path by adjusting ${PWD} (which equals to the current directory) to any path you have write-access to. +Set the filename backup_vmail.tar.gz to any custom name, but leave the path as it is. Example: [...] tar cvfz /backup/my_own_filename_.tar.gz

+

Restore

+
cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker run --rm -it -v $(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/vmail" }}{{ .Name }}{{ end }}{{ end }}' $(docker-compose ps -q dovecot-mailcow)):/vmail -v ${PWD}:/backup debian:jessie tar xvfz /backup/backup_vmail.tar.gz
+
+ + +

Docker Compose Bash completion

+

For the tab-tab... :-)

+
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
+
+ + +

Black and Whitelist

+

Edit a domain as (domain) administrator to add an item to the filter table.

+

Beware that a mailbox user can login to mailcow and override a domain policy filter item.

+

Customize Dockerfiles

+

Make your changes in data/Dockerfiles/$service and build the image locally:

+
docker build data/Dockerfiles/service -t mailcow/$service
+
+ + +

Now auto-recreate modified containers:

+
docker-compose up -d
+
+ + +

Disable sender addresses verification

+

This option is not best-practice and should only be implemented when there is no other option available to archive whatever you are trying to do.

+

Simply create a file data/conf/postfix/check_sasl_access and enter the following content. This user must exist in your installation and needs to authenticate before sending mail.

+
user-to-allow-everything@example.com OK
+
+ + +

Open data/conf/postfix/main.cf and find smtpd_sender_restrictions. Prepend check_sasl_access hash:/opt/postfix/conf/check_sasl_access like this:

+
smtpd_sender_restrictions = check_sasl_access hash:/opt/postfix/conf/check_sasl_access reject_authenticated_sender_login_mismatch [...]
+
+ + +

Run postmap on check_sasl_access:

+
docker-compose exec postfix-mailcow postmap /opt/postfix/conf/check_sasl_access
+
+ + +

Restart the Postfix container.

+

Install Roundcube

+

Download Roundcube 1.3.x (beta at the time of Feb 2017) to the web htdocs directory and extract it (here rc/):

+
cd data/web/rc
+wget -O - https://github.com/roundcube/roundcubemail/releases/download/1.3-beta/roundcubemail-1.3-beta-complete.tar.gz | tar xfvz -
+# Change folder name
+mv roundcubemail-1.3* rc
+# Change permissions
+chown -R root: rc/
+
+ + +

Create a file data/web/rc/config/config.inc.php with the following content.

+

Change the des_key parameter to a random value. It is used to temporarily store your IMAP password.

+
<?php
+error_reporting(0);
+if (!file_exists('/tmp/mime.types')) {
+file_put_contents("/tmp/mime.types", fopen("http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types", 'r'));
+}
+$config = array();
+$config['db_dsnw'] = 'mysql://' . getenv('DBUSER') . ':' . getenv('DBPASS') . '@mysql/' . getenv('DBNAME');
+$config['default_host'] = 'tls://dovecot';
+$config['default_port'] = '143';
+$config['smtp_server'] = 'tls://postfix';
+$config['smtp_port'] = 587;
+$config['smtp_user'] = '%u';
+$config['smtp_pass'] = '%p';
+$config['support_url'] = '';
+$config['product_name'] = 'Roundcube Webmail';
+$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
+$config['log_dir'] = '/dev/null';
+$config['temp_dir'] = '/tmp';
+$config['plugins'] = array(
+    'archive',
+);
+$config['skin'] = 'larry';
+$config['mime_types'] = '/tmp/mime.types';
+$config['imap_conn_options'] = array(
+'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
+);
+$config['enable_installer'] = false;
+$config['smtp_conn_options'] = array(
+'ssl' => array('verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true)
+);
+
+ + +

Point your browser to https://myserver/rc/installer and follow the instructions. +Initialize the database and leave the installer.

+

Delete the directory data/web/rc/installer after a successful installation!

+

Enable change password function in Roundcube

+

Open data/web/rc/config/config.inc.php and enable the password plugin:

+
...
+$config['plugins'] = array(
+    'archive',
+    'password',
+);
+...
+
+ + +

Open data/web/rc/plugins/password/password.php, search for case 'ssha': and add above:

+
        case 'ssha256':
+            $salt = rcube_utils::random_bytes(8);
+            $crypted = base64_encode( hash('sha256', $password . $salt, TRUE ) . $salt );
+            $prefix  = '{SSHA256}';
+            break;
+
+ + +

Open data/web/rc/plugins/password/config.inc.php and change the following parameters (or add them at the bottom of that file):

+
$config['password_driver'] = 'sql';
+$config['password_algorithm'] = 'ssha256';
+$config['password_algorithm_prefix'] = '{SSHA256}';
+$config['password_query'] = "UPDATE mailbox SET password = %P WHERE username = %u";
+
+ + +

MySQL

+

Connect

+
source mailcow.conf
+docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME}
+
+ + +

Backup

+
cd /path/to/mailcow-dockerized
+source mailcow.conf
+DATE=$(date +"%Y%m%d_%H%M%S")
+docker-compose exec mysql-mailcow mysqldump --default-character-set=utf8mb4 -u${DBUSER} -p${DBPASS} ${DBNAME} > backup_${DBNAME}_${DATE}.sql
+
+ + +

Restore

+
cd /path/to/mailcow-dockerized
+source mailcow.conf
+docker-compose exec mysql-mailcow mysql -u${DBUSER} -p${DBPASS} ${DBNAME} < backup_file.sql
+
+ + +

Reset MySQL passwords

+

Stop the stack by running docker-compose stop.

+

When the containers came to a stop, run this command:

+
docker-compose run --rm --entrypoint '/bin/sh -c "gosu mysql mysqld --skip-grant-tables & sleep 10 && mysql -hlocalhost -uroot && exit 0"' mysql-mailcow
+
+ + +

1. Find database name

+
MariaDB [(none)]> show databases;
++--------------------+
+| Database           |
++--------------------+
+| information_schema |
+| mailcow_database   | <=====
+| mysql              |
+| performance_schema |
++--------------------+
+4 rows in set (0.00 sec)
+
+ + +

2. Reset one or more users

+

Both "password" and "authentication_string" exist. Currently "password" is used, but better set both.

+
MariaDB [(none)]> SELECT user FROM mysql.user;
++--------------+
+| user         |
++--------------+
+| mailcow_user | <===== 
+| root         |
++--------------+
+2 rows in set (0.00 sec)
+
+MariaDB [(none)]> FLUSH PRIVILEGES;
+MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('gotr00t'), password = PASSWORD('gotr00t') WHERE User = 'root' AND Host = '%';
+MariaDB [(none)]> UPDATE mysql.user SET authentication_string = PASSWORD('mookuh'), password = PASSWORD('mookuh') WHERE User = 'mailcow' AND Host = '%';
+MariaDB [(none)]> FLUSH PRIVILEGES;
+
+ + +

Debugging

+

You can use docker-compose logs $service-name for all containers.

+

Run docker-compose logs for all logs at once.

+

Follow the log output by running docker-compose with logs -f.

+

Limit the output by calling logs with --tail=300 like docker-compose logs --tail=300 mysql-mailcow.

+

Redirect port 80 to 443

+

Since February the 28th 2017 mailcow does come with port 80 and 443 enabled.

+

Open mailcow.conf and set HTTP_BIND=0.0.0.0.

+

Open data/conf/nginx/site.conf and add a new "catch-all" site at the top of that file:

+
server {
+    listen 80 default_server;
+    include /etc/nginx/conf.d/server_name.active;
+    return 301 https://$host$request_uri;
+}
+
+ + +

Restart the stack, changed containers will be updated:

+

docker-compose up -d

+

Redis

+

Client

+
docker-compose exec redis-mailcow redis-cli
+
+ + +

Remove persistent data

+
    +
  • Remove volume mysql-vol-1 to remove all MySQL data.
  • +
  • Remove volume redis-vol-1 to remove all Redis data.
  • +
  • Remove volume vmail-vol-1 to remove all contents of /var/vmail mounted to dovecot-mailcow.
  • +
  • Remove volume dkim-vol-1 to remove all DKIM keys.
  • +
  • Remove volume rspamd-vol-1 to remove all Rspamd data.
  • +
+

Running docker-compose down -v will destroy all mailcow: dockerized volumes and delete any related containers.

+

Reset admin password

+

Reset mailcow admin to admin:moohoo:

+
cd mailcow_path
+bash reset_admin.sh
+
+ + +

Rspamd

+

Learn spam and ham

+

Rspamd learns mail as spam or ham when you move a message in or out of the junk folder to any mailbox besides trash. +This is archived by using the Dovecot plugin "antispam" and a simple parser script.

+

Rspamd also auto-learns mail when a high or low score is detected (see https://rspamd.com/doc/configuration/statistic.html#autolearning)

+

The bayes statistics are written to Redis as keys BAYES_HAM and BAYES_SPAM.

+

You can also use Rspamd's web ui to learn ham and/or spam.

+

Learn ham or spam from existing directory

+

You can use a one-liner to learn mail in plain-text (uncompressed) format:

+
# Ham
+for file in /my/folder/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_ham < $file; done
+# Spam
+for file in /my/folder/.Junk/cur/*; do docker exec -i $(docker-compose ps -q rspamd-mailcow) rspamc learn_spam < $file; done
+
+ + +

Consider attaching a local folder as new volume to rspamd-mailcow in docker-compose.yml and learn given files inside the container. This can be used as workaround to parse compressed data with zcat. Example:

+
for file in /data/old_mail/.Junk/cur/*; do rspamc learn_spam < zcat $file; done
+
+ + +

CLI tools

+
docker-compose exec rspamd-mailcow rspamc --help
+docker-compose exec rspamd-mailcow rspamadm --help
+
+ + +

See Rspamd documentation

+

Adjust service configurations

+

The most important configuration files are mounted from the host into the related containers:

+
data/conf
+├── bind9
+│   └── named.conf
+├── dovecot
+│   ├── dovecot.conf
+│   ├── dovecot-master.passwd
+│   ├── sieve_after
+│   └── sql
+│       ├── dovecot-dict-sql.conf
+│       └── dovecot-mysql.conf
+├── mysql
+│   └── my.cnf
+├── nginx
+│   ├── dynmaps.conf
+│   ├── site.conf
+│   └── templates
+│       ├── listen_plain.template
+│       ├── listen_ssl.template
+│       └── server_name.template
+├── pdns
+│   ├── pdns_custom.lua
+│   └── recursor.conf
+├── postfix
+│   ├── main.cf
+│   ├── master.cf
+│   ├── postscreen_access.cidr
+│   ├── smtp_dsn_filter
+│   └── sql
+│       ├── mysql_relay_recipient_maps.cf
+│       ├── mysql_tls_enforce_in_policy.cf
+│       ├── mysql_tls_enforce_out_policy.cf
+│       ├── mysql_virtual_alias_domain_catchall_maps.cf
+│       ├── mysql_virtual_alias_domain_maps.cf
+│       ├── mysql_virtual_alias_maps.cf
+│       ├── mysql_virtual_domains_maps.cf
+│       ├── mysql_virtual_mailbox_maps.cf
+│       ├── mysql_virtual_relay_domain_maps.cf
+│       ├── mysql_virtual_sender_acl.cf
+│       └── mysql_virtual_spamalias_maps.cf
+├── rmilter
+│   └── rmilter.conf
+├── rspamd
+│   ├── dynmaps
+│   │   ├── authoritative.php
+│   │   ├── settings.php
+│   │   ├── tags.php
+│   │   └── vars.inc.php -> ../../../web/inc/vars.inc.php
+│   ├── local.d
+│   │   ├── dkim.conf
+│   │   ├── metrics.conf
+│   │   ├── options.inc
+│   │   ├── redis.conf
+│   │   ├── rspamd.conf.local
+│   │   └── statistic.conf
+│   ├── lua
+│   │   └── rspamd.local.lua
+│   └── override.d
+│       ├── logging.inc
+│       ├── worker-controller.inc
+│       └── worker-normal.inc
+└── sogo
+    ├── sieve.creds
+    └── sogo.conf
+
+ + +

Just change the according configuration file on the host and restart the related service:

+
docker-compose restart service-mailcow
+
+ + +

Tagging

+

Mailbox users can tag their mail address like in me+facebook@example.org and choose between to setups to handle this tag:

+

1. Move this message to a subfolder "facebook" (will be created lower case if not existing)

+

2. Prepend the tag to the subject: "[facebook] Subject"

+

Two-factor authentication

+

So far two methods for TFA are implemented. Both work with the fantastic Yubikey.

+

While Yubi OTP needs an active internet connection and an API ID and key, U2F will work with any FIDO U2F USB key out of the box, but can only be used when mailcow is accessed over HTTPS.

+

Both methods support multiple YubiKeys.

+

As administrator you are able to temporary disable a domain administrators TFA login until they successfully logged in.

+

The key used to login will be displayed in green, while other keys remain grey.

+

Yubi OTP

+

The Yubi API ID and Key will be checked against the Yubico Cloud API. When setting up TFA you will be asked for your personal API account for this key. +The API ID, API key and the first 12 characters (your YubiKeys ID in modhex) are stored in the MySQL table as secret.

+

U2F

+

Only Google Chrome (+derivates) and Opera support U2F authentication to this day natively. +For Firefox you will need to install the "U2F Support Add-on" as provided on mozilla.org.

+

U2F works without an internet connection.

+

Portainer

+

In order to enable Portainer, the docker-compose.yml and site.conf for nginx must be modified.

+

1. docker-compose.yml: Insert this block for portainer

+
    portainer-mailcow:
+      image: portainer/portainer
+      volumes:
+        - /var/run/docker.sock:/var/run/docker.sock
+      restart: always
+      dns:
+        - 172.22.1.254
+      dns_search: mailcow-network
+      networks:
+        mailcow-network:
+          aliases:
+            - portainer
+
+ + +

2a. data/conf/nginx/site.conf: Just beneath the opening line, at the same level as a server { block, add this:

+
upstream portainer {
+  server portainer-mailcow:9000;
+}
+
+map $http_upgrade $connection_upgrade {
+  default upgrade;
+  '' close;
+}
+
+ + +

2b. data/conf/nginx/site.conf: Then, inside both (ssl and plain) server blocks, add this:

+
  location /portainer/ {
+    proxy_http_version 1.1;
+    proxy_set_header Host              $http_host;   # required for docker client's sake
+    proxy_set_header X-Real-IP         $remote_addr; # pass on real client's IP
+    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
+    proxy_set_header X-Forwarded-Proto $scheme;
+    proxy_read_timeout                 900;
+
+    proxy_set_header Connection "";
+    proxy_buffers 32 4k;
+    proxy_pass http://portainer/;
+  }
+
+  location /portainer/api/websocket/ {
+    proxy_http_version 1.1;
+    proxy_set_header Upgrade $http_upgrade;
+    proxy_set_header Connection $connection_upgrade;
+    proxy_pass http://portainer/api/websocket/;
+  }
+
+ + +

Now you can simply navigate to https://${MAILCOW_HOSTNAME}/portainer/ to view your Portainer container monitoring page. You’ll then be prompted to specify a new password for the admin account. After specifying your password, you’ll then be able to connect to the Portainer UI.

+

Change autodiscover setup type

+

This disables ActiveSync in the autodiscover service for Outlook and configures it with IMAP and SMTP instead:

+

Open data/web/autodiscover.php and set 'useEASforOutlook' => 'yes' to 'useEASforOutlook' => 'no'.

+

To always use IMAP and SMTP instead of EAS, set 'autodiscoverType' => 'imap'.

+

Why Bind?

+

For DNS blacklist lookups and DNSSEC.

+

Most systems use either a public or a local caching DNS resolver. +That's a very bad idea when it comes to filter spam using DNS-based blackhole lists (DNSBL) or similar technics. +Most if not all providers apply a rate limit based on the DNS resolver that is used to query their service. +Using a public resolver like Googles 4x8, OpenDNS or any other shared DNS resolver like your ISPs will hit that limit very soon.

+ + +
+
+
+
+ + + + +
+ + + + + + + + + + \ No newline at end of file