#!/bin/sh # RPCD backend for Nextcloud LuCI app (LXC version) # Copyright (C) 2025 CyberMind.fr CONFIG="nextcloud" LXC_NAME="nextcloud" LXC_ROOTFS="/srv/lxc/nextcloud/rootfs" uci_get() { uci -q get ${CONFIG}.$1; } uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; } # Check if LXC container is running lxc_running() { lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING" } # Check if container is installed lxc_installed() { [ -d "$LXC_ROOTFS" ] && [ -f "/srv/lxc/$LXC_NAME/config" ] } # Get service status get_status() { local enabled=$(uci_get main.enabled) local http_port=$(uci_get main.http_port) local data_path=$(uci_get main.data_path) local domain=$(uci_get main.domain) local ssl_enabled=$(uci_get ssl.enabled) local ssl_domain=$(uci_get ssl.domain) # Container status local running=0 local installed=0 local version="" local user_count=0 local disk_used="0" lxc_installed && installed=1 lxc_running && running=1 # Get Nextcloud info if running if [ "$running" = "1" ]; then version=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ -V" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "") user_count=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ user:list --output=json" 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l || echo 0) fi # Get disk usage if [ -d "${data_path:-/srv/nextcloud}/data" ]; then disk_used=$(du -sh "${data_path:-/srv/nextcloud}/data" 2>/dev/null | awk '{print $1}' || echo "0") fi # Get LAN IP local lan_ip lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.255.1") # Check web accessibility local web_accessible=0 if [ "$running" = "1" ]; then wget -q -O /dev/null --timeout=2 "http://127.0.0.1:${http_port:-8080}/status.php" 2>/dev/null && web_accessible=1 fi cat </dev/null) local data_path=$(echo "$input" | jsonfilter -e '@.data_path' 2>/dev/null) local domain=$(echo "$input" | jsonfilter -e '@.domain' 2>/dev/null) local admin_user=$(echo "$input" | jsonfilter -e '@.admin_user' 2>/dev/null) local memory_limit=$(echo "$input" | jsonfilter -e '@.memory_limit' 2>/dev/null) local upload_max=$(echo "$input" | jsonfilter -e '@.upload_max' 2>/dev/null) [ -n "$http_port" ] && uci_set main.http_port "$http_port" [ -n "$data_path" ] && uci_set main.data_path "$data_path" [ -n "$domain" ] && uci_set main.domain "$domain" [ -n "$admin_user" ] && uci_set main.admin_user "$admin_user" [ -n "$memory_limit" ] && uci_set main.memory_limit "$memory_limit" [ -n "$upload_max" ] && uci_set main.upload_max "$upload_max" echo '{"success": true}' } # Install Nextcloud do_install() { if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl install >/tmp/nextcloud-install.log 2>&1 & echo '{"success": true, "message": "Installation started in background"}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Start service do_start() { if [ -x /etc/init.d/nextcloud ]; then uci_set main.enabled '1' /etc/init.d/nextcloud start >/dev/null 2>&1 sleep 2 echo '{"success": true}' else echo '{"success": false, "error": "Service not installed"}' fi } # Stop service do_stop() { if [ -x /etc/init.d/nextcloud ]; then /etc/init.d/nextcloud stop >/dev/null 2>&1 echo '{"success": true}' else echo '{"success": false, "error": "Service not installed"}' fi } # Restart service do_restart() { if [ -x /etc/init.d/nextcloud ]; then /etc/init.d/nextcloud restart >/dev/null 2>&1 echo '{"success": true}' else echo '{"success": false, "error": "Service not installed"}' fi } # Update container do_update() { if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl update >/tmp/nextcloud-update.log 2>&1 & echo '{"success": true, "message": "Update started in background"}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Create backup do_backup() { local input read -r input local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null) if command -v nextcloudctl >/dev/null 2>&1; then local result if [ -n "$name" ]; then result=$(nextcloudctl backup "$name" 2>&1) else result=$(nextcloudctl backup 2>&1) fi echo "{\"success\": true, \"backup_name\": \"$result\"}" else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Restore backup do_restore() { local input read -r input local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null) if [ -z "$name" ]; then echo '{"success": false, "error": "No backup name specified"}' return fi if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl restore "$name" >/tmp/nextcloud-restore.log 2>&1 & echo '{"success": true, "message": "Restore started in background"}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # List backups list_backups() { local data_path=$(uci_get main.data_path) local backup_dir="${data_path:-/srv/nextcloud}/backups" local backups="[]" if [ -d "$backup_dir" ]; then backups=$(ls -1 "$backup_dir"/*-db.sql 2>/dev/null | while read f; do local name=$(basename "$f" -db.sql) local data_file="$backup_dir/$name-data.tar.gz" local size="N/A" local timestamp=0 if [ -f "$data_file" ]; then size=$(ls -lh "$data_file" | awk '{print $5}') fi timestamp=$(stat -c %Y "$f" 2>/dev/null || echo 0) printf '{"name":"%s","size":"%s","timestamp":%d},' "$name" "$size" "$timestamp" done | sed 's/,$//' | sed 's/^/[/;s/$/]/') fi [ -z "$backups" ] && backups="[]" echo "{\"backups\": $backups}" } # Enable SSL do_ssl_enable() { local input read -r input local domain=$(echo "$input" | jsonfilter -e '@.domain' 2>/dev/null) if [ -z "$domain" ]; then echo '{"success": false, "error": "No domain specified"}' return fi if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl ssl-enable "$domain" >/tmp/nextcloud-ssl.log 2>&1 echo '{"success": true, "message": "SSL enabled for '$domain'"}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Disable SSL do_ssl_disable() { if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl ssl-disable >/dev/null 2>&1 echo '{"success": true}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Run OCC command do_occ() { local input read -r input local cmd=$(echo "$input" | jsonfilter -e '@.command' 2>/dev/null) if [ -z "$cmd" ]; then echo '{"success": false, "error": "No command specified"}' return fi if command -v nextcloudctl >/dev/null 2>&1; then local output=$(nextcloudctl occ $cmd 2>&1) local escaped=$(echo "$output" | sed 's/"/\\"/g' | tr '\n' ' ') echo "{\"success\": true, \"output\": \"$escaped\"}" else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Get logs get_logs() { local lines=100 local log_content="" # Check install log if [ -f /tmp/nextcloud-install.log ]; then log_content=$(tail -n $lines /tmp/nextcloud-install.log 2>/dev/null | sed 's/"/\\"/g' | tr '\n' '|') fi echo "{\"logs\": \"$log_content\"}" } # List users list_users() { if ! lxc_running; then echo '{"users": []}' return fi local users_json users_json=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ user:list --output=json" 2>/dev/null) if [ -z "$users_json" ] || [ "$users_json" = "{}" ]; then echo '{"users": []}' return fi # Convert from {"uid":"displayname",...} to [{"uid":"x","displayname":"y"},...] # Use sed to transform the JSON local users_array users_array=$(echo "$users_json" | sed 's/^{//;s/}$//' | tr ',' '\n' | while read -r line; do uid=$(echo "$line" | cut -d'"' -f2) displayname=$(echo "$line" | cut -d'"' -f4) [ -n "$uid" ] && printf '{"uid":"%s","displayname":"%s"},' "$uid" "$displayname" done | sed 's/,$//') [ -z "$users_array" ] && users_array="" echo "{\"users\": [$users_array]}" } # Reset user password reset_password() { local input read -r input local uid=$(echo "$input" | jsonfilter -e '@.uid' 2>/dev/null) local password=$(echo "$input" | jsonfilter -e '@.password' 2>/dev/null) if [ -z "$uid" ] || [ -z "$password" ]; then echo '{"success": false, "error": "User ID and password required"}' return fi if ! lxc_running; then echo '{"success": false, "error": "Container not running"}' return fi # Reset password via OCC local result result=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "OC_PASS='$password' php /var/www/nextcloud/occ user:resetpassword --password-from-env '$uid'" 2>&1) local rc=$? if [ $rc -eq 0 ]; then echo '{"success": true, "message": "Password reset for '"$uid"'"}' else local escaped=$(echo "$result" | sed 's/"/\\"/g' | tr '\n' ' ') echo "{\"success\": false, \"error\": \"$escaped\"}" fi } # Uninstall Nextcloud do_uninstall() { if command -v nextcloudctl >/dev/null 2>&1; then nextcloudctl uninstall >/tmp/nextcloud-uninstall.log 2>&1 echo '{"success": true, "message": "Nextcloud uninstalled (data preserved)"}' else echo '{"success": false, "error": "nextcloudctl not found"}' fi } # Get storage stats get_storage() { local data_path=$(uci_get main.data_path) data_path="${data_path:-/srv/nextcloud}" local total_size="0" local data_size="0" local backup_size="0" if [ -d "$data_path" ]; then total_size=$(du -sh "$data_path" 2>/dev/null | awk '{print $1}' || echo "0") fi if [ -d "$data_path/data" ]; then data_size=$(du -sh "$data_path/data" 2>/dev/null | awk '{print $1}' || echo "0") fi if [ -d "$data_path/backups" ]; then backup_size=$(du -sh "$data_path/backups" 2>/dev/null | awk '{print $1}' || echo "0") fi # Get disk free space local disk_free=$(df -h "$data_path" 2>/dev/null | tail -1 | awk '{print $4}' || echo "0") local disk_total=$(df -h "$data_path" 2>/dev/null | tail -1 | awk '{print $2}' || echo "0") local disk_used_pct=$(df "$data_path" 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%' || echo "0") cat </dev/null) if [ -z "$name" ]; then echo '{"success": false, "error": "No backup name specified"}' return fi local data_path=$(uci_get main.data_path) local backup_dir="${data_path:-/srv/nextcloud}/backups" if [ -f "$backup_dir/$name-db.sql" ]; then rm -f "$backup_dir/$name-db.sql" "$backup_dir/$name-data.tar.gz" echo '{"success": true, "message": "Backup deleted: '"$name"'"}' else echo '{"success": false, "error": "Backup not found"}' fi } # Get connection URLs (CalDAV, CardDAV, WebDAV) get_connections() { local http_port=$(uci_get main.http_port) local ssl_enabled=$(uci_get ssl.enabled) local ssl_domain=$(uci_get ssl.domain) # Get LAN IP local lan_ip lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.255.1") local base_url="http://${lan_ip}:${http_port:-8080}" if [ "$ssl_enabled" = "1" ] && [ -n "$ssl_domain" ]; then base_url="https://${ssl_domain}" fi cat </", "carddav": "${base_url}/remote.php/dav/addressbooks/users//contacts/", "webdav": "${base_url}/remote.php/dav/files//", "ios_server": "$base_url", "ios_path": "/remote.php/dav", "davx5_url": "${base_url}/remote.php/dav", "desktop_url": "$base_url", "ios_app": "https://apps.apple.com/app/nextcloud/id1125420102", "android_app": "https://play.google.com/store/apps/details?id=com.nextcloud.client" } EOF } # Setup email/SMTP setup_mail() { local input read -r input local smtp_host=$(echo "$input" | jsonfilter -e '@.smtp_host' 2>/dev/null) local smtp_port=$(echo "$input" | jsonfilter -e '@.smtp_port' 2>/dev/null) local smtp_user=$(echo "$input" | jsonfilter -e '@.smtp_user' 2>/dev/null) local smtp_pass=$(echo "$input" | jsonfilter -e '@.smtp_pass' 2>/dev/null) local smtp_from=$(echo "$input" | jsonfilter -e '@.smtp_from' 2>/dev/null) if [ -z "$smtp_host" ]; then echo '{"success": false, "error": "SMTP host is required"}' return fi if ! lxc_running; then echo '{"success": false, "error": "Nextcloud container not running"}' return fi # Build command arguments local args="$smtp_host ${smtp_port:-587}" [ -n "$smtp_user" ] && args="$args $smtp_user" [ -n "$smtp_pass" ] && args="$args \"$smtp_pass\"" [ -n "$smtp_from" ] && args="$args $smtp_from" if nextcloudctl setup-mail $args >/tmp/nextcloud-mail.log 2>&1; then echo '{"success": true, "message": "SMTP configured successfully"}' else local err=$(tail -1 /tmp/nextcloud-mail.log 2>/dev/null || echo "Unknown error") echo "{\"success\": false, \"error\": \"$err\"}" fi } # Setup backup cron job setup_backup_cron() { if nextcloudctl setup-backup-cron >/tmp/nextcloud-cron.log 2>&1; then echo '{"success": true, "message": "Backup cron job configured"}' else local err=$(tail -1 /tmp/nextcloud-cron.log 2>/dev/null || echo "Unknown error") echo "{\"success\": false, \"error\": \"$err\"}" fi } # RPCD list method list_methods() { cat <<'EOF' { "status": {}, "get_config": {}, "save_config": {"http_port": "string", "data_path": "string", "domain": "string", "admin_user": "string", "memory_limit": "string", "upload_max": "string"}, "install": {}, "uninstall": {}, "start": {}, "stop": {}, "restart": {}, "update": {}, "backup": {"name": "string"}, "restore": {"name": "string"}, "list_backups": {}, "delete_backup": {"name": "string"}, "ssl_enable": {"domain": "string"}, "ssl_disable": {}, "occ": {"command": "string"}, "logs": {}, "list_users": {}, "reset_password": {"uid": "string", "password": "string"}, "get_storage": {}, "get_connections": {}, "setup_mail": {"smtp_host": "string", "smtp_port": "number", "smtp_user": "string", "smtp_pass": "string", "smtp_from": "string"}, "setup_backup_cron": {} } EOF } # Main entry point case "$1" in list) list_methods ;; call) case "$2" in status) get_status ;; get_config) get_config ;; save_config) save_config ;; install) do_install ;; uninstall) do_uninstall ;; start) do_start ;; stop) do_stop ;; restart) do_restart ;; update) do_update ;; backup) do_backup ;; restore) do_restore ;; list_backups) list_backups ;; delete_backup) delete_backup ;; ssl_enable) do_ssl_enable ;; ssl_disable) do_ssl_disable ;; occ) do_occ ;; logs) get_logs ;; list_users) list_users ;; reset_password) reset_password ;; get_storage) get_storage ;; get_connections) get_connections ;; setup_mail) setup_mail ;; setup_backup_cron) setup_backup_cron ;; *) echo '{"error": "Unknown method"}' ;; esac ;; *) echo '{"error": "Unknown command"}' ;; esac