commit 4a4d350e2080d6fc93bf0bd6c0c4ed8d0bbeac62 Author: Markus Pesch Date: Tue Aug 31 18:47:34 2021 +0200 Initial Commit diff --git a/.dev_env b/.dev_env new file mode 100644 index 0000000..892d615 --- /dev/null +++ b/.dev_env @@ -0,0 +1,5 @@ +DATABASE_TYPE=Pg +DATABASE_HOST=localhost +DATABASE_NAME=postgres +DATABASE_USER=fetchmail +DATABASE_PASSWORD=MySecretPassword \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..458b067 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,164 @@ +--- +kind: pipeline +type: kubernetes +name: linter + +platform: + os: linux + arch: amd64 + +steps: +- name: markdown lint + commands: + - markdownlint *.md + image: docker.io/tmknom/markdownlint:0.23.1 + resources: + limits: + cpu: 50 + memory: 50M + +- name: email-notification + environment: + PLUGIN_HOST: + from_secret: smtp_host + PLUGIN_USERNAME: + from_secret: smtp_username + PLUGIN_PASSWORD: + from_secret: smtp_password + PLUGIN_FROM: + from_secret: smtp_mail_address + image: docker.io/drillster/drone-email:latest + resources: + limits: + cpu: 50 + memory: 25M + when: + status: + - changed + - failure + +trigger: + event: + exclude: + - tag +--- +kind: pipeline +type: docker +name: build + +platform: + os: linux + arch: amd64 + +steps: +- name: build + image: docker.io/volkerraschek/build-image:latest + commands: + - make container-image/build + volumes: + - name: docker_socket + path: /var/run/docker.sock + when: + branch: + - master + +- name: push + image: docker.io/volkerraschek/build-image:latest + commands: + - make container-image/push + environment: + FETCHMAIL_IMAGE_REGISTRY_PASSWORD: + from_secret: container_image_registry_password + volumes: + - name: docker_socket + path: /var/run/docker.sock + when: + branch: + - master + repo: + - volker.raschek/fetchmail-docker + +- name: delete + image: docker.io/volkerraschek/build-image:latest + commands: + - make container-image/delete + volumes: + - name: docker_socket + path: /var/run/docker.sock + when: + branch: + - master + +- name: notify + image: drillster/drone-email + environment: + PLUGIN_HOST: + from_secret: smtp_host + PLUGIN_USERNAME: + from_secret: smtp_username + PLUGIN_PASSWORD: + from_secret: smtp_password + PLUGIN_FROM: + from_secret: smtp_mail_address + when: + status: + - changed + - failure + +volumes: +- name: docker_socket + host: + path: /var/run/docker.sock + +trigger: + event: + exclude: + - tag +--- +kind: pipeline +type: kubernetes +name: sync + +platform: + os: linux + arch: amd64 + +steps: +- name: github + image: docker.io/appleboy/drone-git-push:latest + resources: + limits: + cpu: 50 + memory: 25M + settings: + branch: master + remote: ssh://git@github.com/volker-raschek/fetchmail-docker.git + force: true + ssh_key: + from_secret: ssh_key + +- name: email-notification + environment: + PLUGIN_HOST: + from_secret: smtp_host + PLUGIN_USERNAME: + from_secret: smtp_username + PLUGIN_PASSWORD: + from_secret: smtp_password + PLUGIN_FROM: + from_secret: smtp_mail_address + image: docker.io/drillster/drone-email:latest + resources: + limits: + cpu: 50 + memory: 25M + when: + status: + - changed + - failure + +trigger: + event: + - push + repo: + - volker.raschek/fetchmail-docker diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b53e68c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dcd9d00 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Makefile eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..57b2fbf --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,144 @@ +# markdownlint YAML configuration +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "atx" + +# MD004/ul-style - Unordered list style +MD004: + style: "dash" + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 80 + # Number of characters for headings + heading_line_length: 80 + # Number of characters for code blocks + code_block_line_length: 80 + # Include code blocks + code_blocks: false + # Include tables + tables: false + # Include headings + headings: true + # Include headings + headers: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: + # Only check sibling headings + allow_different_nesting: true + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "---" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: [] + # - some-thing + # Include code blocks + code_blocks: false + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: "fenced" + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence syle + style: "backtick" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ef12e99 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM docker.io/library/alpine:3.11.2 + +RUN echo "http://dl-3.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories +RUN apk add --update perl perl-lockfile-simple perl-dbi perl-dbd-pg perl-dbd-mysql fetchmail +RUN mkdir --parents /run/fetchmail + +COPY --chown=fetchmail:fetchmail fetchmail.pl /usr/local/bin/fetchmail.pl + +USER fetchmail + +ENTRYPOINT [ "/usr/bin/perl" ] +CMD [ "/usr/local/bin/fetchmail.pl" ] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5a82408 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2019 Markus Pesch + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..13c6020 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +# CONTAINER_RUNTIME +# The CONTAINER_RUNTIME variable will be used to specified the path to a +# container runtime. This is needed to start and run a container image. +CONTAINER_RUNTIME?=$(shell which docker) + +# FETCHMAIL_IMAGE_REGISTRY_NAME +# Defines the name of the new container to be built using several variables. +FETCHMAIL_IMAGE_REGISTRY_NAME:=docker.io +FETCHMAIL_IMAGE_REGISTRY_USER:=volkerraschek + +FETCHMAIL_IMAGE_NAMESPACE?=${FETCHMAIL_IMAGE_REGISTRY_USER} +FETCHMAIL_IMAGE_NAME:=fetchmail +FETCHMAIL_IMAGE_VERSION?=latest +FETCHMAIL_IMAGE_FULLY_QUALIFIED=${FETCHMAIL_IMAGE_REGISTRY_NAME}/${FETCHMAIL_IMAGE_NAMESPACE}/${FETCHMAIL_IMAGE_NAME}:${FETCHMAIL_IMAGE_VERSION} +FETCHMAIL_IMAGE_UNQUALIFIED=${FETCHMAIL_IMAGE_NAMESPACE}/${FETCHMAIL_IMAGE_NAME}:${FETCHMAIL_IMAGE_VERSION} + +# BUILD CONTAINER IMAGE +# ============================================================================== +PHONY:=container-image/build +container-image/build: + ${CONTAINER_RUNTIME} build \ + --file Dockerfile \ + --no-cache \ + --pull \ + --tag ${FETCHMAIL_IMAGE_FULLY_QUALIFIED} \ + --tag ${FETCHMAIL_IMAGE_UNQUALIFIED} \ + . + +# DELETE CONTAINER IMAGE +# ============================================================================== +PHONY:=container-image/delete +container-image/delete: + - ${CONTAINER_RUNTIME} image rm ${FETCHMAIL_IMAGE_FULLY_QUALIFIED} ${FETCHMAIL_IMAGE_UNQUALIFIED} + - ${CONTAINER_RUNTIME} image rm ${BASE_IMAGE_FULL} + +# PUSH CONTAINER IMAGE +# ============================================================================== +PHONY+=container-image/push +container-image/push: + echo ${FETCHMAIL_IMAGE_REGISTRY_PASSWORD} | ${CONTAINER_RUNTIME} login ${FETCHMAIL_IMAGE_REGISTRY_NAME} --username ${FETCHMAIL_IMAGE_REGISTRY_USER} --password-stdin + ${CONTAINER_RUNTIME} push ${FETCHMAIL_IMAGE_FULLY_QUALIFIED} + +# PHONY +# ============================================================================== +# Declare the contents of the PHONY variable as phony. We keep that information +# in a variable so we can use it in if_changed. +.PHONY: ${PHONY} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d65cc81 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# fetchmail-docker + +[![Build Status](https://drone.cryptic.systems/api/badges/volker.raschek/fetchmail-docker/status.svg)](https://drone.cryptic.systems/volker.raschek/fetchmail-docker) +[![Docker Pulls](https://img.shields.io/docker/pulls/volkerraschek/fetchmail)](https://hub.docker.com/r/volkerraschek/fetchmail) + +This project contains all sources to build the container image +`docker.io/volkerraschek/fetchmail`. The primary goal of the image is to fetch +mails from external servers and forward them to on local running mail server. + +The configuration file will be automatically generated based on information from +a database. As table the fetchmail table from the schema of +[postfixadmin](https://github.com/postfixadmin/postfixadmin) is expected. + +## Usage + +Possible database types are `my` for MySQL and `Pg` for postgres. Make sure that +the database and the SMTP server are accessible. Otherwise, adjust the enclosed +docker-compose or docker command accordingly. Alternatively you can use +docker-compose in addition to the docker commands. + +### PostgreSQL + +```bash +$ docker run \ + --rm \ + --env DATABASE_TYPE: Pg \ + --env DATABASE_HOST: postgres \ + --env DATABASE_NAME: postgres \ + --env DATABASE_USER: fetchmail \ + --env DATABASE_PASSWORD: MySecretPassword \ + --network host \ + volkerraschek/fetchmail:latest +``` + +### MySQL + +```bash +$ docker run \ + --rm \ + --env DATABASE_TYPE: my \ + --env DATABASE_HOST: root \ + --env DATABASE_NAME: mysql \ + --env DATABASE_USER: fetchmail \ + --env DATABASE_PASSWORD: MySecretPassword \ + --network host \ + volkerraschek/fetchmail:latest +``` + +### docker-compose + +The repository contains a default `docker-compose.yml` file, which can be used +to start the container. To set the environment variables you need a `.env` file. +The `.dev_env` from this repository can be used for this. This must be located +exclusively in the same directory as the `docker-compose.yml` file and must be +renamed as `.env`. + +```yml +version: "3" +services: + fetchmail: + image: volkerraschek/fetchmail:latest + environment: + - DATABASE_TYPE=${DATABASE_TYPE} + - DATABASE_HOST=${DATABASE_HOST} + - DATABASE_NAME=${DATABASE_NAME} + - DATABASE_USER=${DATABASE_USER} + - DATABASE_PASSWORD=${DATABASE_PASSWORD} + network_mode: host +``` + +## Build container image manually + +To build the images manually check out the repository on +[github](https://github.com/volker-raschek/fetchmail-docker) with git and use +the make command to build the container image. + +```bash +make container-image/build +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e955902 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3" +services: + fetchmail: + container_name: fetchmail + image: volkerraschek/fetchmail-docker:latest + environment: + - DATABASE_TYPE=${DATABASE_TYPE} + - DATABASE_HOST=${DATABASE_HOST} + - DATABASE_NAME=${DATABASE_NAME} + - DATABASE_USER=${DATABASE_USER} + - DATABASE_PASSWORD=${DATABASE_PASSWORD} + network_mode: host diff --git a/fetchmail.pl b/fetchmail.pl new file mode 100755 index 0000000..18eff1f --- /dev/null +++ b/fetchmail.pl @@ -0,0 +1,134 @@ +#!/usr/bin/perl + +use DBI; +use MIME::Base64; +# use Data::Dumper; +use File::Temp qw/ mkstemp /; +use Sys::Syslog; +# require liblockfile-simple-perl +use LockFile::Simple qw(lock trylock unlock); + +###################################################################### +########## Change the following variables to fit your needs ########## + +# database settings + +# database backend - uncomment one of these +our $db_type=$ENV{'DATABASE_TYPE'}; +our $db_host=$ENV{'DATABASE_HOST'}; +our $db_name=$ENV{'DATABASE_NAME'}; +our $db_username=$ENV{'DATABASE_USER'}; +our $db_password=$ENV{'DATABASE_PASSWORD'}; + +# instead of changing this script, you can put your settings to /etc/mail/postfixadmin/fetchmail.conf +# just use perl syntax there to fill the variables listed above (without the "our" keyword). Example: +# $db_username = 'mail'; +if (-f "/etc/mail/postfixadmin/fetchmail.conf") { + require "/etc/mail/postfixadmin/fetchmail.conf"; +} + +#################### Don't change anything below! #################### +###################################################################### + +openlog("fetchmail-all", "pid", "mail"); + +sub log_and_die { + my($message) = @_; + syslog("err", $message); + die $message; +} + +# read options and arguments + +$configfile = "/etc/fetchmail-all/config"; + +@ARGS1 = @ARGV; + +while ($_ = shift @ARGS1) { + if (/^-/) { + if (/^--config$/) { + $configfile = shift @ARGS1 + } + } +} + +$run_dir="/run/fetchmail"; + +# use specified config file +if (-e $configfile) { + do $configfile; +} + +if($db_type eq "Pg" || $db_type eq "mysql") { + $dsn = "DBI:$db_type:database=$db_name;host=$db_host"; +} else { + log_and_die "unsupported db_type $db_type"; +} + +$lock_file=$run_dir . "/fetchmail-all.lock"; + +$lockmgr = LockFile::Simple->make(-autoclean => 1, -max => 1); +$lockmgr->lock($lock_file) || log_and_die "can't lock ${lock_file}"; + +# database connect +$dbh = DBI->connect($dsn, $db_username, $db_password) || log_and_die "cannot connect the database"; + +if($db_type eq "Pg") { + $sql_cond = "active = 't' AND date_part('epoch',now())-date_part('epoch',date)"; +} elsif($db_type eq "mysql") { + $sql_cond = "active = 1 AND unix_timestamp(now())-unix_timestamp(date)"; +} + +$sql = " + SELECT id,mailbox,src_server,src_auth,src_user,src_password,src_folder,fetchall,keep,protocol,mda,extra_options,usessl, sslcertck, sslcertpath, sslfingerprint + FROM fetchmail + WHERE $sql_cond > poll_time*60 + "; + +my (%config); +map{ + my ($id,$mailbox,$src_server,$src_auth,$src_user,$src_password,$src_folder,$fetchall,$keep,$protocol,$mda,$extra_options,$usessl,$sslcertck,$sslcertpath,$sslfingerprint)=@$_; + + syslog("info","fetch ${src_user}@${src_server} for ${mailbox}"); + + $cmd="user '${src_user}' there with password '".decode_base64($src_password)."'"; + $cmd.=" folder '${src_folder}'" if ($src_folder); + $cmd.=" mda ".$mda if ($mda); + +# $cmd.=" mda \"/usr/local/libexec/dovecot/deliver -m ${mailbox}\""; + $cmd.=" is '${mailbox}' here"; + + $cmd.=" keep" if ($keep); + $cmd.=" fetchall" if ($fetchall); + $cmd.=" ssl" if ($usessl); + $cmd.=" sslcertck" if($sslcertck); + $cmd.=" sslcertpath $sslcertpath" if ($sslcertck && $sslcertpath); + $cmd.=" sslfingerprint \"$sslfingerprint\"" if ($sslfingerprint); + $cmd.=" ".$extra_options if ($extra_options); + + $text=<quote($ret).", date=now() WHERE id=".$id; + $dbh->do($sql); +}@{$dbh->selectall_arrayref($sql)}; + +$lockmgr->unlock($lock_file); +closelog();