Skip to content

Commit

Permalink
move to multi task to users
Browse files Browse the repository at this point in the history
  • Loading branch information
stephdl committed Oct 23, 2023
1 parent 6317674 commit ba27b6d
Show file tree
Hide file tree
Showing 17 changed files with 303 additions and 179 deletions.
19 changes: 11 additions & 8 deletions imageroot/actions/create-task/20configure
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import sys
import json

data = json.load(sys.stdin)
# we want multi task for the same user
task_id = data["task_id"]
# Local user to sync on
localuser = data["localuser"]
# remote credentials
Expand All @@ -20,6 +22,7 @@ remotepassword = data["remotepassword"]

# security encryption tls,ssl,""
security = data.get("security",'')

if security == "tls":
security = "--tls1"
elif security == "ssl":
Expand Down Expand Up @@ -55,20 +58,21 @@ else:
cron = data.get("cron","")
if cron != "":
if 'h' in cron:
cron_env = "1 */"+cron.replace('h','')+" * * * root /usr/local/bin/syncctl start "+localuser
cron_env = "1 */"+cron.replace('h','')+" * * * root /usr/local/bin/syncctl start "+localuser+'_'+task_id
elif 'm' in cron:
cron_env = "*/"+cron.replace('m','')+" * * * * root /usr/local/bin/syncctl start "+localuser
f = open("./cron/"+localuser+".cron", "w", encoding="utf-8")
cron_env = "*/"+cron.replace('m','')+" * * * * root /usr/local/bin/syncctl start "+localuser+'_'+task_id
f = open("./cron/"+localuser+'_'+task_id+".cron", "w", encoding="utf-8")
# MAIL_HOSTNAME is an env variable used by perl/imapsync
f.write('MAIL_HOSTNAME="${MAIL_HOSTNAME}"'+"\n")
f.write(cron_env+"\n")
f.close()
# cron does not exist we remove or ignore
elif cron == "" and os.path.exists("./cron/"+localuser+".cron"):
os.remove("./cron/"+localuser+".cron")
elif cron == "" and os.path.exists("./cron/"+localuser+'_'+task_id+".cron"):
os.remove("./cron/"+localuser+'_'+task_id+".cron")


user_env = f"""
TASK_ID={task_id}
LOCALUSER={localuser}
REMOTEUSERNAME={remoteusername}
REMOTEHOSTNAME={remotehostname}
Expand All @@ -83,11 +87,10 @@ CRON={cron}
"""
os.umask(0o77)

f = open("./imapsync/"+localuser+".env", "w", encoding="utf-8")
f = open("./imapsync/"+localuser+'_'+task_id+".env", "w", encoding="utf-8")
f.write(user_env)
f.close()

f = open("./imapsync/"+localuser+".pwd", "w", encoding="utf-8")
f = open("./imapsync/"+localuser+'_'+task_id+".pwd", "w", encoding="utf-8")
f.write(remotepassword)
f.close()

32 changes: 32 additions & 0 deletions imageroot/actions/create-task/30Imapsync_validation
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env python3

#
# Copyright (C) 2023 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import os
import sys
import json
import agent
import subprocess

agent.set_weight(os.path.basename(__file__), 0)
data = json.load(sys.stdin)
# task id
task_id = data["task_id"]
# Local user to sync on
localuser= data["localuser"]
result = subprocess.run(["podman","exec","imapsync","syncctl", "validation",localuser+'_'+task_id], check=False, capture_output=True, text=True)
if 'ERR_AUTHENTICATION_FAILURE_USER1' in result.stdout:
agent.set_status('validation-failed')
json.dump([{'field':'createTask','parameter':'createTask','value':'bad_user_credentials','error':'bad_user_credentials'}],fp=sys.stdout, default=str)
sys.exit(2)
elif 'SSL connect attempt failed' in result.stdout:
agent.set_status('validation-failed')
json.dump([{'field':'createTask','parameter':'createTask','value':'SSL_encryption_error','error':'SSL_encryption_error'}],fp=sys.stdout, default=str)
sys.exit(4)
elif 'ERR_CONNECTION_FAILURE_HOST1' in result.stdout:
agent.set_status('validation-failed')
json.dump([{'field':'createTask','parameter':'createTask','value':'Check_hostname_or_tcp_port','error':'Check_hostname_or_tcp_port'}],fp=sys.stdout, default=str)
sys.exit(3)
4 changes: 2 additions & 2 deletions imageroot/actions/create-task/50start-service
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ import json
import agent

data = json.load(sys.stdin)

task_id = data["task_id"]
localuser = data["localuser"]
agent.run_helper("run-imapsync", "restart", localuser).check_returncode()
agent.run_helper("run-imapsync", "restart",localuser+'_'+task_id).check_returncode()
20 changes: 17 additions & 3 deletions imageroot/actions/create-task/validate-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
"delete_local": true,
"exclude": "folder1,folder2",
"delete_remote": false,
"cron":"5m"
"cron": "5m",
"task_id":""
}
],
"type": "object",
"required": [
"task_id",
"localuser",
"remoteusername",
"remotehostname",
Expand All @@ -36,7 +38,19 @@
"type": "string",
"title": "Cron task",
"description": "Start a task by a cron",
"minLength": 1
"enum": [
"",
"5m",
"15m",
"30m",
"45m",
"1h"
]
},
"task_id": {
"type": "string",
"title": "Task ID",
"description": "A local user can run many tasks, we need ID"
},
"localuser": {
"type": "string",
Expand Down Expand Up @@ -102,4 +116,4 @@
"description": "Folders exclusion"
}
}
}
}
4 changes: 2 additions & 2 deletions imageroot/actions/delete-task/10stop-service
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ import json
import agent

data = json.load(sys.stdin)

localuser = data["localuser"]
agent.run_helper("run-imapsync", "stop", localuser).check_returncode()
task_id = data["task_id"]
agent.run_helper("run-imapsync", "stop", localuser+'_'+task_id).check_returncode()
3 changes: 2 additions & 1 deletion imageroot/actions/delete-task/20delete
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import glob
import os

data = json.load(sys.stdin)
task_id = data["task_id"]
localuser = data["localuser"]

for file in glob.iglob("imapsync/"+localuser+".*"):
for file in glob.iglob("imapsync/"+localuser+'_'+task_id+".*"):
os.remove(file)
16 changes: 6 additions & 10 deletions imageroot/actions/list-tasks/20read
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,19 @@ else:
for mailbox in user_mailboxes:
if 'enabled' in mailbox and not mailbox['enabled']:
continue # Skip disabled mailboxes
if os.path.exists("imapsync/"+mailbox['user']+".env"):
continue

box = {
"name": mailbox['user'],
"label": mailbox['user'],
"value": mailbox['user']
}
config['enabled_mailboxes'].append(box)



# we load env file from imapsync/*.env and remote password
user_properties = []
env_files = list(glob.iglob("imapsync/*.env"))
for user in env_files:
user = user.removesuffix('.env').removeprefix('imapsync/')
data = agent.read_envfile('imapsync/'+user+'.env')
for task_id in env_files:
task_id = task_id.removesuffix('.env').removeprefix('imapsync/')
data = agent.read_envfile('imapsync/'+task_id+'.env')
# we want minor keys
data = {key.lower(): value for key, value in data.items()}

Expand All @@ -83,12 +79,12 @@ else:
data["delete_remote"] = False

# remote password (single line)
with open('imapsync/'+user+'.pwd', 'r') as file:
with open('imapsync/'+task_id+'.pwd', 'r') as file:
env_lines = file.readlines()
for line in env_lines:
data['remotepassword']=line
#test if service is running
service_status = True if os.path.exists("imapsync/"+user+".pid") else False
service_status = True if os.path.exists("imapsync/"+task_id+".pid") else False

data["service_running"]=service_status
user_properties.append(data)
Expand Down
4 changes: 2 additions & 2 deletions imageroot/actions/start-all-tasks/20start-all-tasks
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import glob
import agent

for file in glob.iglob("imapsync/*.env"):
localuser = file.removeprefix('imapsync/').removesuffix('.env')
agent.run_helper("run-imapsync", "restart", localuser)
task_id = file.removeprefix('imapsync/').removesuffix('.env')
agent.run_helper("run-imapsync", "restart", task_id)
4 changes: 2 additions & 2 deletions imageroot/actions/start-task/50start-service
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ import json
import agent

data = json.load(sys.stdin)

task_id = data["task_id"]
localuser = data["localuser"]
agent.run_helper("run-imapsync", "start", localuser).check_returncode()
agent.run_helper("run-imapsync", "start", localuser+'_'+task_id).check_returncode()
4 changes: 2 additions & 2 deletions imageroot/actions/stop-all-tasks/20stop-all-tasks
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import glob
import agent

for file in glob.iglob("imapsync/*.env"):
localuser = file.removeprefix('imapsync/').removesuffix('.env')
agent.run_helper("run-imapsync", "stop", localuser)
task_id = file.removeprefix('imapsync/').removesuffix('.env')
agent.run_helper("run-imapsync", "stop", task_id)

4 changes: 2 additions & 2 deletions imageroot/actions/stop-task/50stop-service
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ import json
import agent

data = json.load(sys.stdin)

task_id = data["task_id"]
localuser = data["localuser"]
agent.run_helper("run-imapsync", "stop", localuser).check_returncode()
agent.run_helper("run-imapsync", "stop",localuser+'_'+task_id).check_returncode()
10 changes: 5 additions & 5 deletions imageroot/bin/run-imapsync
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
# helper to trigger users task. we need two arguments

action=${1:-}
localuser=${2:-}
task_id=${2:-}

if [[ "$action" == 'start' ]];then
/usr/bin/podman exec -d imapsync /usr/local/bin/syncctl start "$localuser"
/usr/bin/podman exec -d imapsync /usr/local/bin/syncctl start "$task_id"
elif [[ "$action" == 'restart' ]];then
/usr/bin/podman exec -d imapsync /usr/local/bin/syncctl stop "$localuser"
/usr/bin/podman exec -d imapsync /usr/local/bin/syncctl start "$localuser"
/usr/bin/podman exec imapsync /usr/local/bin/syncctl stop "$task_id"
/usr/bin/podman exec -d imapsync /usr/local/bin/syncctl start "$task_id"
elif [[ "$action" == 'stop' ]];then
/usr/bin/podman exec imapsync /usr/local/bin/syncctl stop "$localuser"
/usr/bin/podman exec imapsync /usr/local/bin/syncctl stop "$task_id"
fi
35 changes: 18 additions & 17 deletions imapsync/usr/local/bin/syncctl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#

# control imapsync : syncctl {start,status,stop} username
# start : start the sync for the user username
# status : display the state (Running, Stopped) of the sync for the user username
# stop : stop the sync for the user username
# validate: validate the settings by trying to just login to imapsync and quit
# control imapsync : syncctl {start,status,stop} task_id
# start : start the sync for the task_id
# status : display the state (Running, Stopped) of the sync for the task_id
# stop : stop the sync for the task_id
# validation: validate the settings by trying to just login to imapsync and quit

# shellcheck shell=dash

Expand All @@ -20,44 +20,45 @@ if [[ $# -ne 2 ]];then
exit 0
fi
action=${1}
user=${2}
task_id=${2}

source /etc/imapsync/${user}.env
source /etc/imapsync/${task_id}.env


if [[ "$action" == "start" ]]; then
# source breaks with `|`
exclude_regex=$(echo "$EXCLUDE" | tr ',' '|')

flock -x -n "/etc/imapsync/${user}.lock" \
/usr/bin/imapsync --pidfile "/etc/imapsync/${user}.pid" --nolog \
flock -x -n "/etc/imapsync/${task_id}.lock" \
/usr/bin/imapsync --pidfile "/etc/imapsync/${task_id}.pid" --nolog \
--noauthmd5 --noreleasecheck --allowsizemismatch \
--skipsize --nofoldersizes "${DELETE_LOCAL}" "${DELETEFOLDERS}" "${DELETE_REMOTE}" "${EXPUNGE_REMOTE}" \
--fastio1 --fastio2 --buffersize 8192000 \
--host1 "${REMOTEHOSTNAME}" --user1 "${REMOTEUSERNAME}" \
--passfile1 "/etc/imapsync/${user}.pwd" --port1 "${REMOTEPORT}" "${SECURITY}" \
--passfile1 "/etc/imapsync/${task_id}.pwd" --port1 "${REMOTEPORT}" "${SECURITY}" \
--host2 "${MAIL_HOSTNAME}" --user2 "${LOCALUSER}*vmail" \
--port2 143 --tls2 --passfile2 /etc/imapsync/vmail.pwd \
--exclude "^Public$|^Shared$"${exclude_regex}"" > /proc/$(cat /var/run/crond.pid)/fd/1 2>&1

elif [[ "$action" == "stop" ]];then
if [[ -f "/etc/imapsync/${user}.pid" ]]; then
kill "$(cat "/etc/imapsync/${user}.pid")"
if [[ -f "/etc/imapsync/${task_id}.pid" ]]; then
kill "$(cat "/etc/imapsync/${task_id}.pid")"
# it can take a long time to imapsync to fully stop
rm -f "/etc/imapsync/${task_id}.lock"
else
echo "Not Running"
fi

elif [[ "$action" == "status" ]];then
if [[ -f "/etc/imapsync/${user}.pid" ]];then
if [[ -f "/etc/imapsync/${task_id}.pid" ]];then
echo 'Running'
else
echo 'Stopped'
fi
elif [[ "$action" == "validation" ]];then
/usr/bin/imapsync --nolog \
--host1 "${REMOTEHOSTNAME}" --user1 "${REMOTEUSERNAME}" \
--passfile1 "/etc/imapsync/${user}.pwd"--port1 "${REMOTEPORT}" "${SECURITY}" \
--host2 "${MAIL_HOSTNAME}" --user2 "${LOCALUSER}*vmail" \
--passfile1 "/etc/imapsync/${task_id}.pwd" --port1 "${REMOTEPORT}" "${SECURITY}" \
--host2 "${MAIL_HOSTNAME}" --user2 "${LOCALUSER}*vmail" \
--port2 143 --tls2 --passfile2 /etc/imapsync/vmail.pwd \
--justlogin --timeout 10 > /dev/null
--justlogin --timeout 10
fi
13 changes: 11 additions & 2 deletions ui/public/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"remoteport": "IMAP TCP Port",
"exclude_folder": "Folders exclusion",
"write_one_exclusion_per_line": "Write one exclusion per line",
"delete_on_local": "Delete on local",
"delete_on_local": "Delete Email on local that are not on remote (full clone)",
"delete_tips": "Delete local emails and folders not present on the remote IMAP account",
"trashsync_tips": "Synchronize the deleted emails and folders on the remote server",
"enabled": "Enabled",
Expand All @@ -83,7 +83,16 @@
"minutes":"minutes",
"no_cron": "No sheduled task",
"select_your_cron": "Shedule your tasks",
"set_when_you_want_the_task_start": "Select when to start your task"
"set_when_you_want_the_task_start": "Select when to start your task",
"remove_mails":"Email removal",
"imapsync_removal_explanation":"Imapsync can remove emails on remote once synchronized on the local server or remove all emails on local that are not on the remote server",
"no_deletion":"No deletion",
"delete_on_remote":"Delete Email on remote once synchronized",
"bad_user_credentials": "Verify the user credentials, the remote server refused to authenticate",
"Check_hostname_or_tcp_port": "Verify the Hostname and the TCP port, the remote server refused to authenticate",
"SSL_encryption_error":"Verify the TLS encryption, IMAPS of STARTTLS",
"localuser_string_gte": "The local username cannot be empty"

},
"about": {
"title": "About"
Expand Down
Loading

0 comments on commit ba27b6d

Please sign in to comment.