aboutsummaryrefslogtreecommitdiff
path: root/gn/services/mailman.scm
diff options
context:
space:
mode:
authorEfraim Flashner2020-04-28 05:44:02 -0500
committerEfraim Flashner2020-04-28 05:44:02 -0500
commit55ed6aaaac53876c0c398a13aa45b1e8a0530bbd (patch)
tree48ba6306d8be955e07def87a28e00a861fb11f3c /gn/services/mailman.scm
parent3b5182bdd5d9a24cbc15c47df6acbfdbc1c80d3a (diff)
downloadguix-bioinformatics-55ed6aaaac53876c0c398a13aa45b1e8a0530bbd.tar.gz
WIP mailman servicewip-mailman
Diffstat (limited to 'gn/services/mailman.scm')
-rw-r--r--gn/services/mailman.scm352
1 files changed, 352 insertions, 0 deletions
diff --git a/gn/services/mailman.scm b/gn/services/mailman.scm
new file mode 100644
index 0000000..d68924f
--- /dev/null
+++ b/gn/services/mailman.scm
@@ -0,0 +1,352 @@
+(define-module (gn services mailman)
+ #:use-module (guix gexp)
+ #:use-module (guix records)
+ #:use-module (gnu services)
+ #:use-module (gnu services base)
+ #:use-module (gnu services mcron)
+ #:use-module (gnu services networking)
+ #:use-module (gnu services shepherd)
+ #:use-module (gnu services web)
+ #:use-module (gn packages mailman)
+ #:use-module (ice-9 match)
+ #:export (mailman-service-type))
+
+(define-record-type* <mailman-database-configuration>
+ mailman-database-configuration
+ make-mailman-database-configuration
+ mailman-database-configuration?
+ (engine mailman-database-configuration-engine
+ (default "django.db.backends.postgresql_psycopg2"))
+ (name mailman-database-configuration-name
+ (default "mailman"))
+ (user mailman-database-configuration-user
+ (default "mailman"))
+ (password mailman-database-configuration-password
+ (default ""))
+ (host mailman-database-configuration-host
+ (default ""))
+ (port mailman-database-configuration-port
+ (default "")))
+
+(define-record-type* <mailman-settings-module>
+ mailman-settings-module make-mailman-settings-module
+ mailman-settings-module?
+ (database-configuration mailman-settings-module-database-configuration
+ (default (mailman-database-configuration)))
+ (secret-key-file mailman-settings-module-secret-key-file
+ (default "/etc/mailman/django-secret-key"))
+ (allowed-hosts mailman-settings-module-allowed-hosts)
+ (default-from-email mailman-settings-module-default-from-email)
+ (static-url mailman-settings-module-static-url
+ (default "/static/"))
+ (admins mailman-settings-module-admins
+ (default '()))
+ (debug? mailman-settings-module-debug?
+ (default #f))
+ (enable-rest-api? mailman-settings-module-enable-rest-api?
+ (default #t))
+ (enable-xmlrpc? mailman-settings-module-enable-xmlrpc?
+ (default #t))
+ (force-https-links? mailman-settings-module-force-https-links?
+ (default #t))
+ (extra-settings mailman-settings-module-extra-settings
+ (default "")))
+
+(define-record-type* <mailman-rest-api-configuration>
+ mailman-rest-api-configuration
+ make-mailman-rest-api-configuration
+ mailman-rest-api-configuration?
+ (url mailman-rest-api-configuration-url
+ (default "http://localhost:8001"))
+ (user mailman-rest-api-configuration-user
+ (default "restadmin"))
+ (pass mailman-rest-api-configuration-pass
+ (default "restpass"))
+ (archiver-key mailman-rest-api-configuration-archiver-key
+ (default "SecretArchiverAPIKey"))
+ )
+ ;;MAILMAN_REST_API_URL = 'http://localhost:8001' MAILMAN_REST_API_USER = 'restadmin' MAILMAN_REST_API_PASS = 'restpass' MAILMAN_ARCHIVER_KEY = 'SecretArchiverAPIKey' MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1')
+
+(define-record-type* <mailman-configuration>
+ mailman-configuration
+ make-mailman-configuration
+ mailman-configuration?
+ (package mailman-configuration-package ; package
+ (default mailman))
+ (database mailman-configuration-database)
+ (settings-module mailman-configuration-settings-module)
+ (static-path mailman-configuration-static-url
+ (default "/static/"))
+ )
+
+(define mailman-cronjobs
+ (match-lambda
+ (($ <mailman-configuration> package)
+ (list
+ #~(job '(next-hour '(0))
+ (string-append #$package "/bin/mailman digests --periodic"))
+ #~(job '(next-hour '(8))
+ (string-append #$package "/bin/mailman notify"))))))
+
+;(define mailman-activation
+; (match-lambda
+; (($ <mailman-configuration> package postgresql user)
+; #~(begin
+; (use-modules (guix build utils))
+; (let ((%user (getpwnam "mailman")))
+; ;; Prepare the environment for mailman:
+; ;; https://docs.mailman.io/en-us/install-from-binary/
+; (unless (directory-exists? #$work-dir)
+; (mkdir-p #$work-dir)
+; ;; These two are supposed to be recursive.
+; (chown #$work-dir (passwd:uid %user) (passwd:gid %user))
+; (chmod #$work-dir #o750)))))))
+
+;; Django uses a Python module for configuration, so this compiler generates a
+;; Python module from the configuration record.
+(define-gexp-compiler (mailman-settings-module-compiler
+ (file <mailman-settings-module>) system target)
+ (match file
+ (($ <mailman-settings-module> database-configuration secret-key-file
+ allowed-hosts default-from-email
+ static-url admins debug? enable-rest-api?
+ enable-xmlrpc? force-https-links?
+ extra-configuration)
+ (gexp->derivation
+ "mailman-settings"
+ (with-imported-modules '((guix build utils))
+ #~(let ((output #$output))
+ (define (create-__init__.py filename)
+ (call-with-output-file filename
+ (lambda (port) (display "" port))))
+
+ (use-modules (guix build utils)
+ (srfi srfi-1))
+
+ (mkdir-p (string-append output "/guix/mailman"))
+ (create-__init__.py
+ (string-append output "/guix/__init__.py"))
+ (create-__init__.py
+ (string-append output "/guix/mailman/__init__.py"))
+
+ (call-with-output-file
+ (string-append output "/guix/mailman/settings.py")
+ (lambda (port)
+ (display
+ (string-append "import os
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+# Configuration from Guix
+with open('" #$secret-key-file "') as f:
+ SECRET_KEY = f.read().strip()
+
+DEBUG = " #$(if debug? "True" "False") "
+
+
+ADMINS = [
+" #$(string-concatenate
+ (map (match-lambda
+ ((name email-address)
+ (string-append
+ "('" name "','" email-address "'),")))
+ admins))
+"]
+
+SITE_ID = 1
+
+ALLOWED_HOSTS = [
+" #$(string-concatenate
+ (map (lambda (allowed-host)
+ (string-append " '" allowed-host "'\n"))
+ allowed-hosts))
+"]
+
+# Mailman API credentials
+" #$(if enable-rest-api?
+ (match rest-api-configuration
+ (($ <mailman-rest-api-configuration>
+ url user pass archiver-key)
+ (string-append
+"MAILMAN_REST_API_URL = '"url"'\n"
+"MAILMAN_REST_API_USER = '"user"'\n"
+"MAILMAN_REST_API_PASS = '"pass"'\n"
+"MAILMAN_ARCHIVER_KEY = '"archiver-key"'\n"
+"MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1')\n")))
+ "") "
+
+INSTALLED_APPS = ( 'hyperkitty', 'postorius', 'django_mailman3', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_gravatar', 'compressor', 'haystack', 'django_extensions', 'django_q', 'allauth', 'allauth.account', 'allauth.socialaccount', 'django_mailman3.lib.auth.fedora', 'allauth.socialaccount.providers.openid', 'allauth.socialaccount.providers.github', 'allauth.socialaccount.providers.gitlab', 'allauth.socialaccount.providers.google', # 'allauth.socialaccount.providers.facebook', 'allauth.socialaccount.providers.twitter', 'allauth.socialaccount.providers.stackexchange', ) MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django_mailman3.middleware.TimezoneMiddleware', 'postorius.middleware.PostoriusMiddleware', ) ROOT_URLCONF = 'urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.template.context_processors.csrf', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'django_mailman3.context_processors.common', 'hyperkitty.context_processors.common', 'postorius.context_processors.postorius', ], }, }, ] WSGI_APPLICATION = 'wsgi.application'
+
+DATABASES = {
+ 'default': {
+" #$(match database-configuration
+ (($ <mailman-database-configuration>
+ engine name user password host port)
+ (string-append
+ " 'ENGINE': '" engine "',\n"
+ " 'NAME': '" name "',\n"
+ " 'USER': '" user "',\n"
+ " 'PASSWORD': '" password "',\n"
+ " 'HOST': '" host "',\n"
+ " 'PORT': '" port "',\n"))) "
+ },
+}
+
+AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]
+
+LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True
+
+" #$(if debug?
+ #~(string-append "STATIC_ROOT = '"
+ #$(file-append patchwork "/share/patchwork/htdocs")
+ "'")
+ #~(string-append "STATIC_URL = '" #$static-url "'")) "
+
+STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+ # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+ 'compressor.finders.CompressorFinder', )
+# Django 1.6+ defaults to a JSON serializer, but it won't work with
+# django-openid, see
+# https://bugs.launchpad.net/django-openid-auth/+bug/1252826
+SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
+LOGIN_URL = 'account_login'
+LOGIN_REDIRECT_URL = 'list_index'
+LOGOUT_URL = 'account_logout'
+
+DEFAULT_FROM_EMAIL = 'postorius@localhost.local'
+SERVER_EMAIL = 'root@localhost.local'
+# Change this when you have a real email backend
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+# Compatibility with Bootstrap 3
+from django.contrib.messages import constants as messages # flake8: noqa
+MESSAGE_TAGS = { messages.ERROR: 'danger' }
+
+#Social Auth
+AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend',
+ 'allauth.account.auth_backends.AuthenticationBackend', )
+#Django allauth
+ACCOUNT_AUTHENTICATION_METHOD = \"username_email\"
+ACCOUNT_EMAIL_REQUIRED = True
+ACCOUNT_EMAIL_VERIFICATION = \"mandatory\"
+ACCOUNT_DEFAULT_HTTP_PROTOCOL = \"http\"
+ACCOUNT_UNIQUE_EMAIL = True
+SOCIALACCOUNT_PROVIDERS = { 'openid': { 'SERVERS': [ dict(id='yahoo', name='Yahoo', openid_url='http://me.yahoo.com'), ], }, 'google': { 'SCOPE': ['profile', 'email'], 'AUTH_PARAMS': {'access_type': 'online'}, }, 'facebook': { 'METHOD': 'oauth2', 'SCOPE': ['email'], 'FIELDS': [ 'email', 'name', 'first_name', 'last_name', 'locale', 'timezone', ], 'VERSION': 'v2.4', }, }
+
+COMPRESS_PRECOMPILERS = ( ('text/less', 'lessc {infile} {outfile}'), ('text/x-scss', 'sassc -t compressed {infile} {outfile}'), ('text/x-sass', 'sassc -t compressed {infile} {outfile}'), )
+HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'PATH': os.path.join(BASE_DIR, \"fulltext_index\"), }, }
+
+Q_CLUSTER = { 'timeout': 300, 'save_limit': 100, 'orm': 'default', }
+
+LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' } }, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler' }, 'file':{ 'level': 'INFO', #'class': 'logging.handlers.RotatingFileHandler', 'class': 'logging.handlers.WatchedFileHandler', 'filename': os.path.join(BASE_DIR, 'logs', 'mailmansuite.log'), 'formatter': 'verbose', }, 'console': { 'class': 'logging.StreamHandler', 'formatter': 'simple', }, }, 'loggers': { 'django.request': { 'handlers': ['mail_admins', 'file'], 'level': 'ERROR', 'propagate': True, }, 'django': { 'handlers': ['file'], 'level': 'ERROR', 'propagate': True, }, 'hyperkitty': { 'handlers': ['file'], 'level': 'DEBUG', 'propagate': True, }, 'postorius': { 'handlers': ['console', 'file'], 'level': 'INFO', }, }, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s' }, 'simple': { 'format': '%(levelname)s %(message)s' }, }, #'root': { # 'handlers': ['file'], # 'level': 'INFO', #}, }
+
+if DEBUG == True: EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' EMAIL_FILE_PATH = os.path.join(BASE_DIR, 'emails')
+FILTER_VHOST = False
+POSTORIUS_TEMPLATE_BASE_URL = 'http://localhost:8000'
+try: from settings_local import * except ImportError: pass
+
+"))))))))))
+
+(define mailman-config-file
+ (plain-file "mailman.cfg"
+ "[mailman]
+layout: local
+[mta]
+# https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html#exim
+# For all Exim4 installations.
+incoming: mailman.mta.exim4.LMTP
+outgoing: mailman.mta.deliver.deliver
+# Typical single host with MTA and Mailman configuration.
+# Adjust to your system's configuration.
+# Exim happily works with the \"localhost\" alias rather than IP address.
+lmtp_host: localhost
+smtp_host: localhost
+# Mailman should not be run as root.
+# Use any convenient port > 1024. 8024 is a convention, but can be
+# changed if there is a conflict with other software using that port.
+lmtp_port: 8024
+# smtp_port rarely needs to be set.
+smtp_port: 25
+# Exim4-specific configuration parameter defaults. Currently empty.
+configuration: python:mailman.config.exim4
+\n"))
+
+;; https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html#exim4-configuration
+(define exim-config-file
+ (plain-file "exim.conf" "# /etc/exim4/conf.d/main/25_mm3_macros
+# The colon-separated list of domains served by Mailman.
+domainlist mm_domains=list.example.net
+
+MM3_LMTP_PORT=8024
+
+# MM3_HOME must be set to mailman's var directory, wherever it is
+# according to your installation.
+MM3_HOME=/opt/mailman/var
+MM3_UID=list
+MM3_GID=list
+
+################################################################
+# The configuration below is boilerplate:
+# you should not need to change it.
+
+# The path to the list receipt (used as the required file when
+# matching list addresses)
+MM3_LISTCHK=MM3_HOME/lists/${local_part}.${domain}
+
+# /etc/exim4/conf.d/router/455_mm3_router
+mailman3_router:
+driver = accept
+domains = +mm_domains
+require_files = MM3_LISTCHK
+local_part_suffix_optional
+local_part_suffix = \
+-bounces : -bounces+* : \
+-confirm : -confirm+* : \
+-join : -leave : \
+-owner : -request : \
+-subscribe : -unsubscribe
+transport = mailman3_transport
+
+# /etc/exim4/conf.d/transport/55_mm3_transport
+mailman3_transport:
+driver = smtp
+protocol = lmtp
+allow_localhost
+hosts = localhost
+port = MM3_LMTP_PORT
+rcpt_include_affixes = true
+\n")
+
+(define mailman-shepherd-service
+ (match-lambda
+ (($ <mailman-configuration> package)
+ (list (shepherd-service
+ (documentation "Run the Mailman server.")
+ (requirement '(networking))
+ (provision '(mailman))
+ (start #~(make-forkexec-constructor
+ (list
+ #$(file-append package "/bin/mailman")
+ "start")
+ #:environment-variables
+ (list (string-append "MAILMAN_CONFIG_FILE" mailman-config-file)
+ )
+ ))
+ (stop #~(make-kill-destructor
+ (list
+ #$(file-append package "/bin/mailman")
+ "stop"))))))))
+
+(define mailman-service-type
+ (service-type
+ (name 'mailman)
+ (extensions
+ (list (service-extension shepherd-root-service-type
+ mailman-shepherd-service)
+ (service-extension activation-service-type
+ mailman-activation)
+ (service-extension mcron-service-type
+ mailman-cronjobs)))
+ (description
+ "Run a Mailman server.")
+ (default-value (mailman-configuration))))
+