blob: 19c0093834e3ec80f12dfe3869aec7dad715fd35 [file] [log] [blame]
#!/usr/bin/env python3
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
import sys, os, json, re, time
import getpass
import subprocess
import readline
import argparse
import shutil
if sys.version_info <= (3, 3):
print("This script requires Python 3.4 or higher")
sys.exit(-1)
dopip = False
try:
from elasticsearch import Elasticsearch
from formatflowed import convertToWrapped
from netaddr import IPNetwork
except:
dopip = True
if dopip and (getpass.getuser() != "root"):
print("It looks like you need to install some python modules first")
print("Either run this as root to do so, or run: ")
print("pip3 install elasticsearch formatflowed netaddr")
sys.exit(-1)
elif dopip:
print("Before we get started, we need to install some modules")
print("Hang on!")
try:
subprocess.check_call(('pip3','install','elasticsearch','formatflowed', 'netaddr'))
from elasticsearch import Elasticsearch
from formatflowed import convertToWrapped
from netaddr import IPNetwork
except:
print("Oh dear, looks like this failed :(")
print("Please install elasticsearch and formatflowed before you try again:")
print("pip install elasticsearch formatflowed netaddr")
sys.exit(-1)
# CLI arg parsing
parser = argparse.ArgumentParser(description='Command line options.')
parser.add_argument('--defaults', dest='defaults', action='store_true',
help='Use default settings')
parser.add_argument('--dbhost', dest='dbhost', type=str, nargs=1,
help='ES backend hostname')
parser.add_argument('--dbport', dest='dbport', type=str, nargs=1,
help='DB port')
parser.add_argument('--dbname', dest='dbname', type=str, nargs=1,
help='ES DB name')
parser.add_argument('--mailserver', dest='mailserver', type=str, nargs=1,
help='Host name of outgoing mail server')
parser.add_argument('--mldom', dest='mldom', type=str, nargs=1,
help='Domains to accept mail for via UI')
parser.add_argument('--wordcloud', dest='wc', action='store_true',
help='Enable word cloud')
parser.add_argument('--skiponexist', dest='soe', action='store_true',
help='Skip setup if ES index exists')
parser.add_argument('--nocloud', dest='nwc', action='store_true',
help='Do not enable word cloud')
args = parser.parse_args()
print("Welcome to the Pony Mail setup script!")
print("Let's start by determining some settings...")
print("")
hostname = ""
port = 0
dbname = ""
mlserver = ""
mldom = ""
wc = ""
wce = False
# If called with --defaults (like from Docker), use default values
if args.defaults:
hostname = "localhost"
port = 9200
dbname = "ponymail"
mlserver = "localhost"
mldom = "example.org"
wc = "Y"
wce = True
# Accept CLI args, copy them
if args.dbhost:
hostname = args.dbhost[0]
if args.dbport:
port = int(args.dbport[0])
if args.dbname:
dbname = args.dbname[0]
if args.mailserver:
mlserver = args.mailserver[0]
if args.mldom:
mldom = args.mldom[0]
if args.wc:
wc = args.wc
if args.nwc:
wc = False
while hostname == "":
hostname = input("What is the hostname of the ElasticSearch server? (e.g. localhost): ")
while port < 1:
port = int(input("What port is ElasticSearch listening on? (normally 9200): "))
while dbname == "":
dbname = input("What would you like to call the mail index (e.g. ponymail): ")
while mlserver == "":
mlserver = input("What is the hostname of the outgoing mailserver? (e.g. mail.foo.org): ")
while mldom == "":
mldom = input("Which domains would you accept mail to from web-replies? (e.g. foo.org or *): ")
while wc == "":
wc = input("Would you like to enable the word cloud feature? (Y/N): ")
if wc.lower() == "y":
wce = True
print("Okay, I got all I need, setting up Pony Mail...")
print("Creating index " + dbname)
es = Elasticsearch([
{
'host': hostname,
'port': port,
'use_ssl': False,
'url_prefix': ''
}],
max_retries=5,
retry_on_timeout=True
)
mappings = {
"mbox" : {
"properties" : {
"@import_timestamp" : {
"type" : "date",
"format" : "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd"
},
"@version" : {
"type" : "long"
},
"body" : {
"type" : "string"
},
"date" : {
"type" : "date",
"store" : True,
"format" : "yyyy/MM/dd HH:mm:ss",
"index" : "not_analyzed"
},
"epoch" : {
"type" : "double",
"index" : "not_analyzed"
},
"from" : {
"type" : "string"
},
"from_raw" : {
"type" : "string",
"index" : "not_analyzed"
},
"in-reply-to" : {
"type" : "string"
},
"list" : {
"type" : "string"
},
"list_raw" : {
"type" : "string",
"index" : "not_analyzed"
},
"message-id" : {
"type" : "string"
},
"mid" : {
"type" : "string"
},
"private" : {
"type" : "boolean"
},
"references" : {
"type" : "string"
},
"subject" : {
"type" : "string"
},
"to" : {
"type" : "string"
}
}
},
"mbox_source" : {
"properties" : {
"source" : {
"type" : "string",
"index" : "not_analyzed"
}
}
},
"attachment" : {
"properties" : {
"source" : {
"type" : "binary"
}
}
},
"mbox_source" : {
"properties" : {
"source" : {
"type" : "binary"
}
}
}
}
# Check if index already exists
if es.indices.exists(dbname):
if args.soe:
print("Index already exists and SOE set, exiting quietly")
sys.exit(0)
else:
print("Error: ElasticSearch index %s already exists!" % dbname)
sys.exit(-1)
res = es.indices.create(index = dbname, body = {
"mappings" : mappings
}
)
print("Index created!")
print("Writing importer config (ponymail.cfg)")
with open("ponymail.cfg", "w") as f:
f.write("""
###############################################################
# Pony Mail Configuration file
# Main ES configuration
[elasticsearch]
hostname: %s
dbname: %s
port: %u
ssl: false
###############################################################
""" % (hostname, dbname, port))
f.close()
print("mod_lua configuration (config.lua)")
with open("../site/api/lib/config.lua", "w") as f:
f.write("""
local config = {
es_url = "http://%s:%u/%s/",
mailserver = "%s",
accepted_domains = "%s",
wordcloud = %s,
slow_count = false,
email_footer = nil, -- see the docs for how to set this up.
full_headers = false,
maxResults = 5000, -- max emails to return in one go. Might need to be bumped for large lists
admin_oauth = {}, -- list of domains that may do administrative oauth (private list access)
-- add 'www.googleapis.com' to the list for google oauth to decide, for instance.
oauth_fields = {}, -- used for specifying individual oauth handling parameters.
antispam = true -- Whether or not to add anti-spam measures aimed at anonymous users.
}
return config
""" % (hostname, port, dbname, mlserver, mldom, "true" if wce else "false"))
f.close()
print("Copying sample JS config to config.js (if needed)...")
if not os.path.exists("../site/js/config.js") and os.path.exists("../site/js/config.js.sample"):
shutil.copy("../site/js/config.js.sample", "../site/js/config.js")
print("All done, Pony Mail should...work now :)")
print("If you are using an external mail inbound server, \nmake sure to copy archiver.py and ponymail.cfg to it")