first commit

This commit is contained in:
Stefan Hacker 2026-01-10 13:03:28 +01:00
commit 601a0c2757
20 changed files with 1540 additions and 0 deletions

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# Mail2Fax für STARFACE
Custom Block für den STARFACE Module Designer - E-Mails abrufen und PDF-Anhänge als Fax versenden.
## Quick Start
```bash
# 1. Java 21 installieren (falls nötig)
./install-java21.sh
# 2. STARFACE APIs holen
./fetch-starface-libs.sh <starface-ip>
# 3. Block kompilieren
cd v8-9-10
./build-block.sh
```
## Projektstruktur
```
mail2fax/
├── fetch-starface-libs.sh # Holt STARFACE APIs per SCP
├── install-java21.sh # Installiert Java 21
├── README.md
└── v8-9-10/ # Für STARFACE 8/9/10
├── Mail2FaxBlock.java # Custom Block Quellcode
├── build-block.sh # Kompilier-Script
├── libs/starface/ # STARFACE JARs
└── README.md # Detaillierte Anleitung
```
## Funktionsweise
1. Block ruft E-Mails per IMAP/POP3 ab
2. Ziel-Faxnummer wird aus dem Betreff gelesen
3. PDF-Anhänge werden als Fax gesendet
4. E-Mails werden als gelesen markiert oder gelöscht
## Installation
Siehe [v8-9-10/README.md](v8-9-10/README.md) für die Anleitung zur Integration im Module Designer.

BIN
fax2Mail_v36.sfm Normal file

Binary file not shown.

275
fetch-starface-libs.sh Executable file
View File

@ -0,0 +1,275 @@
#!/bin/bash
#
# STARFACE API Libraries Fetcher
# Holt die benötigten JARs und Klassen von einer STARFACE Installation via SCP
#
# Verwendung:
# ./fetch-starface-libs.sh <starface-host> [user] [target-dir]
#
# Beispiele:
# ./fetch-starface-libs.sh 192.168.1.100
# ./fetch-starface-libs.sh starface.firma.local root
# ./fetch-starface-libs.sh 10.0.0.5 root v8-9-10/libs/starface
#
set -e
# Farben für Output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Argumente
STARFACE_HOST="${1:-}"
SSH_USER="${2:-root}"
TARGET_DIR="${3:-v8-9-10/libs/starface}"
# Script-Verzeichnis
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo -e "${BLUE}========================================"
echo " STARFACE API Libraries Fetcher"
echo -e "========================================${NC}"
echo ""
# Hilfe anzeigen
if [ -z "$STARFACE_HOST" ] || [ "$STARFACE_HOST" == "-h" ] || [ "$STARFACE_HOST" == "--help" ]; then
echo "Verwendung: $0 <starface-host> [user] [target-dir]"
echo ""
echo "Argumente:"
echo " starface-host IP-Adresse oder Hostname der STARFACE (erforderlich)"
echo " user SSH-Benutzer (Standard: root)"
echo " target-dir Zielverzeichnis für die JARs (Standard: v8-9-10/libs/starface)"
echo ""
echo "Beispiele:"
echo " $0 192.168.1.100"
echo " $0 starface.firma.local root"
echo " $0 10.0.0.5 root v8-9-10/libs/starface"
echo ""
echo "Voraussetzungen:"
echo " - SSH-Zugang zur STARFACE (root oder sudo-Benutzer)"
echo " - SSH-Key oder Passwort-Authentifizierung"
echo ""
echo "Tipp: SSH-Key einrichten für passwortloses Login:"
echo " ssh-copy-id root@<starface-host>"
exit 0
fi
echo -e "STARFACE Host: ${GREEN}$STARFACE_HOST${NC}"
echo -e "SSH User: ${GREEN}$SSH_USER${NC}"
echo -e "Zielordner: ${GREEN}$TARGET_DIR${NC}"
echo ""
# Zielverzeichnis erstellen
mkdir -p "$TARGET_DIR"
# STARFACE Version und Pfade ermitteln
echo -e "${YELLOW}Ermittle STARFACE Version...${NC}"
STARFACE_VERSION=$(ssh -o ConnectTimeout=10 "${SSH_USER}@${STARFACE_HOST}" \
"cat /etc/starface-release 2>/dev/null || head -1 /opt/starface/version.txt 2>/dev/null || echo 'unknown'" 2>/dev/null || echo "connection-failed")
if [ "$STARFACE_VERSION" == "connection-failed" ]; then
echo -e "${RED}Fehler: Konnte keine Verbindung zu $STARFACE_HOST herstellen${NC}"
echo ""
echo "Mögliche Ursachen:"
echo " - Falscher Hostname/IP"
echo " - SSH-Dienst nicht erreichbar"
echo " - Firewall blockiert Port 22"
echo ""
echo "Prüfe die Verbindung mit: ssh ${SSH_USER}@${STARFACE_HOST}"
exit 1
fi
echo -e "STARFACE Version: ${GREEN}$STARFACE_VERSION${NC}"
echo ""
# Pfade
LIB_PATH="/opt/tomcat/webapps/localhost/starface/WEB-INF/lib"
CLASSES_PATH="/opt/tomcat/webapps/localhost/starface/WEB-INF/classes"
# Prüfen welcher Pfad existiert
echo -e "${YELLOW}Prüfe Verzeichnisstruktur...${NC}"
ACTUAL_LIB_PATH=$(ssh "${SSH_USER}@${STARFACE_HOST}" \
"if [ -d '$LIB_PATH' ]; then echo '$LIB_PATH'; fi" 2>/dev/null)
ACTUAL_CLASSES_PATH=$(ssh "${SSH_USER}@${STARFACE_HOST}" \
"if [ -d '$CLASSES_PATH' ]; then echo '$CLASSES_PATH'; fi" 2>/dev/null)
if [ -z "$ACTUAL_LIB_PATH" ]; then
echo -e "${RED}Fehler: STARFACE Bibliotheksverzeichnis nicht gefunden${NC}"
exit 1
fi
echo -e "Bibliothekspfad: ${GREEN}$ACTUAL_LIB_PATH${NC}"
echo -e "Klassenpfad: ${GREEN}$ACTUAL_CLASSES_PATH${NC}"
echo ""
# Alte Stub-Dateien löschen
rm -f "$TARGET_DIR/starface-stubs.jar" 2>/dev/null
rm -f "$TARGET_DIR/starface-classes.jar" 2>/dev/null
# ============================================================
# STARFACE 9.x: Module-API ist in /classes, nicht in JARs
# ============================================================
echo -e "${YELLOW}Prüfe auf STARFACE 9.x Struktur (Module-API in classes)...${NC}"
HAS_MODULE_CLASSES=$(ssh "${SSH_USER}@${STARFACE_HOST}" \
"if [ -d '$CLASSES_PATH/de/vertico/starface/module' ]; then echo 'yes'; fi" 2>/dev/null)
if [ "$HAS_MODULE_CLASSES" == "yes" ]; then
echo -e "${GREEN}STARFACE 9.x erkannt - Module-API in classes gefunden${NC}"
echo ""
echo -e "${YELLOW}Erstelle JAR aus STARFACE classes...${NC}"
# Temporäres Verzeichnis auf STARFACE erstellen und JAR bauen
ssh "${SSH_USER}@${STARFACE_HOST}" "cd $CLASSES_PATH && jar cf /tmp/starface-classes.jar de/vertico/starface/module de/vertico/starface/persistence 2>/dev/null || jar cf /tmp/starface-classes.jar de/vertico/starface/module"
# JAR herunterladen
scp -q "${SSH_USER}@${STARFACE_HOST}:/tmp/starface-classes.jar" "$TARGET_DIR/"
ssh "${SSH_USER}@${STARFACE_HOST}" "rm -f /tmp/starface-classes.jar"
echo -e " ${GREEN}starface-classes.jar erstellt${NC}"
echo ""
fi
# ============================================================
# JAR-Dateien herunterladen
# ============================================================
# JARs für STARFACE 9.x (neue Namensgebung)
STARFACE9_JARS=(
"starface-db-*.jar"
"starface-commons-*.jar"
"starface-domain-objects-*.jar"
"starface-rpc-*.jar"
"starface-ng-*.jar"
)
# Allgemeine JARs (alle Versionen)
COMMON_JARS=(
"log4j-api-*.jar"
"log4j-core-*.jar"
)
# Legacy JARs (STARFACE 8.x und älter)
LEGACY_JARS=(
"starface-module-*.jar"
"starface-persistence-*.jar"
)
echo -e "${YELLOW}Lade STARFACE JARs herunter...${NC}"
download_jars() {
local jar_array=("$@")
for pattern in "${jar_array[@]}"; do
files=$(ssh "${SSH_USER}@${STARFACE_HOST}" "ls $ACTUAL_LIB_PATH/$pattern 2>/dev/null" || true)
if [ -n "$files" ]; then
for file in $files; do
filename=$(basename "$file")
echo -n " $filename: "
scp -q "${SSH_USER}@${STARFACE_HOST}:$file" "$TARGET_DIR/" 2>/dev/null
echo -e "${GREEN}OK${NC}"
done
fi
done
}
# Download alle JAR-Typen
download_jars "${STARFACE9_JARS[@]}"
download_jars "${COMMON_JARS[@]}"
download_jars "${LEGACY_JARS[@]}"
echo ""
# ============================================================
# Zusammenfassung
# ============================================================
JAR_COUNT=$(ls -1 "$TARGET_DIR"/*.jar 2>/dev/null | wc -l)
echo -e "${BLUE}========================================"
echo -e " Download abgeschlossen!"
echo -e "========================================${NC}"
echo ""
echo -e "Heruntergeladene Dateien: ${GREEN}$JAR_COUNT${NC}"
echo -e "Speicherort: ${GREEN}$TARGET_DIR/${NC}"
echo ""
if [ "$JAR_COUNT" -gt 0 ]; then
echo "Inhalt:"
ls -lh "$TARGET_DIR"/*.jar 2>/dev/null | while read line; do
filename=$(echo "$line" | awk '{print $NF}')
size=$(echo "$line" | awk '{print $5}')
basename=$(basename "$filename")
echo -e " ${GREEN}$basename${NC} ($size)"
done
echo ""
# Prüfen ob Module-API vorhanden ist
if [ -f "$TARGET_DIR/starface-classes.jar" ] || ls "$TARGET_DIR"/starface-module-*.jar 2>/dev/null | grep -q .; then
echo -e "${GREEN}Module-API gefunden - du kannst jetzt bauen:${NC}"
echo " cd v8-9-10 && ./build-block.sh"
else
echo -e "${YELLOW}Warnung: Module-API nicht gefunden${NC}"
echo "Das Build-Script wird Stub-Klassen erstellen."
fi
else
echo -e "${RED}Keine JARs heruntergeladen. Prüfe die Verbindung und Berechtigungen.${NC}"
exit 1
fi
# ============================================================
# JavaMail-Bibliothek prüfen und ggf. installieren
# ============================================================
echo ""
echo -e "${YELLOW}Prüfe JavaMail-Bibliothek auf STARFACE...${NC}"
HAS_JAVAMAIL=$(ssh "${SSH_USER}@${STARFACE_HOST}" \
"ls $ACTUAL_LIB_PATH/*mail*.jar 2>/dev/null | head -1" || true)
if [ -n "$HAS_JAVAMAIL" ]; then
echo -e "${GREEN}JavaMail bereits vorhanden:${NC}"
ssh "${SSH_USER}@${STARFACE_HOST}" "ls -1 $ACTUAL_LIB_PATH/*mail*.jar 2>/dev/null" | while read jar; do
echo -e " ${GREEN}$(basename $jar)${NC}"
done
else
echo -e "${YELLOW}JavaMail nicht gefunden - wird installiert...${NC}"
# JavaMail herunterladen falls nicht lokal vorhanden
DEPS_DIR="$SCRIPT_DIR/v8-9-10/libs/deps"
mkdir -p "$DEPS_DIR"
if [ ! -f "$DEPS_DIR/javax.mail.jar" ]; then
echo " Lade javax.mail.jar herunter..."
curl -sL -o "$DEPS_DIR/javax.mail.jar" \
"https://repo1.maven.org/maven2/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar"
fi
if [ ! -f "$DEPS_DIR/activation.jar" ]; then
echo " Lade activation.jar herunter..."
curl -sL -o "$DEPS_DIR/activation.jar" \
"https://repo1.maven.org/maven2/javax/activation/activation/1.1.1/activation-1.1.1.jar"
fi
# Auf STARFACE kopieren
echo " Kopiere nach STARFACE..."
scp -q "$DEPS_DIR/javax.mail.jar" "${SSH_USER}@${STARFACE_HOST}:$ACTUAL_LIB_PATH/"
scp -q "$DEPS_DIR/activation.jar" "${SSH_USER}@${STARFACE_HOST}:$ACTUAL_LIB_PATH/"
echo -e "${GREEN}JavaMail erfolgreich auf STARFACE installiert!${NC}"
echo ""
echo -e "${YELLOW}WICHTIG: STARFACE-Dienst neustarten für Aktivierung:${NC}"
echo " ssh ${SSH_USER}@${STARFACE_HOST} 'systemctl restart tomcat'"
fi
echo ""
echo -e "${GREEN}Fertig! Du kannst jetzt den Mail2FaxBlock kompilieren:${NC}"
echo " cd v8-9-10 && ./build-block.sh"

194
install-java21.sh Executable file
View File

@ -0,0 +1,194 @@
#!/bin/bash
#
# Java 21 JDK Installationsscript
# Für Debian/Ubuntu und andere Linux-Distributionen
#
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}========================================"
echo " Java 21 JDK Installation"
echo -e "========================================${NC}"
echo ""
# Prüfen ob Java 21 bereits installiert ist
if command -v javac &> /dev/null; then
JAVA_VERSION=$(javac -version 2>&1 | awk '{print $2}' | cut -d'.' -f1)
if [ "$JAVA_VERSION" == "21" ]; then
echo -e "${GREEN}Java 21 ist bereits installiert!${NC}"
javac -version
java -version
exit 0
else
echo -e "${YELLOW}Java $JAVA_VERSION gefunden, aber Java 21 wird benötigt${NC}"
fi
fi
# Betriebssystem erkennen
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_LIKE=$ID_LIKE
else
OS=$(uname -s)
fi
echo -e "Erkanntes OS: ${GREEN}$OS${NC}"
echo ""
install_debian_ubuntu() {
echo -e "${YELLOW}Installiere Java 21 via apt...${NC}"
echo ""
# Prüfen ob openjdk-21-jdk verfügbar ist
if apt-cache show openjdk-21-jdk &> /dev/null; then
echo "OpenJDK 21 ist in den Repositories verfügbar"
sudo apt update
sudo apt install -y openjdk-21-jdk
else
echo "OpenJDK 21 nicht in Standard-Repos, verwende Adoptium/Temurin..."
# Adoptium Repository hinzufügen
sudo apt install -y wget apt-transport-https gnupg
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo gpg --dearmor -o /usr/share/keyrings/adoptium.gpg
echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/adoptium.list
sudo apt update
sudo apt install -y temurin-21-jdk
fi
}
install_fedora_rhel() {
echo -e "${YELLOW}Installiere Java 21 via dnf/yum...${NC}"
echo ""
if command -v dnf &> /dev/null; then
sudo dnf install -y java-21-openjdk-devel
else
sudo yum install -y java-21-openjdk-devel
fi
}
install_arch() {
echo -e "${YELLOW}Installiere Java 21 via pacman...${NC}"
echo ""
sudo pacman -S --noconfirm jdk21-openjdk
}
install_opensuse() {
echo -e "${YELLOW}Installiere Java 21 via zypper...${NC}"
echo ""
sudo zypper install -y java-21-openjdk-devel
}
install_manual() {
echo -e "${YELLOW}Manuelle Installation von Eclipse Temurin JDK 21...${NC}"
echo ""
ARCH=$(uname -m)
case $ARCH in
x86_64)
JDK_ARCH="x64"
;;
aarch64)
JDK_ARCH="aarch64"
;;
*)
echo -e "${RED}Nicht unterstützte Architektur: $ARCH${NC}"
exit 1
;;
esac
JDK_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_${JDK_ARCH}_linux_hotspot_21.0.2_13.tar.gz"
INSTALL_DIR="/opt/java"
echo "Lade JDK herunter..."
wget -q --show-progress -O /tmp/openjdk21.tar.gz "$JDK_URL"
echo "Entpacke nach $INSTALL_DIR..."
sudo mkdir -p "$INSTALL_DIR"
sudo tar -xzf /tmp/openjdk21.tar.gz -C "$INSTALL_DIR"
rm /tmp/openjdk21.tar.gz
JDK_DIR=$(ls -d $INSTALL_DIR/jdk-21* | head -1)
echo "Erstelle Symlinks..."
sudo ln -sf "$JDK_DIR/bin/java" /usr/local/bin/java
sudo ln -sf "$JDK_DIR/bin/javac" /usr/local/bin/javac
sudo ln -sf "$JDK_DIR/bin/jar" /usr/local/bin/jar
# JAVA_HOME setzen
echo ""
echo -e "${YELLOW}Füge JAVA_HOME zu ~/.bashrc hinzu...${NC}"
echo "" >> ~/.bashrc
echo "# Java 21" >> ~/.bashrc
echo "export JAVA_HOME=$JDK_DIR" >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
export JAVA_HOME="$JDK_DIR"
export PATH="$JAVA_HOME/bin:$PATH"
}
# Installation basierend auf OS
case $OS in
debian|ubuntu|linuxmint|pop)
install_debian_ubuntu
;;
fedora|rhel|centos|rocky|almalinux)
install_fedora_rhel
;;
arch|manjaro|endeavouros)
install_arch
;;
opensuse*|sles)
install_opensuse
;;
*)
# Prüfe ob debian-basiert
if [[ "$OS_LIKE" == *"debian"* ]]; then
install_debian_ubuntu
elif [[ "$OS_LIKE" == *"rhel"* ]] || [[ "$OS_LIKE" == *"fedora"* ]]; then
install_fedora_rhel
elif [[ "$OS_LIKE" == *"arch"* ]]; then
install_arch
else
echo -e "${YELLOW}Unbekanntes OS, versuche manuelle Installation...${NC}"
install_manual
fi
;;
esac
echo ""
echo -e "${BLUE}========================================"
echo " Installation abgeschlossen!"
echo -e "========================================${NC}"
echo ""
# Verifizieren
if command -v javac &> /dev/null; then
echo -e "${GREEN}Java Version:${NC}"
java -version
echo ""
echo -e "${GREEN}Java Compiler:${NC}"
javac -version
echo ""
echo -e "${GREEN}Du kannst jetzt das Modul bauen:${NC}"
echo " cd v8-9-10 && ./build.sh"
else
echo -e "${RED}Installation fehlgeschlagen. Bitte manuell installieren.${NC}"
echo ""
echo "Manuelle Installation:"
echo " 1. Lade JDK 21 von https://adoptium.net herunter"
echo " 2. Entpacke nach /opt/java/"
echo " 3. Füge zu PATH hinzu: export PATH=/opt/java/jdk-21/bin:\$PATH"
exit 1
fi

BIN
v8-9-10/Mail2FaxBlock.class Normal file

Binary file not shown.

801
v8-9-10/Mail2FaxBlock.java Normal file
View File

@ -0,0 +1,801 @@
import de.vertico.starface.module.core.model.VariableType;
import de.vertico.starface.module.core.model.Visibility;
import de.vertico.starface.module.core.runtime.IBaseExecutable;
import de.vertico.starface.module.core.runtime.IRuntimeEnvironment;
import de.vertico.starface.module.core.runtime.annotations.Function;
import de.vertico.starface.module.core.runtime.annotations.InputVar;
import de.vertico.starface.module.core.runtime.annotations.OutputVar;
import javax.mail.*;
import javax.mail.internet.MimeBodyPart;
import javax.mail.search.FlagTerm;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* Mail2FaxBlock - STARFACE Custom Block
*
* Ruft E-Mails ab und sendet PDF-Anhänge als Fax.
* Die Ziel-Faxnummer wird aus dem E-Mail-Betreff gelesen.
*
* Für STARFACE 8.x, 9.x, 10.x (Java 21)
*/
@Function(
visibility = Visibility.Private,
description = "Ruft E-Mails ab und sendet PDF-Anhänge als Fax"
)
public class Mail2FaxBlock implements IBaseExecutable {
// Fax result constants (instead of inner class to avoid separate .class file)
private static final int FAX_SUCCESS = 0;
private static final int FAX_RETRY = 1;
private static final int FAX_FAIL = 2;
// Lock um parallele Ausführung zu verhindern
private static final ReentrantLock executionLock = new ReentrantLock();
// Pfade für Tracking-Dateien
private static final String DATA_DIR = "/var/starface/module-data";
private static final String PROCESSED_FILE = DATA_DIR + "/mail2fax_processed.txt";
private static final String RETRY_FILE = DATA_DIR + "/mail2fax_retry.txt";
// ============================================================
// INPUT VARIABLEN - werden im Module Designer konfiguriert
// ============================================================
@InputVar(
label = "Mail Server",
description = "IMAP oder POP3 Server Adresse",
type = VariableType.STRING
)
public String mailServer = "";
@InputVar(
label = "Mail Port",
description = "Server Port (993 für IMAPS, 995 für POP3S, 143 für IMAP, 110 für POP3)",
type = VariableType.NUMBER
)
public Integer mailPort = 993;
@InputVar(
label = "Protokoll",
description = "IMAP oder POP3",
type = VariableType.STRING
)
public String mailProtocol = "IMAP";
@InputVar(
label = "Benutzername",
description = "E-Mail Benutzername",
type = VariableType.STRING
)
public String mailUsername = "";
@InputVar(
label = "Passwort",
description = "E-Mail Passwort",
type = VariableType.STRING
)
public String mailPassword = "";
@InputVar(
label = "SSL verwenden",
description = "SSL/TLS für die Verbindung aktivieren",
type = VariableType.BOOLEAN
)
public Boolean mailUseSsl = true;
@InputVar(
label = "Ordner",
description = "E-Mail Ordner (Standard: INBOX)",
type = VariableType.STRING
)
public String mailFolder = "INBOX";
@InputVar(
label = "Nach Verarbeitung löschen",
description = "E-Mails nach erfolgreicher Verarbeitung löschen (nur IMAP, bei POP3 werden E-Mails immer nach erfolgreichem Versand gelöscht)",
type = VariableType.BOOLEAN
)
public Boolean deleteAfterProcess = false;
@InputVar(
label = "Fax-Benutzer",
description = "STARFACE Benutzer für den Fax-Versand (muss Fax-Berechtigung haben)",
type = VariableType.STARFACE_USER
)
public Integer faxAccountId = 0;
@InputVar(
label = "Absender-Faxnummer",
description = "Ausgehende Faxnummer (z.B. +49721123456)",
type = VariableType.STRING
)
public String faxSenderNumber = "";
@InputVar(
label = "Erlaubte Absender",
description = "Komma-getrennte Liste erlaubter Absender-Adressen (leer = alle erlaubt)",
type = VariableType.STRING
)
public String authorizedSenders = "";
@InputVar(
label = "PIN",
description = "Sicherheits-PIN (optional). Wenn gesetzt, muss die PIN im E-Mail-Text enthalten sein um das Fax zu senden.",
type = VariableType.STRING
)
public String pin = "";
@InputVar(
label = "Max. Wiederholungen",
description = "Maximale Anzahl Wiederholungsversuche bei besetzter Leitung",
type = VariableType.NUMBER
)
public Integer maxRetries = 3;
@InputVar(
label = "Wartezeit (Minuten)",
description = "Minuten zwischen Wiederholungsversuchen",
type = VariableType.NUMBER
)
public Integer retryDelayMinutes = 5;
// ============================================================
// OUTPUT VARIABLEN - Ergebnisse der Ausführung
// ============================================================
@OutputVar(
label = "Verarbeitete E-Mails",
description = "Anzahl der verarbeiteten E-Mails",
type = VariableType.NUMBER
)
public Integer processedCount = 0;
@OutputVar(
label = "Gesendete Faxe",
description = "Anzahl erfolgreich gesendeter Faxe",
type = VariableType.NUMBER
)
public Integer sentFaxCount = 0;
@OutputVar(
label = "Fehleranzahl",
description = "Anzahl der Fehler",
type = VariableType.NUMBER
)
public Integer errorCount = 0;
@OutputVar(
label = "Wartende Wiederholungen",
description = "Anzahl der Faxe die auf Wiederholung warten",
type = VariableType.NUMBER
)
public Integer pendingRetries = 0;
@OutputVar(
label = "Status",
description = "Status-Meldung der letzten Ausführung",
type = VariableType.STRING
)
public String statusMessage = "";
// Runtime Environment
private IRuntimeEnvironment runtime;
private org.apache.logging.log4j.Logger log;
@Override
public void execute(IRuntimeEnvironment runtime) throws Exception {
this.runtime = runtime;
this.log = runtime.getLog();
// Pflichtfelder validieren
List<String> missingFields = new ArrayList<>();
if (mailServer == null || mailServer.trim().isEmpty()) {
missingFields.add("mailServer (Mail Server)");
}
if (mailPort == null || mailPort <= 0) {
missingFields.add("mailPort (Port)");
}
if (mailUsername == null || mailUsername.trim().isEmpty()) {
missingFields.add("mailUsername (Benutzername)");
}
if (mailPassword == null || mailPassword.trim().isEmpty()) {
missingFields.add("mailPassword (Passwort)");
}
if (faxAccountId == null || faxAccountId <= 0) {
missingFields.add("faxAccountId (Fax-Benutzer)");
}
if (faxSenderNumber == null || faxSenderNumber.trim().isEmpty()) {
missingFields.add("faxSenderNumber (Absender-Faxnummer)");
}
if (!missingFields.isEmpty()) {
statusMessage = "Konfigurationsfehler: Pflichtfelder nicht ausgefüllt";
log.error("Mail2Fax: " + statusMessage + ": " + String.join(", ", missingFields));
return;
}
// Versuche Lock zu bekommen - wenn nicht verfügbar, läuft bereits eine Instanz
if (!executionLock.tryLock()) {
log.info("Mail2Fax: Bereits eine Instanz aktiv, überspringe Ausführung");
statusMessage = "Übersprungen - bereits aktiv";
return;
}
try {
log.info("Mail2Fax: Starte E-Mail-Abruf von " + mailServer);
// Datenverzeichnis erstellen
Files.createDirectories(Paths.get(DATA_DIR));
// Zuerst Retry-Queue verarbeiten
processRetryQueue();
// Dann neue E-Mails abrufen
fetchAndProcessEmails();
statusMessage = String.format(
"Fertig: %d E-Mails verarbeitet, %d Faxe gesendet, %d Fehler, %d warten auf Retry",
processedCount, sentFaxCount, errorCount, pendingRetries
);
log.info("Mail2Fax: " + statusMessage);
} finally {
executionLock.unlock();
}
}
/**
* Verarbeitet die Retry-Queue für fehlgeschlagene Faxe
*/
private void processRetryQueue() {
try {
Path retryPath = Paths.get(RETRY_FILE);
if (!Files.exists(retryPath)) {
return;
}
List<String> lines = Files.readAllLines(retryPath);
List<String> remaining = new ArrayList<>();
long now = System.currentTimeMillis();
for (String line : lines) {
if (line.trim().isEmpty()) continue;
// Format: timestamp|retryCount|destination|senderNumber|pdfPath|messageId
String[] parts = line.split("\\|", 6);
if (parts.length < 6) continue;
long scheduledTime = Long.parseLong(parts[0]);
int retryCount = Integer.parseInt(parts[1]);
String destination = parts[2];
String senderNumber = parts[3];
String pdfPath = parts[4];
String messageId = parts[5];
if (now < scheduledTime) {
// Noch nicht Zeit für Retry
remaining.add(line);
pendingRetries++;
continue;
}
// Retry durchführen
File pdfFile = new File(pdfPath);
if (!pdfFile.exists()) {
log.warn("Mail2Fax: Retry-PDF nicht mehr vorhanden: " + pdfPath);
continue;
}
log.info("Mail2Fax: Retry #" + retryCount + " für " + destination);
int result = sendFax(destination, senderNumber, pdfFile);
if (result == FAX_SUCCESS) {
sentFaxCount++;
pdfFile.delete(); // Temp-Datei löschen
markAsProcessed(messageId);
} else if (result == FAX_RETRY && retryCount < maxRetries) {
// Erneut in Queue
long nextRetry = now + (retryDelayMinutes * 60 * 1000L);
remaining.add(nextRetry + "|" + (retryCount + 1) + "|" + destination + "|" +
senderNumber + "|" + pdfPath + "|" + messageId);
pendingRetries++;
} else {
log.error("Mail2Fax: Endgültig fehlgeschlagen nach " + retryCount + " Versuchen: " + destination);
errorCount++;
pdfFile.delete();
}
}
// Aktualisierte Queue speichern
Files.write(retryPath, remaining);
} catch (Exception e) {
log.error("Mail2Fax: Fehler bei Retry-Queue: " + e.getMessage(), e);
}
}
/**
* Ruft E-Mails ab und verarbeitet sie
*/
private void fetchAndProcessEmails() {
Store store = null;
Folder folder = null;
try {
// Session konfigurieren
Properties props = new Properties();
String protocol = mailProtocol.toUpperCase();
if (protocol.equals("IMAP")) {
if (mailUseSsl) {
props.put("mail.store.protocol", "imaps");
props.put("mail.imaps.host", mailServer);
props.put("mail.imaps.port", String.valueOf(mailPort));
props.put("mail.imaps.ssl.enable", "true");
} else {
props.put("mail.store.protocol", "imap");
props.put("mail.imap.host", mailServer);
props.put("mail.imap.port", String.valueOf(mailPort));
}
} else { // POP3
if (mailUseSsl) {
props.put("mail.store.protocol", "pop3s");
props.put("mail.pop3s.host", mailServer);
props.put("mail.pop3s.port", String.valueOf(mailPort));
props.put("mail.pop3s.ssl.enable", "true");
} else {
props.put("mail.store.protocol", "pop3");
props.put("mail.pop3.host", mailServer);
props.put("mail.pop3.port", String.valueOf(mailPort));
}
}
Session session = Session.getInstance(props);
store = session.getStore();
store.connect(mailServer, mailPort, mailUsername, mailPassword);
folder = store.getFolder(mailFolder);
folder.open(Folder.READ_WRITE);
// Nachrichten abrufen
Message[] messages;
if (protocol.equals("IMAP")) {
// IMAP: Nur ungelesene
messages = folder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
} else {
// POP3: Alle (Tracking über processed-Liste)
messages = folder.getMessages();
}
log.info("Mail2Fax: " + messages.length + " Nachrichten gefunden");
for (Message message : messages) {
try {
processMessage(message, protocol.equals("POP3"), folder);
processedCount++;
} catch (Exception e) {
log.error("Mail2Fax: Fehler bei Nachricht: " + e.getMessage(), e);
errorCount++;
}
}
} catch (Exception e) {
// Bei Verbindungsfehlern nur eine saubere Meldung ohne Stacktrace
String msg = e.getMessage();
if (e instanceof com.sun.mail.util.MailConnectException ||
e instanceof java.net.ConnectException ||
e instanceof java.net.UnknownHostException ||
e instanceof java.net.SocketTimeoutException ||
(msg != null && (msg.contains("Connection refused") ||
msg.contains("connect") ||
msg.contains("timeout") ||
msg.contains("Unknown host")))) {
log.error("Mail2Fax: Verbindung zu " + mailServer + ":" + mailPort + " fehlgeschlagen - " + msg);
} else {
log.error("Mail2Fax: E-Mail-Abruf fehlgeschlagen: " + msg, e);
}
errorCount++;
statusMessage = "Fehler: " + msg;
} finally {
try {
if (folder != null && folder.isOpen()) {
folder.close(true); // expunge deleted messages
}
if (store != null) {
store.close();
}
} catch (Exception e) {
log.warn("Mail2Fax: Fehler beim Schließen: " + e.getMessage());
}
}
}
/**
* Verarbeitet eine einzelne E-Mail
*/
private void processMessage(Message message, boolean isPop3, Folder folder) throws Exception {
String messageId = getMessageId(message);
// Bei POP3: Prüfen ob bereits verarbeitet
if (isPop3 && isAlreadyProcessed(messageId)) {
log.debug("Mail2Fax: Nachricht bereits verarbeitet: " + messageId);
return;
}
String from = message.getFrom()[0].toString();
String subject = message.getSubject();
log.info("Mail2Fax: Verarbeite E-Mail von " + from + " - Betreff: " + subject);
// Absender prüfen
if (!isAuthorizedSender(from)) {
log.warn("Mail2Fax: Nicht autorisierter Absender: " + from);
if (!isPop3) {
message.setFlag(Flags.Flag.SEEN, true);
}
return;
}
// PIN prüfen (wenn gesetzt)
if (pin != null && !pin.trim().isEmpty()) {
String emailBody = getEmailBodyText(message);
if (emailBody == null || !emailBody.contains(pin)) {
log.error("Mail2Fax: PIN in Emailtext nicht vorhanden oder falsch");
if (!isPop3) {
message.setFlag(Flags.Flag.SEEN, true);
}
return;
}
log.debug("Mail2Fax: PIN erfolgreich validiert");
}
// Ziel-Faxnummer aus Betreff extrahieren
String destination = extractFaxNumber(subject);
if (destination == null || destination.isEmpty()) {
log.warn("Mail2Fax: Keine gültige Faxnummer im Betreff: " + subject);
if (!isPop3) {
message.setFlag(Flags.Flag.SEEN, true);
}
return;
}
// PDF-Anhänge suchen und verarbeiten
List<File> pdfFiles = extractPdfAttachments(message);
if (pdfFiles.isEmpty()) {
log.warn("Mail2Fax: Keine PDF-Anhänge gefunden");
if (!isPop3) {
message.setFlag(Flags.Flag.SEEN, true);
}
return;
}
// Jeden PDF-Anhang als Fax senden
boolean allSuccess = true;
for (File pdfFile : pdfFiles) {
int result = sendFax(destination, faxSenderNumber, pdfFile);
if (result == FAX_SUCCESS) {
sentFaxCount++;
pdfFile.delete();
} else if (result == FAX_RETRY) {
// In Retry-Queue aufnehmen
addToRetryQueue(destination, faxSenderNumber, pdfFile, messageId);
allSuccess = false;
} else {
errorCount++;
pdfFile.delete();
allSuccess = false;
}
}
// E-Mail als verarbeitet markieren
if (allSuccess) {
if (isPop3) {
// POP3: Als verarbeitet speichern und löschen
markAsProcessed(messageId);
message.setFlag(Flags.Flag.DELETED, true);
log.info("Mail2Fax: POP3-Nachricht zum Löschen markiert");
} else {
// IMAP: Als gelesen markieren, optional löschen
message.setFlag(Flags.Flag.SEEN, true);
if (deleteAfterProcess) {
message.setFlag(Flags.Flag.DELETED, true);
}
}
}
}
/**
* Ermittelt die Message-ID einer E-Mail
*/
private String getMessageId(Message message) {
try {
String[] headers = message.getHeader("Message-ID");
if (headers != null && headers.length > 0) {
return headers[0];
}
// Fallback: Hash aus Datum und Betreff
return String.valueOf((message.getSentDate() + "|" + message.getSubject()).hashCode());
} catch (Exception e) {
return String.valueOf(System.currentTimeMillis());
}
}
/**
* Prüft ob eine Nachricht bereits verarbeitet wurde (für POP3)
*/
private boolean isAlreadyProcessed(String messageId) {
try {
Path path = Paths.get(PROCESSED_FILE);
if (!Files.exists(path)) {
return false;
}
List<String> processed = Files.readAllLines(path);
return processed.contains(messageId);
} catch (Exception e) {
return false;
}
}
/**
* Markiert eine Nachricht als verarbeitet (für POP3)
*/
private void markAsProcessed(String messageId) {
try {
Path path = Paths.get(PROCESSED_FILE);
List<String> processed = Files.exists(path) ?
new ArrayList<>(Files.readAllLines(path)) : new ArrayList<>();
if (!processed.contains(messageId)) {
processed.add(messageId);
// Maximal 1000 Einträge behalten
while (processed.size() > 1000) {
processed.remove(0);
}
Files.write(path, processed);
}
} catch (Exception e) {
log.warn("Mail2Fax: Konnte Nachricht nicht als verarbeitet markieren: " + e.getMessage());
}
}
/**
* Fügt ein fehlgeschlagenes Fax zur Retry-Queue hinzu
*/
private void addToRetryQueue(String destination, String senderNumber, File pdfFile, String messageId) {
try {
long nextRetry = System.currentTimeMillis() + (retryDelayMinutes * 60 * 1000L);
String entry = nextRetry + "|1|" + destination + "|" + senderNumber + "|" +
pdfFile.getAbsolutePath() + "|" + messageId;
Path path = Paths.get(RETRY_FILE);
List<String> entries = Files.exists(path) ?
new ArrayList<>(Files.readAllLines(path)) : new ArrayList<>();
entries.add(entry);
Files.write(path, entries);
pendingRetries++;
log.info("Mail2Fax: Fax zur Retry-Queue hinzugefügt: " + destination);
} catch (Exception e) {
log.error("Mail2Fax: Konnte nicht zur Retry-Queue hinzufügen: " + e.getMessage());
}
}
/**
* Prüft ob der Absender autorisiert ist
*/
private boolean isAuthorizedSender(String from) {
if (authorizedSenders == null || authorizedSenders.trim().isEmpty()) {
return true; // Keine Einschränkung
}
String fromLower = from.toLowerCase();
String[] authorized = authorizedSenders.split(",");
for (String auth : authorized) {
if (fromLower.contains(auth.trim().toLowerCase())) {
return true;
}
}
return false;
}
/**
* Extrahiert die Faxnummer aus dem E-Mail-Betreff
*/
private String extractFaxNumber(String subject) {
if (subject == null || subject.isEmpty()) {
return null;
}
// Entferne alle Zeichen außer Ziffern und +
String cleaned = subject.replaceAll("[^0-9+]", "");
// Prüfe auf gültiges Format
if (cleaned.matches("\\+?[0-9]{6,}")) {
return cleaned;
}
return null;
}
/**
* Extrahiert den Textinhalt einer E-Mail (für PIN-Prüfung)
*/
private String getEmailBodyText(Message message) {
try {
Object content = message.getContent();
if (content instanceof String) {
return (String) content;
} else if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
StringBuilder bodyText = new StringBuilder();
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
String contentType = bodyPart.getContentType().toLowerCase();
// Nur Text-Parts auslesen, keine Anhänge
if (contentType.startsWith("text/plain") || contentType.startsWith("text/html")) {
Object partContent = bodyPart.getContent();
if (partContent instanceof String) {
bodyText.append((String) partContent);
}
}
}
return bodyText.toString();
}
} catch (Exception e) {
log.warn("Mail2Fax: Fehler beim Lesen des E-Mail-Textes: " + e.getMessage());
}
return null;
}
/**
* Extrahiert PDF-Anhänge aus der E-Mail
*/
private List<File> extractPdfAttachments(Message message) throws Exception {
List<File> pdfFiles = new ArrayList<>();
Object content = message.getContent();
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) ||
(bodyPart.getFileName() != null && bodyPart.getFileName().toLowerCase().endsWith(".pdf"))) {
String fileName = bodyPart.getFileName();
if (fileName != null && fileName.toLowerCase().endsWith(".pdf")) {
// Temp-Datei erstellen
File tempFile = File.createTempFile("mail2fax_", ".pdf");
try (InputStream is = bodyPart.getInputStream();
FileOutputStream fos = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
log.info("Mail2Fax: PDF extrahiert: " + fileName);
pdfFiles.add(tempFile);
}
}
}
}
return pdfFiles;
}
/**
* Sendet ein PDF als Fax über die STARFACE API
*
* Verwendet den internen FaxHandler über den StarfaceComponentProvider.
*
* @return FAX_SUCCESS, FAX_RETRY oder FAX_FAIL
*/
private int sendFax(String destination, String callingNumber, File pdfFile) {
try {
log.info("Mail2Fax: Sende Fax an " + destination + " von " + callingNumber);
// Den eingebauten SendFax-Block direkt instanziieren und aufrufen
Class<?> sendFaxClass = Class.forName(
"de.vertico.starface.module.core.runtime.functions.callHandling.call.SendFax"
);
Object sendFaxBlock = sendFaxClass.getDeclaredConstructor().newInstance();
// Input-Variablen setzen via Reflection
// accountId (Integer) - Benutzer-ID für Fax-Versand
setFieldValue(sendFaxClass, sendFaxBlock, "accountId", faxAccountId);
// extention (String) - Ziel-Faxnummer (Tippfehler im Original!)
setFieldValue(sendFaxClass, sendFaxBlock, "extention", destination);
// signalNumber (String) - Absender-Nummer
setFieldValue(sendFaxClass, sendFaxBlock, "signalNumber", callingNumber);
// signalName (String) - Absender-Name (optional, gleich wie Nummer)
setFieldValue(sendFaxClass, sendFaxBlock, "signalName", callingNumber);
// resource (String) - Pfad zur PDF-Datei
setFieldValue(sendFaxClass, sendFaxBlock, "resource", pdfFile.getAbsolutePath());
// execute() aufrufen mit unserem Runtime
java.lang.reflect.Method executeMethod = sendFaxClass.getMethod("execute",
Class.forName("de.vertico.starface.module.core.runtime.IRuntimeEnvironment"));
executeMethod.invoke(sendFaxBlock, runtime);
// Ergebnis prüfen (exitStatus Feld)
try {
java.lang.reflect.Field exitStatusField = sendFaxClass.getDeclaredField("exitStatus");
exitStatusField.setAccessible(true);
Object exitStatus = exitStatusField.get(sendFaxBlock);
log.info("Mail2Fax: SendFax exitStatus: " + exitStatus);
if (exitStatus != null && exitStatus.toString().contains("FAILED")) {
return FAX_RETRY;
}
} catch (NoSuchFieldException e) {
log.debug("Mail2Fax: Kein exitStatus Feld");
}
log.info("Mail2Fax: Fax erfolgreich gesendet an " + destination);
return FAX_SUCCESS;
} catch (Exception e) {
String error = e.getMessage() != null ? e.getMessage().toLowerCase() : "";
log.error("Mail2Fax: Fax-Fehler: " + e.getMessage(), e);
// Prüfe ob Retry sinnvoll ist
if (error.contains("busy") || error.contains("besetzt") ||
error.contains("no answer") || error.contains("keine antwort") ||
error.contains("temporarily") || error.contains("timeout")) {
log.info("Mail2Fax: Temporärer Fehler, wird erneut versucht");
return FAX_RETRY;
}
return FAX_FAIL;
}
}
/**
* Versucht ein Feld zu setzen, gibt true zurück wenn erfolgreich
*/
private boolean trySetField(Class<?> clazz, Object obj, String fieldName, Object value) {
try {
java.lang.reflect.Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
log.debug("Mail2Fax: " + fieldName + " gesetzt: " + value);
return true;
} catch (NoSuchFieldException e) {
return false;
} catch (Exception e) {
log.warn("Mail2Fax: Fehler beim Setzen von " + fieldName + ": " + e.getMessage());
return false;
}
}
/**
* Setzt ein Feld oder wirft Exception
*/
private void setFieldValue(Class<?> clazz, Object obj, String fieldName, Object value) throws Exception {
java.lang.reflect.Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
log.debug("Mail2Fax: " + fieldName + " gesetzt: " + value);
}
}

138
v8-9-10/README.md Normal file
View File

@ -0,0 +1,138 @@
# Mail2FaxBlock - STARFACE Custom Block
Ein Custom Block für den STARFACE Module Designer, der E-Mails abruft und PDF-Anhänge als Fax versendet.
## Features
- **IMAP/POP3 Unterstützung** mit SSL/TLS
- **POP3**: E-Mails werden IMMER nach erfolgreichem Versand gelöscht (+ Tracking um Duplikate zu vermeiden)
- **IMAP**: E-Mails werden als gelesen markiert (optional löschen)
- **Retry-Logik**: Bei besetzter Leitung oder Fehler wird automatisch erneut versucht
- **Konfigurierbare Wiederholungen**: Anzahl und Wartezeit einstellbar
- **PIN-Schutz**: Optionale Sicherheits-PIN im E-Mail-Text erforderlich
## Kompatibilität
- STARFACE 8.x, 9.x, 10.x (Java 21)
## Dateien
```
v8-9-10/
├── Mail2FaxBlock.java # Quellcode des Custom Blocks
├── build-block.sh # Kompilier-Script
├── libs/starface/ # STARFACE API JARs
└── README.md
```
## Build
### 1. STARFACE APIs holen (falls noch nicht geschehen)
```bash
cd ..
./fetch-starface-libs.sh <starface-ip>
```
### 2. Block kompilieren
```bash
./build-block.sh
```
Ergebnis: `Mail2FaxBlock.class`
## Installation im Module Designer
### 1. Neues Modul erstellen
- STARFACE Admin → Module → Module Designer
- "Neues Modul erstellen"
- Name: "Mail2Fax"
### 2. Block hochladen
- Tab "Ressourcen"
- "Datei hochladen" → `Mail2FaxBlock.class`
### 3. Funktion erstellen
- Tab "Funktionen"
- Neue Funktion erstellen
- Den hochgeladenen Block als Implementierung auswählen
### 4. Eingabe-Variablen konfigurieren
| Variable | Typ | Beschreibung | Default |
|----------|-----|--------------|---------|
| mailServer | STRING | IMAP/POP3 Server | |
| mailPort | NUMBER | Port | 993 |
| mailProtocol | STRING | "IMAP" oder "POP3" | IMAP |
| mailUsername | STRING | E-Mail Benutzer | |
| mailPassword | STRING | E-Mail Passwort | |
| mailUseSsl | BOOLEAN | SSL aktivieren | true |
| mailFolder | STRING | Ordner | INBOX |
| deleteAfterProcess | BOOLEAN | E-Mails löschen (nur IMAP) | false |
| faxAccountId | STARFACE_USER | Fax-Benutzer (Dropdown) | |
| faxSenderNumber | STRING | Absender-Faxnummer | |
| authorizedSenders | STRING | Erlaubte Absender (optional) | |
| pin | STRING | Sicherheits-PIN (optional) | |
| maxRetries | NUMBER | Max. Wiederholungsversuche | 3 |
| retryDelayMinutes | NUMBER | Minuten zwischen Versuchen | 5 |
### 5. Timer konfigurieren
Der Block muss regelmäßig ausgeführt werden um E-Mails abzurufen. Dafür den Timer im Module Designer konfigurieren:
1. Tab **"Timer"** öffnen
2. Auf **[+]** klicken um einen neuen Schedule hinzuzufügen
3. Intervall festlegen (empfohlen: alle 60 Sekunden)
![Timer-Tab im Module Designer](screenshots/timer-tab.png)
**Hinweis:** Der Block hat einen eingebauten Lock-Mechanismus. Wenn der Timer erneut auslöst während der Block noch läuft, wird die neue Ausführung automatisch übersprungen. Keine Gefahr von Duplikaten.
### 6. Modul aktivieren
## Benutzung
1. E-Mail an das konfigurierte Postfach senden
2. **Betreff** = Ziel-Faxnummer (z.B. `+49721123456`)
3. **Anhang** = PDF-Datei(en)
4. **E-Mail-Text** = PIN (falls konfiguriert)
Das Modul ruft regelmäßig E-Mails ab und sendet PDFs als Fax.
## PIN-Schutz
Wenn eine PIN konfiguriert ist, muss diese im E-Mail-Text enthalten sein, damit das Fax gesendet wird.
- **PIN nicht gesetzt**: Alle E-Mails werden verarbeitet (nur Absender-Prüfung falls konfiguriert)
- **PIN gesetzt**: Die PIN muss irgendwo im E-Mail-Text (plain text oder HTML) vorkommen
- **PIN nicht gefunden**: E-Mail wird als gelesen markiert, kein Fax gesendet, Log-Eintrag "PIN in Emailtext nicht vorhanden oder falsch"
## Output-Variablen
| Variable | Typ | Beschreibung |
|----------|-----|--------------|
| processedCount | NUMBER | Verarbeitete E-Mails |
| sentFaxCount | NUMBER | Gesendete Faxe |
| errorCount | NUMBER | Fehleranzahl |
| pendingRetries | NUMBER | Wartende Wiederholungsversuche |
| statusMessage | STRING | Status-Meldung |
## Retry-Verhalten
Bei folgenden Fehlern wird automatisch erneut versucht:
- Leitung besetzt
- Keine Antwort
- Übertragungsfehler
Die Retry-Daten werden gespeichert in:
- `/var/starface/module-data/mail2fax_retry.txt`
Nach Erreichen von `maxRetries` wird der Fax-Versuch verworfen.
## POP3-Tracking
Da POP3 keine "gelesen"-Flags unterstützt, speichert der Block verarbeitete Message-IDs in:
- `/var/starface/module-data/mail2fax_processed.txt`
So werden Duplikate vermieden, auch wenn E-Mails nicht sofort gelöscht werden können.

90
v8-9-10/build-block.sh Executable file
View File

@ -0,0 +1,90 @@
#!/bin/bash
#
# Kompiliert den Mail2FaxBlock für STARFACE Module Designer
#
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "========================================"
echo " Mail2FaxBlock Kompilierung"
echo "========================================"
echo ""
# JavaMail Bibliothek herunterladen falls nicht vorhanden
DEPS_DIR="libs/deps"
mkdir -p "$DEPS_DIR"
if [ ! -f "$DEPS_DIR/javax.mail.jar" ]; then
echo "Lade JavaMail Bibliothek herunter..."
curl -sL -o "$DEPS_DIR/javax.mail.jar" \
"https://repo1.maven.org/maven2/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar"
echo " javax.mail.jar heruntergeladen"
fi
if [ ! -f "$DEPS_DIR/activation.jar" ]; then
echo "Lade Activation Framework herunter..."
curl -sL -o "$DEPS_DIR/activation.jar" \
"https://repo1.maven.org/maven2/javax/activation/activation/1.1.1/activation-1.1.1.jar"
echo " activation.jar heruntergeladen"
fi
echo ""
# Classpath zusammenbauen - STARFACE JARs
CLASSPATH=""
for jar in libs/starface/*.jar; do
if [ -f "$jar" ]; then
CLASSPATH="$CLASSPATH:$jar"
fi
done
# Zusätzliche Dependencies hinzufügen
for jar in libs/deps/*.jar; do
if [ -f "$jar" ]; then
CLASSPATH="$CLASSPATH:$jar"
fi
done
CLASSPATH="${CLASSPATH:1}"
if [ -z "$CLASSPATH" ]; then
echo "FEHLER: Keine STARFACE JARs gefunden in libs/starface/"
echo "Führe zuerst aus: ../fetch-starface-libs.sh <starface-ip>"
exit 1
fi
echo "Kompiliere Mail2FaxBlock.java..."
# Kompilieren - wichtig: nur die .class Datei, kein Package!
javac -source 21 -target 21 \
-cp "$CLASSPATH" \
-proc:none \
Mail2FaxBlock.java
if [ -f "Mail2FaxBlock.class" ]; then
echo ""
echo "========================================"
echo " Erfolgreich!"
echo "========================================"
echo ""
echo "Datei: Mail2FaxBlock.class"
ls -lh Mail2FaxBlock.class
echo ""
echo "WICHTIG: Die JavaMail-Bibliothek muss auch auf der STARFACE sein!"
echo "Falls Fehler auftreten, kopiere javax.mail.jar nach STARFACE:"
echo " scp libs/deps/javax.mail.jar root@<starface>:/opt/tomcat/webapps/localhost/starface/WEB-INF/lib/"
echo ""
echo "Nächste Schritte:"
echo "1. STARFACE Admin öffnen"
echo "2. Module → Module Designer → Neues Modul"
echo "3. Unter 'Ressourcen' die Mail2FaxBlock.class hochladen"
echo "4. Neuen Funktionsbaustein erstellen und Block verknüpfen"
echo "5. Timer-Baustein hinzufügen für regelmäßige Ausführung"
echo ""
else
echo "FEHLER: Kompilierung fehlgeschlagen"
exit 1
fi

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.