From c1eff127b4a12f5f4fbc45c8705fcbd98821ca2a Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 28 Oct 2023 20:13:40 +0200 Subject: [PATCH 001/153] Starting point for a crazy answer bot that can catch spam calls. This answering machine can be connected to a Fritz!Box or other VOIP router as IP phone. --- phoneblock-ab/.classpath | 27 ++ phoneblock-ab/.gitignore | 1 + phoneblock-ab/.project | 23 ++ .../org.eclipse.core.resources.prefs | 3 + .../.settings/org.eclipse.jdt.core.prefs | 9 + .../.settings/org.eclipse.m2e.core.prefs | 4 + phoneblock-ab/conversation/hello/.gitignore | 1 + phoneblock-ab/conversation/hello/README.md | 4 + .../conversation/question/.gitignore | 1 + phoneblock-ab/conversation/question/README.md | 4 + .../conversation/still-there/.gitignore | 1 + .../conversation/still-there/README.md | 4 + phoneblock-ab/conversation/waiting/.gitignore | 1 + phoneblock-ab/conversation/waiting/README.md | 5 + .../conversation/who-is-calling/.gitignore | 1 + .../conversation/who-is-calling/README.md | 4 + phoneblock-ab/pom.xml | 49 ++++ .../org/mjsip/ua/answerbot/AnswerBot.java | 223 ++++++++++++++++ .../mjsip/ua/answerbot/SpeechDispatcher.java | 172 +++++++++++++ pom.xml | 237 ++++++++++++++++++ 20 files changed, 774 insertions(+) create mode 100644 phoneblock-ab/.classpath create mode 100644 phoneblock-ab/.gitignore create mode 100644 phoneblock-ab/.project create mode 100644 phoneblock-ab/.settings/org.eclipse.core.resources.prefs create mode 100644 phoneblock-ab/.settings/org.eclipse.jdt.core.prefs create mode 100644 phoneblock-ab/.settings/org.eclipse.m2e.core.prefs create mode 100644 phoneblock-ab/conversation/hello/.gitignore create mode 100644 phoneblock-ab/conversation/hello/README.md create mode 100644 phoneblock-ab/conversation/question/.gitignore create mode 100644 phoneblock-ab/conversation/question/README.md create mode 100644 phoneblock-ab/conversation/still-there/.gitignore create mode 100644 phoneblock-ab/conversation/still-there/README.md create mode 100644 phoneblock-ab/conversation/waiting/.gitignore create mode 100644 phoneblock-ab/conversation/waiting/README.md create mode 100644 phoneblock-ab/conversation/who-is-calling/.gitignore create mode 100644 phoneblock-ab/conversation/who-is-calling/README.md create mode 100644 phoneblock-ab/pom.xml create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java create mode 100644 pom.xml diff --git a/phoneblock-ab/.classpath b/phoneblock-ab/.classpath new file mode 100644 index 00000000..04eb2980 --- /dev/null +++ b/phoneblock-ab/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phoneblock-ab/.gitignore b/phoneblock-ab/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/phoneblock-ab/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/phoneblock-ab/.project b/phoneblock-ab/.project new file mode 100644 index 00000000..2ecc9910 --- /dev/null +++ b/phoneblock-ab/.project @@ -0,0 +1,23 @@ + + + phoneblock-ab + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/phoneblock-ab/.settings/org.eclipse.core.resources.prefs b/phoneblock-ab/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..c35a6291 --- /dev/null +++ b/phoneblock-ab/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=utf-8 +encoding/=utf-8 diff --git a/phoneblock-ab/.settings/org.eclipse.jdt.core.prefs b/phoneblock-ab/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..55e549bb --- /dev/null +++ b/phoneblock-ab/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.methodParameters=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/phoneblock-ab/.settings/org.eclipse.m2e.core.prefs b/phoneblock-ab/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000..f897a7f1 --- /dev/null +++ b/phoneblock-ab/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/phoneblock-ab/conversation/hello/.gitignore b/phoneblock-ab/conversation/hello/.gitignore new file mode 100644 index 00000000..79a66b20 --- /dev/null +++ b/phoneblock-ab/conversation/hello/.gitignore @@ -0,0 +1 @@ +/*.wav diff --git a/phoneblock-ab/conversation/hello/README.md b/phoneblock-ab/conversation/hello/README.md new file mode 100644 index 00000000..02d4cc35 --- /dev/null +++ b/phoneblock-ab/conversation/hello/README.md @@ -0,0 +1,4 @@ +# Directory for greetings streamed by the answer bot + +When a call arrives, the answer bot selects one of the `*.wav` files placed to this directory and streams it to the +counterpart. \ No newline at end of file diff --git a/phoneblock-ab/conversation/question/.gitignore b/phoneblock-ab/conversation/question/.gitignore new file mode 100644 index 00000000..79a66b20 --- /dev/null +++ b/phoneblock-ab/conversation/question/.gitignore @@ -0,0 +1 @@ +/*.wav diff --git a/phoneblock-ab/conversation/question/README.md b/phoneblock-ab/conversation/question/README.md new file mode 100644 index 00000000..103d6a3d --- /dev/null +++ b/phoneblock-ab/conversation/question/README.md @@ -0,0 +1,4 @@ +# Directory for silly questions streamed by the answer bot + +Whenever the counterpart says something, answer bot selects one of the `*.wav` files placed to this directory and +streams it to the counterpart as response. \ No newline at end of file diff --git a/phoneblock-ab/conversation/still-there/.gitignore b/phoneblock-ab/conversation/still-there/.gitignore new file mode 100644 index 00000000..79a66b20 --- /dev/null +++ b/phoneblock-ab/conversation/still-there/.gitignore @@ -0,0 +1 @@ +/*.wav diff --git a/phoneblock-ab/conversation/still-there/README.md b/phoneblock-ab/conversation/still-there/README.md new file mode 100644 index 00000000..c24cb0ba --- /dev/null +++ b/phoneblock-ab/conversation/still-there/README.md @@ -0,0 +1,4 @@ +# Directory for inquiries streamed by the answer bot + +Whenever the counterpart does not respond to a silly question, the answer bot selects one of the `*.wav` files placed +to this directory and streams it to the counterpart as response. \ No newline at end of file diff --git a/phoneblock-ab/conversation/waiting/.gitignore b/phoneblock-ab/conversation/waiting/.gitignore new file mode 100644 index 00000000..79a66b20 --- /dev/null +++ b/phoneblock-ab/conversation/waiting/.gitignore @@ -0,0 +1 @@ +/*.wav diff --git a/phoneblock-ab/conversation/waiting/README.md b/phoneblock-ab/conversation/waiting/README.md new file mode 100644 index 00000000..7327cfb8 --- /dev/null +++ b/phoneblock-ab/conversation/waiting/README.md @@ -0,0 +1,5 @@ +# Directory for silent noise sounds + +Whenever the answer bot waits for a response from the counterpart, it selects one of the `*.wav` files placed +to this directory and streams it to the counterpart to pretend that there is still somebody at the other end of the +line. \ No newline at end of file diff --git a/phoneblock-ab/conversation/who-is-calling/.gitignore b/phoneblock-ab/conversation/who-is-calling/.gitignore new file mode 100644 index 00000000..79a66b20 --- /dev/null +++ b/phoneblock-ab/conversation/who-is-calling/.gitignore @@ -0,0 +1 @@ +/*.wav diff --git a/phoneblock-ab/conversation/who-is-calling/README.md b/phoneblock-ab/conversation/who-is-calling/README.md new file mode 100644 index 00000000..0efdfeea --- /dev/null +++ b/phoneblock-ab/conversation/who-is-calling/README.md @@ -0,0 +1,4 @@ +# Directory for asking who is calling + +If the counterpart does not say anything in response to the hello message, the answer bot randomly selects one of the +files placed to this directory and streams it to the counterpart. \ No newline at end of file diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml new file mode 100644 index 00000000..59920907 --- /dev/null +++ b/phoneblock-ab/pom.xml @@ -0,0 +1,49 @@ + + 4.0.0 + + + de.haumacher + phoneblock-parent + 1.4.6-SNAPSHOT + + + phoneblock-ab + + PhoneBlock Anrufbeantworter + Ad blocking answering machine for your phone + 2023 + + + + GPL-3.0-only + https://www.gnu.org/licenses/gpl-3.0.html + + + + + + Bernhard Haumacher + https://haumacher.de + + + + + https://phoneblock.haumacher.de + + + + scm:git:https://github.com/haumacher/phoneblock.git + HEAD + + + + + + + + org.mjsip + mjsip-ua + 1.9.0-SNAPSHOT + + + \ No newline at end of file diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java new file mode 100644 index 00000000..dc1e63dd --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007 Luca Veltri - University of Parma - Italy + * + * This source code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author(s): + * Luca Veltri (luca.veltri@unipr.it) + */ + +package org.mjsip.ua.answerbot; + + + +import java.io.File; +import java.io.IOError; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.mjsip.media.AudioFile; +import org.mjsip.media.AudioStreamer; +import org.mjsip.media.FlowSpec; +import org.mjsip.media.MediaDesc; +import org.mjsip.media.MediaStreamer; +import org.mjsip.media.RtpStreamReceiver; +import org.mjsip.media.RtpStreamReceiverListener; +import org.mjsip.media.RtpStreamReceiverListenerAdapter; +import org.mjsip.media.RtpStreamSender; +import org.mjsip.media.RtpStreamSenderListener; +import org.mjsip.media.StreamerOptions; +import org.mjsip.media.rx.AudioReceiver; +import org.mjsip.media.rx.AudioRxHandle; +import org.mjsip.media.rx.RtpAudioRxHandler; +import org.mjsip.media.rx.RtpReceiverOptions; +import org.mjsip.media.tx.AudioTXHandle; +import org.mjsip.media.tx.AudioTransmitter; +import org.mjsip.media.tx.RtpAudioTxHandle; +import org.mjsip.media.tx.RtpSenderOptions; +import org.mjsip.rtp.RtpControl; +import org.mjsip.rtp.RtpPayloadFormat; +import org.mjsip.sip.address.NameAddress; +import org.mjsip.sip.message.SipMessage; +import org.mjsip.sip.provider.SipConfig; +import org.mjsip.sip.provider.SipProvider; +import org.mjsip.sip.provider.SipStack; +import org.mjsip.time.Scheduler; +import org.mjsip.time.SchedulerConfig; +import org.mjsip.ua.MediaConfig; +import org.mjsip.ua.MultipleUAS; +import org.mjsip.ua.PortConfig; +import org.mjsip.ua.PortPool; +import org.mjsip.ua.ServiceConfig; +import org.mjsip.ua.UAConfig; +import org.mjsip.ua.UserAgent; +import org.mjsip.ua.UserAgentListener; +import org.mjsip.ua.UserAgentListenerAdapter; +import org.mjsip.ua.sound.AlawSilenceTrimmer; +import org.mjsip.ua.streamer.StreamerFactory; +import org.slf4j.LoggerFactory; +import org.zoolu.net.UdpSocket; +import org.zoolu.sound.CodecType; +import org.zoolu.sound.SimpleAudioSystem; +import org.zoolu.util.Encoder; +import org.zoolu.util.Flags; + + + +/** + * {@link AnswerBot} is a VOIP server that automatically accepts incoming calls, sends an audio file and records + * input received from the remote end. + */ +public class AnswerBot extends MultipleUAS { + + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnswerBot.class); + + /** Media file to play when answering the call. */ + public static String DEFAULT_ANNOUNCEMENT_FILE="./announcement-8000hz-mono-a-law.wav"; + + private MediaConfig _mediaConfig; + + private PortPool _portPool; + + /** + * Creates an {@link AnswerBot}. + */ + public AnswerBot(SipProvider sip_provider, StreamerFactory streamerFactory, UAConfig uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceConfig serviceConfig) { + super(sip_provider,streamerFactory, uaConfig, serviceConfig); + _mediaConfig = mediaConfig; + _portPool = portPool; + } + + @Override + protected UserAgentListener createCallHandler(SipMessage msg) { + MediaConfig callMedia = MediaConfig.from(_mediaConfig.mediaDescs); + callMedia.allocateMediaPorts(_portPool); + + return new UserAgentListenerAdapter() { + @Override + public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { + LOG.info("Incomming call from: " + callee.getAddress()); + ua.accept(callMedia.mediaDescs); + } + + @Override + public void onUaCallClosed(UserAgent ua) { + LOG.info("Call closed."); + callMedia.releaseMediaPorts(_portPool); + } + }; + } + + /** + * The main entry point. + */ + public static void main(String[] args) { + String program = AnswerBot.class.getSimpleName(); + LOG.info(program + " " + SipStack.version); + + Flags flags=new Flags(program, args); + String config_file=flags.getString("-f","", System.getProperty("user.home") + "/.mjsip-answerbot" ,"loads configuration from the given file"); + SipConfig sipConfig = SipConfig.init(config_file, flags); + UAConfig uaConfig = UAConfig.init(config_file, flags, sipConfig); + SchedulerConfig schedulerConfig = SchedulerConfig.init(config_file); + MediaConfig mediaConfig = MediaConfig.init(config_file, flags, uaConfig); + PortConfig portConfig = PortConfig.init(config_file, flags); + ServiceConfig serviceConfig=ServiceConfig.init(config_file, flags); + flags.close(); + + Map> audioFragments = new HashMap<>(); + File conversation = new File("./conversation"); + for (File type : conversation.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { + ArrayList files = new ArrayList<>(); + String typeName = type.getName(); + audioFragments.put(typeName, files); + for (File wav : type.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { + files.add(wav); + + LOG.info("Found audio fragment for " + typeName + ": " + wav.getPath()); + } + } + + PortPool portPool = new PortPool(portConfig.mediaPort, portConfig.portCount); + StreamerFactory streamerFactory = new StreamerFactory() { + @Override + public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { + SpeechDispatcher speechDispatcher = new SpeechDispatcher(audioFragments); + + int sampleRate = flow_spec.getMediaSpec().getSampleRate(); + try { + OutputStream recording = AudioFile.getAudioFileOutputStream("recording.wav", SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); + + int bufferTime = 20; + int minSilenceTime = 500; + int paddingTime = 100; + double silenceDb = -30; + AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); + + AudioTransmitter tx = new AudioTransmitter() { + @Override + public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket, AudioFormat audio_format, + CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, int sample_rate, int channels, + Encoder additional_encoder, long packet_time, int packet_size, String remote_addr, int remote_port, + RtpStreamSenderListener listener, RtpControl rtpControl) throws IOException { + + RtpStreamSender sender = new RtpStreamSender(options, speechDispatcher, true, payload_type, payloadFormat, sample_rate, + channels, packet_time, packet_size, additional_encoder, udp_socket, remote_addr, remote_port, rtpControl, listener); + return new RtpAudioTxHandle(sender); + } + }; + + AudioReceiver rx = new AudioReceiver() { + @Override + public AudioRxHandle createReceiver(RtpReceiverOptions options, UdpSocket socket, + AudioFormat audio_format, CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, + int sample_rate, int channels, Encoder additional_decoder, + RtpStreamReceiverListener listener) throws IOException, UnsupportedAudioFileException { + + RtpStreamReceiverListener onTerminate = new RtpStreamReceiverListenerAdapter() { + @Override + public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) { + try { + silenceTrimmer.close(); + recording.close(); + } catch (IOException ex) { + LOG.error("Failed to close recording.", ex); + } + } + }.andThen(listener); + + return new RtpAudioRxHandler(new RtpStreamReceiver(options, silenceTrimmer, additional_decoder, payloadFormat, socket, onTerminate)); + } + }; + StreamerOptions options = StreamerOptions.builder().build(); + return new AudioStreamer(flow_spec, tx, rx, options); + } catch (IOException | UnsupportedAudioFileException ex) { + throw new IOError(ex); + } + } + }; + + SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); + new AnswerBot(sipProvider, streamerFactory, uaConfig, mediaConfig, portPool, serviceConfig); + } + +} diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java new file mode 100644 index 00000000..e86767c5 --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package org.mjsip.ua.answerbot; + +import java.io.File; +import java.io.IOError; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.mjsip.media.AudioFile; +import org.mjsip.ua.sound.SilenceListener; +import org.slf4j.LoggerFactory; + +/** + * Audio {@link InputStream} that reads from varying WAV sources depending on the counterpart being silent or not. + */ +final class SpeechDispatcher extends InputStream implements SilenceListener { + + static final org.slf4j.Logger LOG = LoggerFactory.getLogger(SpeechDispatcher.class); + + /** + * The state of the conversation. + */ + private enum State { + /** + * Say hello after accepting the call. + */ + HELLO, + + /** + * Waiting for the counterpart to introduce himself. + */ + WAITING_FOR_INTRO, + + /** + * Ask the counterpart who is calling. + */ + WHO_IS_CALLING, + + /** + * Waiting for a response from the counterpart. + */ + WAITING_FOR_ANSWER, + + /** + * The counterpart did not say anything, ask if he is still there. + */ + NO_ANSWER, + + /** + * Listening to some answer from the counterpart; + */ + LISTENING, + + /** + * Asking a silly question in response to something the counterpart said. + */ + ASKING; + + /** + * If no event happened, what to say next? + */ + SpeechDispatcher.State next() { + switch (this) { + case HELLO: return WAITING_FOR_INTRO; + case WAITING_FOR_INTRO: return WHO_IS_CALLING; + case WHO_IS_CALLING: return WAITING_FOR_INTRO; + case WAITING_FOR_ANSWER: return NO_ANSWER; + case NO_ANSWER: return WAITING_FOR_ANSWER; + case LISTENING: return LISTENING; + case ASKING: return WAITING_FOR_ANSWER; + } + throw new AssertionError("No such state."); + } + + /** + * What audio to play in the this state? + */ + String audioType() { + switch (this) { + case HELLO: return "hello"; + case WAITING_FOR_INTRO: return "waiting"; + case WHO_IS_CALLING: return "who-is-calling"; + case WAITING_FOR_ANSWER: return "waiting"; + case NO_ANSWER: return "still-there"; + case LISTENING: return "waiting"; + case ASKING: return "question"; + } + throw new AssertionError("No such state."); + } + } + + private SpeechDispatcher.State _state = State.HELLO; + + private volatile InputStream _current; + + private final Map> _audioFragments; + + private Random _rnd; + + /** + * Creates a {@link SpeechDispatcher}. + * + * @param audioFragments + */ + public SpeechDispatcher(Map> audioFragments) { + _audioFragments = audioFragments; + _rnd = new Random(); + + _current = openAudio(); + } + + @Override + public int read() throws IOException { + int result = _current.read(); + if (result >= 0) { + return result; + } + + switchState(_state.next()); + return _current.read(); + } + + private void switchState(SpeechDispatcher.State next) { + _state = next; + + InputStream old = _current; + _current = openAudio(); + try { + old.close(); + } catch (IOException ex) { + LOG.warn("Failed to close audio stream.", ex); + } + } + + private InputStream openAudio() { + try { + String type = _state.audioType(); + LOG.info("Choosing audio: " + type); + List list = _audioFragments.get(type); + File file = list.get(_rnd.nextInt(list.size())); + LOG.info("Playing: " + file.getPath()); + return AudioFile.getAudioFileInputStream(file.getAbsolutePath()); + } catch (IOException | UnsupportedAudioFileException ex) { + throw new IOError(ex); + } + } + + @Override + public void onSilenceStarted(long clock) { + switch (_state) { + case LISTENING: switchState(State.ASKING); break; + default: // Ignore. + } + } + + @Override + public void onSilenceEnded(long clock) { + switch (_state) { + case WAITING_FOR_INTRO: + case WAITING_FOR_ANSWER: + switchState(State.LISTENING); break; + default: // Ignore. + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..79c3afed --- /dev/null +++ b/pom.xml @@ -0,0 +1,237 @@ + + 4.0.0 + + de.haumacher + phoneblock-parent + 1.4.6-SNAPSHOT + + PhoneBlock Aggregator + Maven aggregator building all parts of PhoneBlock + 2023 + + pom + + + + GPL-3.0-only + https://www.gnu.org/licenses/gpl-3.0.html + + + + + + Bernhard Haumacher + https://haumacher.de + + + + + https://phoneblock.haumacher.de + + + + scm:git:https://github.com/haumacher/phoneblock.git + HEAD + + + + phoneblock-ab + + + + 11 + 11 + + + true + + yyyy-MM-dd'T'HH:mm:ssXXX + + utf-8 + + 1.1.2 + + + + + org.slf4j + slf4j-api + 2.0.7 + + + + org.slf4j + jcl-over-slf4j + 2.0.7 + + + + org.tinylog + slf4j-tinylog + 2.6.2 + + + + org.tinylog + tinylog-impl + 2.6.2 + + + + org.junit.jupiter + junit-jupiter-api + 5.9.3 + test + + + + + + + ${project.build.sourceDirectory} + + **/*.java + + true + + + + + + de.haumacher.msgbuf + ${msgbuf.version} + msgbuf-generator-maven-plugin + + + + generate-protocols + + generate + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.1.0 + + + enforce-maven + + enforce + + + + + 3.2.5 + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + + org.apache.maven.plugins + maven-clean-plugin + 3.2.0 + + + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0 + + + + org.apache.maven.plugins + maven-install-plugin + 3.0.1 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + org.apache.maven.plugins + maven-site-plugin + 4.0.0-M3 + + + + + + org.owasp + dependency-check-maven + 7.3.0 + + ALL + true + + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.15.0 + + + + + use-latest-releases + update-properties + + + + + + + + + + regex + (.+-alpha.*|.+-M.*|.+-RC.*|26962ce.*) + + + + + + + + + \ No newline at end of file From 7b2684a0b1721dd3302b3d7a68782dce1cb31c77 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 28 Oct 2023 22:38:13 +0200 Subject: [PATCH 002/153] Adjusted to mjsip API change. --- .../org/mjsip/ua/answerbot/AnswerBot.java | 42 +++++++------------ .../mjsip/ua/answerbot/SpeechDispatcher.java | 3 +- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index dc1e63dd..b2f9a5f8 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -18,11 +18,8 @@ * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ - package org.mjsip.ua.answerbot; - - import java.io.File; import java.io.IOError; import java.io.IOException; @@ -35,7 +32,6 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.UnsupportedAudioFileException; -import org.mjsip.media.AudioFile; import org.mjsip.media.AudioStreamer; import org.mjsip.media.FlowSpec; import org.mjsip.media.MediaDesc; @@ -57,21 +53,26 @@ import org.mjsip.rtp.RtpControl; import org.mjsip.rtp.RtpPayloadFormat; import org.mjsip.sip.address.NameAddress; +import org.mjsip.sip.call.RegistrationOptions; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; import org.mjsip.sip.provider.SipStack; +import org.mjsip.sound.AudioFile; import org.mjsip.time.Scheduler; import org.mjsip.time.SchedulerConfig; +import org.mjsip.ua.MediaAgent; import org.mjsip.ua.MediaConfig; import org.mjsip.ua.MultipleUAS; -import org.mjsip.ua.PortConfig; -import org.mjsip.ua.PortPool; import org.mjsip.ua.ServiceConfig; +import org.mjsip.ua.ServiceOptions; import org.mjsip.ua.UAConfig; +import org.mjsip.ua.UAOptions; import org.mjsip.ua.UserAgent; import org.mjsip.ua.UserAgentListener; import org.mjsip.ua.UserAgentListenerAdapter; +import org.mjsip.ua.pool.PortConfig; +import org.mjsip.ua.pool.PortPool; import org.mjsip.ua.sound.AlawSilenceTrimmer; import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; @@ -81,8 +82,6 @@ import org.zoolu.util.Encoder; import org.zoolu.util.Flags; - - /** * {@link AnswerBot} is a VOIP server that automatically accepts incoming calls, sends an audio file and records * input received from the remote end. @@ -96,33 +95,25 @@ public class AnswerBot extends MultipleUAS { private MediaConfig _mediaConfig; - private PortPool _portPool; + private final StreamerFactory _streamerFactory; /** * Creates an {@link AnswerBot}. */ - public AnswerBot(SipProvider sip_provider, StreamerFactory streamerFactory, UAConfig uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceConfig serviceConfig) { - super(sip_provider,streamerFactory, uaConfig, serviceConfig); + public AnswerBot(SipProvider sip_provider, StreamerFactory streamerFactory, RegistrationOptions regOptions, + UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceOptions serviceConfig) { + super(sip_provider,portPool, regOptions, uaConfig, serviceConfig); + _streamerFactory = streamerFactory; _mediaConfig = mediaConfig; - _portPool = portPool; } @Override protected UserAgentListener createCallHandler(SipMessage msg) { - MediaConfig callMedia = MediaConfig.from(_mediaConfig.mediaDescs); - callMedia.allocateMediaPorts(_portPool); - return new UserAgentListenerAdapter() { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { LOG.info("Incomming call from: " + callee.getAddress()); - ua.accept(callMedia.mediaDescs); - } - - @Override - public void onUaCallClosed(UserAgent ua) { - LOG.info("Call closed."); - callMedia.releaseMediaPorts(_portPool); + ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), _streamerFactory)); } }; } @@ -139,9 +130,9 @@ public static void main(String[] args) { SipConfig sipConfig = SipConfig.init(config_file, flags); UAConfig uaConfig = UAConfig.init(config_file, flags, sipConfig); SchedulerConfig schedulerConfig = SchedulerConfig.init(config_file); - MediaConfig mediaConfig = MediaConfig.init(config_file, flags, uaConfig); + MediaConfig mediaConfig = MediaConfig.init(config_file, flags); PortConfig portConfig = PortConfig.init(config_file, flags); - ServiceConfig serviceConfig=ServiceConfig.init(config_file, flags); + ServiceOptions serviceConfig=ServiceConfig.init(config_file, flags); flags.close(); Map> audioFragments = new HashMap<>(); @@ -157,7 +148,6 @@ public static void main(String[] args) { } } - PortPool portPool = new PortPool(portConfig.mediaPort, portConfig.portCount); StreamerFactory streamerFactory = new StreamerFactory() { @Override public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { @@ -217,7 +207,7 @@ public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) }; SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, streamerFactory, uaConfig, mediaConfig, portPool, serviceConfig); + new AnswerBot(sipProvider, streamerFactory, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig); } } diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java index e86767c5..34df99d5 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java @@ -13,7 +13,7 @@ import javax.sound.sampled.UnsupportedAudioFileException; -import org.mjsip.media.AudioFile; +import org.mjsip.sound.AudioFile; import org.mjsip.ua.sound.SilenceListener; import org.slf4j.LoggerFactory; @@ -142,7 +142,6 @@ private void switchState(SpeechDispatcher.State next) { private InputStream openAudio() { try { String type = _state.audioType(); - LOG.info("Choosing audio: " + type); List list = _audioFragments.get(type); File file = list.get(_rnd.nextInt(list.size())); LOG.info("Playing: " + file.getPath()); From 393b8cc978ecf700dca926d889232dc501b6e75a Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 30 Oct 2023 19:53:13 +0100 Subject: [PATCH 003/153] Use new file per recording. --- phoneblock-ab/.gitignore | 1 + .../org/mjsip/ua/answerbot/AnswerBot.java | 121 ++++-------------- .../mjsip/ua/answerbot/DialogueFactory.java | 113 ++++++++++++++++ .../mjsip/ua/answerbot/SpeechDispatcher.java | 18 ++- 4 files changed, 151 insertions(+), 102 deletions(-) create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java diff --git a/phoneblock-ab/.gitignore b/phoneblock-ab/.gitignore index b83d2226..4aaf275a 100644 --- a/phoneblock-ab/.gitignore +++ b/phoneblock-ab/.gitignore @@ -1 +1,2 @@ /target/ +*.wav \ No newline at end of file diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index b2f9a5f8..ea1984a4 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -21,44 +21,20 @@ package org.mjsip.ua.answerbot; import java.io.File; -import java.io.IOError; -import java.io.IOException; -import java.io.OutputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.UnsupportedAudioFileException; - -import org.mjsip.media.AudioStreamer; -import org.mjsip.media.FlowSpec; import org.mjsip.media.MediaDesc; -import org.mjsip.media.MediaStreamer; -import org.mjsip.media.RtpStreamReceiver; -import org.mjsip.media.RtpStreamReceiverListener; -import org.mjsip.media.RtpStreamReceiverListenerAdapter; -import org.mjsip.media.RtpStreamSender; -import org.mjsip.media.RtpStreamSenderListener; -import org.mjsip.media.StreamerOptions; -import org.mjsip.media.rx.AudioReceiver; -import org.mjsip.media.rx.AudioRxHandle; -import org.mjsip.media.rx.RtpAudioRxHandler; -import org.mjsip.media.rx.RtpReceiverOptions; -import org.mjsip.media.tx.AudioTXHandle; -import org.mjsip.media.tx.AudioTransmitter; -import org.mjsip.media.tx.RtpAudioTxHandle; -import org.mjsip.media.tx.RtpSenderOptions; -import org.mjsip.rtp.RtpControl; -import org.mjsip.rtp.RtpPayloadFormat; import org.mjsip.sip.address.NameAddress; import org.mjsip.sip.call.RegistrationOptions; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; import org.mjsip.sip.provider.SipStack; -import org.mjsip.sound.AudioFile; import org.mjsip.time.Scheduler; import org.mjsip.time.SchedulerConfig; import org.mjsip.ua.MediaAgent; @@ -73,13 +49,8 @@ import org.mjsip.ua.UserAgentListenerAdapter; import org.mjsip.ua.pool.PortConfig; import org.mjsip.ua.pool.PortPool; -import org.mjsip.ua.sound.AlawSilenceTrimmer; import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; -import org.zoolu.net.UdpSocket; -import org.zoolu.sound.CodecType; -import org.zoolu.sound.SimpleAudioSystem; -import org.zoolu.util.Encoder; import org.zoolu.util.Flags; /** @@ -88,22 +59,19 @@ */ public class AnswerBot extends MultipleUAS { - private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnswerBot.class); - - /** Media file to play when answering the call. */ - public static String DEFAULT_ANNOUNCEMENT_FILE="./announcement-8000hz-mono-a-law.wav"; + static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnswerBot.class); private MediaConfig _mediaConfig; - private final StreamerFactory _streamerFactory; + private Map> _audioFragments; /** * Creates an {@link AnswerBot}. */ - public AnswerBot(SipProvider sip_provider, StreamerFactory streamerFactory, RegistrationOptions regOptions, + public AnswerBot(SipProvider sip_provider, Map> audioFragments, RegistrationOptions regOptions, UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceOptions serviceConfig) { super(sip_provider,portPool, regOptions, uaConfig, serviceConfig); - _streamerFactory = streamerFactory; + _audioFragments = audioFragments; _mediaConfig = mediaConfig; } @@ -112,8 +80,23 @@ protected UserAgentListener createCallHandler(SipMessage msg) { return new UserAgentListenerAdapter() { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { + String callId = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS").format(new Date()) + " " + name(caller); + StreamerFactory streamerFactory = new DialogueFactory(_audioFragments, callId); + LOG.info("Incomming call from: " + callee.getAddress()); - ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), _streamerFactory)); + ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); + } + + private String name(NameAddress caller) { + return afterAt(caller.getAddress().getSpecificPart()); + } + + private String afterAt(String specificPart) { + int atIndex = specificPart.indexOf('@'); + if (atIndex < 0) { + return specificPart; + } + return specificPart.substring(0, atIndex); } }; } @@ -148,66 +131,8 @@ public static void main(String[] args) { } } - StreamerFactory streamerFactory = new StreamerFactory() { - @Override - public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { - SpeechDispatcher speechDispatcher = new SpeechDispatcher(audioFragments); - - int sampleRate = flow_spec.getMediaSpec().getSampleRate(); - try { - OutputStream recording = AudioFile.getAudioFileOutputStream("recording.wav", SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); - - int bufferTime = 20; - int minSilenceTime = 500; - int paddingTime = 100; - double silenceDb = -30; - AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); - - AudioTransmitter tx = new AudioTransmitter() { - @Override - public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket, AudioFormat audio_format, - CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, int sample_rate, int channels, - Encoder additional_encoder, long packet_time, int packet_size, String remote_addr, int remote_port, - RtpStreamSenderListener listener, RtpControl rtpControl) throws IOException { - - RtpStreamSender sender = new RtpStreamSender(options, speechDispatcher, true, payload_type, payloadFormat, sample_rate, - channels, packet_time, packet_size, additional_encoder, udp_socket, remote_addr, remote_port, rtpControl, listener); - return new RtpAudioTxHandle(sender); - } - }; - - AudioReceiver rx = new AudioReceiver() { - @Override - public AudioRxHandle createReceiver(RtpReceiverOptions options, UdpSocket socket, - AudioFormat audio_format, CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, - int sample_rate, int channels, Encoder additional_decoder, - RtpStreamReceiverListener listener) throws IOException, UnsupportedAudioFileException { - - RtpStreamReceiverListener onTerminate = new RtpStreamReceiverListenerAdapter() { - @Override - public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) { - try { - silenceTrimmer.close(); - recording.close(); - } catch (IOException ex) { - LOG.error("Failed to close recording.", ex); - } - } - }.andThen(listener); - - return new RtpAudioRxHandler(new RtpStreamReceiver(options, silenceTrimmer, additional_decoder, payloadFormat, socket, onTerminate)); - } - }; - StreamerOptions options = StreamerOptions.builder().build(); - return new AudioStreamer(flow_spec, tx, rx, options); - } catch (IOException | UnsupportedAudioFileException ex) { - throw new IOError(ex); - } - } - }; - SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, streamerFactory, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig); + new AnswerBot(sipProvider, audioFragments, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig); } } diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java new file mode 100644 index 00000000..d0d90166 --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package org.mjsip.ua.answerbot; + +import java.io.File; +import java.io.IOError; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.mjsip.media.AudioStreamer; +import org.mjsip.media.FlowSpec; +import org.mjsip.media.MediaStreamer; +import org.mjsip.media.RtpStreamReceiver; +import org.mjsip.media.RtpStreamReceiverListener; +import org.mjsip.media.RtpStreamReceiverListenerAdapter; +import org.mjsip.media.RtpStreamSender; +import org.mjsip.media.RtpStreamSenderListener; +import org.mjsip.media.StreamerOptions; +import org.mjsip.media.rx.AudioReceiver; +import org.mjsip.media.rx.AudioRxHandle; +import org.mjsip.media.rx.RtpAudioRxHandler; +import org.mjsip.media.rx.RtpReceiverOptions; +import org.mjsip.media.tx.AudioTXHandle; +import org.mjsip.media.tx.AudioTransmitter; +import org.mjsip.media.tx.RtpAudioTxHandle; +import org.mjsip.media.tx.RtpSenderOptions; +import org.mjsip.rtp.RtpControl; +import org.mjsip.rtp.RtpPayloadFormat; +import org.mjsip.sound.AudioFile; +import org.mjsip.ua.sound.AlawSilenceTrimmer; +import org.mjsip.ua.streamer.StreamerFactory; +import org.zoolu.net.UdpSocket; +import org.zoolu.sound.CodecType; +import org.zoolu.sound.SimpleAudioSystem; +import org.zoolu.util.Encoder; + +/** + * {@link StreamerFactory} creating a bot correspondence. + */ +public final class DialogueFactory implements StreamerFactory { + private final Map> _audioFragments; + private String _callId; + + /** + * Creates a {@link DialogueFactory}. + */ + public DialogueFactory(Map> audioFragments, String callId) { + _audioFragments = audioFragments; + _callId = callId; + } + + @Override + public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { + SpeechDispatcher speechDispatcher = new SpeechDispatcher(_audioFragments); + + int sampleRate = flow_spec.getMediaSpec().getSampleRate(); + try { + OutputStream recording = AudioFile.getAudioFileOutputStream(_callId + ".wav", SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); + + int bufferTime = 20; + int minSilenceTime = 500; + int paddingTime = 100; + double silenceDb = -30; + AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); + + AudioTransmitter tx = new AudioTransmitter() { + @Override + public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket, AudioFormat audio_format, + CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, int sample_rate, int channels, + Encoder additional_encoder, long packet_time, int packet_size, String remote_addr, int remote_port, + RtpStreamSenderListener listener, RtpControl rtpControl) throws IOException { + + RtpStreamSender sender = new RtpStreamSender(options, speechDispatcher, true, payload_type, payloadFormat, sample_rate, + channels, packet_time, packet_size, additional_encoder, udp_socket, remote_addr, remote_port, rtpControl, listener); + return new RtpAudioTxHandle(sender); + } + }; + + AudioReceiver rx = new AudioReceiver() { + @Override + public AudioRxHandle createReceiver(RtpReceiverOptions options, UdpSocket socket, + AudioFormat audio_format, CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, + int sample_rate, int channels, Encoder additional_decoder, + RtpStreamReceiverListener listener) throws IOException, UnsupportedAudioFileException { + + RtpStreamReceiverListener onTerminate = new RtpStreamReceiverListenerAdapter() { + @Override + public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) { + try { + silenceTrimmer.close(); + recording.close(); + } catch (IOException ex) { + AnswerBot.LOG.error("Failed to close recording.", ex); + } + } + }.andThen(listener); + + return new RtpAudioRxHandler(new RtpStreamReceiver(options, silenceTrimmer, additional_decoder, payloadFormat, socket, onTerminate)); + } + }; + StreamerOptions options = StreamerOptions.builder().build(); + return new AudioStreamer(flow_spec, tx, rx, options); + } catch (IOException | UnsupportedAudioFileException ex) { + throw new IOError(ex); + } + } +} \ No newline at end of file diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java index 34df99d5..a829ea90 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java @@ -7,6 +7,7 @@ import java.io.IOError; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Random; @@ -106,14 +107,20 @@ String audioType() { /** * Creates a {@link SpeechDispatcher}. - * - * @param audioFragments */ public SpeechDispatcher(Map> audioFragments) { _audioFragments = audioFragments; _rnd = new Random(); - _current = openAudio(); + checkFragments(); + } + + private void checkFragments() { + for (State state : State.values()) { + if (_audioFragments.getOrDefault(state.audioType(), Collections.emptyList()).isEmpty()) { + LOG.error("No media for dialogue state '" + state.audioType() + "' found."); + } + } } @Override @@ -142,7 +149,10 @@ private void switchState(SpeechDispatcher.State next) { private InputStream openAudio() { try { String type = _state.audioType(); - List list = _audioFragments.get(type); + List list = _audioFragments.getOrDefault(type, Collections.emptyList()); + if (list.isEmpty()) { + throw new IllegalStateException("No media for dialogue state: " + type); + } File file = list.get(_rnd.nextInt(list.size())); LOG.info("Playing: " + file.getPath()); return AudioFile.getAudioFileInputStream(file.getAbsolutePath()); From 311ada5ef9ec042183d492d409459dc8817f4712 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Tue, 31 Oct 2023 21:01:17 +0100 Subject: [PATCH 004/153] Switched to args4j for command-line arguments parsing. --- .../org/mjsip/ua/answerbot/AnswerBot.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index ea1984a4..5c3e505e 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; +import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; import org.mjsip.sip.address.NameAddress; import org.mjsip.sip.call.RegistrationOptions; @@ -51,7 +52,6 @@ import org.mjsip.ua.pool.PortPool; import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; -import org.zoolu.util.Flags; /** * {@link AnswerBot} is a VOIP server that automatically accepts incoming calls, sends an audio file and records @@ -108,15 +108,18 @@ public static void main(String[] args) { String program = AnswerBot.class.getSimpleName(); LOG.info(program + " " + SipStack.version); - Flags flags=new Flags(program, args); - String config_file=flags.getString("-f","", System.getProperty("user.home") + "/.mjsip-answerbot" ,"loads configuration from the given file"); - SipConfig sipConfig = SipConfig.init(config_file, flags); - UAConfig uaConfig = UAConfig.init(config_file, flags, sipConfig); - SchedulerConfig schedulerConfig = SchedulerConfig.init(config_file); - MediaConfig mediaConfig = MediaConfig.init(config_file, flags); - PortConfig portConfig = PortConfig.init(config_file, flags); - ServiceOptions serviceConfig=ServiceConfig.init(config_file, flags); - flags.close(); + SipConfig sipConfig = new SipConfig(); + UAConfig uaConfig = new UAConfig(); + SchedulerConfig schedulerConfig = new SchedulerConfig(); + MediaConfig mediaConfig = new MediaConfig(); + PortConfig portConfig = new PortConfig(); + ServiceConfig serviceConfig = new ServiceConfig(); + + OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, serviceConfig); + + sipConfig.normalize(); + uaConfig.normalize(sipConfig); + mediaConfig.normalize(); Map> audioFragments = new HashMap<>(); File conversation = new File("./conversation"); From 6872ae8215f4795fd5496bf4771d12ce69285b21 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Wed, 1 Nov 2023 18:57:37 +0100 Subject: [PATCH 005/153] Allow to configure dialog and recoding dirs. --- .../org/mjsip/ua/answerbot/AnswerBot.java | 44 +++++++++--- .../mjsip/ua/answerbot/DialogueFactory.java | 69 ++++++++++--------- 2 files changed, 73 insertions(+), 40 deletions(-) diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index 5c3e505e..65105499 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; +import org.kohsuke.args4j.Option; import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; import org.mjsip.sip.address.NameAddress; @@ -61,18 +62,22 @@ public class AnswerBot extends MultipleUAS { static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnswerBot.class); - private MediaConfig _mediaConfig; + private final MediaConfig _mediaConfig; - private Map> _audioFragments; + private final Map> _audioFragments; + + private final String _recordingDir; /** * Creates an {@link AnswerBot}. + * @param recordingDir */ public AnswerBot(SipProvider sip_provider, Map> audioFragments, RegistrationOptions regOptions, - UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceOptions serviceConfig) { + UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceOptions serviceConfig, String recordingDir) { super(sip_provider,portPool, regOptions, uaConfig, serviceConfig); _audioFragments = audioFragments; _mediaConfig = mediaConfig; + _recordingDir = recordingDir; } @Override @@ -80,8 +85,14 @@ protected UserAgentListener createCallHandler(SipMessage msg) { return new UserAgentListenerAdapter() { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { - String callId = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS").format(new Date()) + " " + name(caller); - StreamerFactory streamerFactory = new DialogueFactory(_audioFragments, callId); + String recordingFile; + if (_recordingDir != null) { + String callId = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS").format(new Date()) + " " + name(caller); + recordingFile = _recordingDir + "/" + callId + ".wav"; + } else { + recordingFile = null; + } + StreamerFactory streamerFactory = new DialogueFactory(_audioFragments, recordingFile); LOG.info("Incomming call from: " + callee.getAddress()); ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); @@ -100,6 +111,21 @@ private String afterAt(String specificPart) { } }; } + + public static class Config { + @Option(name = "--conversation", usage = "Directory with WAV files used for streaming during automatic conversation.") + String conversationDir = "./conversation"; + + @Option(name = "--recodings", usage = "Directory where to store recordings to, 'none' to disable recording. ") + String recordingDir = "."; + + /** + * The configured recoding directory. + */ + public String getRecordingDir() { + return recordingDir == null || recordingDir.isBlank() || "none".equals(recordingDir) ? null : recordingDir; + } + } /** * The main entry point. @@ -114,15 +140,17 @@ public static void main(String[] args) { MediaConfig mediaConfig = new MediaConfig(); PortConfig portConfig = new PortConfig(); ServiceConfig serviceConfig = new ServiceConfig(); + + Config config = new Config(); - OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, serviceConfig); + OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, serviceConfig, config); sipConfig.normalize(); uaConfig.normalize(sipConfig); mediaConfig.normalize(); Map> audioFragments = new HashMap<>(); - File conversation = new File("./conversation"); + File conversation = new File(config.conversationDir); for (File type : conversation.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { ArrayList files = new ArrayList<>(); String typeName = type.getName(); @@ -135,7 +163,7 @@ public static void main(String[] args) { } SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, audioFragments, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig); + new AnswerBot(sipProvider, audioFragments, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig, config.recordingDir); } } diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java index d0d90166..f743ceb8 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java @@ -45,14 +45,14 @@ */ public final class DialogueFactory implements StreamerFactory { private final Map> _audioFragments; - private String _callId; + private final String _recordingFile; /** * Creates a {@link DialogueFactory}. */ - public DialogueFactory(Map> audioFragments, String callId) { + public DialogueFactory(Map> audioFragments, String recordingFile) { _audioFragments = audioFragments; - _callId = callId; + _recordingFile = recordingFile; } @Override @@ -61,14 +61,6 @@ public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { int sampleRate = flow_spec.getMediaSpec().getSampleRate(); try { - OutputStream recording = AudioFile.getAudioFileOutputStream(_callId + ".wav", SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); - - int bufferTime = 20; - int minSilenceTime = 500; - int paddingTime = 100; - double silenceDb = -30; - AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); - AudioTransmitter tx = new AudioTransmitter() { @Override public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket, AudioFormat audio_format, @@ -82,28 +74,41 @@ public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket } }; - AudioReceiver rx = new AudioReceiver() { - @Override - public AudioRxHandle createReceiver(RtpReceiverOptions options, UdpSocket socket, - AudioFormat audio_format, CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, - int sample_rate, int channels, Encoder additional_decoder, - RtpStreamReceiverListener listener) throws IOException, UnsupportedAudioFileException { - - RtpStreamReceiverListener onTerminate = new RtpStreamReceiverListenerAdapter() { - @Override - public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) { - try { - silenceTrimmer.close(); - recording.close(); - } catch (IOException ex) { - AnswerBot.LOG.error("Failed to close recording.", ex); + AudioReceiver rx; + if (_recordingFile != null) { + OutputStream recording = AudioFile.getAudioFileOutputStream(_recordingFile, SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); + + int bufferTime = 20; + int minSilenceTime = 500; + int paddingTime = 100; + double silenceDb = -30; + AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); + + rx = new AudioReceiver() { + @Override + public AudioRxHandle createReceiver(RtpReceiverOptions options, UdpSocket socket, + AudioFormat audio_format, CodecType codec, int payload_type, RtpPayloadFormat payloadFormat, + int sample_rate, int channels, Encoder additional_decoder, + RtpStreamReceiverListener listener) throws IOException, UnsupportedAudioFileException { + + RtpStreamReceiverListener onTerminate = new RtpStreamReceiverListenerAdapter() { + @Override + public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) { + try { + silenceTrimmer.close(); + recording.close(); + } catch (IOException ex) { + AnswerBot.LOG.error("Failed to close recording.", ex); + } } - } - }.andThen(listener); - - return new RtpAudioRxHandler(new RtpStreamReceiver(options, silenceTrimmer, additional_decoder, payloadFormat, socket, onTerminate)); - } - }; + }.andThen(listener); + + return new RtpAudioRxHandler(new RtpStreamReceiver(options, silenceTrimmer, additional_decoder, payloadFormat, socket, onTerminate)); + } + }; + } else { + rx = null; + } StreamerOptions options = StreamerOptions.builder().build(); return new AudioStreamer(flow_spec, tx, rx, options); } catch (IOException | UnsupportedAudioFileException ex) { From 41167f38a031e79dc3325dc1ef7007c04d8b4700 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Wed, 1 Nov 2023 20:03:45 +0100 Subject: [PATCH 006/153] Added DynIP servlet for updating IP addresses for PhoneBlock-AB. The PhoneBlock-AB cloud answering machine is implemented as external VOIP device that registers at the customers router. To find the router's IP address PhoneBlock-AB provides a built-in DynDNS service. This can be used, if the has no domain name registered and no otherwise configured DynDNS service. --- .../phoneblock/app/api/DynIpServlet.java | 71 +++++++++++++++++++ .../de/haumacher/phoneblock/db/Users.java | 12 ++++ .../de/haumacher/phoneblock/db/db-schema.sql | 2 + 3 files changed, 85 insertions(+) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java new file mode 100644 index 00000000..78d1d51b --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.app.api; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.Arrays; +import java.util.stream.Collectors; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.ibatis.session.SqlSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.haumacher.phoneblock.db.DBService; +import de.haumacher.phoneblock.db.Users; + +/** + * Servlet to record a customers dynamic IP address over DynIP protocol. + */ +@WebServlet(urlPatterns = "/api/dynip") +public class DynIpServlet extends HttpServlet { + + private static final Logger LOG = LoggerFactory.getLogger(DynIpServlet.class); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String user = req.getParameter("user"); + String passwd = req.getParameter("passwd"); + + String userName = DBService.getInstance().login(user, passwd); + if (userName == null) { + LOG.warn("Failed to authenticate: " + parameters(req)); + return; + } + + String ip = req.getParameter("ip"); + InetAddress address = InetAddress.getByName(ip); + if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress() + || address.isMCGlobal() || address.isMCLinkLocal() || address.isMCNodeLocal() || address.isMCOrgLocal() + || address.isMCSiteLocal() || address.isMulticastAddress() || address.isSiteLocalAddress()) { + LOG.warn("Provided invalid address: " + parameters(req)); + return; + } + + try (SqlSession session = DBService.getInstance().openSession()) { + Users users = session.getMapper(Users.class); + + Long userId = users.getUserId(userName); + users.setIP(userId, ip); + session.commit(); + + LOG.info("Updated IP address for: " + userName); + } + + // Mark as successfully processed. + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.getWriter().write("OK"); + } + + private String parameters(HttpServletRequest req) { + return req.getParameterMap().entrySet().stream().map(e -> e.getKey() + "=" + Arrays.asList(e.getValue())).collect(Collectors.joining(", ")); + } +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 62650a5b..28ff7384 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -46,6 +46,18 @@ public interface Users { @Select("select ID from USERS where LOGIN=#{login}") Long getUserId(String login); + /** + * Updates the user's dynamic IP address. + * + * @param id + * The user ID. + * @param ip + * The new IP address. + * @return 1 if the update was successful. + */ + @Insert("update USERS set IP=#{ip} where ID=#{id}") + int setIP(long id, String ip); + /** * Retrieves the user ID for the user with the given user name (e-mail). */ diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql index fe132d42..d1fffcab 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql @@ -86,6 +86,8 @@ CREATE TABLE USERS ( NOTIFIED BOOLEAN DEFAULT 0 NOT NULL, WILDCARDS BOOLEAN DEFAULT 1 NOT NULL, WELCOME BOOLEAN DEFAULT 0 NOT NULL, + + IP CHARACTER VARYING(64) DEFAULT NULL, CONSTRAINT USERS_PK PRIMARY KEY (ID) ); From bec1c39d4905fe1ff6f6201c792f03aac260d967 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 5 Nov 2023 11:11:45 +0100 Subject: [PATCH 007/153] Added check for the conversation directory to exist. --- .../src/main/java/org/mjsip/ua/answerbot/AnswerBot.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index 65105499..ccbe3235 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -31,6 +31,8 @@ import org.kohsuke.args4j.Option; import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; +import org.mjsip.pool.PortConfig; +import org.mjsip.pool.PortPool; import org.mjsip.sip.address.NameAddress; import org.mjsip.sip.call.RegistrationOptions; import org.mjsip.sip.message.SipMessage; @@ -49,8 +51,6 @@ import org.mjsip.ua.UserAgent; import org.mjsip.ua.UserAgentListener; import org.mjsip.ua.UserAgentListenerAdapter; -import org.mjsip.ua.pool.PortConfig; -import org.mjsip.ua.pool.PortPool; import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; @@ -151,6 +151,9 @@ public static void main(String[] args) { Map> audioFragments = new HashMap<>(); File conversation = new File(config.conversationDir); + if (!conversation.isDirectory()) { + LOG.error("Conversation directory does not exist: " + conversation.getAbsolutePath()); + } for (File type : conversation.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { ArrayList files = new ArrayList<>(); String typeName = type.getName(); From 8520c64041ff5e0616ebb84af88f86387be3fbce Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 5 Nov 2023 11:14:11 +0100 Subject: [PATCH 008/153] Added configuration template and lanch config for Fritz!Box access. --- .../bin/AnswerBot (Fritz!Box).launch | 20 +++++++++++ phoneblock-ab/configs/.gitignore | 2 ++ .../configs/phoneblock-answerbot.template | 33 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 phoneblock-ab/bin/AnswerBot (Fritz!Box).launch create mode 100644 phoneblock-ab/configs/.gitignore create mode 100644 phoneblock-ab/configs/phoneblock-answerbot.template diff --git a/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch new file mode 100644 index 00000000..d7372b16 --- /dev/null +++ b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/phoneblock-ab/configs/.gitignore b/phoneblock-ab/configs/.gitignore new file mode 100644 index 00000000..48238912 --- /dev/null +++ b/phoneblock-ab/configs/.gitignore @@ -0,0 +1,2 @@ +/phoneblock-answerbot +/test* \ No newline at end of file diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template new file mode 100644 index 00000000..16667112 --- /dev/null +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -0,0 +1,33 @@ +# +# Answerbot configuration for registering to a Fritz!Box in a local network. +# + +################################################################################################### +# This file is a configuration template. Copy the contents to a file named "mjsip-answerbot" +# and fill out the missing options below. Afterwards, you can start the launch configuration +# AnswerBot (Fritz!Box).launch in the /bin folder. +################################################################################################### + +registrar=fritz.box +auth_realm=fritz.box + +# The user name for registering the IP phone device configured in your Fritz!Box +user=phoneblock +auth_user=phoneblock + +# The password for registering the IP phone device configured in your Fritz!Box +auth_passwd= + +# The local ports used by the answerbot +host_port=55060 +media_port=50061 +port-count=20 + +# The directory where the WAV files for the bot conversation are located. +conversation=./conversation + +# The format of the conversation media stord in the directories in ./conversation/* +media=audio 4080 RTP/AVP { 120 PCMA 16000 160 } + +# Enable SIP message debugging. +log-all-packets=no \ No newline at end of file From ca8c3b2b11c1442fff596e731703a6d8abe17e72 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 5 Nov 2023 19:36:20 +0100 Subject: [PATCH 009/153] Added configuration options to control the answerbot dialogue. --- .../configs/phoneblock-answerbot.template | 16 +++- .../org/mjsip/ua/answerbot/AnswerBot.java | 75 ++++++----------- .../mjsip/ua/answerbot/AnswerbotConfig.java | 82 +++++++++++++++++++ .../mjsip/ua/answerbot/AnswerbotOptions.java | 25 ++++++ .../org/mjsip/ua/answerbot/DialogOptions.java | 31 +++++++ .../mjsip/ua/answerbot/DialogueFactory.java | 11 ++- 6 files changed, 183 insertions(+), 57 deletions(-) create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java create mode 100644 phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index 16667112..8ca6a53c 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -23,11 +23,23 @@ host_port=55060 media_port=50061 port-count=20 +# Enable SIP message debugging. +log-all-packets=no + +# Directory where to store recordings to, 'none' to disable recording. +recodings = . + # The directory where the WAV files for the bot conversation are located. conversation=./conversation # The format of the conversation media stord in the directories in ./conversation/* media=audio 4080 RTP/AVP { 120 PCMA 16000 160 } -# Enable SIP message debugging. -log-all-packets=no \ No newline at end of file +# Number of milliseconds silence must be detected before responding. +min-silence-time = 1000 + +# When recording, the number of milliseconds of silence to record before and after the conterpart's speach.") +padding-time = 500 + +# The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") +silence-db = -30 diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index ccbe3235..94d51d30 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Map; -import org.kohsuke.args4j.Option; import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; import org.mjsip.pool.PortConfig; @@ -44,8 +43,6 @@ import org.mjsip.ua.MediaAgent; import org.mjsip.ua.MediaConfig; import org.mjsip.ua.MultipleUAS; -import org.mjsip.ua.ServiceConfig; -import org.mjsip.ua.ServiceOptions; import org.mjsip.ua.UAConfig; import org.mjsip.ua.UAOptions; import org.mjsip.ua.UserAgent; @@ -66,18 +63,30 @@ public class AnswerBot extends MultipleUAS { private final Map> _audioFragments; - private final String _recordingDir; + private AnswerbotOptions _config; /** * Creates an {@link AnswerBot}. - * @param recordingDir */ - public AnswerBot(SipProvider sip_provider, Map> audioFragments, RegistrationOptions regOptions, - UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, ServiceOptions serviceConfig, String recordingDir) { - super(sip_provider,portPool, regOptions, uaConfig, serviceConfig); + public AnswerBot(SipProvider sip_provider, RegistrationOptions regOptions, + UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, AnswerbotOptions botOptions) { + super(sip_provider,portPool, regOptions, uaConfig, botOptions); + + Map> audioFragments = new HashMap<>(); + File conversationDir = botOptions.conversationDir(); + for (File type : conversationDir.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { + ArrayList files = new ArrayList<>(); + String typeName = type.getName(); + audioFragments.put(typeName, files); + for (File wav : type.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { + files.add(wav); + + LOG.info("Found audio fragment for " + typeName + ": " + wav.getPath()); + } + } _audioFragments = audioFragments; _mediaConfig = mediaConfig; - _recordingDir = recordingDir; + _config = botOptions; } @Override @@ -86,13 +95,14 @@ protected UserAgentListener createCallHandler(SipMessage msg) { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { String recordingFile; - if (_recordingDir != null) { + String recordingDir = _config.recordingDir(); + if (recordingDir != null) { String callId = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS").format(new Date()) + " " + name(caller); - recordingFile = _recordingDir + "/" + callId + ".wav"; + recordingFile = recordingDir + "/" + callId + ".wav"; } else { recordingFile = null; } - StreamerFactory streamerFactory = new DialogueFactory(_audioFragments, recordingFile); + StreamerFactory streamerFactory = new DialogueFactory(_config, _audioFragments, recordingFile); LOG.info("Incomming call from: " + callee.getAddress()); ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); @@ -112,21 +122,6 @@ private String afterAt(String specificPart) { }; } - public static class Config { - @Option(name = "--conversation", usage = "Directory with WAV files used for streaming during automatic conversation.") - String conversationDir = "./conversation"; - - @Option(name = "--recodings", usage = "Directory where to store recordings to, 'none' to disable recording. ") - String recordingDir = "."; - - /** - * The configured recoding directory. - */ - public String getRecordingDir() { - return recordingDir == null || recordingDir.isBlank() || "none".equals(recordingDir) ? null : recordingDir; - } - } - /** * The main entry point. */ @@ -139,34 +134,16 @@ public static void main(String[] args) { SchedulerConfig schedulerConfig = new SchedulerConfig(); MediaConfig mediaConfig = new MediaConfig(); PortConfig portConfig = new PortConfig(); - ServiceConfig serviceConfig = new ServiceConfig(); + AnswerbotConfig botConfig = new AnswerbotConfig(); - Config config = new Config(); - - OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, serviceConfig, config); + OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, botConfig); sipConfig.normalize(); uaConfig.normalize(sipConfig); - mediaConfig.normalize(); + botConfig.normalize(); - Map> audioFragments = new HashMap<>(); - File conversation = new File(config.conversationDir); - if (!conversation.isDirectory()) { - LOG.error("Conversation directory does not exist: " + conversation.getAbsolutePath()); - } - for (File type : conversation.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { - ArrayList files = new ArrayList<>(); - String typeName = type.getName(); - audioFragments.put(typeName, files); - for (File wav : type.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { - files.add(wav); - - LOG.info("Found audio fragment for " + typeName + ": " + wav.getPath()); - } - } - SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, audioFragments, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), serviceConfig, config.recordingDir); + new AnswerBot(sipProvider, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), botConfig); } } diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java new file mode 100644 index 00000000..83be8d71 --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package org.mjsip.ua.answerbot; + +import java.io.File; + +import org.kohsuke.args4j.Option; +import org.mjsip.ua.ServiceConfig; + +/** + * Configuration options for the {@link AnswerBot}. + */ +public class AnswerbotConfig extends ServiceConfig implements AnswerbotOptions { + + @Option(name = "--conversation", usage = "Directory with WAV files used for streaming during automatic conversation.") + private File _conversationDir = new File("./conversation"); + + @Option(name = "--recodings", usage = "Directory where to store recordings to, 'none' to disable recording. ") + private String _recordingDir = "."; + + /** + * The configured recoding directory. + */ + public String getRecordingDir() { + return _recordingDir == null || _recordingDir.isBlank() || "none".equals(_recordingDir) ? null : _recordingDir; + } + + @Option(name = "--buffer-time", usage = "Time in milliseconds to buffer the audio stream for silence detection.") + private int _bufferTime = 20; + + @Option(name = "--min-silence-time", usage = "Number of milliseconds silence must be detected before responding.") + private int _minSilenceTime = 500; + + @Option(name = "--padding-time", usage = "When recording, the number of milliseconds of silence to record before and after the conterpart's speach.") + private int _paddingTime = 100; + + @Option(name = "--silence-db", usage = "The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") + private double silenceDb = -30; + + @Override + public int bufferTime() { + return _bufferTime; + } + @Override + public int minSilenceTime() { + return _minSilenceTime; + } + @Override + public int paddingTime() { + return _paddingTime; + } + @Override + public double silenceDb() { + return silenceDb; + } + + @Override + public String recordingDir() { + return _recordingDir; + } + + @Override + public File conversationDir() { + return _conversationDir; + } + + /** + * Normalizes options, must be called after option parsing. + */ + public void normalize() { + if ("none".equals(_recordingDir)) { + _recordingDir = null; + } + + if (!_conversationDir.isDirectory()) { + System.err.println("Conversation directory does not exist: " + conversationDir().getAbsolutePath()); + System.exit(1); + } + } + +} diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java new file mode 100644 index 00000000..82a38cbe --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package org.mjsip.ua.answerbot; + +import java.io.File; + +import org.mjsip.ua.ServiceOptions; + +/** + * Configuration options of the {@link AnswerBot}. + */ +public interface AnswerbotOptions extends DialogOptions, ServiceOptions { + + /** + * The directory where to store recordings, null if recording is diabled. + */ + String recordingDir(); + + /** + * The directory to load the bot conversation WAV files from. + */ + File conversationDir(); + +} diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java new file mode 100644 index 00000000..190e1ea1 --- /dev/null +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package org.mjsip.ua.answerbot; + +/** + * Options for controlling the bot conversation. + */ +public interface DialogOptions { + + /** + * Size of the audio buffer in milliseconds. Silence detection operates on buffered samples of that duration. + */ + int bufferTime(); + + /** + * Minimum time that silence must be detected to respond to the counterpart. + */ + int minSilenceTime(); + + /** + * When recording, the time of silence to include to the recording before and after the counterpart's speach. + */ + int paddingTime(); + + /** + * Value in decibel relative to full scale (dbfs) that is considered silence. + */ + double silenceDb(); + +} diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java index f743ceb8..49249058 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java @@ -46,11 +46,13 @@ public final class DialogueFactory implements StreamerFactory { private final Map> _audioFragments; private final String _recordingFile; + private DialogOptions _config; /** * Creates a {@link DialogueFactory}. */ - public DialogueFactory(Map> audioFragments, String recordingFile) { + public DialogueFactory(DialogOptions config, Map> audioFragments, String recordingFile) { + _config = config; _audioFragments = audioFragments; _recordingFile = recordingFile; } @@ -78,11 +80,8 @@ public AudioTXHandle createSender(RtpSenderOptions options, UdpSocket udp_socket if (_recordingFile != null) { OutputStream recording = AudioFile.getAudioFileOutputStream(_recordingFile, SimpleAudioSystem.getAudioFormat(flow_spec.getMediaSpec().getCodecType(), sampleRate)); - int bufferTime = 20; - int minSilenceTime = 500; - int paddingTime = 100; - double silenceDb = -30; - AlawSilenceTrimmer silenceTrimmer = new AlawSilenceTrimmer(sampleRate, bufferTime, minSilenceTime, paddingTime, silenceDb, recording, speechDispatcher); + AlawSilenceTrimmer silenceTrimmer = + new AlawSilenceTrimmer(sampleRate, _config.bufferTime(), _config.minSilenceTime(), _config.paddingTime(), _config.silenceDb(), recording, speechDispatcher); rx = new AudioReceiver() { @Override From f44d14f3864b4779761a4d0f4b26fd1dc19925b8 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 6 Nov 2023 20:02:53 +0100 Subject: [PATCH 010/153] Completed call handling in phoneblock-ab. Extracted PhoneBlock API to separate module phoneblock-shared. Only accept calls that have sufficient PhoneBlock votes. Added service configuration file to add PhoneBlock as Linux systemd service. --- phoneblock-ab/bin/phoneblock.service | 15 + .../configs/phoneblock-answerbot.template | 9 +- phoneblock-ab/pom.xml | 35 ++ .../org/mjsip/ua/answerbot/AnswerBot.java | 65 ++- .../mjsip/ua/answerbot/AnswerbotConfig.java | 16 + .../mjsip/ua/answerbot/AnswerbotOptions.java | 10 + phoneblock-shared/.classpath | 27 ++ phoneblock-shared/.gitignore | 1 + phoneblock-shared/.project | 23 ++ .../org.eclipse.core.resources.prefs | 3 + .../.settings/org.eclipse.jdt.core.prefs | 9 + .../.settings/org.eclipse.m2e.core.prefs | 4 + phoneblock-shared/pom.xml | 63 +++ .../phoneblock/app/api/model/NumberInfo.java | 377 ++++++++++++++++++ .../phoneblock/app/api/model/RateRequest.java | 0 .../app/api/model/RegistrationChallenge.java | 0 .../app/api/model/RegistrationCompletion.java | 0 .../app/api/model/RegistrationRequest.java | 0 .../app/api/model/RegistrationResult.java | 0 .../phoneblock/app/api/model/SessionInfo.java | 0 .../phoneblock/app/api/model/api.proto | 12 + .../src/main/java/module-info.java | 12 + phoneblock/pom.xml | 41 ++ phoneblock/src/main/java/module-info.java | 1 + pom.xml | 1 + 25 files changed, 721 insertions(+), 3 deletions(-) create mode 100644 phoneblock-ab/bin/phoneblock.service create mode 100644 phoneblock-shared/.classpath create mode 100644 phoneblock-shared/.gitignore create mode 100644 phoneblock-shared/.project create mode 100644 phoneblock-shared/.settings/org.eclipse.core.resources.prefs create mode 100644 phoneblock-shared/.settings/org.eclipse.jdt.core.prefs create mode 100644 phoneblock-shared/.settings/org.eclipse.m2e.core.prefs create mode 100644 phoneblock-shared/pom.xml create mode 100644 phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/NumberInfo.java rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/RateRequest.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationChallenge.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationCompletion.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationRequest.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationResult.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/SessionInfo.java (100%) rename {phoneblock => phoneblock-shared}/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto (89%) create mode 100644 phoneblock-shared/src/main/java/module-info.java diff --git a/phoneblock-ab/bin/phoneblock.service b/phoneblock-ab/bin/phoneblock.service new file mode 100644 index 00000000..39bb5ab6 --- /dev/null +++ b/phoneblock-ab/bin/phoneblock.service @@ -0,0 +1,15 @@ +[Unit] +Description=phoneblock +After=network.target + +[Service] +ExecStart=/usr/bin/java -jar phoneblock-ab-1.4.6-SNAPSHOT-jar-with-dependencies.jar +WorkingDirectory=/home/phoneblock +StandardOutput=inherit +StandardError=inherit +Restart=always +RestartSec=10s +User=phoneblock + +[Install] +WantedBy=multi-user.target diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index 8ca6a53c..7a4d99fc 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -3,7 +3,7 @@ # ################################################################################################### -# This file is a configuration template. Copy the contents to a file named "mjsip-answerbot" +# This file is a configuration template. Copy the contents to a file named "phoneblock-answerbot" # and fill out the missing options below. Afterwards, you can start the launch configuration # AnswerBot (Fritz!Box).launch in the /bin folder. ################################################################################################### @@ -43,3 +43,10 @@ padding-time = 500 # The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") silence-db = -30 + +# The minimum number of PhoneBlock votes for a number to be consideres SPAM. +min-votes = 4 + +# Whether to let PhoneBlock accept anonymous calls. It is not recommended to enable this option. Better configure a +# separate answering machine in you router to gracefully handle anonymous calls. +accept-anonymous = no diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml index 59920907..90ac2b92 100644 --- a/phoneblock-ab/pom.xml +++ b/phoneblock-ab/pom.xml @@ -40,10 +40,45 @@ + + de.haumacher + phoneblock-shared + ${project.version} + + org.mjsip mjsip-ua 1.9.0-SNAPSHOT + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + + org.mjsip.ua.answerbot.AnswerBot + + + + jar-with-dependencies + + + + + + + + \ No newline at end of file diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index 94d51d30..254935e9 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -21,6 +21,11 @@ package org.mjsip.ua.answerbot; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -51,6 +56,10 @@ import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; +import de.haumacher.msgbuf.json.JsonReader; +import de.haumacher.msgbuf.server.io.ReaderAdapter; +import de.haumacher.phoneblock.app.api.model.NumberInfo; + /** * {@link AnswerBot} is a VOIP server that automatically accepts incoming calls, sends an audio file and records * input received from the remote end. @@ -91,6 +100,47 @@ public AnswerBot(SipProvider sip_provider, RegistrationOptions regOptions, @Override protected UserAgentListener createCallHandler(SipMessage msg) { + String from = msg.getFromUser(); + LOG.info("Incomming call from: " + from); + + if (from == null) { + // An anonymous call, accept. + if (_config.getAcceptAnonymous()) { + return spamHandler(); + } else { + return rejectHandler(); + } + } else if (from.startsWith("**")) { + // A local test call, accept. + return spamHandler(); + } else { + NumberInfo info; + try { + URL url = new URL("https://phoneblock.haumacher.de/phoneblock/api/num/" + from + "?format=json"); + URLConnection connection = url.openConnection(); + connection.addRequestProperty("accept", "application/json"); + try (InputStream in = connection.getInputStream()) { + info = NumberInfo.readNumberInfo(new JsonReader(new ReaderAdapter(new InputStreamReader(in)))); + } + } catch (IOException ex) { + LOG.warn("Contacting PhoneBlock failed: " + ex.getMessage()); + return rejectHandler(); + } + + if (info.getVotes() < _config.getMinVotes()) { + // Not considered SPAM. + LOG.info("Not spam: " + from + " (" + info.getVotes() + " votes)"); + return rejectHandler(); + } + + return spamHandler(); + } + } + + /** + * A {@link UserAgentListener} that accepts the call and starts a nonsense conversation with the counterpart. + */ + private UserAgentListener spamHandler() { return new UserAgentListenerAdapter() { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { @@ -104,7 +154,6 @@ public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress calle } StreamerFactory streamerFactory = new DialogueFactory(_config, _audioFragments, recordingFile); - LOG.info("Incomming call from: " + callee.getAddress()); ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); } @@ -121,6 +170,18 @@ private String afterAt(String specificPart) { } }; } + + /** + * A {@link UserAgentListener} that rejects the call. + */ + private UserAgentListener rejectHandler() { + return new UserAgentListenerAdapter() { + @Override + public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { + ua.hangup(); + } + }; + } /** * The main entry point. @@ -136,7 +197,7 @@ public static void main(String[] args) { PortConfig portConfig = new PortConfig(); AnswerbotConfig botConfig = new AnswerbotConfig(); - OptionParser.parseOptions(args, ".mjsip-answerbot", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, botConfig); + OptionParser.parseOptions(args, ".phoneblock", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, botConfig); sipConfig.normalize(); uaConfig.normalize(sipConfig); diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java index 83be8d71..cdb2c7b7 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java @@ -37,6 +37,12 @@ public String getRecordingDir() { @Option(name = "--silence-db", usage = "The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") private double silenceDb = -30; + + @Option(name = "--accept-anonymous", usage = "Whether to let PhoneBlock accept anonymous calls. This is not recommended. Better configure a separate answering machine in you router to handle anonymous calls.") + private boolean _acceptAnonymous = false; + + @Option(name = "--min-votes", usage = "The minimum number of PhoneBlock votes for a number to be consideres SPAM.") + private int _minVotes = 4; @Override public int bufferTime() { @@ -64,6 +70,16 @@ public String recordingDir() { public File conversationDir() { return _conversationDir; } + + @Override + public boolean getAcceptAnonymous() { + return _acceptAnonymous; + } + + @Override + public int getMinVotes() { + return _minVotes; + } /** * Normalizes options, must be called after option parsing. diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java index 82a38cbe..d8a1ebb0 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java @@ -22,4 +22,14 @@ public interface AnswerbotOptions extends DialogOptions, ServiceOptions { */ File conversationDir(); + /** + * Whether to accept anonymous calls. + */ + boolean getAcceptAnonymous(); + + /** + * The minimum PhoneBlock votes to consider a call as SPAM and accept it. + */ + int getMinVotes(); + } diff --git a/phoneblock-shared/.classpath b/phoneblock-shared/.classpath new file mode 100644 index 00000000..04eb2980 --- /dev/null +++ b/phoneblock-shared/.classpath @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phoneblock-shared/.gitignore b/phoneblock-shared/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/phoneblock-shared/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/phoneblock-shared/.project b/phoneblock-shared/.project new file mode 100644 index 00000000..dd43408b --- /dev/null +++ b/phoneblock-shared/.project @@ -0,0 +1,23 @@ + + + phoneblock-shared + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/phoneblock-shared/.settings/org.eclipse.core.resources.prefs b/phoneblock-shared/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..c35a6291 --- /dev/null +++ b/phoneblock-shared/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +encoding//src/main/java=utf-8 +encoding/=utf-8 diff --git a/phoneblock-shared/.settings/org.eclipse.jdt.core.prefs b/phoneblock-shared/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..55e549bb --- /dev/null +++ b/phoneblock-shared/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.methodParameters=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 diff --git a/phoneblock-shared/.settings/org.eclipse.m2e.core.prefs b/phoneblock-shared/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000..f897a7f1 --- /dev/null +++ b/phoneblock-shared/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/phoneblock-shared/pom.xml b/phoneblock-shared/pom.xml new file mode 100644 index 00000000..ec6953ca --- /dev/null +++ b/phoneblock-shared/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + + + de.haumacher + phoneblock-parent + 1.4.6-SNAPSHOT + + + phoneblock-shared + + PhoneBlock shared code + + + + GPL-3.0-only + https://www.gnu.org/licenses/gpl-3.0.html + + + + + + Bernhard Haumacher + https://haumacher.de + + + + + https://phoneblock.haumacher.de + + + + scm:git:https://github.com/haumacher/phoneblock.git + HEAD + + + + + de.haumacher.msgbuf + ${msgbuf.version} + msgbuf-api + + + + + + + de.haumacher.msgbuf + ${msgbuf.version} + msgbuf-generator-maven-plugin + + + + generate-protocols + + generate + + + + + + + \ No newline at end of file diff --git a/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/NumberInfo.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/NumberInfo.java new file mode 100644 index 00000000..258f46f9 --- /dev/null +++ b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/NumberInfo.java @@ -0,0 +1,377 @@ +package de.haumacher.phoneblock.app.api.model; + +public class NumberInfo extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.binary.BinaryDataObject, de.haumacher.msgbuf.observer.Observable, de.haumacher.msgbuf.xml.XmlSerializable { + + /** + * Creates a {@link de.haumacher.phoneblock.app.api.model.NumberInfo} instance. + */ + public static de.haumacher.phoneblock.app.api.model.NumberInfo create() { + return new de.haumacher.phoneblock.app.api.model.NumberInfo(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.app.api.model.NumberInfo} type in JSON format. */ + public static final String NUMBER_INFO__TYPE = "NumberInfo"; + + /** @see #getPhone() */ + public static final String PHONE__PROP = "phone"; + + /** @see #getRating() */ + public static final String RATING__PROP = "rating"; + + /** @see #getVotes() */ + public static final String VOTES__PROP = "votes"; + + /** Identifier for the property {@link #getPhone()} in binary format. */ + static final int PHONE__ID = 1; + + /** Identifier for the property {@link #getRating()} in binary format. */ + static final int RATING__ID = 2; + + /** Identifier for the property {@link #getVotes()} in binary format. */ + static final int VOTES__ID = 3; + + private String _phone = ""; + + private String _rating = ""; + + private int _votes = 0; + + /** + * Creates a {@link NumberInfo} instance. + * + * @see de.haumacher.phoneblock.app.api.model.NumberInfo#create() + */ + protected NumberInfo() { + super(); + } + + /** + * The number being rated. + */ + public final String getPhone() { + return _phone; + } + + /** + * @see #getPhone() + */ + public de.haumacher.phoneblock.app.api.model.NumberInfo setPhone(String value) { + internalSetPhone(value); + return this; + } + + /** Internal setter for {@link #getPhone()} without chain call utility. */ + protected final void internalSetPhone(String value) { + _listener.beforeSet(this, PHONE__PROP, value); + _phone = value; + } + + /** + * The {@link Rating} of the {@link #getPhone() number}. + */ + public final String getRating() { + return _rating; + } + + /** + * @see #getRating() + */ + public de.haumacher.phoneblock.app.api.model.NumberInfo setRating(String value) { + internalSetRating(value); + return this; + } + + /** Internal setter for {@link #getRating()} without chain call utility. */ + protected final void internalSetRating(String value) { + _listener.beforeSet(this, RATING__PROP, value); + _rating = value; + } + + /** + * How often the {@link #getPhone() number} was rated in a {@link #getRating() certain way}. + */ + public final int getVotes() { + return _votes; + } + + /** + * @see #getVotes() + */ + public de.haumacher.phoneblock.app.api.model.NumberInfo setVotes(int value) { + internalSetVotes(value); + return this; + } + + /** Internal setter for {@link #getVotes()} without chain call utility. */ + protected final void internalSetVotes(int value) { + _listener.beforeSet(this, VOTES__PROP, value); + _votes = value; + } + + protected de.haumacher.msgbuf.observer.Listener _listener = de.haumacher.msgbuf.observer.Listener.NONE; + + @Override + public de.haumacher.phoneblock.app.api.model.NumberInfo registerListener(de.haumacher.msgbuf.observer.Listener l) { + internalRegisterListener(l); + return this; + } + + protected final void internalRegisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.register(_listener, l); + } + + @Override + public de.haumacher.phoneblock.app.api.model.NumberInfo unregisterListener(de.haumacher.msgbuf.observer.Listener l) { + internalUnregisterListener(l); + return this; + } + + protected final void internalUnregisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.unregister(_listener, l); + } + + @Override + public String jsonType() { + return NUMBER_INFO__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + PHONE__PROP, + RATING__PROP, + VOTES__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case PHONE__PROP: return getPhone(); + case RATING__PROP: return getRating(); + case VOTES__PROP: return getVotes(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case PHONE__PROP: internalSetPhone((String) value); break; + case RATING__PROP: internalSetRating((String) value); break; + case VOTES__PROP: internalSetVotes((int) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.app.api.model.NumberInfo readNumberInfo(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.app.api.model.NumberInfo result = new de.haumacher.phoneblock.app.api.model.NumberInfo(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(PHONE__PROP); + out.value(getPhone()); + out.name(RATING__PROP); + out.value(getRating()); + out.name(VOTES__PROP); + out.value(getVotes()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case PHONE__PROP: setPhone(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case RATING__PROP: setRating(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case VOTES__PROP: setVotes(in.nextInt()); break; + default: super.readField(in, field); + } + } + + @Override + public final void writeTo(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + out.beginObject(); + writeFields(out); + out.endObject(); + } + + /** + * Serializes all fields of this instance to the given binary output. + * + * @param out + * The binary output to write to. + * @throws java.io.IOException If writing fails. + */ + protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + out.name(PHONE__ID); + out.value(getPhone()); + out.name(RATING__ID); + out.value(getRating()); + out.name(VOTES__ID); + out.value(getVotes()); + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.app.api.model.NumberInfo readNumberInfo(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + in.beginObject(); + de.haumacher.phoneblock.app.api.model.NumberInfo result = de.haumacher.phoneblock.app.api.model.NumberInfo.readNumberInfo_Content(in); + in.endObject(); + return result; + } + + /** Helper for creating an object of type {@link de.haumacher.phoneblock.app.api.model.NumberInfo} from a polymorphic composition. */ + public static de.haumacher.phoneblock.app.api.model.NumberInfo readNumberInfo_Content(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + de.haumacher.phoneblock.app.api.model.NumberInfo result = new NumberInfo(); + result.readContent(in); + return result; + } + + /** Helper for reading all fields of this instance. */ + protected final void readContent(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + while (in.hasNext()) { + int field = in.nextName(); + readField(in, field); + } + } + + /** Consumes the value for the field with the given ID and assigns its value. */ + protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { + switch (field) { + case PHONE__ID: setPhone(in.nextString()); break; + case RATING__ID: setRating(in.nextString()); break; + case VOTES__ID: setVotes(in.nextInt()); break; + default: in.skipValue(); + } + } + + /** XML element name representing a {@link de.haumacher.phoneblock.app.api.model.NumberInfo} type. */ + public static final String NUMBER_INFO__XML_ELEMENT = "number-info"; + + /** XML attribute or element name of a {@link #getPhone} property. */ + private static final String PHONE__XML_ATTR = "phone"; + + /** XML attribute or element name of a {@link #getRating} property. */ + private static final String RATING__XML_ATTR = "rating"; + + /** XML attribute or element name of a {@link #getVotes} property. */ + private static final String VOTES__XML_ATTR = "votes"; + + @Override + public String getXmlTagName() { + return NUMBER_INFO__XML_ELEMENT; + } + + @Override + public final void writeContent(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + writeAttributes(out); + writeElements(out); + } + + /** Serializes all fields that are written as XML attributes. */ + protected void writeAttributes(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + out.writeAttribute(PHONE__XML_ATTR, getPhone()); + out.writeAttribute(RATING__XML_ATTR, getRating()); + out.writeAttribute(VOTES__XML_ATTR, Integer.toString(getVotes())); + } + + /** Serializes all fields that are written as XML elements. */ + protected void writeElements(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + // No element fields. + } + + /** Creates a new {@link de.haumacher.phoneblock.app.api.model.NumberInfo} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static NumberInfo readNumberInfo_XmlContent(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + NumberInfo result = new NumberInfo(); + result.readContentXml(in); + return result; + } + + /** Reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + protected final void readContentXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + for (int n = 0, cnt = in.getAttributeCount(); n < cnt; n++) { + String name = in.getAttributeLocalName(n); + String value = in.getAttributeValue(n); + + readFieldXmlAttribute(name, value); + } + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + assert event == javax.xml.stream.XMLStreamConstants.START_ELEMENT; + + String localName = in.getLocalName(); + readFieldXmlElement(in, localName); + } + } + + /** Parses the given attribute value and assigns it to the field with the given name. */ + protected void readFieldXmlAttribute(String name, String value) { + switch (name) { + case PHONE__XML_ATTR: { + setPhone(value); + break; + } + case RATING__XML_ATTR: { + setRating(value); + break; + } + case VOTES__XML_ATTR: { + setVotes(Integer.parseInt(value)); + break; + } + default: { + // Skip unknown attribute. + } + } + } + + /** Reads the element under the cursor and assigns its contents to the field with the given name. */ + protected void readFieldXmlElement(javax.xml.stream.XMLStreamReader in, String localName) throws javax.xml.stream.XMLStreamException { + switch (localName) { + case PHONE__XML_ATTR: { + setPhone(in.getElementText()); + break; + } + case RATING__XML_ATTR: { + setRating(in.getElementText()); + break; + } + case VOTES__XML_ATTR: { + setVotes(Integer.parseInt(in.getElementText())); + break; + } + default: { + internalSkipUntilMatchingEndElement(in); + } + } + } + + protected static final void internalSkipUntilMatchingEndElement(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + int level = 0; + while (true) { + switch (in.next()) { + case javax.xml.stream.XMLStreamConstants.START_ELEMENT: level++; break; + case javax.xml.stream.XMLStreamConstants.END_ELEMENT: if (level == 0) { return; } else { level--; break; } + } + } + } + + /** Creates a new {@link NumberInfo} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static NumberInfo readNumberInfo(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + in.nextTag(); + return de.haumacher.phoneblock.app.api.model.NumberInfo.readNumberInfo_XmlContent(in); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RateRequest.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RateRequest.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RateRequest.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RateRequest.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationChallenge.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationChallenge.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationChallenge.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationChallenge.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationCompletion.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationCompletion.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationCompletion.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationCompletion.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationRequest.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationRequest.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationRequest.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationRequest.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationResult.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationResult.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationResult.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/RegistrationResult.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/SessionInfo.java b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/SessionInfo.java similarity index 100% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/SessionInfo.java rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/SessionInfo.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto similarity index 89% rename from phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto rename to phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto index 77c0bde3..43451130 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto +++ b/phoneblock-shared/src/main/java/de/haumacher/phoneblock/app/api/model/api.proto @@ -91,3 +91,15 @@ message RateRequest { string comment; } + +message NumberInfo { + /** The number being rated. */ + string phone; + + /** The {@link Rating} of the {@link #phone number}. */ + string rating; + + /** How often the {@link #phone number} was rated in a {@link #rating certain way}. */ + int votes; +} + diff --git a/phoneblock-shared/src/main/java/module-info.java b/phoneblock-shared/src/main/java/module-info.java new file mode 100644 index 00000000..f4676585 --- /dev/null +++ b/phoneblock-shared/src/main/java/module-info.java @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ + +/** + * The PhoneBlock application. + */ +module phoneblock.shared { + exports de.haumacher.phoneblock.app.api.model; + + requires de.haumacher.msgbuf; +} \ No newline at end of file diff --git a/phoneblock/pom.xml b/phoneblock/pom.xml index 55e4fcce..dffbe3a6 100644 --- a/phoneblock/pom.xml +++ b/phoneblock/pom.xml @@ -60,6 +60,12 @@ + + de.haumacher + phoneblock-shared + ${project.version} + + de.haumacher.msgbuf ${msgbuf.version} @@ -459,5 +465,40 @@ + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + de.haumacher.msgbuf + + + msgbuf-generator-maven-plugin + + + [1.1.2,) + + + generate + + + + + + + + + + + + \ No newline at end of file diff --git a/phoneblock/src/main/java/module-info.java b/phoneblock/src/main/java/module-info.java index 9d62e57c..f73ac960 100644 --- a/phoneblock/src/main/java/module-info.java +++ b/phoneblock/src/main/java/module-info.java @@ -24,6 +24,7 @@ exports de.haumacher.phoneblock.db to org.mybatis; exports de.haumacher.phoneblock.db.model to org.mybatis; + requires phoneblock.shared; requires com.google.api.client; requires com.google.auth; requires com.google.auth.oauth2; diff --git a/pom.xml b/pom.xml index 79c3afed..ef10edec 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ phoneblock-ab + phoneblock-shared From c3d8df0bfa2322994e345ac818d9dec0109f5d53 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 6 Nov 2023 20:30:19 +0100 Subject: [PATCH 011/153] Added user agent header for PhoneBlock lookup. --- .../java/org/mjsip/ua/answerbot/AnswerBot.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java index 254935e9..85b0e801 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; @@ -68,6 +69,22 @@ public class AnswerBot extends MultipleUAS { static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnswerBot.class); + private static final String VERSION; + + static { + String version; + try (InputStream in = AnswerBot.class.getResourceAsStream("/META-INF/maven/de.haumacher/phoneblock-ab/pom.properties")) { + Properties properties = new Properties(); + properties.load(in); + version = properties.getProperty("version"); + } catch (IOException ex) { + LOG.error("Faild to read version.", ex); + version = null; + } + + VERSION = version == null ? "unknown" : version; + } + private final MediaConfig _mediaConfig; private final Map> _audioFragments; @@ -119,6 +136,7 @@ protected UserAgentListener createCallHandler(SipMessage msg) { URL url = new URL("https://phoneblock.haumacher.de/phoneblock/api/num/" + from + "?format=json"); URLConnection connection = url.openConnection(); connection.addRequestProperty("accept", "application/json"); + connection.addRequestProperty("User-Agent", "PhoneBlock-AB/" + VERSION); try (InputStream in = connection.getInputStream()) { info = NumberInfo.readNumberInfo(new JsonReader(new ReaderAdapter(new InputStreamReader(in)))); } From 5f4248cb57f0ad9b48e22029085ae02600db6141 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 6 Nov 2023 20:35:17 +0100 Subject: [PATCH 012/153] Moved PhoneBlock answerbot to canonical package. Added version information to startup. --- phoneblock-ab/bin/AnswerBot (Fritz!Box).launch | 4 ++-- phoneblock-ab/pom.xml | 2 +- .../ua => de/haumacher/phoneblock}/answerbot/AnswerBot.java | 5 ++--- .../haumacher/phoneblock}/answerbot/AnswerbotConfig.java | 2 +- .../haumacher/phoneblock}/answerbot/AnswerbotOptions.java | 2 +- .../haumacher/phoneblock}/answerbot/DialogOptions.java | 2 +- .../haumacher/phoneblock}/answerbot/DialogueFactory.java | 2 +- .../haumacher/phoneblock}/answerbot/SpeechDispatcher.java | 2 +- 8 files changed, 10 insertions(+), 11 deletions(-) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/AnswerBot.java (98%) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/AnswerbotConfig.java (98%) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/AnswerbotOptions.java (94%) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/DialogOptions.java (94%) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/DialogueFactory.java (99%) rename phoneblock-ab/src/main/java/{org/mjsip/ua => de/haumacher/phoneblock}/answerbot/SpeechDispatcher.java (99%) diff --git a/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch index d7372b16..191d1e7f 100644 --- a/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch +++ b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch @@ -3,7 +3,7 @@ - + @@ -12,7 +12,7 @@ - + diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml index 90ac2b92..17a9357b 100644 --- a/phoneblock-ab/pom.xml +++ b/phoneblock-ab/pom.xml @@ -68,7 +68,7 @@ - org.mjsip.ua.answerbot.AnswerBot + de.haumacher.phoneblock.answerbot.AnswerBot diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java similarity index 98% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 85b0e801..9d5ccd2d 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -18,7 +18,7 @@ * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; import java.io.File; import java.io.IOException; @@ -43,7 +43,6 @@ import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; -import org.mjsip.sip.provider.SipStack; import org.mjsip.time.Scheduler; import org.mjsip.time.SchedulerConfig; import org.mjsip.ua.MediaAgent; @@ -206,7 +205,7 @@ public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress calle */ public static void main(String[] args) { String program = AnswerBot.class.getSimpleName(); - LOG.info(program + " " + SipStack.version); + LOG.info(program + " " + VERSION); SipConfig sipConfig = new SipConfig(); UAConfig uaConfig = new UAConfig(); diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java similarity index 98% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index cdb2c7b7..cf7ee613 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; import java.io.File; diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java similarity index 94% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java index d8a1ebb0..c2a60483 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/AnswerbotOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; import java.io.File; diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogOptions.java similarity index 94% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogOptions.java index 190e1ea1..d911992f 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogOptions.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; /** * Options for controlling the bot conversation. diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java similarity index 99% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index 49249058..cdffe026 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; import java.io.File; import java.io.IOError; diff --git a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java similarity index 99% rename from phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java rename to phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java index a829ea90..baa3e200 100644 --- a/phoneblock-ab/src/main/java/org/mjsip/ua/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. */ -package org.mjsip.ua.answerbot; +package de.haumacher.phoneblock.answerbot; import java.io.File; import java.io.IOError; From 43206f17a3ffda2d29c70afd2b9ac342a48a1863 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 6 Nov 2023 20:36:29 +0100 Subject: [PATCH 013/153] Create README.md for anwserbot --- phoneblock-ab/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 phoneblock-ab/README.md diff --git a/phoneblock-ab/README.md b/phoneblock-ab/README.md new file mode 100644 index 00000000..90f0ce39 --- /dev/null +++ b/phoneblock-ab/README.md @@ -0,0 +1,16 @@ +# phoneblock-ab - Der PhoneBlock-Anrufbeantworter + +Das Blocklist-Telefonbuch wird immer größer. Mittlerweile hat es die 10.000 Nummmenmarke geknackt. Ein Telefonbuch in der +Fritz!Box kann aber je nach Modell nur höchstens 3000 bis 4000 Nummern fassen. Die Lösung ist der **PhoneBlock-Anrufbeantworter**. + +Den PhoneBlock-Anrufbeantworter meldest du an Deiner Fritz!Box (oder einem beliebigen anderen VOIP-fähigen Internetrouter) +als Telefoniegerät "Telefon mit oder ohne Anrufbeantworter" an. Bei einem Anruf checkt der PhoneBlock-AB blitzschnell, ob die +anrufende Nummer auf der Blocklist steht und geht dann ran. Der Vorteil ist, dass die **Blocklist immer 100% aktuell** ist, da es +keinen nächtlichen Update-Lauf mehr gibt. + +Das Witzige ist, dass der PhoneBlock-Anrufbeantworter deine unerwünschten Anrufer in ein richtiges Gespräch verwickeln und sie so +eine kleine Weile davon abhalten kann, andere zu nerven. Wie das geht? Wenn PhoneBlock einen Anruf annimmt, wird zuerst eine +Begrüßungsmeldung abgespielt. Danach wartet PhoneBlock, bis das Gegenüber etwas sagt und stellt danach vollkommen sinnlose +Rückfragen. Eine professionelle Telfonmarketingfachkraft kann sich so minutenlang mit PhoneBlock unterhalten. + + From 438498b24ab74c62120a93ae76efd3f86fc519dc Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Tue, 7 Nov 2023 22:17:38 +0100 Subject: [PATCH 014/153] Preparation to register answer bot to multiple registrars. --- .../phoneblock/answerbot/AnswerBot.java | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 9d5ccd2d..f63e7251 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -39,7 +39,7 @@ import org.mjsip.pool.PortConfig; import org.mjsip.pool.PortPool; import org.mjsip.sip.address.NameAddress; -import org.mjsip.sip.call.RegistrationOptions; +import org.mjsip.sip.call.ExtendedCall; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; @@ -48,11 +48,14 @@ import org.mjsip.ua.MediaAgent; import org.mjsip.ua.MediaConfig; import org.mjsip.ua.MultipleUAS; +import org.mjsip.ua.StaticOptions; import org.mjsip.ua.UAConfig; -import org.mjsip.ua.UAOptions; import org.mjsip.ua.UserAgent; import org.mjsip.ua.UserAgentListener; import org.mjsip.ua.UserAgentListenerAdapter; +import org.mjsip.ua.UserOptions; +import org.mjsip.ua.registration.RegistrationClient; +import org.mjsip.ua.registration.RegistrationLogger; import org.mjsip.ua.streamer.StreamerFactory; import org.slf4j.LoggerFactory; @@ -88,15 +91,21 @@ public class AnswerBot extends MultipleUAS { private final Map> _audioFragments; - private AnswerbotOptions _config; + private AnswerbotOptions _botConfig; + + private UserOptions _userConfig; /** * Creates an {@link AnswerBot}. */ - public AnswerBot(SipProvider sip_provider, RegistrationOptions regOptions, - UAOptions uaConfig, MediaConfig mediaConfig, PortPool portPool, AnswerbotOptions botOptions) { - super(sip_provider,portPool, regOptions, uaConfig, botOptions); - + public AnswerBot(SipProvider sip_provider, StaticOptions config, UserOptions userConfig, + MediaConfig mediaConfig, PortPool portPool, AnswerbotOptions botOptions) { + super(sip_provider, portPool, config); + _userConfig = userConfig; + + _mediaConfig = mediaConfig; + _botConfig = botOptions; + Map> audioFragments = new HashMap<>(); File conversationDir = botOptions.conversationDir(); for (File type : conversationDir.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { @@ -110,18 +119,26 @@ public AnswerBot(SipProvider sip_provider, RegistrationOptions regOptions, } } _audioFragments = audioFragments; - _mediaConfig = mediaConfig; - _config = botOptions; } @Override - protected UserAgentListener createCallHandler(SipMessage msg) { + protected void onInviteReceived(SipMessage msg) { String from = msg.getFromUser(); LOG.info("Incomming call from: " + from); + // TODO: Lookup depending on caller. + UserOptions user = _userConfig; + + final UserAgent ua = new UserAgent(sip_provider, _portPool, _config.forUser(user), createCallHandler(from)); + + // since there is still no proper method to init the UA with an incoming call, trick it by using the onNewIncomingCall() callback method + new ExtendedCall(sip_provider,msg,ua); + } + + protected UserAgentListener createCallHandler(String from) { if (from == null) { // An anonymous call, accept. - if (_config.getAcceptAnonymous()) { + if (_botConfig.getAcceptAnonymous()) { return spamHandler(); } else { return rejectHandler(); @@ -144,7 +161,7 @@ protected UserAgentListener createCallHandler(SipMessage msg) { return rejectHandler(); } - if (info.getVotes() < _config.getMinVotes()) { + if (info.getVotes() < _botConfig.getMinVotes()) { // Not considered SPAM. LOG.info("Not spam: " + from + " (" + info.getVotes() + " votes)"); return rejectHandler(); @@ -162,14 +179,14 @@ private UserAgentListener spamHandler() { @Override public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress caller, MediaDesc[] media_descs) { String recordingFile; - String recordingDir = _config.recordingDir(); + String recordingDir = _botConfig.recordingDir(); if (recordingDir != null) { String callId = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS").format(new Date()) + " " + name(caller); recordingFile = recordingDir + "/" + callId + ".wav"; } else { recordingFile = null; } - StreamerFactory streamerFactory = new DialogueFactory(_config, _audioFragments, recordingFile); + StreamerFactory streamerFactory = new DialogueFactory(_botConfig, _audioFragments, recordingFile); ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); } @@ -222,6 +239,9 @@ public static void main(String[] args) { SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); new AnswerBot(sipProvider, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), botConfig); - } + + RegistrationClient rc = new RegistrationClient(sipProvider, uaConfig, new RegistrationLogger()); + rc.loopRegister(uaConfig); + } } From 507056842577815922ab12748383ce36523eb1d6 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Tue, 7 Nov 2023 22:48:33 +0100 Subject: [PATCH 015/153] Extracted all options specific for a single customer to CustomerOptions. --- .../configs/phoneblock-answerbot.template | 9 ++-- .../phoneblock/answerbot/AnswerBot.java | 26 +++------ .../phoneblock/answerbot/AnswerbotConfig.java | 31 ++++++++++- .../answerbot/AnswerbotOptions.java | 10 +++- .../phoneblock/answerbot/CustomerConfig.java | 53 +++++++++++++++++++ .../phoneblock/answerbot/CustomerOptions.java | 36 +++++++++++++ 6 files changed, 138 insertions(+), 27 deletions(-) create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index 7a4d99fc..79fdffa8 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -9,18 +9,17 @@ ################################################################################################### registrar=fritz.box -auth_realm=fritz.box +realm=fritz.box # The user name for registering the IP phone device configured in your Fritz!Box user=phoneblock -auth_user=phoneblock # The password for registering the IP phone device configured in your Fritz!Box -auth_passwd= +passwd= # The local ports used by the answerbot -host_port=55060 -media_port=50061 +host-port=55060 +media-port=50061 port-count=20 # Enable SIP message debugging. diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index f63e7251..9d0461e6 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -46,10 +46,7 @@ import org.mjsip.time.Scheduler; import org.mjsip.time.SchedulerConfig; import org.mjsip.ua.MediaAgent; -import org.mjsip.ua.MediaConfig; import org.mjsip.ua.MultipleUAS; -import org.mjsip.ua.StaticOptions; -import org.mjsip.ua.UAConfig; import org.mjsip.ua.UserAgent; import org.mjsip.ua.UserAgentListener; import org.mjsip.ua.UserAgentListenerAdapter; @@ -87,8 +84,6 @@ public class AnswerBot extends MultipleUAS { VERSION = version == null ? "unknown" : version; } - private final MediaConfig _mediaConfig; - private final Map> _audioFragments; private AnswerbotOptions _botConfig; @@ -98,12 +93,9 @@ public class AnswerBot extends MultipleUAS { /** * Creates an {@link AnswerBot}. */ - public AnswerBot(SipProvider sip_provider, StaticOptions config, UserOptions userConfig, - MediaConfig mediaConfig, PortPool portPool, AnswerbotOptions botOptions) { - super(sip_provider, portPool, config); + public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, UserOptions userConfig, PortPool portPool) { + super(sip_provider, portPool, botOptions); _userConfig = userConfig; - - _mediaConfig = mediaConfig; _botConfig = botOptions; Map> audioFragments = new HashMap<>(); @@ -188,7 +180,7 @@ public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress calle } StreamerFactory streamerFactory = new DialogueFactory(_botConfig, _audioFragments, recordingFile); - ua.accept(new MediaAgent(_mediaConfig.getMediaDescs(), streamerFactory)); + ua.accept(new MediaAgent(_botConfig.getMediaDescs(), streamerFactory)); } private String name(NameAddress caller) { @@ -225,23 +217,21 @@ public static void main(String[] args) { LOG.info(program + " " + VERSION); SipConfig sipConfig = new SipConfig(); - UAConfig uaConfig = new UAConfig(); + CustomerConfig userConfig = new CustomerConfig(); SchedulerConfig schedulerConfig = new SchedulerConfig(); - MediaConfig mediaConfig = new MediaConfig(); PortConfig portConfig = new PortConfig(); AnswerbotConfig botConfig = new AnswerbotConfig(); - OptionParser.parseOptions(args, ".phoneblock", sipConfig, uaConfig, schedulerConfig, mediaConfig, portConfig, botConfig); + OptionParser.parseOptions(args, ".phoneblock", sipConfig, botConfig, userConfig, schedulerConfig, portConfig); sipConfig.normalize(); - uaConfig.normalize(sipConfig); botConfig.normalize(); SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, uaConfig, uaConfig, mediaConfig, portConfig.createPool(), botConfig); + new AnswerBot(sipProvider, botConfig, userConfig, portConfig.createPool()); - RegistrationClient rc = new RegistrationClient(sipProvider, uaConfig, new RegistrationLogger()); - rc.loopRegister(uaConfig); + RegistrationClient rc = new RegistrationClient(sipProvider, userConfig, new RegistrationLogger()); + rc.loopRegister(userConfig); } } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index cf7ee613..b511ef8e 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -6,13 +6,18 @@ import java.io.File; import org.kohsuke.args4j.Option; -import org.mjsip.ua.ServiceConfig; +import org.mjsip.media.FlowSpec.Direction; +import org.mjsip.media.MediaDesc; +import org.mjsip.sip.config.MediaDescHandler; /** * Configuration options for the {@link AnswerBot}. */ -public class AnswerbotConfig extends ServiceConfig implements AnswerbotOptions { +public class AnswerbotConfig implements AnswerbotOptions { + @Option(name = "--media", handler = MediaDescHandler.class) + private MediaDesc[] _mediaDescs=new MediaDesc[]{}; + @Option(name = "--conversation", usage = "Directory with WAV files used for streaming during automatic conversation.") private File _conversationDir = new File("./conversation"); @@ -95,4 +100,26 @@ public void normalize() { } } + @Override + public MediaDesc[] getMediaDescs() { + return _mediaDescs; + } + + @Override + public String getMediaAddr() { + return null; + } + @Override + public boolean getNoOffer() { + return false; + } + @Override + public int getRefuseTime() { + return 0; + } + @Override + public Direction getDirection() { + return Direction.FULL_DUPLEX; + } + } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java index c2a60483..d68d4bbf 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java @@ -5,12 +5,13 @@ import java.io.File; -import org.mjsip.ua.ServiceOptions; +import org.mjsip.media.MediaDesc; +import org.mjsip.ua.StaticOptions; /** * Configuration options of the {@link AnswerBot}. */ -public interface AnswerbotOptions extends DialogOptions, ServiceOptions { +public interface AnswerbotOptions extends DialogOptions, StaticOptions { /** * The directory where to store recordings, null if recording is diabled. @@ -32,4 +33,9 @@ public interface AnswerbotOptions extends DialogOptions, ServiceOptions { */ int getMinVotes(); + /** + * Media format configuration. + */ + MediaDesc[] getMediaDescs(); + } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java new file mode 100644 index 00000000..12a332e1 --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +import org.kohsuke.args4j.Option; + +/** + * Command line options to specify a {@link CustomerConfig}. + */ +public class CustomerConfig implements CustomerOptions { + + @Option(name = "--registrar") + private String registrar; + + @Option(name = "--route") + private String route; + + @Option(name = "--user") + private String user; + + @Option(name = "--passwd") + private String passwd; + + @Option(name = "--realm") + private String realm; + + @Override + public String getRegistrar() { + return registrar; + } + + @Override + public String getRoute() { + return route; + } + + @Override + public String getUser() { + return user; + } + + @Override + public String getAuthPasswd() { + return passwd; + } + + @Override + public String getAuthRealm() { + return realm; + } + +} diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java new file mode 100644 index 00000000..4867fcb8 --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +import org.mjsip.sip.address.NameAddress; +import org.mjsip.sip.address.SipURI; +import org.mjsip.ua.UserOptions; +import org.mjsip.ua.registration.RegistrationOptions; + +/** + * Options identifying a PhoneBlock customer. + */ +public interface CustomerOptions extends RegistrationOptions, UserOptions { + + @Override + default String getProxy() { + return getRegistrar(); + } + + @Override + default int getExpires() { + return 60 * 60; + } + + @Override + default String getAuthUser() { + return getUser(); + } + + @Override + default NameAddress getUserURI() { + return new NameAddress(new SipURI(getUser(),getProxy())); + } + +} From 24bc0bb27b11432ad412843d559b2882ab37f828 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Tue, 7 Nov 2023 22:56:34 +0100 Subject: [PATCH 016/153] Made customer config dynamic. --- .../phoneblock/answerbot/AnswerBot.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 9d0461e6..abe141b8 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -33,12 +33,14 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.function.Function; import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; import org.mjsip.pool.PortConfig; import org.mjsip.pool.PortPool; import org.mjsip.sip.address.NameAddress; +import org.mjsip.sip.address.SipURI; import org.mjsip.sip.call.ExtendedCall; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; @@ -88,14 +90,14 @@ public class AnswerBot extends MultipleUAS { private AnswerbotOptions _botConfig; - private UserOptions _userConfig; + private Function _configForUser; /** * Creates an {@link AnswerBot}. */ - public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, UserOptions userConfig, PortPool portPool) { + public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function configForUser, PortPool portPool) { super(sip_provider, portPool, botOptions); - _userConfig = userConfig; + _configForUser = configForUser; _botConfig = botOptions; Map> audioFragments = new HashMap<>(); @@ -118,8 +120,12 @@ protected void onInviteReceived(SipMessage msg) { String from = msg.getFromUser(); LOG.info("Incomming call from: " + from); - // TODO: Lookup depending on caller. - UserOptions user = _userConfig; + String userName = SipURI.parseSipURI(msg.getToHeader().getValue()).getUserName(); + UserOptions user = _configForUser.apply(userName); + if (user == null) { + // Ignore. + return; + } final UserAgent ua = new UserAgent(sip_provider, _portPool, _config.forUser(user), createCallHandler(from)); @@ -228,7 +234,7 @@ public static void main(String[] args) { botConfig.normalize(); SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); - new AnswerBot(sipProvider, botConfig, userConfig, portConfig.createPool()); + new AnswerBot(sipProvider, botConfig, (id) -> userConfig, portConfig.createPool()); RegistrationClient rc = new RegistrationClient(sipProvider, userConfig, new RegistrationLogger()); rc.loopRegister(userConfig); From c6b06d7d958f58c669f64e9aaf8715791edb6fe0 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 10 Nov 2023 18:12:14 +0100 Subject: [PATCH 017/153] Bugfix: Parsing user from SIP header must use SipParser. --- .../main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index abe141b8..d1c713f9 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -44,6 +44,7 @@ import org.mjsip.sip.call.ExtendedCall; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; +import org.mjsip.sip.provider.SipParser; import org.mjsip.sip.provider.SipProvider; import org.mjsip.time.Scheduler; import org.mjsip.time.SchedulerConfig; @@ -120,7 +121,7 @@ protected void onInviteReceived(SipMessage msg) { String from = msg.getFromUser(); LOG.info("Incomming call from: " + from); - String userName = SipURI.parseSipURI(msg.getToHeader().getValue()).getUserName(); + String userName = SipURI.parseSipURI(new SipParser(msg.getToHeader().getValue()).getURISource()).getUserName(); UserOptions user = _configForUser.apply(userName); if (user == null) { // Ignore. From 735d5cc900794c5e9a5e5b91a04818f2b2e446e9 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 10 Nov 2023 18:12:48 +0100 Subject: [PATCH 018/153] Bugfix: Must use a negative refuse time to prevent immediate refuse. --- .../java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index b511ef8e..0d88f5f0 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -115,7 +115,7 @@ public boolean getNoOffer() { } @Override public int getRefuseTime() { - return 0; + return -1; } @Override public Direction getDirection() { From e39b9082f182c9aa425530affe1ef4f83562ff86 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 10 Nov 2023 18:13:12 +0100 Subject: [PATCH 019/153] Ignore test launchers. --- phoneblock-ab/bin/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 phoneblock-ab/bin/.gitignore diff --git a/phoneblock-ab/bin/.gitignore b/phoneblock-ab/bin/.gitignore new file mode 100644 index 00000000..efb280e5 --- /dev/null +++ b/phoneblock-ab/bin/.gitignore @@ -0,0 +1 @@ +/Test *.launch From d5d14deaec8bfee8b6ec90337d663dc24c96f117 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 10 Nov 2023 21:40:23 +0100 Subject: [PATCH 020/153] Enhanced checks of incoming call. --- .../phoneblock/answerbot/AnswerBot.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index d1c713f9..5ff7d5ca 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -42,6 +42,7 @@ import org.mjsip.sip.address.NameAddress; import org.mjsip.sip.address.SipURI; import org.mjsip.sip.call.ExtendedCall; +import org.mjsip.sip.header.ToHeader; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipParser; @@ -121,10 +122,24 @@ protected void onInviteReceived(SipMessage msg) { String from = msg.getFromUser(); LOG.info("Incomming call from: " + from); - String userName = SipURI.parseSipURI(new SipParser(msg.getToHeader().getValue()).getURISource()).getUserName(); + ToHeader toHeader = msg.getToHeader(); + if (toHeader == null) { + LOG.warn("Missing To: header, ignoring."); + return; + } + String toHeaderValue = toHeader.getValue(); + if (toHeaderValue == null || toHeaderValue.isBlank()) { + LOG.warn("Empty To: header, ignoring."); + return; + } + String userName = SipURI.parseSipURI(new SipParser(toHeaderValue).getURISource()).getUserName(); + if (userName == null) { + LOG.warn("No user name in To: header, ignoring."); + return; + } UserOptions user = _configForUser.apply(userName); if (user == null) { - // Ignore. + LOG.warn("No configuration for user '" + userName + "', ignoring."); return; } From 2e3fbd2006520b3bdffd97b13fa3a0dbfd072547 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 10 Nov 2023 22:22:18 +0100 Subject: [PATCH 021/153] Extracted SecureRandomService. --- .../haumacher/phoneblock/app/Application.java | 5 ++- .../phoneblock/app/api/RegisterServlet.java | 7 +--- .../java/de/haumacher/phoneblock/db/DB.java | 7 ++-- .../de/haumacher/phoneblock/db/DBService.java | 10 +++-- .../random/SecureRandomService.java | 42 +++++++++++++++++++ 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java index 04bcce3b..a8ce0a3f 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java @@ -32,6 +32,7 @@ import de.haumacher.phoneblock.jmx.ManagementService; import de.haumacher.phoneblock.mail.MailServiceStarter; import de.haumacher.phoneblock.meta.MetaSearchService; +import de.haumacher.phoneblock.random.SecureRandomService; import de.haumacher.phoneblock.scheduler.SchedulerService; /** @@ -83,13 +84,15 @@ public void contextInitialized(ServletContextEvent sce) { MetaSearchService metaSearch; MailServiceStarter mail; ChatGPTService gpt; + SecureRandomService rnd; _services = new ServletContextListener[] { + rnd = new SecureRandomService(), scheduler = new SchedulerService(), indexer = IndexUpdateService.async(scheduler, IndexUpdateService.tee( new IndexNowUpdateService(), new GoogleUpdateService())), mail = new MailServiceStarter(), - db = new DBService(indexer, scheduler, mail), + db = new DBService(rnd, indexer, scheduler, mail), fetcher = new FetchService(), metaSearch = new MetaSearchService(scheduler, fetcher, indexer), new CrawlerService(fetcher, metaSearch), diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/RegisterServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/RegisterServlet.java index 63c25e76..f2fb5bc6 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/RegisterServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/RegisterServlet.java @@ -4,9 +4,7 @@ package de.haumacher.phoneblock.app.api; import java.io.IOException; -import java.security.SecureRandom; import java.util.Base64; -import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; @@ -28,6 +26,7 @@ import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.mail.MailService; import de.haumacher.phoneblock.mail.MailServiceStarter; +import de.haumacher.phoneblock.random.SecureRandomService; import de.haumacher.phoneblock.scheduler.SchedulerService; import de.haumacher.phoneblock.util.ServletUtil; @@ -37,14 +36,12 @@ @WebServlet(urlPatterns = "/api/register") public class RegisterServlet extends HttpServlet { - private final Random _rnd = new SecureRandom(); - private static final ConcurrentMap _sessions = new ConcurrentHashMap<>(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String session = DBService.getInstance().createPassword(20); - Captcha captcha = new Captcha(_rnd); + Captcha captcha = new Captcha(SecureRandomService.getInstance().getRnd()); String captchaImage = "data:image/png;base64," + Base64.getEncoder().encodeToString(captcha.getPng()); _sessions.put(session, SessionInfo.create().setCreated(System.currentTimeMillis()).setSession(session).setAnswer(captcha.getText())); SchedulerService.getInstance().executor().schedule(() -> _sessions.remove(session), 15, TimeUnit.MINUTES); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java index 03da993d..3a84ef83 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java @@ -102,7 +102,7 @@ public class DB { private MessageDigest _sha256; - private SecureRandom _rnd = new SecureRandom(); + private final SecureRandom _rnd; private SchedulerService _scheduler; @@ -115,7 +115,7 @@ public class DB { private boolean _sendHelpMails; public DB(DataSource dataSource, SchedulerService scheduler) throws SQLException, UnsupportedEncodingException { - this(false, dataSource, IndexUpdateService.NONE, scheduler, null); + this(new SecureRandom(), false, dataSource, IndexUpdateService.NONE, scheduler, null); } /** @@ -124,7 +124,8 @@ public DB(DataSource dataSource, SchedulerService scheduler) throws SQLException * * @param dataSource */ - public DB(boolean sendHelpMails, DataSource dataSource, IndexUpdateService indexer, SchedulerService scheduler, MailService mailService) throws SQLException, UnsupportedEncodingException { + public DB(SecureRandom rnd, boolean sendHelpMails, DataSource dataSource, IndexUpdateService indexer, SchedulerService scheduler, MailService mailService) throws SQLException, UnsupportedEncodingException { + _rnd = rnd; _sendHelpMails = sendHelpMails; _dataSource = dataSource; _indexer = indexer; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBService.java index c711e849..4c7c519c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBService.java @@ -22,6 +22,7 @@ import de.haumacher.phoneblock.db.config.DBConfig; import de.haumacher.phoneblock.index.IndexUpdateService; +import de.haumacher.phoneblock.random.SecureRandomService; import de.haumacher.phoneblock.mail.MailServiceStarter; import de.haumacher.phoneblock.scheduler.SchedulerService; @@ -43,15 +44,18 @@ public class DBService implements ServletContextListener { private JdbcConnectionPool _pool; private MailServiceStarter _mail; + + private SecureRandomService _rnd; public DBService(SchedulerService scheduler) { - this(IndexUpdateService.NONE, scheduler, null); + this(new SecureRandomService(), IndexUpdateService.NONE, scheduler, null); } /** * Creates a {@link DBService}. */ - public DBService(IndexUpdateService indexer, SchedulerService scheduler, MailServiceStarter mail) { + public DBService(SecureRandomService rnd, IndexUpdateService indexer, SchedulerService scheduler, MailServiceStarter mail) { + _rnd = rnd; _indexer = indexer; _scheduler = scheduler; _mail = mail; @@ -116,7 +120,7 @@ private void startDB(DBConfig config) throws SQLException, UnsupportedEncodingEx } _pool = JdbcConnectionPool.create(dataSource); - INSTANCE = new DB(config.isSendHelpMails(), _pool, _indexer, _scheduler, _mail == null ? null : _mail.getMailService()); + INSTANCE = new DB(_rnd.getRnd(), config.isSendHelpMails(), _pool, _indexer, _scheduler, _mail == null ? null : _mail.getMailService()); } protected int defaultDbPort() { diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java new file mode 100644 index 00000000..631c3cc8 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.random; + +import java.security.SecureRandom; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * Service for generating secure random numbers. + */ +public class SecureRandomService implements ServletContextListener { + + private SecureRandom _rnd = new SecureRandom(); + private static SecureRandomService INSTANCE; + + /** + * The secure random number generator. + */ + public SecureRandom getRnd() { + return _rnd; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + INSTANCE = this; + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + INSTANCE = null; + } + + /** + * The {@link SecureRandomService} singleton. + */ + public static SecureRandomService getInstance() { + return INSTANCE; + } +} From c274967d44a1bf7bcccc9b8499d07d46ca1102f6 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:08:17 +0100 Subject: [PATCH 022/153] Moved TestDomUtil to canonical package. --- .../java/de/haumacher/phoneblock/{ => util}/TestDomUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename phoneblock/src/test/java/de/haumacher/phoneblock/{ => util}/TestDomUtil.java (97%) diff --git a/phoneblock/src/test/java/de/haumacher/phoneblock/TestDomUtil.java b/phoneblock/src/test/java/de/haumacher/phoneblock/util/TestDomUtil.java similarity index 97% rename from phoneblock/src/test/java/de/haumacher/phoneblock/TestDomUtil.java rename to phoneblock/src/test/java/de/haumacher/phoneblock/util/TestDomUtil.java index f3f3cdc9..9b7a887e 100644 --- a/phoneblock/src/test/java/de/haumacher/phoneblock/TestDomUtil.java +++ b/phoneblock/src/test/java/de/haumacher/phoneblock/util/TestDomUtil.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. */ -package de.haumacher.phoneblock; +package de.haumacher.phoneblock.util; import static de.haumacher.phoneblock.util.DomUtil.*; import static org.junit.jupiter.api.Assertions.*; From 87771154b08bca23bef8ed09ecdb6da0de862fe1 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:11:23 +0100 Subject: [PATCH 023/153] Do not unconditionally reset services during shutdown. --- .../phoneblock/random/SecureRandomService.java | 10 ++++++---- .../phoneblock/scheduler/SchedulerService.java | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java index 631c3cc8..0101d46a 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/random/SecureRandomService.java @@ -14,7 +14,7 @@ public class SecureRandomService implements ServletContextListener { private SecureRandom _rnd = new SecureRandom(); - private static SecureRandomService INSTANCE; + private static SecureRandomService _instance; /** * The secure random number generator. @@ -25,18 +25,20 @@ public SecureRandom getRnd() { @Override public void contextInitialized(ServletContextEvent sce) { - INSTANCE = this; + _instance = this; } @Override public void contextDestroyed(ServletContextEvent sce) { - INSTANCE = null; + if (_instance == this) { + _instance = null; + } } /** * The {@link SecureRandomService} singleton. */ public static SecureRandomService getInstance() { - return INSTANCE; + return _instance; } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/scheduler/SchedulerService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/scheduler/SchedulerService.java index 4bdcadec..7ec96646 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/scheduler/SchedulerService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/scheduler/SchedulerService.java @@ -56,6 +56,10 @@ public void contextDestroyed(ServletContextEvent sce) { } LOG.info("Scheduler stopped."); + + if (_instance == this) { + _instance = null; + } } /** From 36175ff43f1da6c16b6d4ab69e63be5caa0d268c Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:11:59 +0100 Subject: [PATCH 024/153] Added setter methods. --- .../phoneblock/answerbot/CustomerConfig.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java index 12a332e1..9b9d5b20 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java @@ -29,25 +29,45 @@ public class CustomerConfig implements CustomerOptions { public String getRegistrar() { return registrar; } + + public void setRegistrar(String registrar) { + this.registrar = registrar; + } @Override public String getRoute() { return route; } + + public void setRoute(String route) { + this.route = route; + } @Override public String getUser() { return user; } + + public void setUser(String user) { + this.user = user; + } @Override public String getAuthPasswd() { return passwd; } + + public void setPasswd(String passwd) { + this.passwd = passwd; + } @Override public String getAuthRealm() { return realm; } + + public void setRealm(String realm) { + this.realm = realm; + } } From f28acb5be47a424717238fe09d7b83d8000be254 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:12:28 +0100 Subject: [PATCH 025/153] Applied migration Scheduler is now an interface. --- .../java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 5ff7d5ca..47faf4fe 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -47,7 +47,7 @@ import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipParser; import org.mjsip.sip.provider.SipProvider; -import org.mjsip.time.Scheduler; +import org.mjsip.time.ConfiguredScheduler; import org.mjsip.time.SchedulerConfig; import org.mjsip.ua.MediaAgent; import org.mjsip.ua.MultipleUAS; @@ -249,7 +249,7 @@ public static void main(String[] args) { sipConfig.normalize(); botConfig.normalize(); - SipProvider sipProvider = new SipProvider(sipConfig, new Scheduler(schedulerConfig)); + SipProvider sipProvider = new SipProvider(sipConfig, new ConfiguredScheduler(schedulerConfig)); new AnswerBot(sipProvider, botConfig, (id) -> userConfig, portConfig.createPool()); RegistrationClient rc = new RegistrationClient(sipProvider, userConfig, new RegistrationLogger()); From 3808bc0768ff4ac36cb1f08298e3985b7cd0f94e Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:13:09 +0100 Subject: [PATCH 026/153] Starting point for SipService. --- phoneblock/pom.xml | 6 ++ .../phoneblock/ab/CreateABServlet.java | 31 +++++++ .../haumacher/phoneblock/ab/SipService.java | 91 +++++++++++++++++++ .../haumacher/phoneblock/app/Application.java | 4 +- phoneblock/src/main/java/module-info.java | 5 + 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java diff --git a/phoneblock/pom.xml b/phoneblock/pom.xml index dffbe3a6..dde1f1ed 100644 --- a/phoneblock/pom.xml +++ b/phoneblock/pom.xml @@ -66,6 +66,12 @@ ${project.version} + + de.haumacher + phoneblock-ab + ${project.version} + + de.haumacher.msgbuf ${msgbuf.version} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java new file mode 100644 index 00000000..dddcd399 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.ab; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Servlet creating an answerbot. + */ +@WebServlet(urlPatterns = CreateABServlet.PATH) +public class CreateABServlet extends HttpServlet { + + public static final String PATH = "/ab/create"; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String username = (String) req.getSession().getAttribute("ab-username"); + String passwd = (String) req.getSession().getAttribute("ab-passwd"); + String hostname = req.getParameter("hostname"); + + SipService.getInstance().register(username, passwd, hostname); + + } +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java new file mode 100644 index 00000000..359d5bb1 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.ab; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.mjsip.pool.PortPool; +import org.mjsip.sip.provider.SipConfig; +import org.mjsip.sip.provider.SipProvider; +import org.mjsip.time.Scheduler; +import org.mjsip.ua.registration.RegistrationClient; + +import de.haumacher.phoneblock.answerbot.AnswerBot; +import de.haumacher.phoneblock.answerbot.AnswerbotConfig; +import de.haumacher.phoneblock.answerbot.CustomerConfig; +import de.haumacher.phoneblock.scheduler.SchedulerService; + +/** + * Service managing a SIP stack. + */ +public class SipService implements ServletContextListener { + + private SchedulerService _scheduler; + private SipProvider _sipProvider; + private PortPool _portPool; + private AnswerBot _answerBot; + + private static SipService _instance; + + /** + * Creates a {@link SipService}. + */ + public SipService(SchedulerService scheduler) { + _scheduler = scheduler; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + SipConfig sipConfig = new SipConfig(); + sipConfig.setHostPort(50060); + sipConfig.setViaAddr("phoneblock.haumacher.de"); + _sipProvider = new SipProvider(sipConfig, Scheduler.of(_scheduler.getInstance().executor())); + + _portPool = new PortPool(50061, 50080); + + AnswerbotConfig botOptions = new AnswerbotConfig(); + _answerBot = new AnswerBot(_sipProvider, botOptions, configForUser, _portPool); + + _instance = this; + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + _answerBot = null; + _portPool = null; + + _sipProvider.halt(); + _sipProvider = null; + + if (_instance == this) { + _instance = null; + } + } + + /** + * The {@link SipService} singleton. + */ + public static SipService getInstance() { + return _instance; + } + + /** + * TODO + * + * @param username + * @param passwd + * @param hostname + */ + public void register(String username, String passwd, String hostname) { + CustomerConfig regConfig = new CustomerConfig(); + regConfig.setUser(username); + regConfig.setUser(passwd); + regConfig.setRealm("fritz.box"); + regConfig.setRegistrar(hostname); + RegistrationClient client = new RegistrationClient(_sipProvider, regConfig, null); + client.loopRegister(regConfig); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java index a8ce0a3f..919e129c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java @@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory; import org.tinylog.configuration.Configuration; +import de.haumacher.phoneblock.ab.SipService; import de.haumacher.phoneblock.carddav.resource.AddressBookCache; import de.haumacher.phoneblock.chatgpt.ChatGPTService; import de.haumacher.phoneblock.crawl.CrawlerService; @@ -98,7 +99,8 @@ public void contextInitialized(ServletContextEvent sce) { new CrawlerService(fetcher, metaSearch), gpt = new ChatGPTService(db, scheduler, indexer), new ManagementService(indexer, db, gpt), - new AddressBookCache() + new AddressBookCache(), + new SipService(scheduler) }; for (int n = 0, cnt = _services.length; n < cnt; n++) { diff --git a/phoneblock/src/main/java/module-info.java b/phoneblock/src/main/java/module-info.java index f73ac960..8d945512 100644 --- a/phoneblock/src/main/java/module-info.java +++ b/phoneblock/src/main/java/module-info.java @@ -25,12 +25,17 @@ exports de.haumacher.phoneblock.db.model to org.mybatis; requires phoneblock.shared; + requires phoneblock.ab; + requires mjsip.sip; + requires mjsip.util; + requires mjsip.ua; requires com.google.api.client; requires com.google.auth; requires com.google.auth.oauth2; requires com.h2database; requires com.opencsv; requires ez.vcard; + requires java.base; requires java.desktop; requires java.mail; requires java.management; From 72b30743228cce45bc1c9e226c80f619793f40f0 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 11 Nov 2023 18:13:59 +0100 Subject: [PATCH 027/153] Starting point for answer bot setup pages. --- .../main/webapp/setup-ab/help/01-login.jsp | 63 +++++++++++ .../setup-ab/help/02-configure-dyndns.png | Bin 0 -> 150207 bytes .../setup-ab/help/03-create-new-device.png | Bin 0 -> 197293 bytes .../webapp/setup-ab/help/04-create-phone.png | Bin 0 -> 74995 bytes .../setup-ab/help/05-choose-ip-phone.png | Bin 0 -> 76736 bytes .../help/06-user-name-and-password.png | Bin 0 -> 62283 bytes .../setup-ab/help/07-choose-local-number.png | Bin 0 -> 41549 bytes .../setup-ab/help/08-numbers-to-protect.png | Bin 0 -> 62185 bytes .../webapp/setup-ab/help/09-edit-phone.png | Bin 0 -> 193498 bytes .../setup-ab/help/10-check-settings.png | Bin 0 -> 60782 bytes .../webapp/setup-ab/help/11-prevent-call.png | Bin 0 -> 97392 bytes .../main/webapp/setup-ab/help/12-confirm.png | Bin 0 -> 105372 bytes .../help/13-allow-internet-access.png | Bin 0 -> 96381 bytes .../src/main/webapp/setup-ab/help/index.jsp | 60 +++++++++++ phoneblock/src/main/webapp/setup-ab/index.jsp | 98 ++++++++++++++++++ 15 files changed, 221 insertions(+) create mode 100644 phoneblock/src/main/webapp/setup-ab/help/01-login.jsp create mode 100644 phoneblock/src/main/webapp/setup-ab/help/02-configure-dyndns.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/03-create-new-device.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/04-create-phone.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/05-choose-ip-phone.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/06-user-name-and-password.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/07-choose-local-number.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/08-numbers-to-protect.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/09-edit-phone.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/10-check-settings.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/11-prevent-call.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/12-confirm.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/13-allow-internet-access.png create mode 100644 phoneblock/src/main/webapp/setup-ab/help/index.jsp create mode 100644 phoneblock/src/main/webapp/setup-ab/index.jsp diff --git a/phoneblock/src/main/webapp/setup-ab/help/01-login.jsp b/phoneblock/src/main/webapp/setup-ab/help/01-login.jsp new file mode 100644 index 00000000..9b4562ae --- /dev/null +++ b/phoneblock/src/main/webapp/setup-ab/help/01-login.jsp @@ -0,0 +1,63 @@ + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false"%> +<%@page import="de.haumacher.phoneblock.util.JspUtil"%> + +<% + request.setAttribute("title", "PhoneBlock-Anrufbeantworter einrichten"); +%> + + + + + + + +
+
+

PhoneBlock-Anrufbeantworter einrichten

+ +

+ Der PhoneBlock-Anrufbeantworter wird als "IP-Telefon mit Anrufbeantworter" an der Fritz!Box (oder einem + anderen Internet-Router) eingerichtet. Die Einrichtung läuft in den folgenden Schritte ab: +

+ +<%-- + +--%> + +

An der Fritz!Box anmelden

+ + + +

DynDNS einrichten

+

Neues Telfoniegerät einrichten

+

Telefoniegerät als IP-Telefon mit Anrufbeantworter markieren

+

Anmeldedaten des IP-Telefons eintragen

+

Lokale Telefonnummer auswählen

+

Zu schützende Telefonnummer(n) auswählen

+

Eingaben überprüfen

+

Ausgehende Anrufe deaktivieren

+

Zugang aus dem Internet freischalten

+ + +

+ + + + + Zurück + + + + Weiter + + + + +

+
+
+ + + + \ No newline at end of file diff --git a/phoneblock/src/main/webapp/setup-ab/help/02-configure-dyndns.png b/phoneblock/src/main/webapp/setup-ab/help/02-configure-dyndns.png new file mode 100644 index 0000000000000000000000000000000000000000..0baea678ed7c936a59fcab4ae9957d95056b7529 GIT binary patch literal 150207 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
IUf_ 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 H?w9`osC5fC literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/03-create-new-device.png b/phoneblock/src/main/webapp/setup-ab/help/03-create-new-device.png new file mode 100644 index 0000000000000000000000000000000000000000..399d521d40e19a5799045569d934c9099d1896d4 GIT binary patch literal 197293 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∈Azr)M?^(UUtL|R_1P*+ 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 o2Qh6bZP#&#}e^a&jl4N;!}0+_GPwD)eqL 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 n>(3~z`MR~=L3|bp**3G%7 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 literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/05-choose-ip-phone.png b/phoneblock/src/main/webapp/setup-ab/help/05-choose-ip-phone.png new file mode 100644 index 0000000000000000000000000000000000000000..0460c5dafbf3650acf9c11596a0da60661230ce2 GIT binary patch literal 76736 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 F{{#*^!7Bg& literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/06-user-name-and-password.png b/phoneblock/src/main/webapp/setup-ab/help/06-user-name-and-password.png new file mode 100644 index 0000000000000000000000000000000000000000..9ebf1617bd2f4a17f335940dc981e58f5b1635bb GIT binary patch literal 62283 zcmd?Q^;erg*Dg$z0;Lo$-r`Pihn5BjTD*Ai;_lX#0>KNxy#<1Ma4YVlC3tWPmf$Wq z^y%}y-}l@31J0e5m8^Tsp4qd<_UwIK;qO)D@o_0}v9Pf4feHW(EUZTiSXlSYp4`8C z0~46z zJu{A0PoT_abx`Lj78DCAoO$aIp#h!Mu-&sYCl>Fo3}IxfMj9a{+NB-4jb?O-{1+

0)h;zbgEF?7kDKXJ-oK8{vN@4|cFpPRfj(u}V6UCaX(d z?Bv|FJ@_RtD8MmxX_+z8oM4yQ*v?y~2N}2>c7GT6h%9G# zPAK6**s6_AIa?Qy@ixV-dy*7 zMMm+U{@V5L)1vhqdhg%gQXJr&{r*0`(*D1fK%H4q?+IRywE12YTtC_S;R$BJ$Vw6TH%Rd6lemy@@CWGu=76>Mz2-&-gSMgi0Z5-vjB zQJtQ-vz!|#y?x6rgQI_5)Zza}djJ0N{M?*k^RtSFM%VZg%LkY;rMnz;FbVwk3*AH87fWJiuhc=e#P+Vai`FKG=DS0;*OpZ{h*l( zEjbv|2-!OY8N1z7thv%MC@jRSC5D3udB>RIz2n9UfU|NxnJegD5X#RZ3MD1%y~;3Q zMQ~D5gJi9fjs(MW2v|JHlj}dV<|u>L+wn-Wb^7U|*9~G&qzbpM*z~{8g(U{p%6coEZ^Uz!|-L_9I???&Xq&A&;RV=prn$gTC(#H-l* zGFv+lDEgTM1>BJBCLVf;vK*sjn6F@5;7hlY;EAf%^l6`(PL-ah&MSHH-`QYEoSl%M zHS6I$vl$a~aFUKZEwu#-wE`E?9+K13v&BKlfo@UIcJPPIZ^MKxfG7rf+YgbdH$T)Z z=7IBqxTgP>TTbb+q@<)$^D~}gu%=au&*jk{2Ze?3wGogPy;)u){fLbQ4ZRCCFvMm^ zsJCi2bU(C~b-QqM@xpWrlJ&v%zm+Jz-E%21-4?jmQnNah7CdNOVBC>AWbU3JhR1IR z6~%(B#KYe*lARfQ4{~?)gnz{h;}+%s5jRr*JH%h|9B7m?nbCfJlb_*qG1M~EF^m=O zGVpl?ZMqb$=(dEwFVv?mV6F_$fz*U?k;Ivo?~e5G94^skwSUMpR|!qeHpkR~Z0sZ(VBTBi$@7V9w}vPvnmwFKkl&SFp~ zkDZAUAW-m&cgI#}GWnGfARR$Jaz8N4DkPBPvT}sj1;DV#NX{ZAdOF9lax!zdg!<3gYdj3Y8jFkO_*ELrXN0Ud#CfJB=>5uUto` z&{N0M=zr*uSmp`Sm9)9yz193v{#kryQP0?>ndTjG*8p|NX~iE7_4=z!vqBWlc`Kcy z{FHQ!1KWUGojvh5jbjns0an)CKm;_84M=!-GPg1DQ;GayeXsGfZem)9%HZ374y!j? zwq#4hExR`dLj=c}x5?d##lh>5pOFn!t?3VW{MviId4!Avij>H{&mg`X^PT{EnpPo# z!i+(fGhJbFSlnY?@eC-7LVa*|luY7eJx3<2N7!g04JrQGfYlTpT1(Y_{cCWZT(OznGNwD_7OG4&Sq_78g@}<^*FK4G z>olQEAuMy&8aMy|27eXs8pzM?Q;(JpvUCJgx;&5Pv+d)7!KRNP7c=o+=;Ea`2_9t$DxU0Npip1 zLiOofXPwB>Pb}B@UxZNfgtl?RUMX)6&&bW4Cyehin1BXu zrsuFa6moY!HtOnh=US8Bc?%IDF~3_y35d*PUwn$L(YKHlU*^GQyi;M2P&4jStSDGv zRyIU!hN=#Ze#BQVqQ}GRWdY&&mEbPsxgaLU)u1Qincl*eQy+MFfXk@$^2dE*P$B66 zFz;Q9RrVN=%1s>&&&%66izk|sI8!~aV_$2xem1|FGDRk95;W`a*79w4g z`e6TfFQer=XjH_F*Gp7Mna>thTy%L5p4aAiA`% ztY~X_<0N*^Lb^E^Fb;Do$Hx<*J^un@Px*k^O5gjXM$+VN^LyvC3+-GS+9ZyV7x1k- zsIrQ^#$@y+6t5oqER=b1z+b!lz36%+fj+EIrxLBIJXgECxwezk{Kr}GN|lS_sr}Nz zFzmO+DHO;yfh~M z2o}oHq^ZACigb{bgOACNHqKw^>SkuOXde%T9W>RnSn*m?PD(@&hxf-FNbhh)wJr3P z&`JfgTvGhG+h1Y!lTc^XWFK3py(J@b=8bFxUBb&4hQ=r_7mX$nHalI{t*KcrN4Z{f z4)!9oT7XtU;0w!0MGoGT==%c1HMpWv_xfC18<)22ced>fh8Am`(iLmQ_2gp~E>}KY zxBgU1f4?Hq(~m=wJkGuQ`Q=u3w)_>fw&Ydiss(!8eo&lrR|6N`*w3}_a@=0sZYD=@ zB=cz&O=@&|W^{YDAEsO|w{uVV@}#%pyhXmrxm>%1GqA$SglDEk=3z*7Y}~rIVMsjmL;n~CQMZMC`PkvkO*@i2nWFTT=WMvS z4@Mx)f?BQ8w+X@={Yf+ue9%MBlJetH4oE&n0Yg+~H@x#5KGyq2b1YIcYgRv;EL-z)?LD~b)cc6<(4(>HDv8R(Bn6;M01-RU9w3i_?J}D#mV9krjIo4a za5D}yGcZeQH-?#`ZxoT-W2drNOklrIXIk71-;1GMS-`L5T||*D+XUBjcGPwJmcaE_ zpeG!VxS#dT^2bA$4Bcql^v5<$4vHY3I6q*CK)lU~NBp2zEXb|G*kfde4ds>Bq8|;R zb}}i=8@Z^pKewahy<+PaFrWM;=dzf!82@n@NAp~JUSw9fJ|)x5gnQzCU=U9(JOZ1* zcrBUtt``z4+|Psn2^q#>?HhOfA(~bcUp4pBc*@{o5PwBnq~I5$<#l^e<-88HIMgQ` z&3z0hpJz|9n0_`305B4{0E&w8^ek8Hg%JMi!1gCIJW`0uclf%ehl$Ewll4crDp!`j zToz6^0(JN=!29HR#G+tF$x*(d5=FZ-XsXU|wa(^BLoruCGp)kRm7=VJs+Gy~z2i`M zgpcapo?hF>0N5k)ioL~hXWNTlR&YU-*ZIc}BRX}D*g`lK3m59SRzfpt#~zX0b;MgQ z>ZyHYpe-G}uh%UaKkS`i+kJ&uA8?%6^8rNsPa+^ip16|U6)2OZ)5ncD@Q|yIi3)73 z>`i+z9QlTkDBuELSPJeM?1|e40(tEV+59elBUfXeU%mS_kRN0Ubhp2C@t#hOH4OPrS8%H@$!Gwf1dOv~lOo=Hvzrq+G zn6@i*P>e{Hn>*A_r@~*CC-vj?3vUDDhVk6O6^{eR{3rWL)u=ZVx|G9u$oL~e?P$;` zCF;OxEH^zaGFGxt{#3GAht62f)Hz;s%J*LOLx5WNYhPK!RFTk+v;obIF*8~x5%71z z^6UM&^&9+(N6ryht;IrObiP}U8Z92)8I|c*Plp^`P57sqaVc@NMb?WX2S!v2b@P&x zo!)&M3=ur;xYF#xk73dOd!Oq>WQa1$%6+Dg z!JZ^6-0_k$Kj!UAO@r$$L{P7#u97)tq1nO?7Vc9MF6zT(W8CkziC2l+5rkKzMcDrpYxFtK}ymyp+iNQjDsc1B}fL(-qq* zn5j80vgC4j7-NV=X1JZYQcbt`*HeyqNG6sms;1{hN2o|r-CFoL?}k5wmtZlvT^`n0 zWnZK!FEbl0xSiGpZ>jOn@0VfM&AmP!u!*+KqIj?d%#KG)GGCJV4#ZB|0z4$ zQSs|ix%2$}7NqXoZuMCg6xkZW+)EUi=b#s)J`+D+kky|@Ebk_Sw)@Uo?PM&!rMY94 zRQm?`oK=DG^NtEkHe|Lu+K zl&&A})X8jH0G#=*ao6lb6Vee07&DS0Y^?rrG^{9x6Ix$WWTBK`tUtlW;Fj`xu>i8= z!G8MseAKI+vwL{h-GPiDzr);YYxXO%1F_j8`a6_j!bn+11ilgPXgRvxds@PqUlm;Q zc1?4d<3X|aL#+ev0>9eG0w=YZ8`f^;+Sf+ggGRBTftx$lFDymd-u4a7$Cw}rvL}@` z#+$RE2XLN)X#;}jvr|-*Yu1su22XE=8t3zMGIMZ9OJ3}Le?fNJaZ3W8>Glp89?b)$ z+pO^8daC+_%`QNu2)_pB?rr!3Y~qtqF+NT*kJN%QGj`71Ya=f?R<{;$>(PRi%przi zo@W-P)})e+{z%w|_JM`R&HF+Xf~+hAYTjQU5!iRuM{Wn&ANF>=Vz^KB{LkHQKA#rV zokQ_RgV0kVQTY`Lbi#kIck1Off(xI@}9$q8u>3+dU)VA^g8#ET+N z{zW&cicy<{(UBY0;;Nxhv*1&mSAJ-ln?d7^TET1nG%!eEBRUakqO2Uipqjuw5S67B zEIF$D!TIy0-B>kwHe)0^7Xm0xP0#yMAI z-V?FYtF^OjR)^8d$oVu|e%e8~E~Q7)CVDzkTbWSVycm*bqOa?C&bc3x1xCz^yR#a} z=M}Ea%unPkLcO>5=W|v9#GO6Si%-km&ny||LmMy-zTSPM6e*gx%)s<5X!wPH?;16y zS1tccUL=q&%}7F$enTO2@jZ4h?V4h}s`hz%xg<@-%Ia)4^f3jCqlHvSQG)Mo=%uC; zRj_>5g_g(3DiOLBI)8l+D~lx+GpN!fV2rFn$NL3V}R~^K0mbc{Zz%R)n?1@G?^H;o&Ft5$;$s>N+ zf-)!gMj~0#YAE?V>x$QEp~{InW}Zwc8_PF0b}<|H#aEv5i0q+SEH=$_3zBlqV-_CP z4s>*A3^N^QRvt%w*s52@`?CD1a@H>9!o;iQ;Mn+-(2^S$k?EHCxu|Y8%AY4k-@FU< zIL7IBo7q)PB}Txo>oF~9bh}2+`Q{$Xf^^UN+wW@mGE>LLFe&(LJ;$z^QW+*gFI@=k zOuD0+vgCj){#g8UBC~J}Uay%uLH3S|e}@;wrVO*ORd`CJ-?bzqqL&$Q+F2`bQ>%jQ z)Q1a6rsQ+%rbrrc?91Ef@7=GD93CnPeRDQA97%z-)YHZG;DO!6t(Us=@o>IgtJFHK zm;<(EA4VsZyRV#T6nI2}X|5B`VqE!> z!hX3JB*bn5Rk>Jhtc{B6+WzO_s&*A1vm;#IZw4$ty>mU5$- zD5-?b`ygS$mwnH{aLkOTHf!RskjAcPreyVKs|qZry?ihAfN|4(nwV;;&O!WonL#Ms zU2Hcb?L|J6uZ}j}^HsOhkJKTX%ZB@?UAxIpWi{~W0p10pL#wQb^wrt0B>?YeR!@4Q zq47#x>)>;UAQa|0oAIa~m1gkichZ=da@{5e&qpyRdBRvxZo7>eFWhz&ciQlx#YO1d zV(IkYZp3@D=^KpE*jf^QzI!zAxvBz(^Vo*P!aAzhF5J5G2^Qu{)4^hJDMRTplpoU}l| z3RfU;ww#4GR}5INa3!xHBjM=D?lng%%IR>X|Dp+jo2NFLQPEtsW${U`SR|MK*NW!2 zm|Q4v{edDvF@~Bc@xsIq3(Jy338bVsStdP=9Q*o7E@OVjs2I^#(A_jcOgPYjEDPLo zORjvow|g!g3~DHTAQu|#a1YMNtXppWNMqY)8BCIJ9$Y90hv zKW3{>#dBkA z%|{rgUq(tXq+vI7jcjv2TMJk7Vx$U0Zo`wgw|fcNP1IQExz9V#R&^&!CJ;#F&3C~! zA1;^6Fx1TS3pXWA9tPswn2Vtw_-fc*S$diC53Nl~j*6x(j(6VX*7e$gKwEqQ-6@kx zzM<~Id;=_%LkTx=a7X71nTwn!3=T%*nt#bcHJ-UeVj%VpPBR^Xrc@p*e?MpX~PL}8h| zu*&hA)(=UN5}F(H0JPnYw}#4sO0zA1lxJmtevS*;)C~t4w2SB6&7?3g_DW)thWcVtX+gKk%c5&S5=bRXO=H-!FmLhtVBq_S`U-%-SOPoG!QmkfEPZXwV{)~Ue0LxfIpj2ZQ_KH3su=vzBy~dZVb?LRjhHD zweqIA;x?j!+AQ(fY$dA1;@h%VhDws9#xAqkmCGSRhrks)BFX3PwS?@oJWk(WLLr(T zoMU1Xnc_l0AU6-Kl$NI{iq?GDErny2L=+xcNK*7O0Ptd*8Vk#{1E(HMU1$*GoqQM> z6isSm_snVdR+13DTe}SaH1$zElS~sRp8dX2hX}5Ifjsu*aMC-h9ZAM>Vfv6>G-|Q- zikK4$Sl1QCpe4!c>ROwrcV~ zfz_-cTFFNGzGUVS&O}X@Tma{DKJm^Xnkv~gq zou5;on(JA5=M@+YJ1V{pXR_e)CByIe_S8;-79tt6yU{RT1?fuhxvEDb@jJk*G`g$4 zFCd6kDdIlzJ6piBDLJCEBvk(t1Y9_k%Sw-K$z&aRk4gs`T{XRUrlhG=*kJaYLhy`l zr0pgyIl_w#p#Ec};3T@bgnMIoaHr9@beZJq*83g9Y43B+tegOWz1enAaBAGCw}Ol> zgIbqnHyaH$fsLTIg0ZZN>p~JkKt`Q58LS$OwL-^8lzS1c19~!t*&D1wx+0T%0e4=Az&=ol;$>m@x^X$ z!`;YCFU?&YY{`3h-D(z(;007V!9Ag_|k;+W|R#R27wiVA-U$oHBGo`YwVQBH5 z)&Y;~F5$=E7hni<^WIuWBybC?{sh;$+hbnZxRporN!y20@2ewxXgWi`@#UHbyS9HR z%k&TqCSbBE4yG&C*QFWnwz_g5jU%b{*h6Yx!BEpjTFo)KFe8Kdy_Mc0oFbl4;j;FE zNGYow3~F!7Ra&U=+s@~^h!^K82J2!Jwdj!HdfAzc-x1+#`xCvekX?=-V)?PErIjh( zAn6fg5Oh38QmOyeW_PhvR-e1I1BFnX=vh>_@KX)~@)~5Z9XXoqAQC{FLZ9F1F^Gsh zbIgQ+eAy&jY@8_?D2E4{PRg%CXQ;mvV-rYAXQjfRRLWP3!Ts|~YCFSd<`TU=zvJA} zJ+%VLQ&r@=VY6?#QN{ZnOoKG<)z{C3vu#%$o0k(R%xqO<+Y24szC$%lRaAYB6;AVO z@6>{a=vt}+ALZtb6VMH}m$EGgr*T#)J<4sIOLbp%cmITjiB*94Ud)h}dbM=st`JE6 zoV8aPyqF>JIv9ma4aIV0?-;ryxxh}7aW;;|k{8mF{s#-d3m;;MVwAI(^ZEsqzY8^x zJTAzqk?;K3G)`$K%vc?)wlV;g_ZV@NiqkVYpXJYNt}RCt%+*S&*g=yD9G*lB>$P?| zXVjtJ&ed27Kb&%@>Dvi2;xn?^FE=uOM&f#(KNZoS*6d#EQ@zxell!>D@^vC}unk<> zQu>r43Z7IQLjk2@O?Pi@E21JKG^dEAhXnc^172ZAHVSQ3H^tf=&a|4JF-cyTS*Hn* zn#cD#I}nOfEu(K4lziK+)|*jcfv3}zjR=S;N?2kVMSM_w$lcksxnnl*>r~rN^&1nv zSt@TNq+(8^`E=7*Wx;9D%%%haRznkHc=c!y99M4!+PhlVM@ZSr_d*i zYGl-n=>GW3g~Q^Ff@197~x}gUzC2@zY?a37N8n)8g$%HHs9ExkkDCMwLWn zu5;z&Dl>!Q6MvouuN`EX-maO=o<~WX@w|+2J9eg#oX$5WOMf8TJLgY%mM9e?II@zl z@pc6#*omL_)xkOOHqk(j{#O5L2$xfPs|YmQiaDWKx8QC0)8P=zt&m4D|lYRo{% z8lC)Gb;gIO;$ot@+-R?X({5|O*vQa_NN}oLQp{DFBXxjXhZ|!Rz@iH$oHU%6OcX-{ z#ZutKIjSkm%c?u!6O^nfN~7hU^V)8{RczY`T#3QMMWvIVVpfzteMZ;TI}S4R2m*n2 zLR5DNSCad%qIVUygcBBP21cvJ`HmMCfe}TI3cc*iL@TQI-q$FxGb>F0} zGVN_Lga`?NMTkJDYrpEnaU8C6maxE9Df(*XMLU^!VLzDTOT&u**zo&R3URHpr@(Y$ zxs9}e$PkxV-!xDSgOI$Bm%l?Gb~=9$ogSsBPx+rY=g|pd&{%__V)uUjt2jA%MG8H! zaGR&e6ziff&{(ok915ZO4rmNp6)cNQZ^u*6-^cjH>*LeUyDFtX6#CROy(Wl0g4cA) zD%2>wzHV&TDb)W@-H~;4d8`=bE5PAr8~^R)M&>Etc%-6FDgS=8d@OBTNJ#bf_d3~VI%i_E2 zPk905S8Zw9WO;kRDZ(uqwm>Z+j?SEt(16V&2*LbiJ0p+qx6!vbX85Z^H7%`nVYqW| zgCn05)ze>c|0(ybmm?sk30&HSas?TRJ^$?+T=2)Uwqw?wf}Mu{R&{0V>y}M29)!+8 zIawcf$l^X$i=lgLkockZ_SJ;MUbZ)n45LXdRZY3k1?ugpniy19J8lspd~E-g%+G8) z{gtriLX*#8$=H?`B@c~nYY$BQQ9EOK>8vfvS^BoONA#t{p4O!j&#!^pT$Hu>AWnQeo zO>?!Fnnrag*pPWNUtapUGLfOj@ai179~K1?*_+HvZqu>+$dBN9tGB)_JyEhR3qux z-qXG;pV2SRENO}PburYut4VdoSJA|ad3K3s!@iU|M6>}ethFEa#Mq8IZ&Isf;3cme zF7v-u92N!~-jYw%+krq28;4sn0!khSnz(b&eSD!J+U9G3`GN^4RkH$EFbHS1WP-FP z2fTmq4#*^X#ndDo0eTBEwpPXfa!K)Zd6#ZZ<7H;XPnHuTsS;~=vs5Kd9pf)3XUWjg z3_e||U{xK?4cl`s-GE8KBnn{>gx1OTKMq}e!$gkrv=R)(va7fcD4T@OYR;-xp+#FA zQ$K?)_Lhq*dNy?+;K)A4jVQB^2St?OyHBaAL=74TEO8sl=Ag~b;1WRjbRn!ayj2F6 z5NbN~`uSe19ewJp87ZBU1c{`j@_d^{=UJZ_E*!4cQhl;i{cKMeg;=dI-?1jCJgVxv z8X7>QarC6K#W2WuP{kR&9hqsB7nWW#p}M_XQ}wht{y5dL&-YupV)KJ*H?n;WH^VSs zVR{xD`Av%bw=M;8{s*FZRMk*1L92e`XVZ*9k38X#ryR47;H8Ulm(ZHW&hG5YnF>S{ zxY&YTOG*5KasUQ-%+@)gq|nD7`+6o==(e-p>$Sl8jAq?Y??+JXD|xYg{>w1PqM!4M zW7FC;yeuyFcx|vEIimihV))K<4w1S0F8d^$J6MEiV1fQJco?}j(8H-`l_R%o(bBD( zllLK~7P&CYnwp{lFhKUV2~cquJIfo+k+HA@(MzRMZk6UJt*2}RzV98lICbDs`}uhC zXX8~)&vpZ=mCM(Hm0p|}-Ny|_yE?p)RrfOrQ`Q_8Z8oeq7q@aj< z)JlJchiHt?GTfHj6f4j%XrhLp9~z0D7>7LjV`_lw$NPas&}ky^o?E&y#iAJ^Pjd>_ z`Qe^^m69bzpZc3fW>_zGwQ?U`pRja-V~BLr_L3ii#BI+Wtef`s2sT`vm;^;#2JHAT zsFE`F9iC9>p00qr4yYz!Jk}-50+v0a{ofM$SoSO@)B#4&@eq}$CJ_(oF5=)JV#2tV zcVU#q32n^h>Z7gO=($-s#3fv=)^FNARmh{O>`R$^eaU!DE`D;H|HWn%-DLIxSbtaE z@T-tAil4sn9-)H>^&$Ftp2_4MBT51Y>=ymLi~1UdB8lN5tA<2EPy-K`(%IbH6UrLqgTl z->O~?KnDwAfQ{wC*Rbc!mI0Qt|()_@c)e;D3QGbvAnSv*u-m$xvBq{a4ta zgq!5DzlKXETVzu_Wy^$&ds9Jf?zL=v>h`M9qEtb;H9EEs zp9Hh(6z}G^fa_x9;f`$y`&H`-Y`SwM{L4%zyeax{Tnh=2+G#`^hvU6_=_OX#7E__VW67B~njSTwh@* zW(F5Kj27<>|5PhO`XjH~BmjEL%TLp>x^inNj>lEO1S*+p8ng(4$G|mwJBQ~Fe0X*( zL0+7~er7coxrvmCAx!$0T7tlN3m^0J6+@{6aaP0OpLvHiCAG|As8GG{x>~LdcFsmh z&TbBSra~zO$+U;lo9c~|6^B5Qg2_YyNRRbWmV^Zsh(!={}D zG@fnCXv^F6Uo+5RN9(3bYIrZBp2FVONKFU$$#2k_>ZbEvgcDb3!^Cg4PzoC&bg1(52K`Z@+(Bk0IYa$Y zlJ+j09-m+P>fq&;BN@9r&t(Leh+Y=j)HGrhY5r{uIkRWhS{f>%Gk1gJi!8y@gj3FB%jv^Ts@9IPf!Wz_zg3Dph};*KksFW3Kgs*OEb zAzS;_kvX6bP7s>c_L}&|onPkNFsPCq^ryWpXs$N4fNoBE-^up#7&xPRaEtf$@6NQC zqp~TPRj<3+#@tN>z=P1B`YD5u>K9KPiHf z>?`Lwv9(D^y@WQ+k(+@#gQkv@LFGK)6Gh_XS@nQw=NtB6P1oANp@N?MgUnr?0T<6LWt zRaj_;AQ;vYQQo$m)`SCDPO&-70P!nq8s_Uv$WcktY&R41+Jl`^^lP&^QfVE7eSDU2`dOinQMgL*F*sk=HDl#$~5ik(Br22mMNll)a#@i{T=uymrSA=|-t zcv$_cHoUvzr9uObpWmn?z}z2YN9A}L@IQE`QXt3h z6Ic2CvI*|AP{iW24f?dfmsZ=E<}q18&rJ9z6Y}e@!_8^~v?Nhd#2a!vikm#_LUfdYqq`WalnUw`)$CM8wZv&B^?O+sYEO z)PBMdTx~iBNI5y{-ZuS2y%9PB@e+3T$s~lf&<-|Fe#z0c1h-e3NQ*vkZqZbu>$=fw zGVJ6DGo)gX&U}0r#`XYP_^KQYmu(Rp?HFmyO|R+c3NP|>X!jZ3ZB}!`puch?>Ie&^ zagUC|?F@sI3O)?ZRbK_ZW!CzeP}Wtkv9xc!!*%x{D`l(cpJiC zTV&=V1`QPv#ZgpL{b{O6s+T%L{$zJnIj#|V%|*oZM2VT3abs$fo6e82HrExm59iBz z-E~&V`=5I0ZfDhQ^}Us(5HdJLYIHb+v(}>Qz+5@K%@@(N6UwHVUUyzEKEk52xBEUv zYP+d>dZ4{;&QIt5VvgIyz24OVJ)P^M=Yr-?r1SX5<3u~kENaz-GPRRmc2m#Ds`tJu z*{|B2ZgE5Gl=}!@(5`IEj&^l^vu-q7nRxeVae-H2#xSG~Kj}wiG2%QtV_# zH4s*O4$jG2BPpJl;^*Mk2`ULFNWD7`KU{GCh=N;|;waJ{=!k-?RL?F0S5+Bd5&o~N zh5pE~&?ubY^KDv7t4_AS#)(pu;N;lSEkiyZ+!@lS36TKs|Un9>-(dvB|RrYauofdHPjI&2l zT>emQk%_;5(Ek1T>p!(&=lzH6M4j%s!QAb&90TXLSdmT6tIfX8_^D=R)&gu(yjPDC zZ-aW*Jrm^>y+u3|Z%%Op(-7;%&&^#-f4mC{GTdi7zB3+{;!`WYkQL(i!4YPyP35FM z!hvk$x_Lc*kp|eL+!XnU)2;U*yy%asSsnPnO=||_WHmbYWq6#9QhewO24lIF`1trn zN+)m0aGBx2i5}zd3&Bx$B3ISQfQm5reEQLsImJkP$q7sBDp3k}j|iY^8s6vTU> zvN8J3E@PyLJ)hEirCs~4Icm7A^VVI}l*bLlX#UvUp4??Sw)+N}jtj>ICcSlmt>PlD z{CIgFGgW7|OyHARk#gU#&n|5tSEU;?BpY_o`BrOgR(xhZrP91yf5)WkP(XM{-)*mb z%MKKs2ynBH((@A-QoP+NSk0VTluXUo88ew3&oc>>oUwd&94t|>pEnofy&Cp?F_*LA zRWIX72!{J1cXPl5d$RUlE0vr}My zzuRj7(>RAjI6r~<2flm_tO*>piUM%)((uZ)WISYIVmi9#HZi zmbWki7-kq&ECsgfQG3cUOf+dJH{3&2QNnNSd9=NjyTs40X5|0cEog3}v;H@K?>MEA zM)4&&7pmk1zHLl%W>-x=>o-wxU0LzyXuoVCu-B2Q40W479CXDUD~769*>n)9d1m-AQl!r%{r^1B}_jrPFfP6$-M=tgEI{s zlc4Zw_Y3lzk!$3ybq0(q)Nu`7Q)gF$`z+BYky|l~yr+9D3WN=Ot7|4?q8K$vw0;_4 zI=f>n<7eD}C@xz397@Z@E~4d`guvmHl-!vN&dV8656bX!E%;~-8KaYn$voLaLT>j2 z&g^w(s=nqbL*PBV1;!xnPqoR zAW$+`8Ax7%mnko5&B#PV$VR=3P?jC$mO>1taEjB_jBp}@IPHbonJP|YRvBD*;H3&X z$u`DnH8-X2tsJrSXR8`4(@1`~;z#-EJIcrqq)LoX-f z9jAo44hP&{({v$4)_?boQ+5>a)s>6?Oc)|KBrubm>eyfL>;4^GEtC(_%lXT4;C>sg zn{*i&9@KxxjTOH3;gouivsmeVlOHQYc%CK{9F^M~eE^F7O*-B%*=(gU8e0f4zQ*X}tn{E0#GCzo6x_er!<9+t|(% zQl4`cL^OYNaYcF=Xs!G*?YnFvOxy8A&AOskkjkWIgG_QFO-<%i`G_B`z~8Cw{Rf5L zCR*nOPHc{B{k@}PYrn^QM^7L-WzCqN=a|I_vUzDkZe05%lIIZlPG?u&-}=%Ud!^(# zZ#+H7lAz*P*zwT*1Bc&w*_u`w?-#?22oL0jTOz+){3nu{;0m@`0`QhrDgZR|x2=kZ znRL}VvOzXT2h|7?`tVMoFB&y(adA$|#azm<0w9?mloh%O;~?Hzk0%8`35IwQ%sI%y6C5F>SqXY9kG3Vburq(U93Umi!K) z_t)8!Pi4N!J!|NAd{;sy^^OIW!xves_ZbE?Mfuhi#=&qYDqi${a@rrcQ!S&NQ1e@t ziw<5%3d{BS=R70#rYnr+cby zoT#ZPzH}jQiTc*YIGs4$dUr?(bVIuj7+HZJs&>u%PQ&tf+>%b$dKuUMU$nsjeJ^&q zeDnB+iL0xajTaG1{C=++KH`#gHU3z@3h}#h(GW7M_-B#*Kew7BO);tV|I!@a+})7M z-fYl5pV*Hr&z@9P9i>M-TXAPR_N6sV=N;486BWNgd77m%y4O2SC%xDb08p9{x!lLWT}c@C`Z+XuV{=Jkv7qw2 z<{xcOk><{}&We5ARy)3G2kk`c@s2z|oNkXwo$1MNMjM4OLUYD`AS7@6sjc{&1FU0r z(%$lo6Eg6B8z%rF(nRuen-DP_cw(8&%q#~ls{FbIc><@&JOGqLwSXw^M+dHoDg}=p z3}uR@=PbUZ_>K56RBERCUoutc9i8Bkh>CMpxOt*@M3IOABokio?rZ5MSzb9YE}%Y< zEPc$wM>u@AILP}8#}N_AV|&3V{}_YsAunyD$!CR>tWfxd*I~q4S>H)y3?C6Sbdq(x zu&HIV0TP9a#18=9{|A_YtcRX<^S_7T83BjPnfD zuYSA^Q_6!A-F+%Hm{>ZJgi!TP;>tgU2FLfz7i}O$M^&)=)4!|j4*0Hhhop$B8~nbS z7JKEtqDVwIL?2#=g^wVLQ*Ae^3k6y7q;b{fUr^1|A|6Lgv+`fNprh0m9o#*UK3#IrU z?f-HE(J6MBFMDD=7TlFo^LJY^@xm3x;|pH_8OQqU@7&jI5y;A9yLpY5hKR_S5r-QA zYk2Wrz&;k%o8?zF$qzePH*%XeW&@Y9GiVrX5aZ&Je447WH|NoKfZq*=A0Vh*T#435 z{O9W?cj^Xs)h~@mZfWjz()ENGCPV9gvaFnpD54%Vnh6@=XNJEmC&==bJ;Omj05y)Q z#`M_F5pJ4=Wj6?g?&D+6-Nq;XHPuqc-ByEJO&KJPnZpJLy7SEy(xq@|jwLWjX<9QW zshQUqrL#RVvPzT_{I@J}SieJ+TH!JS#ts{b`^Y^X468M3stI_;)zGxcBocLd(*|Ajj(2dGQsy+C7Q0rIpY z*--3nc%5OUu;llS1hI-pGjqv%^Mm%zH&z*qP4Tqa-yGe)L+umP9&1Wm; z!kgZ)Z4cD4qzqT8ai zz3djtM?MDzWKh$nJj)O{8Twu_HG=2v<%KxC3N@Ql!?Cfu07p}+590LB22-3Sj8DEUORe^{ zWG7{*0{-yEl5-pJnr$v!o@_sllQAyEV+wU#Xk!2m$UUVQcFCw69N(F&b*nGW4DbKb zVTu;U)Hp8xoxv5Hj1HeQbSfNC$b@g2V`EwJRTT-v+SeyARd_Ex$N7b7y5OE`e=Q`M ziW04v>nnI8oso$qQcoPbRiP9V;3*anu&q^QuJIinP1Z)gk7vx{K`Z1F1#SMVyyt5k z{(HFq5toP>Fx;!AfH+Pr7iV`K)3;|OKi5!ew|5IYK7M?%9VlgE*67N8nd!B=aCSC2 zU*)3a-_rV+!2b>bYot?Z-0aZjNPoErW&?~@z;=lHqM3eD2|HEjH%kdfbapJX`F%`Q zX9y!_d^D1F)>S6%6=YDah(QL2sqaNyI*&$GAnF`}fV)ZeG#Zq0{s)_5sq47~zEud``**j1N?*fkY!=nW^#tqeczqJy5+MOqB~N)Fd`rq6^PI zIfkY;V80FMR~-n0wcT2sRLkBIEMC{LnoBuLzJ+dVl!>Dq9asBeij6ovPQZ5Vq)mO+s`+oCU{ zz#xMT?hb>y5AHDd;Db91(ntf%Ffh2gH}3B4?(WdIH}3lSzxO#e_Iq(p#QpHTyoi@k z5na&{Rh6|eb7iiTzqRTC_{DQv>k`BRe9Gzh(QnsUtWnr zU#hWTeQ0&n9gdq$h&i2(qBzZu3w$rq;B>oop>8eKO)=ES^LhWauY(&hz$?H?X9=r_ zQkq~tWufV19;VPkMrsJVpjRVPy6``}cl8mK%kGEw%~knDsrp*Ee4oXAQEkBMbHo-; zx8|Q0z{PytweWMx$xMyf3oE9t$4Q1ei^vg@Oj(OeT{|}QjZI3 z_-~m%B{_bp71tlWWEotO?K)rk|-eEDuLZe<7Wkr&xG1lMziZ8&)m;dRx<+| zg#BI|yU%3r9`|%eWTJ%<&n%(Iob%;c=P!X9%dXJ-z4q)6B_p>;39vYho`>0CJw3f$ z(Cbz{*h{Zm5gKN#>0$-3I~d}0Jy~V$G7j}TiwqQ4`NSRVe&dSdadw)JvGh#sdlgV6dA>{v|6R`N26QkJXnD|>x_WAU+FMOPk9^4s4Gl$%5o(7u^s5k9z@twy z!f4BE?0M~M!BqH}@OFE!iljZ|Y$MUrI_{Lg>EYOZ*6KP|sjuF8b!a<|RA%Ze6o%%q`2G35{Me1gr*GX+8Y{oUe4uUg%;fG9npJ~wmO%j*wv)q>9xHmU# zzOR0|%_mz>3Gsl4_qDdGRWLmeXt59}BGRv9W@gH1_oJQkI*G@w1cdAYo^Q5ad$`?U z(7fK+)=Byc8J&y(ku02s;W@PtuoZa!Oi&cRItk45wuQJKI7-hBy?k)jjiwCBN{mk@ z{sJ;yRI;YOyXUg`(Y~I)Gqn?GOrr?vU)5UEed22QE~67?fvb<7TLdk>b>5b&?;UOI z_BnXM6#YY*W{I}WTDMnceYFW29{Oyt>SLNRiQQtIF|3#Inv>S&XMcA~H)!?D45?;E zhrPswvdzkF>GHL?<7FMuAYEa~Tiz`O@iA_TJnpp1~28d{7m|& zleb0#w+91I#((@9-+xFi!R6Y(M)sAAf8ff>&`xFkM$5#>iCs{T%!7A5Y=((we|a%l z8mWgzDKIEkw99sf_1f|Pf2D`J>{Oz|+0wMAr29Jun-9VeBcV?s)YCMyUaNt$`9S=z zJ{CJffpV3?Pd`XpeLtGGa@Y6dt!YWXv?J^Q~jF zOFk;pZ7adZu9kj)A=70v`N8{)a1Xj;d4b&2*mU^1aePb}Gw7IZ_e|8=PqY7;b}BRW zpCT(U`*uw}64>XIdgnai6m~cZ(yscsb(%us%{#h0e3{L9T5mNu@)Wtfjn%ghJbR>3 zy85$0kcz7IPG&jBPCS;KvKd0q`sJ%W=w{{K|N4jskK{BGThk+m1rJx8*7mMJ;9H(kk3Q&Nvck^X3AKwB-U0V% zv{v85$*(KF$P74NQhIuZTuQ3p;r6}V9d>wK9i7M4tcyJmuh8o_p}MB^+aUSP@0d$o z(|)YASSLt$wYEmE&Ibpl_krCO?&YI)a;uBY;&=U*XNw=(eYX5!Z&58QZ>;LW6ANfX z2fTtq;&MM+UgQB6pa^tUj572D~>LjYic@z)E*Zf%>JxFn9v0L z5fa=UJ-D4`fQ5sJ3C%8_caCvkKu6yw>-!BdR#A^#Jb92+w)U=czc)d$> zyDTZ5#>Tc^?Y?^GGVFmd4yK}}rte#=aS2R1xR=um4r#~7yV;pd&LSuO=yh#Rvy!s8 zIdk;_{nOPIdrOknz~g}DT|D-gK?Lpnq_gwk;o-@e#{^^`<3MmtY(&0Xx0$YEeB4y| z*U;o?5#kCC+dq>2_PJA@;J5HGwAp00F}Nf&v?3gIJLq z?`l#Cp|Ftt^76blz4iANPn;}tTF3r=^ZqiSOuOB*zvtZ8@2}q!8{2_%)1`?AtobuY zMfYtT9j7CR5kGz6n3?yMmOCvi_C;emuy?+BlCT%{mJ1;}jfQy3%k2pX5cW^8SEUPj zlhKn08%qE9@k3v~?7-3-_56wGBknDMC8y2h&Fb_@s>jixnwo|X@M6TNkHeBq#xF85 zA4vTF0hGDp}p=e+SK$U*M2bxzJt*d zt`3=q2uwH$<*Qf1jvNCJ95N2K3AlQ1YJ+yKy@SgHiU6HsNbBk(fG=T#SH7m%d8cXK z?sRFXDUT{Ei=04%nMA;XG{EfzpJ2W$SlIjEDYsavT|zc6B%-1wqwcBh@{YCc@iU>2 z_rZ|vI37%BQmSIB5ZBX680Y!uS%_7XbkM<@&VJVn!g0fa#%#~&G#=KGaMWU`(Jlv#5T<>C76FXp#9f9;O@NEPaOVz3hNFDKlA=8 z*A^^m3&zNQI|pF){zjT^6>M$ikQv@cxdh;sac>&P6j8gn zO*}{Asd=(&^#XCL1(N_>H1K8!vg$ItN>W-FR!?AO&Fy*RhrX3YGrsD%ZnbM~vGMxh zDio8Qx}p%=Q-7x(&eZAMK;dx!3;nqL^Dkm)Q<{dL&_?;7>v|yZqk!!6Ne6h+6#srIGi1K7m7z@Hl*n)lF#4mg%VoN z%p|B3nuN;VYelf0SL*wOgbmazO@&Iaf;#opjeJu4gP)i+RN+80Fl}@;Pj0znyU5;l zeE4O4TG!4;2AM+>gziP%y`Y+p#Ev5=A(4XAml#+%%(hkL7mV=MP!PY0KvPZnT)P*Cj!XmBxfT?8nQ&ZIeC*_`?SQlvkK%ucggl0qDM9m zod_qrT8@{qB$~M}fnT*Bl(m!x@9YV4AtjgcP9^UIt!?!Xw=@rdt>g%AtrJ{f0;6DN zn0F!5>a`lgFSer`d-nhOS>&VvL5l|Wv3o06-*Ilgu2Y)4Dj^4^O(2%o4APeoGfjP)S$qWs} zn(xc(iSGp!3W>~n)iwhuDajc8&_u&F-{SV6#l^&6+uh5H#<$-apx_zqVGd?<$sbME zyr)p5mP6@I17z5w@LNN?^!LGx za?&#SX*h&ayQ~{cCE=0u>-JSzPL}o?_il71By(idEiq0+ej2Ep%{SU!FHty|!RwBw z#-=dWmb^Qp@(=^w$UkW0^lQBV9}VP3N=S#V>k{iax;4T+lV$Bc0V7;B+QisDORYTS z2=SZchrKsyG+QMqKiC6sD`XoWHNS9`2P<_CIbCKM2HTcT8oqt|C$wv(Cir}JlSdQ= z!|$rX{d1e!;U;)dv%?r2M~AB`=yuje^jV)Rb0z?5w8;=1Bb~`oAAE-;-C|o$7axbMBwaIN%5lL zVPPUGjit1=F%M@y4R?APJ)#u!>fCRg>iI5Dx4AL>WQkwoCa3JyQh7zBPKftxFRPpu zc#^n7u%bX!5g+02uCp7E)^eNqe(QRfl#gjlea6p9|&{3>k6b)m?u5b2t57<^C$B>c%PYhkpB z`drk&fM1PD^d{PEoOOSpYpQ-l;X4E}zq1FWdQs8wHMtl6?T7H>R<{S14{V|ZLbs5N zZdO-e&+L#wlnR&hBeOhls1|N&aFqTN#}f0?&GH{Uk=!lsFxt~ID)dOtMoo`|SZR{8o^$A52rEJ+;O)BkYqS>7fIpUm` z{Nh#Ug1-EA8x5PhjmCfn1N6L;Zx1=LeD8xsZmGzR%ch>zorXfG;}UtP6^$2C;U*ci-g^Vnz7NK#jW6o=W$3aj;NC6^|s zQb8W8ixTn4^vcl}s=kkstDyUPcr8iS1(OJO<61w~p32WAQ+Lqo*}I06puu}=Ka28> z6S?@s0u}U4N0tZM+8SeYw&zu%WSob0^`S^j_L@Y~bKlB{{e<)?(67*tfFu)#?e#@Nz%5&Og!whpztW zcpX@BFk(h6zw-JaN-9zez`0fWo^z9q1H75*{jJ3ZRpi5;RMf<9WI?r_npT8gL#w+D4-JA?E3BaKzO+1FdYh~|Ov~fB zx-e>Od)##9%es7~VILyCw}Hhk3|#&+F^QcQ-|+UjS-TtZBo?0o6E~QK64u_PX6)aU zW>Z%B*Qn|rjmO^Hu6)7R=NvEKa0$^=1NH~793)jfwirk&UWT`T$V}$l01jgHUTr6R zCRq;(clfWjMXGg@U;&q(c|*%$Yn6+Plh1BwzrK!}EOy?p`^W)JC5T(o$1Ug&;nxV> zf)N)>E_v6REq--Be%@gubHNuDMacBurYX0&e!Ry1&T&E$cgevTVHwt^sSGMt-@B90 z4c0c+b~%_x;#eAw$a<%89ZwY0_G>`qN&W6nB0#@j966BJ-KNN7b24z*^t~to>nG;Z zHFN{}%9~#cN$cu!032?3p<)|vD=D>BRK4mwyY9%wwil22gP<+mG=);)JO z7Y^Ygfm+R<;r&av>>KdV?lS(6S_0qumlA%%u3z-Lc6^#0)#y6L+)F(#%g*ohUr9*X z^uAEr7PuaXY2LGGXFRUix=PA0T>_K8E#Iqj%Ztz>vKvs4f68OE>$Q8`0y&|Cj6p+y zn)7PkWzdW3^zW=#Y&APJ#6kjO(Z%Javs#0ZJ*n;6XU$Lf0aw zD2RC?JT8t*qs%ggl*DahP|m}J(pP^JpaiO2LUY<3(0#Vk`}q?q{c~VBQSFJFu5(54 zozw1!qjajWGvHPdlF+rKag2zxu%0Jo*ORQ&gxfn0i;cjo`uP||#DwpTlVW9T?nPIx zjE)_Fdcvb))g&PBGV)i9*q0JpvV5i4T|VIcr;hq<_aC8yv5SkF6#E|zA&n8ad#Ol) z{m^G4BUB}g-G`E>0y_FKQaNd-drAyfBBY~_W84-J11fTFiWaK^Lue8bRRfE-4U1h@ zb5T;fSqAI_imfCU;!6|SYwCV0HoujW7<#@{bGqe}5`A)ALKNb~a6Z_Rlj0)^eUWF( zRR_Ir3!KoI{hZURxU5~?slU64TJZEeVE^i^_{zc-Cz@N8Y{_2aM3T;Pso!VSz?4hZ zULu#WwCp~{vnjd$cbp^ZE7_s!EW&3^TdU=qAAsXga3zzu{D{?ZVGPceqJF4gi^=c6 z#6gpQwX`_w?0us%>=8Tbk;RG8cf;cz%m&1&*44cGMl*cXQz7tUo;hSahCi)}$rzWc zRiC(hlXU)|mN}`9xu;Awq2{X+rH2cxdn5m27|UC5H2M7vXuWIQN~p3Jsh6CrF7Fe} zSt!%x0%hb?<()UE%h}$i_vk-}OW`Jo_M!Vn(W%k@mACTHmQbVW)3vG5;p&%E($LlI z>=~R5e!0>grWNC`lGi`9pzQ6JqH@xYf!ORm1T|rp$V>Nk={9m5sVYA^mvNK#FG9`)}_SVoq-zgyI03^p9%zly4I8<{Bhq3lFt zGl`z(YHRCM!+rR4!6Ab;=`5>~hrQYL*S7CSWidA~=;48O4)y!RnQQY?i^e&gms&!d zW#4$V@yVitpNrJlZ9}iswmIc#maj{dfGIL!xIyw{h6qsuYV>iUeG4>eGJVZHkP)wI z9!6yhiB6+}H;wx94hJS)tZZ7q0#gH7J4x!|Tf@r2Zci^=&2j*-SxIg|0q4x3b}KpE z0jz#m+c$#*rqt=n0$)*PV{1Mtq}oD&nW;HjRfaKJ-}F#y#%N^-^D{;Uo9E>AVtfa>(V*t0?x)VVF9 zl79ZsRkv(EKq)C+BO`dpAUXKFP%HU}Es=eg>%4Cpsox+(Nx$0pf|OBR-7CdXRkEfh zB5$3IfYz~$8wDk!ODqo>iL3>q3Q3FzUEo8%hn5Q5wsU!#W`uu zyqtK%hqF$qoLbuoSJ{&2Fk82J_Q8F4LmxM?T9af!E7$CGx6NP}f=>OERvz8}{jtFL zt16>_^n)0d2s*3sn@`G){JSo3_a)b_iH8LRc#9+Yr<4g;9_I>j#m6-a9l53P_E(84 z4fRcbsw%tPRAu{8W|HTW`0+wPVvKsOP;0r4uv3Z^8s4z<^`DR9Z0+@qb}0$r2Zv%x z;`gl+s;9H5UzP^gpkK1T(_(#^J~#7hZ!~^*PgC!Jxw_)|I*iNJD-uzDpHabJ>(h4P zH58CN$jIsP!op){RTKqXBJk6Nb7>i6#zHh2}=7C$t&G9%t ze+l_)K=ORwQsY_TDEtvK3`~$EchZuiud`T1Pd$5Cafa7pSbS76BjpF(nTOTB&e9@GR;jWVsR}XG zh1e)WG4ZzfPdYym`PU15l-k_OM~l0L&5+4gHJ-90=H20%)D0o+=iSxj*h4PQISzgW z56_`@7{pXx_!$^D%zebIc%j{Q@9(~gZH;Sfj7Qwv-J^}EZNDwZ%=Gyds2m%wtP)+_ zdkq@qLgt&SFG{yyPn@V$Wyj-Ve+;T_xQ$Wp`85DgowG(VpkBG>X61#-Nl0iJhsiJ& z*Xvg^LKWH80KyEENnmj3q4rcC?Cybw{*rIc>&FF_`Q+71g_}LLHjZ*eHiL(zB%?Jr zCu4E^Pi&08mMo(@x~Q3sD&ue~SUJ`H23h&sFiO210|JInxJZ>A-wA6i*SW|v)- z%$&(jHz+L%(KSkiNq%E1 zZH(NaF|lFDCW^V9UyTwAj1U}Vu{k_Fz`u@Owzw_9!wkdLc-ou*WXhSE1jUcpkneK$ zl-m5BRdri!oD`IDC&x>UnyXHOGoY__4SRui=j0C-S$;96ZnJd(iK3Y*NvFj9;*1G* zW2~gC_}0T0Dt2jneBP?}oqCuvh^V{b5JTQ!4Se3XM~M&I0fLSs^#hmNTp-k81Av-`L=r*W*XT0z`WZT z?>a%MdI0m@bh-4wdA2jm_dYDU^);kHf938xUJLHNVxrgeu}e8Vn1m+*?@Y<7iiFh8%D zyES}OC9|^!2rfRo5g{eqJ9#sIgVgjLKG5G^Qv6HCPXxp0cY;q8{Vk2IM_u=;?3;{S zrv@zI4K#+NioJn|*MRe2{QL9HQS0+IIVFM-;R?eX@-aulBUGMw1}=r{Y6H!#yB=)y zSpK&7KkRnltiB?@mX)d$%B`;}c(HwiV*2RIn|6g?Wl=DcxzgL*Z`N-#F`J3dq#8C8 zIXwu~Whg_*F#HF_a>u1ki!byNXl$mqvTGaIP6 zpII5|*3gMgg=}Y$1rL;@%&dKY!O zklpcj!qofkaF#{oX~OrXX`U*po~Ednb93>7pulz9pcxmqKEK-9ouV}%r2{tu-sOUt zT3UDe7#(6f>NXmx9Tkq+nI$kP+eKMAXxdvw`mAzc;CDB{%E}51-SqM`s1UjY^AO1fJ4#{^U4K*MsR^PI8byyGtu@e`+>dtTyM4_0PfA5B(0} z`L(G?fb5#ZPFgkkqEf3iVnsD?=2y;nQe2785es$!2}K0fhwA6Y1vv+h+u3%!0QKzy9mZ6hm`q7#Ego(MPza7TEg(g?NAfjZ!WVQ zl4dpg#$U1|KB!>C!w3Mu*U`EGTxa0)`vg@>?i?_$iw-dOlyCUdfh?h|1}RLef9#F& z3#GvN-_1~o>^N=TBX`$Py;<@>20FtJBy8GXVr#(~K|!B4-mL#Pz3a&ji`x?75)$So z5G3_X-QBg^0q=a!vb+Md`9Q(r?Wtf)a%=f?p@#Lz;i}zQY3;4lkMk>M*X=KSkHh)( z+)t=$1$f&W$}k@u`5-;*&--z($ro?NCyi-pE%})d+bmHlKiBnkyk2bVzm}gBd)kc} z%}?e{a+!8ke6U02j>A084!82Jw)-cAXfPxg`;u!OLCHCsV}m6p@%6WV6WmQ9a+mtL zd|nhO(~U&bYzu%to>S-Vq9Uf8F4>4v&yW1~A{9GC#I(1w->k?WT2IhZ`d`_bVR=VfOYK-*MH)q%2?WJZ~Gy9I6 z*shD<=#l(AC84io8Fjfn3=uW6BYo$!iA_y(NC_Rol;3|SRM%pT zL6ZH%PSQ_=;5`{_8fqXS(!#`fZV8eRarjWmpF2w1;PmS}c^w`~Jl;#m;SuDm)Z)lW zN5gP%vaxDD<)#cIGW7iO=ZEEC@l{qdGu^EcG~#hW<@-gQqF7->1T+*BootYr;>o{1 zz!|Q7ov(NVmwhMF6ZClMbUBSV4n+jX*Vfd>_XeIazimMUd+YqrGSEzpi&v!}r|FIW z&>-g}ceLSJul3t3dfaH)N$7d)?9CiI28X8N_Bn2h`|-{cmvKWi#KlY}6S|l`jCy|> zK5_%J2DQJwrz!1P@bXDm+s48I0SR%i(ia9PE4}_1Km!&w7I(?8P!@Yp;c9OM#2)K5`5(AommX1j4Lw^mAP;TuNT z#zH=|>8#uw|F-QPBUX;W#kx|nnwr@lpT)+G!^RdaB4}QgU+U?qvobXKlvKdTP;5%- zCxc9EEGrW#KzK_}!_0qg*5G~Izx(!Z4iVXv#(3SEa77Tc!yvezp?10ZR(;z3dyY*y zkU&|1JAKWB(T@eWo4=g|3zK-8E%(&;m3j2F-?W75Si-P>?Dz-1N_Y8B9wVRfX7y<= zmBgIf{u-QSrScSF24TB2Nc5YLgR=vr+a($dJfP&AZ^&S zb&Cn44U3nDsC^Bverj>{3qq-3%exbDSGMBzU*6pQowL~hpfTt_V_9~{<|S0-`p`u-|JHXUe)%o{78Vy?B(Pw2 z80{^~Wy*;Y=ab#P5JA?ww~E7+d{W=77zfxSM4N>;3RCUrdhM7Q4nM`4RCNkF5mo)^ z<}TS(CEjl`3DcPw>KCAGyJ3+TcE1$56SoHlQ-l0H+Aa{06+zzBFtHJbaby*g@`BOgYkOyVu8Y zypXe^!?_E4abQ#A`2zBKdgJkd$<8Rle6=x3MO7@~2S4ZL-c&qTu7?F9oV`6ZqM%`e z^ZH{K3kGbvvN7TS3tVViI~ZtdiD^(yD5z@FW|KgSZ8)=0y;8<3fbI-2IXVNk%MP{~ zn+HUBQ!s7l5HSM7#GI!whB9{0A0>O7H5}U=V!yc3Wkp7 zYn(i6ueWxJ5=tSk9tNL~Xl-lqQ5hw)av*2FI@Er?Vf3=tXrFu%7Wn+;w~qMEw3w4I zwhNHoS_NHH+}L!kkkOjS+Ad7Xrsw+cpU{{yIG8ekAw!bPgUls`PY+Od6f!>x^}mCs zRRlAScvV1C_PftyR#o;Nq_9z))-U=4;qJzZ>1k=p=@{s3n@}1uza9Z(D}Q(!0~&(R zVFVBRzS|qPjDFg_>N!OLN6**LM0)Ye$opJ?eE;dP<#O$O#~6pw4gpRndR2lzpuKb^8B# z0_fKt(ii7{qf)Rp>wvr~`#%Db1oHm_fztg?(&_)~`u~xd;_>agoBroJGXE~#pBw%Y zwt_ur-Cly(%foJ-ZT#E?bt|uI2_OW853rxz2Y2oAG&+^D?>c3&W9paWd%R zY!ljKF+7?*6+jHtzu>cbRQcyZW8E)h7wxHPic$z<|HblLLYB&>*X|m4 z%G&j=!3D9QE&CET-L_Ew=k6kYpKGmc}&?6T&q9iGnH46zgyD955-`^xqOxEgbUSV_5~_0wsD&+a5+ERm`b`t0>@H2QR1Co| z3;!8D@mdx8KF-7Z-?UB25ssHhs~0ndsG53=vT=Y_@Xv-4pV@_t*4M+TSN?yP8QoA} zFFK~LCet1A8;I^Ze`{kMc+NcJUvG$h2IR$ zUq5SUY27RJ!@;c!R_)#~2K3RXMzQA$du%__)*1%Vk)+JlDk$VIwRvgn=aq(1 zz`Xpgy(uc!hSV1;;pFdqDj?1eUDc}-)g*#XB^=alOBMP&qSC4mlRIR-q(5j~;-Px+ zc(0oXcKg zIe0?lvC-CP$X@h)3HKtr*&fG=ZGZ-X7|gCP&MkE4LY7!B=P~SgpQKyw#cd^y)1?0d z5tz-&-l3rRJK^BvEzI*1YV2ViQ8+R{uOT=0!=aVxak8R6ESavQ>~o2Ci3D04b{ulN z2Q@xD1A%xA@)foTot{EmfY*mR5|O;=*CR;eJGiEInCkcUocQ2CmcJUx;~Pc<60Q@Z zlPSz=vAhg&TL`ReZ5~PGJZ>4};b@{Yn>`D^x9at`HIQiWL(^HuyLH3=pb2jI?Zsal zqyPnl93Lj@UsA*Z*1$VD{KB672!_b4RZ5ItFKtJOnfS_0T=KRwo$B?xAcM$|Ph(`Y z3c1rQ50nADka5~f8N8;l9|ucP1(rF*qivXkxCxOOveDdi?EMaT14oV|enf&^{EY=7 z;(+zLBsslMN>)D$d(XT81V%m5%XPP>%kiDWmd3}S+O3&o(ByB#Ply;)2PAD%z`+=F zQ3MLrWM;UT)$`;{<`yQ<&bLpG7(XSS?r=#6@o#yWRldy{Wr?7K7nFiMfo{|%R$WIW zn|wkavMc2@o00;JYjPqj3CgiJ(E{=m5stZq&B_%V_Wxj$^hw?+s`Q|VqP(6sUuTvz zR`kXGIXrPwXrJ(?cIrLoC?=86L;cuTz!LZE|G}%&@eT;m63!V?j7bF-$g~rqJ2SDC zo_R2a!GN5^nH;ao;n0B+ii5)kg{Zhz{z{q`qs={hvRbm3J*K1_hN&xb<#Q$>jyoeG zvkzKj^}RJ8i0iC85oTd82-0_*=6I;BII_Piy5lGj;AY0plisXARCWfn_7liKlzXEl zU-PhLgZ9I<+B#R&vSKiW-mtg94~YFA`v*Z+c# z(3R61$jXiMyDwpiyE<-H1z*`IY^+JyB^QSivpY5Q z_vV84kx8;=cQI64A3nLT5O&kX&Y3OG=7~HtG=pRtw&S;~KSrX9UMbO-Gkb6z21X$5 zT&RpsnBa>(zTmY*te7jV8+t4iQpt(3Yuo$K9J#-L&&N=C@DP}Fc_g@|b&onKK) zNVAt0Sg$As%8XE3llh^YL#Ofz&+*#~s~@79AwKLh<7EuS2}h`my$-)Qife*Mn8V#k znAsssiyA=(7|g?b9b^CD&J4nb;(#7)Mba`yt z1+I#s6m9C-`rMtZAjP_$aITsSjF3 z>5Y{g{hEX7??nR(+%Bo*`b;YL5Xt@rbObV?F7z2ykyD%4?O@x8eR7SuHR!Vp9)};A za(b*T6`dGlkkJ;TUK2M8>VP! zbfR3_LNweYo>R1kPL%1wS-DS(*Sp4SGY^8xQd)dn<&Q2osangftNmAcdhGFG(8P`K z3HN-({z_YcD*z$tIv?H+k3tG6NJGm9I--rw{~*85yZ3g}!J|Dfu{$O%iO(+U5U`&t z=t|%}a(xYHUeh=eQ*p315oCZV8YdwYfPI8L>flt;p6iB@e9w?!uu5J?YkkvU@`eDa<&P0=XNc0(@fa!nJmUmtiD8~l%aFvftOAI1`{CTjwD*h&hsbHIZCA(=|eJ1EJ z4*%W9fuA<2)WOX654!y}t`a?K0*%&1ZHe*E58WXFHLXc;>BO$<7IkYy*)(G$dZ!tZ zRJ2$zy@r|WHHAZ{Z*Vs4)3rGY4v-Vip${7|2Blm*sl05OV70XKD^oF&`B{&y2s?0Q zDrkmTD7%)PrCv2pKjTDwri?unF7-x2FO=V%m5KgmQTXrB-q^|D)Mg>= zL%P#PN2lj(W;>!uj>4a+&j^ZA_x-->1sKEEA_Ayy_e#5+2gH~!qQ)BrD$6-f^Jl>cNUi%G1;Md&fP50pGv|sg6RL9yD6?6 zHtj2jAJ9=k07Fd0n!w~r>Si!hBo%4J(Zo81jtKEAn1r#4@06c3Zt7l_!r zZ$73-H#LE#+vup-=b3Au#bpI%0+wx$zn7$3_0`b>3uHqZ7cA;+!o$)_8XEot#S~*d zAGF3Ny=`9{xyA%g4J1Z}|7~=%oy#ZxcxZyD3*3&Dpy2rH5Zy3|U|cz+1pd)_?;k}_ zZ?+;1%j6#JlKyz2V#{R@+jFc|N=i?7L1f40R8dWok7C2+S38#*bG4+ZtfQF{H|_l_ z|3gDWMOs?hI%l}Y#|T$3>$^0re++DU=C7z#AtQ$&QiMdey5D^P79l1Phw)JtVmIj}h>?^OZEaK$*) zp(16)W?E{iL2Zv=oeJ;leVtHXrhwWr`OOp#w6GXw8P}rd2-zXkFn<=~r|U!TjT8+t zD7!T#WtB2tCL~+{hd^*zX9a{Z^6Wdd~ZQ7I%l z_hPLt9x*R{?N^hM7#%RX0n#+)EQHu=>;LG}gqKf zxQ3axEZypMRR@(ra^uA3%Di!fR6B{4QRG z#!zn!z0|Nc71A_$EWMt&$qL_~1LESso>VTTjQ(WN_qj|<7O9{~!uCsWMRW2rR(Cgr z)xZDv9r`Ocqo9CAIB9iF>!yt8@spWN%;DU(#aR&Y6a7jX-5a91eBg`)5C9BDt55{V)|3>np;m!n9ofjqP}$pq_ChGWw$f1a!^= z%llZNG5K4)$ZB&PUlzYoJ+U&PIp64e=6rQ5AT#_52K=V&))W zYDpne+;n8Xgj7&S=i=EGo1T_DIFYm%(MtdV>hlnGnYrhnQCo9-6TDdr!y-_6=d&4b z3z?eDr;S28_f?=wG@75Y4=1#56~Vwf$t6?p*0_uFdG9zqRdkVFmhMEM^}Fx_Ffb@JcSga@LC+K^!qa>M^%YXu<_in!}mfUe#--v`=R44*%Yp5;24u> z^xRKOpAJ~m_G5%f*{j=;Ls@LPWWeH^#Rd19K&Xh5IhD4yP6UadnnCcYbQptm!PAp0 z{N&IyShxN0c;_*-JPw=6dGHYbqbQ}{Qth-x;y@(bW%>y(kNxE8Ms{B~5ob;UEg~xn z8llg5ywS8OBo_Thy(HezZ}T9&I7Yz3j>=g}d0rqf z^{f9l#@rE5pZELx@-p+`LGq8-=&?!u%7GR9Sg4q0AJ@EYEmq$xTiba`-KeJgLInrg zg%9)#16`=<4wiWWI;#7&)$wC&vdsPnblGv3OJ;H4+pgMBKe>Mpz5kCnA4LbPy8qyQ zjxhd({d{|}y%`9ZtjPW4=a+1d;g1xI!0FmJ!lNm{mD_n3=*l%!J4lN7>fV%^_r8^Za>PdT^VQe!y+R)Afb2X zC;MZ8gxL~rwy^nIt%4Epl5Pw6@A;a;A9fkpv}Zgw<h4?nRNa4f{s4;V)xCPI zG3R{7GoCSq<8j3?k;(zWS%h&7IacNaIu6t`@6X6BwH~*;hPYbmx`sk9$3x5K@haJI zIMapPfVZCj=>7ZaJ0#_UY@7<<60%byO8_m@90kdfI%e#tTW;lB7dm5SA5Tw&4p z-9WxFiEY3M1I*#?c=0~;GaW5$^nQ+1UAZ7H=d9VUU!dIBE`)hhq4Kk)>AUf2a$Dqa z_{N+oqm!V__Oi)m_o+HdjUGD_1@`fT%}#FT8^-m{lvOE`^QxWf5lK5W9%j~Ef3966 zi5(w2QPOVx!o>rO_#166`d19M7zby{M?Qvs$kR6LWcGXYeYwDwP2ZbPbNa7@qGZFN zxzr?*oSKn=LHg5YF_n+P;!Os_-EUAs#CTty7C^s7%G)qd^VdQvs)aQUJq_cD7-Ot8 ziZ#U(dHf+$HzhLgXgw$?uF@#lc=KA7W;6$_@}&7vYFzVbVSe31xbC*nQs<4oC1Hp* zc_|CijGK{lX80oWTn~D$^t~#Doo!TWUZEj<&tl*CaLi7`sDm(5ITj@iBEoSjrsdSP z?Swg4X2I$SG{}(UsWJVSf3+~`$dd^P*10k)Iq#VMxIy3yQ(CXdsf6-{E-C+5Bbe}7 z{17b11!HN0v$^61d6(Yqvo&7oX3ESpc;FVAixGXzfRn10gf_XIuWDDXyvxk7cdtmY zVj{iSF)#7@5>sUQ?Sq7*>jQ6`(bspf=%Yua7dJ!m>l1{oWNT$$HKlMhGwtq~ zn&-wta@!LMC5E`ua@%%2hdnbxR|@;ex2%Ur8<&@F)zT}f6lDLA}_zv4n4;x(nS`;x)PKbcH%N|<}f1k`%q+KOs$`t@i6G|Mq zE?J(B51D5U+z!i@r25b}+hNB*@E;ULN(xcYSMLiT%{@^Kdn>9HrUE20a7a%`oo;agqgk3}Yd zdKD>-PFOu)zxY2@FY9bchp8Fkyp3xOG0nAm+VVX>|A#C$Ub*i=ub&D%WSjVNU0Mr) zONE?QKIeyIFv0ulzHU`vf9GdHk^zoLTRme6r8;Kr^ZikEcIXGaH~KnfCxlOFyvF+J zSfclGyR#@dq=JcNT2@C=52hbAA=vFUDWQ6VwRbsAP9{~9_RC{;qmTyk{z2NBbXUVn zy4SNvJRe6_;H}-Gu-8(cF;25ENo`ZV|9`It*^9p(71TX}vU>Nm1ZkEo8GC(kb?V#N z7HH_Bev?=OHL^#m^eGRocDU{PkxOjs)#v(1-49zI06)WD!@%tPqNtmV6XzjO?``J2 zfWS$GO)kp7@z4}#jPa*}4GOtB4>1w>#QX(|=BnHlTEc$xx>e&ghboNFc!v>>az{5To9LvEff6jCR>w^Zs0=++By2t>E{)11$0sFe zD(I*@G~bC66|Wrzm2#o(qtsoL*ERgw)V%F^o)p%^j<|yU+Rg(SO`MJCeSaF>HPZR@ z`KRZ3%p201)6pqed>_S2ybteuIJ|gUy(QN(!~Qhf7PM!DS=WUXVIrPfBrg}K+VoSM zt9;SXnJVOlM#1h>6pNB6U!-yzbMlt@CUh>d7ElCRM#;h8S~udxP~}t*OerEtQn^NYYi1)B4?FR5s7Iw^Z$^>uW=cm#Yl+ zwqA~Uf)JddYL{&TLx#hB^e!f_THnT=XV-j_g2BA!d09WHPc%=fT+tZ!wmlunpS3e1 z^{A)4Tlqt2gufy#geT2n)r^ISLG<6LEru-p63RXbF|=lqg!SwwqJ66Su1E6Fdj<{U z+5Xx1Q-iGnJbvkTfj}0)&^Sf?i_an?*Z!u&dFa`In&mO2s2^mu^4f(0eR^AHa5kvh zazsoet)fTs&|;x00j^xP@MlnlZ`Z;-n|M`ge0XvKeAuQN1Lbp>BW*8~m;*keU8&sY z4pC_WNsZy;tKU>^g)kbu;$JaFLj;O8eivs7pK=tjBhL|en*glCRA~B!vzJ5zmOak@ zHN=U|SqT9Fa-5`1mu1R1W*;ycL6EoQ-{CnkNZlM5`F7UMVrapK7yrix+-@dwZoQ%I zd`b&afU^hkI16Xyu5Qp9?Uw?-cqwB{TiT~iCfjx93N4zaeKy!WDIWLOgH zVjT;Yo*^rom&LO)bsofn`9gRt6uPql!q|h57Pz*)c@q8j@G3K?0QpL*(&&6dcU?Ha zMVDthP zxY{ssV0n{70pQzfkqgH0gMu{~7PZhY(JS>uxzqlnpD1ocJm8O=sslj02DjdLT9!YQ z8A#uz%^aJy2(GOxem<=Hm7X<|)waaG-}encyf*lV%c?IYaT*6=e&0I`C>(u9>mf=E zxkCt2#Hk+Y>V2wQ8aS-7MjXwy51pNU)%Td43)OGzEm@e+L`85WM2ob$ zu(8lye3X#K-$~?wl6=kdAtiHpENQ;{lW4zmg-2+#3E`6AD?SUXP1&jHmkA823%}La z3phw?CZklH%^SZK$~n*LdLKP$z4T?${7UO+21!N2WXXEC(~1@et+X$_+_T-e*rp}J zgvIRs&x2zNGy9D{4#vlw(vm^(QY`QAqUkbix(#IFzs>aoLo%oiG@KZV=QJ_T5 z#Y;KY1j7MveO=w>MkJrzm7CBRw;akpCC`_)l&(##29Zg(T2qp#nMI1YsUG&DcOVsP zD2#_nyR2&=#JUF(I`G43p2Y`A{!?>Wq|ul;ix+Clk9x2J&A@mxG|=)iW$*>031X{h zZS0e9Zho_|L~V|H*YX_n!SL%@47!3nYqTy-DF=?B04iem28Wsa(HrxhbZ#COJx1h| z23t*EhUtMOE9=c6O=s+i1Anwofr_{-Qn9CP_It_do(lK)#&#;4ye>tE4^Tw>IJs@Bl{SIOBAhM)fYTe0T<$8Y+F z)Ya7yw!Y$|GDW5(L-j?lS@mwct?Ge{OKZrWHQ)9!yHs&@uz;rr0x7h&^tpB9n_JNoF7se<)ooeHd zsQf#>K67LOT4pY_SU6yz3Z*B1O=Q{ZU$|AKw;1` z87L)7WWj|S>y)`!BNFs^6xH4H?HIVrtQrK&9eSN4O+gIgt@foW|&gV8sO86bt zE`NQZM+HhFMUg()Z`8|=g^veGjKq(s51px22UzPjv0unv^Hqo~cz16KBgfV~57225 zP-!VlPTOp;JtHfwWgyBM+BmI4a=btLOn@fYnFC*(y48n7JX03y%r4;(btM1`wx1eK zPhgv`q1P?Rt2GT`i6_s01p2=7^e@;hy_CEdM9`UkNQ&#n3!co^u z8PkSat|rYLN`hV~5$$3&OB%^VluPemMjn3$T_N%w9?)s&%l9W0j_y<(csT!TzEQP& zl~a)T{#oGVreb0AcnE}8z2Z6``0S3MxTd@J7_tzyPd_)YCV%ckTKf}|A=W!f%+WM-jFc7BVX|iXJRL@6` zKIcuz#RoSJA@3&C*d++GtR=q*{H`Mz6!K}r#or9YDUmnb-&Q-us0PE50Xo&zQtvuZ zn)etMEPxLnXc!O@|e@NR*NbmQ5WM>if4+<@hu{@ta%zWz_i)1Rv| zVTF8GOZjX-MimWZv*NdJbHJ-XlDyVpnw_GZ)UmRO<_xJbz^BMF=fgK^DtIn{< z%AM&etNP+S?rhZ?Y=ugmU#MxUV#r}GozzK5W*b7Tm=By1ID($nbsq{L<)p$E!}nx- zBuPVRY-6ma$29|{Y1$i&3N}?zVR4GWBrVNB+{pw^OwnPxlN)H_in5xN9ISU2^WwBW zo7_hicqzLVjHj)eA6S%uFZv<-H!rXZ^Iq0fJ31s>YffiI>ryli8_o_U&(|WD&qQoz z#Kt5h#w8}EDt1PbudJu6)SNwEdZ`vouDj@b+`l$+oy#R#vS2Hna|SA$LqeO*D%DP2 zu;1fMYsQIrOJrwt-)&!Cyl){@uXKEVTtc_|Bfybmcb{GA@el&{8|q|PUh#^)_<(?% z*!Jq0ikZKMzQlKm1BtW zwMO&Vz3YR6SwV#eo`*(vQO|+zwA}bL6lI#rdPrui(#%*H7rk+GJ>`%VFstMRqBI~RD?B-;%aA8-a}z^~(4x^PvwUBVCKk{S2Zl?cKO=!j9AEQMx3^Tyx?V_R3=)GzSO_mp~!u0#&S40;cC z1p$)<2y4ILj=%J|axu!hNky!3)RxgBRe){F$UoAtw+83ShqVLJ0Qkd`abz|zU|#dMZT zcXCVovRk9~^4lhR3|az#$eO9UINECbyGkAYmx(&_cO7woRttikIXj^}u)87glfo}|>Z!6?L5r54Q36$QX1|I6~ce}r>PD#=7@bJ)j z8iZ(wK!F#zywcLNw6xOv{QRP#LFTxYEkRHl(~C5@XX%(3ML3Vd=O{)=Ql|8>otq2L zy@ngtKnZUt>!M4_LFbEZjvj}pFy^d0{le)5PFu~&)=Z=BaP5KV?wVx2Uxl+hrF1vW zsPsY*!%B;-Hr|ZwN5-zVvj5M+P()N@b&IO@T1{HbdqV5SNN(7GD)B%S!NIOYdaRS- zbae52cv2B=QO8>}37xe2J)1z=vxwnw^y3rKmmoP#uwhJ04E%H7($b@`OmQZyy02zt z1u$&1#THLKmcN6Q^$)Ob{r>%XYN{qZ{ez_Bo1X-rWrAg4T2PbfxeL@}9054esxfg zk`9%yZ83UX91IHz z!sW8ka&T~fws_|9@v%0=@j+w?C;V=~=1RJhty(T0jjcEDx);$Rh6iXxUG(1;3 zgX0;s=Qayk%vA#! z;G)vf#r-7DKp!0aiN*l&tC*O&rmVOL%V_x<`<>-P6Zw-N`hotc#PaFKEY&`5uO3tx zlU}k28RI>t=qQ0u%Gt=rH94_)_%q8ruZIlMJaYl>z29*igQB$$Ni2OE22%1ow0+)o z4&Cb)@m=A8!#efWTl92}0c9{e&<9Zi1B1GT27Cg7TJy0_N=oavIJuL64kzlp!}T#0 z3yf@b)?quUA~n^i(r7I@MjdQvXliO=!tH#xaDIMHOHTqyck4*t1bGC=n`|(r|31=j zXS&k5P38?WmV*WfV0tiBN+QHXV(&?;-ZdX`rEcQ(S|uwK_F5rZ)_2AfBwQL>HHD1mESt_tN!Z2M9F~%$Gr^ zIcJXKB zi3SAC>{C4;I6<1Q9|}JIl3`0mlp#whh-c*p9g1jiJ@~Ux={4PZSc*(0lwu;E;BXNY zXuRA}r^TaJ3BmBHwN^|P7s*&pcCRJmh8-KWq*$GK=^LY17o4a^e6`0o)NXV-DSGsu zKA7H5Hu+6=NNZBk(M>=yG`>9Ls8-uEmLO^2)*CF{_}GG#JODV}n z&plAVhM5@|8Z{QC-wc$Z;(JVIqB1gCazDbWs-E3m0ES_v_bWHod95260|Ud)6690b zv2P2ajw|YV+i0ZzxG#`Q{$8v;d;C_kInL@JWpkU`V(Ky!kFX?&Vs+|ZVfNizkLCBZ z^j~BMNO)88P7_C(B~;$*4};@oa~ilujbB&GS~|2)v#OH>D&rZ2|6&S)=1V&Q4%3~n zvhO2qTO2jX>`Hq-VVK5Sao(*~hwLRhr{dC`n*q8vThP1-{xNY`{Jz3Bbl7RbdxFM~ zzR31-SwtFDD+_esAZh+ew+Lsk`MyGdtCG}N`XTq(O&`_lA*Z*k*GET>#I1uE5`sI7 zd@g(KSgNkgM#YPjwnnJKKSEb#>Cq%sWs4SU6y49Aj@DZC_A$RNOe17_FO`t*KpwjS zZD&O@P^}Cw=j->E99?w#ykT2cqsryDGviT`?sBwX$pS8&?l0! z$`~Eee{f+rUreYWAD%aCzd&*4L>ko(no*_Frb@G_vWv__q++6B%vB+hCPtGV>j>8e zlOsVZb85fjk8t*#>C23lIM3aANok_nJG56xf(N){IpQfuzfqtEbiDsIKGMkhGQoO@ z`Hznq$+Sv#aq!piI~*PtL`f14F!{}(ZSxAA9$h7CItE%kB7tYcVXbK(oPW( z(R`X&+JfxO71s3y95@0g>;%T`eLb3XonZ4GlAOqw4q)Z?#|I*FLLV`7B-gJx1yF5(K3W%!$`fJc1C%)ah4~Vr&z3_ zPXe1e=;@sL6K;6L0s*DizT3ArHn=u?vo$N9xBm=x{%b~Lrs^C{5MjG z)me&x5{{H=ud5}2MlH7i_~I1j3@ug9M>#8#UW}Un>~?3eO^dn7AuGZDR_mE{SCc&j z_eFQ}$C(RiocO{YBz}go0JxHjXJpVR|MYw4K-`(?Xtl!PVD4;dM9BRztjiGh8R*N8 zC!$J-Ks`o~t%gL9czdh9qdM}N2C1$|I_&bE_HpN=D_Pht;*r3LR`Dk%d{R>XYs*0C zJ#}406|uhb$`0#WCK3U!p^!|blyH-;Y;_R&4tdrcLA@Klmm@MM^{Q&WzKgP;jfsBh zxKbRSjU6zy+PNfIkP6mYKj-BhPM%~rs~Q=y5JA6YiH?cs2`8u>T5Ptt+%MRUGz{Gw zoUJe#x;|OcZShdY5_k?$;(DS?k1^Rbc{DlB7GDTXt$(&HqX*!V`YzLFI!Ygt$c z31zodxWB`TMgVj!*oOm;{N+=bh;#K?M>s!KxLL(*P#tX8~y{77c z7j}}K%o{L_j><)?Zz|Kb!o?Hc}`vFRIFX9qo=2LdU~onP6m*C z|4@UZ{R2|6vXUc=QBl-fYzzWqY_d`!8J(e;oM}ziiEnWnul!&+XJ6pN9Ob?Rk(<_X zGAv+c7(cfDc0ARgq#&oHpfp^s6p8kWu@9IZe)BmNK=7xP^{*x8FeXmU{J=(rGL#M$8Z{KhBTd}NR=9u>3xq8r_+m_Ey(XdpD>^E!+$x( zX!bi7p6SlcPCcngbQ-Ykz!1f2E1x83?^H2bGg=b*KU2G-#X5t*sZ_#>rxY23xz8ZJ zc;S2;ipD|GjSVg{qrrh`!TCi-&-w!m)pd0NNDb>36H_s4t&o@Zgk4^ooprph1Nn^T zi33&Gqfo|Teqd;ikmE!WyGs$R|HhgtT@;#%Eu_Z`8L_tIOKC4is@kbNm(|1&L<|0e zVtp1#7u!jcz_R(l_Dz^pE=7+vuU#Q z+?NpXpx^&c49g97qfueA5ODb&OPoYV8j*j0>Y7pv84$mVqX8FMPMHoClpjpsl3xBW znjQ333Db72b-8icZb$PaA9rh+eop7VDrjG^6xGzUfIyIMdw$!iTM#6I#H z3bmqfayCX1SJ%{Zx0rxHXg|jLC$QloY@bC1)sp&Y~$1Oesr=gQKAOcp1UQ zmY70FMn*sGetzV6`;4hG)E*47rf07PD-|L$_Y zy{Os#0T;{tq0Ct6pkWAC1G}AHZGj{p#`1Jb672!d?9+@@ z#>%Rs%KR@uU`>ws8h~_Xj*f*bdmaMKetv$uj=M^p)4-0zgHjXv)H5KjB_yUs4PQbt zBR4)O>K*3m!WMIoPa2_<2tYA|^19D2E`YbF|MPiH>*)<%czWI!9N~kOsRA8=342Fm zdxGeT!z6?l`L%V8R2bCCjfaOf@6`-UR6Ra^R+h-@tnzei5$C{87uJebbT;LG8+(kR_}`FH0Nc0|z)U(bUio3-|3;eSP6T6lLumPR=d3=WbVr z#X26^Rv^$d2w-VIKA10bn)urqbQ?K~fnK96F%=aRKYnz)7iy&^NG{mt8tC4PN=y6k z<3~uy7v;-0AUTG2z!USqq<*xmiTrG*9V zOK3cBw*Q^jEFVMlFe5l6@b~Y}6&1&0K7NF+G*-P{|0Nu(7o@k{8GM?iw7uy>|1RBj zt(%932kQ`ssp0rMO%3ohlfhtcV4!%RN-PSdR0=!EG8^uQ{P1~0><;=$xwaLWTd1V5_@`kd%>9T5KZviyLYRZ`Mox6!{fk_JUa-mu|rG@o?4IM$o$i(z+T&(HHXVdvnm za~M3mkoG$M8W{3AzQ_G7F);x_qs1(1(a=i}wGi;Us43m5^tEas_l@WK(CT>0()Sot z3_|EBfS%m_S-}UdiN5+N?1m7#5MFFJ?3-3<+LlC5Ps5FOZ!AS!9mk;$TNw9FbY`3v zNw85P-VnP2$9bLyN4EP#oskT`%FtV`0t%*5y*bMYW0T>=&CC&cyt)1l$}%dVGj`hr zl6}#&S3zkcX%jMeKBYm*eHW675PL;=u0? zcD*pGiB}zKa1`B}GIVz}e1IJ#Z*Oz0tO%y^MP8m`)|iw-=V8D?5eF`Q?Qdpg_Qwl2 zGA9v%nQrPa=bswCa%t;>gT;qee1^eBCPVigoLKWS@ z&cU_|&i5b7wy3Q}%Qf?#oW#p;qNePx*DHK_@i@~0TvoliQKY2Norgr0jWott&L)G4 z&G#`X=OoQSDxXBiUu$!@B*gFT+oO#E+5XfMdLujFGiyO3SM3x(IMF420- zrrSagyVM)0UB*)ccJ5IHJnRT%-wke=jZ7{PbP(Q~L{DsF(d#lJikXR(5sIy!&#SFAWXN!@~oxT>&kC{&)fB z!X&CSz=}PES-h~3fE2cvIDLbST}NCl%DdBrMrU1QmZ|)63RepiBXE|ThT*I=bPJ&X z;Utsc4T^nF+C|E&sBfCR(~uHEdcz?Atk z#rO_tj&sq#uLgUkVLoW5=hpqtf#k~CD|bIfEt~H0dP?E;jd{wny}HZgb}zOtZnp?= z2$H;t^0%N~ubZL{n3oWa7PCnbHJwxj(67 zMwE;7rByzt*YX*)fn!qR@JK^xbbiN3?@q729Sf>{BTM3ZdgB~y7}J0I?D<=?4rX3n zUJ-V?_0IrvH8G(?9C@`nSy)$BH#4I~3bgiUmZP4So*o$(P*hb_m6zAU6$8-AoIe~$ zNtNFe2RL8p0`zbN({xX{IH`6A4+V4{1*P?rrRQg5Y!9d9e!d^`ypnIk%#kf{oE+bp z3Uv@`T-;W-zn#K*TR!{IKJZ9ccYSB>yST{eb_a&h!o6i7jCpZ;akvt-CnWfxj8~G? z(g>opKw}g*?f_M_rrKxy4pwXiYvi*4EK*O6%Y!{1r?^b51e5qy`9amS$ZGG8HSFw2 zjjs-th11@p$&CExLNPw|`bW+>SDL2}!ttMUH|Zvi1nif#r_7Mhg2%~*Q@L-hT2V8{ zMP9dKcjU+tO?-gRSQ&iWJr=z>isI^fz7QXI3R&+bTZDih!Ej`RY+N8;;u@5Fr}N8rU|}1kd63W2S-KjF#Hx zwWqYKH;y*sBYLXb`)q%xJG&LIiii*nxg9Zekmu%5QhTIoA`0>N{9#7Q1Xe>qK|{ai zkNN#X2F;pSCs7cH<8PFRCge|hQ>2!c{!)JGOJNam3Es+Y`LZ8-c)SmITJ&p_TcGWh zToobl(O;rHYOmMtH#c`YO#*-H7B50R>gwwEa}YOLjuc_D76@S9rk$MV&M5zMF^Wi* zaV5Dt6Wnhu@+R_asrE$R2pFnwopwB1Bg?80`p8`2dv|FXos_fZZ zC{ifKH;m8Xj|VuUDH^v83!QZ@5XD8Mvzd8z7RRG{)+Eu_FuFPw%ez#I4$J{Lz*i>)7H4gP6v6Aa|x6$kE@~*_MHPiT& zhStXQS)z*+K*S_}BWiSRc3hRrxWpRx(@5oS|*~Uxft< zjlVAPi1BM5-_S_VYlZ5uiL+0}>b-cGIzJiD9~lO7duv$7FBt!R^%UURulWOl>MBC| zY5Qp_O7E_8(*;{2O-aQqzNAl6IKmRw<_Js+@+K|$ELJ18%>l(Bnz zF}LU=jIK--8}R7t>I$$Ilk&5~#Er{DI;m^z?({1hb15A!&hantW?H5OMiEM(6lD!? zxnU-m6dm$K)75b$YV{pihzI8z+^;E#cyZT7MJa=W`)T34_&K@A6tjLMZEVdo{t9SH zX|Ofky6@6V#9GIR`k->`bOI887e&)MVC)(WyBur1oQ?(%*G)cZq!dZ(O}QE=`0a`2 z)e#NBatkZ9Al1>v5D`X>3*~3>=-LsLvHR>|C1pP%>RNMk(OKrNrJrfup|`Kah0(G;S68Q{r?;Hnu%q?>N}8SALBZi>^s*uu z31z)0qsRI8SFO#u^eRX3bnm|f1!>ODe0a!AXy0gAEDk~IMa(PPw#3Rf&z&B5xmb60 zeN$L8h*|C?Q{tCL4r!&+QjuwMoqi5ryY%nhNiPcxm)-km+imIzB{HsR1`4?lx-@zP zgjg1x+DGH0KY`Wz`}-7t9ZB;p<(-6=*RP@`Lg9*oNB)}kldc-$8aw@)a9i}Li?+vt z99SqU1^3hLL-r6r*V?c!S1eAi#k@1yTWD55@fh9IJuVxFzc%QZpFvM1t0PA-_ug7qsrP|_TB(*s?#xx z!`R5d=oJtQ(W*7F5P8@91Nw+ezaJ*kLYbiMx*hj(Hzipf30CWFAoLSC`kj z)$HahBGqPl1c|+r5xS>WsV5ALW$55S<3_!nUbi%h7Ogquww8li5m9-nY1sOu7JHMa z#CIcgm7JllX30jc2B2a|junpTdp9q)zQ+jcdrqU7K{RAXMbDJnd)33!nbP=Y%Azu| zx{6&!C!6KP@FEkvza1xTr+go~C6jjWw%HJEgTECFyeE~g^T<$ZZ4`_T!epR2Y-F(| zK|>3+s<(_WW$q4=6bTa(pI&=k(si&2AC_FIXQZ-sz{TldXerEQDO@?lV2~lTS^S2u zwzhPkC+;(Okp)z{oGQ?>N?b|)c>mxx-pTvLD$ciP!KW3cCUOUCKLm7LqYTR{jn>4fg>EVWq~9d584RzL@Rjl;gUkDP0K2_A=Qc7j`U{U_ibUw& z2Ly3zPzHoZIRDV6vvIWy=!yT;o{|!+_?nf~e#ux1B~bX=7{bqb>h3)z=)TqP8a>t* zJJgZ%7Bm?3wa(_1mYN!&=hBfaQK0&;K+>x?7o_qHwNJ3}FEns1Z(7xBFZUOd(I{jo zYTmW_p!L4S6|0s;sEk)F6$uUwnmIR*Co!$sJKl-^SkKkuq1Z6zAb*{u%z1D(X3yGO zqg_fFv8emv%1rZVe8F49g$ZI_U68K?nti3oxw6!vNI3uZeK^#-=RqWqs~X3 zae{4X(BVNc0>CwY2RLB0k;Wh+kLqX9XxVU&|EAy!bEjV9{-8cFienVugnessyo1M^}I6rf`phf$II^F>M#jr`z5iHNF)EdCGb(2QNOq% zy(C}GVFY?LRf!W>N~~3JWwipMCM(R$Od@e&;})b~;ZYRHFc#E|%&D>=G$9}E?;jlO zcZ*>%s&Yd*p!D{fjuz%h%QncN@Ph~tzD-j7-P6%a(<`sAeV?aFGeV*>({iyN8p2ps z63e>W$Xsx5#AE9ueb2UR`eiP3U=L^CeX{FUDYo!}TV~E>&?2|z(8l()xVSc0i~WAF zKGP?UdbceMIt^(dI5}UM4cG4`9!;qJnhu=$_3yo z-=pX4;Z1lBq}PxJ7G@cKm6LpUqnd_6)lE^JRLvpe^2p$}$813#8bm+*xVH-BlyiBe6Iyl=$1@%W)lKzhtV5!HP)x;w0Seq`T zlv*Q9qc!i6N;!lks%%W-FG5XiqG(n%-?lu-Y~( zN?3$&5v?(jid(kax*|(1_kN~Xr!x6CI4F0v%xmlrua)78K{Y*0mdAD18~lCM$sq~1 z$qsM&#GA%LZ|`^L^QkbdRdlqT?JMCxkx=mPu7D8l4|y(2A%neHtRLpy53+-KR}XvN zBTM#HO;c5xG4qq0aQPi2^hDE;ES%f|KG)RrBZRg=uZL4tF;v^S@0aFk*C|UrI;?kD z!%P7~ZyWKMtul=P$dsx9P#!U)HK^FwjP!sOWcGd59;cyHi}rxlO9yDaYe)>xV49mO zv$Kf+7qSB0dwpW}y#jTh-%D|m<@IaBBeymi!+t?Ah0DWG{|&X%cAB0|)OuHVLt~wjI3#wW0pY5CS{V|1528e$qF2@d72d<0hi5 zD5{@$JuDv0Ko>z>$8bt0(k4^g;v^}*7Fh~mKxRTHq13I|nkYU~Yf`JdEO^B0@q! zz%zdH`gK)R)!TZ2%0Yc%P_u;dO1YdSY)n<39lz=ON9V#}C-VPQ=d%0kFFl(cASwGa z(ztC`vnAte>Ko^s4rmGR@>jRSWn^Mw&Q^ZjIeRPoUnosDegH}nV3<{eg&_g1r@sE0 z6N5%jNC@HDYE=@aG9IfR3dsYxkvBPCG76DGP35@Nl!cZZ>p(>@Jp(c_^44%_G6bUI zzX5ovlK(P;7y}jX@bFVpQ?RQe@3*+yIXT@n_1}>RXy+o@7E^JJtco>IpThyV5gwc6 zw!1rz{W2g%gq83Qbp}^XPk@CrSz#!NL6-TwiC?B802Av%0GYO~A!)jFq{2e+%^LxM zW`L1PNJ#ja3>Z&c;YCfX=`l_*(%*Xe!OjH@Vq_$odk#%6!b2+ zfeHE$9u34ncX#m#3AF&t!^+u!;IGx#p}|jTV7&yui73&kM{oQI2l9CS5Ao0O#m%iD zFtBH_(wGOC;GxY5pS{leaA7!=ud<@T>nmW#lNdk>1LyMF)_CLJ$yVqc=LXS6@p zUIpTf=Q5_{f`VH(*mF>x)j!4yTn;P-p0OF-!7Pqw7*<6VbMy-)+6D^9%OHdJA8igP>0*yN%_2|LezfK0=VYx`h-chr6`2>8K?m9 ziPw$#PPKvtEWub{>2iesPrj~V__?3;KRREcznRxRh=EN z_6r{ZL~?9wY;-Ctpbta-Pag!5!)WrlzXew9qWeXLVs5|taGJNm=U3rGlAqq6++^j5 zwcd;6%8pl=Vr^ef&=l%415XA>e7r?Agab`{{tw6fpWscnon60sgxu#Y%~)uIsCa~& zg+)Iy!a*2V-#?&#@OMhk}`MlevO}CMId# z`x+QZi28?xQIlq*3;N+42CXXEd)a0^?bq%X1Fa!AV1iJ&2xR_*s zC4XLD5%}8B(4hZtPg*6h$7E`X@hl;1V)|*1=W#U@Vs{nIW{qSqmuux;{HUdsQc}WL z`$>Y2DzK;+`p6-WdU__I0uG|)YrGq>LB{}Yacy-~qo$Js9s~+vb_X=!pmZZ@nYjwA zFLP-&PC2XAKTEWJg;bFxN2TZH;=rs<0mEVXyTt()SXh{x8Ifjuf1V_$JvRvi2dw`y zadOmBQc{D{llS15gM*z;#MP4^!@jYf#Qb@qvo~&Th=_=eyAwBHfDbhLn|1(1p3=EZ zLtEdQM&b#JN4$*`|Hi_@8y&AIN)WZZ1Jt*F<=_6=85txe4CLGV)zq%J4GYF-Rrq5h zV>EcD9QaNC#@m=1M_lgFs}y#Mv^Gws&&`^)=sis*x9vMvrJ~d5wW$_Mb3Ob0I|IQx z;jDfR6pxYcUt~U`d(~C0NA3S{4DtLwNeX!dTYaIG}VfAhX9u;j_YncI_^`_42-{r81r= zj$%#*UowIW_pzSQcYHr-0k%maovg`Wca}(5sr2R_2&Hcr+}x?i$YMN)VePQ{D$z&} zkIb9ztF1Q3R1g>Ro2l_eK7`#@2RW$4KWYW@ex073{TmQdx{NraSl=+cvRbtZHZ&c? zJw3SrM@&w9{`j7fl9CBk7toZ)wkfxOmf8GSdQ_}ht+|Z`%5Lrclw>2(OCl*IB*sY^ z>e?X|P;hRX564vK!{MB>uEYeN)fj$6*5cGnsVL0lM?=dTt%;G3a{iig@D2;m(Fl{Y z-fV~6&2rS&p0_b9k%7_hix6Q zR#}eYnfm@j(=$M!j%SoaLOLa478b(@FU}7aHMO;ySCxUaCw2Xe1SHsP1YK(GW-CtJ zD}uO@06{AfcgMXuiJB=m7*g6v&lrL!J=#z1)AY>y?)2lfaK>Rc3xq3@+V$SGl-1cy z-6p!nO}obTZx~wunKC(xc+R6yGk3a62ruk5D>OAmHN`LJ^ZDuKif9ZG?3}Ki+Q6-g#JBp}$6_@NaY8h{SM4${MU5t4PxPvRhfuN(Xmlp~O3V_P7O7Vd; z*-nK5x{l&Lxy!*aMpvev*Ysev)QY}LgL#a+2(=}+Iua_I5j%;771)=OcU>9kE>`fB z+=PV-_S8zVxz%`Y&ARl>kAG>F94&JdzU=GxjD}XegG$mYuj=L`ROAyxeI@a^D#^bd zC2)r8>Z*<&72WaHbM5j%l4w`tQ2o3o(a#w@LpLW9hguqQGPr|8C2!zr-@=m9|L{{G%RJbb`CqGD%sdz{piSAU%!Jnf%Q2^ z?)j6-7o+E~*-YGJF=8_#R9vRnrS?#r5X0t922Px-ano@!jzhX3jO6S2b$|2uDsx9M zp9|0fML->;)v0GV+=U6!)ubt`xvL!(ZK$}GQZ|aqB5yTwe;rVxk`zfI5*!o)5%g;3 zGB+9|E(fe7|~5`Gu8Vq51S7_Y~z z)e?r3HqsTUA>Dy?xjlcq12IG(&fYQ<6sY{V9_<}U9_{A`)qVC1*r8orT(#L({3tk_A5i^7ekfOfm4|aMrRD zt)U6l+Mk{XhdD|#{jTl-70Zz>T&na?l};bJ`=Xkb)?u(TkVtOj>}`1PsFdc5e5Xs5 zq-1G^{y=~K*2c!ymEY{Tm`a|585i%41l4p2&z>|X;M^*3A0X!u`QV*Ew!xxfy(*gd z?ONi2IC0Ze%4b0)c`?C7{$Yn854MYt*0&v-kIq>q9>sSAls4Du3WEudjX#m2ryT;Spe_I$hGs#3*_nFD7ggQD4WS=p8j3k#V=jLQhY0}WrvCxXqx+gy#lkB3Yx%GM* z{8S!4TlHMi_bE|VTbp}W5vr_gv7VfMww>SMM|YYJ;zZgtG)A%SmKc*}t%Kv_EgVzs zCQOTA1Rvip2V##F7N~lh`&yF7wW1aE-zhE1?hL%6*E7%W4_mxOs|DGdCoN80K1Z$Ad2995vKmu z)&_U5aL?CZ-Uc}%8pViN`x8Hg7GO4h1M1STY1;k^^@xXD#Q%TmB!YrZ@}#TIWD<~_ zBw?GFfPjYRB_I(3f9f>!J8%0$uEX9hPnRBgH|68pCzBRa)j(s>3K+u zC~Tyu{~jC9P)X>w8`REjolI?tQeDF3)~^I>6Ez%W|40{KHzL_01|q(bn@1=L7>hsK z%gHa@LVglQ(_emg0PU3QF<1=2i`-CZ&MsX+1sx@eIf%I!gzfw&FTij?J^q0`etR;5 zeZZ7>kjvTjyyNcuTRG7}!sz1)8b1Mh2DG`^I+59KZm%O!(#@kb{#t4r>4wMg5NyhcZ1A6F_dv8o-T;Y_FHcwClmU zW)h_cwUT25>{QzZZ)=?QC2Hzh;C^xYK=Fnx_rF=&dVtgG2DEPdy+(eSJNX+}A7khM z+*$#y_VghW0MG>lOl!)&CSgd`3yes^^%dbza|zv z0KP5RD;1EOHg$DBzmt|orYVeP4`5}6Pt_q0&ShsN0SLr9U>RPxZ~=g$(X61-u@2kM zFc=`pPD@KGDk@4#W1+r3ULAi%|NcWmGl21QBqt|VSMNzlxnpl{|B{~kTxtTaMJAWe zBjee*8p{iPFyLtcwI(2f1GH<+!)V+8TVP!13~R9FYr&%`RLJ&z1vpNy2-jD8iB?5L zsdXgqpHJXIC+#oixr?@P(Be^#n;rIOc_P|ZADBy$0NG^l7geQFXDs80=fnR}cQfjs(Z2&nQ8TJk^ zrEFewywG!BqXI}MvR6QHDE89xUMS5({b_&E|3fydG{a@h^&InWF#`XjK-g=Q7kH7= z*#oq}JLhl?_%S{drfZ;a`boWjl97xS{yrJ0dgJ=vWE-$E`X##_Ic)ZRi2QTd?}GO) zFxP-g#3(peUFH0W)}nnU!)^fD1KP!?itDF+11(SEKh;KnGoxb9MOwfoBIKvk$TS@6 z);cxr)|Ha{n`f!`k)7;JkTyoE00BC^IP>)4j9jyU^^tGLR8R6B48UIzbf?S?>Cf#s zUeB_X#P!(h*=*(ZiLPP&GgSU}+Ud>xO=9-GW3hy-!`*@XmJ`SI?wMr7mUPsKLrgA{ zYPDnLAl32v#*WWZJ(IDwVPm^2y-x2JHxB7}y8mT3+}E{RYPaQBD0j-m z^{Zxj`Ff9**u%q0Ng8AWXwUb=aDeP0Iugz8*xn=Mz$Dahsh;^y; zpe%?b?Ko*(Bbs>ayW((_yNP=%#__~{q;1z^bbEZ9Kfl;>lZF~s_MVs^kOF*h9@*JnEt1&nL<3X1ELpFah0fnR;O?{p0 zFept-998w4cr=km{lz2o;&xW#%ZSy)>|0-g=OWw(wO7VqDq($ghutaj3o!t3bcu>p zeD~KS5Ory2$P@LqYQBDn#N%Mg36}bGKzWHSKp=qhP)3mNdo{K064S=CJ7R0)_f=A6 zXDQ3|Y()>hcCl+tVbC1n`b^A7imQ4Um)Wd{h*nlVNj5gq9|4yEf(8)KrTNnAaRV!= z{^ItgK;7f)gXSHW#xjsvbElXzh|_zEJvFv1;3jb?O}i?}m{eO-jSu zStzj-hZk3c7;5|^d%QQMOjd22J|^NsLM~Myf#v-i$dOb~X#RMWM$~R(qh)23!{K}L z{DOwL;wQ<2yg^(lGE^GuN~85tQW$CSPI zw447Tla#rt%&^f-zbJ7$+PIl6V0@f4B=N95?>clZUER)j|IZw*XT=EQ^HSH=DsJb+ zZp5C;0Bd3-Vd@E_xnV|4L!KJ9NK0)qe$*x87(Yequ|o4Rij*em2q8xzjV$Z>4X{fc zC!;o)ja_oGX9ONzULZ$6!eix0hB_<6e5Q&-3W=kg$x;~wZ?d&JSAU*+= z@w7`$M%tSTj*0Q8MQDr21nWy?zbRzTcCq&f4jH^kzc2k4*&YKJ4CsZ|Y6#uCQ+|F^ zcvNg)04;I|(w#C?IU@zBnR(HM}^j7)^s0(XV;YW;sgM7>!<}zu94zxw{DQ-j;NNx0+uY$f7;7=GbDECzH1IDve{S-h=^qZR@oqngGbziR>4GdsFn;#K~_}N*`SZ)c4|Fm z@x(a`9{iUfOO#*w@YBp#xDv4W`gh>+DHMD~HOg-GTp3^PV05^!kSQ1J(o@-53Y5|WT9pwn zg?)SuhL8qKh1!uiuF0;(kK{D_(Fg=Eklb3v&>gS`$L`%Zz@P%V?JB?k(LK~Hf#7&l zhl?CmI3GK&FX0@qM;`9(C??2{{qsGvz7|lHalZVz(NE~{9IrJfE1wwA($=02BsbZc zVP%DG_qDexi+OysEcH3%c*(|}gK>q0g%nJpg-2T|3I*<-p1wU&ViUi5t%X~on3r({ ziCJ80Yt*3D6ktOIo?7F@i#8y$XX>len5a#E&Vo9aDc$eb&&2QOixLYrKYtyq)NiYE zcVSv_a&i(7ub(bXV3Mc|z>ruot@hR0!CQs_k(}`kspEJ2Xci*N=&7dqK(^q zp=IU46if+UzGNbna#rZ!|9&?kL_q<)fHCQre$rzOSkCaJ%P)MY?Iw8Qe*a$f z=7ZbH2*Q_wfq^PJhtIgC3K$DqV2gs*96;(j|CU#$x>zN7=R~^LRrY~;^rr`$Uq(I_f`wprr`qE0l3^!TR{pKHb=Oi;#%U?(Vgb_PD6<{}_t! z;$lZx*?{tznw-Z5@^hWk;P_ODSy=SjKK7?OCHycAW3S9x%sOChcXz;(9Y?HEOU|D8 zJmWdQJ}NGbHx)(yPErTOQ!-~~OmCY6-}n;G3)BmkF~Ff@WSYbg2e zEHDBXUl8%?X5fK78VvrB+`&Oh0Ytbv&vNCv#+pB8F(QZ zzt73pVkbJRq=Y_b_a?mEdizaGT}@SacliMWLlzgGW5(oLAlkOln<;nJ&CdDE?#Rzx zt@6)4>QjRI?Ge&vd)Y#x5?G|NO+cfS$RLU*s<{nyal{@nvYYuY0@Mv; zhN1z1fxxCdTx9qyA%QcT4z-XJapNjw!s=K~;Y(TBx4jty+)^MDj}>d1RvQ~z?V>w^ zZ$pZU1+YJieeT(3xW~lANdDW?YcR8rWhbzA8Yo7KR@}28zoI-`l*JX3;L*K|LJ4{l z3X4{jOf6MZC|swHVi4jK6cps)LF`;?RWSW`)WWm4I20pvOU;6E%vgkLC~#j$Cmbf&Emk1O;iwineG-5|u3(1-{(i09t@&a=c4e5rSQnzufujdEV?OwZaBL(E=YiKy0hqA?ZF0$+Ctu1kL zFFx1ox9yQmPE4e~eS4wE*h%`|_bLJF17g}uEcrk}N?3Ak5=wvm_=sD4!aQ1PZhd_{ zfxlOb8p>aq>q>?~p+>!&{FJVz89Mc|ri+tX*dpOX z?XlT+W9s_xPJs?fy$c%L^Cr|2!~)uBA@IuhCfslxh3{#eM!2s#+t-4*!G@F}iv0oXh7RC^qAK6c;2rD|t)%y4e{%IF%l8SJ z-7)t)qnYSgSsewxWx-&ooUtLaYa<%Er|oNUvc%h;pQ$~geQke0n1od3nKeaOB{l~X zi>r9N`-6tU&Z5m^X%*ZU53Nf1_vTC~Zp+!<&~WvFrGG3R(bLh<%_U#oQutGSeDP)O zSL;}AY+=jVtrE_$#|y}Y{v<_psynLe=L>5-eo&Zy+_=sOJoj_nL3Az%<2BQZ6GT; zMJlIrGyY#JUv=~B!e=Vpt%~+3KC7fOV@7EGHppYlz4&qVcXCSJi|aL&){4W4=EVsQ zo&!7wswm3Mv%M?|>{;sx5&q^zmpE=L!GBIq-vpDn3 zOkeVl?h3oHbM7kkI$irfYAFuzQ=FJRJuDQ_6z%LtgOMJeIU z@V_W}I)Cc*4_>xU>*og!sSbWE;NG*;*jVS_R1n&r0}a+ttm!Cef%$oM14A z3<%pgejC+JZTZ)wv-DI@q^`XDJNU^=ubbEnT;RKFe4}dS^IYj<&6=4!o6f83!dapA z!>R_T2{z+j(##@DUu12CddfxDyH|am%~U)EjAvZlGl9s815=|g_IJy5WX`aXruq-! z25Lb%XonA*$;we_M_tN7fp%?s@d>FOGWL-3w^#K$KX{j}wn9ff7V%M?>GnS!dtSwn z(hRC8EB{c}_WYF5h~BkJToAY)1a?Gn8GiQc{^(gR(#J5L@~a zo$m;;s5jH^H6i-M_kFrYsq##BZS!IQPt<6#L^f%K1~)T<@Bv|+17Qz155HAJ6~Z1^ zk>GEQ;(6G!tRcqqBHkILCLu%GdT2YjU)vKqaA^?=#`MQFxep)*3i*}pOj~IqcKKk1 zKZ&*w#K5%bcOm8zswir+Pi}hU&pM5UMJ& z#g3Xf*xm69;_=?SgHW%13TuI>wfsG`_g9QzTJno`W|6MCySED1Zzj=}lRj*u?cf~W z>(%7xdanQ6V+ft+@Ae$>D?@m>2k}H5NU7yKX;G4T zXc=K_Ye%H|!$t8L&_XL_AX(aN?Sb<0VN+nLrQ0ww9xOPMyi`3B;<-+>oJCEox;`fE zD=Jt~+;Y(7r(Trbj~Xth6yh^HEcn{?I0`w1`{ph!xArKKLPI_YiR3bJjDpm{q|D7X zTbq?#ln)bq7RGB`$<8-ToGks#Es)%ezbqcK6eIHO^u7`ZWomvx^AoX%5NnPk$*x`z zGfgFX3(Lt3h1oe)18Ev7H))#n@E3&A&J>f9vN`rie@s5{=rTo=nsgg=SR%~Gq;@q{ zPUJ7C#?PnAv60};c-vPRPxP7vzT^PtlTRm z^cr$S?5>%Uzdd2C>^>c}#+Au`9{(H@T9AQf;>m_NqGo^>11{;AHO z2FnMfpdJ+|!44g7niim*59xf0R;OI4Dc!>e@!nz#S6TChS)0JDZwxiZnmH^nkFWD~ z%n_Ky4{0nG=#Jzgs)@1Y!0w`>Aoy@{ESUUAoYbVD(o4W@5sD8<;yzbYU>%FO6x#nBaK6I;w(w5#{aviV`+9!qThfiKS3DipXHb|SPKR^b zUqq?D9=sk_tzmM~(vZc+SLa#PXIPs{s>w859RJaCw;OlJ+XVvY^cnlLioAHNuQJru zJSSTBA;~AZUjM;|tG|plCy#&aB%~_Pok-3HGUM+b4&J}Q>;5gl4$kH4Tib9emEUZq zf)xuXNgce*r8)U>j1j6Fk*Ezn>BwgB+t0)*8ucy~a}16bB04K+%M~^m$0tmtbX`mI z>CH=6e|lPD7=o4J-s`v>Oz9%g=@sE2Pi*-x9XJEo+L`wjgni1?ols42&kZ5#ia;eJ zgC{_t?|0^T*o6QR#HX!|Bi`hsLCxizg5J?pc*t{SFifj&f8kc2mxzIZ#xnCQN?_SF zYaqj9)``s%XKv>7yNr93E03NUKdq8>9NGniW5O!T9a-?74Yg=w=5` zSWgWD9QErX4}K0$ry!3St~(YWR0|E{BO22Nw9$m2+mYBPoV8nvUeE2Gnq20z6(RV~ zgK{=rA7ow@VC#GXU4(+=si|?L2cV~Z9THkfI6X~-a)7md31O6K_W>6&e>G=+@04yO z^XE;M7ALKv_ie9*rH#HRP1~t{=Q$M4^_MYq*d2O%%{WgXz0$>4+=^_0Lc|e>(-82wl;3u$eUy6=OFyIB2_d=813X*;5k$0mV!%w;@&CCPuu@!|7D+`433*| z|9(?6BbY;bMMd2{*b?On(X1626sVi5IcR$NV>M+dAJN5@WsP4{c*x$}0=2~8C(%l; zW>O|1t^CINL|N;YQ)#_YhrYX;Pa5naLPA>Bzd+cF)G1%;TS+-B-RN3eDU5zJPFpyl z^6-#JzNXuAL20S)f^F*USQu)`5n1#EQh+jBW((d{{k!G|+OifbPn)WU5-VrPTEzP- zy;wVcrOI#uWN9sgR$#tVytq&9B}~0GPu$Z{z}VPy!URSP4fUEAt5bSUB3H0Qs;cil zEl865{`*y1K$btec(r8fi+|7cROi$S+hfsECAAvz@+jma*vPP;?f~4i${}m=J;!BZ zN!&Iz)hh|+TdW7o4*%t$I@2&iK}pSxoN%~`72vM#&~tq!A?&O_b%Jo|{F(t$T^RIM z!coKP{9=5UW5&gLR41}TSsslsKR%{WO1eNOT*h)wmF53Qc354%{cXefaEl_ozclvo z^jJzLW^6EKQ;7Dc*WuU)viiV)M<=x!Wt?xDc+*2J%h~Jo<_AV93UcDWo!y4(E_6GG5bTh4c)KUX6~WlSFf4t#5tK>P@GGWv;$ z_J(d5)p9mC)#g00x3K`nHoA*~4b=HE-mE(VKHT&*_~X5)+xZ%_s_>@Xs}~!)Nmsvf zKW{RY5P;c1B6DrC*zz;qH(-_P>@DX?zQr=}6=M?#!?kouuc!CwTAtWW4CjFCx%?9M zyJSV2GZss&)m4QnKN6nF)XKp(%k(N!s~5*jN0Z<_i*W;EnV})i+dhz}S?P+4wirFM z{tQ6JF30Kn+GLiwb{vqGf5XVylft|lmSxTN8C8{YzJ zt=j_K>AgPkhuQsI@aGr#rdUeP711#f_AK!sXxdTlK|9R&c-M&4#npdx`HlgB=_?9_ z=gG{>3beW8VjJHtYQpOKOP#8v!+otSsv}wK)784yJXIVkJ6s= zIRCR-PDZA44hG1km6H;UecA@hPyK1K>GmJzB*}j`^!(%qIC(E!3i|KYFY;nkjI5Do z{(Zg0-SG^<_CJ#AH3}KZvhjvX0k#Tf2!Q{hRFi68Gy+p6{C1!$xUW zKeNdlY%k8>H_3Dm?Fhu@&u`91GQ-HC$gM~U+6@5MetGsH(GHzB1;)_9k+ zsiwS}V3$Zb`f~2}@$aq=)KV7k8h_~^W~S!d)fWCNYMef{^}spa15DMK#rl5$CjWT^ m&Pf3>!_ae(_5Z(<13zh&h7=oCb&1n&^Fl#QzDV{>!2bd!M-%G+ literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/07-choose-local-number.png b/phoneblock/src/main/webapp/setup-ab/help/07-choose-local-number.png new file mode 100644 index 0000000000000000000000000000000000000000..fab6738636bc491998b393d67dd74b5cbdcdca96 GIT binary patch literal 41549 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 literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/08-numbers-to-protect.png b/phoneblock/src/main/webapp/setup-ab/help/08-numbers-to-protect.png new file mode 100644 index 0000000000000000000000000000000000000000..e2cebdd8514de3aba824b7c4e0880af7e93747f4 GIT binary patch literal 62185 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 z


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 literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/10-check-settings.png b/phoneblock/src/main/webapp/setup-ab/help/10-check-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..1f28e17520261d46a63003bea55d5d0dc2f49868 GIT binary patch literal 60782 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 literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/11-prevent-call.png b/phoneblock/src/main/webapp/setup-ab/help/11-prevent-call.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce9ad03fa58274a5b30dac248b3eb6d4d12f550 GIT binary patch literal 97392 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 z

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�RkY+UurAMSLX<-H+9 zysI2=I}P1>i+z~+ZF$`0ws*FUY0eUbQ{QXoxgSdB7`r8OB(r^%{N$%;U-{g1wa+nV zJ?d^bdNMeh^^AcVc^Ak@@O;zy0L;MG|);fC6y)~Gh~^QeXQ4t9T2;>`C-HYDQT)}EAIHQLl5;~t`Sm&Gb<&KUY7zO(oEaJ!+p zVDBfxPk1fPo%c_{?72)>8k|4|%Ag54qv;bu9i|F}qIS*M)RD!c6W~=Z+Q77*ELPa- zFzC2%;3g#S@qM1H(C2(nK&6-|3mojY_4&B5rDiVKo4&}!_u1BXGd6xeqoM{}R@YFo zw7Tf+=fa)H6!-%YF}isc2mK}$TNB+vemC-} z9rpF}>+iCUr zh>=uGUA@z!#ltUdlU*{;oP!<+g5*yW-0V!WDt2HDi`q|mf0@w}+uPZtq#7Io^&NH^ zZ~k&E{tPMd#RV`Z;NyHa?#MVh8py65sx-H6xG?@|sUJ$S7H;>`eWgj@I3a-&R0(E~ z_(Ezx%}aP~!&wjPI_*yVHY{+${`QI{wvP;MKgQOCb07RF+&8u~G@p0A+W&_I=+k99 zapJ|#NVct2cvYMK{L-%%Q0)g7S*7T_&dj3v^&0rhPI@``?i3@Mn^Y&YCpV-X+% zPz|9btu011`FAP1p4Xuw+0>yM$CQ>{vxe3hT?5tdfE1m6?s!stb3uD;dW&{~`k%j) zf^>9AYC`mxeQ=~slwA_S`s26vlE}Vc7Ojw-7RZh(-aPyH@M9zN8u^n^l^TuazN_-61#8qbzlZfMm>PuOzVZD z3vb!ro|<~R8G6LDHX7gDYJFuxO$*_<#}ZI>#flB#4|OFFSKS$nkQ*Kn^f~`FyXWQI zWL3LAQZHCxZpnjv>gn22-d%@@%A?!aRkX`TO^Q~3+yPz-+=AG7vFdA;Iv5-8usbLx%7ZcY(QIuW=^pn~T zjN@T*wKX3gRoI}&UcOxhWJhtsS)y6yh$JEmC)#G?|K99o_xFK>%})vQ#T|pL{9!ST zY`oy2WU-PJLhe-C2@+i(AymxXRir{29kZNqgJ%M{Rg)o)tJ-?VS72Kq`|xKR@6y&= zHjsU{^IZ5DR1<`u$trC9@@K}0JE|hBzAa!`^7Fvj`rn?Is%PJv!t!|oa{53(Wx5*# z6wrWWgzd)fUg&+`3KvhhlyqF@_EmAQ10&WdcIt!fCPe0U|0o;6&FR**mBmK}^&%{V z(@WGVgl|Yox@1>Be|`xCqFu|&^GY3*cetD)D0BqHIa1uY%acpXc+A%Lmhrn6&Bm$Q zPjnwRTCf#x_Ooj5nuEO$uWE0T@l}Ll5T?O*xf_ z=9TU3@~Ih_+Tc+&^@#pO4(8t_T27=*?6_ftgRk?k_!-h$Z8?u#)JrjnZEq(UHR6wh zAIdabpUuJ=`<6t=Gs^lN$0pEY_D|c*GGOXQ$Ds{h&+%c^nr*QM9imCcljhmlw7z+96A;u07{QGa>Z=|Qbu zAn(>JtQoVuhop$Zc77~=?i^(hIKW%j+Y<}+a@}9I#~=rN$S-mJm?b5=fT*Jg}O~tQ-41pxAWo(}2UR&dV1E}$+ zNWNa@`MM6krJX5|{>_9oi!IgF$j6SSm8uU%)ft0$RD!j7W$z66dz73Iev}5rLfO@@ zi9|LkCuGH{zWSXV<#nyeY%bP~K@Ualr1flZyvy#EuFb3;)%4u&$zqG;weve)jbYN> z@=p_QZS2C)LDXg42N%2%hcDb<|5;4##6I$Iie$1;V6DapZ#sf4g6C{)*fSLJ$SdqM zwIna)6Pa$fx!;*~J+5v$H>ye4&73^#VbQcQv!up&KA$#bb%4GPEL=M2o6+lOGP=D$ zEy~}biZWxPn}n50#AYdD)KT=T6bPKl>Hw=E7-~U-5D)#at?m&K&Hi>ATL= z9dEwM3M{AF;e$u~xf`2V_&Uevu-zxSyHB|L_u5Uet{)p2!WCh0=_vhCu@Zi|aX}NMNGN`tYj2pRjU-G9={<)3XGOiV{Bgh$ zC}=E0Sw#bDTeYYKiWs)c{xI-!(p!XAqH>-a1PYryunfc0UgC@O;^2HV%Fn}3l|)*K zxSiNfP31wb(8&@v1VACi&d_w2+V4_FR&a%=8sU+!Ah7)!4QRol)(Hfv(I1@z#nhM! zq#3fYfDY`uYme4Xz*eQauw^us;tqucK$XF4E&k~u~RtOiSc>i=XnIo9DZt-9GnFa zI+^%>sFRy+C`03DrM(wAqSSKxX;!Rs|ZWMifv+ zVNL}Fy_=y1S+Ye(3zUEHTa2LTKt7C|n$oq%C<;`Hm#Z#Xpl1iYI(QFbSi6wE?EsLR zf~I~p6@6Z>9gF_ldep%#4+G+%f9J!%BMw&tf)DtbBNi|<)9VjjZ4G=+!oN3n%%CE! zObP-ocXlO&0xB$0mRswibj_OOv@bsiPykS+8o2hsQ^He)u7Mzq9 z_vP>iDH}CF(s}COwJ@2X`o8O{fQ_1^`=fgiM=%;fT+cMmlFh=Kn`+9gDmJ^x(%=H& zSK==MgqW=v&|K5)vt|cR2IS}tQ-^d`NFClNB)@^eB05Irs>_Mu(ZfR(=1!K zdd6DYpnVym^V!0KX|bF+YnJw5jd8+Wm3Q&y2zY6wx>_jqGVthVNvj|KHI69MKd=#1 zE5_(j04C_vW_6HFuegfhdFlZGcS|Fn{S`W6UE9tNKPM|82{HLlvHudJO1Jz zv1$qG^g)WlJG@DaL{*f$3^|k4(+`SqRgD$h!qUaJ^AY&W2_q=8Az(d3l>w8mmnSxR z&2Ghm*Q9m>EPuWT)>%g6$ejCXhw~=4etI~Qx^5@!OJeY6MmV~7C)3KB2wmX7uXo!? zt6P4>bom?T*_Bok{Ps5G{Yu7$3}y7|PXLg=`Q8-jmYAL|#gk0kx?F0UR#D$R+yfOW zkp?TSc0&N(@0%^Y!+)15W##9~(Pt-@u;&N*`;TGgkt19LcLGSLKOX#hp%iBU$)9b! zu1}EYLwuWa`+w-!u$^e=4sGpxEljwC#(KAxDUbS> zS+6`K`Xvitrrji6bvzx&&zJ8(z(A^mcamAu9BQbgQA@3kZqWn?GOccI^B~4h+)6j- zmePS73RR_WC^B-Jb(<0irfa`(uWdf!pvixrXA;t-nSOI+m0C4f3>WZx5F@J5@9&u) zNYC}3PhYro*gJ=xa#tsrq^NchB7+#&Hg8uVvoF)+7sLMS%YWY& zt$>9AdPyQP{k*6ZjQRHT{5FM^PcxsoVcrfO{Zvt7FiLY^t7+kQ>i9BQ=>7lh&BmcS z)$zq?ay9=Ohz13RdV=}JG-va5pyht`OHj4Qe)Yh_l!NfIQCW3N@s9QRg%po^f+So; z1`a=7+0U1+VYF6Cq3ovHH$o;my0Pn960Qt%GbS7{Z@A7?pz&~^!f3So{hS;P3Rey? z@ReL!0lS8~keW>VR!N?M7#%pFZomG#^y~gbZFTAEpzom(1A`GVP45c_&Q(f|U$KL- zV1bAnCI~a=d#TAv`Ljtr<=JCp#zPh+zT)_V%r}NzLtt=1t>xW`&ub>B z^k(90h#l99@Ru+kH0U!Ap90wdbG7JGS^ubI811w==<9ZVmMFn+II28{PNM{p6C`7L zY>LFoK;C3n343%LI4ISpZt&&{EV5Q34rIp6u!dfqu#X zv>2LsZiQ~{lXrqt{Xkahua~XEchBNhLSN74LNDLHpUrY6J@JdSI_Gs%$F{YOZ6EQ6 zSjNGSe1E+HdM@q8S>wa;fG5wqmXqw6#;}~Cy#9gV;}qHlSHwumD$8g7_(8Sn#6ZmI z$pYUWOaF$L$gSZ}A!2Vzja=%6>5tX>+GM?#4W5t;gn%9&6GR4Iv6N-FKFq7 zh?=ii6e>@iY#EHIs(h9CIy$t+;R2E8YoL?QF4(R8rnxr6yB*m0Z-+6xaLwC^fVJh> znb*In`?UQsnIechT!o$qM!2dxPv(4@bMrZAh&aJ4vp6EG^gGJ1S9 zjWLz%1P1i>VmMeI!ytOfLczpBM!9do>=8URceG7VHo~FpTL(tZKTsuzZz@HI=HY&+_CAoec;by!Exk|&9nYMFJ5es_oq;-$^-=l`|PewXY z;Vd)LR7g&_P0kDL@dhrzKXo3TW_V&M$n48x-;*WnO;z|c!fh|gjPYcgj4AXTVJhn1 zo?cS$-TQV6ITFe`)0TrQ zqi|)>){#2S#Qk3;Qix%E7-jDWt!oJV!13?3zSor%-2Z<5{|Vy#f21x5NsQUpuV4l~ zjrD%!ApF~Y7jR@$mG9?mxH!1%CeFAO|7cku6GyTzHRr~{TE0K6mzlSd=1Lq3Z1r=n2y%mx3!2VLt&8{!k z)WJ{m@1s}RvAXAwvlSEaV%mq|4_-RrB{3rH;OajXlYkG*CnZam6g<=9O~hbxrs?yAj2fZzd4K{T7$}K_45{ z#@D`n?lNrW%im)Wivlwrs;%0*po?DBi@sJy=l44VK;R+HQaWOIR^xUBFV~yiCq==@ z$Jrd6XyH8?Kgi%^Gv<#)UVnP3JjL$gJ2vQhSp83g;Cuc@qRv@8v0nu2f|n$L%|P?( z%!>j1-Tn1%gli|^!jDB75HkuohP-Y;T=yo`tmuimC7F3vhpQ6#u3bT?U76-!dkjy! zoPk;&A5Va-rL<9SL+~8N#>&prPtPf0$%?BQzz^GpO{5UnDcg4l;r?SdQ~|!-fEQhc zYXaJJvZ-Y_MyZ5(2mVe{F^gY80F=rpJ>11&$Yt!ElRR1@@0km+!pt~;Lrvr7l$tvB z&GIso7rj@X6U9PV=Z_@xE4m7^+j&-&Ie0NKPe&2{*z?#6!!CG{Oo)Wo^4l-zkmB(# z8#@*sMyA!mBkjl+sr!!VM04eeF;RlvkE>;uFnj%%g5lu1x$m;MID(XUfk3XthlSE+ zzcIQP`-Yw#$W_$)2b=xV>u!9V8$D-pd*W4$Ts>XMq9IaoBl{=Bd#)slpG*HQH12h* zyRBv{lfMTknvXMM2P|kxBXdQMMenwxSK_s?I~6B+e9P~}OiF&-C*}`f~JGj}U z0V(B-`s=eqxfQK9*4l`#+jM?XZm;HrHl&mnfpzXrl|q69th}eEela7U-U`2I%aY^0i3*L_?7g5X6KSKXx`N|2J1#}I?++Ulqx*V0fxjwcbL#=`X@hiwr9KVQh(=F`nnBn7FK_>bR%M~*JV`-Br z7K07P;N~2BN z2nlnOsaM2ku~8=xp#;$z~m0oY<8$3v0LWTNe#PkO~bpHnhDzC*N}9^{LKJp9q%y>&J+q zGA4VA#gHa}&ykZUOGHGGGW5zTD-HuI;2s4(h$^QI@2pvMfA;vi z9QDVfFSR?|fDl`gm<)@~j>Dk&8+`{13=K%@cUi%%uKXD>8(h7boY?F}v0NLijBEUB zlXG;(mj45T=Xj~)K$A!px>wvRBxmoR^E&r20Cub{!`j!b1lvn6!|4XSHVF>|JPTfG z6CiLRg-4$Ek_Ba*rP%l)V2j)4!vhNHc}0O^4q}vnnD6*xUj=8=%=LfC#>=7J*B}-S zSOB9bp^CmTy(h}XOOdJ;L1x$npoNd;g(L-ab>W!I^W^asFEC*dV>4D5*Ips41Yy4W z*4bxYwtxCSqhvk5>qQ?ulDKcANBNn)I4~R4t?EPcK1;7YDEkjF+O$ur;G^vv*CQhlY;^E^+bohclP?mFek8L5qN^9U+ER(eQxeo9+fh4!W4)j!)6{NJPDt<=I;4`W1z@k#3SMW-Jpf*(`DoarjrO6tP?= zuV2872;B___}sFZqYJ;~VJj$afNG(w##0o(5 zo!#_nt;2--K ze{8F4>%KrMMD^B9%}Qv2@r?G#a6{s)%Fh@Gm&>>)3|Td)jIN}-@OvC=smEV)e&{5lqVp)?C<)M-<z6wNczM=)k)aW*ks*XMdLM&h&v zIIk05SYI%0q?i8K7Mi?A#S*c0a7Zw%D8NXf$~-gA@-`{O*U>+Svq6U!>2qyiP!Ctl z^wBTT_3K$P&oo4PVNB zBAToh>z0`N+aMBR zM?v;82W8l@{^J+CYged|jHitELW1P}Ju@q-`pN#cr!d`%DZKYyKh3LpRPZG`pudf_ z_kwfS5d)xJk~^nLV?&1{Db82=0u`xxY5v$HSP`B3mdC+x*BHQn-cay+SOch)y7Xk= zOjO$>9O=E#2f%ShyHb39U?OQoMfOGfljOn_bfZCkR}8p3TTLRmhy{b4N5_oB41Fjx zEZQ11s8@e{`Nu~6xI^^Hdi|58A3t!$R7oic{V~pVm=F-~LBjDb2S+o-0-fFOY`6=6 zmtp-VZM(jy<}McVQ6#0fe9Rk4KZH4&PM`bUCJV z)4RCVpllm&)HpS=vfNfH(KqUqk4Jaj7|30EPL55rpRg$O8~|^Yb~$W-dM`-0u8!k% z{on|iXU&i~0o4&kWj#|VI&#Z6ALigzlbmKrweqM^*3@~iO+2(7pL(egIU6I=>xw$4 z44v7;9)+{c41mFyn$z|mf}q;mT&y)08eLXEjI^iANL4ZE`} z$kE2sUky`SXyzBkOMlHETf4}9R0n7FMGy`jh7&XA^~B_|>kfo~GWN_E_a)%-du!4s z__f{KFFpLw_`(@H16ah0c6b$r8a1}R|LN{X83F$&R{RhXqwPg zD>Ef-xtuV)i2rr@G!&xpEl+*rxIxH=EzYhQD{ZzjK>rtJE#e*lIsNld#=3i3(^*ycX8=e5FcCl0 z6XyeXmdrXRjxoTo)c$m;y15!#Xk0-B5PC5d0#H87V7r&Up^du6)w_+i-6PkW^^5(E zX}z0HyoaG;)m6y7j-qgnrNPryJcX0W)Btz3dR%w+7C=A@?Jk|Ri$ zLC1q}H0V2aUN&IpbOqZhv8~9-0uKQt)@3y5+>a>Yc%YZvF{RW1FmCZStjMZNf0fI! zK^s#-GT5qiVY6_wnU}9jV^GItYUJ)OT5Vb)tFV{DL{+X?;$X@XsD#&B7@<8Lane?chPs-h$%c@HVNm2nWjCWc~X5X(dAfauSLO z3IL4nR5G%&z!F@sbLF*iaxuch*pwDocGiD#?*8%qBhJb9Zi3QK%u`Q(QPIZ}e^+Fp zEEXIc+ZqEyZy=@NTa5e%vKVIcw&1J~2B?k5o0nDmuKEX`*6#SYe^gN(g^IE7ve2_* z_CboAOEDvA+$7KP?pL^r8~osw3_Ff>iz%K4cKw2fskFvQi)*B5aylVM7`vNmcs+2) zj5J;W^811KvdU&TAqNN{tN>(GHq-Z#l0ciA<1PRsbNK@3XkAH3yo~Ekrj|d$Ik^&j z0_~PeE2B(K4O>Cs{{!HrHw>kpoPVXOr3*G#Et=kMv=PL9NfMp0b6XJL|Ix9Lg(*dw znIa~2&xJC!U(lPy&t~;x_QmI7mEzJeNdWB%XrJbTy(bA3idxHpFwY;lO{$biH6ZKL zwMJE!88bm+f;yrn_{-fe0KxT^N9vH8_53yO`Ozh_t~N zUAZ-(sc|}a>XP{KR(_IU_|r;;9c0>RYa*!eHYnaftZ=d^O+j7Vz2KZz$y+Bic$z*) z1fXV-__A^$RFLWU{{YmTJ!iHyqegA)!-9OwmSZBCz25~Ic5(>Q9Cw4WWZDyWu`N&- zYHDiog;B7nHA)+rvEE}MJ5mvE)TO$np7aKqhEj=rzYu(wOK){PnNOalFyDC@)q6D2 zs4BDHwcVylCo4-O8It!)4e`tI)_Uj889A%AGo9s2#Mt=?dqX1?tGQ)wRNm#Osh>Yu zvWv%GdASAq^Sn}&xBueK2rNbM;!|hHnx`Vj0{&k){B3r3-!71burAlC;B6ck-R6{E;o|YrWP+)`rQ@n{@@geeKnK7e*GSFXeFqpwYe)KNSXGdI8Cto2pk{ z0GVsa=N~ya05et$E2y%Xy}quxy0*%!N{fgHe9*z-0bCzOCjHvMi!E@>8N=BoP~QVw z#>qp$Kt>ntWo2W73KH+80RF4aUf3}*3a(_h@V1hPiv5$f-2KBBD#FAWA>n*vRw{?Y-H_@)9rIr?*93LGYpO{I)Zi4ycQ|7;z%mtjtUWr|Vf@!N_c)pHy*>=HC z!7n&Lb6PrA2Uu^{R>kvG^qTQXr>*#C75-+T^s$W{xI<+PxAen<)uP*CaQA3wR8pW_oVOBpZ?8(Pf@o-MT~{ZxnK3CR7^(>7ai>Co`d01Y)3 zopf-=kFL?t;XoKr(?-LfvQ>N5F*+)c1;1`RNS5$LyiJN0$0+ap1#vaGoQ76$3P`xr z7MIIL6THb#D8Xe{4XFg2Hr)RZrf;#zu}IT~A0QF5~;bbpFkEP!l)9 zgPlrAzIuRbZn$5xdh!drYeCQZE?3#CI#on6k#&vb<9F&5blRjC6m7n6} z_Y$7q9{bkd%SHprXHXH*vb>~iB7GAxQj)&XUtb7i{4J2QPB8zmjTzyl1y&cj6A&&I zF9_MT>{dHSopoZNXk4R4*W2G-R=>=Np00v8>ZICP&vxP@A+i5MpLG@`{+I47nD3~R z;o?ZXVQ9j}%y{hYkRJyA_G^^m0bp0cjnB{bbaa%S90WL<>F5T72Qk?{dpvvvf;$=QmbKqB_`QiOra{Dv98}k&Z_~&?WZ<$GgwNrj>;Xb2nj28{QXdcVm0IAzj#}H z^dtbplml={motu-Zf!MsM&UCir`{Bmwk%C|CwCjwmeLp-CzNngL0V*VpQr_CbE~Dj z$MI0V$0vqug_WF3hQ4T2k1}`%-_sLFO_eL9MnOC9HDXZnC8h_cCNATF2cKLsi0wP zYREDmp>56eS=}nWF*!LE629&E6xcXH`2BwnK8Ny}PZ zK=??f%|M7y@Xad>bfUdYun^lv$ESNqS5p+qSZ-n*V(ojWj5)6>#3 zGSbk%f#~RD3Fzo_2o4e=JO3s6;UECk|5kD>`UGffE;QCy5(#jB|CC-JC_DWBmEHrv zNWicDR^ES{d zDiMFc;8xw4&Ohj2pkQb}rR zn)`m_OqCaql9TE8jyqXDHnTG;QKOAjCBc6IPkdZHG7ZY44lp_>$agYoFCJQ+mcVaNX7z@7B3@u)GGtB$b(pZgfKK$t z0>@+}7&Q+cPnbau?w8#0#wWksGqrSUPujcw4rr07{`T1%zu;(3&z*n$Ql2DeamXF~?J;L(W1Z1M2ezjc`*q2Wzcefl#wyWLkYRIwu2Re3TK z4tP~kHt(_P6#0>}6T1d7{k?rRYM{7`FaZjMI_Jw6#+0p&M-mQD+R(@dg7bw2(u#=1 zDsTJ6z>1PeS*=VkuP!$cPW1VGFNd}5`Mrm|LZxY@bmrdzmHp;j>}5b78n^f|RVM?mo>U zXKRLNK+y07qP-q~$0A-HsOENblJ(ZxtRjGLaJD6HPG;;ArSfL&5#c(z>1KP{p1_XM zd+A^AK@R^PXKxu5<@f!84k4(Nf^>A;N@r>)8MRy5C8cXy+H zdRs3}w`O9B(i1kK!OSc=$>^})@W|lMeVm!1l8@4ovW<@vM39J7zO$wKIi;WK_{gK0ZuRR|~`8~9lO0h$Wm~~qoR=w=poVng=C@D2Oj*O1{SO9mVLK}e!v1063qWX#*9Zjb|Fz)(NK2boM8XMZY zvRDv9Tdm8;{=KNMRp-=Zt^*w&UwrTrTt;*X(9=~^BR%qG5OfryoeYF&yT#hNs*8(D z+o1erKK`F1q>hefw^xUY49{HET`}Vfkc4Edo9hevX8!EV7Ksfv82Y2qP}4fi9QgVm z^76#ThlVR$U*4sk_vdclzXI2{rG}=nV;B?D051n9PxCZs+2F2~Wa8lPJ9sW@0B0#AodQ9-l~c~7Vr2?b*|epDq}r%{XR<^`8@%^F|(r; z)jFCU0XmuXZY~~1bH-F(Rt^GWGFRQY)(S}8PpdjO94x z1%}Sf&n|{~E9&T2nyB#~g670(MeNp?t`rhGac$dYIm#CH=GXgFLHnF~O@!zWLe{UP5fG3wjM`-<#5&2{ACUs@fc`Dem!s3b=bMC7t1K^YMRfs2N+F+xz(~ zn&P{<^?NQh5(uK#g`~U`82Wlryro8Nj_`+Q1)tNkHAn3wtY4#%Hg^O(=6*5$E&eV1 zHTlxRM+i81!%=<47Q2MuN1zQtet5GrH-}WTbdGe@dYoh<&KC8U{5+#*Ph5Mg3o|w%iWaP!+I) zNg?RG;rO@-2ZHWg_eb!)K516gDg?MEjNK|w(Y*M-&|>s?oi3dGY*zwpNUz>`ZDMvo z;>KIRZf4~R+ES|ivK*g7+_&V(-roy4>A>?*P|&MgWkwL6y@%>&wXHC=>kPjXM=Fvl z871NNJ^h_i1a|c;(_EaMaKnndr*?m=)*)?WRma_ezU&K2U-8gFL&$T@Abv1?GrUk- z;&gkK55QbYV5&b_;gJ4T3k8ygd7QxbY9e}$eJC~)1VSuQ-l(*w-c@^)i{q=gzlJUx za?u-;`~s!ME2As=elOcV_DO@33g|vR@*P zzHNJqdGs+bDbGcLP}AldaNEBVcwca-Afx@LwSBlL%paj3dwcx1qW)SmKzY}|j?+?f z1UxswFgmD`BHd1qUBWr$L4(<2JIH(b`}<2uN^%vFxO@lpF@8qmC(a-RMVK0!YDiJn za!hpuq9+S@{Cdz))R+TnY$^3|Y6&UGb`i5A|JCf=r$|C}$Gz;6z-cnbn^z%JJqo%l z4vesjA3f`?`+JX5#heQ|y5~>wsgNL6P|e9KZ9m^8^`aK%i`$%{X)xTDY?h#Ql|H6> znk!hAHb(pfKC?c*=e;UYdak%p#?okP@VbEB-=$R}y2x}M6;++nE;{mXa@N9Kx}n8E zwa3IEgLjGO=Kbj{U~6aJGwH8j6#E5s1#qN>K;-t`!NJ}dI*Jc|m6Id5E)<%sT^yau zQNiR+4G(|9?GYKRLSFevmRDO_!L1JSJK5`rmyNEwBqZ2)q@>zf>hf}%acN0$aWPhu zuXwn5CYbfL)LYGGPX~H%-Y~=!e!q))c|}z~t*#$N-HVY@`;*T}4@LvaupfT#|6Flh z6)DJ8{O&dV%}Uhz?aMeR`T~9E8T<&!!D%w}3k`{s#MoA$wBs4OW56AavDfa@F zlFV20Sw!6QvJ$uG_mQZgf*QkQMT{96(cw0lV6i4R=r1uT8SqmDvUmwU+|N4;z@Eo$ zrQ4Bw+p}3zrAY?WDOzqbq1oq*7#@^dyK_`y?}Ela|oy-RYH)ckuYcnQL%i zEqmRC7sOb;l9!Zyuo#?U2u6n7TjiX~DJg5!`he+*8o47GJ%^3$N(aW_ZVTgf-1BBG zd=mB`Oo~C4z!=lNU%WR`fusE}D<)7%N5yJ&R!ge%zM=?v9pUa-F4PJC0|In^ZwZ#gWWg@5Lkaq}9DaR~M{fB@`#E2lQnS(9#9JiVS7xu{ zJRPdtV_JdE@(jiQ0j^+WixQ-$uz%0S&L1cpwNnOr7OGz&k%KVe(LJ{-1i>GqWb>Rt9mtoCce;{mTK@c^)|4a&+bC-F*9ya1tEfRR@s zQ#Gx{es|HsZfy6Q>dq}7P@;JGniDeMbWFVB$uZZ-U9S;+hr;*zFHf8RvcI8YOt|+tek(*-5uI@R_QvSmYk8Y3cf19pA zrYK<9sp4}mtlRwC_yd3&AY{LCrK1Gj9m3_h=A8O(m!5Y!egfguou2s@^QKm|2;UcG)>uHtw74Fh zI43v~{Eipg=~~M{(;A^T z(GS*tGQMSR6iwSHb6bLq?fOBE#C>h?xcq7;LMyg(S+uQ^EaXnB;Tcp z6fCqH!z!{Q-V>q+i#8swyo4B3X+q%@-9u9-5YMZ<_ijV7zowS2^vI52r4Q|*Z;<~5 z5`3P_l#{NqUHBxS0YzTlnS?TYNn`rb0-S-zOPfr=A?S@iV$IHEQ zB&)r5e;IyEvoNWjmcRt3Nk$fU$91eK7KPH~Wh)=^({kwLC?m8j^t9|tgg@o+-LnQ* zyZrBWVfdX=+`+vYxm5I&dOYm0IYgRvaZ!i$LzVx>4raYpb*df??5Dnu9=MYo_h1R+ z4W?-*(MwBtNk$tVDtyt&%&={{bvZq`xJ2{=Dfxt3kJs71kr;#DUDj4tUt0`nLF(N% ziChMbmqN2oH^98*bQyDpfDBvH*7ox9YrxHuCk_tJbm9B0aXu^`_VkQjsrjyxTjfVA zgSVJkuI8Nhn^&Ou{=4XBJC@-KSZS$~w|fVjd|7$^bSQX z7I+Cc;{Of~!GyfKt%*|oDp!!I1ra*dlQAvS%eg2kq=2 zia=Be_~q)($DGheTc!tVaOl63xOsxnjGxfcZ;#maLl`OlD(?Kc2XpVU!8?L1^6fUr z%GRF9Taq6-jPm^O4K%0i0X9A>7hh}Q2R3%jKo-1*vesua$?j{ehqEkEk{&uoeaG>w zBdj_9*bvAu;F0uPcv6QZL}#+J|83**nXA|!IS9le6?ySZKCeirKBN4H=gP+UgrW_g zqW2J=1iV;Df8_f3PWC4GtN05h4(&`ub1QaTZC~Nn|8iPI71_y=zhOHIJ>1HNSD*Yt ztHvw4^zV=T-G+WPxu0)XL`gEm^lOyd_$&{VTwui83L88L5S#Gl>1pFEGznUBH79_8 z-tJ%g6fPd?y!qlL4oAv)=qkpGd~!pirSrpDMpwmd`;A698TY=cYq4r6xJ}P>r9DYd zIXiGe+W}#G((+=r3Li{0aK1E%fEyE_iOf}zUK-WoNf&fwWE3RHQ~4^)8avL7C@6=5 z7OL7;G*=yNGWgIlA7A12QJlO8&`^4M)@|y%Ae>om(DM+w=&Jm9ehgq`kNy0fAk#$ zB94p^Tu)9&f-Nq0!R9@)(tSy>=Pi!-401b^hVca>m32R9XF3lCprcAQt7_u?Q<9@w#Y%$w0WNrQvd>;u&mKb zBQw&6hBIVgX}@9HI=g1K1RKC0k4_Eu%>3b8;|T!cdBeJYhI+#lB(-WkPZF!p`CKo} zV;sNHc+;K%gW%GIQ&?MDTjJN&Ru-3~so&4kas}e;&t?%zMK2=VYqyvJu4ypf-xh@c zK|fed=-;HKra|)i$KBrY1X>pt^}OER;{R~}QB%<41G}Q;qq3*CDI>DRu>QTedVJ(5 z+Q6Xf)$!ca^OA{Qqviw(ky0ES97NooXO`z(zqY4tt3Q(!jMGAztU5prwHfk?h3@PG zXT0_Y49?zs&aYq5$-BCO5)c=67wVhsV?pgtiJUDi9g#L2te9+iYBEOVgkLFqbx!28 z>j3YYA8gO}--y`H9DIS_Dn}C62bUZRdas>p!n>uTzH6oTN>XHWL%{5oUL5E3T6L7N zC}_?8RT`BaJ{F0v)Z}~JX*_ht4S6sW3Hj*Ks@BB-aJpW z?;tNB-U7by$mB{dG_7V*B6)tUO6c^UjM@34`dfW*8@nT=ns=O8>Tl(T2c+e^@|V-@ z^0%~iX5ab|?RlSf?#>xKq=>+79mLh~E`a#Io;Nhr^wH?m^DZpG5`f&SeD5D7R^b}M zsWWv>`vbcw=-VMxT$RD{ zuyx1Hnm0c?w*YbQ_r2IBF|uTRnKQSA%zm)b*64!lUZ}cboAAKEs3uc}IZGs)A#g z%-lEcwC#Nck5n!=k1309X15YI)3>V;kBR;1Nw>D7ung_bXUFk!Ld?JZ8&d|h{iS67 zj`{wNv@!b$HcqaE+@g;lOr^hbt&YnP>x8444e298m=jnneiQ6XKEQ9?y*zTWb4dkV z#F_9S$=Jsh2CDPxujj&7$QF^rQ7}qa0^5;p8@PB)Ot!PLbA4K`QvjHLj{Nlw>(`Ke zbEVDrZEmqf+4LHL*4EhM{^OD0Q%J?LU_ejXF7|<0!;b$?R#7dHs4G3&8eseI!P20b zTKENH0?VCaZyi`3%z6#3Cw=U286)xVL=KOy54#fN%_#}#j}#2K`StFHfpWKpK@q9e zwh;i>;jtNy0D`znO^wm_{s|;sxG)|MuPC17&USKGxxo4O%SSIP63?-|Q5=1rONT10 z0>jHN3(eVvD5X*C;PbUjt3Z~dl%x`^6FDR&{5qy`7D4#VIyeknbZw$K2v)4CV3Um< zC!S?v8~Ts4Xxc=w#Gv0UpXG0K!GL*{)n~JDvk&(4SB*|xn98L)a<2I*OIw0$6yU)L zaeTTSpg9tyHZDWGY=sR+4@drH$4(rpmrmK`jqi@rDwNk+Qm0nE<0PJe+1X1bf+8UW zK@P)%oy7X^lXFPvTG4ZbNoolBdAtsFKgtp{_Y}MW9#%x3-`Spofze9c-yo>j} zXn3f@CK4{zYPBA%+ypR5!)~F`b3MM*__Jdl@Kx*)+sb8)f+#ttAp>J%Vu%P=_#Un> z0&~7S2)J5Knm?}_2TwRq>k=K(xyC>$#Ze9c6>^!b|4ar?)v`v-&z62pn!jh12`wR> z2I>1=cgZ}+MM2g!-*2>5KYGjljozAL|7wzvMW@kowQq7@z&cLL#?Fk_*Z|tW*Sw1d zJqZpD{<{~ht)_;pf`bF3Et}(|lKmqaVk|OlU%EA42Pyi{US}5VrY z28(h$PkwKj&VC^_>Edu~4bLt}y=+W%yV?fn_VO9b3k&k~A?Y62~6@!K-b|h-Ba?j_Lo!pw|}}>93yUjz0Is6FXsieW}y0eUOr8H@8X#C zjAL`3MUWl)uU*ih(wkUj`H1mcS(5kGf2Nmxmn*@+l>p}5Q1DENzKNC2vGkAH+fCA3 zhW{O3O4TkPZeYz*c(yaQ`R7ChgfYsBPi@{`Se7E6a#VabSXKts-0dLau zk|a7NMIxF9;B5enxIsvH$t1R*IC^V`2!`}(|a-H{9Of&|-^>%EJJ^690n7X-*sWcYgjgyZC= zJ^#tRWsS3TvQ3W^g~iR@?Y~(-fc+-29wMz|VL=F)nV&6vSmotOT^fB)`A^wH8K`(^ z$pG&x$uHq&Crg1SOYAxGye*)rG44SL9O*9vw8-TkG>y-RdF{df^fcr&L^>1j=51*J z0233kWq;w`cxlNAR=`*Jk!Ro@smzgoaFGE|+t1%X_s_+C?l4s4I@b2njFbppgoPDw zTwA56OG6y@mrfEvemv1pQILOxP#DcmRB1Xx-?mAxD+7xqC4FCN*_$42=MkONk zXL5XM%>2X{_00TytqIrveJ{m*t2`2I+L)xory!%SuHLu_)i8F{PMBvTJlEaiuj7wEZDJvMQie7)|r}X4HU1XY(TlgsPRZC`1GMajxtrGv4 zFeIEJ(fRAt0OMH>QDiD$OYgfFp9hTD8`&;p%@OZ+W{f8>(WWky_I#U&vcMWo!6y6P z2e2f850GpWE$v2bQhQ0E5%YRCI&6&Agpdb2mMB#{=-H);Bz85Aa>3|svd5>nU4cuq z_P)NczQUGc1Aw}#g9yRSL?W*OHU8iG?QFhCV(dC-EO`Nbx9KafuEDF?_l%;>uc1yE z4ePHiakOsG0Y__}D@tD6^Sl|d*4aiU~;^p~Y`;BirD6TKgY8lIiWh4WF-;XQNK(I}VQLJ8OBPmNC&P zjAE2z3G)pxkx5DPYG%@wT4Fi*Rb>jwj+SC3G@)ImfZcE|x2qrR%#JNSASrwcv3GSm zSm>o=WTbn~-Sy4@i&KMzCDN5w;C~Oh21mAhUUeLkC3gnH>gdFxh$Mx&SKtdj@mmiN z<;BEI_P1vKwG$fMO9zXmLqm+x;d;>v@HOCe7~}dnvtJQ~AMLKHo2hAPa+{i(@;5!n zpG>|}8oeSDeTiHW6Umw06CSP*77|R%;hy39v{{lmA|!Z!WBu)mzhh*kon3UvDM=^p z3n)Yv_HOJPA40+vcBUqmXC&KfSUpmPzsv(s~nnOiTE6bW8rVLp42{$pcxOKaQO!CRmV zWIw7>z?1@5OF-2)a*;snom^V+3Dsz#2#AT50;Hl3aAy3wJ|p<$1!;qsd3ySsp{KM= z2`^M^vU}6<8c{$ED`oI=l_rD&PXiY>!TIuH)t-CUQRv^=`?tVqA|ukzimQvkCHojV z_<8n|Hor^&$Xjx-b)_Aoq&&s(Ev*9R^uF(V>+Bqz} zhE)iDgN13VetqF_(lkOY5pplSwd!J~AVH+#DxZWaOOg^2Cm2{-e&zhX5U?XQdcs}X zlu{IwQuIKdDi7^eA{5CSv5&9~RxN}Us}>+oyyvE&rqjtDE1Jj;V3(jAq ztD@d`&g8Tx`nX<{%i~ZZ)fXbeFpz`EB$Un7TBIfw?4}$uxqBV`1PZ*Xv*Ld@Nt&_l zWF0K3rKl=O^%OWxQL1d#4c{k0R(6j}t*mV7QyUNO@7c7}Xgm_5cEQ02UNv;O5>H#+ zSX&p$wAIj}NVn zn1G=Jea(!=w+95>H{5X;JrdL2Z`3%@pAyX4$7kC+;aEZ zNALTt&wKB2oeY&6tki6+jnB6RcFDc(Z~i!_bcYdH*qO~Q5LIg({+R9#__kXYo@knx9;Kn{_TBd~E3*IDb=ukb5i z)%|Rin0GsLaC9V?fRP6c9?kQPbhI38q9k_>PMQqtQ|&r8wzMoJPV29q5)sFoQhLSe zD}MT9Y-2)5=5QQ$_~vMDjn8yK5}f90X2!a85rrrK%c2q?H3b3>bz9mYl$C(=u?|m! z_9OY7;g_-Hl!M4sL*{b#?gIxA zNWMYja+W2LiWb4iY$}pL<>)i`00^wS>>OZgFt9Q+`b;@6Fo5EhIO7gKno?KiZ*;TS zr+Dc(sngexua{eY^YhX&@Kp z=H$(P(q@70v4Rl~)JOFDxzG%ISi-|eFfg zTL#x15mr);q36eA4aLRx=MAAwrm`s?9PamV^5D1QS6}$OAVQ|9Ttg%| z;26Ab@iEiVY7{BRt{q5Wby`ZPA~LV5DvOjZ5%5}#7Re-dZ@okJ=mitkA^?i_*d`-O zI=6Vs6)?6O2|AzeJS^TK?hAw_VNqp--SJAEaW?%Eou80~nBZt0OIpeflOiM|b z?kXGS)Q1)v>$?JeL6G-qV{@CAm#6h{s5xyB39_;itP-X4o+=1y9tb}UQo;J$^2taKqxrt*1x=}q=G$A`QMWOEW~zs7fIhu!zH zo*YLIaAdbe+J_e+HoD$ipLGG=ptk%Ss^2g^lq#;b)v0-({s2ImyKcCIYO4hJbT zA{=DnL=XU9>L2PR0iQ#})qkOzgJFJKgAB22zPa3ZwhkVFY#g`i>A;DK+2!#Z3Wl%5 zPa-?Me{OQ@A<;`(*4yU=S}%I5H2rk)5wl)p|{O*OH=kuDXFQ0XeW0(B0HO` zR<{#CCb=Q^_TIV|^ZBJVAGMxWx>R4|cr(V&`vA(x+2qPvKRQSBw}0+pT^x3~J*Pjz6u-)>oMe5No|52waZ}K7 zx9jYw5+NjIVc}@06?ufP*yO6lpavvcbbYwZVmA=e1bxn7fZ!>8+_wwP_SCrzA%_Ns z1Z&?Mj*++Of@rG{9+Y=qZCav3`)D@a7{2fOmj3O0vrEuJj=x02xOsI<&}Hj;7v-$u zilJQG=Uo8llFww_&90-?J=6?IQe4~i{sN*W`@#|I?hywZQa@LrD^SkWisI71obOP~h^zB;^;5laiOyhVM7t*bNHQ zn{EHp9M>qmb_g*5(eTBIF4?1_DBAK76 zts8Dnf^ECd{+SY_13r9U2=pW(PN&fLgL;05gyH)Qqv-VFa_!80)Zqk@pZ%@I2r=Hyqwy_wM}IGIpo2g~Abh)oWYt zqge#2s%n5F+!2H!<&$XpNiCHH&_#Xy<32zD+A=k5UD5+oS+nc9x{77d@M%scu#IhR z?!Na}t0!t2NagPs?+70>Wj}ce_EBQOXM78b$cU|FbsB(BTP*?%#{t=j0@e3v33QD& zaoi0akQeyr1P+3yO*x^S7Y6_~T+a5Ooo^bgt>HvRCon(!9-5jOn%eSr+t%lllw=wl z#Bb(J2TC&O3nF#1@dl3@HSFGR6} z?z6LJ2^lpkkS~7ZGer{ik}k9!`>vnT+w^pp^H$Mj${Bc4$wq3>*cBNCUiWq^4$hCW z@^D~6z%hnRt|wXJyK-1z!`tJ8=y;*@uZUO)!U7c(Q#C9Y^<;!UqJUam+=N&}A+ek{ z%mVMt|4^9!?o*N;|35E)TkL~Z#;E%XPYb^B7MN%JxsQc?Ske~qpH9os^|#qNy4fhX zFUJe1wnu4m7^10nY%dq&IhbO$*Dr16hdK568f8gPpj(QB|QbAI46-at1WH)@e^4mM%J4;J^t_(%~;nnb`fAntG1F zrl{?MjEHd2CTgx@gbxh4rlen1ew3#zHYxdYcwOwc0;Lr&QpdMNk>SJJ2oQFdr-*W8 zl#tLOaq;Wd?;8rGKRDcy!0~Ft!LD}Amu6@!r(`xhB#+54rEbQe13WF#>aOz2j(x-` zwCr=Gt0jB_=`0oxOebtwE*RDv{i93F$4UYaHgBy(9n02PJRM@hhZvj^!KXD5STOO; zTTX)X#Ms#AMz3Y)gbKjUiQyVWY#cLRBbre7!Rx_07m$K6v>lMeu0g5bg;3rc7QB*9bq|ThLO1)RPh9wjWHiB zrXC!JRcY+OV97c-6+b-~V$zM=AR8A;B^UjJ+Aq9n_bHl-dF9+((sti?S63)@gE%W6 z!R{`0#k1k**rC@hk*7470YvN&$h(FE2wiE>%hb$Cyo6p#N43vBzdmA{a(or$U{XP$ zXGjPzW?<~yv)wrOT}Tj3YB0z~`zIRP!u<8_R_ED!!;#6oSSG1hNr_2Av$CVy3svuinw$HB_3Ay1Wx)GkZJNjgL_b=F zOv+K6cimA^z=^({z z)b@;r`e{qH=Q$r3XiGIBZCyB#sex>De#?^qh#MKSvyWbG97Ik|W}km018Z7LcO<{t z$!S3q{FCQmm!DS8zIZ&v`Dol-8N z8^`CP^Ym+mrEMHqn<6Mmgq7T}(sHErZ~n=y1k6z;H@P8XXO`cfBlqnlE-tWe3*4^g zhHF(_4$E2A@Vw_X0oq*+*E{nRob2gXD|+*t72pe0p_8j8^$gm3gE?|={N|e;I-5Ga zmz-X}htxr5)>TZ$=~Nlz?|$X3AIMDQRAcFXnPdlZ*9GBchLm|q<}9niHyN_vYrRc~@}|CM17d-j|HNX)H>#Nm5}t;$0No|?oMG-BJ1Mbj%e zgl8!B>>iYH@_%Y8SKRQ6Y-81-8M4;lo-(-fH_y?mO8vtPnrSpOOC{e-c+6MwG})gS z`1a&BZ>K%arj-V8eX^!Gz6k-E+IT;7y*(ki70I4GL5 zqtTGh!QzH;+dFI~ju~f!rpSKMgeQvXvPO-cscOoQ3b%{vYWCg7)q%2LSTtL9zo ztE^#@*1R=qQ6XCf9kpxY7TFv$AF)~TX0`1X%p32OyHaI8C*lY7NxEQrB!=vow%7b% zjA4vbdLm0qXR3`JxO;s8IHmlLqT*u2rg5|)nXA=iuQc%gIn0Tcu9I7ihoJvVeIHRS z^{=K)Ekh#{%)CEG`|em+dw&JzgLrs4cAB1Co`!{m*c$A(Yn%7IeEJLxh;EP8OXcgw zf^PTlZQv-@*4CWbVb)9WZ_&+>1cZYS|Kx63*yicwDH8)TU}{Fq-I+L2 zG$uy7GC=ukmDlnYpaMCYA4k}vOzr0-T6#9Qtev=n_T;E48cFqa(}*pdyDJ+KFkzOP8Ab!A9F$O*)n4=^y;2 z4yVEq9*>n_Hv6-NrWP0Z$owNt68t6Rd5%(!R?|M^&E1XXT=!?S34`LP|zS zBd6M~_4Gn#>8?R{CHveB&j{%!-w4w)6{;m0?meT~Y872!l37S1>dG$MDrrCHtzt_*OvoaSc zUrj1UtCkR&!8$!2GT#wQHBsm!dduP(ZAeLQFmO@(nGk7EM2Je#CMgzKS%vb63>C^B zHWMBS++@eMiBAcb{GvaLeTg6K)g75kUbh)Orlc-pP&J7x8sFPVi`Rawv+;PX~&E1w~#~umuRh$2S7n2Y0hH(`s@o|F9UiWUw`F1Yp&eX zo-?C5{AXH}<|kF}`-!CUfnvPrDN#9Y#P-b979_7mF7CNc+Deasm}CUlz7o?bE$4dt zjl47S@anBdpgCWE%ib*Vox6pCX z906GvAxWi64@}NO1z8w)a!{4t1N#*&Zrtt9?A^nu-mc!@UgIP9vAwwI)Rzv4G9vW) z9P7D8etN*{a&HEu`?|O~6Yyn_SC3iPj9aaDTrFY3a4KxJVZiFB$=$%~daPEUDz{K4 zQRfFp0rk=cc7fNshMwJ6MNrn4L@MOf>J1Rfr$jt&sG0cK?n?#2t#2g~$3UqKfbP6^ zx4@i6>pu!8iHDC8jV>2-WRd>nng(yVgrDb}?%Tnphxs%-ubRDaLR zs+XaS>@6!jt!Zg|(0-k^BpV&FM;=j~qf9C_5h9s+(u3we#S;@47vc!#c%iHY)puss-+~vV8qYh9TS((x}(GuTRM)pKwe%2C)y5r zax0aS?uj!p?RW&1R&b*zs%3Cv7WfZc9KxcA5YmX+A7|=m+AR_(IN2g+mO|sWK@)Qq&T55_v{E*dZ zzI6?#qTZF&sP=$ z1GN@IsjN=pQ-?mD7i`#1IZ7UFI&w?U1a7|euSSrue||`*N~19a^<~ExR*#^PQX-vY z%aL1`$8B%4wPp zHu`BmkR)%ta&>SBewbA2Pq0~Tv6~!*0R|GwtxnTUwxld=SWvIr^e`|$pXq2Wr=O)L zULlpeY}5^O&5%IB(7xL?4fu7NU_&PnguCp0c(gZm?rO!#!Et+#^`zO#U%99vMmZg=f`gSDRh=fsa(8H!1o##nHlX&vRFL=W+q6Dfk@kV{-yL z0GwioYjn+vye-ygwVsby1Zsw=6`K#D*F(c4K3wFy#1R3QZa{1H%pcMw{7r&{c_YP< z_K`=6L%PkUpo&sOp$XM_5mcCLqXqFB{hQ?=ePLzfWo2x-yuW$d6c3D|%@+HYSj*7) zcZE~%2$B3j3rSo^y6`(N^S&FrV1ksWYnbJVF6K?~%MVcJM6Xc#2rHLNa9Djf*!!lF z7aR`;rK_eJ40S@$4@q6)t9%-i|#z32KD9LTn$3J zqi(CYWIN8;@_CaWeJruPsT3n6BJw2sw%k&;zU-4S1s9h~WWv+%sX6C`4W$a)EZ>om z5{W1nZ*DxC&Nse=!bjalH9GnxVWT^2JOGW2TCgEP&ewZ`w}uA&(&)S>`-oUXUnlw? zhVeg}T)Zw+#f7}U!|cvF*y);$(+F|iSih+FNTzDC12G>E5;P)@zqKvq!sVj6zfqUI z$wAia9S^Sghg7U2WlHO*N|!`9*^^g$3;W9WQt~Ddp4tSglMvnzZgNVucXt`r<3=SY zc~q%N2;feu>>G_E-#=?5QBS%y57fFkm$636val^aUFZvaqDA_`HZ2!P*16(Y+~@~S zA$%NYE$xTaEusC}b&I_LM!P4;oLb_!mZw;l3|)iN4GUpbDm24eeHMM@oz+aJLBh9ec|j5h)Q@JL8)14X@XbO_}Q6-@v!qt;b)KB=aP!uxK3ZK>xEmZCmc%xd+k@Oj%oU(lijEinHXa66HiEW zzjfS%c;gZfS0$RL)yZg^*qGbwQRM{jHl>s)ek#U}Vq;X%Qu0*$j4EqAMS#~IK^Ix1 zXuSWFkr-wFM4TMWL^7C?T5j7^+RU8oM%mL3b-MT{9rhFrAyvI#sDLtn107{#;0ar$ zV%*2C!I~}s>hUD|&ozZ@Cv6Dllf|S9CoENj?W($~baH>Jca2lW%h*|J%6lh^XENtH zpzyF6)(orv=f%y`=${$QdX5$sO8KsO&9z6=`vADrEuIxgF3nKHi;}^prbTn?oStYd zjhEH;idW8gTcB8wvOKGmr??HEzhK8!r|f>(%svalOSp1#v2jy7N4!r zv7&NFZ)X@GIrRq_%cP}+EU%Hzn9QeHG?aIn!8v=?i0q+aocLpe#x4Ad4T!lf%^7N( z(F8Cc$lfR#d9mtPpno5PqbZUuDzWU(+k}LdYzUFN6P>T6kvpb!+lCTA=b<}HyR6nn z$V0>UQuJF7$)))r`=2d1PoGicJj)6bp^sI|sLlyPd~(ZJRIF?+Lx@qWC_a-d^pyHV zrQ5+w#Cppn&Ae7^vB%ZjI(C;aSHwCYOdks?()mia@z0pgxo({N<7-a(jkOs`(`y#$ z?hvGNwC)frx-eW+h@X)`@%8K;TX#r@ANuL1lasyb?hqqg2grXfzxFQ##Q*!Ffjz~) z`}p@FAo&hXL;vrEf(n|4{rCF+_)&xV+qI&yGT3H>3BJDTAVys?d66boAl@pPMJ4!?60IA}<56n%WxpGLAx7{aSLUy530}RiwJA`c>+Kkl_i92K+!s z>7-4|Ym1?%5I-I2AY(Nz33v|(#6VRz32>YmZC>3Sd#t=Ho4%T%!V);^OqcEcllO3nIDF<~931jtFqNIE z&lo$c_3}1l_~F9NY|a*(DsMPGT}Se>;z3t0kZ{d3%puRqr}N`DI>c|(wxe6FKb}d? zcl85hpQM5|mtBM^0}W-Ar1NxY9Y+19zzARTbXz5p%{YwkDg5+1r*G{-qBwNMcW~d_ z;o*y!#2v5WTbJvCj$M+WGO5zxYjOx=CH9qaE}frUOy;j2m;1GTCW$Xv#}y*@2b&l> zXCUw7>2ZNYs!)gmG$Hve`Lz5=KTFHrkp7Z)dnJu%=h^)pmf(%?AU;{7nTY(Q-BJDI z=Lr7X{M@ZAv$V&%%*x8n>+9Jn({lkAEiwAI=adZ0+|~9=I;)b$>W2$0+xm9RlQR>s z1mw{E@M(o~o`COQ$n6^8oJGr?{IooKdDF{d6AQAU`lZ@}URz;#WL_J_4aj`M`Zrbj zI*n^%{!Z<)n3?4YUib8fWGWsbFOm(Dx-HVhNk3jLjMpuD8nu74mnP&fPfU!}UyhO0sqY{Uazu{wd(n z_oP4G`|PGC^6{iyW$QD+)Glwy~_%)MO$y}zh)dpPY-n6h4M_5Th| z8SjV`Xt@7vLf&$hK5y4DdVH0BV6d!VG2D93?{)Qf7GSn~b7ggLfdR@&cYB$^5(~qn z7oWXXtr7Wc01m5JlX`8ql~AMZ|!3ip!3 z&a3T{6$I{nZeI)XJHtd?)BQ@<_nHK^?d|OXrw{r5aGbdeJ(-*ay7F$LU27^BH#?|{ z_RlU^(X!Ip;bC9T&HIL`VDr`HhGid^N&h9OhMud`aWi&BqM_!?{y;C7LXuVQni%;1 z7AYnCea6+iKS84~H~1b0KXKWWPH&&xdY|Bhs%Ir}`)FN%{rT5`TtljCdhx1B zA16vWCz;*I)5Byk_`whXVh|?vzVDXs`R@@!FnEN&zkh9i=lz~z_=Gv3sAzuu?2bDE zCsa&2K%{80Z}U17(Jw0M1^HFb^0Lpq9OwPj;uRQ9m$GTM!%2;Ec|~2_`SEr5p$}GI zB(ndP2zjq#HdL%Q`Q$_@^+>r8MyaOcXfAF^;1t( z&$?Vt(pTHghsR;2M#a7U$iSme30AECNB&MqDk^`+tT&X&h2kaSS0{n{E_-v+z^~ZY zP@#(!%^csfZ9KRXy50=ZWY5<18oUq|^=|R=)~Cdxe@3capfWoNnCzQu62UI zH#Sa}kI{XD9VD7_d%h*FXvomY5XVi;PI1$ddSi6fbhFN+^^X58kb_;b$$Cu@MzJE$IY_*oyp}$g9J(Kng3(?Z92m#2Zq5I+hD#DGtk;t*z^i1)P6j;zi418e%W;WRWLg*^-py&oap&F zP-St^;Xc>Tk`%#dCF6QGeK@|Cd!QO?^(hX?4nh?|=*%LgsyG#;2LJ388Uw{G8E>^~J^TwZ2){QWy2 zK~c+;=}or@YiD*&R@3_MNPVqqa%r-*+Wlok|77b)Uk zAr5qBXP+J%+|Y)upO0}R>Xlg8S_FmF5IKUvo9e3LA3tU$voNEC_)uloj|%B~Z6}W+ zn*O?mbsED$8;@eS(L7B^mvhwhiU)J=uJ*2cZ@@7bct;n~u|$y|;qYps^+iMZ*8FzU z8Ve3@eRt6e&2mG{ouXTYerMD(SZOwv5wSV1t?1fMDQ{4|& zeH#xJsjYyEHpy;we)DCSZ?2@)$;3jlUAE@VW5_>g{q|Zz$T8VYKDV`t=Ej?jf(la^_<1@SiTg2vE#7*y z9sg!$i@t}BE{KMfm5i?Ec!46UenuCF6WHVz4)HtwkFOk~VV!It&~~m@U^rOlSmcH7 zmVV+-frVe|ZY3jg3`~OGZv_hrcVp#9QSzW57~3m~Rfjr_H;0#Ay1KKnjEv8}DKo{I zn3;BTh`YK>oUP%D#)l|7P$%1F-SaiLah+fPXhKCvi5Wh;Io0S2EMQoT1~$%eP}i!1 z-E5QNT#_blatxHqcFHIuB#DD{-9Ky6diUb-_iVZD*$TTmprjJ1k?=t~2df*;(Hdw16ej$Tr=TgkMJ}1zta|?6KB2wffvwjnK$mMV&$|51VCEn~ zZSax*a;2gWid)9e``tg(sRfR12q!~CiN~;#@{m%5 zMmQA814DZ#Vi`JR9wH+p1u8QETeL<>;{=anXB)x;N$RDNlh}Dcz18Y`>wM10BP?vaYn*3x7BJpa#T0QcCM zQ^mO5nU@iN$z^A%_2_zLlD$eRG-p~_d;ZVV)Y$R~Hdb#r)}zLD>*_e;wf+=U9f#cV zdU1yE%s14}Yn2-1)*ayfoy5e+j~eCD6o}~PsZ*e2IlV=S1@S&RubI$aBD#A%YIJ;L zBN5W!e~E6InX@|w7ouf-UowtS`x}fsxIS^cYH0+mmW6^;IMXa)MMXtbRpnmH_;i`4 z2|Q&QGs`E{lh#0GSZ!@(%cOAF3skfPOZuG_3mHxd5v?FcS#8H9R6I zSApi*ry0;MGZf)EwuVvXv{kN7Akmv9G8}4@E(3=8`u5%a;u0B*oq$4Cd^v6C@NI=z zl4Rn4TeF2i)~G9E_NbH;DbWD2$Am?)_IQqJrsDe_IO*9Nt43Y^{z)T-pY|oNGLog~ z7lfa@&l$ESnjanQKjQBUK=&Y~$L?M|9FT*H7rE5U9@wwlMh(vlS+Z=j8?hn|I=Vgt z8p4Q~2989dBBRUPr(tVrF%uJzKo+bg|72p&K-dG({-x|%LrqOjK}$QhyqT9Ga7V&KXnLuE|! zato25+Vbw;-Rr-j+=MNU)!Qax9n$}UC855kADO{}ZpW)zt3Z;e{90Bfn29Lk79|#rANxD8qDW*~Tt0@t1c79hG^DOso5U*B{oB-gG{i!RAi^j6u2|C2?}HR#CMOS_)@PrR9iaBrThlC{tv# zx*BnFJr9;-X2kJGRQ{G_z;z|}@go$7O_H3MrsiZQ_nd~VZll-5Azw-JZN+Fo-N1l? za%66v-6DPdsd@ zVSkl_L<@&4wWqD09@sZVcCWA%sX4YRE{R6PVd{_nS@={>PC@bLAOHmgof+(d z+a8f|_66&a$ZH6{4_kFnqcNiCX7Nr+s@MO^(qOjn)o6xHHcSpaPs-CI9%xd|&i?3t z5;T)7>>J7BuC6}h z7jo>0T3K0nOyc&-w&sQL5pueUW36qj>k|!J0w;Xap)R1)U74!0L-d8&E(+?{=;&-J z=aGTJU;g>4dz}YF_|NQ#@Rz{mWDd(_YBF%$y?MUbX>*8}&t>OotDw^RD(Y-rXkn;6 z)jgYznb~!7s680p_heL^Egqe{@}5EI|Dy0UV#w0}Qlkr60&L|~Q`yqj6q5a?q^Kyr zSeD(J%F7)(LS9+W(l(`#aPcM5(IYb7je4KS4)m?K3o>($8`vX2yrX1v6+d9OmA<0caH{(Yqe4yWNqpm$~(YFj$zVGh>0k-o8_I&l=RcC z<)SvbtC{bJ@5k45!df#$MM3#h%Qj=&9z;VhI}WC3k8RDo>%2FR$lsIW(nd8Xk?2%X z()|=O$l903BAlJ#S zT-N&lX+SZ^{L8PerdDmg+;KAR)e03A(M>*_^CW!{pb=A)pP#_%Og9F33~;9kl1qTP zb%c+NjS<>49Hb%y)|=N;7IVYaQJe2>gi_qsvJ-7BLDC(J;NO&a36QzZ_M%jgs_N<` z{Od%M`ggqioKmUYr_}g53Q9`pDsu=^qYAG6eCJvqNGdY++bB~Gw6AS<|0U=?(sbg;5CI^V8AGhIrL)~GIlO-O(!i@7h{SdeR*75r{&P&K?&621RG1X#D4H^R3@Y}4P<_4*n>U@ zL(5ikaE);WImZ>us1NuVwYe7ZSJevIjq*xLNIyMAzT5ad5)<9P!J|WVV}lzBvqG4c zmX{L~5*kd8{;Ka*X%wni2@ALURmT8#uq6H07U2+Lf*?pD$f;Gc^76X=KmXI--d&4q7s zWLK+f3F!Ocrc29YIqswr(yJ93KumeIAA-jrKCSr1aGgCHKG*0NQ2IoxQBGY=4+O=T zVuv}cky1%T#HOU&cGt{b?$xbVvgKJe7o?o)tX7`1N)dw zkt3qdnh$F$Wz=S!H!Jryg^f6FsxjwE$W3Zcc z*_$LJHR~Se!;~J#m1K&AH>CG0Iyt2MP|J~(lL~>cVYCFl#SP2J$*ZGe z^vAbPL{!m$pnjRNsj#|eCl;MAkfqM2MkI}R((vFfHO)6fLjh*SOcVlT0jNw)dVYR= zc5(lhAS*Q=ug7Oadv*1*0bNfH0$DY2-d?uqQ<(P^nyFl>C5QO)v2EU0IRHRV^)3gq zS3K>-As?yk^vK$?q4Vv#THo-kb?&(&xDzenh(q*7h#X5@BaRwx-sftUE*c;%sANsO zofjfRVWTXbhLDKw8dV|D(2n`(mtZwp6GTAZjg7|pxA%+G*uAw<)CxAed)Cj~y#*KF zyUo0&H)3ZWGB!4P7W`tbmpP8rCcm5GM{aF#Qwqd%EW|?i*p{o4ikgyS`19{h9M+dl z&c9XTp+HnfS6pu`odsAqo}*fmdsr%;nYrHS%bJMk+3saWVB5$I{8V3LqK)TsKa8N1 zCh^=UQ??BzesYkU@;ZHc`GEi8Ym!i#7)|?=3NZCL{KwSIcFG8X?|ogVU9BApIg4F5B@TT1iz= zb#!=n)RI$~N)|m(7=Rqaayd$AaX%t)vFzR6d9pzAiwf*)Z8uNdRva{ib#$bpB0lw) zZco64e7oNUgXsiN7tQ$1&`?>XnndVWv9`RZa*hlvD2U`8JXua%(>-f;5wpx1@c-B; zbg3er<@EH>?>sk?Gwt{-Vj>qS%@hkZvKuFtL*<7tV_an#NfAj=Afmw3vwjpC6J1k1 z3GMpGA=lLn68urkv42f&4OYo6v)4b@zA)9ET-VR0Y+q;8j1a0dtp2dVMBA-fTbj7; zx;vSnLMI=e(fe(0yXh~#^97He62}nIv;VA2m_FLzPX6riM4CbF#sb;5Vccf{pbiMn z+^!2LzslCpr9TCFFHyhGWglzVZ-Y-~b2oC|j{KimT5Tt4YvruV#tRtNcMeyW`GT}+ zdrPUq7I()?C5ziH>3?HpPcT9m8H*$(pMvrTL2U6-z4Thz)37G~l=(*2)b}OYz$>BT za+w;L@jvH@F~RQ#fr(q4$U(Q3DUmb<8nIS(X)wetG-L_FKHVR?~3*8hLRmf73>+QwmEip1q-`*kwWQ})B zu{erS4ofNC7kkA_Y1@2nS;;7A6+c*Th*#IRa&Xp&OGpF+e`kTT2ux!~e4xZ8SIWCe za0Jj_wcr3K)Gghjh0pI)IoQ&x&# zSkpj8+lq>dwl_BZn2(66=xVQ@U9B_4QAbM#eJDz4upd_Y)|xlIEw}A)KdHaNmAd+8 zZ|D5qW&?NUn=E>tHWr!NdgoW|?LD6RSzk_QgGE!!QW+85-8jLq44UPh2$BHdK}5UH zV34=e{~;|Y#*hqv=>&aXiWQR(13yoLEzlkVZ_r>AmzcQ9ZSi_W66b9SJq^Pg$itcU zb;_;;btLCC(5_gzGrx@=mZ*pEiU^jJ)YWA*q|-s3xi`+u;c6PmduEJVxYs$F z_CL9c^>`ynoo?_NY=jxsCMX0hGF50XJaj?CA}P=iLiUS_yiR5geo*)UkVG1qT1zU6 z`teNIq{2ejK{2am0sJ$)7$}%)r}moiF+vAJ)8jWwF%4);Zs~RLF9NR5<~-|H&-a^C zk1j~8%)8(VUkPoScKu!mD$qqw$O4}er6X-|qjSw225mE9#lmWpNlYLVqy&*hDCx-j zx@Jt3h7n`T#M;^zs+d@o32r2{EYJIT(z$D5l1!gWO1+YjbAzDSJ(lPHjJKL_3qJL-rSsBUvkBm+#U3>3(+z}sjFFz7p5TuDb|C`=70QtN3T|^ zR=6Q4>F&Eb1rGi~dN-0{yZ9ze%1jQ(0QRS90vJR)O9d-@c9GxbMbW^39QhWanh6z? zkkH1)cCV7}(GOQ&pLqF*@?C<~b>b2B&14i^-NCi{7jP}h0)?R^pt^NYjP@nVg#5|%yhxMpB5 zwoz^^G4e8}cVxUMA@bj}8q}MYb${U5-^~kZMA_&ZO(ysj>3L6{jOS!#YB1ncOj?&| zK)dQaYd~2Bts+iNP9X8cp59_FlhNj8W{a!o_oi0SHkqKN60#$NK=#Eya;T_PRaEWf z>-YdT#7p@`PW+5^`x!H9k!<(1r4?t8D#G98HO#So__BzUr71Ohv##(cm zc36XRaFu~!X4^#vUpGQI6HxzYqaSsp7lJV zrfPdE^80&_B{;Hg%xb{pE-U*DcU9%yOd~fVr6ee>@5%21H#U;v!gIg>S$RrnvMMVg zo%@Qp>+M-ePVDSi9Q*G~n{ut9vB2z(N8|~tp2h9hcGVgLQ?u`b*0y{8M5q_q?ERvZ zMouxvi$IDFs`TT^5d#_xWo?kTJ#$q%Nt75V0D-q-22n0uIGT=*af2Ft+KCBbGq>c#e(JKzD~p+J-Ds6hnAFw*4EFOXA3f5VWVZ>8bJKj zNnetDB0|`wVRqV+?S(&<_w3%r`Ci{_pwROL$I{cX$0EzlK8^5t#Hcdv7Ik?lLte1= zy9NMj3!x^j$e+2A{KUV9A|7qH)rq%(!P|IGn*;S1gq>ULW{vXy|iac3xd%hZf%agRB)W2gkRY`672v zP%_$XA0dXCW6{D&JhvYUOyJ88HT%d8={H;59C7!z&gAE)wcQxO4h~gS6X|DLbvTKZ zBkx~XO2@SKP0)ym!!wnaxn|iHz;?w#d_u$}iz0WSvJl3q_%CbgnPuggS>KWOhsNB{0;wKaE`# z{;?gC;8O8!cfTM3yanp4l^ zHx5b?1er?FI#rcU7t#7-z!@r7Ft`--nxh~A}O_S={Vg<*|P`|LP zqwgKV_40J>6j*Q9IF4vMNtsq3uc}8CZDVm-CK@IVPIq!pOdCD=i$9j0o$_gvD%$OY zS!s=`V0-s=-g~ALUYrO`(ZX__E$kv~)H!HtkC8MrQHAfTla8#m(e3dui|1f^Ywk3h z4L|aQ?!yD9tq@NVQuAY^C;Fpp(_34Ut5xbW0|)y5V1|z_`W|kt%{r50Xf=3!-HSz& zF^aKw6GqdgX>?RndEM4rQRDbNpLdkz>MFb1OszxrN5*mx9>4z9i-T#OSV}BbxE0*a zqvmvvl2s@bw;QLirshb9Z+do4!6js=DT`He(1F|od_&mbF6ms{<=@z`h_w_6CJv#d z;&>@kn>`j-6s~U#+s?nv)MKw_)dljx?|j8zLX0)Xx2rkBIQ5&!Q+86i;ZlPl_zBF3 zMjKZuX09yTWlN7^+8+sDH&;&&^o}%nf$XuIyIams?E-i@$5L+&WKR#90*h62<;5Yh z&-~>;3hcCaA&O@eN2q#5>$${v_1=Q2or+z_rWDJE)Mf@B({w4GrmXz!aOOhQ-KLcf zn|1?1O&sIzB#;TzIgQFVPrIEJv&-8eX&RfQ%WC7y4xk(?ndec$)$QDhi_-JOml)ug7ojBXiHy=+-aW~IAyf4Uo2}X?Grl}xDLMO$H#{^A&ECuULZ0V8m zC}Z(EvBX#?zYkOMZ+1>S+%#-gm&YZy^3KlR?#3=U0xY&vr)}=0Owr7|T1h2Au>v<&{sY!it zlOVqM?Zt2JRIW#FZ^s^Hzk~W(diO%_tnX1QO|!YihG|FS=|<$)I6U%b7J+Vn9_|XH zfgaX+&)5`fd!+6zkL2%Gk6!WGX=6I~hjf9lE|2Z$wV9hwCT!%8zFIo7VYar$jnKWD zPcY-I)Hog@Tqn?-Q`k2ocJ)ypT{yp>-8$sjw-d_8e{P%Sq~DaUM2|n~sweaH{bldj zR<)XQK*h|Vo((j^GX~7mYf`})uD1t1k5FC<3jw4t-qTaQb6?Q*b$ZdLZi#-R(~b4= zUj5!G&he4bNb^nZt>29cxx=YHN%KSmCRx$`F;4hurqfJf(5DEb#h*x9a}R2HQ(MZXG>qv zs_R-6AbK`;BiY*d&?P1fHxk3)spG9Qu8aKN&sFOgiAp(?xpTAAldfp;6De3VK#!)4 zRAuGU3rIC(WYc3V1$g|C0u^%m4tr8h~hrFZg5Fs)8LcJeE>u>&=p_4wIN8Z#ZyUX2(vNV1G(Ew{Ll^PYy7f~NaQbpl{g8@0;m*s#J#l;$!(ugE$jeqXHj1K+Qi^Oq8!U22L~vSUxOSOc{;8Avmj`l zk!2gMGKB{&Ifjtn#11YA$6M4_iVj6f<{>uQ&#v#cn2Ogur! zl-BC$d7Xgs>?G@VTYYm^-Lw89nE+XV;iYJXRwC=Yrw-TaVlYH7?Domflog`-h)td! zb!K^;&PI?t;XtN)Fr<;UfI{V4v&)GTD^{I%+CBYekPMIeGu+>F>hMe?TowA*4jygZ zJtGdg>%vYEwHYGviR}BJf~6SUk3TTonWqyh{G>F<+%Easy2YvEw-&R2a@TPeoIw+1 zbZVU_1K~m)H-j15p;YQ~`zljnOsv82Z#n|^Y~^*&Ds#eaF)JFgJ%5J}EV9^r z=F=qeP9k4Kd`XkHlT()R{k#79n$4}J*<|tPi_qb1A2i}_`Fn;?>&by5;oJZU%Kd*6 zu-oph4p;jpcRhbhXo7P@z-9t$&%p1-{3ksxwkC7$5cXixr7ii?{2$qmJykUNvzC1C z>uwx>P2sGZZ$9mT3!Ywn^`2FI@#|;hr*p>sF(3U!y~dK(t-t!tANxxsO;^zzm+Vkb z2%3;*1f=2F0pmNQhbEdF!b0gO65qS{R5n!I{W^(xqCUIpy#$r z_;U^)Jq5|hml=h{!zDjT0ax>#9!b)!AfEwsy02b*ZbZ4L%JAY zcz98eE#`Dp*X%tnE}Dw}_H@KqKgCR;cOsc{_HCJi-H+z=$iU(J#&+EE^|fk!W|*F* z=Vi|09q}qRmxxOVX7vXM19(a4dyB=teAK$Y4Y`{dlaqM#9cPdkKW;3VC-uC(u5#3B zOtO01Nta*I7bwlA(p}I9!l)(Y;AE{jn$b7QJbGk?LVOFDRK_^rl8jA0YAf4(Q@*FB zCjmH4$+N$2Fj8uYrS}jqAI?(n9jCYI^&6BuS5;73S%53Stb<2t_alXkBIL2I;NCRb znP`QuhU*XLSE9R?jxj`VvV0LJ)VfKpJ*A~{FFsDQu|%2D#B=}YcxT?+o+kFqwtVjdE)Bm?F>Gg|r)R$yfKE#?>Bqo0Ws`Um4aL&T# zO0iLsx{)98skCl=Nu3Tfi+stg{xCFuym~U5I(JW-Ad-Y~{-|}-?`pHVBynGVI5)iP zj5$;{A!}A+5BqfCt)t&)i(z(5kSS%J$9)}_kePHLA~=ES(wXgt_|*Jc{qW^b;$8B< zAU=1EUd(I#sY&|}T z^6b{LwSni6TKtm^->c9O{eOJClr0kx3>^^Z6UHTp~B+6mIe<~%xL2FZ6XX0>u(|0Nkj3!t`hR!p zGPckV22XJEZIwwJnR&qpIcZ5bNjajmSEjQ4 zjP31|b&=F!5{ZEodMJ>9;;vxGJB}wJqU1*CQdn3%J|K|zxyM4nDf_B|kvqI?9$I0q}r) zGDz<564VE73LyNuXm_r392@*M#=m|*@*(i_^7;a}=Rn^p;m^Bg7vS&_ zw&7tXiKX3Io(dsM8b*5Vn#uIXoO=L4bEpe}8X2WyRMkfel|w&>B&}ACFCgIyaPcsT zU>iH8Dd>88Crw&I{~-CAS1(?=xJ-$qPgy8CwstE;U=tLLZEa2c4lVNp^_a~|wXC)Y z{2|jIpaV#O4R0$zv0bc(erZ$nmw>Qk8z#XNOWD?*mH8&%2)w43o-UcmmF6WjtdzkM znLz?rqqdQ(N*QAS19EotRIwawOWK*6JpqF=I$8y|qHOUT14fe4lBN@_>V_zs`i<@ zW@;I>6_X$_VbBR35K61~hyW9kCV>pf_DYut-}Q~$UQtt8SymP&Ev@0@2(2QOvW2^t zl~v-W{NC9rPk;u=)fmhrftW};xgucYuSnvwxP$H!5D+?2%O?ogJr|JyAssi;pP=_& zQN=tOZ9$OO|NI=}8{9k$XrWC-G`sqK5zk0rw-T3A%N|~R{=ly#@46-Z@-i@_as<$4 z^Hh2Ol=v9+cN(1GzizN>08|O^y~)x~Vb8%itY=qNmfJW>OG?TST()ys`txz&;T-|B zj|wQ&Pai)E_Vn<0 zDJ8~&Nkd-}QV|!>rl=?$ucD>lRod9tY96w)xAi>+uB6YvYefqz@af<+7e_d;aU!G`fPk!4MT2@w%V z`k#J{&0)#SkIaB9AZ#DH@DCsw@Y^z^bad-!%@dpsyQ_vKIr!}CC^33K%j`~z^b<2< z2-R_G%BOf0f;l%Crbw)C#J)NWE^#tUfo8<>vZkF#+o?3Of6SIce9K7-Aho_fX#^NK zd3#>!DK}R2Q$=?BqiE-<+x-Fb<@atk|INq5*D6r zy3wxL2iP{1d=e%hj;^-4zM5j4!5mSRFxiR;#7NAqPRLST{zWSbI5^;3`TrKKcwQ8= z#IOcP);&F2fQlbGJ!@_wB`YbYiHqKfiG?^4T{*hi_^^JlSit}Y_%}X6poVa|dxe>r z+W1RS23jUVrN~I7h)5_|^ZfqVRr6#iYf|uX?GryAh2sAjj)EQrFhL58N930K8~moD zOkk;HA%SQRNWt{*=miAiuI&>`j;%EFrw1cbro-D#)H4B||JT?Af;ma4Kn3fOx3aVq zh}Fq3NrN!Iq|}alfKpHt)Jrc_$^Bd%HD%e`$CsxowkPxLWW469O=k=Y5V9c;r(tE) z10$C;1nM#zGf8oPN>Bf_?w0~#1QM$!q@Vy(8heQWJ8xyBy9w=P`1Bi zQQ+#ZN=w#K%E8*{I7q_zg^G!DXyRX0&&X#YmkxACfP;8$jD12DzCaJ zEvJ(Z%Mh|4jbx7iy&uk{a%F60W@0^EX65^SF~maIBkTsi;=-}c@?mH14DHWrdshcQ z!CG~?;bEn|HTHy%T_;`vlA!T*D_dI|8!KrUXs|$erG_0tFI{wxc!6q$+5x}5vi7=z zYhIMpB17wsdD2oY&H@!Fa*$a9vT!Co7T96uy#eG|Kye5(OqafBQlTbao02Mm+PSJ{;A3KGQSj1WTe!?8?0qgq+gmqY`{;}WxMB7$4zr@48^71KC zGyb#PZd8U?2O-&=1CJ`L{*jq(spyDxH!EdFRasfr8i_7WGHQBOGM^2tQBqJ%!`5~D zIM2OBxW zL%Dz`#XCUsfF3(oBM_O}#Wq5}BEExxlF_4ZVvHt}-PMk^#?7InJvEh}t>yRorwZMm zg>!uT=2{$t%*#XrI08gwtTmO@ARq$@(ePAUs62e}7PM2!!NHBKT7z6IPX)a-=_5YN zf9v8i8LTYm!y7zGO3F8slu6{oBCM=~tkelS0_5l(-ZnPLT3I98mA^5^$HvqYHKSt^ z99mAg|Hp!xLyUGOrPyRC~V1{+$WO|keVn0 zsL|0Le=b8|Jq-e?Wv4I%{Dj;7N3cc-c?G#ZN6C=ew|#c311OoS9=@DE%>Yk^5SbY2 z@zhmrahiJO!#$J`?M5}MyWh$nL-i4GGDB=Px*mbN--QAYUtvYw+e6$GX#`yi(y=}3 zss{0^z|`K|wV=2R?OgJ%N1q3gl&}y>^wz(kfRO|^SQWR^`x5nHz>}RB^S$yu5B}Ww z&K?CaTUyM65+V-n^QwKE2#G;5*(a9#DA7gMli?Z#gCW*vfb9sWo!P^f_ty6+6=jIQ z1mT`VyT`(%V6d^zuRBxFo+3n6@f!b8s@5$Mm3 zUWSeg6@dX4YdgLLS)GtZ;8Oph8HLj)Qm23^pa`WXi=+9_#$tqRPb48KTs|j2D15JV z)(H4eHbUncg5}8%L_`^S9Yo0kaf8K>Z;LV1OgaBsXbo7`EWBhLk3O@$s|{U(Y6LDq z)EKC+5Dr9$>ra1Pqo5=a`*)8U#TY^Y4Z%Zh3=|Jgj2|G|-sms*EM+}F%oaQoWT*+F z#zNSCyQ-5P3jHzx=#|HO7Wy3WEziu*d)a^>+4=@YgjWPY`a!QSKsvR73R)NhUrlh? zJ!KiZnuk0lt`*dp0QFUpWc{(0+y1tJKn*{lTiEAA!9GdVD@(xTVluSA6JKLB&$5-EeS_Y*i(XUS@iR+bGwZspOw{*-0U%jQJn zSbEPO1i8U54*cXtDLS=CK`{;dI*2v(IT>#1DGEy#ecxix~|C~*H7IVs6oKJ`rw$a$Km%7SCQyFaSXtcc7allZ{(IgNdVwOW`=pT!QQ z!^F2Z34PMq_rz~}n@i1X7t2(=X=iE}Cv00;dp_o)Pnjii(jL%57ZYTl9(3=(;)BtS0t@gvq~&B@28k{D_4gNnqw)9=X!6s1 zynMX7AX}-uLljLyBb;WyC@-%7Wc)O;rw9sO(-0Zh&DHA`DV@OPj?lgXk3MOV9hzI* zlvZ8c)VZxnhf6Cd3DQv{Ei`R(1+Z-9>k^^ZI7`$kAF)8X6CGngDIXDX4`DPQBTo@X zMxIy%n0nX1Z{Sf;n**jTNK`WDXL{l(MkrBdyST*q%ExjB7ux?eT|8DnF)6-_ zARlXR(89)Ypznh+&C8bX&qAeE`+~4#nB?T#TsS5^H8wFUS2NJ2l1mJ7Oc(8dav~)f z0sLP%xNe48@2ZeS7XQnoEkc4wY;1of+mz+t=P_?6+rWmfnrr$?%TmbkNXW>`I-RjO z!?4hxq0LIm==!Sum{`8I>;j`7Ili}%=L}Z?s#dZZI~gB+%g>VhVhnqokN#hv{3#^B zzDQeX8+qxtsF|{0=H}< z(bncB8F`f!vEY!Y)r&`dojK@_{6NCyU*#%sSy2H20n~zV%(T>)*zL{D$!f>5Q(`zE z9&diAaZh?1vwi_cc#9SiW@aD_BPfUpG!gK5u$Pyscx{A*z2>4M+kiK$0v*DARwq>y z(UU3alv^p2GrG1a2ZOS-b##0*GaDNjJD3@?J$gDBZ|Yd6!z3*&O-WuP7nvLl*g7B? z_k>qhq7%g|@P9VIaV&3GU05*RkO!S_ygL?Ddwz5YOTjLGaGx65aYf6^YkAOeQ5Q+& zz^|^L(Ew;=&GYi2O7R>$AFVBI@ghJa2rxJ;Ul#`zvlVD6&GdcDavH~3j+XUReh%+J zE8>FQdcA4+3FMHX*ZialcI+sXg&kwEGL$i!;!xMOe)Qtp`9I_PYlHjEQCXG}E!G9< zy?7ZqqPM0iE_M!+gUVk(DT+4{?3TE_<#^JC?by>ZGaf&4fj4AynE6!nCBE5YP4lny zqu!50V_e!&4&^`e3Wf)g1uDw(JqMQ-GFd_{X!cX<{SylN6w7pG+uKXhExPMg-nR=H80VEM2%d0Y>%Yq zl6eJqFgx!wrb3s`kN>=*OP*?A<33}6Eyozyn=QjaV6nZ-^WMdyF|o1in>ER#)Jp?O z7T_Eb7u%++thnwb?;oBSmoC)RPE-OMZXo--@ofe?jqH`!;H8K2kva|DGshzYK}D!` zd(hc9a8ghvQn$)s$sed*)Q}{2@sCc=Ho8AkmyporO1$X0m>50TTBeqtdS0`)vrv3e zb&|K4Ly=*uo1A5oR$5lZ$|-PjxLR|Q z8ZC`aDhJL2loyImkfVyc&^fA^*O~CpQBxlN?Il=KS~@Xzy63(>?3c6mB5bbSu|}(i zi<{Hid95#Tx1Uf6LmwSuj+Er~q=t|<>r|L*KFR8K?=Y)*PoUQM>P)LBhIz%)g;yJl z+G)!96{%0*aM=oaWZTV%J_*iiXyGC1=jv48EbPN%5}qoTT=i=B^~=a2rl zBKS5iLK74Aa^GH9f@-oSaK!CaYrg>({JQXhT-vF9Y B%YFa= literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/13-allow-internet-access.png b/phoneblock/src/main/webapp/setup-ab/help/13-allow-internet-access.png new file mode 100644 index 0000000000000000000000000000000000000000..291f4d6a7d651bd7b11b4d47a1f46cadd4967802 GIT binary patch literal 96381 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�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 literal 0 HcmV?d00001 diff --git a/phoneblock/src/main/webapp/setup-ab/help/index.jsp b/phoneblock/src/main/webapp/setup-ab/help/index.jsp new file mode 100644 index 00000000..2ea42373 --- /dev/null +++ b/phoneblock/src/main/webapp/setup-ab/help/index.jsp @@ -0,0 +1,60 @@ + +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false"%> +<%@page import="de.haumacher.phoneblock.util.JspUtil"%> + +<% + request.setAttribute("title", "PhoneBlock-Anrufbeantworter einrichten"); +%> + + + + + + + +

+ +
+ + + + \ No newline at end of file diff --git a/phoneblock/src/main/webapp/setup-ab/index.jsp b/phoneblock/src/main/webapp/setup-ab/index.jsp new file mode 100644 index 00000000..be7532c0 --- /dev/null +++ b/phoneblock/src/main/webapp/setup-ab/index.jsp @@ -0,0 +1,98 @@ + +<%@page import="de.haumacher.phoneblock.ab.CreateABServlet"%> +<%@page import="de.haumacher.phoneblock.random.SecureRandomService"%> +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false"%> +<%@page import="de.haumacher.phoneblock.util.JspUtil"%> + +<% +request.setAttribute("title", "PhoneBlock-Anrufbeantworter"); + +HttpSession session = request.getSession(); +String username = (String) session.getAttribute("ab-username"); +String passwd = (String) session.getAttribute("ab-passwd"); +if (username == null) { + username = "ab-" + Math.abs(SecureRandomService.getInstance().getRnd().nextLong()); + passwd = Long.toHexString(Math.abs(SecureRandomService.getInstance().getRnd().nextLong())); + + session.setAttribute("ab-username", username); + session.setAttribute("ab-passwd", passwd); +} +%> + + + + + + + +
+
+

PhoneBlock-Anrufbeantworter

+ +

+ Der PhoneBlock-Anrufbeantworter wird als "IP-Telefon mit Anrufbeantworter" an der Fritz!Box (oder einem + anderen Internet-Router) eingerichtet. +

+
+ +
+ +
+
+ +
+
+
+

+ + + + +

+
+
+
+ +
+
+ +
+
+
+

+ <%=JspUtil.quote(username) %> +

+
+
+
+ +
+
+ +
+
+
+

+ <%=JspUtil.quote(passwd) %> +

+
+
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ + + + \ No newline at end of file From ff01c3c542975f7ad1f350737317dc0f811d6b77 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 13 Nov 2023 22:25:22 +0100 Subject: [PATCH 028/153] Fixed accept-anonymous option accepting yes/no. --- .../de/haumacher/phoneblock/answerbot/AnswerbotConfig.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index 0d88f5f0..7a289157 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -6,6 +6,7 @@ import java.io.File; import org.kohsuke.args4j.Option; +import org.mjsip.config.YesNoHandler; import org.mjsip.media.FlowSpec.Direction; import org.mjsip.media.MediaDesc; import org.mjsip.sip.config.MediaDescHandler; @@ -43,7 +44,7 @@ public String getRecordingDir() { @Option(name = "--silence-db", usage = "The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") private double silenceDb = -30; - @Option(name = "--accept-anonymous", usage = "Whether to let PhoneBlock accept anonymous calls. This is not recommended. Better configure a separate answering machine in you router to handle anonymous calls.") + @Option(name = "--accept-anonymous", handler = YesNoHandler.class, usage = "Whether to let PhoneBlock accept anonymous calls. This is not recommended. Better configure a separate answering machine in you router to handle anonymous calls.") private boolean _acceptAnonymous = false; @Option(name = "--min-votes", usage = "The minimum number of PhoneBlock votes for a number to be consideres SPAM.") From 46a5262de5648ff3485780c1b6f5a7d3db8738fd Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 13 Nov 2023 22:27:27 +0100 Subject: [PATCH 029/153] Added starting point for WAV resampling tool. The answering machine must support different audio formats depending on the remote receiver. --- phoneblock-ab/pom.xml | 7 + .../phoneblock/answerbot/ResampleWav.java | 228 ++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml index 17a9357b..71976c38 100644 --- a/phoneblock-ab/pom.xml +++ b/phoneblock-ab/pom.xml @@ -51,6 +51,13 @@ mjsip-ua 1.9.0-SNAPSHOT
+ + + com.github.dnault + libresample4j + 0.1.0-SNAPSHOT + + diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java new file mode 100644 index 00000000..93a42f67 --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.FloatBuffer; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.mjsip.sound.AudioFile; + +import com.laszlosystems.libresample4j.Resampler; + +/** + * Tool for producing versions of WAV files of different sample rate from a master file. + */ +public class ResampleWav { + + public static void main(String[] args) throws IOException, UnsupportedAudioFileException { + AudioInputStream in = AudioFile.getAudioFileInputStream(args[0]); + AudioInputStream signed = encode(in, Encoding.PCM_SIGNED); + AudioInputStream mono = mono(signed); + AudioInputStream scaled = scale(mono, 16); + AudioInputStream resampled = resample(scaled, 16000); + AudioInputStream encoded = encode(resampled, Encoding.ALAW); + + try (OutputStream out = AudioFile.getAudioFileOutputStream(args[0] + "-16000.wav", encoded.getFormat())) { + byte buffer[] = new byte[4096]; + int direct; + while ((direct = encoded.read(buffer)) > 0) { + out.write(buffer, 0, direct); + } + } + } + + private static AudioInputStream encode(AudioInputStream in, Encoding encoding) { + return AudioSystem.getAudioInputStream(withEncoding(in.getFormat(), encoding), in); + } + + private static AudioFormat withEncoding(AudioFormat format, Encoding encoding) { + int sampleSizeInBits = encoding == Encoding.ALAW || encoding == Encoding.ULAW ? 8 : format.getSampleSizeInBits(); + return new AudioFormat(encoding, + format.getSampleRate(), + sampleSizeInBits, + format.getChannels(), + format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), + format.getFrameRate(), + format.isBigEndian()); + } + + private static AudioInputStream resample(AudioInputStream in, int sampleRate) { + return AudioSystem.getAudioInputStream(withSampleRate(in.getFormat(), sampleRate), in); + } + + private static AudioInputStream resample2(AudioInputStream in, int sampleRate) { + AudioFormat inputFormat = in.getFormat(); + float origSampleRate = inputFormat.getSampleRate(); + if (sampleRate == origSampleRate) { + return in; + } + + AudioFormat outputFormat = withSampleRate(inputFormat, sampleRate); + + if (inputFormat.getChannels() > 1) { + throw new IllegalArgumentException("Only mono streams supported."); + } + if (inputFormat.getEncoding() != Encoding.PCM_SIGNED) { + throw new IllegalArgumentException("Only PCM_SIGNED streams supported."); + } + + double factor = ((double) sampleRate) / origSampleRate; + + Resampler resampler = new Resampler(true, factor, factor); + + int frameSize = inputFormat.getFrameSize(); + + byte[] inBuffer = new byte[frameSize * 1024]; + byte[] outBuffer = new byte[frameSize * 1024]; + + FloatBuffer inputBuffer = FloatBuffer.allocate(1024); + FloatBuffer outputBuffer = FloatBuffer.allocate(1024); + + int sampleSizeInBits = inputFormat.getSampleSizeInBits(); + int fullScale = (1 << (sampleSizeInBits - 1)) - 1; + + boolean bigEndian = inputFormat.isBigEndian(); + int sampleSizeInBytes = sampleSizeInBits / 8; + if (sampleSizeInBits % 8 > 0) { + sampleSizeInBytes++; + } + int maxShift = (sampleSizeInBytes - 1) * 8; + + long targetLength = (long) (in.getFrameLength() * sampleRate / origSampleRate); + + return new AudioInputStream(new InputStream() { + int inSize = 0; + + int outSize = 0; + int outPos = 0; + + @Override + public int read() throws IOException { + while (true) { + if (outPos < outSize) { + return outBuffer[outPos++] & 0xFF; + } + + outPos = 0; + outSize = 0; + + int direct = in.read(inBuffer, inSize, inBuffer.length - inSize); + if (direct < 0) { + boolean complete = resampler.process(factor, inputBuffer, true, outputBuffer); + inputBuffer.flip(); + if (complete) { + return -1; + } + + encode(); + continue; + } + + inSize += direct; + + int max = inSize - (inSize % frameSize); + for (int n = 0; n < max;) { + int sample; + + if (bigEndian) { + // Read sign. + sample = inBuffer[n++] << maxShift; + for (int shift = maxShift - 1; shift >= 0; shift-=8) { + sample |= (inBuffer[n++] & 0xFF) << shift; + } + } else { + sample = 0; + for (int shift = 0; shift < maxShift; shift+=8) { + sample |= (inBuffer[n++] & 0xFF) << shift; + } + // Read sign. + sample |= inBuffer[n++] << maxShift; + } + + float value = ((float) sample) / fullScale; + inputBuffer.put(value); + } + + if (max < inSize) { + System.arraycopy(inBuffer, max, inBuffer, 0, inSize - max); + inSize -= max; + } else { + inSize = 0; + } + + resampler.process(factor, inputBuffer, false, outputBuffer); + inputBuffer.flip(); + encode(); + } + } + + private void encode() { + while (outputBuffer.hasRemaining()) { + int outSample = (int) (outputBuffer.get() * fullScale); + + if (bigEndian) { + for (int shift = maxShift; shift >= 0; shift -= 8) { + outBuffer[outSize++] = (byte) (outSample >>> shift); + } + } else { + for (int shift = 0; shift <= maxShift; shift += 8) { + outBuffer[outSize++] = (byte) (outSample >>> shift); + } + } + } + outputBuffer.flip(); + } + }, outputFormat, targetLength); + } + + private static AudioFormat withSampleRate(AudioFormat format, int sampleRate) { + return new AudioFormat( + format.getEncoding(), + sampleRate, + format.getSampleSizeInBits(), + format.getChannels(), + format.getFrameSize(), + sampleRate * format.getFrameRate() / format.getSampleRate(), + format.isBigEndian()); + } + + private static AudioInputStream scale(AudioInputStream in, int sampleSizeInBits) { + return AudioSystem.getAudioInputStream(withSampleSizeInBits(in.getFormat(), sampleSizeInBits), in); + } + + private static AudioFormat withSampleSizeInBits(AudioFormat format, int sampleSizeInBits) { + return new AudioFormat( + format.getEncoding(), + format.getSampleRate(), + sampleSizeInBits, + format.getChannels(), + format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), + format.getFrameRate(), + format.isBigEndian()); + } + + private static AudioInputStream mono(AudioInputStream in) { + return AudioSystem.getAudioInputStream(withChannels(in.getFormat(), 1), in); + } + + private static AudioFormat withChannels(AudioFormat format, int channels) { + return new AudioFormat( + format.getEncoding(), + format.getSampleRate(), + format.getSampleSizeInBits(), + channels, + format.getFrameSize() * channels / format.getChannels(), + format.getFrameRate(), + format.isBigEndian()); + } + +} From 20da882dc75f5eb3fd9f6aa7e448b598953f23f1 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 13 Nov 2023 23:56:46 +0100 Subject: [PATCH 030/153] Fixed buffer handling in resample tool, use libresample4j. --- .../phoneblock/answerbot/ResampleWav.java | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java index 93a42f67..89757f6a 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java @@ -17,6 +17,7 @@ import org.mjsip.sound.AudioFile; import com.laszlosystems.libresample4j.Resampler; +import com.laszlosystems.libresample4j.SampleBuffers; /** * Tool for producing versions of WAV files of different sample rate from a master file. @@ -25,16 +26,17 @@ public class ResampleWav { public static void main(String[] args) throws IOException, UnsupportedAudioFileException { AudioInputStream in = AudioFile.getAudioFileInputStream(args[0]); - AudioInputStream signed = encode(in, Encoding.PCM_SIGNED); - AudioInputStream mono = mono(signed); - AudioInputStream scaled = scale(mono, 16); - AudioInputStream resampled = resample(scaled, 16000); - AudioInputStream encoded = encode(resampled, Encoding.ALAW); - try (OutputStream out = AudioFile.getAudioFileOutputStream(args[0] + "-16000.wav", encoded.getFormat())) { + in = encode(in, Encoding.PCM_SIGNED); + in = mono(in); + in = resample2(in, 16000); + in = scale(in, 16); + in = encode(in, Encoding.ALAW); + + try (OutputStream out = AudioFile.getAudioFileOutputStream(args[0] + "-16000.wav", in.getFormat())) { byte buffer[] = new byte[4096]; int direct; - while ((direct = encoded.read(buffer)) > 0) { + while ((direct = in.read(buffer)) > 0) { out.write(buffer, 0, direct); } } @@ -86,6 +88,28 @@ private static AudioInputStream resample2(AudioInputStream in, int sampleRate) { FloatBuffer inputBuffer = FloatBuffer.allocate(1024); FloatBuffer outputBuffer = FloatBuffer.allocate(1024); + + SampleBuffers buffers = new SampleBuffers() { + @Override + public int getInputBufferLength() { + return inputBuffer.remaining(); + } + + @Override + public int getOutputBufferLength() { + return outputBuffer.remaining(); + } + + @Override + public void produceInput(float[] array, int offset, int length) { + inputBuffer.get(array, offset, length); + } + + @Override + public void consumeOutput(float[] array, int offset, int length) { + outputBuffer.put(array, offset, length); + } + }; int sampleSizeInBits = inputFormat.getSampleSizeInBits(); int fullScale = (1 << (sampleSizeInBits - 1)) - 1; @@ -117,7 +141,7 @@ public int read() throws IOException { int direct = in.read(inBuffer, inSize, inBuffer.length - inSize); if (direct < 0) { - boolean complete = resampler.process(factor, inputBuffer, true, outputBuffer); + boolean complete = resampler.process(factor, buffers, true); inputBuffer.flip(); if (complete) { return -1; @@ -129,7 +153,7 @@ public int read() throws IOException { inSize += direct; - int max = inSize - (inSize % frameSize); + int max = Math.min(inSize - (inSize % frameSize), inputBuffer.remaining() * frameSize); for (int n = 0; n < max;) { int sample; @@ -159,13 +183,15 @@ public int read() throws IOException { inSize = 0; } - resampler.process(factor, inputBuffer, false, outputBuffer); + inputBuffer.flip(); + resampler.process(factor, buffers, false); inputBuffer.flip(); encode(); } } private void encode() { + outputBuffer.flip(); while (outputBuffer.hasRemaining()) { int outSample = (int) (outputBuffer.get() * fullScale); @@ -179,7 +205,7 @@ private void encode() { } } } - outputBuffer.flip(); + outputBuffer.clear(); } }, outputFormat, targetLength); } From 7e14cba0be8815816df0914845143d21ad823375 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 18:01:48 +0100 Subject: [PATCH 031/153] Adjusted to modularization of mjsip, introduced answer bot module. --- phoneblock-ab/.classpath | 2 ++ phoneblock-ab/src/main/java/module-info.java | 29 ++++++++++++++++++++ phoneblock/src/main/java/module-info.java | 9 ++++-- 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 phoneblock-ab/src/main/java/module-info.java diff --git a/phoneblock-ab/.classpath b/phoneblock-ab/.classpath index 04eb2980..1d314d0a 100644 --- a/phoneblock-ab/.classpath +++ b/phoneblock-ab/.classpath @@ -15,12 +15,14 @@ + + diff --git a/phoneblock-ab/src/main/java/module-info.java b/phoneblock-ab/src/main/java/module-info.java new file mode 100644 index 00000000..394c22ca --- /dev/null +++ b/phoneblock-ab/src/main/java/module-info.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ + +/** + * The PhoneBlock answer bot. + */ +module phoneblock.ab { + requires phoneblock.shared; + + requires org.mjsip.sound; + requires org.mjsip.sip; + requires org.mjsip.ua; + requires org.mjsip.util; + requires org.mjsip.net; + + requires args4j; + + requires java.base; + requires java.desktop; + requires de.haumacher.msgbuf; + requires org.slf4j; + requires org.tinylog.api; + + exports de.haumacher.phoneblock.answerbot; + exports de.haumacher.phoneblock.answerbot.tools; + + opens de.haumacher.phoneblock.answerbot.tools to args4j; +} \ No newline at end of file diff --git a/phoneblock/src/main/java/module-info.java b/phoneblock/src/main/java/module-info.java index 8d945512..a400d1fa 100644 --- a/phoneblock/src/main/java/module-info.java +++ b/phoneblock/src/main/java/module-info.java @@ -26,9 +26,12 @@ requires phoneblock.shared; requires phoneblock.ab; - requires mjsip.sip; - requires mjsip.util; - requires mjsip.ua; + + requires org.mjsip.sound; + requires org.mjsip.sip; + requires org.mjsip.ua; + requires org.mjsip.util; + requires com.google.api.client; requires com.google.auth; requires com.google.auth.oauth2; From 746f933fb1786a246c18f52d284d8ddc5ded7a89 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 18:03:39 +0100 Subject: [PATCH 032/153] Removed libresample4j dependency. The Java default implementation for audio resampling is good enough. --- phoneblock-ab/pom.xml | 7 - .../phoneblock/answerbot/ResampleWav.java | 254 ------------------ .../answerbot/tools/WavResampler.java | 149 ++++++++++ 3 files changed, 149 insertions(+), 261 deletions(-) delete mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/WavResampler.java diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml index 71976c38..17a9357b 100644 --- a/phoneblock-ab/pom.xml +++ b/phoneblock-ab/pom.xml @@ -51,13 +51,6 @@ mjsip-ua 1.9.0-SNAPSHOT - - - com.github.dnault - libresample4j - 0.1.0-SNAPSHOT - - diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java deleted file mode 100644 index 89757f6a..00000000 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/ResampleWav.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. - */ -package de.haumacher.phoneblock.answerbot; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.FloatBuffer; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioFormat.Encoding; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.UnsupportedAudioFileException; - -import org.mjsip.sound.AudioFile; - -import com.laszlosystems.libresample4j.Resampler; -import com.laszlosystems.libresample4j.SampleBuffers; - -/** - * Tool for producing versions of WAV files of different sample rate from a master file. - */ -public class ResampleWav { - - public static void main(String[] args) throws IOException, UnsupportedAudioFileException { - AudioInputStream in = AudioFile.getAudioFileInputStream(args[0]); - - in = encode(in, Encoding.PCM_SIGNED); - in = mono(in); - in = resample2(in, 16000); - in = scale(in, 16); - in = encode(in, Encoding.ALAW); - - try (OutputStream out = AudioFile.getAudioFileOutputStream(args[0] + "-16000.wav", in.getFormat())) { - byte buffer[] = new byte[4096]; - int direct; - while ((direct = in.read(buffer)) > 0) { - out.write(buffer, 0, direct); - } - } - } - - private static AudioInputStream encode(AudioInputStream in, Encoding encoding) { - return AudioSystem.getAudioInputStream(withEncoding(in.getFormat(), encoding), in); - } - - private static AudioFormat withEncoding(AudioFormat format, Encoding encoding) { - int sampleSizeInBits = encoding == Encoding.ALAW || encoding == Encoding.ULAW ? 8 : format.getSampleSizeInBits(); - return new AudioFormat(encoding, - format.getSampleRate(), - sampleSizeInBits, - format.getChannels(), - format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), - format.getFrameRate(), - format.isBigEndian()); - } - - private static AudioInputStream resample(AudioInputStream in, int sampleRate) { - return AudioSystem.getAudioInputStream(withSampleRate(in.getFormat(), sampleRate), in); - } - - private static AudioInputStream resample2(AudioInputStream in, int sampleRate) { - AudioFormat inputFormat = in.getFormat(); - float origSampleRate = inputFormat.getSampleRate(); - if (sampleRate == origSampleRate) { - return in; - } - - AudioFormat outputFormat = withSampleRate(inputFormat, sampleRate); - - if (inputFormat.getChannels() > 1) { - throw new IllegalArgumentException("Only mono streams supported."); - } - if (inputFormat.getEncoding() != Encoding.PCM_SIGNED) { - throw new IllegalArgumentException("Only PCM_SIGNED streams supported."); - } - - double factor = ((double) sampleRate) / origSampleRate; - - Resampler resampler = new Resampler(true, factor, factor); - - int frameSize = inputFormat.getFrameSize(); - - byte[] inBuffer = new byte[frameSize * 1024]; - byte[] outBuffer = new byte[frameSize * 1024]; - - FloatBuffer inputBuffer = FloatBuffer.allocate(1024); - FloatBuffer outputBuffer = FloatBuffer.allocate(1024); - - SampleBuffers buffers = new SampleBuffers() { - @Override - public int getInputBufferLength() { - return inputBuffer.remaining(); - } - - @Override - public int getOutputBufferLength() { - return outputBuffer.remaining(); - } - - @Override - public void produceInput(float[] array, int offset, int length) { - inputBuffer.get(array, offset, length); - } - - @Override - public void consumeOutput(float[] array, int offset, int length) { - outputBuffer.put(array, offset, length); - } - }; - - int sampleSizeInBits = inputFormat.getSampleSizeInBits(); - int fullScale = (1 << (sampleSizeInBits - 1)) - 1; - - boolean bigEndian = inputFormat.isBigEndian(); - int sampleSizeInBytes = sampleSizeInBits / 8; - if (sampleSizeInBits % 8 > 0) { - sampleSizeInBytes++; - } - int maxShift = (sampleSizeInBytes - 1) * 8; - - long targetLength = (long) (in.getFrameLength() * sampleRate / origSampleRate); - - return new AudioInputStream(new InputStream() { - int inSize = 0; - - int outSize = 0; - int outPos = 0; - - @Override - public int read() throws IOException { - while (true) { - if (outPos < outSize) { - return outBuffer[outPos++] & 0xFF; - } - - outPos = 0; - outSize = 0; - - int direct = in.read(inBuffer, inSize, inBuffer.length - inSize); - if (direct < 0) { - boolean complete = resampler.process(factor, buffers, true); - inputBuffer.flip(); - if (complete) { - return -1; - } - - encode(); - continue; - } - - inSize += direct; - - int max = Math.min(inSize - (inSize % frameSize), inputBuffer.remaining() * frameSize); - for (int n = 0; n < max;) { - int sample; - - if (bigEndian) { - // Read sign. - sample = inBuffer[n++] << maxShift; - for (int shift = maxShift - 1; shift >= 0; shift-=8) { - sample |= (inBuffer[n++] & 0xFF) << shift; - } - } else { - sample = 0; - for (int shift = 0; shift < maxShift; shift+=8) { - sample |= (inBuffer[n++] & 0xFF) << shift; - } - // Read sign. - sample |= inBuffer[n++] << maxShift; - } - - float value = ((float) sample) / fullScale; - inputBuffer.put(value); - } - - if (max < inSize) { - System.arraycopy(inBuffer, max, inBuffer, 0, inSize - max); - inSize -= max; - } else { - inSize = 0; - } - - inputBuffer.flip(); - resampler.process(factor, buffers, false); - inputBuffer.flip(); - encode(); - } - } - - private void encode() { - outputBuffer.flip(); - while (outputBuffer.hasRemaining()) { - int outSample = (int) (outputBuffer.get() * fullScale); - - if (bigEndian) { - for (int shift = maxShift; shift >= 0; shift -= 8) { - outBuffer[outSize++] = (byte) (outSample >>> shift); - } - } else { - for (int shift = 0; shift <= maxShift; shift += 8) { - outBuffer[outSize++] = (byte) (outSample >>> shift); - } - } - } - outputBuffer.clear(); - } - }, outputFormat, targetLength); - } - - private static AudioFormat withSampleRate(AudioFormat format, int sampleRate) { - return new AudioFormat( - format.getEncoding(), - sampleRate, - format.getSampleSizeInBits(), - format.getChannels(), - format.getFrameSize(), - sampleRate * format.getFrameRate() / format.getSampleRate(), - format.isBigEndian()); - } - - private static AudioInputStream scale(AudioInputStream in, int sampleSizeInBits) { - return AudioSystem.getAudioInputStream(withSampleSizeInBits(in.getFormat(), sampleSizeInBits), in); - } - - private static AudioFormat withSampleSizeInBits(AudioFormat format, int sampleSizeInBits) { - return new AudioFormat( - format.getEncoding(), - format.getSampleRate(), - sampleSizeInBits, - format.getChannels(), - format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), - format.getFrameRate(), - format.isBigEndian()); - } - - private static AudioInputStream mono(AudioInputStream in) { - return AudioSystem.getAudioInputStream(withChannels(in.getFormat(), 1), in); - } - - private static AudioFormat withChannels(AudioFormat format, int channels) { - return new AudioFormat( - format.getEncoding(), - format.getSampleRate(), - format.getSampleSizeInBits(), - channels, - format.getFrameSize() * channels / format.getChannels(), - format.getFrameRate(), - format.isBigEndian()); - } - -} diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/WavResampler.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/WavResampler.java new file mode 100644 index 00000000..6f6e05a3 --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/WavResampler.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot.tools; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.kohsuke.args4j.Argument; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.mjsip.sound.AudioFile; + +/** + * Tool for producing versions of WAV files of different sample rate from a master file. + */ +public class WavResampler { + + static class Options { + @Argument(usage = "The input audio files.", required = true, multiValued = true) + String[] inputs; + + @Option(name = "-o", usage = "The output audio file name.") + String output; + + @Option(name = "-r", usage = "The sample rate to use.") + int sampleRate = 16000; + + public String outputName(String inputName) { + return output != null && inputs.length == 1 ? output : baseName(inputName) + "-" + sampleRate + ".wav"; + } + } + + static String baseName(String fileName) { + int sepIndex = fileName.lastIndexOf('.'); + if (sepIndex < 0) { + return fileName; + } + return fileName.substring(0, sepIndex); + } + + /** + * Main entry point form the command line. + */ + public static void main(String[] args) throws IOException, UnsupportedAudioFileException, CmdLineException { + Options config = new Options(); + CmdLineParser parser = new CmdLineParser(config); + parser.parseArgument(args); + + for (String input : config.inputs) { + convertToALaw(input, config.outputName(input), config.sampleRate); + } + } + + /** + * Converts a given audio file to ALaw encoded WAV with the given sample rate. + * + * @param inFileName The source audio file. + * @param outFileName The converted audio file to create. + * @param sampleRate The sample rate to use for the target audio file. + */ + public static void convertToALaw(String inFileName, String outFileName, int sampleRate) + throws FileNotFoundException, IOException, UnsupportedAudioFileException { + AudioInputStream in = AudioFile.getAudioFileInputStream(inFileName); + + in = encode(in, Encoding.PCM_SIGNED); + in = mono(in); + in = resample(in, sampleRate); + in = scale(in, 16); + in = encode(in, Encoding.ALAW); + + try (OutputStream out = AudioFile.getAudioFileOutputStream(outFileName, in.getFormat())) { + byte buffer[] = new byte[4096]; + int direct; + while ((direct = in.read(buffer)) > 0) { + out.write(buffer, 0, direct); + } + } + } + + private static AudioInputStream encode(AudioInputStream in, Encoding encoding) { + return AudioSystem.getAudioInputStream(withEncoding(in.getFormat(), encoding), in); + } + + private static AudioFormat withEncoding(AudioFormat format, Encoding encoding) { + int sampleSizeInBits = encoding == Encoding.ALAW || encoding == Encoding.ULAW ? 8 : (format.getSampleSizeInBits() < 16 ? 16 : format.getSampleSizeInBits()); + return new AudioFormat(encoding, + format.getSampleRate(), + sampleSizeInBits, + format.getChannels(), + format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), + format.getFrameRate(), + format.isBigEndian()); + } + + private static AudioInputStream resample(AudioInputStream in, int sampleRate) { + return AudioSystem.getAudioInputStream(withSampleRate(in.getFormat(), sampleRate), in); + } + + private static AudioFormat withSampleRate(AudioFormat format, int sampleRate) { + return new AudioFormat( + format.getEncoding(), + sampleRate, + format.getSampleSizeInBits(), + format.getChannels(), + format.getFrameSize(), + sampleRate * format.getFrameRate() / format.getSampleRate(), + format.isBigEndian()); + } + + private static AudioInputStream scale(AudioInputStream in, int sampleSizeInBits) { + return AudioSystem.getAudioInputStream(withSampleSizeInBits(in.getFormat(), sampleSizeInBits), in); + } + + private static AudioFormat withSampleSizeInBits(AudioFormat format, int sampleSizeInBits) { + return new AudioFormat( + format.getEncoding(), + format.getSampleRate(), + sampleSizeInBits, + format.getChannels(), + format.getFrameSize() * sampleSizeInBits / format.getSampleSizeInBits(), + format.getFrameRate(), + format.isBigEndian()); + } + + private static AudioInputStream mono(AudioInputStream in) { + return AudioSystem.getAudioInputStream(withChannels(in.getFormat(), 1), in); + } + + private static AudioFormat withChannels(AudioFormat format, int channels) { + return new AudioFormat( + format.getEncoding(), + format.getSampleRate(), + format.getSampleSizeInBits(), + channels, + format.getFrameSize() * channels / format.getChannels(), + format.getFrameRate(), + format.isBigEndian()); + } + +} From b38940d5b451f7ce1c48e656fce54dbac5417fd9 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 18:04:23 +0100 Subject: [PATCH 033/153] Added tool to initialize an answer bot conversion from recorded audio. --- phoneblock-ab/conversation/hello/.gitignore | 2 + .../conversation/question/.gitignore | 2 + .../conversation/still-there/.gitignore | 2 + .../conversation/who-is-calling/.gitignore | 2 + .../tools/ConversationInitializer.java | 74 +++++++++++++++++++ 5 files changed, 82 insertions(+) create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java diff --git a/phoneblock-ab/conversation/hello/.gitignore b/phoneblock-ab/conversation/hello/.gitignore index 79a66b20..1e697ecb 100644 --- a/phoneblock-ab/conversation/hello/.gitignore +++ b/phoneblock-ab/conversation/hello/.gitignore @@ -1 +1,3 @@ /*.wav +/PCMA-WB/ +/PCMA/ diff --git a/phoneblock-ab/conversation/question/.gitignore b/phoneblock-ab/conversation/question/.gitignore index 79a66b20..1e697ecb 100644 --- a/phoneblock-ab/conversation/question/.gitignore +++ b/phoneblock-ab/conversation/question/.gitignore @@ -1 +1,3 @@ /*.wav +/PCMA-WB/ +/PCMA/ diff --git a/phoneblock-ab/conversation/still-there/.gitignore b/phoneblock-ab/conversation/still-there/.gitignore index 79a66b20..1e697ecb 100644 --- a/phoneblock-ab/conversation/still-there/.gitignore +++ b/phoneblock-ab/conversation/still-there/.gitignore @@ -1 +1,3 @@ /*.wav +/PCMA-WB/ +/PCMA/ diff --git a/phoneblock-ab/conversation/who-is-calling/.gitignore b/phoneblock-ab/conversation/who-is-calling/.gitignore index 79a66b20..1e697ecb 100644 --- a/phoneblock-ab/conversation/who-is-calling/.gitignore +++ b/phoneblock-ab/conversation/who-is-calling/.gitignore @@ -1 +1,3 @@ /*.wav +/PCMA-WB/ +/PCMA/ diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java new file mode 100644 index 00000000..1dbb6eff --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot.tools; + +import java.io.File; +import java.io.IOException; + +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.mjsip.ua.sound.WavFileSplitter; + +/** + * Tool to set-up a conversation directory with appropriate WAV files. + */ +public class ConversationInitializer { + + static class Options { + @Option(name = "-c") + String conversationDir = "./conversation"; + } + + static class Variant { + int sampleRate; + String formatName; + + /** + * Creates a {@link Variant}. + */ + public Variant(String format, int sampleRate) { + this.formatName = format; + this.sampleRate = sampleRate; + } + } + + static final Variant[] VARIANTS = { + new Variant("PCMA-WB", 16000), + new Variant("PCMA", 8000), + }; + + /** + * Main entry point form the command line. + */ + public static void main(String[] args) throws CmdLineException, IOException, UnsupportedAudioFileException { + Options options = new Options(); + new CmdLineParser(options).parseArgument(args); + + for (File dir : new File(options.conversationDir).listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { + for (File input : dir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { + for (Variant variant : VARIANTS) { + String baseName = WavResampler.baseName(input.getName()); + File tmp = File.createTempFile(baseName, ".wav", dir); + WavResampler.convertToALaw(input.getAbsolutePath(), tmp.getAbsolutePath(), variant.sampleRate); + + WavFileSplitter splitter = new WavFileSplitter(tmp) { + @Override + protected String getOutputFileName(int partId) { + return baseName + "-" + partId + ".wav"; + } + }; + File outputDir = new File(dir, variant.formatName); + outputDir.mkdir(); + splitter.setOutputDir(outputDir); + splitter.run(); + + tmp.delete(); + } + } + } + } +} From 4d9b61ddcfd67ab58d2c4c40903b00e1c71e9c56 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 18:36:56 +0100 Subject: [PATCH 034/153] Better defined types of speech produced by the answer bot. --- .../phoneblock/answerbot/AnswerBot.java | 19 ++-- .../phoneblock/answerbot/DialogState.java | 83 ++++++++++++++++ .../phoneblock/answerbot/DialogueFactory.java | 6 +- .../answerbot/SpeechDispatcher.java | 95 +++---------------- .../phoneblock/answerbot/SpeechType.java | 32 +++++++ .../tools/ConversationInitializer.java | 51 ++++++---- 6 files changed, 174 insertions(+), 112 deletions(-) create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogState.java create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechType.java diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 47faf4fe..31ef0b22 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -88,7 +88,7 @@ public class AnswerBot extends MultipleUAS { VERSION = version == null ? "unknown" : version; } - private final Map> _audioFragments; + private final Map> _audioFragments; private AnswerbotOptions _botConfig; @@ -102,16 +102,21 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function _configForUser = configForUser; _botConfig = botOptions; - Map> audioFragments = new HashMap<>(); + Map> audioFragments = new HashMap<>(); File conversationDir = botOptions.conversationDir(); - for (File type : conversationDir.listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { + for (SpeechType state : SpeechType.values()) { ArrayList files = new ArrayList<>(); - String typeName = type.getName(); - audioFragments.put(typeName, files); + + File type = new File(conversationDir, state.getDirName()); + audioFragments.put(state, files); for (File wav : type.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { files.add(wav); - - LOG.info("Found audio fragment for " + typeName + ": " + wav.getPath()); + } + int cnt = files.size(); + if (cnt == 0) { + LOG.warn("Found no audio fragment for dialogue state " + state + "."); + } else { + LOG.info("Found " + cnt + " audio fragment for dialogue state " + state + "."); } } _audioFragments = audioFragments; diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogState.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogState.java new file mode 100644 index 00000000..4b136bd4 --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogState.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +/** + * The state of the conversation. + */ +public enum DialogState { + /** + * Say hello after accepting the call. + */ + HELLO, + + /** + * Waiting for the counterpart to introduce himself. + */ + WAITING_FOR_INTRO, + + /** + * Ask the counterpart who is calling. + */ + WHO_IS_CALLING, + + /** + * Waiting for a response from the counterpart. + */ + WAITING_FOR_ANSWER, + + /** + * The counterpart did not say anything, ask if he is still there. + */ + NO_ANSWER, + + /** + * Listening to some answer from the counterpart; + */ + LISTENING, + + /** + * Asking a silly question in response to something the counterpart said. + */ + ASKING; + + /** + * If no event happened, what to say next? + */ + public DialogState next() { + switch (this) { + case HELLO: return WAITING_FOR_INTRO; + case WAITING_FOR_INTRO: return WHO_IS_CALLING; + case WHO_IS_CALLING: return WAITING_FOR_INTRO; + case WAITING_FOR_ANSWER: return NO_ANSWER; + case NO_ANSWER: return WAITING_FOR_ANSWER; + case LISTENING: return LISTENING; + case ASKING: return WAITING_FOR_ANSWER; + } + throw new AssertionError("No such state."); + } + + /** + * The name of the directory to lookup audio files to play in a certain state of the dialogue. + */ + public SpeechType getSpeechType() { + switch (this) { + case HELLO: return SpeechType.HELLO; + case WAITING_FOR_INTRO: return SpeechType.WAITING; + case WHO_IS_CALLING: return SpeechType.WHO_IS_CALLING; + case WAITING_FOR_ANSWER: return SpeechType.WAITING; + case NO_ANSWER: return SpeechType.STILL_THERE; + case LISTENING: return SpeechType.WAITING; + case ASKING: return SpeechType.QUESTION; + } + throw new AssertionError("No such state."); + } + + /** + * Whether the answer bot is (almost) silent in this state. + */ + boolean isSilent() { + return this == WAITING_FOR_ANSWER || this == WAITING_FOR_INTRO || this == LISTENING; + } +} \ No newline at end of file diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index cdffe026..07456323 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -44,14 +44,14 @@ * {@link StreamerFactory} creating a bot correspondence. */ public final class DialogueFactory implements StreamerFactory { - private final Map> _audioFragments; + private final Map> _audioFragments; private final String _recordingFile; private DialogOptions _config; /** * Creates a {@link DialogueFactory}. */ - public DialogueFactory(DialogOptions config, Map> audioFragments, String recordingFile) { + public DialogueFactory(DialogOptions config, Map> audioFragments, String recordingFile) { _config = config; _audioFragments = audioFragments; _recordingFile = recordingFile; @@ -110,7 +110,7 @@ public void onRtpStreamReceiverTerminated(RtpStreamReceiver rr, Exception error) } StreamerOptions options = StreamerOptions.builder().build(); return new AudioStreamer(flow_spec, tx, rx, options); - } catch (IOException | UnsupportedAudioFileException ex) { + } catch (IOException ex) { throw new IOError(ex); } } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java index baa3e200..1c370f21 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java @@ -25,90 +25,18 @@ final class SpeechDispatcher extends InputStream implements SilenceListener { static final org.slf4j.Logger LOG = LoggerFactory.getLogger(SpeechDispatcher.class); - /** - * The state of the conversation. - */ - private enum State { - /** - * Say hello after accepting the call. - */ - HELLO, - - /** - * Waiting for the counterpart to introduce himself. - */ - WAITING_FOR_INTRO, - - /** - * Ask the counterpart who is calling. - */ - WHO_IS_CALLING, - - /** - * Waiting for a response from the counterpart. - */ - WAITING_FOR_ANSWER, - - /** - * The counterpart did not say anything, ask if he is still there. - */ - NO_ANSWER, - - /** - * Listening to some answer from the counterpart; - */ - LISTENING, - - /** - * Asking a silly question in response to something the counterpart said. - */ - ASKING; - - /** - * If no event happened, what to say next? - */ - SpeechDispatcher.State next() { - switch (this) { - case HELLO: return WAITING_FOR_INTRO; - case WAITING_FOR_INTRO: return WHO_IS_CALLING; - case WHO_IS_CALLING: return WAITING_FOR_INTRO; - case WAITING_FOR_ANSWER: return NO_ANSWER; - case NO_ANSWER: return WAITING_FOR_ANSWER; - case LISTENING: return LISTENING; - case ASKING: return WAITING_FOR_ANSWER; - } - throw new AssertionError("No such state."); - } - - /** - * What audio to play in the this state? - */ - String audioType() { - switch (this) { - case HELLO: return "hello"; - case WAITING_FOR_INTRO: return "waiting"; - case WHO_IS_CALLING: return "who-is-calling"; - case WAITING_FOR_ANSWER: return "waiting"; - case NO_ANSWER: return "still-there"; - case LISTENING: return "waiting"; - case ASKING: return "question"; - } - throw new AssertionError("No such state."); - } - } - - private SpeechDispatcher.State _state = State.HELLO; + private DialogState _state = DialogState.HELLO; private volatile InputStream _current; - private final Map> _audioFragments; + private final Map> _audioFragments; private Random _rnd; /** * Creates a {@link SpeechDispatcher}. */ - public SpeechDispatcher(Map> audioFragments) { + public SpeechDispatcher(Map> audioFragments) { _audioFragments = audioFragments; _rnd = new Random(); _current = openAudio(); @@ -116,9 +44,9 @@ public SpeechDispatcher(Map> audioFragments) { } private void checkFragments() { - for (State state : State.values()) { - if (_audioFragments.getOrDefault(state.audioType(), Collections.emptyList()).isEmpty()) { - LOG.error("No media for dialogue state '" + state.audioType() + "' found."); + for (SpeechType type : SpeechType.values()) { + if (_audioFragments.getOrDefault(type, Collections.emptyList()).isEmpty()) { + LOG.error("No media for dialogue state '" + type + "' found."); } } } @@ -134,7 +62,7 @@ public int read() throws IOException { return _current.read(); } - private void switchState(SpeechDispatcher.State next) { + private void switchState(DialogState next) { _state = next; InputStream old = _current; @@ -148,10 +76,9 @@ private void switchState(SpeechDispatcher.State next) { private InputStream openAudio() { try { - String type = _state.audioType(); - List list = _audioFragments.getOrDefault(type, Collections.emptyList()); + List list = _audioFragments.getOrDefault(_state, Collections.emptyList()); if (list.isEmpty()) { - throw new IllegalStateException("No media for dialogue state: " + type); + throw new IllegalStateException("No media for dialogue state: " + _state); } File file = list.get(_rnd.nextInt(list.size())); LOG.info("Playing: " + file.getPath()); @@ -164,7 +91,7 @@ private InputStream openAudio() { @Override public void onSilenceStarted(long clock) { switch (_state) { - case LISTENING: switchState(State.ASKING); break; + case LISTENING: switchState(DialogState.ASKING); break; default: // Ignore. } } @@ -174,7 +101,7 @@ public void onSilenceEnded(long clock) { switch (_state) { case WAITING_FOR_INTRO: case WAITING_FOR_ANSWER: - switchState(State.LISTENING); break; + switchState(DialogState.LISTENING); break; default: // Ignore. } } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechType.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechType.java new file mode 100644 index 00000000..ea118ebc --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechType.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +/** + * Type of audio file to play during bot conversation. + */ +public enum SpeechType { + + HELLO, WAITING, WHO_IS_CALLING, STILL_THERE, QUESTION; + + @Override + public String toString() { + return getDirName(); + } + + /** + * The name of the directory where audio files for this speech type are found. + */ + public String getDirName() { + return name().toLowerCase().replace('_', '-'); + } + + /** + * Whether this type is (almost) silent audio. + */ + public boolean isSilent() { + return this == WAITING; + } + +} diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java index 1dbb6eff..d6153f94 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java @@ -13,6 +13,8 @@ import org.kohsuke.args4j.Option; import org.mjsip.ua.sound.WavFileSplitter; +import de.haumacher.phoneblock.answerbot.SpeechType; + /** * Tool to set-up a conversation directory with appropriate WAV files. */ @@ -48,25 +50,38 @@ public static void main(String[] args) throws CmdLineException, IOException, Uns Options options = new Options(); new CmdLineParser(options).parseArgument(args); - for (File dir : new File(options.conversationDir).listFiles(f -> f.isDirectory() && !f.getName().startsWith("."))) { - for (File input : dir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { + for (SpeechType state : SpeechType.values()) { + File stateDir = new File(options.conversationDir, state.getDirName()); + stateDir.mkdirs(); + + for (File input : stateDir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { for (Variant variant : VARIANTS) { - String baseName = WavResampler.baseName(input.getName()); - File tmp = File.createTempFile(baseName, ".wav", dir); - WavResampler.convertToALaw(input.getAbsolutePath(), tmp.getAbsolutePath(), variant.sampleRate); - - WavFileSplitter splitter = new WavFileSplitter(tmp) { - @Override - protected String getOutputFileName(int partId) { - return baseName + "-" + partId + ".wav"; - } - }; - File outputDir = new File(dir, variant.formatName); - outputDir.mkdir(); - splitter.setOutputDir(outputDir); - splitter.run(); - - tmp.delete(); + String formatName = variant.formatName; + File outputDir = new File(stateDir, formatName); + + if (state.isSilent()) { + WavResampler.convertToALaw(input.getAbsolutePath(), new File(outputDir, input.getName()).getAbsolutePath(), variant.sampleRate); + } else { + String baseName = WavResampler.baseName(input.getName()); + File tmp = File.createTempFile(baseName, ".wav", stateDir); + WavResampler.convertToALaw(input.getAbsolutePath(), tmp.getAbsolutePath(), variant.sampleRate); + + WavFileSplitter splitter = new WavFileSplitter(tmp) { + @Override + protected String getOutputFileName(int partId) { + return baseName + "-" + partId + ".wav"; + } + }; + outputDir.mkdir(); + splitter.setOutputDir(outputDir); + splitter.run(); + + tmp.delete(); + + if (splitter.getPartCntCreated() == 0) { + System.err.println("WARNING: No speech files produced for input: " + input.getAbsolutePath()); + } + } } } } From 9723815ed2402d5a2d8b798cc33773f349d7e55d Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 18:37:37 +0100 Subject: [PATCH 035/153] Made org.mjsip.sound a transitive dependency. --- phoneblock-ab/src/main/java/module-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/module-info.java b/phoneblock-ab/src/main/java/module-info.java index 394c22ca..e41c0047 100644 --- a/phoneblock-ab/src/main/java/module-info.java +++ b/phoneblock-ab/src/main/java/module-info.java @@ -8,7 +8,7 @@ module phoneblock.ab { requires phoneblock.shared; - requires org.mjsip.sound; + requires transitive org.mjsip.sound; requires org.mjsip.sip; requires org.mjsip.ua; requires org.mjsip.util; From 11641b894fcfc99669a3aedd38e80e0273fe4049 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 23:20:25 +0100 Subject: [PATCH 036/153] The registrar option is mandatory. --- .../java/de/haumacher/phoneblock/answerbot/CustomerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java index 9b9d5b20..c6cdf619 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java @@ -10,7 +10,7 @@ */ public class CustomerConfig implements CustomerOptions { - @Option(name = "--registrar") + @Option(name = "--registrar", required = true) private String registrar; @Option(name = "--route") From aeb5539c6ba7171a85875767b797d302d588f7c2 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 23:21:39 +0100 Subject: [PATCH 037/153] Use AudioType enum for supported audio formats of the answer bot. --- .../phoneblock/answerbot/AnswerBot.java | 44 +++++++----- .../phoneblock/answerbot/AudioType.java | 68 +++++++++++++++++++ .../phoneblock/answerbot/DialogueFactory.java | 37 ++++++++-- .../tools/ConversationInitializer.java | 27 ++------ 4 files changed, 132 insertions(+), 44 deletions(-) create mode 100644 phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AudioType.java diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 31ef0b22..de0dc81a 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -29,7 +29,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Properties; @@ -88,7 +88,7 @@ public class AnswerBot extends MultipleUAS { VERSION = version == null ? "unknown" : version; } - private final Map> _audioFragments; + private final Map>> _audioFragmentsByType; private AnswerbotOptions _botConfig; @@ -102,24 +102,30 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function _configForUser = configForUser; _botConfig = botOptions; - Map> audioFragments = new HashMap<>(); + Map>> audioFragmentsByType = new EnumMap<>(AudioType.class); File conversationDir = botOptions.conversationDir(); for (SpeechType state : SpeechType.values()) { - ArrayList files = new ArrayList<>(); - - File type = new File(conversationDir, state.getDirName()); - audioFragments.put(state, files); - for (File wav : type.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { - files.add(wav); - } - int cnt = files.size(); - if (cnt == 0) { - LOG.warn("Found no audio fragment for dialogue state " + state + "."); - } else { - LOG.info("Found " + cnt + " audio fragment for dialogue state " + state + "."); + File stateDir = new File(conversationDir, state.getDirName()); + + for (AudioType formatType : AudioType.values()) { + File typeDir = new File(stateDir, formatType.dirName()); + + Map> audioFragments = audioFragmentsByType.computeIfAbsent(formatType, k -> new EnumMap<>(SpeechType.class)); + + ArrayList files = new ArrayList<>(); + audioFragments.put(state, files); + for (File wav : typeDir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { + files.add(wav); + } + int cnt = files.size(); + if (cnt == 0) { + LOG.warn("Found no audio fragment for dialogue state " + state + " and format '" + formatType + "'."); + } else { + LOG.info("Found " + cnt + " audio fragment for dialogue state " + state + " and format '" + formatType + "'."); + } } } - _audioFragments = audioFragments; + _audioFragmentsByType = audioFragmentsByType; } @Override @@ -158,11 +164,13 @@ protected UserAgentListener createCallHandler(String from) { if (from == null) { // An anonymous call, accept. if (_botConfig.getAcceptAnonymous()) { + LOG.info("Accepting anonymous call."); return spamHandler(); } else { + LOG.info("Not accepting anonymous call."); return rejectHandler(); } - } else if (from.startsWith("**")) { + } else if (from.startsWith("*")) { // A local test call, accept. return spamHandler(); } else { @@ -205,7 +213,7 @@ public void onUaIncomingCall(UserAgent ua, NameAddress callee, NameAddress calle } else { recordingFile = null; } - StreamerFactory streamerFactory = new DialogueFactory(_botConfig, _audioFragments, recordingFile); + StreamerFactory streamerFactory = new DialogueFactory(_botConfig, _audioFragmentsByType, recordingFile); ua.accept(new MediaAgent(_botConfig.getMediaDescs(), streamerFactory)); } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AudioType.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AudioType.java new file mode 100644 index 00000000..ad4c364c --- /dev/null +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AudioType.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.answerbot; + +import org.mjsip.media.MediaSpec; + +/** + * Audio formats supported by the answer bot. + */ +public enum AudioType { + PCMA_WB("PCMA", 16000, 1), + PCMA("PCMA", 8000, 1), + ; + + private String _codec; + private int _sampleRate; + private int _channels; + + /** + * Creates a {@link AudioType}. + */ + AudioType(String name, int sampleRate, int channels) { + _codec = name; + _sampleRate = sampleRate; + _channels = channels; + } + + /** + * The name of the format compatible with {@link MediaSpec#getCodec()}. + */ + public String codec() { + return _codec; + } + + /** + * The sample rate of this audio format. + */ + public int sampleRate() { + return _sampleRate; + } + + /** + * The number of channels. + */ + public int channels() { + return _channels; + } + + @Override + public String toString() { + return dirName(); + } + + /** + * Whether this {@link AudioType} is compatible with the given {@link MediaSpec}. + */ + public boolean matches(MediaSpec spec) { + return codec().equals(spec.getCodec()) && sampleRate() == spec.getSampleRate() && channels() == spec.getChannels(); + } + + /** + * Directory where audio files of this type are stored. + */ + public String dirName() { + return name().replace('_', '-'); + } +} diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index 07456323..44b15b07 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -15,6 +15,7 @@ import org.mjsip.media.AudioStreamer; import org.mjsip.media.FlowSpec; +import org.mjsip.media.MediaSpec; import org.mjsip.media.MediaStreamer; import org.mjsip.media.RtpStreamReceiver; import org.mjsip.media.RtpStreamReceiverListener; @@ -35,6 +36,7 @@ import org.mjsip.sound.AudioFile; import org.mjsip.ua.sound.AlawSilenceTrimmer; import org.mjsip.ua.streamer.StreamerFactory; +import org.slf4j.LoggerFactory; import org.zoolu.net.UdpSocket; import org.zoolu.sound.CodecType; import org.zoolu.sound.SimpleAudioSystem; @@ -44,22 +46,49 @@ * {@link StreamerFactory} creating a bot correspondence. */ public final class DialogueFactory implements StreamerFactory { - private final Map> _audioFragments; + + static final org.slf4j.Logger LOG = LoggerFactory.getLogger(DialogueFactory.class); + + private final Map>> _audioFragmentsByType; private final String _recordingFile; private DialogOptions _config; /** * Creates a {@link DialogueFactory}. */ - public DialogueFactory(DialogOptions config, Map> audioFragments, String recordingFile) { + public DialogueFactory(DialogOptions config, Map>> audioFragmentsByType, String recordingFile) { _config = config; - _audioFragments = audioFragments; + _audioFragmentsByType = audioFragmentsByType; _recordingFile = recordingFile; } @Override public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { - SpeechDispatcher speechDispatcher = new SpeechDispatcher(_audioFragments); + AudioType matchedType = null; + int maxSampleRate = 0; + MediaSpec spec = flow_spec.getMediaSpec(); + for (AudioType type : _audioFragmentsByType.keySet()) { + if (type.matches(spec)) { + int sampleRate = type.sampleRate(); + if (sampleRate > maxSampleRate) { + matchedType = type; + maxSampleRate = sampleRate; + } + } + } + + if (matchedType == null) { + LOG.warn("No compatible audio found."); + return null; + } + + Map> dialogue = _audioFragmentsByType.get(matchedType); + if (dialogue == null) { + LOG.warn("No audio data found for audio format: " + matchedType); + return null; + } + + SpeechDispatcher speechDispatcher = new SpeechDispatcher(dialogue); int sampleRate = flow_spec.getMediaSpec().getSampleRate(); try { diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java index d6153f94..bbb4001f 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java @@ -13,6 +13,7 @@ import org.kohsuke.args4j.Option; import org.mjsip.ua.sound.WavFileSplitter; +import de.haumacher.phoneblock.answerbot.AudioType; import de.haumacher.phoneblock.answerbot.SpeechType; /** @@ -24,24 +25,6 @@ static class Options { @Option(name = "-c") String conversationDir = "./conversation"; } - - static class Variant { - int sampleRate; - String formatName; - - /** - * Creates a {@link Variant}. - */ - public Variant(String format, int sampleRate) { - this.formatName = format; - this.sampleRate = sampleRate; - } - } - - static final Variant[] VARIANTS = { - new Variant("PCMA-WB", 16000), - new Variant("PCMA", 8000), - }; /** * Main entry point form the command line. @@ -55,16 +38,16 @@ public static void main(String[] args) throws CmdLineException, IOException, Uns stateDir.mkdirs(); for (File input : stateDir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { - for (Variant variant : VARIANTS) { - String formatName = variant.formatName; + for (AudioType formatType : AudioType.values()) { + String formatName = formatType.dirName(); File outputDir = new File(stateDir, formatName); if (state.isSilent()) { - WavResampler.convertToALaw(input.getAbsolutePath(), new File(outputDir, input.getName()).getAbsolutePath(), variant.sampleRate); + WavResampler.convertToALaw(input.getAbsolutePath(), new File(outputDir, input.getName()).getAbsolutePath(), formatType.sampleRate()); } else { String baseName = WavResampler.baseName(input.getName()); File tmp = File.createTempFile(baseName, ".wav", stateDir); - WavResampler.convertToALaw(input.getAbsolutePath(), tmp.getAbsolutePath(), variant.sampleRate); + WavResampler.convertToALaw(input.getAbsolutePath(), tmp.getAbsolutePath(), formatType.sampleRate()); WavFileSplitter splitter = new WavFileSplitter(tmp) { @Override From 623098c5dd0e728c29d481e13d8bc79d58df551f Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 23:22:16 +0100 Subject: [PATCH 038/153] Open config types to args4j to allow command line parsing with modules. --- phoneblock-ab/src/main/java/module-info.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phoneblock-ab/src/main/java/module-info.java b/phoneblock-ab/src/main/java/module-info.java index e41c0047..d0a365f3 100644 --- a/phoneblock-ab/src/main/java/module-info.java +++ b/phoneblock-ab/src/main/java/module-info.java @@ -22,6 +22,8 @@ requires org.slf4j; requires org.tinylog.api; + opens de.haumacher.phoneblock.answerbot to args4j; + exports de.haumacher.phoneblock.answerbot; exports de.haumacher.phoneblock.answerbot.tools; From 8c27edc5c92184ddeb1a814453574e5f10fdc5c8 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 23:22:50 +0100 Subject: [PATCH 039/153] Added module path to Eclipse settings. --- phoneblock-ab/.classpath | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phoneblock-ab/.classpath b/phoneblock-ab/.classpath index 1d314d0a..a52ad1f5 100644 --- a/phoneblock-ab/.classpath +++ b/phoneblock-ab/.classpath @@ -4,6 +4,7 @@ + @@ -15,8 +16,8 @@ - + From 263b647f20dfd19b1d0ab9c11755036467bc0cea Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 16 Nov 2023 23:23:06 +0100 Subject: [PATCH 040/153] Use utf-8 for test sources. --- phoneblock-ab/.settings/org.eclipse.core.resources.prefs | 1 + 1 file changed, 1 insertion(+) diff --git a/phoneblock-ab/.settings/org.eclipse.core.resources.prefs b/phoneblock-ab/.settings/org.eclipse.core.resources.prefs index c35a6291..a05fb4b5 100644 --- a/phoneblock-ab/.settings/org.eclipse.core.resources.prefs +++ b/phoneblock-ab/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,4 @@ eclipse.preferences.version=1 encoding//src/main/java=utf-8 +encoding//src/test/java=utf-8 encoding/=utf-8 From ce9feb5e15653d6382b0a7df7c796bab348911c6 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 17 Nov 2023 16:29:34 +0100 Subject: [PATCH 041/153] Updated URLs after moving to phoneblock.net. --- phoneblock-ab/pom.xml | 2 +- .../main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/pom.xml b/phoneblock-ab/pom.xml index 17a9357b..8ea0e361 100644 --- a/phoneblock-ab/pom.xml +++ b/phoneblock-ab/pom.xml @@ -28,7 +28,7 @@ - https://phoneblock.haumacher.de + https://haumacher.de diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index de0dc81a..f67389ab 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -176,7 +176,7 @@ protected UserAgentListener createCallHandler(String from) { } else { NumberInfo info; try { - URL url = new URL("https://phoneblock.haumacher.de/phoneblock/api/num/" + from + "?format=json"); + URL url = new URL("https://phoneblock.net/phoneblock/api/num/" + from + "?format=json"); URLConnection connection = url.openConnection(); connection.addRequestProperty("accept", "application/json"); connection.addRequestProperty("User-Agent", "PhoneBlock-AB/" + VERSION); From ed77f0b7d061463ca8b465c8ba8ff6db1eb0dd74 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 17 Nov 2023 23:08:29 +0100 Subject: [PATCH 042/153] Added route and force-rport options to the config template. Those options are necessary, when accessing a Fritz!Box from a cloud instance (not in the local network). --- phoneblock-ab/configs/phoneblock-answerbot.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index 79fdffa8..d538bf3b 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -10,6 +10,8 @@ registrar=fritz.box realm=fritz.box +#route=fritz.box +#force-rport=yes # The user name for registering the IP phone device configured in your Fritz!Box user=phoneblock From 801d0266b240f89a7768339daf8a9548c94622d7 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 17 Nov 2023 23:18:33 +0100 Subject: [PATCH 043/153] Fixed wrong key for lookup of audio file. --- .../de/haumacher/phoneblock/answerbot/SpeechDispatcher.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java index 1c370f21..bec17e7f 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java @@ -76,9 +76,10 @@ private void switchState(DialogState next) { private InputStream openAudio() { try { - List list = _audioFragments.getOrDefault(_state, Collections.emptyList()); + SpeechType speechType = _state.getSpeechType(); + List list = _audioFragments.getOrDefault(speechType, Collections.emptyList()); if (list.isEmpty()) { - throw new IllegalStateException("No media for dialogue state: " + _state); + throw new IllegalStateException("No media for speech: " + speechType); } File file = list.get(_rnd.nextInt(list.size())); LOG.info("Playing: " + file.getPath()); From 0735e954e75e41f49c4e9125be5e17f80975d75b Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 18 Nov 2023 19:40:03 +0100 Subject: [PATCH 044/153] More logging. --- .../java/de/haumacher/phoneblock/answerbot/DialogueFactory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index 44b15b07..3943eb4e 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -87,6 +87,7 @@ public MediaStreamer createMediaStreamer(FlowSpec flow_spec) { LOG.warn("No audio data found for audio format: " + matchedType); return null; } + LOG.info("Starting dialogue with audio type: " + matchedType); SpeechDispatcher speechDispatcher = new SpeechDispatcher(dialogue); From e3adaa367dcf3dbe41b954e37458693adae8f62f Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 18 Nov 2023 19:40:21 +0100 Subject: [PATCH 045/153] Added transitive module requirement. --- phoneblock-ab/src/main/java/module-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/module-info.java b/phoneblock-ab/src/main/java/module-info.java index d0a365f3..827f81a3 100644 --- a/phoneblock-ab/src/main/java/module-info.java +++ b/phoneblock-ab/src/main/java/module-info.java @@ -9,7 +9,7 @@ requires phoneblock.shared; requires transitive org.mjsip.sound; - requires org.mjsip.sip; + requires transitive org.mjsip.sip; requires org.mjsip.ua; requires org.mjsip.util; requires org.mjsip.net; From 353af0462ebd2a63b3db65d398c2eea083d9cd9e Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 18 Nov 2023 22:49:59 +0100 Subject: [PATCH 046/153] Added test number prefix configuration option. --- .../configs/phoneblock-answerbot.template | 5 +++++ .../phoneblock/answerbot/AnswerBot.java | 2 +- .../phoneblock/answerbot/AnswerbotConfig.java | 14 +++++++++++++- .../phoneblock/answerbot/AnswerbotOptions.java | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index d538bf3b..0fa7cc67 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -51,3 +51,8 @@ min-votes = 4 # Whether to let PhoneBlock accept anonymous calls. It is not recommended to enable this option. Better configure a # separate answering machine in you router to gracefully handle anonymous calls. accept-anonymous = no + +# Prefix of a number that triggers PhoneBlock to respond to the call (for testing). A local number typically starts +# with a '*' character. Using this prefix allows to call PhoneBlock locally to verify it is working. +test-prefix=* + diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index f67389ab..5bbd76e9 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -170,7 +170,7 @@ protected UserAgentListener createCallHandler(String from) { LOG.info("Not accepting anonymous call."); return rejectHandler(); } - } else if (from.startsWith("*")) { + } else if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestNumber())) { // A local test call, accept. return spamHandler(); } else { diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index 7a289157..a5db7fdd 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -22,9 +22,13 @@ public class AnswerbotConfig implements AnswerbotOptions { @Option(name = "--conversation", usage = "Directory with WAV files used for streaming during automatic conversation.") private File _conversationDir = new File("./conversation"); - @Option(name = "--recodings", usage = "Directory where to store recordings to, 'none' to disable recording. ") + @Option(name = "--recodings", usage = "Directory where to store recordings to, 'none' to disable recording.") private String _recordingDir = "."; + @Option(name = "--test-prefix", usage = "Phone number prefix that triggers the answer bot to respond (for testing). " + + "A local number typically starts with '*', therefore this is prefix can be used to allow calling the answer bot locally.") + private String _testPrefix = "*"; + /** * The configured recoding directory. */ @@ -86,6 +90,11 @@ public boolean getAcceptAnonymous() { public int getMinVotes() { return _minVotes; } + + @Override + public String getTestNumber() { + return _testPrefix; + } /** * Normalizes options, must be called after option parsing. @@ -99,6 +108,9 @@ public void normalize() { System.err.println("Conversation directory does not exist: " + conversationDir().getAbsolutePath()); System.exit(1); } + if (_testPrefix != null && _testPrefix.isBlank()) { + _testPrefix = null; + } } @Override diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java index d68d4bbf..0771d7b3 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java @@ -38,4 +38,20 @@ public interface AnswerbotOptions extends DialogOptions, StaticOptions { */ MediaDesc[] getMediaDescs(); + /** + * Whether a {@link #getTestNumber()} has been configured. + */ + default boolean hasTestNumber() { + return getTestNumber() != null; + } + + /** + * A phone number that certainly trigger the answer bot without checking the SPAM database. + * + *

+ * Use this for testing only. + *

+ */ + String getTestNumber(); + } From 7fef195aa057932e8408775be2a0c60801db3204 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 09:46:54 +0100 Subject: [PATCH 047/153] Added README for the conversion folder. --- phoneblock-ab/conversation/README.md | 73 ++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 phoneblock-ab/conversation/README.md diff --git a/phoneblock-ab/conversation/README.md b/phoneblock-ab/conversation/README.md new file mode 100644 index 00000000..55236eb3 --- /dev/null +++ b/phoneblock-ab/conversation/README.md @@ -0,0 +1,73 @@ +# Verzeichnis mit Audiodateien, aus denen PhoneBlock einen Dialog mit dem Anrufer zusammensetzt + +Ein PhoneBlock-Dialog besteht aus folgenden Komponenten: + * Eine Begrüßung ("hello") + * Warten auf eine Meldung des Gegenüber ("waiting") + * Eine Rückfrage ("question") + * Warten auf eine Antwort ("waiting") + * Weiter bei "Rückfrage". + * Eine Nachfrage, wenn sich das Gegenüber auf die Begrüßung nicht meldet ("who-is-calling") + * Eine Nachfrage, wenn das Gegenüber auf eine Rückfrage nicht antwortet ("still-there") + +Für jeden Dialog-Teil gibt es ein Unterverzeichnis, in den eine Audiodatei (WAV) abgelegt werden muss, in der (am +besten) viele mögliche Sätze gesprochen werden, die zur jeweiligen Situation des Dialoges passen. Zwischen jedem Satz +muss sich ausreichend Stille (1s) befinden, damit PhoneBlock die Sätze voneinander trennen kann. Die folgenden +Abschnitte machen Vorschläge, was man in den jeweiligen Audio-Dateien aufsprechen könnte. + +Wenn die Audiodateien vorliegen, muss das Dialogverzeichnis mit dem Tool `ConversionInitializer` initialisiert werden. +Diese Tool splittet die Audiodateien in die einzelnen Sätze und bereite verschiedene Formatversionen vor, die für die +Kommunikation benötigt werden (je nachdem was für ein Audioformat mit der Gegenstelle ausgehandelt wird). + + +## Ansagen, mit denen das Gespräch begonnen wird ("hello") + * Guten Tag, wie kann ich Ihnen behilflich sein? + * Guten Tag, was kann ich für Sie tun? + * Hallo, wie kann ich Ihnen weiterhelfen? + * Einen schönen guten Tag, wie kann ich Ihnen helfen? + +## Nach der Ansage meldet sich das Call-Center nicht sofort, PhoneBlock fragt nach, wer anruft ("who-is-calling") + + * Hallo, wer ist da bitte? + * Hallo, wer ist in der Leitung? + * Hallo, ist da jemand? + * Hallo, bitte melden Sie sich! + * Ich höre nichts, wer sind Sie? + * Ist da jemand? Ich höre nichts! + +## Das Gegenüber sagt nichts, PhoneBlock stellt eine Nachfrage ("still-there") + + * Entschuldigen Sie, aber ich höre nichts. Können Sie mich hören? + * Gibt es ein Problem mit der Verbindung? Haben Sie etwas gesagt? + * Ist die Verbindung unterbrochen? Hören Sie mich? + * Ich habe Sie nicht verstanden, können Sie das nocheinmal wiederholen? + * Es scheint, als ob es Kommunikationsprobleme gibt. Könnten Sie noch einmal anzurufen? + * Tut mir leid, aber ich kann nichts hören. Können Sie bitte wiederholen, was Sie gesagt haben? + * Gibt ein technisches Problem mit der Verbindung? Ich höre Sie nicht! + * Es sieht so aus, als hätten wir ein Verbindungsproblem. Haben Sie etwas gesagt? + +## Das Gegenüber hat etwas gesagt, PhoneBlock stellt eine idiotische Rückfrage ("question") + + * Können Sie das bitte genauer erklären? + * Können Sie mir mehr Informationen dazu geben? + * Lassen Sie mich darüber nachdenken - muss das jetzt sofort entschieden werden? + * Können Sie Beispiele dafür nennen? + * Ich verstehe noch nicht ganz, können Sie das nocheinmal erklären? + * Ich bin mir nicht sicher, können Sie weitere Einzelheiten mitteilen? + * Gibt es noch etwas, was Sie hinzufügen möchten? + * Können Sie bitte mehr Details dazu geben? + * Könnten Sie das bitte genauer erklären? + * Gibt es weitere Informationen, die relevant sein könnten? + * Könnten Sie mir mehr Hintergrundinformationen geben? + * Ist das unbedingt notwendig? Welche anderen Optionen können in Betracht gezogen werden? + * Das kann ich mir nicht vorstellen, könnten Sie das bitte ausführlicher erläutern? + * Das klingt sehr allgemein. Welche genauen Daten haben Sie hier? + * Gibt es zusätzliche Fakten, die berücksichtigt werden sollten? + * Können Sie das genauer in einzelnen Schritten erklären? + +## Während PhoneBlock auf eine Antwort wartet, wird Rauschen abgespielt, um eine Verbindung vorzutäuschen ("waiting") + + * Eine Audio-Datei, in der einfach für 3 bis 5 Sekunden Stille (am besten mit ganz leichtem Grundrauschen) aufgenommen + wird. + + + \ No newline at end of file From 7c947fd7637e0eebe71686c6e734412cad3cccff Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 13:02:06 +0100 Subject: [PATCH 048/153] Migrated due to mySIP API change of registrar and route settings. --- .../phoneblock/answerbot/CustomerConfig.java | 18 ++++++++++-------- .../phoneblock/answerbot/CustomerOptions.java | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java index c6cdf619..302f7376 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerConfig.java @@ -4,17 +4,19 @@ package de.haumacher.phoneblock.answerbot; import org.kohsuke.args4j.Option; +import org.mjsip.sip.address.SipURI; +import org.mjsip.sip.config.SipURIHandler; /** * Command line options to specify a {@link CustomerConfig}. */ public class CustomerConfig implements CustomerOptions { - @Option(name = "--registrar", required = true) - private String registrar; + @Option(name = "--registrar", required = true, handler = SipURIHandler.class) + private SipURI registrar; - @Option(name = "--route") - private String route; + @Option(name = "--route", handler = SipURIHandler.class) + private SipURI route; @Option(name = "--user") private String user; @@ -26,20 +28,20 @@ public class CustomerConfig implements CustomerOptions { private String realm; @Override - public String getRegistrar() { + public SipURI getRegistrar() { return registrar; } - public void setRegistrar(String registrar) { + public void setRegistrar(SipURI registrar) { this.registrar = registrar; } @Override - public String getRoute() { + public SipURI getRoute() { return route; } - public void setRoute(String route) { + public void setRoute(SipURI route) { this.route = route; } diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java index 4867fcb8..650f43af 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/CustomerOptions.java @@ -15,7 +15,7 @@ public interface CustomerOptions extends RegistrationOptions, UserOptions { @Override default String getProxy() { - return getRegistrar(); + return getRegistrar().getHost(); } @Override From 5b2e7a8e804d48eeeafbcf9793c00c805b853f56 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 13:02:52 +0100 Subject: [PATCH 049/153] Separated schema for used, dyndns and sip settings. --- .../haumacher/phoneblock/app/Application.java | 3 +- .../phoneblock/app/api/DynIpServlet.java | 86 +++- .../phoneblock/db/DBAnswerBotDynDns.java | 28 ++ .../de/haumacher/phoneblock/db/Users.java | 21 +- .../de/haumacher/phoneblock/db/db-schema.sql | 47 +- .../db/settings/AnswerBotDynDns.java | 288 +++++++++++ .../db/settings/AnswerBotSetting.java | 288 +++++++++++ .../phoneblock/db/settings/AnswerBotSip.java | 475 ++++++++++++++++++ .../phoneblock/db/settings/settings.proto | 56 +++ .../phoneblock/util/ServletUtil.java | 2 +- 10 files changed, 1260 insertions(+), 34 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java index 919e129c..d27b319d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java @@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory; import org.tinylog.configuration.Configuration; -import de.haumacher.phoneblock.ab.SipService; import de.haumacher.phoneblock.carddav.resource.AddressBookCache; import de.haumacher.phoneblock.chatgpt.ChatGPTService; import de.haumacher.phoneblock.crawl.CrawlerService; @@ -100,7 +99,7 @@ public void contextInitialized(ServletContextEvent sce) { gpt = new ChatGPTService(db, scheduler, indexer), new ManagementService(indexer, db, gpt), new AddressBookCache(), - new SipService(scheduler) +// new SipService(scheduler) }; for (int n = 0, cnt = _services.length; n < cnt; n++) { diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java index 78d1d51b..ae660950 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java @@ -4,7 +4,10 @@ package de.haumacher.phoneblock.app.api; import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.Arrays; import java.util.stream.Collectors; @@ -20,6 +23,7 @@ import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; +import de.haumacher.phoneblock.db.settings.AnswerBotDynDns; /** * Servlet to record a customers dynamic IP address over DynIP protocol. @@ -29,34 +33,56 @@ public class DynIpServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(DynIpServlet.class); + private static final long ONE_DAY = 1000 * 60 * 60 * 24; + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String user = req.getParameter("user"); - String passwd = req.getParameter("passwd"); - - String userName = DBService.getInstance().login(user, passwd); - if (userName == null) { - LOG.warn("Failed to authenticate: " + parameters(req)); + if (user == null) { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "No user name provided."); return; } + user = user.trim(); - String ip = req.getParameter("ip"); - InetAddress address = InetAddress.getByName(ip); - if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress() - || address.isMCGlobal() || address.isMCLinkLocal() || address.isMCNodeLocal() || address.isMCOrgLocal() - || address.isMCSiteLocal() || address.isMulticastAddress() || address.isSiteLocalAddress()) { - LOG.warn("Provided invalid address: " + parameters(req)); + String passwd = req.getParameter("passwd"); + if (passwd == null) { + LOG.warn("DynDNS update without password: " + user); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "No password provided."); return; } + passwd = passwd.trim(); try (SqlSession session = DBService.getInstance().openSession()) { Users users = session.getMapper(Users.class); - Long userId = users.getUserId(userName); - users.setIP(userId, ip); - session.commit(); + AnswerBotDynDns settings = users.getDynDns(user); + if (settings == null || !settings.getDynDnsPasswd().equals(passwd)) { + LOG.warn("DynDNS update with wrong password (" + passwd.length() + " characters): " + user); + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Wrong user name or password."); + return; + } - LOG.info("Updated IP address for: " + userName); + long now = System.currentTimeMillis(); + AnswerBotDynDns update = AnswerBotDynDns.create().setDyndnsUser(user); + + processAddress(update, req.getParameter("ip")); + processAddress(update, req.getParameter("ip4")); + processAddress(update, req.getParameter("ip6")); + + if (changed(settings.getIpv4(), update.getIpv4()) || changed(settings.getIpv6(), update.getIpv6()) || now - settings.getUpdated() > ONE_DAY) { + users.updateDynDny(settings.getUserId(), update.getIpv4(), update.getIpv6(), now); + + session.commit(); + LOG.info("Updated IP address for: " + user); + } else { + LOG.info("IP address of user unchanged: " + user); + } + + if (update.getIpv4().isEmpty() && update.getIpv6().isEmpty()) { + LOG.warn("No addresses provided: " + user); + resp.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE, "No valid addresses provided."); + return; + } } // Mark as successfully processed. @@ -65,7 +91,33 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se resp.getWriter().write("OK"); } - private String parameters(HttpServletRequest req) { - return req.getParameterMap().entrySet().stream().map(e -> e.getKey() + "=" + Arrays.asList(e.getValue())).collect(Collectors.joining(", ")); + private static boolean changed(String a, String b) { + return ! a.equals(b); + } + + private void processAddress(AnswerBotDynDns update, String ip) throws UnknownHostException { + if (ip == null) { + return; + } + + ip = ip.trim(); + + if (ip.isEmpty()) { + return; + } + + InetAddress address = InetAddress.getByName(ip); + if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress() + || address.isSiteLocalAddress() || address.isMulticastAddress()) { + + String message = "Provided invalid address (" + ip + "): " + update.getDyndnsUser(); + LOG.warn(message); + } else { + if (address instanceof Inet4Address) { + update.setIpv4(ip); + } else if (address instanceof Inet6Address) { + update.setIpv6(ip); + } + } } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java new file mode 100644 index 00000000..7d739c1d --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.db; + +import de.haumacher.phoneblock.db.settings.AnswerBotDynDns; + +/** + * Variant of {@link AnswerBotDynDns} for fetching from DB. + * + * @author Bernhard Haumacher + */ +public class DBAnswerBotDynDns extends AnswerBotDynDns { + + /** + * Creates a {@link DBAnswerBotDynDns}. + */ + public DBAnswerBotDynDns(long userId, long created, long updated, String user, String passwd, String ip4, String ip6) { + setUserId(userId); + setCreated(created); + setUpdated(updated); + setDyndnsUser(user); + setDynDnsPasswd(passwd); + setIpv4(ip4); + setIpv6(ip6); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 28ff7384..82751d8f 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -46,18 +46,6 @@ public interface Users { @Select("select ID from USERS where LOGIN=#{login}") Long getUserId(String login); - /** - * Updates the user's dynamic IP address. - * - * @param id - * The user ID. - * @param ip - * The new IP address. - * @return 1 if the update was successful. - */ - @Insert("update USERS set IP=#{ip} where ID=#{id}") - int setIP(long id, String ip); - /** * Retrieves the user ID for the user with the given user name (e-mail). */ @@ -122,4 +110,13 @@ public interface Users { @Update("update CALLERS set CALLS=CALLS + 1, LASTUPDATE=#{now} where USERID=#{userId} and PHONE=#{phone}") int addCall(long userId, String phone, long now); + @Select("select USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where DYNDNS_USER=#{user}") + DBAnswerBotDynDns getDynDns(String user); + + /** + * Updates the user's dynamic IP address. + */ + @Insert("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where USERID=#{userId}") + void updateDynDny(long userId, String ip4, String ip6, long updated); + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql index d1fffcab..1b25558d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql @@ -87,14 +87,57 @@ CREATE TABLE USERS ( WILDCARDS BOOLEAN DEFAULT 1 NOT NULL, WELCOME BOOLEAN DEFAULT 0 NOT NULL, - IP CHARACTER VARYING(64) DEFAULT NULL, - CONSTRAINT USERS_PK PRIMARY KEY (ID) ); CREATE INDEX USERS_CLIENTNAME_IDX ON USERS (CLIENTNAME,EXTID); CREATE UNIQUE INDEX USERS_UN_INDEX_4 ON USERS (LOGIN); +-- PUBLIC.ANSWERBOT definition + +-- Drop table + +-- DROP TABLE ANSWERBOT; + +CREATE TABLE ANSWERBOT_DYNDNS ( + USERID BIGINT NOT NULL, + + CREATED BIGINT, + UPDATED BIGINT, + + DYNDNS_USER CHARACTER VARYING(64) NOT NULL, + DYNDNS_PASSWD CHARACTER VARYING(64) NOT NULL, + IP4 CHARACTER VARYING(64), + IP6 CHARACTER VARYING(64), + + CONSTRAINT ANSWERBOT_DYNDNS_PK PRIMARY KEY (USERID) +); +CREATE UNIQUE INDEX ANSWERBOT_DYNDNS_USER ON ANSWERBOT_DYNDNS (DYNDNS_USER); + + +CREATE TABLE ANSWERBOT_SIP ( + USERID BIGINT NOT NULL, + + CREATED BIGINT, + UPDATED BIGINT, + + HOST CHARACTER VARYING(64), + + SIP_REGISTRAR CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, + SIP_DOMAIN CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, + SIP_USER CHARACTER VARYING(64) NOT NULL, + SIP_PASSWD CHARACTER VARYING(64) NOT NULL, + + REGISTERED BOOLEAN DEFAULT FALSE NOT NULL, + LAST_REGISTER BIGINT, + REGISTER_ERROR CHARACTER VARYING(512), + + CALLS_ACCEPTED INTEGER DEFAULT 0 NOT NULL, + + CONSTRAINT ANSWERBOT_SIP_PK PRIMARY KEY (USERID) +); +CREATE UNIQUE INDEX ANSWERBOT_SIP_USER ON ANSWERBOT_SIP (SIP_USER); + -- PUBLIC.CALLREPORT definition -- Drop table diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java new file mode 100644 index 00000000..82ca5acd --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java @@ -0,0 +1,288 @@ +package de.haumacher.phoneblock.db.settings; + +/** + * Configuration options and state of an answer bot. + */ +public class AnswerBotDynDns extends AnswerBotSetting { + + /** + * Creates a {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns} instance. + */ + public static de.haumacher.phoneblock.db.settings.AnswerBotDynDns create() { + return new de.haumacher.phoneblock.db.settings.AnswerBotDynDns(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns} type in JSON format. */ + public static final String ANSWER_BOT_DYN_DNS__TYPE = "AnswerBotDynDns"; + + /** @see #getDyndnsUser() */ + public static final String DYNDNS_USER__PROP = "dyndnsUser"; + + /** @see #getDynDnsPasswd() */ + public static final String DYN_DNS_PASSWD__PROP = "dynDnsPasswd"; + + /** @see #getIpv4() */ + public static final String IPV_4__PROP = "ipv4"; + + /** @see #getIpv6() */ + public static final String IPV_6__PROP = "ipv6"; + + /** Identifier for the {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns} type in binary format. */ + static final int ANSWER_BOT_DYN_DNS__TYPE_ID = 1; + + /** Identifier for the property {@link #getDyndnsUser()} in binary format. */ + static final int DYNDNS_USER__ID = 4; + + /** Identifier for the property {@link #getDynDnsPasswd()} in binary format. */ + static final int DYN_DNS_PASSWD__ID = 5; + + /** Identifier for the property {@link #getIpv4()} in binary format. */ + static final int IPV_4__ID = 6; + + /** Identifier for the property {@link #getIpv6()} in binary format. */ + static final int IPV_6__ID = 7; + + private String _dyndnsUser = ""; + + private String _dynDnsPasswd = ""; + + private String _ipv4 = ""; + + private String _ipv6 = ""; + + /** + * Creates a {@link AnswerBotDynDns} instance. + * + * @see de.haumacher.phoneblock.db.settings.AnswerBotDynDns#create() + */ + protected AnswerBotDynDns() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.ANSWER_BOT_DYN_DNS; + } + + /** + * The user name for DynDNS update and SIP registration. + */ + public final String getDyndnsUser() { + return _dyndnsUser; + } + + /** + * @see #getDyndnsUser() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setDyndnsUser(String value) { + internalSetDyndnsUser(value); + return this; + } + + /** Internal setter for {@link #getDyndnsUser()} without chain call utility. */ + protected final void internalSetDyndnsUser(String value) { + _listener.beforeSet(this, DYNDNS_USER__PROP, value); + _dyndnsUser = value; + } + + /** + * Password hash for DynDNS update. + */ + public final String getDynDnsPasswd() { + return _dynDnsPasswd; + } + + /** + * @see #getDynDnsPasswd() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setDynDnsPasswd(String value) { + internalSetDynDnsPasswd(value); + return this; + } + + /** Internal setter for {@link #getDynDnsPasswd()} without chain call utility. */ + protected final void internalSetDynDnsPasswd(String value) { + _listener.beforeSet(this, DYN_DNS_PASSWD__PROP, value); + _dynDnsPasswd = value; + } + + /** + * IPv4 address transmitted during the last DynDNS update. + */ + public final String getIpv4() { + return _ipv4; + } + + /** + * @see #getIpv4() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setIpv4(String value) { + internalSetIpv4(value); + return this; + } + + /** Internal setter for {@link #getIpv4()} without chain call utility. */ + protected final void internalSetIpv4(String value) { + _listener.beforeSet(this, IPV_4__PROP, value); + _ipv4 = value; + } + + /** + * IPv6 address transmitted during the last DynDNS update. + */ + public final String getIpv6() { + return _ipv6; + } + + /** + * @see #getIpv6() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setIpv6(String value) { + internalSetIpv6(value); + return this; + } + + /** Internal setter for {@link #getIpv6()} without chain call utility. */ + protected final void internalSetIpv6(String value) { + _listener.beforeSet(this, IPV_6__PROP, value); + _ipv6 = value; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setUserId(long value) { + internalSetUserId(value); + return this; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setCreated(long value) { + internalSetCreated(value); + return this; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setUpdated(long value) { + internalSetUpdated(value); + return this; + } + + @Override + public String jsonType() { + return ANSWER_BOT_DYN_DNS__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + DYNDNS_USER__PROP, + DYN_DNS_PASSWD__PROP, + IPV_4__PROP, + IPV_6__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case DYNDNS_USER__PROP: return getDyndnsUser(); + case DYN_DNS_PASSWD__PROP: return getDynDnsPasswd(); + case IPV_4__PROP: return getIpv4(); + case IPV_6__PROP: return getIpv6(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case DYNDNS_USER__PROP: internalSetDyndnsUser((String) value); break; + case DYN_DNS_PASSWD__PROP: internalSetDynDnsPasswd((String) value); break; + case IPV_4__PROP: internalSetIpv4((String) value); break; + case IPV_6__PROP: internalSetIpv6((String) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotDynDns readAnswerBotDynDns(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.settings.AnswerBotDynDns result = new de.haumacher.phoneblock.db.settings.AnswerBotDynDns(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(DYNDNS_USER__PROP); + out.value(getDyndnsUser()); + out.name(DYN_DNS_PASSWD__PROP); + out.value(getDynDnsPasswd()); + out.name(IPV_4__PROP); + out.value(getIpv4()); + out.name(IPV_6__PROP); + out.value(getIpv6()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case DYNDNS_USER__PROP: setDyndnsUser(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case DYN_DNS_PASSWD__PROP: setDynDnsPasswd(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IPV_4__PROP: setIpv4(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IPV_6__PROP: setIpv6(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + + /** The binary identifier for this concrete type in the polymorphic {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns} hierarchy. */ + public int typeId() { + return ANSWER_BOT_DYN_DNS__TYPE_ID; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(DYNDNS_USER__ID); + out.value(getDyndnsUser()); + out.name(DYN_DNS_PASSWD__ID); + out.value(getDynDnsPasswd()); + out.name(IPV_4__ID); + out.value(getIpv4()); + out.name(IPV_6__ID); + out.value(getIpv6()); + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotDynDns readAnswerBotDynDns(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + in.beginObject(); + de.haumacher.phoneblock.db.settings.AnswerBotDynDns result = de.haumacher.phoneblock.db.settings.AnswerBotDynDns.readAnswerBotDynDns_Content(in); + in.endObject(); + return result; + } + + /** Helper for creating an object of type {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns} from a polymorphic composition. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotDynDns readAnswerBotDynDns_Content(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.settings.AnswerBotDynDns result = new AnswerBotDynDns(); + result.readContent(in); + return result; + } + + @Override + protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { + switch (field) { + case DYNDNS_USER__ID: setDyndnsUser(in.nextString()); break; + case DYN_DNS_PASSWD__ID: setDynDnsPasswd(in.nextString()); break; + case IPV_4__ID: setIpv4(in.nextString()); break; + case IPV_6__ID: setIpv6(in.nextString()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.db.settings.AnswerBotSetting.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java new file mode 100644 index 00000000..c663a637 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java @@ -0,0 +1,288 @@ +package de.haumacher.phoneblock.db.settings; + +/** + * Common options of answer bot settings. + */ +public abstract class AnswerBotSetting extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.binary.BinaryDataObject, de.haumacher.msgbuf.observer.Observable { + + /** Type codes for the {@link de.haumacher.phoneblock.db.settings.AnswerBotSetting} hierarchy. */ + public enum TypeKind { + + /** Type literal for {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns}. */ + ANSWER_BOT_DYN_DNS, + + /** Type literal for {@link de.haumacher.phoneblock.db.settings.AnswerBotSip}. */ + ANSWER_BOT_SIP, + ; + + } + + /** Visitor interface for the {@link de.haumacher.phoneblock.db.settings.AnswerBotSetting} hierarchy.*/ + public interface Visitor { + + /** Visit case for {@link de.haumacher.phoneblock.db.settings.AnswerBotDynDns}.*/ + R visit(de.haumacher.phoneblock.db.settings.AnswerBotDynDns self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.db.settings.AnswerBotSip}.*/ + R visit(de.haumacher.phoneblock.db.settings.AnswerBotSip self, A arg) throws E; + + } + + /** @see #getUserId() */ + public static final String USER_ID__PROP = "userId"; + + /** @see #getCreated() */ + public static final String CREATED__PROP = "created"; + + /** @see #getUpdated() */ + public static final String UPDATED__PROP = "updated"; + + /** Identifier for the property {@link #getUserId()} in binary format. */ + static final int USER_ID__ID = 1; + + /** Identifier for the property {@link #getCreated()} in binary format. */ + static final int CREATED__ID = 2; + + /** Identifier for the property {@link #getUpdated()} in binary format. */ + static final int UPDATED__ID = 3; + + private long _userId = 0L; + + private long _created = 0L; + + private long _updated = 0L; + + /** + * Creates a {@link AnswerBotSetting} instance. + */ + protected AnswerBotSetting() { + super(); + } + + /** The type code of this instance. */ + public abstract TypeKind kind(); + + /** + * ID of the PhoneBlock user. + */ + public final long getUserId() { + return _userId; + } + + /** + * @see #getUserId() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSetting setUserId(long value) { + internalSetUserId(value); + return this; + } + + /** Internal setter for {@link #getUserId()} without chain call utility. */ + protected final void internalSetUserId(long value) { + _listener.beforeSet(this, USER_ID__PROP, value); + _userId = value; + } + + /** + * Time when the setting was created. + */ + public final long getCreated() { + return _created; + } + + /** + * @see #getCreated() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSetting setCreated(long value) { + internalSetCreated(value); + return this; + } + + /** Internal setter for {@link #getCreated()} without chain call utility. */ + protected final void internalSetCreated(long value) { + _listener.beforeSet(this, CREATED__PROP, value); + _created = value; + } + + /** + * Time when the setting was last updated. + */ + public final long getUpdated() { + return _updated; + } + + /** + * @see #getUpdated() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSetting setUpdated(long value) { + internalSetUpdated(value); + return this; + } + + /** Internal setter for {@link #getUpdated()} without chain call utility. */ + protected final void internalSetUpdated(long value) { + _listener.beforeSet(this, UPDATED__PROP, value); + _updated = value; + } + + protected de.haumacher.msgbuf.observer.Listener _listener = de.haumacher.msgbuf.observer.Listener.NONE; + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSetting registerListener(de.haumacher.msgbuf.observer.Listener l) { + internalRegisterListener(l); + return this; + } + + protected final void internalRegisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.register(_listener, l); + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSetting unregisterListener(de.haumacher.msgbuf.observer.Listener l) { + internalUnregisterListener(l); + return this; + } + + protected final void internalUnregisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.unregister(_listener, l); + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + USER_ID__PROP, + CREATED__PROP, + UPDATED__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case USER_ID__PROP: return getUserId(); + case CREATED__PROP: return getCreated(); + case UPDATED__PROP: return getUpdated(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case USER_ID__PROP: internalSetUserId((long) value); break; + case CREATED__PROP: internalSetCreated((long) value); break; + case UPDATED__PROP: internalSetUpdated((long) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSetting readAnswerBotSetting(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.settings.AnswerBotSetting result; + in.beginArray(); + String type = in.nextString(); + switch (type) { + case AnswerBotDynDns.ANSWER_BOT_DYN_DNS__TYPE: result = de.haumacher.phoneblock.db.settings.AnswerBotDynDns.readAnswerBotDynDns(in); break; + case AnswerBotSip.ANSWER_BOT_SIP__TYPE: result = de.haumacher.phoneblock.db.settings.AnswerBotSip.readAnswerBotSip(in); break; + default: in.skipValue(); result = null; break; + } + in.endArray(); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + out.beginArray(); + out.value(jsonType()); + writeContent(out); + out.endArray(); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(USER_ID__PROP); + out.value(getUserId()); + out.name(CREATED__PROP); + out.value(getCreated()); + out.name(UPDATED__PROP); + out.value(getUpdated()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case USER_ID__PROP: setUserId(in.nextLong()); break; + case CREATED__PROP: setCreated(in.nextLong()); break; + case UPDATED__PROP: setUpdated(in.nextLong()); break; + default: super.readField(in, field); + } + } + + /** The binary identifier for this concrete type in the polymorphic {@link de.haumacher.phoneblock.db.settings.AnswerBotSetting} hierarchy. */ + abstract int typeId(); + + @Override + public final void writeTo(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + out.beginObject(); + out.name(0); + out.value(typeId()); + writeFields(out); + out.endObject(); + } + + /** + * Serializes all fields of this instance to the given binary output. + * + * @param out + * The binary output to write to. + * @throws java.io.IOException If writing fails. + */ + protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + out.name(USER_ID__ID); + out.value(getUserId()); + out.name(CREATED__ID); + out.value(getCreated()); + out.name(UPDATED__ID); + out.value(getUpdated()); + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSetting readAnswerBotSetting(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + in.beginObject(); + int typeField = in.nextName(); + assert typeField == 0; + int type = in.nextInt(); + de.haumacher.phoneblock.db.settings.AnswerBotSetting result; + switch (type) { + case de.haumacher.phoneblock.db.settings.AnswerBotDynDns.ANSWER_BOT_DYN_DNS__TYPE_ID: result = de.haumacher.phoneblock.db.settings.AnswerBotDynDns.readAnswerBotDynDns_Content(in); break; + case de.haumacher.phoneblock.db.settings.AnswerBotSip.ANSWER_BOT_SIP__TYPE_ID: result = de.haumacher.phoneblock.db.settings.AnswerBotSip.readAnswerBotSip_Content(in); break; + default: result = null; while (in.hasNext()) {in.skipValue(); } + } + in.endObject(); + return result; + } + + /** Helper for reading all fields of this instance. */ + protected final void readContent(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + while (in.hasNext()) { + int field = in.nextName(); + readField(in, field); + } + } + + /** Consumes the value for the field with the given ID and assigns its value. */ + protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { + switch (field) { + case USER_ID__ID: setUserId(in.nextLong()); break; + case CREATED__ID: setCreated(in.nextLong()); break; + case UPDATED__ID: setUpdated(in.nextLong()); break; + default: in.skipValue(); + } + } + + /** Accepts the given visitor. */ + public abstract R visit(Visitor v, A arg) throws E; + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java new file mode 100644 index 00000000..cc9d3555 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java @@ -0,0 +1,475 @@ +package de.haumacher.phoneblock.db.settings; + +public class AnswerBotSip extends AnswerBotSetting { + + /** + * Creates a {@link de.haumacher.phoneblock.db.settings.AnswerBotSip} instance. + */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { + return new de.haumacher.phoneblock.db.settings.AnswerBotSip(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.db.settings.AnswerBotSip} type in JSON format. */ + public static final String ANSWER_BOT_SIP__TYPE = "AnswerBotSip"; + + /** @see #getHost() */ + public static final String HOST__PROP = "host"; + + /** @see #getSipRegistrar() */ + public static final String SIP_REGISTRAR__PROP = "sipRegistrar"; + + /** @see #getSipDomain() */ + public static final String SIP_DOMAIN__PROP = "sipDomain"; + + /** @see #getSipUser() */ + public static final String SIP_USER__PROP = "sipUser"; + + /** @see #getSipPasswd() */ + public static final String SIP_PASSWD__PROP = "sipPasswd"; + + /** @see #isRegistered() */ + public static final String REGISTERED__PROP = "registered"; + + /** @see #getLastRegister() */ + public static final String LAST_REGISTER__PROP = "lastRegister"; + + /** @see #getRegisterError() */ + public static final String REGISTER_ERROR__PROP = "registerError"; + + /** @see #getCallsAccepted() */ + public static final String CALLS_ACCEPTED__PROP = "callsAccepted"; + + /** Identifier for the {@link de.haumacher.phoneblock.db.settings.AnswerBotSip} type in binary format. */ + static final int ANSWER_BOT_SIP__TYPE_ID = 2; + + /** Identifier for the property {@link #getHost()} in binary format. */ + static final int HOST__ID = 4; + + /** Identifier for the property {@link #getSipRegistrar()} in binary format. */ + static final int SIP_REGISTRAR__ID = 5; + + /** Identifier for the property {@link #getSipDomain()} in binary format. */ + static final int SIP_DOMAIN__ID = 6; + + /** Identifier for the property {@link #getSipUser()} in binary format. */ + static final int SIP_USER__ID = 7; + + /** Identifier for the property {@link #getSipPasswd()} in binary format. */ + static final int SIP_PASSWD__ID = 8; + + /** Identifier for the property {@link #isRegistered()} in binary format. */ + static final int REGISTERED__ID = 9; + + /** Identifier for the property {@link #getLastRegister()} in binary format. */ + static final int LAST_REGISTER__ID = 10; + + /** Identifier for the property {@link #getRegisterError()} in binary format. */ + static final int REGISTER_ERROR__ID = 11; + + /** Identifier for the property {@link #getCallsAccepted()} in binary format. */ + static final int CALLS_ACCEPTED__ID = 12; + + private String _host = ""; + + private String _sipRegistrar = ""; + + private String _sipDomain = ""; + + private String _sipUser = ""; + + private String _sipPasswd = ""; + + private boolean _registered = false; + + private long _lastRegister = 0L; + + private String _registerError = ""; + + private int _callsAccepted = 0; + + /** + * Creates a {@link AnswerBotSip} instance. + * + * @see de.haumacher.phoneblock.db.settings.AnswerBotSip#create() + */ + protected AnswerBotSip() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.ANSWER_BOT_SIP; + } + + /** + * Static host name configured for the user's box. + */ + public final String getHost() { + return _host; + } + + /** + * @see #getHost() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setHost(String value) { + internalSetHost(value); + return this; + } + + /** Internal setter for {@link #getHost()} without chain call utility. */ + protected final void internalSetHost(String value) { + _listener.beforeSet(this, HOST__PROP, value); + _host = value; + } + + /** + * The SIP name of the user's box. + */ + public final String getSipRegistrar() { + return _sipRegistrar; + } + + /** + * @see #getSipRegistrar() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipRegistrar(String value) { + internalSetSipRegistrar(value); + return this; + } + + /** Internal setter for {@link #getSipRegistrar()} without chain call utility. */ + protected final void internalSetSipRegistrar(String value) { + _listener.beforeSet(this, SIP_REGISTRAR__PROP, value); + _sipRegistrar = value; + } + + /** + * The SIP domain for authentication at the user's box. + */ + public final String getSipDomain() { + return _sipDomain; + } + + /** + * @see #getSipDomain() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipDomain(String value) { + internalSetSipDomain(value); + return this; + } + + /** Internal setter for {@link #getSipDomain()} without chain call utility. */ + protected final void internalSetSipDomain(String value) { + _listener.beforeSet(this, SIP_DOMAIN__PROP, value); + _sipDomain = value; + } + + /** + * The user name for SIP registration at the user's box. + */ + public final String getSipUser() { + return _sipUser; + } + + /** + * @see #getSipUser() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipUser(String value) { + internalSetSipUser(value); + return this; + } + + /** Internal setter for {@link #getSipUser()} without chain call utility. */ + protected final void internalSetSipUser(String value) { + _listener.beforeSet(this, SIP_USER__PROP, value); + _sipUser = value; + } + + /** + * The password used for SIP registration at the user's box. + */ + public final String getSipPasswd() { + return _sipPasswd; + } + + /** + * @see #getSipPasswd() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipPasswd(String value) { + internalSetSipPasswd(value); + return this; + } + + /** Internal setter for {@link #getSipPasswd()} without chain call utility. */ + protected final void internalSetSipPasswd(String value) { + _listener.beforeSet(this, SIP_PASSWD__PROP, value); + _sipPasswd = value; + } + + /** + * Whether this answer bot is currently registered sucessfully. + */ + public final boolean isRegistered() { + return _registered; + } + + /** + * @see #isRegistered() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setRegistered(boolean value) { + internalSetRegistered(value); + return this; + } + + /** Internal setter for {@link #isRegistered()} without chain call utility. */ + protected final void internalSetRegistered(boolean value) { + _listener.beforeSet(this, REGISTERED__PROP, value); + _registered = value; + } + + /** + * Whether the last registration process finished. + */ + public final long getLastRegister() { + return _lastRegister; + } + + /** + * @see #getLastRegister() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setLastRegister(long value) { + internalSetLastRegister(value); + return this; + } + + /** Internal setter for {@link #getLastRegister()} without chain call utility. */ + protected final void internalSetLastRegister(long value) { + _listener.beforeSet(this, LAST_REGISTER__PROP, value); + _lastRegister = value; + } + + /** + * An error message transmitted during the last registration process. + */ + public final String getRegisterError() { + return _registerError; + } + + /** + * @see #getRegisterError() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setRegisterError(String value) { + internalSetRegisterError(value); + return this; + } + + /** Internal setter for {@link #getRegisterError()} without chain call utility. */ + protected final void internalSetRegisterError(String value) { + _listener.beforeSet(this, REGISTER_ERROR__PROP, value); + _registerError = value; + } + + /** + * The number of calls accepted by this answer bot. + */ + public final int getCallsAccepted() { + return _callsAccepted; + } + + /** + * @see #getCallsAccepted() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setCallsAccepted(int value) { + internalSetCallsAccepted(value); + return this; + } + + /** Internal setter for {@link #getCallsAccepted()} without chain call utility. */ + protected final void internalSetCallsAccepted(int value) { + _listener.beforeSet(this, CALLS_ACCEPTED__PROP, value); + _callsAccepted = value; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSip setUserId(long value) { + internalSetUserId(value); + return this; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSip setCreated(long value) { + internalSetCreated(value); + return this; + } + + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSip setUpdated(long value) { + internalSetUpdated(value); + return this; + } + + @Override + public String jsonType() { + return ANSWER_BOT_SIP__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + HOST__PROP, + SIP_REGISTRAR__PROP, + SIP_DOMAIN__PROP, + SIP_USER__PROP, + SIP_PASSWD__PROP, + REGISTERED__PROP, + LAST_REGISTER__PROP, + REGISTER_ERROR__PROP, + CALLS_ACCEPTED__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case HOST__PROP: return getHost(); + case SIP_REGISTRAR__PROP: return getSipRegistrar(); + case SIP_DOMAIN__PROP: return getSipDomain(); + case SIP_USER__PROP: return getSipUser(); + case SIP_PASSWD__PROP: return getSipPasswd(); + case REGISTERED__PROP: return isRegistered(); + case LAST_REGISTER__PROP: return getLastRegister(); + case REGISTER_ERROR__PROP: return getRegisterError(); + case CALLS_ACCEPTED__PROP: return getCallsAccepted(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case HOST__PROP: internalSetHost((String) value); break; + case SIP_REGISTRAR__PROP: internalSetSipRegistrar((String) value); break; + case SIP_DOMAIN__PROP: internalSetSipDomain((String) value); break; + case SIP_USER__PROP: internalSetSipUser((String) value); break; + case SIP_PASSWD__PROP: internalSetSipPasswd((String) value); break; + case REGISTERED__PROP: internalSetRegistered((boolean) value); break; + case LAST_REGISTER__PROP: internalSetLastRegister((long) value); break; + case REGISTER_ERROR__PROP: internalSetRegisterError((String) value); break; + case CALLS_ACCEPTED__PROP: internalSetCallsAccepted((int) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSip readAnswerBotSip(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.settings.AnswerBotSip result = new de.haumacher.phoneblock.db.settings.AnswerBotSip(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(HOST__PROP); + out.value(getHost()); + out.name(SIP_REGISTRAR__PROP); + out.value(getSipRegistrar()); + out.name(SIP_DOMAIN__PROP); + out.value(getSipDomain()); + out.name(SIP_USER__PROP); + out.value(getSipUser()); + out.name(SIP_PASSWD__PROP); + out.value(getSipPasswd()); + out.name(REGISTERED__PROP); + out.value(isRegistered()); + out.name(LAST_REGISTER__PROP); + out.value(getLastRegister()); + out.name(REGISTER_ERROR__PROP); + out.value(getRegisterError()); + out.name(CALLS_ACCEPTED__PROP); + out.value(getCallsAccepted()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case HOST__PROP: setHost(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case SIP_REGISTRAR__PROP: setSipRegistrar(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case SIP_DOMAIN__PROP: setSipDomain(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case SIP_USER__PROP: setSipUser(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case SIP_PASSWD__PROP: setSipPasswd(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case REGISTERED__PROP: setRegistered(in.nextBoolean()); break; + case LAST_REGISTER__PROP: setLastRegister(in.nextLong()); break; + case REGISTER_ERROR__PROP: setRegisterError(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case CALLS_ACCEPTED__PROP: setCallsAccepted(in.nextInt()); break; + default: super.readField(in, field); + } + } + + /** The binary identifier for this concrete type in the polymorphic {@link de.haumacher.phoneblock.db.settings.AnswerBotSip} hierarchy. */ + public int typeId() { + return ANSWER_BOT_SIP__TYPE_ID; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(HOST__ID); + out.value(getHost()); + out.name(SIP_REGISTRAR__ID); + out.value(getSipRegistrar()); + out.name(SIP_DOMAIN__ID); + out.value(getSipDomain()); + out.name(SIP_USER__ID); + out.value(getSipUser()); + out.name(SIP_PASSWD__ID); + out.value(getSipPasswd()); + out.name(REGISTERED__ID); + out.value(isRegistered()); + out.name(LAST_REGISTER__ID); + out.value(getLastRegister()); + out.name(REGISTER_ERROR__ID); + out.value(getRegisterError()); + out.name(CALLS_ACCEPTED__ID); + out.value(getCallsAccepted()); + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSip readAnswerBotSip(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + in.beginObject(); + de.haumacher.phoneblock.db.settings.AnswerBotSip result = de.haumacher.phoneblock.db.settings.AnswerBotSip.readAnswerBotSip_Content(in); + in.endObject(); + return result; + } + + /** Helper for creating an object of type {@link de.haumacher.phoneblock.db.settings.AnswerBotSip} from a polymorphic composition. */ + public static de.haumacher.phoneblock.db.settings.AnswerBotSip readAnswerBotSip_Content(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.settings.AnswerBotSip result = new AnswerBotSip(); + result.readContent(in); + return result; + } + + @Override + protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { + switch (field) { + case HOST__ID: setHost(in.nextString()); break; + case SIP_REGISTRAR__ID: setSipRegistrar(in.nextString()); break; + case SIP_DOMAIN__ID: setSipDomain(in.nextString()); break; + case SIP_USER__ID: setSipUser(in.nextString()); break; + case SIP_PASSWD__ID: setSipPasswd(in.nextString()); break; + case REGISTERED__ID: setRegistered(in.nextBoolean()); break; + case LAST_REGISTER__ID: setLastRegister(in.nextLong()); break; + case REGISTER_ERROR__ID: setRegisterError(in.nextString()); break; + case CALLS_ACCEPTED__ID: setCallsAccepted(in.nextInt()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.db.settings.AnswerBotSetting.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto index 79aa597e..db7278f2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto @@ -28,4 +28,60 @@ message UserSettings { /** Timestamp when the user requested the blocklist last time */ long lastAccess; +} + +/** Common options of answer bot settings. */ +abstract message AnswerBotSetting { + /** ID of the PhoneBlock user. */ + long userId; + + /** Time when the setting was created. */ + long created; + + /** Time when the setting was last updated. */ + long updated; +} + +/** Configuration options and state of an answer bot. */ +message AnswerBotDynDns extends AnswerBotSetting { + /** The user name for DynDNS update and SIP registration. */ + string dyndnsUser; + + /** Password hash for DynDNS update. */ + string dynDnsPasswd; + + /** IPv4 address transmitted during the last DynDNS update. */ + string ipv4; + + /** IPv6 address transmitted during the last DynDNS update. */ + string ipv6; +} + +message AnswerBotSip extends AnswerBotSetting { + /** Static host name configured for the user's box. */ + string host; + + /** The SIP name of the user's box. */ + string sipRegistrar; + + /** The SIP domain for authentication at the user's box. */ + string sipDomain; + + /** The user name for SIP registration at the user's box. */ + string sipUser; + + /** The password used for SIP registration at the user's box. */ + string sipPasswd; + + /** Whether this answer bot is currently registered sucessfully. */ + bool registered; + + /** Whether the last registration process finished. */ + long lastRegister; + + /** An error message transmitted during the last registration process. */ + string registerError; + + /** The number of calls accepted by this answer bot. */ + int callsAccepted; } \ No newline at end of file diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/util/ServletUtil.java b/phoneblock/src/main/java/de/haumacher/phoneblock/util/ServletUtil.java index 3ba99a22..93cd32db 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/util/ServletUtil.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/util/ServletUtil.java @@ -18,7 +18,7 @@ import de.haumacher.phoneblock.db.DBService; /** - * TODO + * Utility methods for servlet implementations. * * @author Bernhard Haumacher */ From b95f8fa1fead0bde68b57f4b4d1350a5598b890d Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 21:06:59 +0100 Subject: [PATCH 050/153] Prevent NPE, if conversation directory does not exist. --- .../java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 5bbd76e9..de4a27ad 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -109,6 +109,10 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function for (AudioType formatType : AudioType.values()) { File typeDir = new File(stateDir, formatType.dirName()); + if (!typeDir.isDirectory()) { + LOG.warn("Missing conversation directory: " + typeDir.getAbsolutePath()); + continue; + } Map> audioFragments = audioFragmentsByType.computeIfAbsent(formatType, k -> new EnumMap<>(SpeechType.class)); From 9e9f6bab6cd313076fa7d57cf114521228666dc2 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 21:07:13 +0100 Subject: [PATCH 051/153] Added setters to bot configuration. --- .../phoneblock/answerbot/AnswerbotConfig.java | 106 +++++++++++++++--- 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index a5db7fdd..ce4fe048 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -58,81 +58,157 @@ public String getRecordingDir() { public int bufferTime() { return _bufferTime; } + + /** + * @see #bufferTime() + */ + public void setBufferTime(int bufferTime) { + _bufferTime = bufferTime; + } + @Override public int minSilenceTime() { return _minSilenceTime; } + + /** + * @see #minSilenceTime() + */ + public void setMinSilenceTime(int minSilenceTime) { + _minSilenceTime = minSilenceTime; + } + @Override public int paddingTime() { return _paddingTime; } + + /** + * @see #paddingTime() + */ + public void setPaddingTime(int paddingTime) { + _paddingTime = paddingTime; + } + @Override public double silenceDb() { return silenceDb; } + + /** + * @see #silenceDb() + */ + public void setSilenceDb(double silenceDb) { + this.silenceDb = silenceDb; + } @Override public String recordingDir() { return _recordingDir; } + /** + * @see #recordingDir() + */ + public void setRecordingDir(String recordingDir) { + _recordingDir = recordingDir; + } + @Override public File conversationDir() { return _conversationDir; } + /** + * @see #conversationDir() + */ + public void setConversationDir(File conversationDir) { + _conversationDir = conversationDir; + } + @Override public boolean getAcceptAnonymous() { return _acceptAnonymous; } + /** + * @see #getAcceptAnonymous() + */ + public void setAcceptAnonymous(boolean acceptAnonymous) { + _acceptAnonymous = acceptAnonymous; + } + @Override public int getMinVotes() { return _minVotes; } + /** + * @see #getMinVotes() + */ + public void setMinVotes(int minVotes) { + _minVotes = minVotes; + } + @Override public String getTestNumber() { return _testPrefix; } - + /** - * Normalizes options, must be called after option parsing. + * @see #getTestNumber() */ - public void normalize() { - if ("none".equals(_recordingDir)) { - _recordingDir = null; - } - - if (!_conversationDir.isDirectory()) { - System.err.println("Conversation directory does not exist: " + conversationDir().getAbsolutePath()); - System.exit(1); - } - if (_testPrefix != null && _testPrefix.isBlank()) { - _testPrefix = null; - } + public void setTestPrefix(String testPrefix) { + _testPrefix = testPrefix; } - + @Override public MediaDesc[] getMediaDescs() { return _mediaDescs; } + /** + * @see #getMediaDescs() + */ + public void setMediaDescs(MediaDesc[] mediaDescs) { + _mediaDescs = mediaDescs; + } + @Override public String getMediaAddr() { return null; } + @Override public boolean getNoOffer() { return false; } + @Override public int getRefuseTime() { return -1; } + @Override public Direction getDirection() { return Direction.FULL_DUPLEX; } + + /** + * Normalizes options, must be called after option parsing. + */ + public void normalize() { + if ("none".equals(_recordingDir)) { + _recordingDir = null; + } + + if (!_conversationDir.isDirectory()) { + System.err.println("Conversation directory does not exist: " + conversationDir().getAbsolutePath()); + System.exit(1); + } + if (_testPrefix != null && _testPrefix.isBlank()) { + _testPrefix = null; + } + } } From f9b3760f57f428aac398918dc4141176f8cefe83 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 21:08:47 +0100 Subject: [PATCH 052/153] Log failure, if DynDNS request crashes. --- .../phoneblock/app/api/DynIpServlet.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java index ae660950..396758e9 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java @@ -8,8 +8,6 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; @@ -37,6 +35,15 @@ public class DynIpServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + tryGet(req, resp); + } catch (Throwable ex) { + LOG.error("Faild to process request.", ex); + throw ex; + } + } + + protected void tryGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String user = req.getParameter("user"); if (user == null) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "No user name provided."); @@ -95,7 +102,7 @@ private static boolean changed(String a, String b) { return ! a.equals(b); } - private void processAddress(AnswerBotDynDns update, String ip) throws UnknownHostException { + private void processAddress(AnswerBotDynDns update, String ip) { if (ip == null) { return; } @@ -106,12 +113,17 @@ private void processAddress(AnswerBotDynDns update, String ip) throws UnknownHos return; } - InetAddress address = InetAddress.getByName(ip); + InetAddress address; + try { + address = InetAddress.getByName(ip); + } catch (UnknownHostException ex) { + LOG.warn("Cannot resolve host: (" + ip + ") for user '" + update.getDyndnsUser() + "': " + ex.getMessage()); + return; + } if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress() || address.isSiteLocalAddress() || address.isMulticastAddress()) { - String message = "Provided invalid address (" + ip + "): " + update.getDyndnsUser(); - LOG.warn(message); + LOG.warn("Provided invalid address (" + ip + "): " + update.getDyndnsUser()); } else { if (address instanceof Inet4Address) { update.setIpv4(ip); From 6b01b92afd022d1b4d82bf5c34fce82a046af4d2 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 21:10:09 +0100 Subject: [PATCH 053/153] First working version of built-in SIP service. --- .../phoneblock/ab/CreateABServlet.java | 5 +- .../haumacher/phoneblock/ab/SipService.java | 232 ++++++++++++++++-- .../haumacher/phoneblock/app/Application.java | 9 +- .../phoneblock/db/DBAnswerBotSip.java | 27 ++ .../de/haumacher/phoneblock/db/Users.java | 9 + .../de/haumacher/phoneblock/db/db-schema.sql | 25 +- .../phoneblock/db/settings/AnswerBotSip.java | 184 +++++++------- .../phoneblock/db/settings/settings.proto | 8 +- phoneblock/src/main/java/module-info.java | 1 + 9 files changed, 370 insertions(+), 130 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index dddcd399..308132d8 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -11,6 +11,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import de.haumacher.phoneblock.db.settings.AnswerBotSip; + /** * Servlet creating an answerbot. */ @@ -25,7 +27,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String passwd = (String) req.getSession().getAttribute("ab-passwd"); String hostname = req.getParameter("hostname"); - SipService.getInstance().register(username, passwd, hostname); + AnswerBotSip bot = AnswerBotSip.create().setUserName(username).setPasswd(passwd).setHost(hostname); + SipService.getInstance().register(bot); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 359d5bb1..3b0271f5 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -3,52 +3,223 @@ */ package de.haumacher.phoneblock.ab; +import java.io.File; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; +import org.apache.ibatis.session.SqlSession; +import org.kohsuke.args4j.ClassParser; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.mjsip.pool.PortConfig; import org.mjsip.pool.PortPool; +import org.mjsip.sip.address.NameAddress; +import org.mjsip.sip.address.SipURI; import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; import org.mjsip.time.Scheduler; +import org.mjsip.ua.UserOptions; import org.mjsip.ua.registration.RegistrationClient; +import org.mjsip.ua.registration.RegistrationClientListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zoolu.util.ConfigFile; import de.haumacher.phoneblock.answerbot.AnswerBot; import de.haumacher.phoneblock.answerbot.AnswerbotConfig; import de.haumacher.phoneblock.answerbot.CustomerConfig; +import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBAnswerBotDynDns; +import de.haumacher.phoneblock.db.DBService; +import de.haumacher.phoneblock.db.Users; +import de.haumacher.phoneblock.db.settings.AnswerBotSip; import de.haumacher.phoneblock.scheduler.SchedulerService; /** * Service managing a SIP stack. */ -public class SipService implements ServletContextListener { +public class SipService implements ServletContextListener, RegistrationClientListener { + private static final Logger LOG = LoggerFactory.getLogger(SipService.class); + private SchedulerService _scheduler; + private DBService _dbService; + private SipProvider _sipProvider; private PortPool _portPool; private AnswerBot _answerBot; + private final ConcurrentHashMap _clients = new ConcurrentHashMap<>(); + private static SipService _instance; /** * Creates a {@link SipService}. */ - public SipService(SchedulerService scheduler) { + public SipService(SchedulerService scheduler, DBService dbService) { _scheduler = scheduler; + _dbService = dbService; } @Override public void contextInitialized(ServletContextEvent sce) { SipConfig sipConfig = new SipConfig(); - sipConfig.setHostPort(50060); - sipConfig.setViaAddr("phoneblock.haumacher.de"); - _sipProvider = new SipProvider(sipConfig, Scheduler.of(_scheduler.getInstance().executor())); - - _portPool = new PortPool(50061, 50080); + + String hostname = "phoneblock.net"; + String ip4 = null; + String ip6 = null; + try { + InetAddress[] addresses = InetAddress.getAllByName(hostname); + for (InetAddress address : addresses) { + if (address.isAnyLocalAddress()) { + continue; + } + if (address.isLinkLocalAddress()) { + continue; + } + if (address.isLoopbackAddress()) { + continue; + } + if (address.isMulticastAddress()) { + continue; + } + if (address.isSiteLocalAddress()) { + continue; + } + + if (address instanceof Inet6Address) { + ip6 = address.getHostAddress(); + LOG.info("Found IPv6 address: " + ip6); + } else { + ip4 = address.getHostAddress(); + LOG.info("Found IPv4 address: " + ip4); + } + } + } catch (UnknownHostException ex) { + LOG.error("Cannot resolve own address.", ex); + } + + sipConfig.setViaAddr(ip6 != null ? ip6 : ip4 != null ? ip4 : hostname); + + PortConfig portConfig = new PortConfig(); + portConfig.setMediaPort(50061); + portConfig.setPortCount(20); AnswerbotConfig botOptions = new AnswerbotConfig(); + + loadConfig(sipConfig, portConfig, botOptions); + + Scheduler scheduler = Scheduler.of(SchedulerService.getInstance().executor()); + _sipProvider = new SipProvider(sipConfig, scheduler); + _portPool = portConfig.createPool(); + Function configForUser = (username) -> null; _answerBot = new AnswerBot(_sipProvider, botOptions, configForUser, _portPool); _instance = this; + + DB db = _dbService.db(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + List bots = users.getEnabledAnswerBots(); + for (AnswerBotSip bot : bots) { + String host = bot.getHost(); + if (host == null || host.isEmpty()) { + DBAnswerBotDynDns dynDns = users.getDynDnsByUserId(bot.getUserId()); + if (dynDns != null) { + String ipv6 = dynDns.getIpv6(); + if (ipv6 != null && !ipv6.isEmpty()) { + bot.setHost(ipv6); + } else { + String ipv4 = dynDns.getIpv4(); + if (ipv4 != null && !ipv4.isEmpty()) { + bot.setHost(ipv4); + } else { + // Cannot enable. + LOG.warn("Disabling answer bot without host address: " + bot.getUserId() + "/" + bot.getUserName()); + users.disableAnswerBot(bot.getUserId()); + session.commit(); + continue; + } + } + } + } + + register(bot); + } + } + } + + private void loadConfig(Object...beans) { + try { + CmdLineParser parser = new CmdLineParser(null); + for (Object bean : beans) { + new ClassParser().parse(bean, parser); + } + + InitialContext initCtx = new InitialContext(); + Context envCtx = (Context) initCtx.lookup("java:comp/env"); + + try { + String fileName = null; + Collection jndiOptions = new ArrayList<>(); + + NamingEnumeration options = envCtx.list("answerbot"); + for (; options.hasMore(); ) { + NameClassPair pair = options.next(); + String name = pair.getName(); + Object lookup = envCtx.lookup("answerbot/" + name); + + if ("configfile".equals(name)) { + fileName = lookup.toString(); + } else { + jndiOptions.add(name); + if (lookup != null) { + jndiOptions.add(lookup.toString()); + } + } + } + + Collection fileOptions; + if (fileName != null) { + File file = new File(fileName); + if (!file.exists()) { + LOG.error("Answerbot configuration file does not exits: " + file.getAbsolutePath()); + } + + ConfigFile configFile = new ConfigFile(file); + fileOptions = configFile.toArguments(); + } else { + fileOptions = Collections.emptyList(); + } + + Collection arguments = new ArrayList<>(fileOptions); + arguments.addAll(jndiOptions); + + parser.parseArgument(arguments); + } catch (NamingException ex) { + LOG.error("Error loading JDNI configuration: " + ex.getMessage()); + } catch (CmdLineException ex) { + LOG.error("Invalid answer bot configuration: " + ex.getMessage()); + } + } catch (NamingException ex) { + LOG.info("Not using JNDI configuration: " + ex.getMessage()); + } } @Override @@ -56,8 +227,15 @@ public void contextDestroyed(ServletContextEvent sce) { _answerBot = null; _portPool = null; - _sipProvider.halt(); - _sipProvider = null; + for (RegistrationClient client : _clients.values()) { + client.halt(); + } + _clients.clear(); + + if (_sipProvider != null) { + _sipProvider.halt(); + _sipProvider = null; + } if (_instance == this) { _instance = null; @@ -73,19 +251,35 @@ public static SipService getInstance() { /** * TODO - * - * @param username - * @param passwd - * @param hostname */ - public void register(String username, String passwd, String hostname) { + public void register(AnswerBotSip bot) { CustomerConfig regConfig = new CustomerConfig(); - regConfig.setUser(username); - regConfig.setUser(passwd); - regConfig.setRealm("fritz.box"); - regConfig.setRegistrar(hostname); - RegistrationClient client = new RegistrationClient(_sipProvider, regConfig, null); + regConfig.setUser(bot.getUserName()); + regConfig.setPasswd(bot.getPasswd()); + regConfig.setRealm(bot.getRealm()); + regConfig.setRegistrar(new SipURI(bot.getRegistrar())); + regConfig.setRoute(new SipURI(bot.getHost()).addLr()); + + RegistrationClient client = new RegistrationClient(_sipProvider, regConfig, this); + RegistrationClient clash = _clients.put(bot.getUserName(), client); + + if (clash != null) { + clash.halt(); + } + client.loopRegister(regConfig); } + @Override + public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, + String result) { + LOG.info("Sucessfully registered: " + registration.getUsername()); + } + + @Override + public void onRegistrationFailure(RegistrationClient registration, NameAddress target, NameAddress contact, + String result) { + LOG.warn("Failed to register user '" + registration.getUsername() + "': " + result); + } + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java index d27b319d..37b10472 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java @@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory; import org.tinylog.configuration.Configuration; +import de.haumacher.phoneblock.ab.SipService; import de.haumacher.phoneblock.carddav.resource.AddressBookCache; import de.haumacher.phoneblock.chatgpt.ChatGPTService; import de.haumacher.phoneblock.crawl.CrawlerService; @@ -99,11 +100,15 @@ public void contextInitialized(ServletContextEvent sce) { gpt = new ChatGPTService(db, scheduler, indexer), new ManagementService(indexer, db, gpt), new AddressBookCache(), -// new SipService(scheduler) + new SipService(scheduler, db) }; for (int n = 0, cnt = _services.length; n < cnt; n++) { - _services[n].contextInitialized(sce); + try { + _services[n].contextInitialized(sce); + } catch (Exception ex) { + LOG.error("Failed to initialize service '" + _services[n].getClass().getName() + "'.", ex); + } } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java new file mode 100644 index 00000000..45174fc5 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.db; + +import de.haumacher.phoneblock.db.settings.AnswerBotSip; + +/** + * TODO + * + * @author Bernhard Haumacher + */ +public class DBAnswerBotSip extends AnswerBotSip { + + /** + * Creates a {@link DBAnswerBotSip}. + */ + public DBAnswerBotSip(long userId, String host, String registrar, String realm, String userName, String passwd) { + setUserId(userId); + setHost(host); + setRegistrar(registrar); + setRealm(realm); + setUserName(userName); + setPasswd(passwd); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 82751d8f..b1907714 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -113,10 +113,19 @@ public interface Users { @Select("select USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where DYNDNS_USER=#{user}") DBAnswerBotDynDns getDynDns(String user); + @Select("select USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where USERID=#{userId}") + DBAnswerBotDynDns getDynDnsByUserId(long userId); + /** * Updates the user's dynamic IP address. */ @Insert("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where USERID=#{userId}") void updateDynDny(long userId, String ip4, String ip6, long updated); + @Select("select USERID, HOST, REGISTRAR, REALM, USERNAME, PASSWD from ANSWERBOT_SIP where ENABLED = true") + List getEnabledAnswerBots(); + + @Update("update ANSWERBOT_SIP set ENABLED=false where USERID=#{userId}") + void disableAnswerBot(long userId); + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql index 1b25558d..00ef20c7 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql @@ -102,13 +102,13 @@ CREATE UNIQUE INDEX USERS_UN_INDEX_4 ON USERS (LOGIN); CREATE TABLE ANSWERBOT_DYNDNS ( USERID BIGINT NOT NULL, - CREATED BIGINT, - UPDATED BIGINT, + CREATED BIGINT DEFAULT 0 NOT NULL, + UPDATED BIGINT DEFAULT 0 NOT NULL, DYNDNS_USER CHARACTER VARYING(64) NOT NULL, DYNDNS_PASSWD CHARACTER VARYING(64) NOT NULL, - IP4 CHARACTER VARYING(64), - IP6 CHARACTER VARYING(64), + IP4 CHARACTER VARYING(64) DEFAULT '' NOT NULL, + IP6 CHARACTER VARYING(64) DEFAULT '' NOT NULL, CONSTRAINT ANSWERBOT_DYNDNS_PK PRIMARY KEY (USERID) ); @@ -118,25 +118,26 @@ CREATE UNIQUE INDEX ANSWERBOT_DYNDNS_USER ON ANSWERBOT_DYNDNS (DYNDNS_USER); CREATE TABLE ANSWERBOT_SIP ( USERID BIGINT NOT NULL, - CREATED BIGINT, - UPDATED BIGINT, + CREATED BIGINT DEFAULT 0 NOT NULL, + UPDATED BIGINT DEFAULT 0 NOT NULL, + ENABLED BOOLEAN DEFAULT TRUE NOT NULL, HOST CHARACTER VARYING(64), - SIP_REGISTRAR CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, - SIP_DOMAIN CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, - SIP_USER CHARACTER VARYING(64) NOT NULL, - SIP_PASSWD CHARACTER VARYING(64) NOT NULL, + REGISTRAR CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, + REALM CHARACTER VARYING(64) DEFAULT 'fritz.box' NOT NULL, + USERNAME CHARACTER VARYING(64) NOT NULL, + PASSWD CHARACTER VARYING(64) NOT NULL, REGISTERED BOOLEAN DEFAULT FALSE NOT NULL, - LAST_REGISTER BIGINT, + LAST_REGISTER BIGINT DEFAULT 0 NOT NULL, REGISTER_ERROR CHARACTER VARYING(512), CALLS_ACCEPTED INTEGER DEFAULT 0 NOT NULL, CONSTRAINT ANSWERBOT_SIP_PK PRIMARY KEY (USERID) ); -CREATE UNIQUE INDEX ANSWERBOT_SIP_USER ON ANSWERBOT_SIP (SIP_USER); +CREATE UNIQUE INDEX ANSWERBOT_SIP_USERNAME ON ANSWERBOT_SIP (USERNAME); -- PUBLIC.CALLREPORT definition diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java index cc9d3555..d48f5ab1 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java @@ -15,17 +15,17 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { /** @see #getHost() */ public static final String HOST__PROP = "host"; - /** @see #getSipRegistrar() */ - public static final String SIP_REGISTRAR__PROP = "sipRegistrar"; + /** @see #getRegistrar() */ + public static final String REGISTRAR__PROP = "registrar"; - /** @see #getSipDomain() */ - public static final String SIP_DOMAIN__PROP = "sipDomain"; + /** @see #getRealm() */ + public static final String REALM__PROP = "realm"; - /** @see #getSipUser() */ - public static final String SIP_USER__PROP = "sipUser"; + /** @see #getUserName() */ + public static final String USER_NAME__PROP = "userName"; - /** @see #getSipPasswd() */ - public static final String SIP_PASSWD__PROP = "sipPasswd"; + /** @see #getPasswd() */ + public static final String PASSWD__PROP = "passwd"; /** @see #isRegistered() */ public static final String REGISTERED__PROP = "registered"; @@ -45,17 +45,17 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { /** Identifier for the property {@link #getHost()} in binary format. */ static final int HOST__ID = 4; - /** Identifier for the property {@link #getSipRegistrar()} in binary format. */ - static final int SIP_REGISTRAR__ID = 5; + /** Identifier for the property {@link #getRegistrar()} in binary format. */ + static final int REGISTRAR__ID = 5; - /** Identifier for the property {@link #getSipDomain()} in binary format. */ - static final int SIP_DOMAIN__ID = 6; + /** Identifier for the property {@link #getRealm()} in binary format. */ + static final int REALM__ID = 6; - /** Identifier for the property {@link #getSipUser()} in binary format. */ - static final int SIP_USER__ID = 7; + /** Identifier for the property {@link #getUserName()} in binary format. */ + static final int USER_NAME__ID = 7; - /** Identifier for the property {@link #getSipPasswd()} in binary format. */ - static final int SIP_PASSWD__ID = 8; + /** Identifier for the property {@link #getPasswd()} in binary format. */ + static final int PASSWD__ID = 8; /** Identifier for the property {@link #isRegistered()} in binary format. */ static final int REGISTERED__ID = 9; @@ -71,13 +71,13 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { private String _host = ""; - private String _sipRegistrar = ""; + private String _registrar = ""; - private String _sipDomain = ""; + private String _realm = ""; - private String _sipUser = ""; + private String _userName = ""; - private String _sipPasswd = ""; + private String _passwd = ""; private boolean _registered = false; @@ -125,85 +125,85 @@ protected final void internalSetHost(String value) { /** * The SIP name of the user's box. */ - public final String getSipRegistrar() { - return _sipRegistrar; + public final String getRegistrar() { + return _registrar; } /** - * @see #getSipRegistrar() + * @see #getRegistrar() */ - public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipRegistrar(String value) { - internalSetSipRegistrar(value); + public de.haumacher.phoneblock.db.settings.AnswerBotSip setRegistrar(String value) { + internalSetRegistrar(value); return this; } - /** Internal setter for {@link #getSipRegistrar()} without chain call utility. */ - protected final void internalSetSipRegistrar(String value) { - _listener.beforeSet(this, SIP_REGISTRAR__PROP, value); - _sipRegistrar = value; + /** Internal setter for {@link #getRegistrar()} without chain call utility. */ + protected final void internalSetRegistrar(String value) { + _listener.beforeSet(this, REGISTRAR__PROP, value); + _registrar = value; } /** * The SIP domain for authentication at the user's box. */ - public final String getSipDomain() { - return _sipDomain; + public final String getRealm() { + return _realm; } /** - * @see #getSipDomain() + * @see #getRealm() */ - public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipDomain(String value) { - internalSetSipDomain(value); + public de.haumacher.phoneblock.db.settings.AnswerBotSip setRealm(String value) { + internalSetRealm(value); return this; } - /** Internal setter for {@link #getSipDomain()} without chain call utility. */ - protected final void internalSetSipDomain(String value) { - _listener.beforeSet(this, SIP_DOMAIN__PROP, value); - _sipDomain = value; + /** Internal setter for {@link #getRealm()} without chain call utility. */ + protected final void internalSetRealm(String value) { + _listener.beforeSet(this, REALM__PROP, value); + _realm = value; } /** * The user name for SIP registration at the user's box. */ - public final String getSipUser() { - return _sipUser; + public final String getUserName() { + return _userName; } /** - * @see #getSipUser() + * @see #getUserName() */ - public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipUser(String value) { - internalSetSipUser(value); + public de.haumacher.phoneblock.db.settings.AnswerBotSip setUserName(String value) { + internalSetUserName(value); return this; } - /** Internal setter for {@link #getSipUser()} without chain call utility. */ - protected final void internalSetSipUser(String value) { - _listener.beforeSet(this, SIP_USER__PROP, value); - _sipUser = value; + /** Internal setter for {@link #getUserName()} without chain call utility. */ + protected final void internalSetUserName(String value) { + _listener.beforeSet(this, USER_NAME__PROP, value); + _userName = value; } /** * The password used for SIP registration at the user's box. */ - public final String getSipPasswd() { - return _sipPasswd; + public final String getPasswd() { + return _passwd; } /** - * @see #getSipPasswd() + * @see #getPasswd() */ - public de.haumacher.phoneblock.db.settings.AnswerBotSip setSipPasswd(String value) { - internalSetSipPasswd(value); + public de.haumacher.phoneblock.db.settings.AnswerBotSip setPasswd(String value) { + internalSetPasswd(value); return this; } - /** Internal setter for {@link #getSipPasswd()} without chain call utility. */ - protected final void internalSetSipPasswd(String value) { - _listener.beforeSet(this, SIP_PASSWD__PROP, value); - _sipPasswd = value; + /** Internal setter for {@link #getPasswd()} without chain call utility. */ + protected final void internalSetPasswd(String value) { + _listener.beforeSet(this, PASSWD__PROP, value); + _passwd = value; } /** @@ -316,10 +316,10 @@ public String jsonType() { private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( java.util.Arrays.asList( HOST__PROP, - SIP_REGISTRAR__PROP, - SIP_DOMAIN__PROP, - SIP_USER__PROP, - SIP_PASSWD__PROP, + REGISTRAR__PROP, + REALM__PROP, + USER_NAME__PROP, + PASSWD__PROP, REGISTERED__PROP, LAST_REGISTER__PROP, REGISTER_ERROR__PROP, @@ -334,10 +334,10 @@ public java.util.List properties() { public Object get(String field) { switch (field) { case HOST__PROP: return getHost(); - case SIP_REGISTRAR__PROP: return getSipRegistrar(); - case SIP_DOMAIN__PROP: return getSipDomain(); - case SIP_USER__PROP: return getSipUser(); - case SIP_PASSWD__PROP: return getSipPasswd(); + case REGISTRAR__PROP: return getRegistrar(); + case REALM__PROP: return getRealm(); + case USER_NAME__PROP: return getUserName(); + case PASSWD__PROP: return getPasswd(); case REGISTERED__PROP: return isRegistered(); case LAST_REGISTER__PROP: return getLastRegister(); case REGISTER_ERROR__PROP: return getRegisterError(); @@ -350,10 +350,10 @@ public Object get(String field) { public void set(String field, Object value) { switch (field) { case HOST__PROP: internalSetHost((String) value); break; - case SIP_REGISTRAR__PROP: internalSetSipRegistrar((String) value); break; - case SIP_DOMAIN__PROP: internalSetSipDomain((String) value); break; - case SIP_USER__PROP: internalSetSipUser((String) value); break; - case SIP_PASSWD__PROP: internalSetSipPasswd((String) value); break; + case REGISTRAR__PROP: internalSetRegistrar((String) value); break; + case REALM__PROP: internalSetRealm((String) value); break; + case USER_NAME__PROP: internalSetUserName((String) value); break; + case PASSWD__PROP: internalSetPasswd((String) value); break; case REGISTERED__PROP: internalSetRegistered((boolean) value); break; case LAST_REGISTER__PROP: internalSetLastRegister((long) value); break; case REGISTER_ERROR__PROP: internalSetRegisterError((String) value); break; @@ -374,14 +374,14 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. super.writeFields(out); out.name(HOST__PROP); out.value(getHost()); - out.name(SIP_REGISTRAR__PROP); - out.value(getSipRegistrar()); - out.name(SIP_DOMAIN__PROP); - out.value(getSipDomain()); - out.name(SIP_USER__PROP); - out.value(getSipUser()); - out.name(SIP_PASSWD__PROP); - out.value(getSipPasswd()); + out.name(REGISTRAR__PROP); + out.value(getRegistrar()); + out.name(REALM__PROP); + out.value(getRealm()); + out.name(USER_NAME__PROP); + out.value(getUserName()); + out.name(PASSWD__PROP); + out.value(getPasswd()); out.name(REGISTERED__PROP); out.value(isRegistered()); out.name(LAST_REGISTER__PROP); @@ -396,10 +396,10 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { switch (field) { case HOST__PROP: setHost(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; - case SIP_REGISTRAR__PROP: setSipRegistrar(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; - case SIP_DOMAIN__PROP: setSipDomain(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; - case SIP_USER__PROP: setSipUser(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; - case SIP_PASSWD__PROP: setSipPasswd(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case REGISTRAR__PROP: setRegistrar(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case REALM__PROP: setRealm(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case USER_NAME__PROP: setUserName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case PASSWD__PROP: setPasswd(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; case REGISTERED__PROP: setRegistered(in.nextBoolean()); break; case LAST_REGISTER__PROP: setLastRegister(in.nextLong()); break; case REGISTER_ERROR__PROP: setRegisterError(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; @@ -418,14 +418,14 @@ protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws jav super.writeFields(out); out.name(HOST__ID); out.value(getHost()); - out.name(SIP_REGISTRAR__ID); - out.value(getSipRegistrar()); - out.name(SIP_DOMAIN__ID); - out.value(getSipDomain()); - out.name(SIP_USER__ID); - out.value(getSipUser()); - out.name(SIP_PASSWD__ID); - out.value(getSipPasswd()); + out.name(REGISTRAR__ID); + out.value(getRegistrar()); + out.name(REALM__ID); + out.value(getRealm()); + out.name(USER_NAME__ID); + out.value(getUserName()); + out.name(PASSWD__ID); + out.value(getPasswd()); out.name(REGISTERED__ID); out.value(isRegistered()); out.name(LAST_REGISTER__ID); @@ -455,10 +455,10 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip readAnswerBotSip_ protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { switch (field) { case HOST__ID: setHost(in.nextString()); break; - case SIP_REGISTRAR__ID: setSipRegistrar(in.nextString()); break; - case SIP_DOMAIN__ID: setSipDomain(in.nextString()); break; - case SIP_USER__ID: setSipUser(in.nextString()); break; - case SIP_PASSWD__ID: setSipPasswd(in.nextString()); break; + case REGISTRAR__ID: setRegistrar(in.nextString()); break; + case REALM__ID: setRealm(in.nextString()); break; + case USER_NAME__ID: setUserName(in.nextString()); break; + case PASSWD__ID: setPasswd(in.nextString()); break; case REGISTERED__ID: setRegistered(in.nextBoolean()); break; case LAST_REGISTER__ID: setLastRegister(in.nextLong()); break; case REGISTER_ERROR__ID: setRegisterError(in.nextString()); break; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto index db7278f2..867abb00 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto @@ -62,16 +62,16 @@ message AnswerBotSip extends AnswerBotSetting { string host; /** The SIP name of the user's box. */ - string sipRegistrar; + string registrar; /** The SIP domain for authentication at the user's box. */ - string sipDomain; + string realm; /** The user name for SIP registration at the user's box. */ - string sipUser; + string userName; /** The password used for SIP registration at the user's box. */ - string sipPasswd; + string passwd; /** Whether this answer bot is currently registered sucessfully. */ bool registered; diff --git a/phoneblock/src/main/java/module-info.java b/phoneblock/src/main/java/module-info.java index a400d1fa..bb80e1ad 100644 --- a/phoneblock/src/main/java/module-info.java +++ b/phoneblock/src/main/java/module-info.java @@ -32,6 +32,7 @@ requires org.mjsip.ua; requires org.mjsip.util; + requires args4j; requires com.google.api.client; requires com.google.auth; requires com.google.auth.oauth2; From 8106405f2a1e8f4453113d6208c9971c95353d1e Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 19 Nov 2023 22:32:42 +0100 Subject: [PATCH 054/153] Added call handling for multiple clients. --- .../phoneblock/answerbot/AnswerBot.java | 2 +- .../phoneblock/answerbot/AnswerbotConfig.java | 4 +- .../answerbot/AnswerbotOptions.java | 6 +-- .../phoneblock/ab/CreateABServlet.java | 3 +- .../haumacher/phoneblock/ab/Registration.java | 34 ++++++++++++ .../haumacher/phoneblock/ab/SipService.java | 53 ++++++++++++++----- .../de/haumacher/phoneblock/db/Users.java | 3 ++ 7 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index de4a27ad..54a43da6 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -174,7 +174,7 @@ protected UserAgentListener createCallHandler(String from) { LOG.info("Not accepting anonymous call."); return rejectHandler(); } - } else if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestNumber())) { + } else if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestPrefix())) { // A local test call, accept. return spamHandler(); } else { diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java index ce4fe048..b2de0936 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotConfig.java @@ -151,12 +151,12 @@ public void setMinVotes(int minVotes) { } @Override - public String getTestNumber() { + public String getTestPrefix() { return _testPrefix; } /** - * @see #getTestNumber() + * @see #getTestPrefix() */ public void setTestPrefix(String testPrefix) { _testPrefix = testPrefix; diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java index 0771d7b3..65fff71e 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerbotOptions.java @@ -39,10 +39,10 @@ public interface AnswerbotOptions extends DialogOptions, StaticOptions { MediaDesc[] getMediaDescs(); /** - * Whether a {@link #getTestNumber()} has been configured. + * Whether a {@link #getTestPrefix()} has been configured. */ default boolean hasTestNumber() { - return getTestNumber() != null; + return getTestPrefix() != null; } /** @@ -52,6 +52,6 @@ default boolean hasTestNumber() { * Use this for testing only. *

*/ - String getTestNumber(); + String getTestPrefix(); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index 308132d8..e6b4204d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -28,7 +28,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String hostname = req.getParameter("hostname"); AnswerBotSip bot = AnswerBotSip.create().setUserName(username).setPasswd(passwd).setHost(hostname); + SipService r = SipService.getInstance(); - SipService.getInstance().register(bot); + r.register(bot); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java new file mode 100644 index 00000000..70b6d456 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.ab; + +import org.mjsip.sip.provider.SipProvider; +import org.mjsip.ua.registration.RegistrationClient; +import org.mjsip.ua.registration.RegistrationClientListener; + +import de.haumacher.phoneblock.answerbot.CustomerOptions; + +/** + * {@link RegistrationClient} that holds the complete configuration for the customer. + */ +public class Registration extends RegistrationClient { + + private CustomerOptions _customer; + + /** + * Creates a {@link Registration}. + */ + public Registration(SipProvider sipProvider, CustomerOptions customer, RegistrationClientListener sipService) { + super(sipProvider, customer, sipService); + _customer = customer; + } + + /** + * The customer configuration of this client. + */ + public CustomerOptions getCustomer() { + return _customer; + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 3b0271f5..a39c3dea 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -12,7 +12,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; import javax.naming.Context; import javax.naming.InitialContext; @@ -33,7 +32,6 @@ import org.mjsip.sip.provider.SipConfig; import org.mjsip.sip.provider.SipProvider; import org.mjsip.time.Scheduler; -import org.mjsip.ua.UserOptions; import org.mjsip.ua.registration.RegistrationClient; import org.mjsip.ua.registration.RegistrationClientListener; import org.slf4j.Logger; @@ -43,6 +41,7 @@ import de.haumacher.phoneblock.answerbot.AnswerBot; import de.haumacher.phoneblock.answerbot.AnswerbotConfig; import de.haumacher.phoneblock.answerbot.CustomerConfig; +import de.haumacher.phoneblock.answerbot.CustomerOptions; import de.haumacher.phoneblock.db.DB; import de.haumacher.phoneblock.db.DBAnswerBotDynDns; import de.haumacher.phoneblock.db.DBService; @@ -64,7 +63,7 @@ public class SipService implements ServletContextListener, RegistrationClientLis private PortPool _portPool; private AnswerBot _answerBot; - private final ConcurrentHashMap _clients = new ConcurrentHashMap<>(); + private final ConcurrentHashMap _clients = new ConcurrentHashMap<>(); private static SipService _instance; @@ -127,8 +126,7 @@ public void contextInitialized(ServletContextEvent sce) { Scheduler scheduler = Scheduler.of(SchedulerService.getInstance().executor()); _sipProvider = new SipProvider(sipConfig, scheduler); _portPool = portConfig.createPool(); - Function configForUser = (username) -> null; - _answerBot = new AnswerBot(_sipProvider, botOptions, configForUser, _portPool); + _answerBot = new AnswerBot(_sipProvider, botOptions, this::getCustomer, _portPool); _instance = this; @@ -160,7 +158,8 @@ public void contextInitialized(ServletContextEvent sce) { } } - register(bot); + CustomerConfig regConfig = toCustomerConfig(bot); + register(regConfig); } } } @@ -250,36 +249,64 @@ public static SipService getInstance() { } /** - * TODO + * Dynamically registers a new answer bot. */ public void register(AnswerBotSip bot) { + register(toCustomerConfig(bot)); + } + + private CustomerConfig toCustomerConfig(AnswerBotSip bot) { CustomerConfig regConfig = new CustomerConfig(); regConfig.setUser(bot.getUserName()); regConfig.setPasswd(bot.getPasswd()); regConfig.setRealm(bot.getRealm()); regConfig.setRegistrar(new SipURI(bot.getRegistrar())); regConfig.setRoute(new SipURI(bot.getHost()).addLr()); - - RegistrationClient client = new RegistrationClient(_sipProvider, regConfig, this); - RegistrationClient clash = _clients.put(bot.getUserName(), client); + return regConfig; + } + + private void register(CustomerOptions customerConfig) { + Registration client = new Registration(_sipProvider, customerConfig, this); + Registration clash = _clients.put(customerConfig.getUser(), client); if (clash != null) { clash.halt(); } - client.loopRegister(regConfig); + client.loopRegister(customerConfig); } @Override public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, String result) { - LOG.info("Sucessfully registered: " + registration.getUsername()); + String userName = registration.getUsername(); + updateRegistration(userName, true, result); + LOG.info("Sucessfully registered " + userName + ": " + result); } @Override public void onRegistrationFailure(RegistrationClient registration, NameAddress target, NameAddress contact, String result) { - LOG.warn("Failed to register user '" + registration.getUsername() + "': " + result); + String userName = registration.getUsername(); + updateRegistration(userName, false, result); + LOG.warn("Failed to register '" + userName + "': " + result); + } + + private void updateRegistration(String username, boolean registered, String result) { + try (SqlSession session = _dbService.db().openSession()) { + Users users = session.getMapper(Users.class); + + users.updateSipRegistration(username, registered, System.currentTimeMillis(), result); + session.commit(); + } + } + + private CustomerOptions getCustomer(String userName) { + Registration registration = _clients.get(userName); + if (registration == null) { + return null; + } + return registration.getCustomer(); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index b1907714..43cde18c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -128,4 +128,7 @@ public interface Users { @Update("update ANSWERBOT_SIP set ENABLED=false where USERID=#{userId}") void disableAnswerBot(long userId); + @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, LAST_REGISTER=#{lastRegister}, REGISTER_ERROR=#{message} where USERNAME=#{userName}") + void updateSipRegistration(String userName, boolean registered, long lastRegister, String message); + } From df858c5f9dc716016ebd66f79161e292b3d852dc Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 25 Nov 2023 21:48:38 +0100 Subject: [PATCH 055/153] Create format directories in conversion directory, if they do not exist. --- .../phoneblock/answerbot/tools/ConversationInitializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java index bbb4001f..0ccd0728 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/tools/ConversationInitializer.java @@ -41,6 +41,7 @@ public static void main(String[] args) throws CmdLineException, IOException, Uns for (AudioType formatType : AudioType.values()) { String formatName = formatType.dirName(); File outputDir = new File(stateDir, formatName); + outputDir.mkdirs(); if (state.isSilent()) { WavResampler.convertToALaw(input.getAbsolutePath(), new File(outputDir, input.getName()).getAbsolutePath(), formatType.sampleRate()); From d67241fcaad89d7a08a13ed8b3e54a325d27cd8b Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 26 Nov 2023 09:19:44 +0100 Subject: [PATCH 056/153] Detect answer of counterpart, even if PhoneBlock is currently speaking. --- .../phoneblock/answerbot/SpeechDispatcher.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java index bec17e7f..fdf0594b 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java @@ -26,6 +26,7 @@ final class SpeechDispatcher extends InputStream implements SilenceListener { static final org.slf4j.Logger LOG = LoggerFactory.getLogger(SpeechDispatcher.class); private DialogState _state = DialogState.HELLO; + private DialogState _nextState = null; private volatile InputStream _current; @@ -58,7 +59,9 @@ public int read() throws IOException { return result; } - switchState(_state.next()); + switchState(_nextState == null ? _state.next() : _nextState); + _nextState = null; + return _current.read(); } @@ -102,8 +105,10 @@ public void onSilenceEnded(long clock) { switch (_state) { case WAITING_FOR_INTRO: case WAITING_FOR_ANSWER: - switchState(DialogState.LISTENING); break; - default: // Ignore. + switchState(DialogState.LISTENING); + break; + default: + _nextState = DialogState.LISTENING; } } } \ No newline at end of file From f1a2cf202f3dd0b1f0a7fc5e9bc89e4083f2db2c Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 26 Nov 2023 09:22:23 +0100 Subject: [PATCH 057/153] Updated configuration template. --- phoneblock-ab/configs/phoneblock-answerbot.template | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/phoneblock-ab/configs/phoneblock-answerbot.template b/phoneblock-ab/configs/phoneblock-answerbot.template index 0fa7cc67..bb9b9730 100644 --- a/phoneblock-ab/configs/phoneblock-answerbot.template +++ b/phoneblock-ab/configs/phoneblock-answerbot.template @@ -34,23 +34,23 @@ recodings = . conversation=./conversation # The format of the conversation media stord in the directories in ./conversation/* -media=audio 4080 RTP/AVP { 120 PCMA 16000 160 } +media=audio 4080 RTP/AVP { 8 PCMA 8000 160 ; 120 PCMA 16000 320 } # Number of milliseconds silence must be detected before responding. -min-silence-time = 1000 +min-silence-time = 800 # When recording, the number of milliseconds of silence to record before and after the conterpart's speach.") padding-time = 500 # The maximum value in decibel relative to full scale (dbfs) for an audio segment to be classified as silence.") -silence-db = -30 +silence-db = -35 # The minimum number of PhoneBlock votes for a number to be consideres SPAM. min-votes = 4 # Whether to let PhoneBlock accept anonymous calls. It is not recommended to enable this option. Better configure a # separate answering machine in you router to gracefully handle anonymous calls. -accept-anonymous = no +accept-anonymous=no # Prefix of a number that triggers PhoneBlock to respond to the call (for testing). A local number typically starts # with a '*' character. Using this prefix allows to call PhoneBlock locally to verify it is working. From ff0753ba88ffc235d083e7dd69051b07806e330c Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 26 Nov 2023 19:57:22 +0100 Subject: [PATCH 058/153] There is only a single match for media to select. --- .../haumacher/phoneblock/answerbot/DialogueFactory.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index 3943eb4e..bc554e9f 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -65,15 +65,11 @@ public DialogueFactory(DialogOptions config, Map maxSampleRate) { - matchedType = type; - maxSampleRate = sampleRate; - } + matchedType = type; + break; } } From 0c1a9235cfe99a1125fb739c0cfd66871e0497c9 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 26 Nov 2023 19:58:00 +0100 Subject: [PATCH 059/153] Clear state selection if there is silence again. --- .../java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java index fdf0594b..0f293fdc 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/SpeechDispatcher.java @@ -98,6 +98,7 @@ public void onSilenceStarted(long clock) { case LISTENING: switchState(DialogState.ASKING); break; default: // Ignore. } + _nextState = null; } @Override From bd9aca85f4d35daf119adfddcbe4fe779589a653 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 26 Nov 2023 19:58:21 +0100 Subject: [PATCH 060/153] Typo, added more suggestions to conversation. --- phoneblock-ab/conversation/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/conversation/README.md b/phoneblock-ab/conversation/README.md index 55236eb3..3deca429 100644 --- a/phoneblock-ab/conversation/README.md +++ b/phoneblock-ab/conversation/README.md @@ -23,7 +23,9 @@ Kommunikation benötigt werden (je nachdem was für ein Audioformat mit der Gege * Guten Tag, wie kann ich Ihnen behilflich sein? * Guten Tag, was kann ich für Sie tun? * Hallo, wie kann ich Ihnen weiterhelfen? - * Einen schönen guten Tag, wie kann ich Ihnen helfen? + * Einen schönen guten Tag, mit wem spreche ich? + * Einen schönen guten Tag, wer ist da bitte? + * Einen schönen guten Tag, was wünschen Sie? ## Nach der Ansage meldet sich das Call-Center nicht sofort, PhoneBlock fragt nach, wer anruft ("who-is-calling") @@ -40,7 +42,7 @@ Kommunikation benötigt werden (je nachdem was für ein Audioformat mit der Gege * Gibt es ein Problem mit der Verbindung? Haben Sie etwas gesagt? * Ist die Verbindung unterbrochen? Hören Sie mich? * Ich habe Sie nicht verstanden, können Sie das nocheinmal wiederholen? - * Es scheint, als ob es Kommunikationsprobleme gibt. Könnten Sie noch einmal anzurufen? + * Es scheint, als ob es Kommunikationsprobleme gibt. Könnten Sie noch einmal anrufen? * Tut mir leid, aber ich kann nichts hören. Können Sie bitte wiederholen, was Sie gesagt haben? * Gibt ein technisches Problem mit der Verbindung? Ich höre Sie nicht! * Es sieht so aus, als hätten wir ein Verbindungsproblem. Haben Sie etwas gesagt? From e4ec38ddfeef3efe7be9122bf15c83e663f78a85 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 27 Nov 2023 20:02:46 +0100 Subject: [PATCH 061/153] Added parsing label of caller. This allows to detect, whether the caller is known in the local address book of the router. --- .../phoneblock/answerbot/AnswerBot.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 54a43da6..9fd37a21 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -42,6 +42,7 @@ import org.mjsip.sip.address.NameAddress; import org.mjsip.sip.address.SipURI; import org.mjsip.sip.call.ExtendedCall; +import org.mjsip.sip.header.Analyzer; import org.mjsip.sip.header.ToHeader; import org.mjsip.sip.message.SipMessage; import org.mjsip.sip.provider.SipConfig; @@ -134,9 +135,6 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function @Override protected void onInviteReceived(SipMessage msg) { - String from = msg.getFromUser(); - LOG.info("Incomming call from: " + from); - ToHeader toHeader = msg.getToHeader(); if (toHeader == null) { LOG.warn("Missing To: header, ignoring."); @@ -158,13 +156,14 @@ protected void onInviteReceived(SipMessage msg) { return; } - final UserAgent ua = new UserAgent(sip_provider, _portPool, _config.forUser(user), createCallHandler(from)); + final UserAgent ua = new UserAgent(sip_provider, _portPool, _config.forUser(user), createCallHandler(msg)); // since there is still no proper method to init the UA with an incoming call, trick it by using the onNewIncomingCall() callback method new ExtendedCall(sip_provider,msg,ua); } - protected UserAgentListener createCallHandler(String from) { + protected UserAgentListener createCallHandler(SipMessage msg) { + String from = msg.getFromUser(); if (from == null) { // An anonymous call, accept. if (_botConfig.getAcceptAnonymous()) { @@ -174,10 +173,31 @@ protected UserAgentListener createCallHandler(String from) { LOG.info("Not accepting anonymous call."); return rejectHandler(); } - } else if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestPrefix())) { + } + + LOG.info("Incomming call from: " + from); + + if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestPrefix())) { // A local test call, accept. return spamHandler(); } else { + String fromLabel; + { + Analyzer in = new Analyzer(msg.getFromHeader().getValue()).findChar('<').limit(); + in.skipWSPCRLF(); + if (in.lookingAt('"')) { + in.skip(); + in.findChar('"'); + fromLabel = in.stringBefore().trim(); + } else { + fromLabel = in.remaining().trim(); + } + } + + if (!fromLabel.isEmpty() && !fromLabel.equals(from)) { + LOG.info("Incomming labeled call: " + fromLabel); + } + NumberInfo info; try { URL url = new URL("https://phoneblock.net/phoneblock/api/num/" + from + "?format=json"); From 46a6a78517f5d67b807f8ca68bdb02b246a63773 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 27 Nov 2023 20:16:15 +0100 Subject: [PATCH 062/153] Adjusted to mjSip API change. --- .../src/main/java/de/haumacher/phoneblock/ab/SipService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index a39c3dea..69fa9e43 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -278,7 +278,7 @@ private void register(CustomerOptions customerConfig) { @Override public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, - String result) { + int renewTime, String result) { String userName = registration.getUsername(); updateRegistration(userName, true, result); LOG.info("Sucessfully registered " + userName + ": " + result); From 3f43d1e34bd9d67f293d97e13706e4f4406c1b9e Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 27 Nov 2023 22:25:58 +0100 Subject: [PATCH 063/153] Added task updating answerbot registrations upon change. --- .../haumacher/phoneblock/ab/Registration.java | 11 +- .../haumacher/phoneblock/ab/SipService.java | 104 +++++++++++------- .../phoneblock/db/DBAnswerBotSip.java | 8 +- .../de/haumacher/phoneblock/db/Users.java | 14 ++- .../phoneblock/db/settings/AnswerBotSip.java | 92 ++++++++++++++-- .../phoneblock/db/settings/settings.proto | 6 + 6 files changed, 176 insertions(+), 59 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java index 70b6d456..32f0ef7f 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java @@ -15,15 +15,24 @@ public class Registration extends RegistrationClient { private CustomerOptions _customer; + private long _userId; /** * Creates a {@link Registration}. */ - public Registration(SipProvider sipProvider, CustomerOptions customer, RegistrationClientListener sipService) { + public Registration(SipProvider sipProvider, long userId, CustomerOptions customer, RegistrationClientListener sipService) { super(sipProvider, customer, sipService); + _userId = userId; _customer = customer; } + /** + * The user ID of the registered user. + */ + public long getUserId() { + return _userId; + } + /** * The customer configuration of this client. */ diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 69fa9e43..bc73b0f6 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -12,6 +12,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import javax.naming.Context; import javax.naming.InitialContext; @@ -43,7 +44,6 @@ import de.haumacher.phoneblock.answerbot.CustomerConfig; import de.haumacher.phoneblock.answerbot.CustomerOptions; import de.haumacher.phoneblock.db.DB; -import de.haumacher.phoneblock.db.DBAnswerBotDynDns; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; import de.haumacher.phoneblock.db.settings.AnswerBotSip; @@ -64,6 +64,8 @@ public class SipService implements ServletContextListener, RegistrationClientLis private AnswerBot _answerBot; private final ConcurrentHashMap _clients = new ConcurrentHashMap<>(); + + private long _lastRegister; private static SipService _instance; @@ -123,43 +125,59 @@ public void contextInitialized(ServletContextEvent sce) { loadConfig(sipConfig, portConfig, botOptions); - Scheduler scheduler = Scheduler.of(SchedulerService.getInstance().executor()); + Scheduler scheduler = Scheduler.of(_scheduler.executor()); _sipProvider = new SipProvider(sipConfig, scheduler); _portPool = portConfig.createPool(); _answerBot = new AnswerBot(_sipProvider, botOptions, this::getCustomer, _portPool); _instance = this; + _scheduler.executor().scheduleAtFixedRate(this::registerBots, 10, 5 * 60, TimeUnit.SECONDS); + } + + /** + * Registers all answer bots that have been updated since the last run. + */ + private void registerBots() { + long since = _lastRegister; + _lastRegister = System.currentTimeMillis(); + DB db = _dbService.db(); + + List bots; try (SqlSession session = db.openSession()) { Users users = session.getMapper(Users.class); - List bots = users.getEnabledAnswerBots(); - for (AnswerBotSip bot : bots) { - String host = bot.getHost(); + bots = users.getEnabledAnswerBots(since); + } + + List failed = new ArrayList<>(); + for (AnswerBotSip bot : bots) { + String host = bot.getHost(); + if (host == null || host.isEmpty()) { + host = bot.getIpv6(); if (host == null || host.isEmpty()) { - DBAnswerBotDynDns dynDns = users.getDynDnsByUserId(bot.getUserId()); - if (dynDns != null) { - String ipv6 = dynDns.getIpv6(); - if (ipv6 != null && !ipv6.isEmpty()) { - bot.setHost(ipv6); - } else { - String ipv4 = dynDns.getIpv4(); - if (ipv4 != null && !ipv4.isEmpty()) { - bot.setHost(ipv4); - } else { - // Cannot enable. - LOG.warn("Disabling answer bot without host address: " + bot.getUserId() + "/" + bot.getUserName()); - users.disableAnswerBot(bot.getUserId()); - session.commit(); - continue; - } - } + host = bot.getIpv4(); + if (host == null || host.isEmpty()) { + failed.add(bot); } } - - CustomerConfig regConfig = toCustomerConfig(bot); - register(regConfig); + } + + CustomerConfig regConfig = toCustomerConfig(bot); + register(bot.getUserId(), regConfig); + } + + if (!failed.isEmpty()) { + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + for (AnswerBotSip bot : failed) { + LOG.warn("Disabling answer bot without host address: " + bot.getUserId() + "/" + bot.getUserName()); + users.disableAnswerBot(bot.getUserId()); + } + + session.commit(); } } } @@ -223,13 +241,16 @@ private void loadConfig(Object...beans) { @Override public void contextDestroyed(ServletContextEvent sce) { - _answerBot = null; - _portPool = null; - for (RegistrationClient client : _clients.values()) { client.halt(); } _clients.clear(); + + if (_answerBot != null) { + _answerBot.halt(); + } + _answerBot = null; + _portPool = null; if (_sipProvider != null) { _sipProvider.halt(); @@ -252,7 +273,7 @@ public static SipService getInstance() { * Dynamically registers a new answer bot. */ public void register(AnswerBotSip bot) { - register(toCustomerConfig(bot)); + register(bot.getUserId(), toCustomerConfig(bot)); } private CustomerConfig toCustomerConfig(AnswerBotSip bot) { @@ -265,39 +286,42 @@ private CustomerConfig toCustomerConfig(AnswerBotSip bot) { return regConfig; } - private void register(CustomerOptions customerConfig) { - Registration client = new Registration(_sipProvider, customerConfig, this); + private void register(long userId, CustomerOptions customerConfig) { + Registration client = new Registration(_sipProvider, userId, customerConfig, this); Registration clash = _clients.put(customerConfig.getUser(), client); if (clash != null) { clash.halt(); } + LOG.info("Started registration for " + customerConfig.getUser() + "."); client.loopRegister(customerConfig); } @Override public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, int renewTime, String result) { - String userName = registration.getUsername(); - updateRegistration(userName, true, result); - LOG.info("Sucessfully registered " + userName + ": " + result); + long userId = ((Registration) registration).getUserId(); + updateRegistration(userId, result); + LOG.info("Sucessfully registered " + registration.getUsername() + ": " + result); } @Override public void onRegistrationFailure(RegistrationClient registration, NameAddress target, NameAddress contact, String result) { - String userName = registration.getUsername(); - updateRegistration(userName, false, result); - LOG.warn("Failed to register '" + userName + "': " + result); + long userId = ((Registration) registration).getUserId(); + updateRegistration(userId, result); + LOG.warn("Failed to register '" + registration.getUsername() + "': " + result); } - private void updateRegistration(String username, boolean registered, String result) { + private void updateRegistration(long userId, String result) { try (SqlSession session = _dbService.db().openSession()) { Users users = session.getMapper(Users.class); - users.updateSipRegistration(username, registered, System.currentTimeMillis(), result); - session.commit(); + int cnt = users.updateSipRegistration(userId, result); + if (cnt > 0) { + session.commit(); + } } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java index 45174fc5..da8fee5c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java @@ -6,18 +6,18 @@ import de.haumacher.phoneblock.db.settings.AnswerBotSip; /** - * TODO - * - * @author Bernhard Haumacher + * Record representing a single answer bot. */ public class DBAnswerBotSip extends AnswerBotSip { /** * Creates a {@link DBAnswerBotSip}. */ - public DBAnswerBotSip(long userId, String host, String registrar, String realm, String userName, String passwd) { + public DBAnswerBotSip(long userId, String host, String ip4, String ip6, String registrar, String realm, String userName, String passwd) { setUserId(userId); setHost(host); + setIpv4(ip4); + setIpv6(ip6); setRegistrar(registrar); setRealm(realm); setUserName(userName); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 43cde18c..72e44129 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -121,14 +121,16 @@ public interface Users { */ @Insert("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where USERID=#{userId}") void updateDynDny(long userId, String ip4, String ip6, long updated); + + @Select("select s.USERID, s.HOST, d.IP4, s.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + "where s.ENABLED = true and (s.UPDATED > #{since} or d.UPDATED > #{since})") + List getEnabledAnswerBots(long since); - @Select("select USERID, HOST, REGISTRAR, REALM, USERNAME, PASSWD from ANSWERBOT_SIP where ENABLED = true") - List getEnabledAnswerBots(); - - @Update("update ANSWERBOT_SIP set ENABLED=false where USERID=#{userId}") + @Update("update ANSWERBOT_SIP set ENABLED=false, REGISTER_MSG=NULL where USERID=#{userId}") void disableAnswerBot(long userId); - @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, LAST_REGISTER=#{lastRegister}, REGISTER_ERROR=#{message} where USERNAME=#{userName}") - void updateSipRegistration(String userName, boolean registered, long lastRegister, String message); + @Update("update ANSWERBOT_SIP set REGISTER_MSG=#{message} where USERID=#{userId} and not REGISTER_MSG=#{message}") + int updateSipRegistration(long userId, String message); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java index d48f5ab1..ef059936 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java @@ -15,6 +15,12 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { /** @see #getHost() */ public static final String HOST__PROP = "host"; + /** @see #getIpv4() */ + public static final String IPV_4__PROP = "ipv4"; + + /** @see #getIpv6() */ + public static final String IPV_6__PROP = "ipv6"; + /** @see #getRegistrar() */ public static final String REGISTRAR__PROP = "registrar"; @@ -45,32 +51,42 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { /** Identifier for the property {@link #getHost()} in binary format. */ static final int HOST__ID = 4; + /** Identifier for the property {@link #getIpv4()} in binary format. */ + static final int IPV_4__ID = 5; + + /** Identifier for the property {@link #getIpv6()} in binary format. */ + static final int IPV_6__ID = 6; + /** Identifier for the property {@link #getRegistrar()} in binary format. */ - static final int REGISTRAR__ID = 5; + static final int REGISTRAR__ID = 7; /** Identifier for the property {@link #getRealm()} in binary format. */ - static final int REALM__ID = 6; + static final int REALM__ID = 8; /** Identifier for the property {@link #getUserName()} in binary format. */ - static final int USER_NAME__ID = 7; + static final int USER_NAME__ID = 9; /** Identifier for the property {@link #getPasswd()} in binary format. */ - static final int PASSWD__ID = 8; + static final int PASSWD__ID = 10; /** Identifier for the property {@link #isRegistered()} in binary format. */ - static final int REGISTERED__ID = 9; + static final int REGISTERED__ID = 11; /** Identifier for the property {@link #getLastRegister()} in binary format. */ - static final int LAST_REGISTER__ID = 10; + static final int LAST_REGISTER__ID = 12; /** Identifier for the property {@link #getRegisterError()} in binary format. */ - static final int REGISTER_ERROR__ID = 11; + static final int REGISTER_ERROR__ID = 13; /** Identifier for the property {@link #getCallsAccepted()} in binary format. */ - static final int CALLS_ACCEPTED__ID = 12; + static final int CALLS_ACCEPTED__ID = 14; private String _host = ""; + private String _ipv4 = ""; + + private String _ipv6 = ""; + private String _registrar = ""; private String _realm = ""; @@ -122,6 +138,48 @@ protected final void internalSetHost(String value) { _host = value; } + /** + * IPv4 address transmitted during the last DynDNS update. + */ + public final String getIpv4() { + return _ipv4; + } + + /** + * @see #getIpv4() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setIpv4(String value) { + internalSetIpv4(value); + return this; + } + + /** Internal setter for {@link #getIpv4()} without chain call utility. */ + protected final void internalSetIpv4(String value) { + _listener.beforeSet(this, IPV_4__PROP, value); + _ipv4 = value; + } + + /** + * IPv6 address transmitted during the last DynDNS update. + */ + public final String getIpv6() { + return _ipv6; + } + + /** + * @see #getIpv6() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSip setIpv6(String value) { + internalSetIpv6(value); + return this; + } + + /** Internal setter for {@link #getIpv6()} without chain call utility. */ + protected final void internalSetIpv6(String value) { + _listener.beforeSet(this, IPV_6__PROP, value); + _ipv6 = value; + } + /** * The SIP name of the user's box. */ @@ -316,6 +374,8 @@ public String jsonType() { private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( java.util.Arrays.asList( HOST__PROP, + IPV_4__PROP, + IPV_6__PROP, REGISTRAR__PROP, REALM__PROP, USER_NAME__PROP, @@ -334,6 +394,8 @@ public java.util.List properties() { public Object get(String field) { switch (field) { case HOST__PROP: return getHost(); + case IPV_4__PROP: return getIpv4(); + case IPV_6__PROP: return getIpv6(); case REGISTRAR__PROP: return getRegistrar(); case REALM__PROP: return getRealm(); case USER_NAME__PROP: return getUserName(); @@ -350,6 +412,8 @@ public Object get(String field) { public void set(String field, Object value) { switch (field) { case HOST__PROP: internalSetHost((String) value); break; + case IPV_4__PROP: internalSetIpv4((String) value); break; + case IPV_6__PROP: internalSetIpv6((String) value); break; case REGISTRAR__PROP: internalSetRegistrar((String) value); break; case REALM__PROP: internalSetRealm((String) value); break; case USER_NAME__PROP: internalSetUserName((String) value); break; @@ -374,6 +438,10 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. super.writeFields(out); out.name(HOST__PROP); out.value(getHost()); + out.name(IPV_4__PROP); + out.value(getIpv4()); + out.name(IPV_6__PROP); + out.value(getIpv6()); out.name(REGISTRAR__PROP); out.value(getRegistrar()); out.name(REALM__PROP); @@ -396,6 +464,8 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { switch (field) { case HOST__PROP: setHost(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IPV_4__PROP: setIpv4(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IPV_6__PROP: setIpv6(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; case REGISTRAR__PROP: setRegistrar(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; case REALM__PROP: setRealm(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; case USER_NAME__PROP: setUserName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; @@ -418,6 +488,10 @@ protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws jav super.writeFields(out); out.name(HOST__ID); out.value(getHost()); + out.name(IPV_4__ID); + out.value(getIpv4()); + out.name(IPV_6__ID); + out.value(getIpv6()); out.name(REGISTRAR__ID); out.value(getRegistrar()); out.name(REALM__ID); @@ -455,6 +529,8 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip readAnswerBotSip_ protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { switch (field) { case HOST__ID: setHost(in.nextString()); break; + case IPV_4__ID: setIpv4(in.nextString()); break; + case IPV_6__ID: setIpv6(in.nextString()); break; case REGISTRAR__ID: setRegistrar(in.nextString()); break; case REALM__ID: setRealm(in.nextString()); break; case USER_NAME__ID: setUserName(in.nextString()); break; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto index 867abb00..416fb594 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto @@ -61,6 +61,12 @@ message AnswerBotSip extends AnswerBotSetting { /** Static host name configured for the user's box. */ string host; + /** IPv4 address transmitted during the last DynDNS update. */ + string ipv4; + + /** IPv6 address transmitted during the last DynDNS update. */ + string ipv6; + /** The SIP name of the user's box. */ string registrar; From 8055eb35f2babb5bd6cb65a589fc45f1a0e846f4 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 18 Dec 2023 19:40:06 +0100 Subject: [PATCH 064/153] Always anser local (test) calls. --- .../main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 9fd37a21..7f50f87c 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -177,7 +177,7 @@ protected UserAgentListener createCallHandler(SipMessage msg) { LOG.info("Incomming call from: " + from); - if (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestPrefix())) { + if (from.startsWith("*") || (_botConfig.hasTestNumber() && from.startsWith(_botConfig.getTestPrefix()))) { // A local test call, accept. return spamHandler(); } else { From 7edc4ae8b4bdf44d17ccee9474cfe704fdcef4eb Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 18 Dec 2023 19:41:12 +0100 Subject: [PATCH 065/153] Allow to enable/disable answer bots through JMX. --- .../haumacher/phoneblock/ab/SipService.java | 79 ++++++++++++++----- .../haumacher/phoneblock/app/Application.java | 5 +- .../de/haumacher/phoneblock/db/Users.java | 14 +++- .../de/haumacher/phoneblock/jmx/AppState.java | 20 ++++- .../phoneblock/jmx/AppStateMBean.java | 4 + .../phoneblock/jmx/ManagementService.java | 8 +- 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index bc73b0f6..58eef608 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -44,6 +44,7 @@ import de.haumacher.phoneblock.answerbot.CustomerConfig; import de.haumacher.phoneblock.answerbot.CustomerOptions; import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBAnswerBotSip; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; import de.haumacher.phoneblock.db.settings.AnswerBotSip; @@ -153,19 +154,12 @@ private void registerBots() { List failed = new ArrayList<>(); for (AnswerBotSip bot : bots) { - String host = bot.getHost(); + String host = getHost(bot); if (host == null || host.isEmpty()) { - host = bot.getIpv6(); - if (host == null || host.isEmpty()) { - host = bot.getIpv4(); - if (host == null || host.isEmpty()) { - failed.add(bot); - } - } + failed.add(bot); + } else { + register(bot); } - - CustomerConfig regConfig = toCustomerConfig(bot); - register(bot.getUserId(), regConfig); } if (!failed.isEmpty()) { @@ -174,13 +168,24 @@ private void registerBots() { for (AnswerBotSip bot : failed) { LOG.warn("Disabling answer bot without host address: " + bot.getUserId() + "/" + bot.getUserName()); - users.disableAnswerBot(bot.getUserId()); + users.enableAnswerBot(bot.getUserId(), false, _lastRegister); } session.commit(); } } } + + private String getHost(AnswerBotSip bot) { + String host = bot.getHost(); + if (host == null || host.isEmpty()) { + host = bot.getIpv6(); + if (host == null || host.isEmpty()) { + host = bot.getIpv4(); + } + } + return host; + } private void loadConfig(Object...beans) { try { @@ -268,6 +273,34 @@ public void contextDestroyed(ServletContextEvent sce) { public static SipService getInstance() { return _instance; } + + public void enableAnwserBot(String userName, boolean enable) { + try (SqlSession tx = _dbService.db().openSession()) { + Users users = tx.getMapper(Users.class); + + DBAnswerBotSip bot = users.getAnswerBot(userName); + if (bot == null) { + LOG.warn("User with ID '" + userName + "' not found."); + return; + } + + users.enableAnswerBot(bot.getUserId(), enable, System.currentTimeMillis()); + tx.commit(); + + if (enable) { + register(bot); + } else { + Registration registration = _clients.remove(bot.getUserName()); + if (registration == null) { + LOG.info("No active registration for user '" + userName + "'."); + return; + } + + LOG.info("Stopping answer bot '" + userName + "'."); + registration.halt(); + } + } + } /** * Dynamically registers a new answer bot. @@ -282,20 +315,24 @@ private CustomerConfig toCustomerConfig(AnswerBotSip bot) { regConfig.setPasswd(bot.getPasswd()); regConfig.setRealm(bot.getRealm()); regConfig.setRegistrar(new SipURI(bot.getRegistrar())); - regConfig.setRoute(new SipURI(bot.getHost()).addLr()); + regConfig.setRoute(new SipURI(getHost(bot)).addLr()); return regConfig; } private void register(long userId, CustomerOptions customerConfig) { - Registration client = new Registration(_sipProvider, userId, customerConfig, this); - Registration clash = _clients.put(customerConfig.getUser(), client); - - if (clash != null) { - clash.halt(); + try { + Registration client = new Registration(_sipProvider, userId, customerConfig, this); + Registration clash = _clients.put(customerConfig.getUser(), client); + + if (clash != null) { + clash.halt(); + } + + LOG.info("Started registration for " + customerConfig.getUser() + "."); + client.loopRegister(customerConfig); + } catch (Exception ex) { + LOG.error("Registration for " + customerConfig.getUser() + " failed.", ex); } - - LOG.info("Started registration for " + customerConfig.getUser() + "."); - client.loopRegister(customerConfig); } @Override diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java index 37b10472..c71d1ad2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/Application.java @@ -86,6 +86,7 @@ public void contextInitialized(ServletContextEvent sce) { MailServiceStarter mail; ChatGPTService gpt; SecureRandomService rnd; + SipService sip; _services = new ServletContextListener[] { rnd = new SecureRandomService(), scheduler = new SchedulerService(), @@ -98,9 +99,9 @@ public void contextInitialized(ServletContextEvent sce) { metaSearch = new MetaSearchService(scheduler, fetcher, indexer), new CrawlerService(fetcher, metaSearch), gpt = new ChatGPTService(db, scheduler, indexer), - new ManagementService(indexer, db, gpt), + sip = new SipService(scheduler, db), + new ManagementService(indexer, db, gpt, sip), new AddressBookCache(), - new SipService(scheduler, db) }; for (int n = 0, cnt = _services.length; n < cnt; n++) { diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 72e44129..ba685285 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -122,15 +122,21 @@ public interface Users { @Insert("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where USERID=#{userId}") void updateDynDny(long userId, String ip4, String ip6, long updated); - @Select("select s.USERID, s.HOST, d.IP4, s.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + "where s.ENABLED = true and (s.UPDATED > #{since} or d.UPDATED > #{since})") List getEnabledAnswerBots(long since); - @Update("update ANSWERBOT_SIP set ENABLED=false, REGISTER_MSG=NULL where USERID=#{userId}") - void disableAnswerBot(long userId); + @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTER_MSG=NULL where USERID=#{userId}") + void enableAnswerBot(long userId, boolean enabled, long updated); - @Update("update ANSWERBOT_SIP set REGISTER_MSG=#{message} where USERID=#{userId} and not REGISTER_MSG=#{message}") + @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + "where s.USERNAME = #{userName}") + DBAnswerBotSip getAnswerBot(String userName); + + + @Update("update ANSWERBOT_SIP set REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message})") int updateSipRegistration(long userId, String message); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java index 0bd6c6e0..0c33bec2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java @@ -3,6 +3,10 @@ */ package de.haumacher.phoneblock.jmx; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.haumacher.phoneblock.ab.SipService; import de.haumacher.phoneblock.chatgpt.ChatGPTService; import de.haumacher.phoneblock.db.DB; import de.haumacher.phoneblock.index.IndexUpdateService; @@ -11,18 +15,22 @@ * {@link AppStateMBean} implementation. */ public class AppState implements AppStateMBean { + + private static final Logger LOG = LoggerFactory.getLogger(SipService.class); private IndexUpdateService _updater; private DB _db; private ChatGPTService _gpt; + private SipService _sip; /** * Creates a {@link AppState}. */ - public AppState(IndexUpdateService updater, DB db, ChatGPTService gpt) { + public AppState(IndexUpdateService updater, DB db, ChatGPTService gpt, SipService sip) { _updater = updater; _db = db; _gpt = gpt; + _sip = sip; } @Override @@ -82,4 +90,14 @@ public void triggerSummaryCreation(String phone) { } } + @Override + public void triggerAnswerBotRegistration(String userName, boolean enabled) { + try { + _sip.enableAnwserBot(userName, enabled); + } catch (Exception ex) { + LOG.error("Failed to change answer bot state for user '" + userName + "'.", ex); + throw new RuntimeException("Failed to change answer bot state for user '" + userName + "': " + ex.getMessage()); + } + } + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java index f8456702..db084724 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java @@ -70,4 +70,8 @@ public interface AppStateMBean { */ void triggerSummaryCreation(String phone); + /** + * Enables or disables a registered answer bot. + */ + void triggerAnswerBotRegistration(String userName, boolean enabled); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/ManagementService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/ManagementService.java index 12020546..dc759421 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/ManagementService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/ManagementService.java @@ -18,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.haumacher.phoneblock.ab.SipService; import de.haumacher.phoneblock.chatgpt.ChatGPTService; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.index.IndexUpdateService; @@ -39,15 +40,18 @@ public class ManagementService implements ServletContextListener { private DBService _db; private ChatGPTService _gpt; + + private SipService _sip; /** * Creates a {@link ManagementService}. */ - public ManagementService(IndexUpdateService updater, DBService db, ChatGPTService gpt) { + public ManagementService(IndexUpdateService updater, DBService db, ChatGPTService gpt, SipService sip) { super(); _updater = updater; _db = db; _gpt = gpt; + _sip = sip; } @Override @@ -55,7 +59,7 @@ public void contextInitialized(ServletContextEvent servletContextEvent) { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); try { ObjectName name = beanName(servletContextEvent); - mBeanServer.registerMBean(new AppState(_updater, _db.db(), _gpt), name); + mBeanServer.registerMBean(new AppState(_updater, _db.db(), _gpt, _sip), name); _started = true; LOG.info("Registered management bean: " + name); } catch (NotCompliantMBeanException | MalformedObjectNameException | InstanceAlreadyExistsException | MBeanRegistrationException ex) { From 842d45bc51dbb8819e4e0f806c8ba3911f16a45b Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Wed, 20 Dec 2023 22:12:27 +0100 Subject: [PATCH 066/153] Update "registered" flag when registering/unregistering a bot. --- .../main/java/de/haumacher/phoneblock/ab/SipService.java | 8 ++++---- .../src/main/java/de/haumacher/phoneblock/db/Users.java | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 58eef608..8e8b77d1 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -339,7 +339,7 @@ private void register(long userId, CustomerOptions customerConfig) { public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, int renewTime, String result) { long userId = ((Registration) registration).getUserId(); - updateRegistration(userId, result); + updateRegistration(userId, true, result); LOG.info("Sucessfully registered " + registration.getUsername() + ": " + result); } @@ -347,15 +347,15 @@ public void onRegistrationSuccess(RegistrationClient registration, NameAddress t public void onRegistrationFailure(RegistrationClient registration, NameAddress target, NameAddress contact, String result) { long userId = ((Registration) registration).getUserId(); - updateRegistration(userId, result); + updateRegistration(userId, false, result); LOG.warn("Failed to register '" + registration.getUsername() + "': " + result); } - private void updateRegistration(long userId, String result) { + private void updateRegistration(long userId, boolean registered, String result) { try (SqlSession session = _dbService.db().openSession()) { Users users = session.getMapper(Users.class); - int cnt = users.updateSipRegistration(userId, result); + int cnt = users.updateSipRegistration(userId, registered, result); if (cnt > 0) { session.commit(); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index ba685285..0e06750c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -127,7 +127,7 @@ public interface Users { "where s.ENABLED = true and (s.UPDATED > #{since} or d.UPDATED > #{since})") List getEnabledAnswerBots(long since); - @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTER_MSG=NULL where USERID=#{userId}") + @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTERED=false, REGISTER_MSG=NULL where USERID=#{userId}") void enableAnswerBot(long userId, boolean enabled, long updated); @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + @@ -136,7 +136,7 @@ public interface Users { DBAnswerBotSip getAnswerBot(String userName); - @Update("update ANSWERBOT_SIP set REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message})") - int updateSipRegistration(long userId, String message); + @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message} or not REGISTERED=#{registered})") + int updateSipRegistration(long userId, boolean registered, String message); } From 89a11b24d0fb65b6c623a8281b918aa4ff88a4d3 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 28 Dec 2023 12:08:38 +0100 Subject: [PATCH 067/153] Allow to start/stop the SIP service from JMX. --- .../haumacher/phoneblock/ab/SipService.java | 121 ++++++++++-------- .../de/haumacher/phoneblock/jmx/AppState.java | 10 ++ .../phoneblock/jmx/AppStateMBean.java | 10 ++ 3 files changed, 91 insertions(+), 50 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 8e8b77d1..f7746724 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -70,6 +70,10 @@ public class SipService implements ServletContextListener, RegistrationClientLis private static SipService _instance; + private String _fileName; + + private Collection _jndiOptions; + /** * Creates a {@link SipService}. */ @@ -80,6 +84,45 @@ public SipService(SchedulerService scheduler, DBService dbService) { @Override public void contextInitialized(ServletContextEvent sce) { + loadJndiOptions(); + + start(); + } + + private void loadJndiOptions() { + _fileName = null; + _jndiOptions = new ArrayList<>(); + try { + InitialContext initCtx = new InitialContext(); + Context envCtx = (Context) initCtx.lookup("java:comp/env"); + + NamingEnumeration options = envCtx.list("answerbot"); + for (; options.hasMore(); ) { + NameClassPair pair = options.next(); + String name = pair.getName(); + Object lookup = envCtx.lookup("answerbot/" + name); + + if ("configfile".equals(name)) { + _fileName = lookup.toString(); + } else { + _jndiOptions.add(name); + if (lookup != null) { + _jndiOptions.add(lookup.toString()); + } + } + } + } catch (NamingException ex) { + LOG.error("Error loading JDNI configuration: " + ex.getMessage()); + } + } + + public void start() { + if (_answerBot != null) { + LOG.warn("SIP service already active."); + return; + } + LOG.info("Starting SIP service."); + SipConfig sipConfig = new SipConfig(); String hostname = "phoneblock.net"; @@ -188,64 +231,42 @@ private String getHost(AnswerBotSip bot) { } private void loadConfig(Object...beans) { + CmdLineParser parser = new CmdLineParser(null); + for (Object bean : beans) { + new ClassParser().parse(bean, parser); + } + try { - CmdLineParser parser = new CmdLineParser(null); - for (Object bean : beans) { - new ClassParser().parse(bean, parser); - } - - InitialContext initCtx = new InitialContext(); - Context envCtx = (Context) initCtx.lookup("java:comp/env"); - - try { - String fileName = null; - Collection jndiOptions = new ArrayList<>(); - - NamingEnumeration options = envCtx.list("answerbot"); - for (; options.hasMore(); ) { - NameClassPair pair = options.next(); - String name = pair.getName(); - Object lookup = envCtx.lookup("answerbot/" + name); - - if ("configfile".equals(name)) { - fileName = lookup.toString(); - } else { - jndiOptions.add(name); - if (lookup != null) { - jndiOptions.add(lookup.toString()); - } - } + Collection fileOptions; + if (_fileName != null) { + File file = new File(_fileName); + if (!file.exists()) { + LOG.error("Answerbot configuration file does not exits: " + file.getAbsolutePath()); } - - Collection fileOptions; - if (fileName != null) { - File file = new File(fileName); - if (!file.exists()) { - LOG.error("Answerbot configuration file does not exits: " + file.getAbsolutePath()); - } - - ConfigFile configFile = new ConfigFile(file); - fileOptions = configFile.toArguments(); - } else { - fileOptions = Collections.emptyList(); - } - - Collection arguments = new ArrayList<>(fileOptions); - arguments.addAll(jndiOptions); - parser.parseArgument(arguments); - } catch (NamingException ex) { - LOG.error("Error loading JDNI configuration: " + ex.getMessage()); - } catch (CmdLineException ex) { - LOG.error("Invalid answer bot configuration: " + ex.getMessage()); + LOG.info("Loading configuration from: " + _fileName); + ConfigFile configFile = new ConfigFile(file); + fileOptions = configFile.toArguments(); + } else { + fileOptions = Collections.emptyList(); } - } catch (NamingException ex) { - LOG.info("Not using JNDI configuration: " + ex.getMessage()); + + Collection arguments = new ArrayList<>(fileOptions); + arguments.addAll(_jndiOptions); + + parser.parseArgument(arguments); + } catch (CmdLineException ex) { + LOG.error("Invalid answer bot configuration: " + ex.getMessage()); } } - + @Override public void contextDestroyed(ServletContextEvent sce) { + stop(); + } + + public void stop() { + LOG.info("Stopping SIP service."); for (RegistrationClient client : _clients.values()) { client.halt(); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java index 0c33bec2..5e5d65dd 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java @@ -90,6 +90,16 @@ public void triggerSummaryCreation(String phone) { } } + @Override + public void startSipService() { + _sip.start(); + } + + @Override + public void stopSipService() { + _sip.stop(); + } + @Override public void triggerAnswerBotRegistration(String userName, boolean enabled) { try { diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java index db084724..386a791b 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppStateMBean.java @@ -69,7 +69,17 @@ public interface AppStateMBean { *

*/ void triggerSummaryCreation(String phone); + + /** + * Starts the SIP service. + */ + void startSipService(); + /** + * Stops the SIP service. + */ + void stopSipService(); + /** * Enables or disables a registered answer bot. */ From 1be81422c5222cc77c89200122de01f7d52d4ef0 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 28 Dec 2023 19:15:21 +0100 Subject: [PATCH 068/153] Use version of phoneblock-shared artifact. The pom properties of the main artifact get not included, when building a jar with all dependencies. --- .../java/de/haumacher/phoneblock/answerbot/AnswerBot.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index 7f50f87c..d40b92eb 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -77,11 +77,11 @@ public class AnswerBot extends MultipleUAS { static { String version; - try (InputStream in = AnswerBot.class.getResourceAsStream("/META-INF/maven/de.haumacher/phoneblock-ab/pom.properties")) { + try (InputStream in = AnswerBot.class.getResourceAsStream("/META-INF/maven/de.haumacher/phoneblock-shared/pom.properties")) { Properties properties = new Properties(); properties.load(in); version = properties.getProperty("version"); - } catch (IOException ex) { + } catch (Exception ex) { LOG.error("Faild to read version.", ex); version = null; } From 0d16659b927f529a3f55f929616f483784054568 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 28 Dec 2023 19:16:05 +0100 Subject: [PATCH 069/153] Only read supported (configured) media types. --- .../phoneblock/answerbot/AnswerBot.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java index d40b92eb..d34b82f1 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/AnswerBot.java @@ -37,6 +37,7 @@ import org.mjsip.config.OptionParser; import org.mjsip.media.MediaDesc; +import org.mjsip.media.MediaSpec; import org.mjsip.pool.PortConfig; import org.mjsip.pool.PortPool; import org.mjsip.sip.address.NameAddress; @@ -103,12 +104,20 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function _configForUser = configForUser; _botConfig = botOptions; + for (MediaDesc descr : _botConfig.getMediaDescs()) { + LOG.info("Supported media: " + descr); + } + Map>> audioFragmentsByType = new EnumMap<>(AudioType.class); File conversationDir = botOptions.conversationDir(); - for (SpeechType state : SpeechType.values()) { - File stateDir = new File(conversationDir, state.getDirName()); + for (SpeechType type : SpeechType.values()) { + File stateDir = new File(conversationDir, type.getDirName()); for (AudioType formatType : AudioType.values()) { + if (!isSupported(formatType)) { + continue; + } + File typeDir = new File(stateDir, formatType.dirName()); if (!typeDir.isDirectory()) { LOG.warn("Missing conversation directory: " + typeDir.getAbsolutePath()); @@ -118,20 +127,31 @@ public AnswerBot(SipProvider sip_provider, AnswerbotOptions botOptions, Function Map> audioFragments = audioFragmentsByType.computeIfAbsent(formatType, k -> new EnumMap<>(SpeechType.class)); ArrayList files = new ArrayList<>(); - audioFragments.put(state, files); + audioFragments.put(type, files); for (File wav : typeDir.listFiles(f -> f.isFile() && f.getName().endsWith(".wav"))) { files.add(wav); } int cnt = files.size(); if (cnt == 0) { - LOG.warn("Found no audio fragment for dialogue state " + state + " and format '" + formatType + "'."); + LOG.warn("Found no audio fragment for dialogue state " + type + " and format '" + formatType + "'."); } else { - LOG.info("Found " + cnt + " audio fragment for dialogue state " + state + " and format '" + formatType + "'."); + LOG.info("Found " + cnt + " audio fragment for dialogue state " + type + " and format '" + formatType + "'."); } } } _audioFragmentsByType = audioFragmentsByType; } + + private boolean isSupported(AudioType formatType) { + for (MediaDesc descr : _botConfig.getMediaDescs()) { + for (MediaSpec spec : descr.getMediaSpecs()) { + if (formatType.matches(spec)) { + return true; + } + } + } + return false; + } @Override protected void onInviteReceived(SipMessage msg) { From 2b0d67b0fa523690621936b944dbad3b26080e3a Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 28 Dec 2023 19:16:45 +0100 Subject: [PATCH 070/153] Added logging configuration for standalone phoneblock-ab. --- phoneblock-ab/src/main/java/tinylog.properties | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 phoneblock-ab/src/main/java/tinylog.properties diff --git a/phoneblock-ab/src/main/java/tinylog.properties b/phoneblock-ab/src/main/java/tinylog.properties new file mode 100644 index 00000000..97e98c82 --- /dev/null +++ b/phoneblock-ab/src/main/java/tinylog.properties @@ -0,0 +1,9 @@ +writer = console +writer.buffered = false +writer.level = debug +writer.format = [{date}] {level}: [{class}]: {message} +writer.stream = out + +level = info +#level@de.haumacher.phoneblock = debug +#level@org.mjsip = debug From 9a683968d5894e6c898cc330afbe80a34bfc9597 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Fri, 29 Dec 2023 10:24:59 +0100 Subject: [PATCH 071/153] Use more reasonable module names. --- phoneblock-ab/bin/AnswerBot (Fritz!Box).launch | 11 ++++++++++- phoneblock-ab/src/main/java/module-info.java | 4 ++-- phoneblock-shared/src/main/java/module-info.java | 2 +- phoneblock/src/main/java/module-info.java | 6 +++--- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch index 191d1e7f..614b212e 100644 --- a/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch +++ b/phoneblock-ab/bin/AnswerBot (Fritz!Box).launch @@ -11,9 +11,18 @@ + + + + + - + + + + + diff --git a/phoneblock-ab/src/main/java/module-info.java b/phoneblock-ab/src/main/java/module-info.java index 827f81a3..9aa7779b 100644 --- a/phoneblock-ab/src/main/java/module-info.java +++ b/phoneblock-ab/src/main/java/module-info.java @@ -5,8 +5,8 @@ /** * The PhoneBlock answer bot. */ -module phoneblock.ab { - requires phoneblock.shared; +module de.haumacher.phoneblock.ab { + requires de.haumacher.phoneblock.shared; requires transitive org.mjsip.sound; requires transitive org.mjsip.sip; diff --git a/phoneblock-shared/src/main/java/module-info.java b/phoneblock-shared/src/main/java/module-info.java index f4676585..88d375e6 100644 --- a/phoneblock-shared/src/main/java/module-info.java +++ b/phoneblock-shared/src/main/java/module-info.java @@ -5,7 +5,7 @@ /** * The PhoneBlock application. */ -module phoneblock.shared { +module de.haumacher.phoneblock.shared { exports de.haumacher.phoneblock.app.api.model; requires de.haumacher.msgbuf; diff --git a/phoneblock/src/main/java/module-info.java b/phoneblock/src/main/java/module-info.java index bb80e1ad..fbd8d055 100644 --- a/phoneblock/src/main/java/module-info.java +++ b/phoneblock/src/main/java/module-info.java @@ -5,7 +5,7 @@ /** * The PhoneBlock application. */ -module phoneblock { +module de.haumacher.phoneblock { exports de.haumacher.phoneblock.app; exports de.haumacher.phoneblock.app.api; exports de.haumacher.phoneblock.crawl; @@ -24,8 +24,8 @@ exports de.haumacher.phoneblock.db to org.mybatis; exports de.haumacher.phoneblock.db.model to org.mybatis; - requires phoneblock.shared; - requires phoneblock.ab; + requires de.haumacher.phoneblock.shared; + requires de.haumacher.phoneblock.ab; requires org.mjsip.sound; requires org.mjsip.sip; From 4e52c1699209934d41f06bd6d7acc4ec88a2d0ea Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 31 Dec 2023 12:29:27 +0100 Subject: [PATCH 072/153] Applied migration: Use thread pool for RTP streams. --- .../de/haumacher/phoneblock/answerbot/DialogueFactory.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java index bc554e9f..13f4c06f 100644 --- a/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java +++ b/phoneblock-ab/src/main/java/de/haumacher/phoneblock/answerbot/DialogueFactory.java @@ -9,6 +9,7 @@ import java.io.OutputStream; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.UnsupportedAudioFileException; @@ -63,7 +64,7 @@ public DialogueFactory(DialogOptions config, Map Date: Mon, 1 Jan 2024 14:51:12 +0100 Subject: [PATCH 073/153] Initially working starting point for Flutter UI for answerbot config. --- phoneblock/pom.xml | 4 + .../phoneblock/ab/CreateABServlet.java | 67 +++++- .../ab/proto/CreateAnswerbotResponse.java | 102 ++++++++ .../de/haumacher/phoneblock/ab/proto/ab.proto | 11 + .../haumacher/phoneblock/app/LoginFilter.java | 1 + .../phoneblock/app/SettingsServlet.java | 16 +- .../java/de/haumacher/phoneblock/db/DB.java | 12 + .../de/haumacher/phoneblock/db/Users.java | 8 + phoneblock/src/main/webapp/WEB-INF/web.xml | 36 +++ phoneblock/src/main/webapp/settings.jsp | 55 +++++ phoneblock_answerbot_ui/.gitignore | 44 ++++ phoneblock_answerbot_ui/.metadata | 36 +++ phoneblock_answerbot_ui/README.md | 16 ++ phoneblock_answerbot_ui/analysis_options.yaml | 29 +++ phoneblock_answerbot_ui/android/.gitignore | 13 ++ .../android/app/build.gradle | 59 +++++ .../android/app/src/debug/AndroidManifest.xml | 8 + .../android/app/src/main/AndroidManifest.xml | 35 +++ .../phoneblock_answerbot_ui/MainActivity.java | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 18 ++ .../app/src/profile/AndroidManifest.xml | 8 + phoneblock_answerbot_ui/android/build.gradle | 31 +++ .../android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../android/settings.gradle | 11 + phoneblock_answerbot_ui/lib/main.dart | 169 ++++++++++++++ phoneblock_answerbot_ui/lib/proto.dart | 93 ++++++++ phoneblock_answerbot_ui/linux/.gitignore | 1 + phoneblock_answerbot_ui/linux/CMakeLists.txt | 138 +++++++++++ .../linux/flutter/CMakeLists.txt | 88 +++++++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 ++ .../linux/flutter/generated_plugins.cmake | 23 ++ phoneblock_answerbot_ui/linux/main.cc | 6 + .../linux/my_application.cc | 104 +++++++++ .../linux/my_application.h | 18 ++ phoneblock_answerbot_ui/pubspec.lock | 220 ++++++++++++++++++ phoneblock_answerbot_ui/pubspec.yaml | 92 ++++++++ phoneblock_answerbot_ui/test/widget_test.dart | 30 +++ phoneblock_answerbot_ui/web/favicon.png | Bin 0 -> 917 bytes .../web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes phoneblock_answerbot_ui/web/index.html | 62 +++++ phoneblock_answerbot_ui/web/manifest.json | 35 +++ 54 files changed, 1771 insertions(+), 10 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto create mode 100644 phoneblock_answerbot_ui/.gitignore create mode 100644 phoneblock_answerbot_ui/.metadata create mode 100644 phoneblock_answerbot_ui/README.md create mode 100644 phoneblock_answerbot_ui/analysis_options.yaml create mode 100644 phoneblock_answerbot_ui/android/.gitignore create mode 100644 phoneblock_answerbot_ui/android/app/build.gradle create mode 100644 phoneblock_answerbot_ui/android/app/src/debug/AndroidManifest.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/main/AndroidManifest.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/main/java/de/haumacher/phoneblock_answerbot_ui/MainActivity.java create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/drawable/launch_background.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/values-night/styles.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/main/res/values/styles.xml create mode 100644 phoneblock_answerbot_ui/android/app/src/profile/AndroidManifest.xml create mode 100644 phoneblock_answerbot_ui/android/build.gradle create mode 100644 phoneblock_answerbot_ui/android/gradle.properties create mode 100644 phoneblock_answerbot_ui/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 phoneblock_answerbot_ui/android/settings.gradle create mode 100644 phoneblock_answerbot_ui/lib/main.dart create mode 100644 phoneblock_answerbot_ui/lib/proto.dart create mode 100644 phoneblock_answerbot_ui/linux/.gitignore create mode 100644 phoneblock_answerbot_ui/linux/CMakeLists.txt create mode 100644 phoneblock_answerbot_ui/linux/flutter/CMakeLists.txt create mode 100644 phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc create mode 100644 phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.h create mode 100644 phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake create mode 100644 phoneblock_answerbot_ui/linux/main.cc create mode 100644 phoneblock_answerbot_ui/linux/my_application.cc create mode 100644 phoneblock_answerbot_ui/linux/my_application.h create mode 100644 phoneblock_answerbot_ui/pubspec.lock create mode 100644 phoneblock_answerbot_ui/pubspec.yaml create mode 100644 phoneblock_answerbot_ui/test/widget_test.dart create mode 100644 phoneblock_answerbot_ui/web/favicon.png create mode 100644 phoneblock_answerbot_ui/web/icons/Icon-192.png create mode 100644 phoneblock_answerbot_ui/web/icons/Icon-512.png create mode 100644 phoneblock_answerbot_ui/web/icons/Icon-maskable-192.png create mode 100644 phoneblock_answerbot_ui/web/icons/Icon-maskable-512.png create mode 100644 phoneblock_answerbot_ui/web/index.html create mode 100644 phoneblock_answerbot_ui/web/manifest.json diff --git a/phoneblock/pom.xml b/phoneblock/pom.xml index dde1f1ed..6a3e53ac 100644 --- a/phoneblock/pom.xml +++ b/phoneblock/pom.xml @@ -394,6 +394,10 @@ src/main/webapp true + + ../phoneblock_answerbot_ui/build/web + ab +
diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index e6b4204d..dc5007d9 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -11,7 +11,19 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.ibatis.session.SqlSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.haumacher.msgbuf.json.JsonWriter; +import de.haumacher.msgbuf.server.io.WriterAdapter; +import de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse; +import de.haumacher.phoneblock.app.LoginFilter; +import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBService; +import de.haumacher.phoneblock.db.Users; import de.haumacher.phoneblock.db.settings.AnswerBotSip; +import de.haumacher.phoneblock.random.SecureRandomService; /** * Servlet creating an answerbot. @@ -19,17 +31,60 @@ @WebServlet(urlPatterns = CreateABServlet.PATH) public class CreateABServlet extends HttpServlet { + private static final Logger LOG = LoggerFactory.getLogger(CreateABServlet.class); + public static final String PATH = "/ab/create"; @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String username = (String) req.getSession().getAttribute("ab-username"); - String passwd = (String) req.getSession().getAttribute("ab-passwd"); - String hostname = req.getParameter("hostname"); + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + if (login == null) { + LOG.warn("Rejected answerbot creation for unauthorized request."); + sendError(resp, HttpServletResponse.SC_UNAUTHORIZED, "Not logged in."); + return; + } - AnswerBotSip bot = AnswerBotSip.create().setUserName(username).setPasswd(passwd).setHost(hostname); - SipService r = SipService.getInstance(); + DB db = DBService.getInstance(); + String userName = "ab-" + db.createId(16); + String password = db.createPassword(16); + + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + Long userId = users.getUserId(login); + if (userId == null) { + LOG.warn("Rejected answerbot creation for unknown user: " + login); + sendError(resp, HttpServletResponse.SC_UNAUTHORIZED, "User not found."); + return; + } + + long now = System.currentTimeMillis(); + int ok = users.createAnswerBot(userId.longValue(), userName, password, now); + if (ok < 1) { + LOG.warn("Cannot create answerbot for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "Creation failed."); + return; + } + + session.commit(); + } catch (RuntimeException ex) { + LOG.error("Answerbot creation failed for: " + login, ex); + + sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + return; + } - r.register(bot); + resp.setContentType("application/json"); + resp.setCharacterEncoding("utf-8"); + CreateAnswerbotResponse.create().setUserName(userName).writeTo(new JsonWriter(new WriterAdapter(resp.getWriter()))); + + LOG.warn("Created answerbot '" + userName + "' for: " + login); + } + + protected void sendError(HttpServletResponse resp, int code, String message) throws IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.setStatus(code); + resp.getWriter().append(message); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java new file mode 100644 index 00000000..42d413a8 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java @@ -0,0 +1,102 @@ +package de.haumacher.phoneblock.ab.proto; + +public class CreateAnswerbotResponse extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.data.ReflectiveDataObject { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse} instance. + */ + public static de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse create() { + return new de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse} type in JSON format. */ + public static final String CREATE_ANSWERBOT_RESPONSE__TYPE = "CreateAnswerbotResponse"; + + /** @see #getUserName() */ + public static final String USER_NAME__PROP = "userName"; + + private String _userName = ""; + + /** + * Creates a {@link CreateAnswerbotResponse} instance. + * + * @see de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse#create() + */ + protected CreateAnswerbotResponse() { + super(); + } + + public final String getUserName() { + return _userName; + } + + /** + * @see #getUserName() + */ + public de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse setUserName(String value) { + internalSetUserName(value); + return this; + } + + /** Internal setter for {@link #getUserName()} without chain call utility. */ + protected final void internalSetUserName(String value) { + _userName = value; + } + + @Override + public String jsonType() { + return CREATE_ANSWERBOT_RESPONSE__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + USER_NAME__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case USER_NAME__PROP: return getUserName(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case USER_NAME__PROP: internalSetUserName((String) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse readCreateAnswerbotResponse(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse result = new de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(USER_NAME__PROP); + out.value(getUserName()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case USER_NAME__PROP: setUserName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto new file mode 100644 index 00000000..cd97c69e --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto @@ -0,0 +1,11 @@ +package de.haumacher.phoneblock.ab.proto; + +option NoXml; +option NoInterfaces; +option NoListener; +option NoBinary; +option DartLib = "../../../../phoneblock_answerbot_ui/lib/proto.dart"; + +message CreateAnswerbotResponse { + string userName; +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java index 35342f63..3f1c86f3 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java @@ -27,6 +27,7 @@ @WebFilter(urlPatterns = { CardDavServlet.URL_PATTERN, CallReportServlet.URL_PATTERN, + "/ab/*", }) public class LoginFilter implements Filter { diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java index dd0e34b6..dea9f716 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java @@ -21,6 +21,7 @@ import de.haumacher.phoneblock.carddav.resource.AddressBookCache; import de.haumacher.phoneblock.db.BlockList; import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBAnswerBotSip; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; import de.haumacher.phoneblock.db.settings.UserSettings; @@ -40,20 +41,27 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se DB db = DBService.getInstance(); try (SqlSession session = db.openSession()) { Users users = session.getMapper(Users.class); - Long userId = users.getUserId(userName); + Long userIdOpt = users.getUserId(userName); List blacklist; List whitelist; - if (userId == null) { + List answerBots; + if (userIdOpt == null) { blacklist = Collections.emptyList(); whitelist = Collections.emptyList(); + answerBots = Collections.emptyList(); } else { + long userId = userIdOpt.longValue(); + BlockList blocklist = session.getMapper(BlockList.class); - blacklist = blocklist.getPersonalizations(userId.longValue()); - whitelist = blocklist.getWhiteList(userId.longValue()); + blacklist = blocklist.getPersonalizations(userId); + whitelist = blocklist.getWhiteList(userId); + + answerBots = users.getAnswerBots(userId); } req.setAttribute("blacklist", blacklist); req.setAttribute("whitelist", whitelist); + req.setAttribute("answerBots", answerBots); } req.getRequestDispatcher("/settings.jsp").forward(req, resp); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java index 3a84ef83..7ecd575b 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java @@ -247,6 +247,18 @@ public String createPassword(int length) { return buffer.toString(); } + /** + * Creates a unique random (numeric) ID with the given length. + */ + public String createId(int length) { + StringBuilder buffer = new StringBuilder(); + for (int n = 0; n < length; n++) { + char ch = (char) ('0' + _rnd.nextInt(10)); + buffer.append(ch); + } + return buffer.toString(); + } + /** * Sets the user's e-mail address. */ diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 0e06750c..337c6887 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -130,11 +130,19 @@ public interface Users { @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTERED=false, REGISTER_MSG=NULL where USERID=#{userId}") void enableAnswerBot(long userId, boolean enabled, long updated); + @Insert("insert into ANSWERBOT_SIP (USERID, USERNAME, PASSWD, CREATED, UPDATED) values (#{userId}, #{userName}, #{password}, #{now}, #{now})") + int createAnswerBot(long userId, String userName, String password, long now); + @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + "where s.USERNAME = #{userName}") DBAnswerBotSip getAnswerBot(String userName); + @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + "where s.USERID= #{userId}") + List getAnswerBots(long userId); + @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message} or not REGISTERED=#{registered})") int updateSipRegistration(long userId, boolean registered, String message); diff --git a/phoneblock/src/main/webapp/WEB-INF/web.xml b/phoneblock/src/main/webapp/WEB-INF/web.xml index 300fa238..799e09a5 100644 --- a/phoneblock/src/main/webapp/WEB-INF/web.xml +++ b/phoneblock/src/main/webapp/WEB-INF/web.xml @@ -37,6 +37,42 @@ + + CorsFilter + org.apache.catalina.filters.CorsFilter + + cors.allowed.origins + http://localhost:44313 + + + cors.allowed.methods + GET,POST,HEAD,OPTIONS,PUT + + + cors.allowed.headers + Authorization,Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers + + + cors.exposed.headers + Access-Control-Allow-Origin,Access-Control-Allow-Credentials + + + cors.support.credentials + true + + + cors.preflight.maxage + 10 + + + + + + + CorsFilter + /ab/* + + loginFilter /oauth/login diff --git a/phoneblock/src/main/webapp/settings.jsp b/phoneblock/src/main/webapp/settings.jsp index 6b7c5639..fa65d4a1 100644 --- a/phoneblock/src/main/webapp/settings.jsp +++ b/phoneblock/src/main/webapp/settings.jsp @@ -1,4 +1,6 @@ +<%@page import="de.haumacher.phoneblock.ab.CreateABServlet"%> +<%@page import="de.haumacher.phoneblock.db.DBAnswerBotSip"%> <%@page import="de.haumacher.phoneblock.app.SettingsServlet"%> <%@page import="java.util.List"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="true"%> @@ -236,6 +238,59 @@ List whitelist = (List) request.getAttribute("whitelist"); +
+
+

Dein PhoneBlock-Anrufbeantworter

+ +

Der PhoneBlock-Anrufbeantworter prüft, ob ein Anrufer unter Spam-Verdacht steht und nimmt genau dann den Anruf an. Mit dem + PhoneBlock-Anrufbeantworter kannst Du die volle Blocklist nutzen ohne die Einschränkung der maximalen Telefonbuchgröße.

+ +<% List answerBots = (List) request.getAttribute("answerBots"); %> +<% if (answerBots.isEmpty()) { %> +

Du hast noch keinen PhoneBlock-Anrufbeantworter.

+ +
+
+

+ +

+
+
+<% } else {%> + <% for (DBAnswerBotSip answerBot : answerBots) {%> +
+
+

Answerbot

+

<%= JspUtil.quote(answerBot.getUserName())%>

+
+
+ + +
+
+ <% } %> + + <% if (answerBots.size() < 3) {%> +
+
+

+ +

+
+
+ <% } %> +<% } %> +
+
+

Gefahrenzone

diff --git a/phoneblock_answerbot_ui/.gitignore b/phoneblock_answerbot_ui/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/phoneblock_answerbot_ui/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/phoneblock_answerbot_ui/.metadata b/phoneblock_answerbot_ui/.metadata new file mode 100644 index 00000000..08980b4b --- /dev/null +++ b/phoneblock_answerbot_ui/.metadata @@ -0,0 +1,36 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 4b12645012342076800eb701bcdfe18f87da21cf + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + - platform: android + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + - platform: linux + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + - platform: web + create_revision: 4b12645012342076800eb701bcdfe18f87da21cf + base_revision: 4b12645012342076800eb701bcdfe18f87da21cf + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/phoneblock_answerbot_ui/README.md b/phoneblock_answerbot_ui/README.md new file mode 100644 index 00000000..2780e0b5 --- /dev/null +++ b/phoneblock_answerbot_ui/README.md @@ -0,0 +1,16 @@ +# phoneblock_answerbot_ui + +PhoneBlock Answerbot UI + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/phoneblock_answerbot_ui/analysis_options.yaml b/phoneblock_answerbot_ui/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/phoneblock_answerbot_ui/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/phoneblock_answerbot_ui/android/.gitignore b/phoneblock_answerbot_ui/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/phoneblock_answerbot_ui/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/phoneblock_answerbot_ui/android/app/build.gradle b/phoneblock_answerbot_ui/android/app/build.gradle new file mode 100644 index 00000000..81894e3f --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/build.gradle @@ -0,0 +1,59 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "de.haumacher.phoneblock_answerbot_ui" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} diff --git a/phoneblock_answerbot_ui/android/app/src/debug/AndroidManifest.xml b/phoneblock_answerbot_ui/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..b74c1c2a --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/main/AndroidManifest.xml b/phoneblock_answerbot_ui/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9dd0319c --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/main/java/de/haumacher/phoneblock_answerbot_ui/MainActivity.java b/phoneblock_answerbot_ui/android/app/src/main/java/de/haumacher/phoneblock_answerbot_ui/MainActivity.java new file mode 100644 index 00000000..aefacd5e --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/java/de/haumacher/phoneblock_answerbot_ui/MainActivity.java @@ -0,0 +1,6 @@ +package de.haumacher.phoneblock_answerbot_ui; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/drawable-v21/launch_background.xml b/phoneblock_answerbot_ui/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/drawable/launch_background.xml b/phoneblock_answerbot_ui/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/phoneblock_answerbot_ui/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/values-night/styles.xml b/phoneblock_answerbot_ui/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/main/res/values/styles.xml b/phoneblock_answerbot_ui/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/phoneblock_answerbot_ui/android/app/src/profile/AndroidManifest.xml b/phoneblock_answerbot_ui/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..b74c1c2a --- /dev/null +++ b/phoneblock_answerbot_ui/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/phoneblock_answerbot_ui/android/build.gradle b/phoneblock_answerbot_ui/android/build.gradle new file mode 100644 index 00000000..58a8c74b --- /dev/null +++ b/phoneblock_answerbot_ui/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/phoneblock_answerbot_ui/android/gradle.properties b/phoneblock_answerbot_ui/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/phoneblock_answerbot_ui/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/phoneblock_answerbot_ui/android/gradle/wrapper/gradle-wrapper.properties b/phoneblock_answerbot_ui/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3c472b99 --- /dev/null +++ b/phoneblock_answerbot_ui/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/phoneblock_answerbot_ui/android/settings.gradle b/phoneblock_answerbot_ui/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/phoneblock_answerbot_ui/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/phoneblock_answerbot_ui/lib/main.dart b/phoneblock_answerbot_ui/lib/main.dart new file mode 100644 index 00000000..91cd1b34 --- /dev/null +++ b/phoneblock_answerbot_ui/lib/main.dart @@ -0,0 +1,169 @@ +import 'dart:convert'; +import 'dart:html'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:jsontool/jsontool.dart'; +import 'package:phoneblock_answerbot_ui/proto.dart'; + +String basePath = getBasePath(); + +String getBasePath() { + if (kDebugMode) { + return "https://phoneblock.net/pb-test"; + } + + String protocol = window.location.protocol; + String host = window.location.host; + String contextPath = getContextPath(); + String base = "$protocol//$host$contextPath"; + return base; +} + +String getContextPath() { + var path = window.location.pathname; + if (path == null || path.isEmpty) { + return ""; + } + + var sep = path.indexOf("/", 1); + if (sep < 0) { + return path; + } + + return path.substring(0, sep); +} + +// Todo: test only. +var username = "b6c95db0-986e-47b0-af24-e51e56b09ecf"; +var password = "moykCqj2XqEo7XR3FidN"; +var authHeader = 'Basic ${base64Encode(utf8.encode('$username:$password'))}'; +var authHeaders = { + 'authorization': authHeader, +}; + +void main() { + runApp(const MyApp()); +} + +const Color pbColor = Color(0xFF00d1b2); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'PhoneBlock', + theme: ThemeData( + primaryColorLight: pbColor, + appBarTheme: const AppBarTheme( + color: pbColor, + foregroundColor: Colors.white, + ), + primaryColor: pbColor, + floatingActionButtonTheme: const FloatingActionButtonThemeData( + backgroundColor: pbColor, + foregroundColor: Colors.white, + ), + ), + home: const HomeScreen(), + ); + } +} + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("PhoneBlock Anrufbeantworter"), + ), + body: const Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text('Du hast noch keinen Anrufbeantworter, klicke den Plus-Knopf unten, um einen PhoneBlock-Anrufbeantworter anzulegen.', + style: TextStyle(fontSize: 20), + textAlign: TextAlign.center, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => _createAnswerBot(context), + tooltip: 'Anrufbeantworter anlegen', + child: const Icon(Icons.add), + ), + ); + } + + void _createAnswerBot(BuildContext context) async { + var response = await http.post(Uri.parse('$basePath/ab/create'), + encoding: const Utf8Codec(), + headers: { + "Content-Type": "application/json", + 'Authorization': authHeader, + }, + body: "{}", + ); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response); + } + + var creation = CreateAnswerbotResponse.read(JsonReader.fromString(response.body)); + Navigator.push(context, MaterialPageRoute(builder: (context) => _setupAnswerBot(context, creation))); + } + + Widget _setupAnswerBot(BuildContext context, CreateAnswerbotResponse creation) { + return Scaffold( + appBar: AppBar( + title: Text("Anrufbeantworter einrichten"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(creation.userName), + ], + ), + ), + ); + } +} + +Future showErrorDialog(BuildContext context, http.Response response) { + return showDialog( + context: context, + barrierDismissible: false, // user must tap button! + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Anlage fehlgeschlagen'), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text('Der Anrufbeantworter konnte nicht angelegt werden (Fehler ${response.statusCode}).'), + Text(response.body), + ], + ), + ), + actions: [ + TextButton( + child: const Text('Ok'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); +} diff --git a/phoneblock_answerbot_ui/lib/proto.dart b/phoneblock_answerbot_ui/lib/proto.dart new file mode 100644 index 00000000..d0a83fa7 --- /dev/null +++ b/phoneblock_answerbot_ui/lib/proto.dart @@ -0,0 +1,93 @@ +import 'package:jsontool/jsontool.dart'; + +/// Common functionality for JSON generation and parsing. +abstract class _JsonObject { + @override + String toString() { + var buffer = StringBuffer(); + writeTo(jsonStringWriter(buffer)); + return buffer.toString(); + } + + /// The ID to announce the type of the object. + String _jsonType(); + + /// Reads the object contents (after the type information). + void _readContent(JsonReader json) { + json.expectObject(); + while (json.hasNextKey()) { + var key = json.nextKey(); + _readProperty(key!, json); + } + } + + /// Reads the value of the property with the given name. + void _readProperty(String key, JsonReader json) { + json.skipAnyValue(); + } + + /// Writes this object to the given writer (including type information). + void writeTo(JsonSink json) { + json.startArray(); + json.addString(_jsonType()); + writeContent(json); + json.endArray(); + } + + /// Writes the contents of this object to the given writer (excluding type information). + void writeContent(JsonSink json) { + json.startObject(); + _writeProperties(json); + json.endObject(); + } + + /// Writes all key/value pairs of this object. + void _writeProperties(JsonSink json) { + // No properties. + } +} + +class CreateAnswerbotResponse extends _JsonObject { + String userName; + + /// Creates a CreateAnswerbotResponse. + CreateAnswerbotResponse({ + this.userName = "", + }); + + /// Parses a CreateAnswerbotResponse from a string source. + static CreateAnswerbotResponse? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a CreateAnswerbotResponse instance from the given reader. + static CreateAnswerbotResponse read(JsonReader json) { + CreateAnswerbotResponse result = CreateAnswerbotResponse(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "CreateAnswerbotResponse"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "userName": { + userName = json.expectString(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("userName"); + json.addString(userName); + } + +} + diff --git a/phoneblock_answerbot_ui/linux/.gitignore b/phoneblock_answerbot_ui/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/phoneblock_answerbot_ui/linux/CMakeLists.txt b/phoneblock_answerbot_ui/linux/CMakeLists.txt new file mode 100644 index 00000000..168f1996 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "phoneblock_answerbot_ui") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "de.haumacher.phoneblock_answerbot_ui") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/phoneblock_answerbot_ui/linux/flutter/CMakeLists.txt b/phoneblock_answerbot_ui/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..e71a16d2 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.h b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake b/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2e1de87a --- /dev/null +++ b/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/phoneblock_answerbot_ui/linux/main.cc b/phoneblock_answerbot_ui/linux/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/phoneblock_answerbot_ui/linux/my_application.cc b/phoneblock_answerbot_ui/linux/my_application.cc new file mode 100644 index 00000000..14944c26 --- /dev/null +++ b/phoneblock_answerbot_ui/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "phoneblock_answerbot_ui"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "phoneblock_answerbot_ui"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/phoneblock_answerbot_ui/linux/my_application.h b/phoneblock_answerbot_ui/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/phoneblock_answerbot_ui/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/phoneblock_answerbot_ui/pubspec.lock b/phoneblock_answerbot_ui/pubspec.lock new file mode 100644 index 00000000..528442a8 --- /dev/null +++ b/phoneblock_answerbot_ui/pubspec.lock @@ -0,0 +1,220 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + url: "https://pub.dev" + source: hosted + version: "3.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + jsontool: + dependency: "direct main" + description: + name: jsontool + sha256: "7741060cd6615ae82b07d70ebeca6fa6402b1c5cc1e801054911918300a780ac" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" +sdks: + dart: ">=3.2.0 <4.0.0" diff --git a/phoneblock_answerbot_ui/pubspec.yaml b/phoneblock_answerbot_ui/pubspec.yaml new file mode 100644 index 00000000..8da2e2a7 --- /dev/null +++ b/phoneblock_answerbot_ui/pubspec.yaml @@ -0,0 +1,92 @@ +name: phoneblock_answerbot_ui +description: PhoneBlock Answerbot UI +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.19.6 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + http: ^1.1.2 + jsontool: ^1.2.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^3.0.1 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/phoneblock_answerbot_ui/test/widget_test.dart b/phoneblock_answerbot_ui/test/widget_test.dart new file mode 100644 index 00000000..314ec1d5 --- /dev/null +++ b/phoneblock_answerbot_ui/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:phoneblock_answerbot_ui/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/phoneblock_answerbot_ui/web/favicon.png b/phoneblock_answerbot_ui/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/web/icons/Icon-192.png b/phoneblock_answerbot_ui/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/web/icons/Icon-512.png b/phoneblock_answerbot_ui/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/web/icons/Icon-maskable-192.png b/phoneblock_answerbot_ui/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/web/icons/Icon-maskable-512.png b/phoneblock_answerbot_ui/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/phoneblock_answerbot_ui/web/index.html b/phoneblock_answerbot_ui/web/index.html new file mode 100644 index 00000000..9b88bb40 --- /dev/null +++ b/phoneblock_answerbot_ui/web/index.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + PhoneBlock Anrufbeantworter + + + + + + + + + + diff --git a/phoneblock_answerbot_ui/web/manifest.json b/phoneblock_answerbot_ui/web/manifest.json new file mode 100644 index 00000000..87bdd7c6 --- /dev/null +++ b/phoneblock_answerbot_ui/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "phoneblock_answerbot_ui", + "short_name": "phoneblock_answerbot_ui", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "PhoneBlock Answerbot UI", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} From a4b96c9b2702bdab9c2de761a75ff58e1eeae761 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 1 Jan 2024 21:53:08 +0100 Subject: [PATCH 074/153] Added listing answer bots and added starting point for bot config. --- .../haumacher/phoneblock/ab/ABApiServlet.java | 17 + .../phoneblock/ab/CreateABServlet.java | 8 +- .../phoneblock/ab/DBAnswerbotInfo.java | 32 + .../phoneblock/ab/ListABServlet.java | 70 +++ .../phoneblock/ab/proto/AnswerbotInfo.java | 565 ++++++++++++++++++ .../ab/proto/ListAnswerbotResponse.java | 141 +++++ .../de/haumacher/phoneblock/ab/proto/ab.proto | 59 ++ .../phoneblock/app/SettingsServlet.java | 3 +- .../de/haumacher/phoneblock/db/Users.java | 8 +- phoneblock/src/main/webapp/settings.jsp | 5 +- phoneblock_answerbot_ui/lib/main.dart | 250 +++++++- phoneblock_answerbot_ui/lib/proto.dart | 263 ++++++++ .../flutter/generated_plugin_registrant.cc | 4 + .../linux/flutter/generated_plugins.cmake | 1 + phoneblock_answerbot_ui/pubspec.lock | 78 +++ phoneblock_answerbot_ui/pubspec.yaml | 1 + 16 files changed, 1470 insertions(+), 35 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/ABApiServlet.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/ListABServlet.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ListAnswerbotResponse.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ABApiServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ABApiServlet.java new file mode 100644 index 00000000..3daf4134 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ABApiServlet.java @@ -0,0 +1,17 @@ +package de.haumacher.phoneblock.ab; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletResponse; + +public abstract class ABApiServlet extends HttpServlet { + + protected void sendError(HttpServletResponse resp, int code, String message) throws IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.setStatus(code); + resp.getWriter().append(message); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index dc5007d9..16cfef30 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -29,7 +29,7 @@ * Servlet creating an answerbot. */ @WebServlet(urlPatterns = CreateABServlet.PATH) -public class CreateABServlet extends HttpServlet { +public class CreateABServlet extends ABApiServlet { private static final Logger LOG = LoggerFactory.getLogger(CreateABServlet.class); @@ -81,10 +81,4 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S LOG.warn("Created answerbot '" + userName + "' for: " + login); } - protected void sendError(HttpServletResponse resp, int code, String message) throws IOException { - resp.setContentType("text/plain"); - resp.setCharacterEncoding("utf-8"); - resp.setStatus(code); - resp.getWriter().append(message); - } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java new file mode 100644 index 00000000..6e030770 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java @@ -0,0 +1,32 @@ +package de.haumacher.phoneblock.ab; + +import de.haumacher.phoneblock.ab.proto.AnswerbotInfo; + +public class DBAnswerbotInfo extends AnswerbotInfo { + + public DBAnswerbotInfo( + long id, + boolean enabled, String registrar, String host, String ip4, String ip6, String realm, + boolean registered, String msg, + int callsAccepted, + String userName, String password, String dyndnsUser, String dyndnsPassword + ) { + setId(id); + setEnabled(enabled); + setRegistrar(registrar); + setHost(host); + setIp4(ip4); + setIp6(ip6); + setRealm(realm); + setRegistered(registered); + setRegisterMsg(msg); + setCallsAccepted(callsAccepted); + + setUserName(userName); + setPassword(password); + + setDyndnsUser(dyndnsUser); + setDyndnsPassword(dyndnsPassword); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ListABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ListABServlet.java new file mode 100644 index 00000000..23eae906 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/ListABServlet.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.ab; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.ibatis.session.SqlSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.haumacher.msgbuf.json.JsonWriter; +import de.haumacher.msgbuf.server.io.WriterAdapter; +import de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse; +import de.haumacher.phoneblock.app.LoginFilter; +import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBService; +import de.haumacher.phoneblock.db.Users; + +/** + * Servlet listing all answerbots of a user. + */ +@WebServlet(urlPatterns = ListABServlet.PATH) +public class ListABServlet extends ABApiServlet { + + private static final Logger LOG = LoggerFactory.getLogger(ListABServlet.class); + + public static final String PATH = "/ab/list"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + if (login == null) { + LOG.warn("Not logged in."); + sendError(resp, HttpServletResponse.SC_UNAUTHORIZED, "Not logged in."); + return; + } + + List bots; + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + Long userId = users.getUserId(login); + if (userId == null) { + LOG.warn("User not found: " + login); + sendError(resp, HttpServletResponse.SC_UNAUTHORIZED, "User not found."); + return; + } + + bots = users.getAnswerBots(userId.longValue()); + } catch (RuntimeException ex) { + LOG.error("DB looku failed for: " + login, ex); + + sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); + return; + } + + resp.setContentType("application/json"); + resp.setCharacterEncoding("utf-8"); + ListAnswerbotResponse.create().setBots(bots).writeTo(new JsonWriter(new WriterAdapter(resp.getWriter()))); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java new file mode 100644 index 00000000..748dce28 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java @@ -0,0 +1,565 @@ +package de.haumacher.phoneblock.ab.proto; + +/** + * Information of a single answer bot. + */ +public class AnswerbotInfo extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.data.ReflectiveDataObject { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.AnswerbotInfo} instance. + */ + public static de.haumacher.phoneblock.ab.proto.AnswerbotInfo create() { + return new de.haumacher.phoneblock.ab.proto.AnswerbotInfo(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.AnswerbotInfo} type in JSON format. */ + public static final String ANSWERBOT_INFO__TYPE = "AnswerbotInfo"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + /** @see #isEnabled() */ + public static final String ENABLED__PROP = "enabled"; + + /** @see #isRegistered() */ + public static final String REGISTERED__PROP = "registered"; + + /** @see #getRegisterMsg() */ + public static final String REGISTER_MSG__PROP = "registerMsg"; + + /** @see #getCallsAccepted() */ + public static final String CALLS_ACCEPTED__PROP = "callsAccepted"; + + /** @see #getRegistrar() */ + public static final String REGISTRAR__PROP = "registrar"; + + /** @see #getRealm() */ + public static final String REALM__PROP = "realm"; + + /** @see #getUserName() */ + public static final String USER_NAME__PROP = "userName"; + + /** @see #getPassword() */ + public static final String PASSWORD__PROP = "password"; + + /** @see #getHost() */ + public static final String HOST__PROP = "host"; + + /** @see #getIp4() */ + public static final String IP_4__PROP = "ip4"; + + /** @see #getIp6() */ + public static final String IP_6__PROP = "ip6"; + + /** @see #getDyndnsUser() */ + public static final String DYNDNS_USER__PROP = "dyndnsUser"; + + /** @see #getDyndnsPassword() */ + public static final String DYNDNS_PASSWORD__PROP = "dyndnsPassword"; + + private long _id = 0L; + + private boolean _enabled = false; + + private boolean _registered = false; + + private String _registerMsg = null; + + private int _callsAccepted = 0; + + private String _registrar = ""; + + private String _realm = ""; + + private String _userName = ""; + + private String _password = ""; + + private String _host = null; + + private String _ip4 = null; + + private String _ip6 = null; + + private String _dyndnsUser = null; + + private String _dyndnsPassword = null; + + /** + * Creates a {@link AnswerbotInfo} instance. + * + * @see de.haumacher.phoneblock.ab.proto.AnswerbotInfo#create() + */ + protected AnswerbotInfo() { + super(); + } + + /** + * The primary key identifier of this bot. + */ + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + /** + * Whether the bot is enabled (registration is active). + */ + public final boolean isEnabled() { + return _enabled; + } + + /** + * @see #isEnabled() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setEnabled(boolean value) { + internalSetEnabled(value); + return this; + } + + /** Internal setter for {@link #isEnabled()} without chain call utility. */ + protected final void internalSetEnabled(boolean value) { + _enabled = value; + } + + /** + * Whether the bot has sucessfully registered (can accept calls). + */ + public final boolean isRegistered() { + return _registered; + } + + /** + * @see #isRegistered() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setRegistered(boolean value) { + internalSetRegistered(value); + return this; + } + + /** Internal setter for {@link #isRegistered()} without chain call utility. */ + protected final void internalSetRegistered(boolean value) { + _registered = value; + } + + /** + * The message received during the last registration attempt. + */ + public final String getRegisterMsg() { + return _registerMsg; + } + + /** + * @see #getRegisterMsg() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setRegisterMsg(String value) { + internalSetRegisterMsg(value); + return this; + } + + /** Internal setter for {@link #getRegisterMsg()} without chain call utility. */ + protected final void internalSetRegisterMsg(String value) { + _registerMsg = value; + } + + /** + * Checks, whether {@link #getRegisterMsg()} has a value. + */ + public final boolean hasRegisterMsg() { + return _registerMsg != null; + } + + /** + * The total number of calls accepted by this bot so far. + */ + public final int getCallsAccepted() { + return _callsAccepted; + } + + /** + * @see #getCallsAccepted() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setCallsAccepted(int value) { + internalSetCallsAccepted(value); + return this; + } + + /** Internal setter for {@link #getCallsAccepted()} without chain call utility. */ + protected final void internalSetCallsAccepted(int value) { + _callsAccepted = value; + } + + /** + * The name of the box to register at. + */ + public final String getRegistrar() { + return _registrar; + } + + /** + * @see #getRegistrar() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setRegistrar(String value) { + internalSetRegistrar(value); + return this; + } + + /** Internal setter for {@link #getRegistrar()} without chain call utility. */ + protected final void internalSetRegistrar(String value) { + _registrar = value; + } + + /** + * The authentication realm expected for registration. + */ + public final String getRealm() { + return _realm; + } + + /** + * @see #getRealm() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setRealm(String value) { + internalSetRealm(value); + return this; + } + + /** Internal setter for {@link #getRealm()} without chain call utility. */ + protected final void internalSetRealm(String value) { + _realm = value; + } + + /** + * The user name used for SIP registration. + */ + public final String getUserName() { + return _userName; + } + + /** + * @see #getUserName() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setUserName(String value) { + internalSetUserName(value); + return this; + } + + /** Internal setter for {@link #getUserName()} without chain call utility. */ + protected final void internalSetUserName(String value) { + _userName = value; + } + + /** + * The password for SIP registration. + */ + public final String getPassword() { + return _password; + } + + /** + * @see #getPassword() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setPassword(String value) { + internalSetPassword(value); + return this; + } + + /** Internal setter for {@link #getPassword()} without chain call utility. */ + protected final void internalSetPassword(String value) { + _password = value; + } + + /** + * The host name of the box to register at (only set, if a third-party DynDNS service is used. + */ + public final String getHost() { + return _host; + } + + /** + * @see #getHost() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setHost(String value) { + internalSetHost(value); + return this; + } + + /** Internal setter for {@link #getHost()} without chain call utility. */ + protected final void internalSetHost(String value) { + _host = value; + } + + /** + * Checks, whether {@link #getHost()} has a value. + */ + public final boolean hasHost() { + return _host != null; + } + + /** + * The IPv4 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). + */ + public final String getIp4() { + return _ip4; + } + + /** + * @see #getIp4() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setIp4(String value) { + internalSetIp4(value); + return this; + } + + /** Internal setter for {@link #getIp4()} without chain call utility. */ + protected final void internalSetIp4(String value) { + _ip4 = value; + } + + /** + * Checks, whether {@link #getIp4()} has a value. + */ + public final boolean hasIp4() { + return _ip4 != null; + } + + /** + * The IPv6 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). + */ + public final String getIp6() { + return _ip6; + } + + /** + * @see #getIp6() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setIp6(String value) { + internalSetIp6(value); + return this; + } + + /** Internal setter for {@link #getIp6()} without chain call utility. */ + protected final void internalSetIp6(String value) { + _ip6 = value; + } + + /** + * Checks, whether {@link #getIp6()} has a value. + */ + public final boolean hasIp6() { + return _ip6 != null; + } + + /** + * The user name for DynDNS registration of the box (only filled, if internal DynDNS is set up). + */ + public final String getDyndnsUser() { + return _dyndnsUser; + } + + /** + * @see #getDyndnsUser() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setDyndnsUser(String value) { + internalSetDyndnsUser(value); + return this; + } + + /** Internal setter for {@link #getDyndnsUser()} without chain call utility. */ + protected final void internalSetDyndnsUser(String value) { + _dyndnsUser = value; + } + + /** + * Checks, whether {@link #getDyndnsUser()} has a value. + */ + public final boolean hasDyndnsUser() { + return _dyndnsUser != null; + } + + /** + * The password for DynDNS registration of the box. + */ + public final String getDyndnsPassword() { + return _dyndnsPassword; + } + + /** + * @see #getDyndnsPassword() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setDyndnsPassword(String value) { + internalSetDyndnsPassword(value); + return this; + } + + /** Internal setter for {@link #getDyndnsPassword()} without chain call utility. */ + protected final void internalSetDyndnsPassword(String value) { + _dyndnsPassword = value; + } + + /** + * Checks, whether {@link #getDyndnsPassword()} has a value. + */ + public final boolean hasDyndnsPassword() { + return _dyndnsPassword != null; + } + + @Override + public String jsonType() { + return ANSWERBOT_INFO__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP, + ENABLED__PROP, + REGISTERED__PROP, + REGISTER_MSG__PROP, + CALLS_ACCEPTED__PROP, + REGISTRAR__PROP, + REALM__PROP, + USER_NAME__PROP, + PASSWORD__PROP, + HOST__PROP, + IP_4__PROP, + IP_6__PROP, + DYNDNS_USER__PROP, + DYNDNS_PASSWORD__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + case ENABLED__PROP: return isEnabled(); + case REGISTERED__PROP: return isRegistered(); + case REGISTER_MSG__PROP: return getRegisterMsg(); + case CALLS_ACCEPTED__PROP: return getCallsAccepted(); + case REGISTRAR__PROP: return getRegistrar(); + case REALM__PROP: return getRealm(); + case USER_NAME__PROP: return getUserName(); + case PASSWORD__PROP: return getPassword(); + case HOST__PROP: return getHost(); + case IP_4__PROP: return getIp4(); + case IP_6__PROP: return getIp6(); + case DYNDNS_USER__PROP: return getDyndnsUser(); + case DYNDNS_PASSWORD__PROP: return getDyndnsPassword(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + case ENABLED__PROP: internalSetEnabled((boolean) value); break; + case REGISTERED__PROP: internalSetRegistered((boolean) value); break; + case REGISTER_MSG__PROP: internalSetRegisterMsg((String) value); break; + case CALLS_ACCEPTED__PROP: internalSetCallsAccepted((int) value); break; + case REGISTRAR__PROP: internalSetRegistrar((String) value); break; + case REALM__PROP: internalSetRealm((String) value); break; + case USER_NAME__PROP: internalSetUserName((String) value); break; + case PASSWORD__PROP: internalSetPassword((String) value); break; + case HOST__PROP: internalSetHost((String) value); break; + case IP_4__PROP: internalSetIp4((String) value); break; + case IP_6__PROP: internalSetIp6((String) value); break; + case DYNDNS_USER__PROP: internalSetDyndnsUser((String) value); break; + case DYNDNS_PASSWORD__PROP: internalSetDyndnsPassword((String) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.AnswerbotInfo readAnswerbotInfo(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.AnswerbotInfo result = new de.haumacher.phoneblock.ab.proto.AnswerbotInfo(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + out.name(ENABLED__PROP); + out.value(isEnabled()); + out.name(REGISTERED__PROP); + out.value(isRegistered()); + if (hasRegisterMsg()) { + out.name(REGISTER_MSG__PROP); + out.value(getRegisterMsg()); + } + out.name(CALLS_ACCEPTED__PROP); + out.value(getCallsAccepted()); + out.name(REGISTRAR__PROP); + out.value(getRegistrar()); + out.name(REALM__PROP); + out.value(getRealm()); + out.name(USER_NAME__PROP); + out.value(getUserName()); + out.name(PASSWORD__PROP); + out.value(getPassword()); + if (hasHost()) { + out.name(HOST__PROP); + out.value(getHost()); + } + if (hasIp4()) { + out.name(IP_4__PROP); + out.value(getIp4()); + } + if (hasIp6()) { + out.name(IP_6__PROP); + out.value(getIp6()); + } + if (hasDyndnsUser()) { + out.name(DYNDNS_USER__PROP); + out.value(getDyndnsUser()); + } + if (hasDyndnsPassword()) { + out.name(DYNDNS_PASSWORD__PROP); + out.value(getDyndnsPassword()); + } + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + case ENABLED__PROP: setEnabled(in.nextBoolean()); break; + case REGISTERED__PROP: setRegistered(in.nextBoolean()); break; + case REGISTER_MSG__PROP: setRegisterMsg(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case CALLS_ACCEPTED__PROP: setCallsAccepted(in.nextInt()); break; + case REGISTRAR__PROP: setRegistrar(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case REALM__PROP: setRealm(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case USER_NAME__PROP: setUserName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case PASSWORD__PROP: setPassword(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case HOST__PROP: setHost(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IP_4__PROP: setIp4(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case IP_6__PROP: setIp6(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case DYNDNS_USER__PROP: setDyndnsUser(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case DYNDNS_PASSWORD__PROP: setDyndnsPassword(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ListAnswerbotResponse.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ListAnswerbotResponse.java new file mode 100644 index 00000000..7c559f1a --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ListAnswerbotResponse.java @@ -0,0 +1,141 @@ +package de.haumacher.phoneblock.ab.proto; + +/** + * Result of the {@link de.haumacher.phoneblock.ab.ListABServlet}. + */ +public class ListAnswerbotResponse extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.data.ReflectiveDataObject { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse} instance. + */ + public static de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse create() { + return new de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse} type in JSON format. */ + public static final String LIST_ANSWERBOT_RESPONSE__TYPE = "ListAnswerbotResponse"; + + /** @see #getBots() */ + public static final String BOTS__PROP = "bots"; + + private final java.util.List _bots = new java.util.ArrayList<>(); + + /** + * Creates a {@link ListAnswerbotResponse} instance. + * + * @see de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse#create() + */ + protected ListAnswerbotResponse() { + super(); + } + + /** + * Infos for all answer bots of the current user. + */ + public final java.util.List getBots() { + return _bots; + } + + /** + * @see #getBots() + */ + public de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse setBots(java.util.List value) { + internalSetBots(value); + return this; + } + + /** Internal setter for {@link #getBots()} without chain call utility. */ + protected final void internalSetBots(java.util.List value) { + if (value == null) throw new IllegalArgumentException("Property 'bots' cannot be null."); + _bots.clear(); + _bots.addAll(value); + } + + /** + * Adds a value to the {@link #getBots()} list. + */ + public de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse addBot(de.haumacher.phoneblock.ab.proto.AnswerbotInfo value) { + internalAddBot(value); + return this; + } + + /** Implementation of {@link #addBot(de.haumacher.phoneblock.ab.proto.AnswerbotInfo)} without chain call utility. */ + protected final void internalAddBot(de.haumacher.phoneblock.ab.proto.AnswerbotInfo value) { + _bots.add(value); + } + + /** + * Removes a value from the {@link #getBots()} list. + */ + public final void removeBot(de.haumacher.phoneblock.ab.proto.AnswerbotInfo value) { + _bots.remove(value); + } + + @Override + public String jsonType() { + return LIST_ANSWERBOT_RESPONSE__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + BOTS__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case BOTS__PROP: return getBots(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case BOTS__PROP: internalSetBots(de.haumacher.msgbuf.util.Conversions.asList(de.haumacher.phoneblock.ab.proto.AnswerbotInfo.class, value)); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse readListAnswerbotResponse(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse result = new de.haumacher.phoneblock.ab.proto.ListAnswerbotResponse(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(BOTS__PROP); + out.beginArray(); + for (de.haumacher.phoneblock.ab.proto.AnswerbotInfo x : getBots()) { + x.writeTo(out); + } + out.endArray(); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case BOTS__PROP: { + in.beginArray(); + while (in.hasNext()) { + addBot(de.haumacher.phoneblock.ab.proto.AnswerbotInfo.readAnswerbotInfo(in)); + } + in.endArray(); + } + break; + default: super.readField(in, field); + } + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto index cd97c69e..0c4f71b7 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto @@ -9,3 +9,62 @@ option DartLib = "../../../../phoneblock_answerbot_ui/lib/proto.dart"; message CreateAnswerbotResponse { string userName; } + +/** Information of a single answer bot. */ +message AnswerbotInfo { + /** The primary key identifier of this bot. */ + long id; + + /** Whether the bot is enabled (registration is active). */ + bool enabled; + + /** Whether the bot has sucessfully registered (can accept calls). */ + bool registered; + + /** The message received during the last registration attempt. */ + @Nullable + string registerMsg; + + /** The total number of calls accepted by this bot so far. */ + int callsAccepted; + + /** The name of the box to register at. */ + string registrar; + + /** The authentication realm expected for registration. */ + string realm; + + /** The user name used for SIP registration. */ + string userName; + + /** The password for SIP registration. */ + string password; + + /** The host name of the box to register at (only set, if a third-party DynDNS service is used. */ + @Nullable + string host; + + /** The IPv4 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). */ + @Nullable + string ip4; + + /** The IPv6 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). */ + @Nullable + string ip6; + + /** The user name for DynDNS registration of the box (only filled, if internal DynDNS is set up). */ + @Nullable + string dyndnsUser; + + /** The password for DynDNS registration of the box. */ + @Nullable + string dyndnsPassword; +} + +/** Result of the {@link de.haumacher.phoneblock.ab.ListABServlet}. */ +message ListAnswerbotResponse { + + /** Infos for all answer bots of the current user. */ + repeated AnswerbotInfo bots; + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java index dea9f716..83aa2890 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java @@ -16,6 +16,7 @@ import org.apache.ibatis.session.SqlSession; +import de.haumacher.phoneblock.ab.DBAnswerbotInfo; import de.haumacher.phoneblock.analysis.NumberAnalyzer; import de.haumacher.phoneblock.analysis.PhoneNumer; import de.haumacher.phoneblock.carddav.resource.AddressBookCache; @@ -44,7 +45,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se Long userIdOpt = users.getUserId(userName); List blacklist; List whitelist; - List answerBots; + List answerBots; if (userIdOpt == null) { blacklist = Collections.emptyList(); whitelist = Collections.emptyList(); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 337c6887..c1c8b7d5 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -10,6 +10,9 @@ import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; +import de.haumacher.phoneblock.ab.DBAnswerbotInfo; +import de.haumacher.phoneblock.ab.proto.AnswerbotInfo; + /** * Operations for user management. */ @@ -138,11 +141,10 @@ public interface Users { "where s.USERNAME = #{userName}") DBAnswerBotSip getAnswerBot(String userName); - @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + @Select("select s.USERID, s.ENABLED, s.REGISTRAR, s.HOST, d.IP4, d.IP6, s.REALM, s.REGISTERED, s.REGISTER_MSG, s.CALLS_ACCEPTED, s.USERNAME, s.PASSWD, d.DYNDNS_USER, d.DYNDNS_PASSWD from ANSWERBOT_SIP s " + "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + "where s.USERID= #{userId}") - List getAnswerBots(long userId); - + List getAnswerBots(long userId); @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message} or not REGISTERED=#{registered})") int updateSipRegistration(long userId, boolean registered, String message); diff --git a/phoneblock/src/main/webapp/settings.jsp b/phoneblock/src/main/webapp/settings.jsp index fa65d4a1..14a7b668 100644 --- a/phoneblock/src/main/webapp/settings.jsp +++ b/phoneblock/src/main/webapp/settings.jsp @@ -1,4 +1,5 @@ +<%@page import="de.haumacher.phoneblock.ab.DBAnswerbotInfo"%> <%@page import="de.haumacher.phoneblock.ab.CreateABServlet"%> <%@page import="de.haumacher.phoneblock.db.DBAnswerBotSip"%> <%@page import="de.haumacher.phoneblock.app.SettingsServlet"%> @@ -245,7 +246,7 @@ List whitelist = (List) request.getAttribute("whitelist");

Der PhoneBlock-Anrufbeantworter prüft, ob ein Anrufer unter Spam-Verdacht steht und nimmt genau dann den Anruf an. Mit dem PhoneBlock-Anrufbeantworter kannst Du die volle Blocklist nutzen ohne die Einschränkung der maximalen Telefonbuchgröße.

-<% List answerBots = (List) request.getAttribute("answerBots"); %> +<% List answerBots = (List) request.getAttribute("answerBots"); %> <% if (answerBots.isEmpty()) { %>

Du hast noch keinen PhoneBlock-Anrufbeantworter.

@@ -259,7 +260,7 @@ List whitelist = (List) request.getAttribute("whitelist");
<% } else {%> - <% for (DBAnswerBotSip answerBot : answerBots) {%> + <% for (DBAnswerbotInfo answerBot : answerBots) {%>

Answerbot

diff --git a/phoneblock_answerbot_ui/lib/main.dart b/phoneblock_answerbot_ui/lib/main.dart index 91cd1b34..8d9880a3 100644 --- a/phoneblock_answerbot_ui/lib/main.dart +++ b/phoneblock_answerbot_ui/lib/main.dart @@ -1,11 +1,14 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:html'; +import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:jsontool/jsontool.dart'; import 'package:phoneblock_answerbot_ui/proto.dart'; +import 'package:url_launcher/url_launcher.dart'; String basePath = getBasePath(); @@ -39,9 +42,6 @@ String getContextPath() { var username = "b6c95db0-986e-47b0-af24-e51e56b09ecf"; var password = "moykCqj2XqEo7XR3FidN"; var authHeader = 'Basic ${base64Encode(utf8.encode('$username:$password'))}'; -var authHeaders = { - 'authorization': authHeader, -}; void main() { runApp(const MyApp()); @@ -81,21 +81,9 @@ class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("PhoneBlock Anrufbeantworter"), - ), - body: const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text('Du hast noch keinen Anrufbeantworter, klicke den Plus-Knopf unten, um einen PhoneBlock-Anrufbeantworter anzulegen.', - style: TextStyle(fontSize: 20), - textAlign: TextAlign.center, - ), - ], - ), + title: Text("Deine PhoneBlock Anrufbeantworter"), ), + body: const AnswerBotList(), floatingActionButton: FloatingActionButton( onPressed: () => _createAnswerBot(context), tooltip: 'Anrufbeantworter anlegen', @@ -109,14 +97,15 @@ class HomeScreen extends StatelessWidget { encoding: const Utf8Codec(), headers: { "Content-Type": "application/json", - 'Authorization': authHeader, + if (kDebugMode) + 'Authorization': authHeader, }, body: "{}", ); if (!context.mounted) return; if (response.statusCode != 200) { - return showErrorDialog(context, response); + return showErrorDialog(context, response, 'Anlage fehlgeschlagen', "Der Anrufbeantworter konnte nicht angelegt werden"); } var creation = CreateAnswerbotResponse.read(JsonReader.fromString(response.body)); @@ -140,17 +129,234 @@ class HomeScreen extends StatelessWidget { } } -Future showErrorDialog(BuildContext context, http.Response response) { +class AnswerBotList extends StatefulWidget { + const AnswerBotList({super.key}); + + @override + State createState() => AnswerBotListState(); +} + +class AnswerBotListState extends State { + + String msg = 'Loading data...'; + + List? bots; + + @override + void initState() { + super.initState(); + + http.get(Uri.parse('$basePath/ab/list'), + headers: { + if (kDebugMode) 'Authorization': authHeader, + }, + ).then(processResponse); + } + + void processResponse(http.Response response) { + setState(() { + if (response.statusCode != 200) { + msg = "Informationen können nicht abgerufen werden (Fehler ${response.statusCode}): ${response.body}"; + return; + } + + var bots = ListAnswerbotResponse.read(JsonReader.fromString(response.body)).bots; + if (bots.isEmpty) { + msg = "Du hast noch keinen Anrufbeantworter, klicke den Plus-Knopf unten, um einen PhoneBlock-Anrufbeantworter anzulegen."; + } else { + this.bots = bots; + } + }); + } + + @override + Widget build(BuildContext context) { + var bots = this.bots; + + if (bots == null || bots.isEmpty) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text(msg, + style: const TextStyle(fontSize: 20), + textAlign: TextAlign.center, + ) + ], + ), + ); + } + + return ListView.separated( + padding: const EdgeInsets.all(8), + itemCount: bots.length, + itemBuilder: (BuildContext context, int index) { + return SizedBox( + height: 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Anrufbeantworter ${bots[index].userName}'), + ], + ), + IconButton( + icon: Icon(Icons.arrow_right), + iconSize: 32, + onPressed: () => showAnswerBot(context, bots[index]), + ) + ], + ) + ); + }, + separatorBuilder: (BuildContext context, int index) => const Divider(), + ); + } + + showAnswerBot(BuildContext context, AnswerbotInfo bot) { + Navigator.push(context, MaterialPageRoute(builder: (context) => AnswerBotView(bot))); + } +} + +class AnswerBotView extends StatefulWidget { + AnswerbotInfo bot; + + AnswerBotView(this.bot, {super.key}); + + @override + State createState() => AnswerBotViewState(); +} + +class AnswerBotViewState extends State { + AnswerbotInfo get bot => widget.bot; + final _formKey = GlobalKey(); + + bool internalDynDns = true; + + @override + void initState() { + super.initState(); + + var host = bot.host; + internalDynDns = host == null || host.isEmpty; + } + + final MaterialStateProperty switchIcon = MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.selected)) { + return const Icon(Icons.check); + } + return const Icon(Icons.close); + }, + ); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Anrufbeantworter ${bot.userName}"), + ), + body: Form( + key: _formKey, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Row( + children: [ + Expanded(child: Text("PhoneBlock-DynDNS benutzen")), + Switch( + thumbIcon: switchIcon, + value: internalDynDns, + onChanged: (bool value) { + setState(() { + internalDynDns = value; + }); + }, + ) + ], + ), + ), + + if (internalDynDns) InfoField('DynDNS-User', bot.dyndnsUser, help: "Trage diesen ...."), + if (internalDynDns) InfoField('DynDNS-Password', bot.dyndnsPassword), + if (!internalDynDns) TextFormField( + decoration: const InputDecoration( + labelText: 'Host', + ), + initialValue: bot.host, + ), + + InfoField('User', bot.userName), + InfoField('Password', bot.password), + ], + ), + ), + ), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.help), + tooltip: "Hilfe anzeigen", + onPressed: () async { + await launchUrl(Uri.parse("https://phoneblock.net/")); + }, + ), + ); + } +} + +class InfoField extends StatelessWidget { + final String label; + final String? help; + final String? value; + + const InfoField(this.label, this.value, {this.help, super.key}); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: TextFormField( + decoration: InputDecoration( + labelText: label, + helperText: help, + ), + initialValue: value ?? "", + readOnly: true, + ), + ), + IconButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: value ?? "")); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Copied to clipboard."))); + }, + icon: Icon(Icons.copy) + ), + ], + ); + } + +} + +Future showErrorDialog(BuildContext context, http.Response response, String title, String msg) { return showDialog( context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { return AlertDialog( - title: const Text('Anlage fehlgeschlagen'), + title: Text(title), content: SingleChildScrollView( child: ListBody( children: [ - Text('Der Anrufbeantworter konnte nicht angelegt werden (Fehler ${response.statusCode}).'), + Text('$msg (Fehler ${response.statusCode}).'), Text(response.body), ], ), diff --git a/phoneblock_answerbot_ui/lib/proto.dart b/phoneblock_answerbot_ui/lib/proto.dart index d0a83fa7..9b7dda36 100644 --- a/phoneblock_answerbot_ui/lib/proto.dart +++ b/phoneblock_answerbot_ui/lib/proto.dart @@ -91,3 +91,266 @@ class CreateAnswerbotResponse extends _JsonObject { } +/// Information of a single answer bot. +class AnswerbotInfo extends _JsonObject { + /// The primary key identifier of this bot. + int id; + + /// Whether the bot is enabled (registration is active). + bool enabled; + + /// Whether the bot has sucessfully registered (can accept calls). + bool registered; + + /// The message received during the last registration attempt. + String? registerMsg; + + /// The total number of calls accepted by this bot so far. + int callsAccepted; + + /// The name of the box to register at. + String registrar; + + /// The authentication realm expected for registration. + String realm; + + /// The user name used for SIP registration. + String userName; + + /// The password for SIP registration. + String password; + + /// The host name of the box to register at (only set, if a third-party DynDNS service is used. + String? host; + + /// The IPv4 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). + String? ip4; + + /// The IPv6 address of the box to register at (only filled, if internal DynDNS is set up and succeeded). + String? ip6; + + /// The user name for DynDNS registration of the box (only filled, if internal DynDNS is set up). + String? dyndnsUser; + + /// The password for DynDNS registration of the box. + String? dyndnsPassword; + + /// Creates a AnswerbotInfo. + AnswerbotInfo({ + this.id = 0, + this.enabled = false, + this.registered = false, + this.registerMsg, + this.callsAccepted = 0, + this.registrar = "", + this.realm = "", + this.userName = "", + this.password = "", + this.host, + this.ip4, + this.ip6, + this.dyndnsUser, + this.dyndnsPassword, + }); + + /// Parses a AnswerbotInfo from a string source. + static AnswerbotInfo? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a AnswerbotInfo instance from the given reader. + static AnswerbotInfo read(JsonReader json) { + AnswerbotInfo result = AnswerbotInfo(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "AnswerbotInfo"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + case "enabled": { + enabled = json.expectBool(); + break; + } + case "registered": { + registered = json.expectBool(); + break; + } + case "registerMsg": { + registerMsg = json.expectString(); + break; + } + case "callsAccepted": { + callsAccepted = json.expectInt(); + break; + } + case "registrar": { + registrar = json.expectString(); + break; + } + case "realm": { + realm = json.expectString(); + break; + } + case "userName": { + userName = json.expectString(); + break; + } + case "password": { + password = json.expectString(); + break; + } + case "host": { + host = json.expectString(); + break; + } + case "ip4": { + ip4 = json.expectString(); + break; + } + case "ip6": { + ip6 = json.expectString(); + break; + } + case "dyndnsUser": { + dyndnsUser = json.expectString(); + break; + } + case "dyndnsPassword": { + dyndnsPassword = json.expectString(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + + json.addKey("enabled"); + json.addBool(enabled); + + json.addKey("registered"); + json.addBool(registered); + + var _registerMsg = registerMsg; + if (_registerMsg != null) { + json.addKey("registerMsg"); + json.addString(_registerMsg); + } + + json.addKey("callsAccepted"); + json.addNumber(callsAccepted); + + json.addKey("registrar"); + json.addString(registrar); + + json.addKey("realm"); + json.addString(realm); + + json.addKey("userName"); + json.addString(userName); + + json.addKey("password"); + json.addString(password); + + var _host = host; + if (_host != null) { + json.addKey("host"); + json.addString(_host); + } + + var _ip4 = ip4; + if (_ip4 != null) { + json.addKey("ip4"); + json.addString(_ip4); + } + + var _ip6 = ip6; + if (_ip6 != null) { + json.addKey("ip6"); + json.addString(_ip6); + } + + var _dyndnsUser = dyndnsUser; + if (_dyndnsUser != null) { + json.addKey("dyndnsUser"); + json.addString(_dyndnsUser); + } + + var _dyndnsPassword = dyndnsPassword; + if (_dyndnsPassword != null) { + json.addKey("dyndnsPassword"); + json.addString(_dyndnsPassword); + } + } + + } + +/// Result of the {@link de.haumacher.phoneblock.ab.ListABServlet}. +class ListAnswerbotResponse extends _JsonObject { + /// Infos for all answer bots of the current user. + List bots; + + /// Creates a ListAnswerbotResponse. + ListAnswerbotResponse({ + this.bots = const [], + }); + + /// Parses a ListAnswerbotResponse from a string source. + static ListAnswerbotResponse? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a ListAnswerbotResponse instance from the given reader. + static ListAnswerbotResponse read(JsonReader json) { + ListAnswerbotResponse result = ListAnswerbotResponse(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "ListAnswerbotResponse"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "bots": { + json.expectArray(); + bots = []; + while (json.hasNext()) { + if (!json.tryNull()) { + bots.add(AnswerbotInfo.read(json)); + } + } + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("bots"); + json.startArray(); + for (var _element in bots) { + _element.writeContent(json); + } + json.endArray(); + } + +} + diff --git a/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc index e71a16d2..f6f23bfe 100644 --- a/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc +++ b/phoneblock_answerbot_ui/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake b/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake index 2e1de87a..f16b4c34 100644 --- a/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake +++ b/phoneblock_answerbot_ui/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/phoneblock_answerbot_ui/pubspec.lock b/phoneblock_answerbot_ui/pubspec.lock index 528442a8..b95cd6a9 100644 --- a/phoneblock_answerbot_ui/pubspec.lock +++ b/phoneblock_answerbot_ui/pubspec.lock @@ -75,6 +75,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" http: dependency: "direct main" description: @@ -139,6 +144,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 + url: "https://pub.dev" + source: hosted + version: "2.1.7" sky_engine: dependency: transitive description: flutter @@ -200,6 +213,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + url: "https://pub.dev" + source: hosted + version: "6.2.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + url: "https://pub.dev" + source: hosted + version: "6.2.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" vector_math: dependency: transitive description: @@ -218,3 +295,4 @@ packages: version: "0.3.0" sdks: dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" diff --git a/phoneblock_answerbot_ui/pubspec.yaml b/phoneblock_answerbot_ui/pubspec.yaml index 8da2e2a7..1ac274a5 100644 --- a/phoneblock_answerbot_ui/pubspec.yaml +++ b/phoneblock_answerbot_ui/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: cupertino_icons: ^1.0.2 http: ^1.1.2 jsontool: ^1.2.0 + url_launcher: ^6.2.2 dev_dependencies: flutter_test: From 08539adf45d82106c331d0c499b13a507f14f3ce Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Mon, 8 Jan 2024 20:34:53 +0100 Subject: [PATCH 075/153] Removed hard-coded phoneblock.net address. --- .../haumacher/phoneblock/ab/SipService.java | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index f7746724..4df5d4fe 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -124,47 +124,9 @@ public void start() { LOG.info("Starting SIP service."); SipConfig sipConfig = new SipConfig(); - - String hostname = "phoneblock.net"; - String ip4 = null; - String ip6 = null; - try { - InetAddress[] addresses = InetAddress.getAllByName(hostname); - for (InetAddress address : addresses) { - if (address.isAnyLocalAddress()) { - continue; - } - if (address.isLinkLocalAddress()) { - continue; - } - if (address.isLoopbackAddress()) { - continue; - } - if (address.isMulticastAddress()) { - continue; - } - if (address.isSiteLocalAddress()) { - continue; - } - - if (address instanceof Inet6Address) { - ip6 = address.getHostAddress(); - LOG.info("Found IPv6 address: " + ip6); - } else { - ip4 = address.getHostAddress(); - LOG.info("Found IPv4 address: " + ip4); - } - } - } catch (UnknownHostException ex) { - LOG.error("Cannot resolve own address.", ex); - } - - sipConfig.setViaAddr(ip6 != null ? ip6 : ip4 != null ? ip4 : hostname); - PortConfig portConfig = new PortConfig(); portConfig.setMediaPort(50061); portConfig.setPortCount(20); - AnswerbotConfig botOptions = new AnswerbotConfig(); loadConfig(sipConfig, portConfig, botOptions); From ea01c4aedf84928975159f855d21f852d05e8ee0 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 13 Jan 2024 20:09:00 +0100 Subject: [PATCH 076/153] Resolved configured via host names to IP addresses. When using host names for RTP connection information, no audio connection is established - at least when communicating with Fritz!Box VOIP routers. --- .../haumacher/phoneblock/ab/SipService.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index 4df5d4fe..d8cd1e10 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -37,6 +37,7 @@ import org.mjsip.ua.registration.RegistrationClientListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.zoolu.net.AddressType; import org.zoolu.util.ConfigFile; import de.haumacher.phoneblock.answerbot.AnswerBot; @@ -130,8 +131,11 @@ public void start() { AnswerbotConfig botOptions = new AnswerbotConfig(); loadConfig(sipConfig, portConfig, botOptions); + sipConfig.normalize(); + resolveViaAddress(sipConfig); Scheduler scheduler = Scheduler.of(_scheduler.executor()); + _sipProvider = new SipProvider(sipConfig, scheduler); _portPool = portConfig.createPool(); _answerBot = new AnswerBot(_sipProvider, botOptions, this::getCustomer, _portPool); @@ -140,11 +144,34 @@ public void start() { _scheduler.executor().scheduleAtFixedRate(this::registerBots, 10, 5 * 60, TimeUnit.SECONDS); } + + private void resolveViaAddress(SipConfig sipConfig) { + sipConfig.setViaAddrIPv4(resolve(AddressType.IP4, sipConfig.getViaAddrIPv4())); + sipConfig.setViaAddrIPv6(resolve(AddressType.IP6, sipConfig.getViaAddrIPv6())); + } + + private String resolve(AddressType type, String hostName) { + boolean resolveV6 = type == AddressType.IP6; + + try { + for (InetAddress address : InetAddress.getAllByName(hostName)) { + boolean ipv6 = address instanceof Inet6Address; + if (resolveV6 == ipv6) { + return address.getHostAddress(); + } + } + } catch (UnknownHostException e) { + // Ignore. + } + return hostName; + } /** * Registers all answer bots that have been updated since the last run. */ private void registerBots() { + resolveViaAddress((SipConfig) _sipProvider.sipConfig()); + long since = _lastRegister; _lastRegister = System.currentTimeMillis(); From a37ea791f88c4c3060c8397c7e88fa4f7f4c9535 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 14 Jan 2024 08:11:00 +0100 Subject: [PATCH 077/153] Applied fixes suggested by dart fix. --- phoneblock_answerbot_ui/lib/main.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/phoneblock_answerbot_ui/lib/main.dart b/phoneblock_answerbot_ui/lib/main.dart index 8d9880a3..7bddfbd3 100644 --- a/phoneblock_answerbot_ui/lib/main.dart +++ b/phoneblock_answerbot_ui/lib/main.dart @@ -81,7 +81,7 @@ class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Deine PhoneBlock Anrufbeantworter"), + title: const Text("Deine PhoneBlock Anrufbeantworter"), ), body: const AnswerBotList(), floatingActionButton: FloatingActionButton( @@ -115,7 +115,7 @@ class HomeScreen extends StatelessWidget { Widget _setupAnswerBot(BuildContext context, CreateAnswerbotResponse creation) { return Scaffold( appBar: AppBar( - title: Text("Anrufbeantworter einrichten"), + title: const Text("Anrufbeantworter einrichten"), ), body: Center( child: Column( @@ -206,7 +206,7 @@ class AnswerBotListState extends State { ], ), IconButton( - icon: Icon(Icons.arrow_right), + icon: const Icon(Icons.arrow_right), iconSize: 32, onPressed: () => showAnswerBot(context, bots[index]), ) @@ -264,14 +264,14 @@ class AnswerBotViewState extends State { body: Form( key: _formKey, child: Padding( - padding: EdgeInsets.symmetric(horizontal: 8), + padding: const EdgeInsets.symmetric(horizontal: 8), child: Column( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 8), + padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ - Expanded(child: Text("PhoneBlock-DynDNS benutzen")), + const Expanded(child: Text("PhoneBlock-DynDNS benutzen")), Switch( thumbIcon: switchIcon, value: internalDynDns, @@ -301,11 +301,11 @@ class AnswerBotViewState extends State { ), ), floatingActionButton: FloatingActionButton( - child: Icon(Icons.help), tooltip: "Hilfe anzeigen", onPressed: () async { await launchUrl(Uri.parse("https://phoneblock.net/")); }, + child: const Icon(Icons.help), ), ); } @@ -336,9 +336,9 @@ class InfoField extends StatelessWidget { onPressed: () async { await Clipboard.setData(ClipboardData(text: value ?? "")); if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Copied to clipboard."))); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard."))); }, - icon: Icon(Icons.copy) + icon: const Icon(Icons.copy) ), ], ); From f7692887953f2402ab0cacfff9f6742c8f873f06 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 18 Jan 2024 18:33:45 +0100 Subject: [PATCH 078/153] First working prototype of Flutter answer bot configuration UI. --- .../phoneblock/ab/CreateABServlet.java | 300 ++++++++++- .../phoneblock/ab/DBAnswerbotInfo.java | 2 + .../haumacher/phoneblock/ab/Registration.java | 37 +- .../phoneblock/ab/RequestContext.java | 16 + .../haumacher/phoneblock/ab/SipService.java | 112 ++-- .../phoneblock/ab/SipServiceConfig.java | 13 + .../phoneblock/ab/proto/AnswerbotInfo.java | 31 ++ .../phoneblock/ab/proto/CheckAnswerBot.java | 108 ++++ .../phoneblock/ab/proto/CheckDynDns.java | 108 ++++ .../phoneblock/ab/proto/CreateAnswerBot.java | 46 ++ .../ab/proto/CreateAnswerbotResponse.java | 58 ++- .../phoneblock/ab/proto/DisableAnswerBot.java | 108 ++++ .../phoneblock/ab/proto/EnableAnswerBot.java | 108 ++++ .../phoneblock/ab/proto/EnterHostName.java | 136 +++++ .../phoneblock/ab/proto/SetupDynDns.java | 136 +++++ .../ab/proto/SetupDynDnsResponse.java | 158 ++++++ .../phoneblock/ab/proto/SetupRequest.java | 98 ++++ .../de/haumacher/phoneblock/ab/proto/ab.proto | 46 ++ .../phoneblock/app/api/DynIpServlet.java | 2 +- .../phoneblock/db/DBAnswerBotDynDns.java | 3 +- .../phoneblock/db/DBAnswerBotSip.java | 3 +- .../de/haumacher/phoneblock/db/Users.java | 50 +- .../de/haumacher/phoneblock/db/db-schema.sql | 19 +- .../db/settings/AnswerBotDynDns.java | 14 +- .../db/settings/AnswerBotSetting.java | 44 +- .../phoneblock/db/settings/AnswerBotSip.java | 28 +- .../phoneblock/db/settings/settings.proto | 3 + .../de/haumacher/phoneblock/jmx/AppState.java | 2 +- phoneblock/src/main/webapp/WEB-INF/web.xml | 4 +- phoneblock_answerbot_ui/lib/main.dart | 479 ++++++++++++++++-- phoneblock_answerbot_ui/lib/proto.dart | 479 ++++++++++++++++++ phoneblock_answerbot_ui/web/index.html | 3 - 32 files changed, 2600 insertions(+), 154 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/RequestContext.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipServiceConfig.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckAnswerBot.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckDynDns.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerBot.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DisableAnswerBot.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnableAnswerBot.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnterHostName.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDns.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDnsResponse.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index 16cfef30..9aef454d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -4,10 +4,15 @@ package de.haumacher.phoneblock.ab; import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -15,25 +20,35 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.haumacher.msgbuf.data.DataObject; +import de.haumacher.msgbuf.json.JsonReader; import de.haumacher.msgbuf.json.JsonWriter; +import de.haumacher.msgbuf.server.io.ReaderAdapter; import de.haumacher.msgbuf.server.io.WriterAdapter; +import de.haumacher.phoneblock.ab.proto.CheckAnswerBot; +import de.haumacher.phoneblock.ab.proto.CheckDynDns; +import de.haumacher.phoneblock.ab.proto.CreateAnswerBot; import de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse; +import de.haumacher.phoneblock.ab.proto.DisableAnswerBot; +import de.haumacher.phoneblock.ab.proto.EnableAnswerBot; +import de.haumacher.phoneblock.ab.proto.EnterHostName; +import de.haumacher.phoneblock.ab.proto.SetupDynDns; +import de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse; +import de.haumacher.phoneblock.ab.proto.SetupRequest; import de.haumacher.phoneblock.app.LoginFilter; import de.haumacher.phoneblock.db.DB; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; -import de.haumacher.phoneblock.db.settings.AnswerBotSip; -import de.haumacher.phoneblock.random.SecureRandomService; /** * Servlet creating an answerbot. */ @WebServlet(urlPatterns = CreateABServlet.PATH) -public class CreateABServlet extends ABApiServlet { +public class CreateABServlet extends ABApiServlet implements SetupRequest.Visitor { private static final Logger LOG = LoggerFactory.getLogger(CreateABServlet.class); - public static final String PATH = "/ab/create"; + public static final String PATH = "/ab/setup"; @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -44,6 +59,26 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S return; } + SetupRequest cmd = SetupRequest.readSetupRequest(new JsonReader(new ReaderAdapter(req.getReader()))); + if (cmd == null) { + LOG.warn("Command not understood."); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "Command not understood."); + return; + } + + try { + cmd.visit(this, new RequestContext(req, resp)); + } catch (Throwable ex) { + LOG.error("Request failed for: " + login, ex); + } + } + + @Override + public Void visit(CreateAnswerBot self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + DB db = DBService.getInstance(); String userName = "ab-" + db.createId(16); String password = db.createPassword(16); @@ -55,30 +90,259 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S if (userId == null) { LOG.warn("Rejected answerbot creation for unknown user: " + login); sendError(resp, HttpServletResponse.SC_UNAUTHORIZED, "User not found."); - return; + return null; } long now = System.currentTimeMillis(); - int ok = users.createAnswerBot(userId.longValue(), userName, password, now); - if (ok < 1) { - LOG.warn("Cannot create answerbot for: " + login); - sendError(resp, HttpServletResponse.SC_CONFLICT, "Creation failed."); - return; + + long id; + try (PreparedStatement statement = session.getConnection().prepareStatement( + "insert into ANSWERBOT_SIP (USERID, USERNAME, PASSWD, CREATED, UPDATED) values (?, ?, ?, ?, ?)", + Statement.RETURN_GENERATED_KEYS)) { + statement.setLong(1, userId.longValue()); + statement.setString(2, userName); + statement.setString(3, password); + statement.setLong(4, now); + statement.setLong(5, now); + + int affectedRows = statement.executeUpdate(); + if (affectedRows < 1) { + LOG.warn("Cannot create answerbot for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "Creation failed."); + return null; + } + + try (ResultSet generatedKeys = statement.getGeneratedKeys()) { + if (!generatedKeys.next()) { + LOG.warn("Cannot retrieve answerbot ID for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "No ID retrieved."); + return null; + } + + id = generatedKeys.getLong(1); + } + session.commit(); + + sendResult(resp, CreateAnswerbotResponse.create() + .setId(id) + .setUserName(userName) + .setPassword(password)); + LOG.info("Created answerbot '" + userName + "' for: " + login); + } catch (SQLException ex) { + LOG.warn("Answerbot creation failed for : " + login); + sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Creation failed: " + ex.getMessage()); + return null; } + } + return null; + } + + @Override + public Void visit(EnterHostName self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + String hostName = self.getHostName(); + + boolean ok = checkHostName(hostName); + if (!ok) { + LOG.warn("Invalid host name '" + hostName + "' for: " + login); + sendError(resp, HttpServletResponse.SC_BAD_REQUEST, "Invalid host name."); + return null; + } + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + users.answerbotDeleteDynDns(self.getId()); + users.answerbotEnterHostName(self.getId(), hostName); session.commit(); - } catch (RuntimeException ex) { - LOG.error("Answerbot creation failed for: " + login, ex); + } + + sendOk(resp); + LOG.info("Set up host name '" + hostName + "' for: " + login); + return null; + } - sendError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage()); - return; + @Override + public Void visit(SetupDynDns self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + DB db = DBService.getInstance(); + + long id = self.getId(); + String dynDnsUser = "fb-" + db.createId(16); + String dynDnsPassword = db.createPassword(16); + + long now = System.currentTimeMillis(); + + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + DBAnswerbotInfo answerBot = users.getAnswerBot(id); + if (answerBot == null) { + LOG.warn("Answerbot '" + id + "' not found for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "Answer bot not found: " + id); + return null; + } + + users.answerbotDeleteDynDns(id); + users.setupDynDns(id, answerBot.getUserId(), now, dynDnsUser, dynDnsPassword); + + session.commit(); + } + sendResult(resp, SetupDynDnsResponse.create() + .setId(id) + .setDyndnsUser(dynDnsUser) + .setDyndnsPassword(dynDnsPassword)); + + LOG.info("Set up DynDNS user '" + dynDnsUser + "' for: " + login); + return null; + } + + @Override + public Void visit(CheckDynDns self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + long id = self.getId(); + DBAnswerbotInfo answerBot = users.getAnswerBot(id); + if (answerBot == null) { + LOG.warn("Answerbot '" + id + "' not found for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "Answer bot not found: " + id); + return null; + } + + String ipv4 = answerBot.getIp4(); + String ipv6 = answerBot.getIp6(); + if (isEmpty(ipv4) && isEmpty(ipv6)) { + sendError(resp, HttpServletResponse.SC_CONFLICT, "No domain name set."); + return null; + } + } + + sendOk(resp); + LOG.info("DynDNS checked successfully for: " + login); + return null; + } + + private boolean checkHostName(String host) { + try { + InetAddress[] addresses = InetAddress.getAllByName(host); + for (InetAddress address : addresses) { + if (address.isAnyLocalAddress() || address.isLinkLocalAddress() || address.isLoopbackAddress() + || address.isMulticastAddress() || address.isSiteLocalAddress()) { + // None of the potential addresses must be a local address. Otherwise "fritz.box" would be accepted. + return false; + } + } + } catch (UnknownHostException e) { + LOG.warn("Cannot resolve host name of answerbot: " + host); + return false; + } + return true; + } + + @Override + public Void visit(EnableAnswerBot self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + long id = self.getId(); + DBAnswerbotInfo bot = users.getAnswerBot(id); + if (isEmpty(bot.getHost()) && isEmpty(bot.getIp4()) && isEmpty(bot.getIp6())) { + LOG.warn("No connection information for answer bot '" + id + "' for: " + login); + sendError(resp, HttpServletResponse.SC_CONFLICT, "No connection information."); + } + + SipService sipService = SipService.getInstance(); + String userName = bot.getUserName(); + sipService.disableAnwserBot(userName); + sipService.enableAnwserBot(userName, true); + } + + sendOk(resp); + LOG.info("Answerbot enabled for: " + login); + return null; + } + + @Override + public Void visit(DisableAnswerBot self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + DBAnswerbotInfo bot = users.getAnswerBot(self.getId()); + SipService.getInstance().disableAnwserBot(bot.getUserName()); + } + + sendOk(resp); + LOG.info("Answerbot disabled for: " + login); + return null; + } + + @Override + public Void visit(CheckAnswerBot self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + long id = self.getId(); + DBAnswerbotInfo bot = users.getAnswerBot(id); + + if (!bot.isRegistered()) { + String registerMsg = bot.getRegisterMsg(); + if (registerMsg == null) { + registerMsg = "No response."; + } + sendError(resp, HttpServletResponse.SC_CONFLICT, registerMsg); + LOG.warn("Answerbot '" + id + "' not registered for: " + login + " (" + registerMsg + ")"); + return null; + } } + sendOk(resp); + LOG.info("Answerbot checked sucessfully for: " + login); + return null; + } + + private void sendOk(HttpServletResponse resp) throws IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.getWriter().append("OK"); + } + + private void sendResult(HttpServletResponse resp, DataObject result) throws IOException { resp.setContentType("application/json"); resp.setCharacterEncoding("utf-8"); - CreateAnswerbotResponse.create().setUserName(userName).writeTo(new JsonWriter(new WriterAdapter(resp.getWriter()))); - - LOG.warn("Created answerbot '" + userName + "' for: " + login); + result.writeTo(new JsonWriter(new WriterAdapter(resp.getWriter()))); + } + + private static boolean isEmpty(String str) { + return str == null || str.isBlank(); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java index 6e030770..b376c569 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/DBAnswerbotInfo.java @@ -6,12 +6,14 @@ public class DBAnswerbotInfo extends AnswerbotInfo { public DBAnswerbotInfo( long id, + long userId, boolean enabled, String registrar, String host, String ip4, String ip6, String realm, boolean registered, String msg, int callsAccepted, String userName, String password, String dyndnsUser, String dyndnsPassword ) { setId(id); + setUserId(userId); setEnabled(enabled); setRegistrar(registrar); setHost(host); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java index 32f0ef7f..38ebd253 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/Registration.java @@ -8,6 +8,7 @@ import org.mjsip.ua.registration.RegistrationClientListener; import de.haumacher.phoneblock.answerbot.CustomerOptions; +import de.haumacher.phoneblock.db.settings.AnswerBotSip; /** * {@link RegistrationClient} that holds the complete configuration for the customer. @@ -15,22 +16,46 @@ public class Registration extends RegistrationClient { private CustomerOptions _customer; - private long _userId; + private boolean _temporary; + private AnswerBotSip _bot; + private int _failures; /** * Creates a {@link Registration}. + * @param temporary */ - public Registration(SipProvider sipProvider, long userId, CustomerOptions customer, RegistrationClientListener sipService) { + public Registration(SipProvider sipProvider, AnswerBotSip bot, CustomerOptions customer, RegistrationClientListener sipService, boolean temporary) { super(sipProvider, customer, sipService); - _userId = userId; + _bot = bot; _customer = customer; + _temporary = temporary; } + public boolean isTemporary() { + return _temporary; + } + + public void setPermanent() { + _temporary = false; + } + + public int getFailures() { + return _failures; + } + + public void incFailures() { + _failures++; + } + + public void resetFailures() { + _failures = 0; + } + /** - * The user ID of the registered user. + * The ID of the registered answerbot. */ - public long getUserId() { - return _userId; + public long getId() { + return _bot.getId(); } /** diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/RequestContext.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/RequestContext.java new file mode 100644 index 00000000..7fc9ee46 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/RequestContext.java @@ -0,0 +1,16 @@ +package de.haumacher.phoneblock.ab; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class RequestContext { + + public final HttpServletRequest req; + public final HttpServletResponse resp; + + public RequestContext(HttpServletRequest req, HttpServletResponse resp) { + this.req = req; + this.resp = resp; + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java index d8cd1e10..9d75d482 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipService.java @@ -75,6 +75,8 @@ public class SipService implements ServletContextListener, RegistrationClientLis private Collection _jndiOptions; + private SipServiceConfig _config; + /** * Creates a {@link SipService}. */ @@ -128,6 +130,7 @@ public void start() { PortConfig portConfig = new PortConfig(); portConfig.setMediaPort(50061); portConfig.setPortCount(20); + _config = new SipServiceConfig(); AnswerbotConfig botOptions = new AnswerbotConfig(); loadConfig(sipConfig, portConfig, botOptions); @@ -190,7 +193,7 @@ private void registerBots() { if (host == null || host.isEmpty()) { failed.add(bot); } else { - register(bot); + register(bot, false); } } @@ -200,7 +203,7 @@ private void registerBots() { for (AnswerBotSip bot : failed) { LOG.warn("Disabling answer bot without host address: " + bot.getUserId() + "/" + bot.getUserName()); - users.enableAnswerBot(bot.getUserId(), false, _lastRegister); + users.enableAnswerBot(bot.getId(), false, _lastRegister); } session.commit(); @@ -284,39 +287,56 @@ public static SipService getInstance() { return _instance; } - public void enableAnwserBot(String userName, boolean enable) { + public void enableAnwserBot(String userName) { + enableAnwserBot(userName, false); + } + + public void enableAnwserBot(String userName, boolean temporary) { + try (SqlSession tx = _dbService.db().openSession()) { + Users users = tx.getMapper(Users.class); + + DBAnswerBotSip bot = users.getAnswerBotBySipUser(userName); + if (bot == null) { + LOG.warn("User with ID '" + userName + "' not found."); + return; + } + + register(bot, temporary); + } + } + + public void disableAnwserBot(String userName) { try (SqlSession tx = _dbService.db().openSession()) { Users users = tx.getMapper(Users.class); - DBAnswerBotSip bot = users.getAnswerBot(userName); + DBAnswerBotSip bot = users.getAnswerBotBySipUser(userName); if (bot == null) { LOG.warn("User with ID '" + userName + "' not found."); return; } - users.enableAnswerBot(bot.getUserId(), enable, System.currentTimeMillis()); + users.enableAnswerBot(bot.getId(), false, System.currentTimeMillis()); tx.commit(); - if (enable) { - register(bot); - } else { - Registration registration = _clients.remove(bot.getUserName()); - if (registration == null) { - LOG.info("No active registration for user '" + userName + "'."); - return; - } - - LOG.info("Stopping answer bot '" + userName + "'."); - registration.halt(); + Registration registration = _clients.remove(bot.getUserName()); + if (registration == null) { + LOG.info("No active registration for user '" + userName + "'."); + return; } + + LOG.info("Stopping answer bot '" + userName + "'."); + registration.halt(); } } - /** + /** * Dynamically registers a new answer bot. + * + * @param temporary Whether the bot is first activated and should only be + * permanently activated, if the first registration succeeds. */ - public void register(AnswerBotSip bot) { - register(bot.getUserId(), toCustomerConfig(bot)); + public void register(AnswerBotSip bot, boolean temporary) { + register(bot, toCustomerConfig(bot), temporary); } private CustomerConfig toCustomerConfig(AnswerBotSip bot) { @@ -329,10 +349,10 @@ private CustomerConfig toCustomerConfig(AnswerBotSip bot) { return regConfig; } - private void register(long userId, CustomerOptions customerConfig) { + private void register(AnswerBotSip bot, CustomerOptions customerConfig, boolean temporary) { try { - Registration client = new Registration(_sipProvider, userId, customerConfig, this); - Registration clash = _clients.put(customerConfig.getUser(), client); + Registration client = new Registration(_sipProvider, bot, customerConfig, this, temporary); + Registration clash = _clients.put(bot.getUserName(), client); if (clash != null) { clash.halt(); @@ -346,26 +366,54 @@ private void register(long userId, CustomerOptions customerConfig) { } @Override - public void onRegistrationSuccess(RegistrationClient registration, NameAddress target, NameAddress contact, int expires, + public void onRegistrationSuccess(RegistrationClient client, NameAddress target, NameAddress contact, int expires, int renewTime, String result) { - long userId = ((Registration) registration).getUserId(); - updateRegistration(userId, true, result); - LOG.info("Sucessfully registered " + registration.getUsername() + ": " + result); + Registration registration = (Registration) client; + long id = registration.getId(); + + if (registration.isTemporary()) { + // Bot was only started temporarily, now enable permanently. + + try (SqlSession session = _dbService.db().openSession()) { + Users users = session.getMapper(Users.class); + + users.enableAnswerBot(id, true, System.currentTimeMillis()); + session.commit(); + } + + registration.setPermanent(); + } + registration.resetFailures(); + + updateRegistration(id, true, result); + + LOG.info("Sucessfully registered " + client.getUsername() + ": " + result); } @Override - public void onRegistrationFailure(RegistrationClient registration, NameAddress target, NameAddress contact, + public void onRegistrationFailure(RegistrationClient client, NameAddress target, NameAddress contact, String result) { - long userId = ((Registration) registration).getUserId(); - updateRegistration(userId, false, result); - LOG.warn("Failed to register '" + registration.getUsername() + "': " + result); + Registration registration = (Registration) client; + long id = registration.getId(); + + registration.incFailures(); + int failures = registration.getFailures(); + + LOG.warn("Failed to register '" + client.getUsername() + "' (" + failures + " failures): " + result); + updateRegistration(id, false, result); + + boolean temporary = registration.isTemporary(); + if (temporary || failures > _config.maxFailures) { + LOG.warn("Stopping " + (temporary ? "temporary " : "") + "registration '" + client.getUsername() + "'."); + registration.halt(); + } } - private void updateRegistration(long userId, boolean registered, String result) { + private void updateRegistration(long id, boolean registered, String result) { try (SqlSession session = _dbService.db().openSession()) { Users users = session.getMapper(Users.class); - int cnt = users.updateSipRegistration(userId, registered, result); + int cnt = users.updateSipRegistration(id, registered, result); if (cnt > 0) { session.commit(); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipServiceConfig.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipServiceConfig.java new file mode 100644 index 00000000..8f705f2d --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/SipServiceConfig.java @@ -0,0 +1,13 @@ +package de.haumacher.phoneblock.ab; + +import org.kohsuke.args4j.Option; + +/** + * Configuration options for {@link SipService}. + */ +public class SipServiceConfig { + + @Option(name = "--max-failures", usage = "Maximum number of consecutive registration failures before an answerbot is disabled.") + public int maxFailures = 500; + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java index 748dce28..1b55a75d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/AnswerbotInfo.java @@ -18,6 +18,9 @@ public static de.haumacher.phoneblock.ab.proto.AnswerbotInfo create() { /** @see #getId() */ public static final String ID__PROP = "id"; + /** @see #getUserId() */ + public static final String USER_ID__PROP = "userId"; + /** @see #isEnabled() */ public static final String ENABLED__PROP = "enabled"; @@ -59,6 +62,8 @@ public static de.haumacher.phoneblock.ab.proto.AnswerbotInfo create() { private long _id = 0L; + private long _userId = 0L; + private boolean _enabled = false; private boolean _registered = false; @@ -114,6 +119,26 @@ protected final void internalSetId(long value) { _id = value; } + /** + * The ID of the owning user. + */ + public final long getUserId() { + return _userId; + } + + /** + * @see #getUserId() + */ + public de.haumacher.phoneblock.ab.proto.AnswerbotInfo setUserId(long value) { + internalSetUserId(value); + return this; + } + + /** Internal setter for {@link #getUserId()} without chain call utility. */ + protected final void internalSetUserId(long value) { + _userId = value; + } + /** * Whether the bot is enabled (registration is active). */ @@ -424,6 +449,7 @@ public String jsonType() { private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( java.util.Arrays.asList( ID__PROP, + USER_ID__PROP, ENABLED__PROP, REGISTERED__PROP, REGISTER_MSG__PROP, @@ -447,6 +473,7 @@ public java.util.List properties() { public Object get(String field) { switch (field) { case ID__PROP: return getId(); + case USER_ID__PROP: return getUserId(); case ENABLED__PROP: return isEnabled(); case REGISTERED__PROP: return isRegistered(); case REGISTER_MSG__PROP: return getRegisterMsg(); @@ -468,6 +495,7 @@ public Object get(String field) { public void set(String field, Object value) { switch (field) { case ID__PROP: internalSetId((long) value); break; + case USER_ID__PROP: internalSetUserId((long) value); break; case ENABLED__PROP: internalSetEnabled((boolean) value); break; case REGISTERED__PROP: internalSetRegistered((boolean) value); break; case REGISTER_MSG__PROP: internalSetRegisterMsg((String) value); break; @@ -501,6 +529,8 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. super.writeFields(out); out.name(ID__PROP); out.value(getId()); + out.name(USER_ID__PROP); + out.value(getUserId()); out.name(ENABLED__PROP); out.value(isEnabled()); out.name(REGISTERED__PROP); @@ -545,6 +575,7 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { switch (field) { case ID__PROP: setId(in.nextLong()); break; + case USER_ID__PROP: setUserId(in.nextLong()); break; case ENABLED__PROP: setEnabled(in.nextBoolean()); break; case REGISTERED__PROP: setRegistered(in.nextBoolean()); break; case REGISTER_MSG__PROP: setRegisterMsg(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckAnswerBot.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckAnswerBot.java new file mode 100644 index 00000000..b77bfc20 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckAnswerBot.java @@ -0,0 +1,108 @@ +package de.haumacher.phoneblock.ab.proto; + +public class CheckAnswerBot extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot} instance. + */ + public static de.haumacher.phoneblock.ab.proto.CheckAnswerBot create() { + return new de.haumacher.phoneblock.ab.proto.CheckAnswerBot(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot} type in JSON format. */ + public static final String CHECK_ANSWER_BOT__TYPE = "CheckAnswerBot"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + private long _id = 0L; + + /** + * Creates a {@link CheckAnswerBot} instance. + * + * @see de.haumacher.phoneblock.ab.proto.CheckAnswerBot#create() + */ + protected CheckAnswerBot() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.CHECK_ANSWER_BOT; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.CheckAnswerBot setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + @Override + public String jsonType() { + return CHECK_ANSWER_BOT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.CheckAnswerBot readCheckAnswerBot(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.CheckAnswerBot result = new de.haumacher.phoneblock.ab.proto.CheckAnswerBot(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckDynDns.java new file mode 100644 index 00000000..62ebf53e --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CheckDynDns.java @@ -0,0 +1,108 @@ +package de.haumacher.phoneblock.ab.proto; + +public class CheckDynDns extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.CheckDynDns} instance. + */ + public static de.haumacher.phoneblock.ab.proto.CheckDynDns create() { + return new de.haumacher.phoneblock.ab.proto.CheckDynDns(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.CheckDynDns} type in JSON format. */ + public static final String CHECK_DYN_DNS__TYPE = "CheckDynDns"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + private long _id = 0L; + + /** + * Creates a {@link CheckDynDns} instance. + * + * @see de.haumacher.phoneblock.ab.proto.CheckDynDns#create() + */ + protected CheckDynDns() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.CHECK_DYN_DNS; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.CheckDynDns setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + @Override + public String jsonType() { + return CHECK_DYN_DNS__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.CheckDynDns readCheckDynDns(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.CheckDynDns result = new de.haumacher.phoneblock.ab.proto.CheckDynDns(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerBot.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerBot.java new file mode 100644 index 00000000..ccacf354 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerBot.java @@ -0,0 +1,46 @@ +package de.haumacher.phoneblock.ab.proto; + +public class CreateAnswerBot extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.CreateAnswerBot} instance. + */ + public static de.haumacher.phoneblock.ab.proto.CreateAnswerBot create() { + return new de.haumacher.phoneblock.ab.proto.CreateAnswerBot(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.CreateAnswerBot} type in JSON format. */ + public static final String CREATE_ANSWER_BOT__TYPE = "CreateAnswerBot"; + + /** + * Creates a {@link CreateAnswerBot} instance. + * + * @see de.haumacher.phoneblock.ab.proto.CreateAnswerBot#create() + */ + protected CreateAnswerBot() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.CREATE_ANSWER_BOT; + } + + @Override + public String jsonType() { + return CREATE_ANSWER_BOT__TYPE; + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.CreateAnswerBot readCreateAnswerBot(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.CreateAnswerBot result = new de.haumacher.phoneblock.ab.proto.CreateAnswerBot(); + result.readContent(in); + return result; + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java index 42d413a8..d091fdc7 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/CreateAnswerbotResponse.java @@ -12,11 +12,21 @@ public static de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse create() /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse} type in JSON format. */ public static final String CREATE_ANSWERBOT_RESPONSE__TYPE = "CreateAnswerbotResponse"; + /** @see #getId() */ + public static final String ID__PROP = "id"; + /** @see #getUserName() */ public static final String USER_NAME__PROP = "userName"; + /** @see #getPassword() */ + public static final String PASSWORD__PROP = "password"; + + private long _id = 0L; + private String _userName = ""; + private String _password = ""; + /** * Creates a {@link CreateAnswerbotResponse} instance. * @@ -26,6 +36,23 @@ protected CreateAnswerbotResponse() { super(); } + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + public final String getUserName() { return _userName; } @@ -43,6 +70,23 @@ protected final void internalSetUserName(String value) { _userName = value; } + public final String getPassword() { + return _password; + } + + /** + * @see #getPassword() + */ + public de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse setPassword(String value) { + internalSetPassword(value); + return this; + } + + /** Internal setter for {@link #getPassword()} without chain call utility. */ + protected final void internalSetPassword(String value) { + _password = value; + } + @Override public String jsonType() { return CREATE_ANSWERBOT_RESPONSE__TYPE; @@ -50,7 +94,9 @@ public String jsonType() { private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( java.util.Arrays.asList( - USER_NAME__PROP)); + ID__PROP, + USER_NAME__PROP, + PASSWORD__PROP)); @Override public java.util.List properties() { @@ -60,7 +106,9 @@ public java.util.List properties() { @Override public Object get(String field) { switch (field) { + case ID__PROP: return getId(); case USER_NAME__PROP: return getUserName(); + case PASSWORD__PROP: return getPassword(); default: return null; } } @@ -68,7 +116,9 @@ public Object get(String field) { @Override public void set(String field, Object value) { switch (field) { + case ID__PROP: internalSetId((long) value); break; case USER_NAME__PROP: internalSetUserName((String) value); break; + case PASSWORD__PROP: internalSetPassword((String) value); break; } } @@ -87,14 +137,20 @@ public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.i @Override protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); out.name(USER_NAME__PROP); out.value(getUserName()); + out.name(PASSWORD__PROP); + out.value(getPassword()); } @Override protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { switch (field) { + case ID__PROP: setId(in.nextLong()); break; case USER_NAME__PROP: setUserName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case PASSWORD__PROP: setPassword(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; default: super.readField(in, field); } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DisableAnswerBot.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DisableAnswerBot.java new file mode 100644 index 00000000..a30699bb --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DisableAnswerBot.java @@ -0,0 +1,108 @@ +package de.haumacher.phoneblock.ab.proto; + +public class DisableAnswerBot extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot} instance. + */ + public static de.haumacher.phoneblock.ab.proto.DisableAnswerBot create() { + return new de.haumacher.phoneblock.ab.proto.DisableAnswerBot(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot} type in JSON format. */ + public static final String DISABLE_ANSWER_BOT__TYPE = "DisableAnswerBot"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + private long _id = 0L; + + /** + * Creates a {@link DisableAnswerBot} instance. + * + * @see de.haumacher.phoneblock.ab.proto.DisableAnswerBot#create() + */ + protected DisableAnswerBot() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.DISABLE_ANSWER_BOT; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.DisableAnswerBot setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + @Override + public String jsonType() { + return DISABLE_ANSWER_BOT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.DisableAnswerBot readDisableAnswerBot(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.DisableAnswerBot result = new de.haumacher.phoneblock.ab.proto.DisableAnswerBot(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnableAnswerBot.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnableAnswerBot.java new file mode 100644 index 00000000..863fa9c1 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnableAnswerBot.java @@ -0,0 +1,108 @@ +package de.haumacher.phoneblock.ab.proto; + +public class EnableAnswerBot extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.EnableAnswerBot} instance. + */ + public static de.haumacher.phoneblock.ab.proto.EnableAnswerBot create() { + return new de.haumacher.phoneblock.ab.proto.EnableAnswerBot(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.EnableAnswerBot} type in JSON format. */ + public static final String ENABLE_ANSWER_BOT__TYPE = "EnableAnswerBot"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + private long _id = 0L; + + /** + * Creates a {@link EnableAnswerBot} instance. + * + * @see de.haumacher.phoneblock.ab.proto.EnableAnswerBot#create() + */ + protected EnableAnswerBot() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.ENABLE_ANSWER_BOT; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.EnableAnswerBot setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + @Override + public String jsonType() { + return ENABLE_ANSWER_BOT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.EnableAnswerBot readEnableAnswerBot(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.EnableAnswerBot result = new de.haumacher.phoneblock.ab.proto.EnableAnswerBot(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnterHostName.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnterHostName.java new file mode 100644 index 00000000..31174049 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/EnterHostName.java @@ -0,0 +1,136 @@ +package de.haumacher.phoneblock.ab.proto; + +public class EnterHostName extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.EnterHostName} instance. + */ + public static de.haumacher.phoneblock.ab.proto.EnterHostName create() { + return new de.haumacher.phoneblock.ab.proto.EnterHostName(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.EnterHostName} type in JSON format. */ + public static final String ENTER_HOST_NAME__TYPE = "EnterHostName"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + /** @see #getHostName() */ + public static final String HOST_NAME__PROP = "hostName"; + + private long _id = 0L; + + private String _hostName = ""; + + /** + * Creates a {@link EnterHostName} instance. + * + * @see de.haumacher.phoneblock.ab.proto.EnterHostName#create() + */ + protected EnterHostName() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.ENTER_HOST_NAME; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.EnterHostName setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + public final String getHostName() { + return _hostName; + } + + /** + * @see #getHostName() + */ + public de.haumacher.phoneblock.ab.proto.EnterHostName setHostName(String value) { + internalSetHostName(value); + return this; + } + + /** Internal setter for {@link #getHostName()} without chain call utility. */ + protected final void internalSetHostName(String value) { + _hostName = value; + } + + @Override + public String jsonType() { + return ENTER_HOST_NAME__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP, + HOST_NAME__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + case HOST_NAME__PROP: return getHostName(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + case HOST_NAME__PROP: internalSetHostName((String) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.EnterHostName readEnterHostName(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.EnterHostName result = new de.haumacher.phoneblock.ab.proto.EnterHostName(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + out.name(HOST_NAME__PROP); + out.value(getHostName()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + case HOST_NAME__PROP: setHostName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDns.java new file mode 100644 index 00000000..8e9f6258 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDns.java @@ -0,0 +1,136 @@ +package de.haumacher.phoneblock.ab.proto; + +public class SetupDynDns extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.SetupDynDns} instance. + */ + public static de.haumacher.phoneblock.ab.proto.SetupDynDns create() { + return new de.haumacher.phoneblock.ab.proto.SetupDynDns(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.SetupDynDns} type in JSON format. */ + public static final String SETUP_DYN_DNS__TYPE = "SetupDynDns"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + /** @see #getHostName() */ + public static final String HOST_NAME__PROP = "hostName"; + + private long _id = 0L; + + private String _hostName = ""; + + /** + * Creates a {@link SetupDynDns} instance. + * + * @see de.haumacher.phoneblock.ab.proto.SetupDynDns#create() + */ + protected SetupDynDns() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.SETUP_DYN_DNS; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.SetupDynDns setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + public final String getHostName() { + return _hostName; + } + + /** + * @see #getHostName() + */ + public de.haumacher.phoneblock.ab.proto.SetupDynDns setHostName(String value) { + internalSetHostName(value); + return this; + } + + /** Internal setter for {@link #getHostName()} without chain call utility. */ + protected final void internalSetHostName(String value) { + _hostName = value; + } + + @Override + public String jsonType() { + return SETUP_DYN_DNS__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP, + HOST_NAME__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + case HOST_NAME__PROP: return getHostName(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + case HOST_NAME__PROP: internalSetHostName((String) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.SetupDynDns readSetupDynDns(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.SetupDynDns result = new de.haumacher.phoneblock.ab.proto.SetupDynDns(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + out.name(HOST_NAME__PROP); + out.value(getHostName()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + case HOST_NAME__PROP: setHostName(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDnsResponse.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDnsResponse.java new file mode 100644 index 00000000..ffb9d153 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupDynDnsResponse.java @@ -0,0 +1,158 @@ +package de.haumacher.phoneblock.ab.proto; + +public class SetupDynDnsResponse extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.data.ReflectiveDataObject { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse} instance. + */ + public static de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse create() { + return new de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse} type in JSON format. */ + public static final String SETUP_DYN_DNS_RESPONSE__TYPE = "SetupDynDnsResponse"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + /** @see #getDyndnsUser() */ + public static final String DYNDNS_USER__PROP = "dyndnsUser"; + + /** @see #getDyndnsPassword() */ + public static final String DYNDNS_PASSWORD__PROP = "dyndnsPassword"; + + private long _id = 0L; + + private String _dyndnsUser = ""; + + private String _dyndnsPassword = ""; + + /** + * Creates a {@link SetupDynDnsResponse} instance. + * + * @see de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse#create() + */ + protected SetupDynDnsResponse() { + super(); + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + public final String getDyndnsUser() { + return _dyndnsUser; + } + + /** + * @see #getDyndnsUser() + */ + public de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse setDyndnsUser(String value) { + internalSetDyndnsUser(value); + return this; + } + + /** Internal setter for {@link #getDyndnsUser()} without chain call utility. */ + protected final void internalSetDyndnsUser(String value) { + _dyndnsUser = value; + } + + public final String getDyndnsPassword() { + return _dyndnsPassword; + } + + /** + * @see #getDyndnsPassword() + */ + public de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse setDyndnsPassword(String value) { + internalSetDyndnsPassword(value); + return this; + } + + /** Internal setter for {@link #getDyndnsPassword()} without chain call utility. */ + protected final void internalSetDyndnsPassword(String value) { + _dyndnsPassword = value; + } + + @Override + public String jsonType() { + return SETUP_DYN_DNS_RESPONSE__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP, + DYNDNS_USER__PROP, + DYNDNS_PASSWORD__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + case DYNDNS_USER__PROP: return getDyndnsUser(); + case DYNDNS_PASSWORD__PROP: return getDyndnsPassword(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + case DYNDNS_USER__PROP: internalSetDyndnsUser((String) value); break; + case DYNDNS_PASSWORD__PROP: internalSetDyndnsPassword((String) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse readSetupDynDnsResponse(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse result = new de.haumacher.phoneblock.ab.proto.SetupDynDnsResponse(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + out.name(DYNDNS_USER__PROP); + out.value(getDyndnsUser()); + out.name(DYNDNS_PASSWORD__PROP); + out.value(getDyndnsPassword()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + case DYNDNS_USER__PROP: setDyndnsUser(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case DYNDNS_PASSWORD__PROP: setDyndnsPassword(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + default: super.readField(in, field); + } + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java new file mode 100644 index 00000000..d2529b7a --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java @@ -0,0 +1,98 @@ +package de.haumacher.phoneblock.ab.proto; + +public abstract class SetupRequest extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.data.ReflectiveDataObject { + + /** Type codes for the {@link de.haumacher.phoneblock.ab.proto.SetupRequest} hierarchy. */ + public enum TypeKind { + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.CreateAnswerBot}. */ + CREATE_ANSWER_BOT, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.EnterHostName}. */ + ENTER_HOST_NAME, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.SetupDynDns}. */ + SETUP_DYN_DNS, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.CheckDynDns}. */ + CHECK_DYN_DNS, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.EnableAnswerBot}. */ + ENABLE_ANSWER_BOT, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot}. */ + DISABLE_ANSWER_BOT, + + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot}. */ + CHECK_ANSWER_BOT, + ; + + } + + /** Visitor interface for the {@link de.haumacher.phoneblock.ab.proto.SetupRequest} hierarchy.*/ + public interface Visitor { + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.CreateAnswerBot}.*/ + R visit(de.haumacher.phoneblock.ab.proto.CreateAnswerBot self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.EnterHostName}.*/ + R visit(de.haumacher.phoneblock.ab.proto.EnterHostName self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.SetupDynDns}.*/ + R visit(de.haumacher.phoneblock.ab.proto.SetupDynDns self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.CheckDynDns}.*/ + R visit(de.haumacher.phoneblock.ab.proto.CheckDynDns self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.EnableAnswerBot}.*/ + R visit(de.haumacher.phoneblock.ab.proto.EnableAnswerBot self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot}.*/ + R visit(de.haumacher.phoneblock.ab.proto.DisableAnswerBot self, A arg) throws E; + + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot}.*/ + R visit(de.haumacher.phoneblock.ab.proto.CheckAnswerBot self, A arg) throws E; + + } + + /** + * Creates a {@link SetupRequest} instance. + */ + protected SetupRequest() { + super(); + } + + /** The type code of this instance. */ + public abstract TypeKind kind(); + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.SetupRequest readSetupRequest(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.SetupRequest result; + in.beginArray(); + String type = in.nextString(); + switch (type) { + case CreateAnswerBot.CREATE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.CreateAnswerBot.readCreateAnswerBot(in); break; + case EnterHostName.ENTER_HOST_NAME__TYPE: result = de.haumacher.phoneblock.ab.proto.EnterHostName.readEnterHostName(in); break; + case SetupDynDns.SETUP_DYN_DNS__TYPE: result = de.haumacher.phoneblock.ab.proto.SetupDynDns.readSetupDynDns(in); break; + case CheckDynDns.CHECK_DYN_DNS__TYPE: result = de.haumacher.phoneblock.ab.proto.CheckDynDns.readCheckDynDns(in); break; + case EnableAnswerBot.ENABLE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.EnableAnswerBot.readEnableAnswerBot(in); break; + case DisableAnswerBot.DISABLE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.DisableAnswerBot.readDisableAnswerBot(in); break; + case CheckAnswerBot.CHECK_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.CheckAnswerBot.readCheckAnswerBot(in); break; + default: in.skipValue(); result = null; break; + } + in.endArray(); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + out.beginArray(); + out.value(jsonType()); + writeContent(out); + out.endArray(); + } + + /** Accepts the given visitor. */ + public abstract R visit(Visitor v, A arg) throws E; + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto index 0c4f71b7..a81303de 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto @@ -6,14 +6,60 @@ option NoListener; option NoBinary; option DartLib = "../../../../phoneblock_answerbot_ui/lib/proto.dart"; +abstract message SetupRequest { + +} + message CreateAnswerbotResponse { + long id; string userName; + string password; +} + +message CreateAnswerBot extends SetupRequest { + // No data. +} + +message EnterHostName extends SetupRequest { + long id; + string hostName; +} + +message SetupDynDns extends SetupRequest { + long id; + string hostName; +} + +message SetupDynDnsResponse { + long id; + string dyndnsUser; + string dyndnsPassword; +} + +message CheckDynDns extends SetupRequest { + long id; +} + +message EnableAnswerBot extends SetupRequest { + long id; } +message DisableAnswerBot extends SetupRequest { + long id; +} + +message CheckAnswerBot extends SetupRequest { + long id; +} + + /** Information of a single answer bot. */ message AnswerbotInfo { /** The primary key identifier of this bot. */ long id; + + /** The ID of the owning user. */ + long userId; /** Whether the bot is enabled (registration is active). */ bool enabled; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java index 396758e9..b277afbb 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/DynIpServlet.java @@ -77,7 +77,7 @@ protected void tryGet(HttpServletRequest req, HttpServletResponse resp) throws I processAddress(update, req.getParameter("ip6")); if (changed(settings.getIpv4(), update.getIpv4()) || changed(settings.getIpv6(), update.getIpv6()) || now - settings.getUpdated() > ONE_DAY) { - users.updateDynDny(settings.getUserId(), update.getIpv4(), update.getIpv6(), now); + users.updateDynDns(settings.getId(), update.getIpv4(), update.getIpv6(), now); session.commit(); LOG.info("Updated IP address for: " + user); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java index 7d739c1d..95b9d128 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotDynDns.java @@ -15,7 +15,8 @@ public class DBAnswerBotDynDns extends AnswerBotDynDns { /** * Creates a {@link DBAnswerBotDynDns}. */ - public DBAnswerBotDynDns(long userId, long created, long updated, String user, String passwd, String ip4, String ip6) { + public DBAnswerBotDynDns(long id, long userId, long created, long updated, String user, String passwd, String ip4, String ip6) { + setId(id); setUserId(userId); setCreated(created); setUpdated(updated); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java index da8fee5c..9e1a7ea2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBAnswerBotSip.java @@ -13,7 +13,8 @@ public class DBAnswerBotSip extends AnswerBotSip { /** * Creates a {@link DBAnswerBotSip}. */ - public DBAnswerBotSip(long userId, String host, String ip4, String ip6, String registrar, String realm, String userName, String passwd) { + public DBAnswerBotSip(long id, long userId, String host, String ip4, String ip6, String registrar, String realm, String userName, String passwd) { + setId(id); setUserId(userId); setHost(host); setIpv4(ip4); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index c1c8b7d5..922edea8 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -113,40 +113,48 @@ public interface Users { @Update("update CALLERS set CALLS=CALLS + 1, LASTUPDATE=#{now} where USERID=#{userId} and PHONE=#{phone}") int addCall(long userId, String phone, long now); - @Select("select USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where DYNDNS_USER=#{user}") - DBAnswerBotDynDns getDynDns(String user); - - @Select("select USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where USERID=#{userId}") - DBAnswerBotDynDns getDynDnsByUserId(long userId); + @Select("select ABID, USERID, CREATED, UPDATED, DYNDNS_USER, DYNDNS_PASSWD, IP4, IP6 from ANSWERBOT_DYNDNS where DYNDNS_USER=#{dynDnsUser}") + DBAnswerBotDynDns getDynDns(String dynDnsUser); + + @Insert("insert into ANSWERBOT_DYNDNS (ABID, USERID, CREATED, DYNDNS_USER, DYNDNS_PASSWD) values (#{abId}, #{userId}, #{now}, #{dynDnsUser}, #{dynDnsPassword})") + int setupDynDns(long abId, long userId, long now, String dynDnsUser, String dynDnsPassword); /** * Updates the user's dynamic IP address. */ - @Insert("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where USERID=#{userId}") - void updateDynDny(long userId, String ip4, String ip6, long updated); + @Update("update ANSWERBOT_DYNDNS set IP4=#{ip4}, IP6=#{ip6}, UPDATED=#{updated} where ABID=#{abId}") + void updateDynDns(long abId, String ip4, String ip6, long updated); - @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + - "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + @Select("select s.ID, s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.ABID=s.ID " + "where s.ENABLED = true and (s.UPDATED > #{since} or d.UPDATED > #{since})") List getEnabledAnswerBots(long since); - @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTERED=false, REGISTER_MSG=NULL where USERID=#{userId}") - void enableAnswerBot(long userId, boolean enabled, long updated); - - @Insert("insert into ANSWERBOT_SIP (USERID, USERNAME, PASSWD, CREATED, UPDATED) values (#{userId}, #{userName}, #{password}, #{now}, #{now})") - int createAnswerBot(long userId, String userName, String password, long now); + @Update("update ANSWERBOT_SIP set ENABLED=#{enabled}, UPDATED=#{updated}, REGISTERED=false, REGISTER_MSG=NULL where ID=#{id}") + void enableAnswerBot(long id, boolean enabled, long updated); - @Select("select s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + - "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + @Select("select s.ID, s.USERID, s.HOST, d.IP4, d.IP6, s.REGISTRAR, s.REALM, s.USERNAME, s.PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.ABID=s.ID " + "where s.USERNAME = #{userName}") - DBAnswerBotSip getAnswerBot(String userName); + DBAnswerBotSip getAnswerBotBySipUser(String userName); - @Select("select s.USERID, s.ENABLED, s.REGISTRAR, s.HOST, d.IP4, d.IP6, s.REALM, s.REGISTERED, s.REGISTER_MSG, s.CALLS_ACCEPTED, s.USERNAME, s.PASSWD, d.DYNDNS_USER, d.DYNDNS_PASSWD from ANSWERBOT_SIP s " + - "left outer join ANSWERBOT_DYNDNS d on d.USERID=s.USERID " + + @Select("select s.ID, s.USERID, s.ENABLED, s.REGISTRAR, s.HOST, d.IP4, d.IP6, s.REALM, s.REGISTERED, s.REGISTER_MSG, s.CALLS_ACCEPTED, s.USERNAME, s.PASSWD, d.DYNDNS_USER, d.DYNDNS_PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.ABID=s.ID " + "where s.USERID= #{userId}") List getAnswerBots(long userId); - @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, REGISTER_MSG=#{message} where USERID=#{userId} and (REGISTER_MSG is null or not REGISTER_MSG=#{message} or not REGISTERED=#{registered})") - int updateSipRegistration(long userId, boolean registered, String message); + @Select("select s.ID, s.USERID, s.ENABLED, s.REGISTRAR, s.HOST, d.IP4, d.IP6, s.REALM, s.REGISTERED, s.REGISTER_MSG, s.CALLS_ACCEPTED, s.USERNAME, s.PASSWD, d.DYNDNS_USER, d.DYNDNS_PASSWD from ANSWERBOT_SIP s " + + "left outer join ANSWERBOT_DYNDNS d on d.ABID=s.ID " + + "where s.ID= #{id}") + DBAnswerbotInfo getAnswerBot(long id); + + @Update("update ANSWERBOT_SIP set REGISTERED=#{registered}, REGISTER_MSG=#{message} where ID=#{id} and ((REGISTER_MSG is null) or (not (REGISTER_MSG=#{message})) or (not (REGISTERED=#{registered})))") + int updateSipRegistration(long id, boolean registered, String message); + + @Delete("delete from ANSWERBOT_DYNDNS where ABID=#{abId}") + int answerbotDeleteDynDns(long abId); + + @Update("update ANSWERBOT_SIP set HOST=#{host} where ID=#{id}") + void answerbotEnterHostName(long id, String host); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql index 00ef20c7..7e828e9d 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/db-schema.sql @@ -93,26 +93,35 @@ CREATE TABLE USERS ( CREATE INDEX USERS_CLIENTNAME_IDX ON USERS (CLIENTNAME,EXTID); CREATE UNIQUE INDEX USERS_UN_INDEX_4 ON USERS (LOGIN); --- PUBLIC.ANSWERBOT definition + +-- PUBLIC.ANSWERBOT_DYNDNS definition -- Drop table --- DROP TABLE ANSWERBOT; +-- DROP TABLE ANSWERBOT_DYNDNS; CREATE TABLE ANSWERBOT_DYNDNS ( USERID BIGINT NOT NULL, + ABID BIGINT DEFAULT 0 NOT NULL, CREATED BIGINT DEFAULT 0 NOT NULL, UPDATED BIGINT DEFAULT 0 NOT NULL, - + DYNDNS_USER CHARACTER VARYING(64) NOT NULL, DYNDNS_PASSWD CHARACTER VARYING(64) NOT NULL, IP4 CHARACTER VARYING(64) DEFAULT '' NOT NULL, IP6 CHARACTER VARYING(64) DEFAULT '' NOT NULL, - - CONSTRAINT ANSWERBOT_DYNDNS_PK PRIMARY KEY (USERID) + + CONSTRAINT ANSWERBOT_DYNDNS_SIP_FK PRIMARY KEY (ABID) ); + CREATE UNIQUE INDEX ANSWERBOT_DYNDNS_USER ON ANSWERBOT_DYNDNS (DYNDNS_USER); +CREATE INDEX ANSWERBOT_DYNDNS_USER_FK_INDEX_A ON ANSWERBOT_DYNDNS (USERID); + +-- PUBLIC.ANSWERBOT_DYNDNS foreign keys + +ALTER TABLE PUBLIC.ANSWERBOT_DYNDNS ADD CONSTRAINT ANSWERBOT_DYNDNS_SIP_FK FOREIGN KEY (ABID) REFERENCES ANSWERBOT_SIP(ID) ON DELETE CASCADE ON UPDATE RESTRICT; +ALTER TABLE PUBLIC.ANSWERBOT_DYNDNS ADD CONSTRAINT ANSWERBOT_DYNDNS_USER_FK FOREIGN KEY (USERID) REFERENCES USERS(ID) ON DELETE CASCADE ON UPDATE RESTRICT; CREATE TABLE ANSWERBOT_SIP ( diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java index 82ca5acd..1f5c6fce 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotDynDns.java @@ -31,16 +31,16 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotDynDns create() { static final int ANSWER_BOT_DYN_DNS__TYPE_ID = 1; /** Identifier for the property {@link #getDyndnsUser()} in binary format. */ - static final int DYNDNS_USER__ID = 4; + static final int DYNDNS_USER__ID = 5; /** Identifier for the property {@link #getDynDnsPasswd()} in binary format. */ - static final int DYN_DNS_PASSWD__ID = 5; + static final int DYN_DNS_PASSWD__ID = 6; /** Identifier for the property {@link #getIpv4()} in binary format. */ - static final int IPV_4__ID = 6; + static final int IPV_4__ID = 7; /** Identifier for the property {@link #getIpv6()} in binary format. */ - static final int IPV_6__ID = 7; + static final int IPV_6__ID = 8; private String _dyndnsUser = ""; @@ -148,6 +148,12 @@ protected final void internalSetIpv6(String value) { _ipv6 = value; } + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setId(long value) { + internalSetId(value); + return this; + } + @Override public de.haumacher.phoneblock.db.settings.AnswerBotDynDns setUserId(long value) { internalSetUserId(value); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java index c663a637..b58a17f2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSetting.java @@ -28,6 +28,9 @@ public interface Visitor { } + /** @see #getId() */ + public static final String ID__PROP = "id"; + /** @see #getUserId() */ public static final String USER_ID__PROP = "userId"; @@ -37,14 +40,19 @@ public interface Visitor { /** @see #getUpdated() */ public static final String UPDATED__PROP = "updated"; + /** Identifier for the property {@link #getId()} in binary format. */ + static final int ID__ID = 1; + /** Identifier for the property {@link #getUserId()} in binary format. */ - static final int USER_ID__ID = 1; + static final int USER_ID__ID = 2; /** Identifier for the property {@link #getCreated()} in binary format. */ - static final int CREATED__ID = 2; + static final int CREATED__ID = 3; /** Identifier for the property {@link #getUpdated()} in binary format. */ - static final int UPDATED__ID = 3; + static final int UPDATED__ID = 4; + + private long _id = 0L; private long _userId = 0L; @@ -62,6 +70,27 @@ protected AnswerBotSetting() { /** The type code of this instance. */ public abstract TypeKind kind(); + /** + * ID of the answer bot. + */ + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.db.settings.AnswerBotSetting setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _listener.beforeSet(this, ID__PROP, value); + _id = value; + } + /** * ID of the PhoneBlock user. */ @@ -149,6 +178,7 @@ protected final void internalUnregisterListener(de.haumacher.msgbuf.observer.Lis private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( java.util.Arrays.asList( + ID__PROP, USER_ID__PROP, CREATED__PROP, UPDATED__PROP)); @@ -161,6 +191,7 @@ public java.util.List properties() { @Override public Object get(String field) { switch (field) { + case ID__PROP: return getId(); case USER_ID__PROP: return getUserId(); case CREATED__PROP: return getCreated(); case UPDATED__PROP: return getUpdated(); @@ -171,6 +202,7 @@ public Object get(String field) { @Override public void set(String field, Object value) { switch (field) { + case ID__PROP: internalSetId((long) value); break; case USER_ID__PROP: internalSetUserId((long) value); break; case CREATED__PROP: internalSetCreated((long) value); break; case UPDATED__PROP: internalSetUpdated((long) value); break; @@ -202,6 +234,8 @@ public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.i @Override protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); out.name(USER_ID__PROP); out.value(getUserId()); out.name(CREATED__PROP); @@ -213,6 +247,7 @@ protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java. @Override protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { switch (field) { + case ID__PROP: setId(in.nextLong()); break; case USER_ID__PROP: setUserId(in.nextLong()); break; case CREATED__PROP: setCreated(in.nextLong()); break; case UPDATED__PROP: setUpdated(in.nextLong()); break; @@ -240,6 +275,8 @@ public final void writeTo(de.haumacher.msgbuf.binary.DataWriter out) throws java * @throws java.io.IOException If writing fails. */ protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { + out.name(ID__ID); + out.value(getId()); out.name(USER_ID__ID); out.value(getUserId()); out.name(CREATED__ID); @@ -275,6 +312,7 @@ protected final void readContent(de.haumacher.msgbuf.binary.DataReader in) throw /** Consumes the value for the field with the given ID and assigns its value. */ protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { switch (field) { + case ID__ID: setId(in.nextLong()); break; case USER_ID__ID: setUserId(in.nextLong()); break; case CREATED__ID: setCreated(in.nextLong()); break; case UPDATED__ID: setUpdated(in.nextLong()); break; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java index ef059936..3555460b 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/AnswerBotSip.java @@ -49,37 +49,37 @@ public static de.haumacher.phoneblock.db.settings.AnswerBotSip create() { static final int ANSWER_BOT_SIP__TYPE_ID = 2; /** Identifier for the property {@link #getHost()} in binary format. */ - static final int HOST__ID = 4; + static final int HOST__ID = 5; /** Identifier for the property {@link #getIpv4()} in binary format. */ - static final int IPV_4__ID = 5; + static final int IPV_4__ID = 6; /** Identifier for the property {@link #getIpv6()} in binary format. */ - static final int IPV_6__ID = 6; + static final int IPV_6__ID = 7; /** Identifier for the property {@link #getRegistrar()} in binary format. */ - static final int REGISTRAR__ID = 7; + static final int REGISTRAR__ID = 8; /** Identifier for the property {@link #getRealm()} in binary format. */ - static final int REALM__ID = 8; + static final int REALM__ID = 9; /** Identifier for the property {@link #getUserName()} in binary format. */ - static final int USER_NAME__ID = 9; + static final int USER_NAME__ID = 10; /** Identifier for the property {@link #getPasswd()} in binary format. */ - static final int PASSWD__ID = 10; + static final int PASSWD__ID = 11; /** Identifier for the property {@link #isRegistered()} in binary format. */ - static final int REGISTERED__ID = 11; + static final int REGISTERED__ID = 12; /** Identifier for the property {@link #getLastRegister()} in binary format. */ - static final int LAST_REGISTER__ID = 12; + static final int LAST_REGISTER__ID = 13; /** Identifier for the property {@link #getRegisterError()} in binary format. */ - static final int REGISTER_ERROR__ID = 13; + static final int REGISTER_ERROR__ID = 14; /** Identifier for the property {@link #getCallsAccepted()} in binary format. */ - static final int CALLS_ACCEPTED__ID = 14; + static final int CALLS_ACCEPTED__ID = 15; private String _host = ""; @@ -348,6 +348,12 @@ protected final void internalSetCallsAccepted(int value) { _callsAccepted = value; } + @Override + public de.haumacher.phoneblock.db.settings.AnswerBotSip setId(long value) { + internalSetId(value); + return this; + } + @Override public de.haumacher.phoneblock.db.settings.AnswerBotSip setUserId(long value) { internalSetUserId(value); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto index 416fb594..7373057b 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/settings/settings.proto @@ -32,6 +32,9 @@ message UserSettings { /** Common options of answer bot settings. */ abstract message AnswerBotSetting { + /** ID of the answer bot. */ + long id; + /** ID of the PhoneBlock user. */ long userId; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java index 5e5d65dd..8146bf0e 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/jmx/AppState.java @@ -103,7 +103,7 @@ public void stopSipService() { @Override public void triggerAnswerBotRegistration(String userName, boolean enabled) { try { - _sip.enableAnwserBot(userName, enabled); + _sip.enableAnwserBot(userName); } catch (Exception ex) { LOG.error("Failed to change answer bot state for user '" + userName + "'.", ex); throw new RuntimeException("Failed to change answer bot state for user '" + userName + "': " + ex.getMessage()); diff --git a/phoneblock/src/main/webapp/WEB-INF/web.xml b/phoneblock/src/main/webapp/WEB-INF/web.xml index 799e09a5..ce2d2ec8 100644 --- a/phoneblock/src/main/webapp/WEB-INF/web.xml +++ b/phoneblock/src/main/webapp/WEB-INF/web.xml @@ -40,10 +40,12 @@ CorsFilter org.apache.catalina.filters.CorsFilter + cors.allowed.methods GET,POST,HEAD,OPTIONS,PUT diff --git a/phoneblock_answerbot_ui/lib/main.dart b/phoneblock_answerbot_ui/lib/main.dart index 7bddfbd3..2700b098 100644 --- a/phoneblock_answerbot_ui/lib/main.dart +++ b/phoneblock_answerbot_ui/lib/main.dart @@ -13,9 +13,7 @@ import 'package:url_launcher/url_launcher.dart'; String basePath = getBasePath(); String getBasePath() { - if (kDebugMode) { - return "https://phoneblock.net/pb-test"; - } + return "https://phoneblock.net/pb-test"; String protocol = window.location.protocol; String host = window.location.host; @@ -75,6 +73,7 @@ class MyApp extends StatelessWidget { } class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); @override @@ -82,6 +81,14 @@ class HomeScreen extends StatelessWidget { return Scaffold( appBar: AppBar( title: const Text("Deine PhoneBlock Anrufbeantworter"), + actions: [ + IconButton( + onPressed: () { + // TODO + }, + icon: const Icon(Icons.refresh), + ) + ], ), body: const AnswerBotList(), floatingActionButton: FloatingActionButton( @@ -93,15 +100,7 @@ class HomeScreen extends StatelessWidget { } void _createAnswerBot(BuildContext context) async { - var response = await http.post(Uri.parse('$basePath/ab/create'), - encoding: const Utf8Codec(), - headers: { - "Content-Type": "application/json", - if (kDebugMode) - 'Authorization': authHeader, - }, - body: "{}", - ); + http.Response response = await sendRequest(CreateAnswerBot()); if (!context.mounted) return; if (response.statusCode != 200) { @@ -113,22 +112,406 @@ class HomeScreen extends StatelessWidget { } Widget _setupAnswerBot(BuildContext context, CreateAnswerbotResponse creation) { + return BotSetupForm(creation); + } +} + +Future sendRequest(SetupRequest request) async { + var response = http.post(Uri.parse('$basePath/ab/setup'), + encoding: const Utf8Codec(), + headers: { + "Content-Type": "application/json", + if (kDebugMode) + 'Authorization': authHeader, + }, + body: request.toString(), + ); + return response; +} + +class BotSetupForm extends StatefulWidget { + final CreateAnswerbotResponse creation; + + const BotSetupForm(this.creation, {super.key}); + + @override + State createState() { + return BotSetupState(); + } +} + +enum SetupState { + domainSetup, + dynDnsSetup, + enableSip, + finish +} + +class BotSetupState extends State { + final _formKey = GlobalKey(); + + SetupState state = SetupState.domainSetup; + bool phoneblockDns = false; + final _hostName = TextEditingController(); + + SetupDynDnsResponse? dynDns; + + @override + Widget build(BuildContext context) { + switch (state) { + case SetupState.domainSetup: + return domainSetup(context); + case SetupState.dynDnsSetup: + return dynDnsSetup(context); + case SetupState.enableSip: + return enableSip(context); + case SetupState.finish: + return sipFinish(context); + default: return showError(context); + } + } + + Widget domainSetup(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Anrufbeantworter einrichten"), ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(creation.userName), - ], + body: Form( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + children: [ + const Expanded( + child: Text("PhoneBlock-DynDNS benutzen") + ), + Switch( + thumbIcon: switchIcon, + value: phoneblockDns, + onChanged: (bool value) { + setState(() { + phoneblockDns = value; + }); + }, + ) + ], + ), + ), + + hintText("PhoneBlock muss die Internet-Adresse Deiner " + "Fritz!Box kennen, um den Anrufbeantworter an Deiner " + "Fritz!Box anmelden zu können. Wenn Deine Fritz!Box " + "über MyFRITZ! oder DynDNS bereits einen Domainnamen hat, " + "kannst Du diesen verwenden. Wenn nicht kannst Du ganz einfach " + "DynDNS von PhoneBlock einrichten. Aktiviere dann diesen Schalter."), + + if (phoneblockDns) ...[ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ElevatedButton( + onPressed: () async { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Richte PhoneBlock DynDNS ein.')), + ); + + http.Response response = await sendRequest(SetupDynDns()..id=widget.creation.id); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response, 'Einrichtung Fehlgeschlagen', "DynDNS kann nicht eingerichtet werden: ${response.body}"); + } + + setState(() { + dynDns = SetupDynDnsResponse.fromString(response.body); + state = SetupState.dynDnsSetup; + }); + }, + child: + const Text("PhoneBlock-DynDNS einrichten") + ), + ) + ] + else ...[ + TextFormField( + decoration: const InputDecoration( + labelText: 'Domainname', + hintText: "Domainname Deiner Fritz!Box (entweder MyFRITZ!-Adresse, oder DynDNS Domainname)" + ), + controller: _hostName, + validator: (value) { + if (value == null || value.isEmpty) { + return "Eingabe darf nicht leer sein."; + } + if (!hostNamePattern.hasMatch(value)) { + return "Kein gültiger Domain-Name."; + } + if (value.length > 255) { + return "Der Domain-Name ist zu lang."; + } + return null; + }, + onTapOutside: (evt) => _formKey.currentState!.validate(), + ), + hintText("Wenn Du schon DynDNS eines anderen Anbieters in Deiner Fritz!Box eingerichtet " + "hast, musst Du hier den Domainnamen Deiner Fritz!Box eingeben (Unter Internet > Freigaben > DynDNS). " + "Wenn Du einen My!Fritz-Account hast (Internet > MyFRITZ!-Konto), kannst Du hier auch Deine " + "MyFRITZ!-Adresse angeben (z.B. z4z...l4n.myfritz.net)."), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ElevatedButton( + onPressed: () async { + if (_formKey.currentState!.validate()) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Überprüfe Domainnamen.')), + ); + + http.Response response = await sendRequest( + EnterHostName() + ..id=widget.creation.id + ..hostName=_hostName.text); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response, 'Einrichtung Fehlgeschlagen', "Domainname wurde nicht akzeptiert: ${response.body}"); + } + + setState(() { + state = SetupState.enableSip; + }); + } + }, + child: + const Text("Domainnamen überprüfen")), + ) + ] + ], + ), ), ), ); } + + Widget dynDnsSetup(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("DynDNS einrichten"), + ), + body: Form( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + hintText("Öffne in Deinen Fritz!Box-Einstellungen die Seite die " + "Internet > Freigaben > DynDNS und trage die hier angegebenen Informationen ein."), + + const InfoField('Update-URL', + "https://phoneblock.net/phoneblock/api/dynip?user=&passwd=&ip4=&ip6=", + key: Key("dynip.updateurl"), + help: "Die URL, die Deine Fritz!Box aufruft, um PhoneBlock ihre Internetadresse bekannt zu geben. " + "Gib die URL genau so ein, wie sie hier geschrieben ist. Ersetze nicht die Werte in den spitzen " + "Klammern, das macht Deine Fritz!Box beim Aufruf automatisch. Nutze am besten die Kopierfuntion, " + "um die Werte zu übernehmen."), + InfoField('Domainname', "${dynDns!.dyndnsUser}.box.phoneblock.net", + key: Key("dynip.domainname"), + help: "Dieser Domainname kann später nicht öffentlich aufgelöst werden. Deine Internetadresse wird " + "ausschließlich mit PhoneBlock geteilt."), + InfoField('Benutzername', dynDns!.dyndnsUser, + key: Key("dynip.username"), + help: "Der Benutzername, mit dem sich Deine Fritz!Box bei " + "PhoneBlock anmeldet, um ihre Internetadresse bekannt zu geben."), + InfoField('Kennwort', dynDns!.dyndnsPassword, + key: Key("dynip.password"), + help: "Das Kennwort, mit dem sich Deine Fritz!Box bei PhoneBlock anmeldet, " + "um ihre Internetadresse bekannt zu geben. Aus Sicherheitsgründen kannst Du kein eigenes Kennwort " + "eingeben, sondern musst das von PhoneBlock sicher generierte Kennwort verwenden. "), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ElevatedButton( + onPressed: () async { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Überprüfe DynDNS Einrichtung.')), + ); + + http.Response response = await sendRequest( + CheckDynDns() + ..id=widget.creation.id + ); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response, 'Einrichtung Fehlgeschlagen', "DynDNS nicht aktuell: ${response.body}"); + } + + setState(() { + state = SetupState.enableSip; + }); + }, + child: const Text("DynDNS überprüfen")), + ) + ] + ), + ), + ), + ); + } + + Widget enableSip(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Anrufbeantworter erstellen"), + ), + body: Form( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + hintText( + "1. Öffne in Deinen Fritz!Box-Einstellungen die Seite die " + "Telefonie > Telefoniegeräte und klicke auf den Knopf \"Neues Gerät einrichten\". " + "Wähle die Option \"Telefon (mit und ohne Anrufbeantworter)\" und klicke auf \"Weiter\"."), + hintText( + "2. Wähle die Option \"Telefon (mit und ohne Anrufbeantworter)\" und klicke auf \"Weiter\"."), + hintText( + "3. Wähle die Option \"LAN/WLAN (IP-Telefon)\", gib dem Telefon den Namen \"PhoneBlock\" und klicke auf \"Weiter\"."), + + InfoField('Benutzername', widget.creation.userName, + key: Key("sip.username"), + help: "Der Benutzername, mit dem sich der PhoneBlock-Anrufbeantworter an Deiner Fritz!Box anmeldet."), + InfoField('Kennwort', widget.creation.password, + key: Key("sip.password"), + help: "Das Kennwort, das der PhoneBlock-Anrufbeantworter " + "nutzt, um sich an Deiner Fritz!Box anzumelden. " + "PhoneBlock hat für Dich ein sicheres Kennwort generiert. "), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ElevatedButton( + onPressed: () async { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Der PhoneBlock-Anrufbeantworter meldet sich an...')), + ); + + { + http.Response response = await sendRequest( + EnableAnswerBot() + ..id=widget.creation.id + ); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response, 'Anmeldung des Anrufbeantworters fehlgeschlagen', + "Registrierung fehlgeschlagen: ${response.body}"); + } + } + + int sleep = 500; + int n = 0; + while (true) { + http.Response response = await sendRequest( + CheckAnswerBot() + ..id=widget.creation.id + ); + if (!context.mounted) return; + + if (response.statusCode == 200) { + break; + } else { + if (n++ == 4) { + return showErrorDialog(context, response, 'Anmeldung des Anrufbeantworters fehlgeschlagen', + "Registrierung fehlgeschlagen: ${response.body}"); + } else { + await Future.delayed(Duration(milliseconds: sleep)); + sleep = sleep * 2; + } + } + } + + setState(() { + state = SetupState.finish; + }); + }, + child: const Text("Anrufbeantworter anmelden")), + ) + ] + ), + ), + ), + ); + } + + Widget sipFinish(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Anrufbeantworter angemeldet"), + ), + body: Form( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Dein PhoneBlock-Anrufbeantworter ist erfolgreich angemeldet. " + "Die nächsten Spam-Anrufer können sich jetzt ausgibig mit " + "PhoneBlock unterhalten. Wenn Du den PhoneBlock-Anrufbeantworter selber testen möchtest," + "dann wähle die interne Rufnummer des von Dir eingerichteten Telefoniegerätes \"PhoneBlock\". " + "Die interne Nummer beginnt i.d.R. mit \"**\"."), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text("Schließen")), + ) + ] + ), + ), + ), + ); + } + + Widget showError(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Fehler"), + ), + body: const Text("") + ); + } + + hintText(String hint) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(right: 8), + child: Icon(Icons.info_outline), + ), + Expanded(child: + Text(hint), + ), + ], + ) + ); + } + } +RegExp hostNamePattern = RegExp(r'^([a-zA-Z0-9]|[a-zA-Z0-9][-a-zA-Z0-9]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][-a-zA-Z0-9]{0,61}[a-zA-Z0-9]))*$'); + class AnswerBotList extends StatefulWidget { const AnswerBotList({super.key}); @@ -223,6 +606,15 @@ class AnswerBotListState extends State { } } +final MaterialStateProperty switchIcon = MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.selected)) { + return const Icon(Icons.check); + } + return const Icon(Icons.close); + }, +); + class AnswerBotView extends StatefulWidget { AnswerbotInfo bot; @@ -246,15 +638,6 @@ class AnswerBotViewState extends State { internalDynDns = host == null || host.isEmpty; } - final MaterialStateProperty switchIcon = MaterialStateProperty.resolveWith( - (Set states) { - if (states.contains(MaterialState.selected)) { - return const Icon(Icons.check); - } - return const Icon(Icons.close); - }, - ); - @override Widget build(BuildContext context) { return Scaffold( @@ -320,30 +703,36 @@ class InfoField extends StatelessWidget { @override Widget build(BuildContext context) { - return Row( + var help = this.help; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: TextFormField( - decoration: InputDecoration( - labelText: label, - helperText: help, + Row( + children: [ + Expanded( + child: TextFormField( + decoration: InputDecoration( + labelText: label, + ), + initialValue: value ?? "", + readOnly: true, + ), ), - initialValue: value ?? "", - readOnly: true, - ), - ), - IconButton( - onPressed: () async { - await Clipboard.setData(ClipboardData(text: value ?? "")); - if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard."))); - }, - icon: const Icon(Icons.copy) + IconButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: value ?? "")); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Copied to clipboard."))); + }, + icon: const Icon(Icons.copy) + ), + ], ), + if (help != null) Text(help, style: const TextStyle(fontSize: 12, color: Colors.black54)) ], ); } - } Future showErrorDialog(BuildContext context, http.Response response, String title, String msg) { diff --git a/phoneblock_answerbot_ui/lib/proto.dart b/phoneblock_answerbot_ui/lib/proto.dart index 9b7dda36..85417b25 100644 --- a/phoneblock_answerbot_ui/lib/proto.dart +++ b/phoneblock_answerbot_ui/lib/proto.dart @@ -47,12 +47,76 @@ abstract class _JsonObject { } } +/// Visitor interface for SetupRequest. +abstract class SetupRequestVisitor { + R visitCreateAnswerBot(CreateAnswerBot self, A arg); + R visitEnterHostName(EnterHostName self, A arg); + R visitSetupDynDns(SetupDynDns self, A arg); + R visitCheckDynDns(CheckDynDns self, A arg); + R visitEnableAnswerBot(EnableAnswerBot self, A arg); + R visitDisableAnswerBot(DisableAnswerBot self, A arg); + R visitCheckAnswerBot(CheckAnswerBot self, A arg); +} + +abstract class SetupRequest extends _JsonObject { + /// Creates a SetupRequest. + SetupRequest(); + + /// Parses a SetupRequest from a string source. + static SetupRequest? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a SetupRequest instance from the given reader. + static SetupRequest? read(JsonReader json) { + SetupRequest? result; + + json.expectArray(); + if (!json.hasNext()) { + return null; + } + + switch (json.expectString()) { + case "CreateAnswerBot": result = CreateAnswerBot(); break; + case "EnterHostName": result = EnterHostName(); break; + case "SetupDynDns": result = SetupDynDns(); break; + case "CheckDynDns": result = CheckDynDns(); break; + case "EnableAnswerBot": result = EnableAnswerBot(); break; + case "DisableAnswerBot": result = DisableAnswerBot(); break; + case "CheckAnswerBot": result = CheckAnswerBot(); break; + default: result = null; + } + + if (!json.hasNext() || json.tryNull()) { + return null; + } + + if (result == null) { + json.skipAnyValue(); + } else { + result._readContent(json); + } + json.endArray(); + + return result; + } + + R visitSetupRequest(SetupRequestVisitor v, A arg); + +} + class CreateAnswerbotResponse extends _JsonObject { + int id; + String userName; + String password; + /// Creates a CreateAnswerbotResponse. CreateAnswerbotResponse({ + this.id = 0, this.userName = "", + this.password = "", }); /// Parses a CreateAnswerbotResponse from a string source. @@ -73,10 +137,18 @@ class CreateAnswerbotResponse extends _JsonObject { @override void _readProperty(String key, JsonReader json) { switch (key) { + case "id": { + id = json.expectInt(); + break; + } case "userName": { userName = json.expectString(); break; } + case "password": { + password = json.expectString(); + break; + } default: super._readProperty(key, json); } } @@ -85,17 +157,416 @@ class CreateAnswerbotResponse extends _JsonObject { void _writeProperties(JsonSink json) { super._writeProperties(json); + json.addKey("id"); + json.addNumber(id); + json.addKey("userName"); json.addString(userName); + + json.addKey("password"); + json.addString(password); } } +class CreateAnswerBot extends SetupRequest { + /// Creates a CreateAnswerBot. + CreateAnswerBot(); + + /// Parses a CreateAnswerBot from a string source. + static CreateAnswerBot? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a CreateAnswerBot instance from the given reader. + static CreateAnswerBot read(JsonReader json) { + CreateAnswerBot result = CreateAnswerBot(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "CreateAnswerBot"; + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitCreateAnswerBot(this, arg); + +} + +class EnterHostName extends SetupRequest { + int id; + + String hostName; + + /// Creates a EnterHostName. + EnterHostName({ + this.id = 0, + this.hostName = "", + }); + + /// Parses a EnterHostName from a string source. + static EnterHostName? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a EnterHostName instance from the given reader. + static EnterHostName read(JsonReader json) { + EnterHostName result = EnterHostName(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "EnterHostName"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + case "hostName": { + hostName = json.expectString(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + + json.addKey("hostName"); + json.addString(hostName); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitEnterHostName(this, arg); + +} + +class SetupDynDns extends SetupRequest { + int id; + + String hostName; + + /// Creates a SetupDynDns. + SetupDynDns({ + this.id = 0, + this.hostName = "", + }); + + /// Parses a SetupDynDns from a string source. + static SetupDynDns? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a SetupDynDns instance from the given reader. + static SetupDynDns read(JsonReader json) { + SetupDynDns result = SetupDynDns(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "SetupDynDns"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + case "hostName": { + hostName = json.expectString(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + + json.addKey("hostName"); + json.addString(hostName); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitSetupDynDns(this, arg); + +} + +class SetupDynDnsResponse extends _JsonObject { + int id; + + String dyndnsUser; + + String dyndnsPassword; + + /// Creates a SetupDynDnsResponse. + SetupDynDnsResponse({ + this.id = 0, + this.dyndnsUser = "", + this.dyndnsPassword = "", + }); + + /// Parses a SetupDynDnsResponse from a string source. + static SetupDynDnsResponse? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a SetupDynDnsResponse instance from the given reader. + static SetupDynDnsResponse read(JsonReader json) { + SetupDynDnsResponse result = SetupDynDnsResponse(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "SetupDynDnsResponse"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + case "dyndnsUser": { + dyndnsUser = json.expectString(); + break; + } + case "dyndnsPassword": { + dyndnsPassword = json.expectString(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + + json.addKey("dyndnsUser"); + json.addString(dyndnsUser); + + json.addKey("dyndnsPassword"); + json.addString(dyndnsPassword); + } + +} + +class CheckDynDns extends SetupRequest { + int id; + + /// Creates a CheckDynDns. + CheckDynDns({ + this.id = 0, + }); + + /// Parses a CheckDynDns from a string source. + static CheckDynDns? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a CheckDynDns instance from the given reader. + static CheckDynDns read(JsonReader json) { + CheckDynDns result = CheckDynDns(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "CheckDynDns"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitCheckDynDns(this, arg); + +} + +class EnableAnswerBot extends SetupRequest { + int id; + + /// Creates a EnableAnswerBot. + EnableAnswerBot({ + this.id = 0, + }); + + /// Parses a EnableAnswerBot from a string source. + static EnableAnswerBot? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a EnableAnswerBot instance from the given reader. + static EnableAnswerBot read(JsonReader json) { + EnableAnswerBot result = EnableAnswerBot(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "EnableAnswerBot"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitEnableAnswerBot(this, arg); + +} + +class DisableAnswerBot extends SetupRequest { + int id; + + /// Creates a DisableAnswerBot. + DisableAnswerBot({ + this.id = 0, + }); + + /// Parses a DisableAnswerBot from a string source. + static DisableAnswerBot? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a DisableAnswerBot instance from the given reader. + static DisableAnswerBot read(JsonReader json) { + DisableAnswerBot result = DisableAnswerBot(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "DisableAnswerBot"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitDisableAnswerBot(this, arg); + +} + +class CheckAnswerBot extends SetupRequest { + int id; + + /// Creates a CheckAnswerBot. + CheckAnswerBot({ + this.id = 0, + }); + + /// Parses a CheckAnswerBot from a string source. + static CheckAnswerBot? fromString(String source) { + return read(JsonReader.fromString(source)); + } + + /// Reads a CheckAnswerBot instance from the given reader. + static CheckAnswerBot read(JsonReader json) { + CheckAnswerBot result = CheckAnswerBot(); + result._readContent(json); + return result; + } + + @override + String _jsonType() => "CheckAnswerBot"; + + @override + void _readProperty(String key, JsonReader json) { + switch (key) { + case "id": { + id = json.expectInt(); + break; + } + default: super._readProperty(key, json); + } + } + + @override + void _writeProperties(JsonSink json) { + super._writeProperties(json); + + json.addKey("id"); + json.addNumber(id); + } + + @override + R visitSetupRequest(SetupRequestVisitor v, A arg) => v.visitCheckAnswerBot(this, arg); + +} + /// Information of a single answer bot. class AnswerbotInfo extends _JsonObject { /// The primary key identifier of this bot. int id; + /// The ID of the owning user. + int userId; + /// Whether the bot is enabled (registration is active). bool enabled; @@ -138,6 +609,7 @@ class AnswerbotInfo extends _JsonObject { /// Creates a AnswerbotInfo. AnswerbotInfo({ this.id = 0, + this.userId = 0, this.enabled = false, this.registered = false, this.registerMsg, @@ -175,6 +647,10 @@ class AnswerbotInfo extends _JsonObject { id = json.expectInt(); break; } + case "userId": { + userId = json.expectInt(); + break; + } case "enabled": { enabled = json.expectBool(); break; @@ -238,6 +714,9 @@ class AnswerbotInfo extends _JsonObject { json.addKey("id"); json.addNumber(id); + json.addKey("userId"); + json.addNumber(userId); + json.addKey("enabled"); json.addBool(enabled); diff --git a/phoneblock_answerbot_ui/web/index.html b/phoneblock_answerbot_ui/web/index.html index 9b88bb40..5a681e04 100644 --- a/phoneblock_answerbot_ui/web/index.html +++ b/phoneblock_answerbot_ui/web/index.html @@ -14,10 +14,7 @@ This is a placeholder for base href that will be replaced by the value of the `--base-href` argument provided to `flutter build`. --> - - From 9c56cb02d843837111966b1af8d8f9de20b7cc64 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Thu, 18 Jan 2024 19:01:34 +0100 Subject: [PATCH 079/153] Allow refreshing the bot list. --- phoneblock_answerbot_ui/lib/main.dart | 83 +++++++++++++++------------ 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/phoneblock_answerbot_ui/lib/main.dart b/phoneblock_answerbot_ui/lib/main.dart index 2700b098..95209041 100644 --- a/phoneblock_answerbot_ui/lib/main.dart +++ b/phoneblock_answerbot_ui/lib/main.dart @@ -73,46 +73,11 @@ class MyApp extends StatelessWidget { } class HomeScreen extends StatelessWidget { - const HomeScreen({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("Deine PhoneBlock Anrufbeantworter"), - actions: [ - IconButton( - onPressed: () { - // TODO - }, - icon: const Icon(Icons.refresh), - ) - ], - ), - body: const AnswerBotList(), - floatingActionButton: FloatingActionButton( - onPressed: () => _createAnswerBot(context), - tooltip: 'Anrufbeantworter anlegen', - child: const Icon(Icons.add), - ), - ); - } - - void _createAnswerBot(BuildContext context) async { - http.Response response = await sendRequest(CreateAnswerBot()); - if (!context.mounted) return; - - if (response.statusCode != 200) { - return showErrorDialog(context, response, 'Anlage fehlgeschlagen', "Der Anrufbeantworter konnte nicht angelegt werden"); - } - - var creation = CreateAnswerbotResponse.read(JsonReader.fromString(response.body)); - Navigator.push(context, MaterialPageRoute(builder: (context) => _setupAnswerBot(context, creation))); - } - - Widget _setupAnswerBot(BuildContext context, CreateAnswerbotResponse creation) { - return BotSetupForm(creation); + return const AnswerBotList(); } } @@ -528,7 +493,10 @@ class AnswerBotListState extends State { @override void initState() { super.initState(); + requestBotList(); + } + void requestBotList() { http.get(Uri.parse('$basePath/ab/list'), headers: { if (kDebugMode) 'Authorization': authHeader, @@ -554,6 +522,44 @@ class AnswerBotListState extends State { @override Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Deine PhoneBlock Anrufbeantworter"), + actions: [ + IconButton( + onPressed: () { + setState(refreshBotList); + }, + icon: const Icon(Icons.refresh), + ) + ], + ), + body: _botList(context), + floatingActionButton: FloatingActionButton( + onPressed: () => _createAnswerBot(context), + tooltip: 'Anrufbeantworter anlegen', + child: const Icon(Icons.add), + ), + ); + } + + void _createAnswerBot(BuildContext context) async { + http.Response response = await sendRequest(CreateAnswerBot()); + if (!context.mounted) return; + + if (response.statusCode != 200) { + return showErrorDialog(context, response, 'Anlage fehlgeschlagen', "Der Anrufbeantworter konnte nicht angelegt werden"); + } + + var creation = CreateAnswerbotResponse.read(JsonReader.fromString(response.body)); + Navigator.push(context, MaterialPageRoute(builder: (context) => _setupAnswerBot(context, creation))).then((value) => refreshBotList()); + } + + Widget _setupAnswerBot(BuildContext context, CreateAnswerbotResponse creation) { + return BotSetupForm(creation); + } + + Widget _botList(BuildContext context) { var bots = this.bots; if (bots == null || bots.isEmpty) { @@ -604,6 +610,11 @@ class AnswerBotListState extends State { showAnswerBot(BuildContext context, AnswerbotInfo bot) { Navigator.push(context, MaterialPageRoute(builder: (context) => AnswerBotView(bot))); } + + refreshBotList() { + msg = 'Refreshing data...'; + requestBotList(); + } } final MaterialStateProperty switchIcon = MaterialStateProperty.resolveWith( From 30c9d75beb1369d4443da82653f94296f290a6c2 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sat, 20 Jan 2024 19:36:04 +0100 Subject: [PATCH 080/153] Updated H2 to version 2.2.224. --- phoneblock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock/pom.xml b/phoneblock/pom.xml index 6a3e53ac..a3e90b37 100644 --- a/phoneblock/pom.xml +++ b/phoneblock/pom.xml @@ -164,7 +164,7 @@ com.h2database h2 - 2.2.222 + 2.2.224 From 952b6bda464ea9c8a61cf9688cdba8ef2d96c85b Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 21 Jan 2024 10:35:38 +0100 Subject: [PATCH 081/153] Added API to delete an answerbot. --- .../phoneblock/ab/CreateABServlet.java | 39 ++++++- .../phoneblock/ab/proto/DeleteAnswerBot.java | 108 ++++++++++++++++++ .../phoneblock/ab/proto/SetupRequest.java | 7 ++ .../de/haumacher/phoneblock/ab/proto/ab.proto | 4 + .../de/haumacher/phoneblock/db/Users.java | 3 + 5 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DeleteAnswerBot.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java index 9aef454d..3835a424 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/CreateABServlet.java @@ -29,6 +29,7 @@ import de.haumacher.phoneblock.ab.proto.CheckDynDns; import de.haumacher.phoneblock.ab.proto.CreateAnswerBot; import de.haumacher.phoneblock.ab.proto.CreateAnswerbotResponse; +import de.haumacher.phoneblock.ab.proto.DeleteAnswerBot; import de.haumacher.phoneblock.ab.proto.DisableAnswerBot; import de.haumacher.phoneblock.ab.proto.EnableAnswerBot; import de.haumacher.phoneblock.ab.proto.EnterHostName; @@ -287,16 +288,50 @@ public Void visit(DisableAnswerBot self, RequestContext context) throws IOExcept HttpServletRequest req = context.req; String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + long id = self.getId(); + + DB db = DBService.getInstance(); + try (SqlSession session = db.openSession()) { + Users users = session.getMapper(Users.class); + + DBAnswerbotInfo bot = users.getAnswerBot(id); + if (bot == null) { + sendError(resp, HttpServletResponse.SC_NOT_FOUND, "Answerbot '" + id + "' not found."); + return null; + } + SipService.getInstance().disableAnwserBot(bot.getUserName()); + } + + sendOk(resp); + LOG.info("Answerbot '" + id + "' disabled for: " + login); + return null; + } + + @Override + public Void visit(DeleteAnswerBot self, RequestContext context) throws IOException { + HttpServletResponse resp = context.resp; + HttpServletRequest req = context.req; + String login = LoginFilter.getAuthenticatedUser(req.getSession(false)); + + long id = self.getId(); + DB db = DBService.getInstance(); try (SqlSession session = db.openSession()) { Users users = session.getMapper(Users.class); - DBAnswerbotInfo bot = users.getAnswerBot(self.getId()); + DBAnswerbotInfo bot = users.getAnswerBot(id); + if (bot == null) { + sendError(resp, HttpServletResponse.SC_NOT_FOUND, "Answerbot '" + id + "' not found."); + return null; + } SipService.getInstance().disableAnwserBot(bot.getUserName()); + + users.answerbotDelete(id); + session.commit(); } sendOk(resp); - LOG.info("Answerbot disabled for: " + login); + LOG.info("Answerbot '" + id + "' deleted for: " + login); return null; } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DeleteAnswerBot.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DeleteAnswerBot.java new file mode 100644 index 00000000..9f55070f --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/DeleteAnswerBot.java @@ -0,0 +1,108 @@ +package de.haumacher.phoneblock.ab.proto; + +public class DeleteAnswerBot extends SetupRequest { + + /** + * Creates a {@link de.haumacher.phoneblock.ab.proto.DeleteAnswerBot} instance. + */ + public static de.haumacher.phoneblock.ab.proto.DeleteAnswerBot create() { + return new de.haumacher.phoneblock.ab.proto.DeleteAnswerBot(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.ab.proto.DeleteAnswerBot} type in JSON format. */ + public static final String DELETE_ANSWER_BOT__TYPE = "DeleteAnswerBot"; + + /** @see #getId() */ + public static final String ID__PROP = "id"; + + private long _id = 0L; + + /** + * Creates a {@link DeleteAnswerBot} instance. + * + * @see de.haumacher.phoneblock.ab.proto.DeleteAnswerBot#create() + */ + protected DeleteAnswerBot() { + super(); + } + + @Override + public TypeKind kind() { + return TypeKind.DELETE_ANSWER_BOT; + } + + public final long getId() { + return _id; + } + + /** + * @see #getId() + */ + public de.haumacher.phoneblock.ab.proto.DeleteAnswerBot setId(long value) { + internalSetId(value); + return this; + } + + /** Internal setter for {@link #getId()} without chain call utility. */ + protected final void internalSetId(long value) { + _id = value; + } + + @Override + public String jsonType() { + return DELETE_ANSWER_BOT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + ID__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case ID__PROP: return getId(); + default: return super.get(field); + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case ID__PROP: internalSetId((long) value); break; + default: super.set(field, value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.ab.proto.DeleteAnswerBot readDeleteAnswerBot(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.ab.proto.DeleteAnswerBot result = new de.haumacher.phoneblock.ab.proto.DeleteAnswerBot(); + result.readContent(in); + return result; + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(ID__PROP); + out.value(getId()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case ID__PROP: setId(in.nextLong()); break; + default: super.readField(in, field); + } + } + + @Override + public R visit(de.haumacher.phoneblock.ab.proto.SetupRequest.Visitor v, A arg) throws E { + return v.visit(this, arg); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java index d2529b7a..a4105fb1 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/SetupRequest.java @@ -23,6 +23,9 @@ public enum TypeKind { /** Type literal for {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot}. */ DISABLE_ANSWER_BOT, + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.DeleteAnswerBot}. */ + DELETE_ANSWER_BOT, + /** Type literal for {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot}. */ CHECK_ANSWER_BOT, ; @@ -50,6 +53,9 @@ public interface Visitor { /** Visit case for {@link de.haumacher.phoneblock.ab.proto.DisableAnswerBot}.*/ R visit(de.haumacher.phoneblock.ab.proto.DisableAnswerBot self, A arg) throws E; + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.DeleteAnswerBot}.*/ + R visit(de.haumacher.phoneblock.ab.proto.DeleteAnswerBot self, A arg) throws E; + /** Visit case for {@link de.haumacher.phoneblock.ab.proto.CheckAnswerBot}.*/ R visit(de.haumacher.phoneblock.ab.proto.CheckAnswerBot self, A arg) throws E; @@ -77,6 +83,7 @@ public static de.haumacher.phoneblock.ab.proto.SetupRequest readSetupRequest(de. case CheckDynDns.CHECK_DYN_DNS__TYPE: result = de.haumacher.phoneblock.ab.proto.CheckDynDns.readCheckDynDns(in); break; case EnableAnswerBot.ENABLE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.EnableAnswerBot.readEnableAnswerBot(in); break; case DisableAnswerBot.DISABLE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.DisableAnswerBot.readDisableAnswerBot(in); break; + case DeleteAnswerBot.DELETE_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.DeleteAnswerBot.readDeleteAnswerBot(in); break; case CheckAnswerBot.CHECK_ANSWER_BOT__TYPE: result = de.haumacher.phoneblock.ab.proto.CheckAnswerBot.readCheckAnswerBot(in); break; default: in.skipValue(); result = null; break; } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto index a81303de..34e03c3c 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/ab/proto/ab.proto @@ -48,6 +48,10 @@ message DisableAnswerBot extends SetupRequest { long id; } +message DeleteAnswerBot extends SetupRequest { + long id; +} + message CheckAnswerBot extends SetupRequest { long id; } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java index 922edea8..42ed7804 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/Users.java @@ -154,6 +154,9 @@ public interface Users { @Delete("delete from ANSWERBOT_DYNDNS where ABID=#{abId}") int answerbotDeleteDynDns(long abId); + @Delete("delete from ANSWERBOT_SIP where ID=#{id}") + int answerbotDelete(long id); + @Update("update ANSWERBOT_SIP set HOST=#{host} where ID=#{id}") void answerbotEnterHostName(long id, String host); From b8fc013c2f774f1b7a33ff18afa14798b29e460d Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 21 Jan 2024 10:54:28 +0100 Subject: [PATCH 082/153] Updated msgbuf dependency. --- phoneblock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phoneblock/pom.xml b/phoneblock/pom.xml index a3e90b37..3db6eddb 100644 --- a/phoneblock/pom.xml +++ b/phoneblock/pom.xml @@ -52,7 +52,7 @@ 3.6.4 4.18.3 - 1.1.2 + 1.1.4 http://localhost:8080/manager/text phoneblock From 2eee646a7b35143d0e0859be45bab67bd8edfd12 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 21 Jan 2024 10:55:42 +0100 Subject: [PATCH 083/153] Added API to retrieve same number infos as web page search. --- .../phoneblock/analysis/AreaCodes.java | 12 +- .../phoneblock/analysis/NumberAnalyzer.java | 2 + .../phoneblock/analysis/analysis.proto | 16 - .../phoneblock/app/SearchServlet.java | 95 +- .../phoneblock/app/SettingsServlet.java | 3 +- .../phoneblock/app/api/NumServlet.java | 2 +- .../phoneblock/app/api/SearchApiServlet.java | 51 ++ .../callreport/model/CallReport.java | 2 +- .../carddav/resource/AddressBookCache.java | 6 +- .../java/de/haumacher/phoneblock/db/DB.java | 13 +- .../haumacher/phoneblock/db/DBSpamReport.java | 23 + .../haumacher/phoneblock/db/SpamReport.java | 82 -- .../haumacher/phoneblock/db/SpamReports.java | 14 +- .../phoneblock/db/model/Blocklist.java | 2 +- .../{analysis => db/model}/PhoneNumer.java | 267 ++++-- .../phoneblock/db/model/Ratings.java | 2 +- .../phoneblock/db/model/SearchResult.java | 848 ++++++++++++++++++ .../phoneblock/db/model/SpamReport.java | 426 +++++++++ .../de/haumacher/phoneblock/db/model/db.proto | 50 ++ .../index/google/impl/UpdateMessage_Impl.java | 2 +- phoneblock/src/main/webapp/WEB-INF/web.xml | 4 +- phoneblock/src/main/webapp/phone-info.jsp | 2 +- .../analysis/TestNumberAnalyzer.java | 4 +- .../de/haumacher/phoneblock/db/TestDB.java | 2 +- 24 files changed, 1668 insertions(+), 262 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/app/api/SearchApiServlet.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/DBSpamReport.java delete mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReport.java rename phoneblock/src/main/java/de/haumacher/phoneblock/{analysis => db/model}/PhoneNumer.java (53%) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SearchResult.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SpamReport.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/AreaCodes.java b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/AreaCodes.java index 7da5bbe6..c7e16397 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/AreaCodes.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/AreaCodes.java @@ -18,7 +18,17 @@ public static de.haumacher.phoneblock.analysis.AreaCodes create() { /** Identifier for the property {@link #getCodes()} in binary format. */ static final int CODES__ID = 1; - private final java.util.Map _codes = new java.util.HashMap<>(); + private final java.util.Map _codes = new de.haumacher.msgbuf.util.ReferenceMap<>() { + @Override + protected void beforeAdd(String index, de.haumacher.phoneblock.analysis.AreaCode element) { + _listener.beforeAdd(AreaCodes.this, CODES__PROP, index, element); + } + + @Override + protected void afterRemove(String index, de.haumacher.phoneblock.analysis.AreaCode element) { + _listener.afterRemove(AreaCodes.this, CODES__PROP, index, element); + } + }; /** * Creates a {@link AreaCodes} instance. diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/NumberAnalyzer.java b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/NumberAnalyzer.java index f82003d0..d4c6eba6 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/NumberAnalyzer.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/NumberAnalyzer.java @@ -17,6 +17,8 @@ import com.opencsv.ICSVParser; import com.opencsv.exceptions.CsvValidationException; +import de.haumacher.phoneblock.db.model.PhoneNumer; + /** * Utility for analyzing phone numbers. */ diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/analysis.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/analysis.proto index 2f7faf18..b0cfdbd9 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/analysis.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/analysis.proto @@ -3,22 +3,6 @@ package de.haumacher.phoneblock.analysis; option NoInterfaces; option NoXml; -message PhoneNumer { - @Nullable - string shortcut; - - string plus; - string zeroZero; - - string countryCode; - string country; - - @Nullable - string cityCode; - @Nullable - string city; -} - message AreaCodes { map codes; } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SearchServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SearchServlet.java index 339ad208..62b0e00f 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SearchServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SearchServlet.java @@ -23,15 +23,16 @@ import org.apache.ibatis.session.SqlSession; import de.haumacher.phoneblock.analysis.NumberAnalyzer; -import de.haumacher.phoneblock.analysis.PhoneNumer; import de.haumacher.phoneblock.db.DB; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Ratings; -import de.haumacher.phoneblock.db.SpamReport; import de.haumacher.phoneblock.db.SpamReports; +import de.haumacher.phoneblock.db.model.PhoneNumer; import de.haumacher.phoneblock.db.model.Rating; import de.haumacher.phoneblock.db.model.RatingInfo; import de.haumacher.phoneblock.db.model.SearchInfo; +import de.haumacher.phoneblock.db.model.SearchResult; +import de.haumacher.phoneblock.db.model.SpamReport; import de.haumacher.phoneblock.db.model.UserComment; import de.haumacher.phoneblock.meta.MetaSearchService; import de.haumacher.phoneblock.util.JspUtil; @@ -136,23 +137,38 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se return; } - String phone = NumberAnalyzer.normalizeNumber(pathInfo.substring(1)); - if (phone.isEmpty() || phone.contains("*")) { + String query = pathInfo.substring(1); + + boolean bot = isBot(req); + boolean isSeachHit = !bot && req.getParameter("link") == null; + + SearchResult searchResult = analyze(query, bot, isSeachHit); + if (searchResult == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND); - return; + } + + sendResult(req, resp, searchResult); + } + + public static SearchResult analyze(String query) { + return analyze(query, false, true); + } + + private static SearchResult analyze(String query, boolean isBot, boolean isSeachHit) { + String phone = NumberAnalyzer.normalizeNumber(query); + if (phone.isEmpty() || phone.contains("*")) { + return null; } PhoneNumer number = NumberAnalyzer.analyze(phone); if (number == null) { - resp.sendError(HttpServletResponse.SC_NOT_FOUND); - return; + return null; } + String phoneId = NumberAnalyzer.getPhoneId(number); - boolean bot = isBot(req); - // Note: Search for comments first, since new comments may change the state of the number. - List comments = MetaSearchService.getInstance().fetchComments(phoneId, bot); + List comments = MetaSearchService.getInstance().fetchComments(phoneId, isBot); SpamReport info; List searches; @@ -169,7 +185,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se long now = System.currentTimeMillis(); - if (!bot && req.getParameter("link") == null) { + if (isSeachHit) { db.addSearchHit(reports, phoneId, now); commit = true; } @@ -217,7 +233,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se int ratingVotes = ratingInfo.getVotes(); ratings.put(currentRating, ratingVotes); - if (ratingVotes > maxVotes && currentRating != Rating.A_LEGITIMATE) { + if (ratingVotes >= maxVotes && currentRating != Rating.A_LEGITIMATE) { topRating = currentRating; maxVotes = ratingVotes; } @@ -228,65 +244,70 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se topRating = Rating.A_LEGITIMATE; } - String ratingAttribute = RatingServlet.ratingAttribute(phoneId); + return SearchResult.create().setPhoneId(phoneId).setNumber(number).setComments(comments).setInfo(info).setSearches(searches).setAiSummary(aiSummary).setRelatedNumbers(relatedNumbers).setPrev(prev).setNext(next) + .setTopRating(topRating).setRatings(ratings); + } + + private void sendResult(HttpServletRequest req, HttpServletResponse resp, SearchResult searchResult) throws ServletException, IOException { + String ratingAttribute = RatingServlet.ratingAttribute(searchResult.getPhoneId()); if (getSessionAttribute(req, ratingAttribute) != null) { req.setAttribute("thanks", Boolean.TRUE); } - String status = status(votes); + String status = status(searchResult.getInfo().getVotes()); - String defaultSummary = defaultSummary(req, info); + String defaultSummary = defaultSummary(req, searchResult.getInfo()); String summary; String description; - if (isEmpty(aiSummary)) { + if (isEmpty(searchResult.getAiSummary())) { summary = null; - description = defaultSimpleSummary(info); + description = defaultSimpleSummary(searchResult.getInfo()); } else { - description = summary = JspUtil.quote(aiSummary); + description = summary = JspUtil.quote(searchResult.getAiSummary()); } - req.setAttribute("comments", comments); + req.setAttribute("comments", searchResult.getComments()); // The canonical path of this page. - req.setAttribute("path", req.getServletPath() + '/' + phoneId); + req.setAttribute("path", req.getServletPath() + '/' + searchResult.getPhoneId()); - req.setAttribute("info", info); - req.setAttribute("number", number); - req.setAttribute("prev", prev); - req.setAttribute("next", next); + req.setAttribute("info", searchResult.getInfo()); + req.setAttribute("number", searchResult.getNumber()); + req.setAttribute("prev", searchResult.getPrev()); + req.setAttribute("next", searchResult.getNext()); req.setAttribute("summary", summary); req.setAttribute("defaultSummary", defaultSummary); - req.setAttribute("rating", topRating); - req.setAttribute("ratings", ratings); - req.setAttribute("searches", searches); - req.setAttribute("relatedNumbers", relatedNumbers); - req.setAttribute("title", status + ": Rufnummer ☎ " + phoneId + " - PhoneBlock"); + req.setAttribute("rating", searchResult.getTopRating()); + req.setAttribute("ratings", searchResult.getRatings()); + req.setAttribute("searches", searchResult.getSearches()); + req.setAttribute("relatedNumbers", searchResult.getRelatedNumbers()); + req.setAttribute("title", status + ": Rufnummer ☎ " + searchResult.getPhoneId() + " - PhoneBlock"); req.setAttribute("description", description + ". Mit PhoneBlock Werbeanrufe automatisch blockieren, kostenlos und ohne Zusatzhardware."); StringBuilder keywords = new StringBuilder(); keywords.append("Anrufe, Bewertung"); - if (number.getShortcut() != null) { + if (searchResult.getNumber().getShortcut() != null) { keywords.append(", "); - keywords.append(number.getShortcut()); + keywords.append(searchResult.getNumber().getShortcut()); } keywords.append(", "); - keywords.append(number.getZeroZero()); + keywords.append(searchResult.getNumber().getZeroZero()); keywords.append(", "); - keywords.append(number.getPlus()); + keywords.append(searchResult.getNumber().getPlus()); - if (topRating != Rating.B_MISSED) { + if (searchResult.getTopRating() != Rating.B_MISSED) { keywords.append(", "); - keywords.append(Ratings.getLabel(topRating)); + keywords.append(Ratings.getLabel(searchResult.getTopRating())); } keywords.append(", "); keywords.append(status); - if (number.getCity() != null) { + if (searchResult.getNumber().getCity() != null) { keywords.append(", "); - keywords.append(number.getCity()); + keywords.append(searchResult.getNumber().getCity()); } req.setAttribute("keywords", keywords.toString()); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java index 83aa2890..f06aa0b2 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/SettingsServlet.java @@ -18,13 +18,12 @@ import de.haumacher.phoneblock.ab.DBAnswerbotInfo; import de.haumacher.phoneblock.analysis.NumberAnalyzer; -import de.haumacher.phoneblock.analysis.PhoneNumer; import de.haumacher.phoneblock.carddav.resource.AddressBookCache; import de.haumacher.phoneblock.db.BlockList; import de.haumacher.phoneblock.db.DB; -import de.haumacher.phoneblock.db.DBAnswerBotSip; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.Users; +import de.haumacher.phoneblock.db.model.PhoneNumer; import de.haumacher.phoneblock.db.settings.UserSettings; /** diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/NumServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/NumServlet.java index b1949907..0aa3c747 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/NumServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/NumServlet.java @@ -12,11 +12,11 @@ import javax.servlet.http.HttpServletResponse; import de.haumacher.phoneblock.analysis.NumberAnalyzer; -import de.haumacher.phoneblock.analysis.PhoneNumer; import de.haumacher.phoneblock.app.SearchServlet; import de.haumacher.phoneblock.db.DB; import de.haumacher.phoneblock.db.DBService; import de.haumacher.phoneblock.db.model.PhoneInfo; +import de.haumacher.phoneblock.db.model.PhoneNumer; import de.haumacher.phoneblock.meta.MetaSearchService; import de.haumacher.phoneblock.util.ServletUtil; diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/SearchApiServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/SearchApiServlet.java new file mode 100644 index 00000000..e4b27b95 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/api/SearchApiServlet.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.app.api; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import de.haumacher.phoneblock.analysis.NumberAnalyzer; +import de.haumacher.phoneblock.app.SearchServlet; +import de.haumacher.phoneblock.db.DB; +import de.haumacher.phoneblock.db.DBService; +import de.haumacher.phoneblock.db.model.PhoneInfo; +import de.haumacher.phoneblock.db.model.PhoneNumer; +import de.haumacher.phoneblock.db.model.SearchResult; +import de.haumacher.phoneblock.meta.MetaSearchService; +import de.haumacher.phoneblock.util.ServletUtil; + +/** + * Servlet for search API returning the info from the web search in a machine readable form. + */ +@WebServlet(urlPatterns = SearchApiServlet.PREFIX + "/*") +public class SearchApiServlet extends HttpServlet { + + static final String PREFIX = "/api/search"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String pathInfo = req.getPathInfo(); + if (pathInfo == null || pathInfo.length() < 1) { + ServletUtil.sendError(resp, "Missing phone number."); + return; + } + + String query = pathInfo.substring(1); + SearchResult searchResult = SearchServlet.analyze(query); + + if (searchResult == null) { + ServletUtil.sendError(resp, "Invalid phone number."); + return; + } + + ServletUtil.sendResult(req, resp, searchResult); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/callreport/model/CallReport.java b/phoneblock/src/main/java/de/haumacher/phoneblock/callreport/model/CallReport.java index 2d1a42f0..91353d1e 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/callreport/model/CallReport.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/callreport/model/CallReport.java @@ -37,7 +37,7 @@ public static de.haumacher.phoneblock.callreport.model.CallReport create() { private String _lastid = ""; - private final java.util.List _callers = new de.haumacher.msgbuf.util.ReferenceList() { + private final java.util.List _callers = new de.haumacher.msgbuf.util.ReferenceList<>() { @Override protected void beforeAdd(int index, String element) { _listener.beforeAdd(CallReport.this, CALLERS__PROP, index, element); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/carddav/resource/AddressBookCache.java b/phoneblock/src/main/java/de/haumacher/phoneblock/carddav/resource/AddressBookCache.java index 8b705a97..3c3fa5c1 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/carddav/resource/AddressBookCache.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/carddav/resource/AddressBookCache.java @@ -22,7 +22,7 @@ import de.haumacher.phoneblock.analysis.NumberTree; import de.haumacher.phoneblock.db.BlockList; import de.haumacher.phoneblock.db.DBService; -import de.haumacher.phoneblock.db.SpamReport; +import de.haumacher.phoneblock.db.DBSpamReport; import de.haumacher.phoneblock.db.SpamReports; import de.haumacher.phoneblock.db.Users; import de.haumacher.phoneblock.db.settings.UserSettings; @@ -177,10 +177,10 @@ private List getCommonNumbers(SpamReports reports, ListType listTyp private List loadNumbers(SpamReports reports, List personalizations, Set exclusions, ListType listType) { - List result = reports.getReports(); + List result = reports.getReports(); long now = System.currentTimeMillis(); NumberTree numberTree = new NumberTree(); - for (SpamReport report : result) { + for (DBSpamReport report : result) { String phone = report.getPhone(); int votes = report.getVotes(); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java index 7ecd575b..25de849e 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DB.java @@ -53,6 +53,7 @@ import de.haumacher.phoneblock.db.model.PhoneInfo; import de.haumacher.phoneblock.db.model.Rating; import de.haumacher.phoneblock.db.model.SearchInfo; +import de.haumacher.phoneblock.db.model.SpamReport; import de.haumacher.phoneblock.db.model.UserComment; import de.haumacher.phoneblock.db.settings.UserSettings; import de.haumacher.phoneblock.index.IndexUpdateService; @@ -452,7 +453,7 @@ public SqlSession openSession() { /** * Looks up all spam reports that were done after the given time in milliseconds since epoch. */ - public List getLatestSpamReports(long notBefore) { + public List getLatestSpamReports(long notBefore) { try (SqlSession session = openSession()) { SpamReports reports = session.getMapper(SpamReports.class); return reports.getLatestReports(notBefore); @@ -519,7 +520,7 @@ List getTopSearches(int cntLatest, int cntSearches) { /** * Looks all spam reports. */ - public List getAll(int limit) { + public List getAll(int limit) { try (SqlSession session = openSession()) { SpamReports reports = session.getMapper(SpamReports.class); return reports.getAll(limit); @@ -529,7 +530,7 @@ public List getAll(int limit) { /** * Looks up spam reports with the most votes in the last month. */ - public List getTopSpamReports(int cnt) { + public List getTopSpamReports(int cnt) { Calendar cal = GregorianCalendar.getInstance(); cal.add(Calendar.MONTH, -1); cal.set(Calendar.HOUR_OF_DAY, 0); @@ -545,7 +546,7 @@ public List getTopSpamReports(int cnt) { /** * Looks up the newest entries in the blocklist. */ - public List getLatestBlocklistEntries(String login) { + public List getLatestBlocklistEntries(String login) { try (SqlSession session = openSession()) { SpamReports reports = session.getMapper(SpamReports.class); @@ -648,14 +649,14 @@ public int getVotesFor(String phone) { */ public SpamReport getPhoneInfo(SpamReports reports, String phone) { if (reports.isWhiteListed(phone)) { - return new SpamReport(phone, 0, 0, 0).setWhiteListed(true); + return new DBSpamReport(phone, 0, 0, 0).setWhiteListed(true); } SpamReport result = reports.getPhoneInfo(phone); if (result == null) { result = reports.getPhoneInfoArchived(phone); if (result == null) { - result = new SpamReport(phone, 0, 0, 0); + result = new DBSpamReport(phone, 0, 0, 0); } else { result.setArchived(true); } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBSpamReport.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBSpamReport.java new file mode 100644 index 00000000..270c388c --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/DBSpamReport.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.db; + +import de.haumacher.phoneblock.db.model.SpamReport; + +/** + * Data object representing a single spam report. + */ +public class DBSpamReport extends SpamReport { + + /** + * Creates a {@link DBSpamReport}. + */ + public DBSpamReport(String phone, int votes, long lastUpdate, long dateAdded) { + setPhone(phone); + setVotes(votes); + setLastUpdate(lastUpdate); + setDateAdded(dateAdded); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReport.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReport.java deleted file mode 100644 index 3040601f..00000000 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReport.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. - */ -package de.haumacher.phoneblock.db; - -/** - * Data object representing a single spam report. - */ -public class SpamReport { - - private final String _phone; - private final int _votes; - private final long _lastUpdate; - private final long _dateAdded; - private boolean _archived; - private boolean _whiteListed; - - /** - * Creates a {@link SpamReport}. - */ - public SpamReport(String phone, int votes, long lastUpdate, long dateAdded) { - _phone = phone; - _votes = votes; - _lastUpdate = lastUpdate; - _dateAdded = dateAdded; - } - - /** - * Whether the number is on the white list and can not get spam reports. - */ - public boolean isWhiteListed() { - return _whiteListed; - } - - /** - * @see #isWhiteListed() - */ - public SpamReport setWhiteListed(boolean whiteListed) { - _whiteListed = whiteListed; - return this; - } - - /** - * Whether this entry is not in the active list. - */ - public boolean isArchived() { - return _archived; - } - - public void setArchived(boolean archived) { - _archived = archived; - } - - /** - * The phone number reported as spam. - */ - public String getPhone() { - return _phone; - } - - /** - * Time in milliseconds since epoch when the last vote for this phone number was seen. - */ - public long getLastUpdate() { - return _lastUpdate; - } - - /** - * The total number of votes that report this phone number as spam. - */ - public int getVotes() { - return _votes; - } - - /** - * Time in milliseconds since epoch when the first spam report for this number was received. - */ - public long getDateAdded() { - return _dateAdded; - } - -} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReports.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReports.java index 083a96e0..fc48aa20 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReports.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/SpamReports.java @@ -75,13 +75,13 @@ public interface SpamReports { int deleteArchivedReports(long now); @Select("select PHONE, VOTES, LASTUPDATE, DATEADDED from SPAMREPORTS where LASTUPDATE >= #{after} order by LASTUPDATE desc") - List getLatestReports(long after); + List getLatestReports(long after); @Select("select PHONE, VOTES, LASTUPDATE, DATEADDED from SPAMREPORTS order by LASTUPDATE desc limit #{limit}") - List getAll(int limit); + List getAll(int limit); @Select("select PHONE, VOTES, LASTUPDATE, DATEADDED from SPAMREPORTS where PHONE = #{phone}") - SpamReport getPhoneInfo(String phone); + DBSpamReport getPhoneInfo(String phone); @Select("SELECT * FROM SPAMREPORTS s \n" + " WHERE s.PHONE > #{phone}\n" @@ -96,16 +96,16 @@ public interface SpamReports { String getPrevPhone(String phone); @Select("select PHONE, VOTES, LASTUPDATE, DATEADDED from OLDREPORTS where PHONE = #{phone}") - SpamReport getPhoneInfoArchived(String phone); + DBSpamReport getPhoneInfoArchived(String phone); @Select("SELECT PHONE, VOTES, LASTUPDATE, DATEADDED FROM SPAMREPORTS s" + " WHERE s.LASTUPDATE >= #{notBefore}" + " ORDER BY s.VOTES DESC LIMIT #{cnt}") - List getTopSpammers(int cnt, long notBefore); + List getTopSpammers(int cnt, long notBefore); @Select("SELECT x.* FROM SPAMREPORTS x" + " WHERE VOTES >= #{minVotes} AND DATEADDED > 0 ORDER BY DATEADDED DESC LIMIT 10") - List getLatestBlocklistEntries(int minVotes); + List getLatestBlocklistEntries(int minVotes); @Select("select s.PHONE, s.VOTES, case when r.RATING is null then 'B_MISSED' else r.RATING end, case when r.COUNT is null then 0 else r.COUNT end from SPAMREPORTS s" + " left outer join RATINGS r on r.PHONE = s.PHONE" @@ -173,7 +173,7 @@ public interface SpamReports { List getSearchesAtAll(int revision, Collection numbers); @Select("select PHONE, VOTES, LASTUPDATE, DATEADDED from SPAMREPORTS") - List getReports(); + List getReports(); @Select("select PHONE from WHITELIST") Set getWhiteList(); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Blocklist.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Blocklist.java index 98109913..1538b4d8 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Blocklist.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Blocklist.java @@ -18,7 +18,7 @@ public static de.haumacher.phoneblock.db.model.Blocklist create() { /** @see #getNumbers() */ public static final String NUMBERS__PROP = "numbers"; - private final java.util.List _numbers = new de.haumacher.msgbuf.util.ReferenceList() { + private final java.util.List _numbers = new de.haumacher.msgbuf.util.ReferenceList<>() { @Override protected void beforeAdd(int index, de.haumacher.phoneblock.db.model.PhoneInfo element) { _listener.beforeAdd(Blocklist.this, NUMBERS__PROP, index, element); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/PhoneNumer.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/PhoneNumer.java similarity index 53% rename from phoneblock/src/main/java/de/haumacher/phoneblock/analysis/PhoneNumer.java rename to phoneblock/src/main/java/de/haumacher/phoneblock/db/model/PhoneNumer.java index b8770dde..40ded1c1 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/analysis/PhoneNumer.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/PhoneNumer.java @@ -1,15 +1,15 @@ -package de.haumacher.phoneblock.analysis; +package de.haumacher.phoneblock.db.model; -public class PhoneNumer extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.binary.BinaryDataObject, de.haumacher.msgbuf.observer.Observable { +public class PhoneNumer extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.observer.Observable, de.haumacher.msgbuf.xml.XmlSerializable { /** - * Creates a {@link de.haumacher.phoneblock.analysis.PhoneNumer} instance. + * Creates a {@link de.haumacher.phoneblock.db.model.PhoneNumer} instance. */ - public static de.haumacher.phoneblock.analysis.PhoneNumer create() { - return new de.haumacher.phoneblock.analysis.PhoneNumer(); + public static de.haumacher.phoneblock.db.model.PhoneNumer create() { + return new de.haumacher.phoneblock.db.model.PhoneNumer(); } - /** Identifier for the {@link de.haumacher.phoneblock.analysis.PhoneNumer} type in JSON format. */ + /** Identifier for the {@link de.haumacher.phoneblock.db.model.PhoneNumer} type in JSON format. */ public static final String PHONE_NUMER__TYPE = "PhoneNumer"; /** @see #getShortcut() */ @@ -33,27 +33,6 @@ public static de.haumacher.phoneblock.analysis.PhoneNumer create() { /** @see #getCity() */ public static final String CITY__PROP = "city"; - /** Identifier for the property {@link #getShortcut()} in binary format. */ - static final int SHORTCUT__ID = 1; - - /** Identifier for the property {@link #getPlus()} in binary format. */ - static final int PLUS__ID = 2; - - /** Identifier for the property {@link #getZeroZero()} in binary format. */ - static final int ZERO_ZERO__ID = 3; - - /** Identifier for the property {@link #getCountryCode()} in binary format. */ - static final int COUNTRY_CODE__ID = 4; - - /** Identifier for the property {@link #getCountry()} in binary format. */ - static final int COUNTRY__ID = 5; - - /** Identifier for the property {@link #getCityCode()} in binary format. */ - static final int CITY_CODE__ID = 6; - - /** Identifier for the property {@link #getCity()} in binary format. */ - static final int CITY__ID = 7; - private String _shortcut = null; private String _plus = ""; @@ -71,7 +50,7 @@ public static de.haumacher.phoneblock.analysis.PhoneNumer create() { /** * Creates a {@link PhoneNumer} instance. * - * @see de.haumacher.phoneblock.analysis.PhoneNumer#create() + * @see de.haumacher.phoneblock.db.model.PhoneNumer#create() */ protected PhoneNumer() { super(); @@ -84,7 +63,7 @@ public final String getShortcut() { /** * @see #getShortcut() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setShortcut(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setShortcut(String value) { internalSetShortcut(value); return this; } @@ -109,7 +88,7 @@ public final String getPlus() { /** * @see #getPlus() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setPlus(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setPlus(String value) { internalSetPlus(value); return this; } @@ -127,7 +106,7 @@ public final String getZeroZero() { /** * @see #getZeroZero() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setZeroZero(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setZeroZero(String value) { internalSetZeroZero(value); return this; } @@ -145,7 +124,7 @@ public final String getCountryCode() { /** * @see #getCountryCode() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setCountryCode(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setCountryCode(String value) { internalSetCountryCode(value); return this; } @@ -163,7 +142,7 @@ public final String getCountry() { /** * @see #getCountry() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setCountry(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setCountry(String value) { internalSetCountry(value); return this; } @@ -181,7 +160,7 @@ public final String getCityCode() { /** * @see #getCityCode() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setCityCode(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setCityCode(String value) { internalSetCityCode(value); return this; } @@ -206,7 +185,7 @@ public final String getCity() { /** * @see #getCity() */ - public de.haumacher.phoneblock.analysis.PhoneNumer setCity(String value) { + public de.haumacher.phoneblock.db.model.PhoneNumer setCity(String value) { internalSetCity(value); return this; } @@ -227,7 +206,7 @@ public final boolean hasCity() { protected de.haumacher.msgbuf.observer.Listener _listener = de.haumacher.msgbuf.observer.Listener.NONE; @Override - public de.haumacher.phoneblock.analysis.PhoneNumer registerListener(de.haumacher.msgbuf.observer.Listener l) { + public de.haumacher.phoneblock.db.model.PhoneNumer registerListener(de.haumacher.msgbuf.observer.Listener l) { internalRegisterListener(l); return this; } @@ -237,7 +216,7 @@ protected final void internalRegisterListener(de.haumacher.msgbuf.observer.Liste } @Override - public de.haumacher.phoneblock.analysis.PhoneNumer unregisterListener(de.haumacher.msgbuf.observer.Listener l) { + public de.haumacher.phoneblock.db.model.PhoneNumer unregisterListener(de.haumacher.msgbuf.observer.Listener l) { internalUnregisterListener(l); return this; } @@ -294,8 +273,8 @@ public void set(String field, Object value) { } /** Reads a new instance from the given reader. */ - public static de.haumacher.phoneblock.analysis.PhoneNumer readPhoneNumer(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { - de.haumacher.phoneblock.analysis.PhoneNumer result = new de.haumacher.phoneblock.analysis.PhoneNumer(); + public static de.haumacher.phoneblock.db.model.PhoneNumer readPhoneNumer(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.model.PhoneNumer result = new de.haumacher.phoneblock.db.model.PhoneNumer(); result.readContent(in); return result; } @@ -344,78 +323,172 @@ protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) t } } + /** XML element name representing a {@link de.haumacher.phoneblock.db.model.PhoneNumer} type. */ + public static final String PHONE_NUMER__XML_ELEMENT = "phone-numer"; + + /** XML attribute or element name of a {@link #getShortcut} property. */ + private static final String SHORTCUT__XML_ATTR = "shortcut"; + + /** XML attribute or element name of a {@link #getPlus} property. */ + private static final String PLUS__XML_ATTR = "plus"; + + /** XML attribute or element name of a {@link #getZeroZero} property. */ + private static final String ZERO_ZERO__XML_ATTR = "zero-zero"; + + /** XML attribute or element name of a {@link #getCountryCode} property. */ + private static final String COUNTRY_CODE__XML_ATTR = "country-code"; + + /** XML attribute or element name of a {@link #getCountry} property. */ + private static final String COUNTRY__XML_ATTR = "country"; + + /** XML attribute or element name of a {@link #getCityCode} property. */ + private static final String CITY_CODE__XML_ATTR = "city-code"; + + /** XML attribute or element name of a {@link #getCity} property. */ + private static final String CITY__XML_ATTR = "city"; + @Override - public final void writeTo(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { - out.beginObject(); - writeFields(out); - out.endObject(); + public String getXmlTagName() { + return PHONE_NUMER__XML_ELEMENT; } - /** - * Serializes all fields of this instance to the given binary output. - * - * @param out - * The binary output to write to. - * @throws java.io.IOException If writing fails. - */ - protected void writeFields(de.haumacher.msgbuf.binary.DataWriter out) throws java.io.IOException { - if (hasShortcut()) { - out.name(SHORTCUT__ID); - out.value(getShortcut()); - } - out.name(PLUS__ID); - out.value(getPlus()); - out.name(ZERO_ZERO__ID); - out.value(getZeroZero()); - out.name(COUNTRY_CODE__ID); - out.value(getCountryCode()); - out.name(COUNTRY__ID); - out.value(getCountry()); - if (hasCityCode()) { - out.name(CITY_CODE__ID); - out.value(getCityCode()); - } - if (hasCity()) { - out.name(CITY__ID); - out.value(getCity()); - } + @Override + public final void writeContent(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + writeAttributes(out); + writeElements(out); } - /** Reads a new instance from the given reader. */ - public static de.haumacher.phoneblock.analysis.PhoneNumer readPhoneNumer(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { - in.beginObject(); - de.haumacher.phoneblock.analysis.PhoneNumer result = de.haumacher.phoneblock.analysis.PhoneNumer.readPhoneNumer_Content(in); - in.endObject(); - return result; + /** Serializes all fields that are written as XML attributes. */ + protected void writeAttributes(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + out.writeAttribute(SHORTCUT__XML_ATTR, getShortcut()); + out.writeAttribute(PLUS__XML_ATTR, getPlus()); + out.writeAttribute(ZERO_ZERO__XML_ATTR, getZeroZero()); + out.writeAttribute(COUNTRY_CODE__XML_ATTR, getCountryCode()); + out.writeAttribute(COUNTRY__XML_ATTR, getCountry()); + out.writeAttribute(CITY_CODE__XML_ATTR, getCityCode()); + out.writeAttribute(CITY__XML_ATTR, getCity()); } - /** Helper for creating an object of type {@link de.haumacher.phoneblock.analysis.PhoneNumer} from a polymorphic composition. */ - public static de.haumacher.phoneblock.analysis.PhoneNumer readPhoneNumer_Content(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { - de.haumacher.phoneblock.analysis.PhoneNumer result = new PhoneNumer(); - result.readContent(in); + /** Serializes all fields that are written as XML elements. */ + protected void writeElements(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + // No element fields. + } + + /** Creates a new {@link de.haumacher.phoneblock.db.model.PhoneNumer} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static PhoneNumer readPhoneNumer_XmlContent(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + PhoneNumer result = new PhoneNumer(); + result.readContentXml(in); return result; } - /** Helper for reading all fields of this instance. */ - protected final void readContent(de.haumacher.msgbuf.binary.DataReader in) throws java.io.IOException { - while (in.hasNext()) { - int field = in.nextName(); - readField(in, field); + /** Reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + protected final void readContentXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + for (int n = 0, cnt = in.getAttributeCount(); n < cnt; n++) { + String name = in.getAttributeLocalName(n); + String value = in.getAttributeValue(n); + + readFieldXmlAttribute(name, value); + } + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + assert event == javax.xml.stream.XMLStreamConstants.START_ELEMENT; + + String localName = in.getLocalName(); + readFieldXmlElement(in, localName); } } - /** Consumes the value for the field with the given ID and assigns its value. */ - protected void readField(de.haumacher.msgbuf.binary.DataReader in, int field) throws java.io.IOException { - switch (field) { - case SHORTCUT__ID: setShortcut(in.nextString()); break; - case PLUS__ID: setPlus(in.nextString()); break; - case ZERO_ZERO__ID: setZeroZero(in.nextString()); break; - case COUNTRY_CODE__ID: setCountryCode(in.nextString()); break; - case COUNTRY__ID: setCountry(in.nextString()); break; - case CITY_CODE__ID: setCityCode(in.nextString()); break; - case CITY__ID: setCity(in.nextString()); break; - default: in.skipValue(); + /** Parses the given attribute value and assigns it to the field with the given name. */ + protected void readFieldXmlAttribute(String name, String value) { + switch (name) { + case SHORTCUT__XML_ATTR: { + setShortcut(value); + break; + } + case PLUS__XML_ATTR: { + setPlus(value); + break; + } + case ZERO_ZERO__XML_ATTR: { + setZeroZero(value); + break; + } + case COUNTRY_CODE__XML_ATTR: { + setCountryCode(value); + break; + } + case COUNTRY__XML_ATTR: { + setCountry(value); + break; + } + case CITY_CODE__XML_ATTR: { + setCityCode(value); + break; + } + case CITY__XML_ATTR: { + setCity(value); + break; + } + default: { + // Skip unknown attribute. + } + } + } + + /** Reads the element under the cursor and assigns its contents to the field with the given name. */ + protected void readFieldXmlElement(javax.xml.stream.XMLStreamReader in, String localName) throws javax.xml.stream.XMLStreamException { + switch (localName) { + case SHORTCUT__XML_ATTR: { + setShortcut(in.getElementText()); + break; + } + case PLUS__XML_ATTR: { + setPlus(in.getElementText()); + break; + } + case ZERO_ZERO__XML_ATTR: { + setZeroZero(in.getElementText()); + break; + } + case COUNTRY_CODE__XML_ATTR: { + setCountryCode(in.getElementText()); + break; + } + case COUNTRY__XML_ATTR: { + setCountry(in.getElementText()); + break; + } + case CITY_CODE__XML_ATTR: { + setCityCode(in.getElementText()); + break; + } + case CITY__XML_ATTR: { + setCity(in.getElementText()); + break; + } + default: { + internalSkipUntilMatchingEndElement(in); + } + } + } + + protected static final void internalSkipUntilMatchingEndElement(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + int level = 0; + while (true) { + switch (in.next()) { + case javax.xml.stream.XMLStreamConstants.START_ELEMENT: level++; break; + case javax.xml.stream.XMLStreamConstants.END_ELEMENT: if (level == 0) { return; } else { level--; break; } + } } } + /** Creates a new {@link PhoneNumer} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static PhoneNumer readPhoneNumer(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + in.nextTag(); + return de.haumacher.phoneblock.db.model.PhoneNumer.readPhoneNumer_XmlContent(in); + } + } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Ratings.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Ratings.java index e9aa7acf..8564d7f5 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Ratings.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/Ratings.java @@ -15,7 +15,7 @@ public static de.haumacher.phoneblock.db.model.Ratings create() { /** @see #getValues() */ public static final String VALUES__PROP = "values"; - private final java.util.List _values = new de.haumacher.msgbuf.util.ReferenceList() { + private final java.util.List _values = new de.haumacher.msgbuf.util.ReferenceList<>() { @Override protected void beforeAdd(int index, de.haumacher.phoneblock.db.model.Rating element) { _listener.beforeAdd(Ratings.this, VALUES__PROP, index, element); diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SearchResult.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SearchResult.java new file mode 100644 index 00000000..2da09a1e --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SearchResult.java @@ -0,0 +1,848 @@ +package de.haumacher.phoneblock.db.model; + +public class SearchResult extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.observer.Observable, de.haumacher.msgbuf.xml.XmlSerializable { + + /** + * Creates a {@link de.haumacher.phoneblock.db.model.SearchResult} instance. + */ + public static de.haumacher.phoneblock.db.model.SearchResult create() { + return new de.haumacher.phoneblock.db.model.SearchResult(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.db.model.SearchResult} type in JSON format. */ + public static final String SEARCH_RESULT__TYPE = "SearchResult"; + + /** @see #getPhoneId() */ + public static final String PHONE_ID__PROP = "phoneId"; + + /** @see #getNumber() */ + public static final String NUMBER__PROP = "number"; + + /** @see #getComments() */ + public static final String COMMENTS__PROP = "comments"; + + /** @see #getInfo() */ + public static final String INFO__PROP = "info"; + + /** @see #getSearches() */ + public static final String SEARCHES__PROP = "searches"; + + /** @see #getAiSummary() */ + public static final String AI_SUMMARY__PROP = "aiSummary"; + + /** @see #getRelatedNumbers() */ + public static final String RELATED_NUMBERS__PROP = "relatedNumbers"; + + /** @see #getPrev() */ + public static final String PREV__PROP = "prev"; + + /** @see #getNext() */ + public static final String NEXT__PROP = "next"; + + /** @see #getTopRating() */ + public static final String TOP_RATING__PROP = "topRating"; + + /** @see #getRatings() */ + public static final String RATINGS__PROP = "ratings"; + + private String _phoneId = ""; + + private de.haumacher.phoneblock.db.model.PhoneNumer _number = null; + + private final java.util.List _comments = new de.haumacher.msgbuf.util.ReferenceList<>() { + @Override + protected void beforeAdd(int index, de.haumacher.phoneblock.db.model.UserComment element) { + _listener.beforeAdd(SearchResult.this, COMMENTS__PROP, index, element); + } + + @Override + protected void afterRemove(int index, de.haumacher.phoneblock.db.model.UserComment element) { + _listener.afterRemove(SearchResult.this, COMMENTS__PROP, index, element); + } + }; + + private de.haumacher.phoneblock.db.model.SpamReport _info = null; + + private final java.util.List _searches = new de.haumacher.msgbuf.util.ReferenceList<>() { + @Override + protected void beforeAdd(int index, de.haumacher.phoneblock.db.model.SearchInfo element) { + _listener.beforeAdd(SearchResult.this, SEARCHES__PROP, index, element); + } + + @Override + protected void afterRemove(int index, de.haumacher.phoneblock.db.model.SearchInfo element) { + _listener.afterRemove(SearchResult.this, SEARCHES__PROP, index, element); + } + }; + + private String _aiSummary = ""; + + private final java.util.List _relatedNumbers = new de.haumacher.msgbuf.util.ReferenceList<>() { + @Override + protected void beforeAdd(int index, String element) { + _listener.beforeAdd(SearchResult.this, RELATED_NUMBERS__PROP, index, element); + } + + @Override + protected void afterRemove(int index, String element) { + _listener.afterRemove(SearchResult.this, RELATED_NUMBERS__PROP, index, element); + } + }; + + private String _prev = ""; + + private String _next = ""; + + private de.haumacher.phoneblock.db.model.Rating _topRating = de.haumacher.phoneblock.db.model.Rating.A_LEGITIMATE; + + private final java.util.Map _ratings = new de.haumacher.msgbuf.util.ReferenceMap<>() { + @Override + protected void beforeAdd(de.haumacher.phoneblock.db.model.Rating index, Integer element) { + _listener.beforeAdd(SearchResult.this, RATINGS__PROP, index, element); + } + + @Override + protected void afterRemove(de.haumacher.phoneblock.db.model.Rating index, Integer element) { + _listener.afterRemove(SearchResult.this, RATINGS__PROP, index, element); + } + }; + + /** + * Creates a {@link SearchResult} instance. + * + * @see de.haumacher.phoneblock.db.model.SearchResult#create() + */ + protected SearchResult() { + super(); + } + + public final String getPhoneId() { + return _phoneId; + } + + /** + * @see #getPhoneId() + */ + public de.haumacher.phoneblock.db.model.SearchResult setPhoneId(String value) { + internalSetPhoneId(value); + return this; + } + + /** Internal setter for {@link #getPhoneId()} without chain call utility. */ + protected final void internalSetPhoneId(String value) { + _listener.beforeSet(this, PHONE_ID__PROP, value); + _phoneId = value; + } + + public final de.haumacher.phoneblock.db.model.PhoneNumer getNumber() { + return _number; + } + + /** + * @see #getNumber() + */ + public de.haumacher.phoneblock.db.model.SearchResult setNumber(de.haumacher.phoneblock.db.model.PhoneNumer value) { + internalSetNumber(value); + return this; + } + + /** Internal setter for {@link #getNumber()} without chain call utility. */ + protected final void internalSetNumber(de.haumacher.phoneblock.db.model.PhoneNumer value) { + _listener.beforeSet(this, NUMBER__PROP, value); + _number = value; + } + + /** + * Checks, whether {@link #getNumber()} has a value. + */ + public final boolean hasNumber() { + return _number != null; + } + + public final java.util.List getComments() { + return _comments; + } + + /** + * @see #getComments() + */ + public de.haumacher.phoneblock.db.model.SearchResult setComments(java.util.List value) { + internalSetComments(value); + return this; + } + + /** Internal setter for {@link #getComments()} without chain call utility. */ + protected final void internalSetComments(java.util.List value) { + if (value == null) throw new IllegalArgumentException("Property 'comments' cannot be null."); + _comments.clear(); + _comments.addAll(value); + } + + /** + * Adds a value to the {@link #getComments()} list. + */ + public de.haumacher.phoneblock.db.model.SearchResult addComment(de.haumacher.phoneblock.db.model.UserComment value) { + internalAddComment(value); + return this; + } + + /** Implementation of {@link #addComment(de.haumacher.phoneblock.db.model.UserComment)} without chain call utility. */ + protected final void internalAddComment(de.haumacher.phoneblock.db.model.UserComment value) { + _comments.add(value); + } + + /** + * Removes a value from the {@link #getComments()} list. + */ + public final void removeComment(de.haumacher.phoneblock.db.model.UserComment value) { + _comments.remove(value); + } + + public final de.haumacher.phoneblock.db.model.SpamReport getInfo() { + return _info; + } + + /** + * @see #getInfo() + */ + public de.haumacher.phoneblock.db.model.SearchResult setInfo(de.haumacher.phoneblock.db.model.SpamReport value) { + internalSetInfo(value); + return this; + } + + /** Internal setter for {@link #getInfo()} without chain call utility. */ + protected final void internalSetInfo(de.haumacher.phoneblock.db.model.SpamReport value) { + _listener.beforeSet(this, INFO__PROP, value); + _info = value; + } + + /** + * Checks, whether {@link #getInfo()} has a value. + */ + public final boolean hasInfo() { + return _info != null; + } + + public final java.util.List getSearches() { + return _searches; + } + + /** + * @see #getSearches() + */ + public de.haumacher.phoneblock.db.model.SearchResult setSearches(java.util.List value) { + internalSetSearches(value); + return this; + } + + /** Internal setter for {@link #getSearches()} without chain call utility. */ + protected final void internalSetSearches(java.util.List value) { + if (value == null) throw new IllegalArgumentException("Property 'searches' cannot be null."); + _searches.clear(); + _searches.addAll(value); + } + + /** + * Adds a value to the {@link #getSearches()} list. + */ + public de.haumacher.phoneblock.db.model.SearchResult addSearche(de.haumacher.phoneblock.db.model.SearchInfo value) { + internalAddSearche(value); + return this; + } + + /** Implementation of {@link #addSearche(de.haumacher.phoneblock.db.model.SearchInfo)} without chain call utility. */ + protected final void internalAddSearche(de.haumacher.phoneblock.db.model.SearchInfo value) { + _searches.add(value); + } + + /** + * Removes a value from the {@link #getSearches()} list. + */ + public final void removeSearche(de.haumacher.phoneblock.db.model.SearchInfo value) { + _searches.remove(value); + } + + public final String getAiSummary() { + return _aiSummary; + } + + /** + * @see #getAiSummary() + */ + public de.haumacher.phoneblock.db.model.SearchResult setAiSummary(String value) { + internalSetAiSummary(value); + return this; + } + + /** Internal setter for {@link #getAiSummary()} without chain call utility. */ + protected final void internalSetAiSummary(String value) { + _listener.beforeSet(this, AI_SUMMARY__PROP, value); + _aiSummary = value; + } + + public final java.util.List getRelatedNumbers() { + return _relatedNumbers; + } + + /** + * @see #getRelatedNumbers() + */ + public de.haumacher.phoneblock.db.model.SearchResult setRelatedNumbers(java.util.List value) { + internalSetRelatedNumbers(value); + return this; + } + + /** Internal setter for {@link #getRelatedNumbers()} without chain call utility. */ + protected final void internalSetRelatedNumbers(java.util.List value) { + _relatedNumbers.clear(); + _relatedNumbers.addAll(value); + } + + /** + * Adds a value to the {@link #getRelatedNumbers()} list. + */ + public de.haumacher.phoneblock.db.model.SearchResult addRelatedNumber(String value) { + internalAddRelatedNumber(value); + return this; + } + + /** Implementation of {@link #addRelatedNumber(String)} without chain call utility. */ + protected final void internalAddRelatedNumber(String value) { + _relatedNumbers.add(value); + } + + /** + * Removes a value from the {@link #getRelatedNumbers()} list. + */ + public final void removeRelatedNumber(String value) { + _relatedNumbers.remove(value); + } + + public final String getPrev() { + return _prev; + } + + /** + * @see #getPrev() + */ + public de.haumacher.phoneblock.db.model.SearchResult setPrev(String value) { + internalSetPrev(value); + return this; + } + + /** Internal setter for {@link #getPrev()} without chain call utility. */ + protected final void internalSetPrev(String value) { + _listener.beforeSet(this, PREV__PROP, value); + _prev = value; + } + + public final String getNext() { + return _next; + } + + /** + * @see #getNext() + */ + public de.haumacher.phoneblock.db.model.SearchResult setNext(String value) { + internalSetNext(value); + return this; + } + + /** Internal setter for {@link #getNext()} without chain call utility. */ + protected final void internalSetNext(String value) { + _listener.beforeSet(this, NEXT__PROP, value); + _next = value; + } + + public final de.haumacher.phoneblock.db.model.Rating getTopRating() { + return _topRating; + } + + /** + * @see #getTopRating() + */ + public de.haumacher.phoneblock.db.model.SearchResult setTopRating(de.haumacher.phoneblock.db.model.Rating value) { + internalSetTopRating(value); + return this; + } + + /** Internal setter for {@link #getTopRating()} without chain call utility. */ + protected final void internalSetTopRating(de.haumacher.phoneblock.db.model.Rating value) { + if (value == null) throw new IllegalArgumentException("Property 'topRating' cannot be null."); + _listener.beforeSet(this, TOP_RATING__PROP, value); + _topRating = value; + } + + public final java.util.Map getRatings() { + return _ratings; + } + + /** + * @see #getRatings() + */ + public de.haumacher.phoneblock.db.model.SearchResult setRatings(java.util.Map value) { + internalSetRatings(value); + return this; + } + + /** Internal setter for {@link #getRatings()} without chain call utility. */ + protected final void internalSetRatings(java.util.Map value) { + if (value == null) throw new IllegalArgumentException("Property 'ratings' cannot be null."); + _ratings.clear(); + _ratings.putAll(value); + } + + /** + * Adds a key value pair to the {@link #getRatings()} map. + */ + public de.haumacher.phoneblock.db.model.SearchResult putRating(de.haumacher.phoneblock.db.model.Rating key, int value) { + internalPutRating(key, value); + return this; + } + + /** Implementation of {@link #putRating(de.haumacher.phoneblock.db.model.Rating, int)} without chain call utility. */ + protected final void internalPutRating(de.haumacher.phoneblock.db.model.Rating key, int value) { + if (_ratings.containsKey(key)) { + throw new IllegalArgumentException("Property 'ratings' already contains a value for key '" + key + "'."); + } + _ratings.put(key, value); + } + + /** + * Removes a key from the {@link #getRatings()} map. + */ + public final void removeRating(de.haumacher.phoneblock.db.model.Rating key) { + _ratings.remove(key); + } + + protected de.haumacher.msgbuf.observer.Listener _listener = de.haumacher.msgbuf.observer.Listener.NONE; + + @Override + public de.haumacher.phoneblock.db.model.SearchResult registerListener(de.haumacher.msgbuf.observer.Listener l) { + internalRegisterListener(l); + return this; + } + + protected final void internalRegisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.register(_listener, l); + } + + @Override + public de.haumacher.phoneblock.db.model.SearchResult unregisterListener(de.haumacher.msgbuf.observer.Listener l) { + internalUnregisterListener(l); + return this; + } + + protected final void internalUnregisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.unregister(_listener, l); + } + + @Override + public String jsonType() { + return SEARCH_RESULT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + PHONE_ID__PROP, + NUMBER__PROP, + COMMENTS__PROP, + INFO__PROP, + SEARCHES__PROP, + AI_SUMMARY__PROP, + RELATED_NUMBERS__PROP, + PREV__PROP, + NEXT__PROP, + TOP_RATING__PROP, + RATINGS__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case PHONE_ID__PROP: return getPhoneId(); + case NUMBER__PROP: return getNumber(); + case COMMENTS__PROP: return getComments(); + case INFO__PROP: return getInfo(); + case SEARCHES__PROP: return getSearches(); + case AI_SUMMARY__PROP: return getAiSummary(); + case RELATED_NUMBERS__PROP: return getRelatedNumbers(); + case PREV__PROP: return getPrev(); + case NEXT__PROP: return getNext(); + case TOP_RATING__PROP: return getTopRating(); + case RATINGS__PROP: return getRatings(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case PHONE_ID__PROP: internalSetPhoneId((String) value); break; + case NUMBER__PROP: internalSetNumber((de.haumacher.phoneblock.db.model.PhoneNumer) value); break; + case COMMENTS__PROP: internalSetComments(de.haumacher.msgbuf.util.Conversions.asList(de.haumacher.phoneblock.db.model.UserComment.class, value)); break; + case INFO__PROP: internalSetInfo((de.haumacher.phoneblock.db.model.SpamReport) value); break; + case SEARCHES__PROP: internalSetSearches(de.haumacher.msgbuf.util.Conversions.asList(de.haumacher.phoneblock.db.model.SearchInfo.class, value)); break; + case AI_SUMMARY__PROP: internalSetAiSummary((String) value); break; + case RELATED_NUMBERS__PROP: internalSetRelatedNumbers(de.haumacher.msgbuf.util.Conversions.asList(String.class, value)); break; + case PREV__PROP: internalSetPrev((String) value); break; + case NEXT__PROP: internalSetNext((String) value); break; + case TOP_RATING__PROP: internalSetTopRating((de.haumacher.phoneblock.db.model.Rating) value); break; + case RATINGS__PROP: internalSetRatings((java.util.Map) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.model.SearchResult readSearchResult(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.model.SearchResult result = new de.haumacher.phoneblock.db.model.SearchResult(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(PHONE_ID__PROP); + out.value(getPhoneId()); + if (hasNumber()) { + out.name(NUMBER__PROP); + getNumber().writeTo(out); + } + out.name(COMMENTS__PROP); + out.beginArray(); + for (de.haumacher.phoneblock.db.model.UserComment x : getComments()) { + x.writeTo(out); + } + out.endArray(); + if (hasInfo()) { + out.name(INFO__PROP); + getInfo().writeTo(out); + } + out.name(SEARCHES__PROP); + out.beginArray(); + for (de.haumacher.phoneblock.db.model.SearchInfo x : getSearches()) { + x.writeTo(out); + } + out.endArray(); + out.name(AI_SUMMARY__PROP); + out.value(getAiSummary()); + out.name(RELATED_NUMBERS__PROP); + out.beginArray(); + for (String x : getRelatedNumbers()) { + out.value(x); + } + out.endArray(); + out.name(PREV__PROP); + out.value(getPrev()); + out.name(NEXT__PROP); + out.value(getNext()); + out.name(TOP_RATING__PROP); + getTopRating().writeTo(out); + out.name(RATINGS__PROP); + out.beginArray(); + for (java.util.Map.Entry entry : getRatings().entrySet()) { + out.beginObject(); + out.name("key"); + entry.getKey().writeTo(out); + out.name("value"); + out.value(entry.getValue()); + out.endObject(); + } + out.endArray(); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case PHONE_ID__PROP: setPhoneId(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case NUMBER__PROP: setNumber(de.haumacher.phoneblock.db.model.PhoneNumer.readPhoneNumer(in)); break; + case COMMENTS__PROP: { + in.beginArray(); + while (in.hasNext()) { + addComment(de.haumacher.phoneblock.db.model.UserComment.readUserComment(in)); + } + in.endArray(); + } + break; + case INFO__PROP: setInfo(de.haumacher.phoneblock.db.model.SpamReport.readSpamReport(in)); break; + case SEARCHES__PROP: { + in.beginArray(); + while (in.hasNext()) { + addSearche(de.haumacher.phoneblock.db.model.SearchInfo.readSearchInfo(in)); + } + in.endArray(); + } + break; + case AI_SUMMARY__PROP: setAiSummary(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case RELATED_NUMBERS__PROP: { + in.beginArray(); + while (in.hasNext()) { + addRelatedNumber(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); + } + in.endArray(); + } + break; + case PREV__PROP: setPrev(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case NEXT__PROP: setNext(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case TOP_RATING__PROP: setTopRating(de.haumacher.phoneblock.db.model.Rating.readRating(in)); break; + case RATINGS__PROP: { + in.beginArray(); + while (in.hasNext()) { + in.beginObject(); + de.haumacher.phoneblock.db.model.Rating key = de.haumacher.phoneblock.db.model.Rating.A_LEGITIMATE; + int value = 0; + while (in.hasNext()) { + switch (in.nextName()) { + case "key": key = de.haumacher.phoneblock.db.model.Rating.readRating(in); break; + case "value": value = in.nextInt(); break; + default: in.skipValue(); break; + } + } + putRating(key, value); + in.endObject(); + } + in.endArray(); + break; + } + default: super.readField(in, field); + } + } + + /** XML element name representing a {@link de.haumacher.phoneblock.db.model.SearchResult} type. */ + public static final String SEARCH_RESULT__XML_ELEMENT = "search-result"; + + /** XML attribute or element name of a {@link #getPhoneId} property. */ + private static final String PHONE_ID__XML_ATTR = "phone-id"; + + /** XML attribute or element name of a {@link #getNumber} property. */ + private static final String NUMBER__XML_ATTR = "number"; + + /** XML attribute or element name of a {@link #getComments} property. */ + private static final String COMMENTS__XML_ATTR = "comments"; + + /** XML attribute or element name of a {@link #getInfo} property. */ + private static final String INFO__XML_ATTR = "info"; + + /** XML attribute or element name of a {@link #getSearches} property. */ + private static final String SEARCHES__XML_ATTR = "searches"; + + /** XML attribute or element name of a {@link #getAiSummary} property. */ + private static final String AI_SUMMARY__XML_ATTR = "ai-summary"; + + /** XML attribute or element name of a {@link #getRelatedNumbers} property. */ + private static final String RELATED_NUMBERS__XML_ATTR = "related-numbers"; + + /** XML attribute or element name of a {@link #getPrev} property. */ + private static final String PREV__XML_ATTR = "prev"; + + /** XML attribute or element name of a {@link #getNext} property. */ + private static final String NEXT__XML_ATTR = "next"; + + /** XML attribute or element name of a {@link #getTopRating} property. */ + private static final String TOP_RATING__XML_ATTR = "top-rating"; + + /** XML attribute or element name of a {@link #getRatings} property. */ + private static final String RATINGS__XML_ATTR = "ratings"; + + @Override + public String getXmlTagName() { + return SEARCH_RESULT__XML_ELEMENT; + } + + @Override + public final void writeContent(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + writeAttributes(out); + writeElements(out); + } + + /** Serializes all fields that are written as XML attributes. */ + protected void writeAttributes(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + out.writeAttribute(PHONE_ID__XML_ATTR, getPhoneId()); + out.writeAttribute(AI_SUMMARY__XML_ATTR, getAiSummary()); + out.writeAttribute(RELATED_NUMBERS__XML_ATTR, getRelatedNumbers().stream().map(x -> x).collect(java.util.stream.Collectors.joining(", "))); + out.writeAttribute(PREV__XML_ATTR, getPrev()); + out.writeAttribute(NEXT__XML_ATTR, getNext()); + out.writeAttribute(TOP_RATING__XML_ATTR, getTopRating().protocolName()); + } + + /** Serializes all fields that are written as XML elements. */ + protected void writeElements(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + if (hasNumber()) { + out.writeStartElement(NUMBER__XML_ATTR); + getNumber().writeContent(out); + out.writeEndElement(); + } + out.writeStartElement(COMMENTS__XML_ATTR); + for (de.haumacher.phoneblock.db.model.UserComment element : getComments()) { + element.writeTo(out); + } + out.writeEndElement(); + if (hasInfo()) { + out.writeStartElement(INFO__XML_ATTR); + getInfo().writeContent(out); + out.writeEndElement(); + } + out.writeStartElement(SEARCHES__XML_ATTR); + for (de.haumacher.phoneblock.db.model.SearchInfo element : getSearches()) { + element.writeTo(out); + } + out.writeEndElement(); + } + + /** Creates a new {@link de.haumacher.phoneblock.db.model.SearchResult} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static SearchResult readSearchResult_XmlContent(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + SearchResult result = new SearchResult(); + result.readContentXml(in); + return result; + } + + /** Reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + protected final void readContentXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + for (int n = 0, cnt = in.getAttributeCount(); n < cnt; n++) { + String name = in.getAttributeLocalName(n); + String value = in.getAttributeValue(n); + + readFieldXmlAttribute(name, value); + } + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + assert event == javax.xml.stream.XMLStreamConstants.START_ELEMENT; + + String localName = in.getLocalName(); + readFieldXmlElement(in, localName); + } + } + + /** Parses the given attribute value and assigns it to the field with the given name. */ + protected void readFieldXmlAttribute(String name, String value) { + switch (name) { + case PHONE_ID__XML_ATTR: { + setPhoneId(value); + break; + } + case AI_SUMMARY__XML_ATTR: { + setAiSummary(value); + break; + } + case RELATED_NUMBERS__XML_ATTR: { + setRelatedNumbers(java.util.Arrays.stream(value.split("\\s*,\\s*")).map(x -> x).collect(java.util.stream.Collectors.toList())); + break; + } + case PREV__XML_ATTR: { + setPrev(value); + break; + } + case NEXT__XML_ATTR: { + setNext(value); + break; + } + case TOP_RATING__XML_ATTR: { + setTopRating(de.haumacher.phoneblock.db.model.Rating.valueOfProtocol(value)); + break; + } + default: { + // Skip unknown attribute. + } + } + } + + /** Reads the element under the cursor and assigns its contents to the field with the given name. */ + protected void readFieldXmlElement(javax.xml.stream.XMLStreamReader in, String localName) throws javax.xml.stream.XMLStreamException { + switch (localName) { + case PHONE_ID__XML_ATTR: { + setPhoneId(in.getElementText()); + break; + } + case NUMBER__XML_ATTR: { + setNumber(de.haumacher.phoneblock.db.model.PhoneNumer.readPhoneNumer_XmlContent(in)); + break; + } + case COMMENTS__XML_ATTR: { + internalReadCommentsListXml(in); + break; + } + case INFO__XML_ATTR: { + setInfo(de.haumacher.phoneblock.db.model.SpamReport.readSpamReport_XmlContent(in)); + break; + } + case SEARCHES__XML_ATTR: { + internalReadSearchesListXml(in); + break; + } + case AI_SUMMARY__XML_ATTR: { + setAiSummary(in.getElementText()); + break; + } + case RELATED_NUMBERS__XML_ATTR: { + setRelatedNumbers(java.util.Arrays.stream(in.getElementText().split("\\s*,\\s*")).map(x -> x).collect(java.util.stream.Collectors.toList())); + break; + } + case PREV__XML_ATTR: { + setPrev(in.getElementText()); + break; + } + case NEXT__XML_ATTR: { + setNext(in.getElementText()); + break; + } + case TOP_RATING__XML_ATTR: { + setTopRating(de.haumacher.phoneblock.db.model.Rating.valueOfProtocol(in.getElementText())); + break; + } + default: { + internalSkipUntilMatchingEndElement(in); + } + } + } + + protected static final void internalSkipUntilMatchingEndElement(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + int level = 0; + while (true) { + switch (in.next()) { + case javax.xml.stream.XMLStreamConstants.START_ELEMENT: level++; break; + case javax.xml.stream.XMLStreamConstants.END_ELEMENT: if (level == 0) { return; } else { level--; break; } + } + } + } + + private void internalReadCommentsListXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + + addComment(de.haumacher.phoneblock.db.model.UserComment.readUserComment_XmlContent(in)); + } + } + + private void internalReadSearchesListXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + + addSearche(de.haumacher.phoneblock.db.model.SearchInfo.readSearchInfo_XmlContent(in)); + } + } + + /** Creates a new {@link SearchResult} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static SearchResult readSearchResult(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + in.nextTag(); + return de.haumacher.phoneblock.db.model.SearchResult.readSearchResult_XmlContent(in); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SpamReport.java b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SpamReport.java new file mode 100644 index 00000000..9cc859fb --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/SpamReport.java @@ -0,0 +1,426 @@ +package de.haumacher.phoneblock.db.model; + +public class SpamReport extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.msgbuf.observer.Observable, de.haumacher.msgbuf.xml.XmlSerializable { + + /** + * Creates a {@link de.haumacher.phoneblock.db.model.SpamReport} instance. + */ + public static de.haumacher.phoneblock.db.model.SpamReport create() { + return new de.haumacher.phoneblock.db.model.SpamReport(); + } + + /** Identifier for the {@link de.haumacher.phoneblock.db.model.SpamReport} type in JSON format. */ + public static final String SPAM_REPORT__TYPE = "SpamReport"; + + /** @see #getPhone() */ + public static final String PHONE__PROP = "phone"; + + /** @see #getVotes() */ + public static final String VOTES__PROP = "votes"; + + /** @see #getLastUpdate() */ + public static final String LAST_UPDATE__PROP = "lastUpdate"; + + /** @see #getDateAdded() */ + public static final String DATE_ADDED__PROP = "dateAdded"; + + /** @see #isArchived() */ + public static final String ARCHIVED__PROP = "archived"; + + /** @see #isWhiteListed() */ + public static final String WHITE_LISTED__PROP = "whiteListed"; + + private String _phone = ""; + + private int _votes = 0; + + private long _lastUpdate = 0L; + + private long _dateAdded = 0L; + + private boolean _archived = false; + + private boolean _whiteListed = false; + + /** + * Creates a {@link SpamReport} instance. + * + * @see de.haumacher.phoneblock.db.model.SpamReport#create() + */ + protected SpamReport() { + super(); + } + + public final String getPhone() { + return _phone; + } + + /** + * @see #getPhone() + */ + public de.haumacher.phoneblock.db.model.SpamReport setPhone(String value) { + internalSetPhone(value); + return this; + } + + /** Internal setter for {@link #getPhone()} without chain call utility. */ + protected final void internalSetPhone(String value) { + _listener.beforeSet(this, PHONE__PROP, value); + _phone = value; + } + + public final int getVotes() { + return _votes; + } + + /** + * @see #getVotes() + */ + public de.haumacher.phoneblock.db.model.SpamReport setVotes(int value) { + internalSetVotes(value); + return this; + } + + /** Internal setter for {@link #getVotes()} without chain call utility. */ + protected final void internalSetVotes(int value) { + _listener.beforeSet(this, VOTES__PROP, value); + _votes = value; + } + + public final long getLastUpdate() { + return _lastUpdate; + } + + /** + * @see #getLastUpdate() + */ + public de.haumacher.phoneblock.db.model.SpamReport setLastUpdate(long value) { + internalSetLastUpdate(value); + return this; + } + + /** Internal setter for {@link #getLastUpdate()} without chain call utility. */ + protected final void internalSetLastUpdate(long value) { + _listener.beforeSet(this, LAST_UPDATE__PROP, value); + _lastUpdate = value; + } + + public final long getDateAdded() { + return _dateAdded; + } + + /** + * @see #getDateAdded() + */ + public de.haumacher.phoneblock.db.model.SpamReport setDateAdded(long value) { + internalSetDateAdded(value); + return this; + } + + /** Internal setter for {@link #getDateAdded()} without chain call utility. */ + protected final void internalSetDateAdded(long value) { + _listener.beforeSet(this, DATE_ADDED__PROP, value); + _dateAdded = value; + } + + public final boolean isArchived() { + return _archived; + } + + /** + * @see #isArchived() + */ + public de.haumacher.phoneblock.db.model.SpamReport setArchived(boolean value) { + internalSetArchived(value); + return this; + } + + /** Internal setter for {@link #isArchived()} without chain call utility. */ + protected final void internalSetArchived(boolean value) { + _listener.beforeSet(this, ARCHIVED__PROP, value); + _archived = value; + } + + public final boolean isWhiteListed() { + return _whiteListed; + } + + /** + * @see #isWhiteListed() + */ + public de.haumacher.phoneblock.db.model.SpamReport setWhiteListed(boolean value) { + internalSetWhiteListed(value); + return this; + } + + /** Internal setter for {@link #isWhiteListed()} without chain call utility. */ + protected final void internalSetWhiteListed(boolean value) { + _listener.beforeSet(this, WHITE_LISTED__PROP, value); + _whiteListed = value; + } + + protected de.haumacher.msgbuf.observer.Listener _listener = de.haumacher.msgbuf.observer.Listener.NONE; + + @Override + public de.haumacher.phoneblock.db.model.SpamReport registerListener(de.haumacher.msgbuf.observer.Listener l) { + internalRegisterListener(l); + return this; + } + + protected final void internalRegisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.register(_listener, l); + } + + @Override + public de.haumacher.phoneblock.db.model.SpamReport unregisterListener(de.haumacher.msgbuf.observer.Listener l) { + internalUnregisterListener(l); + return this; + } + + protected final void internalUnregisterListener(de.haumacher.msgbuf.observer.Listener l) { + _listener = de.haumacher.msgbuf.observer.Listener.unregister(_listener, l); + } + + @Override + public String jsonType() { + return SPAM_REPORT__TYPE; + } + + private static java.util.List PROPERTIES = java.util.Collections.unmodifiableList( + java.util.Arrays.asList( + PHONE__PROP, + VOTES__PROP, + LAST_UPDATE__PROP, + DATE_ADDED__PROP, + ARCHIVED__PROP, + WHITE_LISTED__PROP)); + + @Override + public java.util.List properties() { + return PROPERTIES; + } + + @Override + public Object get(String field) { + switch (field) { + case PHONE__PROP: return getPhone(); + case VOTES__PROP: return getVotes(); + case LAST_UPDATE__PROP: return getLastUpdate(); + case DATE_ADDED__PROP: return getDateAdded(); + case ARCHIVED__PROP: return isArchived(); + case WHITE_LISTED__PROP: return isWhiteListed(); + default: return null; + } + } + + @Override + public void set(String field, Object value) { + switch (field) { + case PHONE__PROP: internalSetPhone((String) value); break; + case VOTES__PROP: internalSetVotes((int) value); break; + case LAST_UPDATE__PROP: internalSetLastUpdate((long) value); break; + case DATE_ADDED__PROP: internalSetDateAdded((long) value); break; + case ARCHIVED__PROP: internalSetArchived((boolean) value); break; + case WHITE_LISTED__PROP: internalSetWhiteListed((boolean) value); break; + } + } + + /** Reads a new instance from the given reader. */ + public static de.haumacher.phoneblock.db.model.SpamReport readSpamReport(de.haumacher.msgbuf.json.JsonReader in) throws java.io.IOException { + de.haumacher.phoneblock.db.model.SpamReport result = new de.haumacher.phoneblock.db.model.SpamReport(); + result.readContent(in); + return result; + } + + @Override + public final void writeTo(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + writeContent(out); + } + + @Override + protected void writeFields(de.haumacher.msgbuf.json.JsonWriter out) throws java.io.IOException { + super.writeFields(out); + out.name(PHONE__PROP); + out.value(getPhone()); + out.name(VOTES__PROP); + out.value(getVotes()); + out.name(LAST_UPDATE__PROP); + out.value(getLastUpdate()); + out.name(DATE_ADDED__PROP); + out.value(getDateAdded()); + out.name(ARCHIVED__PROP); + out.value(isArchived()); + out.name(WHITE_LISTED__PROP); + out.value(isWhiteListed()); + } + + @Override + protected void readField(de.haumacher.msgbuf.json.JsonReader in, String field) throws java.io.IOException { + switch (field) { + case PHONE__PROP: setPhone(de.haumacher.msgbuf.json.JsonUtil.nextStringOptional(in)); break; + case VOTES__PROP: setVotes(in.nextInt()); break; + case LAST_UPDATE__PROP: setLastUpdate(in.nextLong()); break; + case DATE_ADDED__PROP: setDateAdded(in.nextLong()); break; + case ARCHIVED__PROP: setArchived(in.nextBoolean()); break; + case WHITE_LISTED__PROP: setWhiteListed(in.nextBoolean()); break; + default: super.readField(in, field); + } + } + + /** XML element name representing a {@link de.haumacher.phoneblock.db.model.SpamReport} type. */ + public static final String SPAM_REPORT__XML_ELEMENT = "spam-report"; + + /** XML attribute or element name of a {@link #getPhone} property. */ + private static final String PHONE__XML_ATTR = "phone"; + + /** XML attribute or element name of a {@link #getVotes} property. */ + private static final String VOTES__XML_ATTR = "votes"; + + /** XML attribute or element name of a {@link #getLastUpdate} property. */ + private static final String LAST_UPDATE__XML_ATTR = "last-update"; + + /** XML attribute or element name of a {@link #getDateAdded} property. */ + private static final String DATE_ADDED__XML_ATTR = "date-added"; + + /** XML attribute or element name of a {@link #isArchived} property. */ + private static final String ARCHIVED__XML_ATTR = "archived"; + + /** XML attribute or element name of a {@link #isWhiteListed} property. */ + private static final String WHITE_LISTED__XML_ATTR = "white-listed"; + + @Override + public String getXmlTagName() { + return SPAM_REPORT__XML_ELEMENT; + } + + @Override + public final void writeContent(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + writeAttributes(out); + writeElements(out); + } + + /** Serializes all fields that are written as XML attributes. */ + protected void writeAttributes(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + out.writeAttribute(PHONE__XML_ATTR, getPhone()); + out.writeAttribute(VOTES__XML_ATTR, Integer.toString(getVotes())); + out.writeAttribute(LAST_UPDATE__XML_ATTR, Long.toString(getLastUpdate())); + out.writeAttribute(DATE_ADDED__XML_ATTR, Long.toString(getDateAdded())); + out.writeAttribute(ARCHIVED__XML_ATTR, Boolean.toString(isArchived())); + out.writeAttribute(WHITE_LISTED__XML_ATTR, Boolean.toString(isWhiteListed())); + } + + /** Serializes all fields that are written as XML elements. */ + protected void writeElements(javax.xml.stream.XMLStreamWriter out) throws javax.xml.stream.XMLStreamException { + // No element fields. + } + + /** Creates a new {@link de.haumacher.phoneblock.db.model.SpamReport} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static SpamReport readSpamReport_XmlContent(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + SpamReport result = new SpamReport(); + result.readContentXml(in); + return result; + } + + /** Reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + protected final void readContentXml(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + for (int n = 0, cnt = in.getAttributeCount(); n < cnt; n++) { + String name = in.getAttributeLocalName(n); + String value = in.getAttributeValue(n); + + readFieldXmlAttribute(name, value); + } + while (true) { + int event = in.nextTag(); + if (event == javax.xml.stream.XMLStreamConstants.END_ELEMENT) { + break; + } + assert event == javax.xml.stream.XMLStreamConstants.START_ELEMENT; + + String localName = in.getLocalName(); + readFieldXmlElement(in, localName); + } + } + + /** Parses the given attribute value and assigns it to the field with the given name. */ + protected void readFieldXmlAttribute(String name, String value) { + switch (name) { + case PHONE__XML_ATTR: { + setPhone(value); + break; + } + case VOTES__XML_ATTR: { + setVotes(Integer.parseInt(value)); + break; + } + case LAST_UPDATE__XML_ATTR: { + setLastUpdate(Long.parseLong(value)); + break; + } + case DATE_ADDED__XML_ATTR: { + setDateAdded(Long.parseLong(value)); + break; + } + case ARCHIVED__XML_ATTR: { + setArchived(Boolean.parseBoolean(value)); + break; + } + case WHITE_LISTED__XML_ATTR: { + setWhiteListed(Boolean.parseBoolean(value)); + break; + } + default: { + // Skip unknown attribute. + } + } + } + + /** Reads the element under the cursor and assigns its contents to the field with the given name. */ + protected void readFieldXmlElement(javax.xml.stream.XMLStreamReader in, String localName) throws javax.xml.stream.XMLStreamException { + switch (localName) { + case PHONE__XML_ATTR: { + setPhone(in.getElementText()); + break; + } + case VOTES__XML_ATTR: { + setVotes(Integer.parseInt(in.getElementText())); + break; + } + case LAST_UPDATE__XML_ATTR: { + setLastUpdate(Long.parseLong(in.getElementText())); + break; + } + case DATE_ADDED__XML_ATTR: { + setDateAdded(Long.parseLong(in.getElementText())); + break; + } + case ARCHIVED__XML_ATTR: { + setArchived(Boolean.parseBoolean(in.getElementText())); + break; + } + case WHITE_LISTED__XML_ATTR: { + setWhiteListed(Boolean.parseBoolean(in.getElementText())); + break; + } + default: { + internalSkipUntilMatchingEndElement(in); + } + } + } + + protected static final void internalSkipUntilMatchingEndElement(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + int level = 0; + while (true) { + switch (in.next()) { + case javax.xml.stream.XMLStreamConstants.START_ELEMENT: level++; break; + case javax.xml.stream.XMLStreamConstants.END_ELEMENT: if (level == 0) { return; } else { level--; break; } + } + } + } + + /** Creates a new {@link SpamReport} and reads properties from the content (attributes and inner tags) of the currently open element in the given {@link javax.xml.stream.XMLStreamReader}. */ + public static SpamReport readSpamReport(javax.xml.stream.XMLStreamReader in) throws javax.xml.stream.XMLStreamException { + in.nextTag(); + return de.haumacher.phoneblock.db.model.SpamReport.readSpamReport_XmlContent(in); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/db.proto b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/db.proto index 5735d650..aa9f60e7 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/db.proto +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/db/model/db.proto @@ -124,3 +124,53 @@ message UserComment { /** Number of "thumbs down" ratings for this comment. */ int down; } + +message SpamReport { + + string phone; + int votes; + long lastUpdate; + long dateAdded; + boolean archived; + boolean whiteListed; +} + +message PhoneNumer { + @Nullable + string shortcut; + + string plus; + string zeroZero; + + string countryCode; + string country; + + @Nullable + string cityCode; + @Nullable + string city; +} + +message SearchResult { + string phoneId; + + PhoneNumer number; + + repeated UserComment comments; + + SpamReport info; + + repeated SearchInfo searches; + + string aiSummary; + + repeated string relatedNumbers; + + string prev; + + string next; + + Rating topRating; + + map ratings; +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/index/google/impl/UpdateMessage_Impl.java b/phoneblock/src/main/java/de/haumacher/phoneblock/index/google/impl/UpdateMessage_Impl.java index 00fa9c8a..1ee83719 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/index/google/impl/UpdateMessage_Impl.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/index/google/impl/UpdateMessage_Impl.java @@ -1,7 +1,7 @@ package de.haumacher.phoneblock.index.google.impl; /** - * Message sent to the Google update service. + * Implementation of {@link de.haumacher.phoneblock.index.google.UpdateMessage}. */ public class UpdateMessage_Impl extends de.haumacher.msgbuf.data.AbstractDataObject implements de.haumacher.phoneblock.index.google.UpdateMessage { diff --git a/phoneblock/src/main/webapp/WEB-INF/web.xml b/phoneblock/src/main/webapp/WEB-INF/web.xml index ce2d2ec8..3a19d85d 100644 --- a/phoneblock/src/main/webapp/WEB-INF/web.xml +++ b/phoneblock/src/main/webapp/WEB-INF/web.xml @@ -40,12 +40,10 @@ CorsFilter org.apache.catalina.filters.CorsFilter - cors.allowed.methods GET,POST,HEAD,OPTIONS,PUT diff --git a/phoneblock/src/main/webapp/phone-info.jsp b/phoneblock/src/main/webapp/phone-info.jsp index 5defae8a..9da06f23 100644 --- a/phoneblock/src/main/webapp/phone-info.jsp +++ b/phoneblock/src/main/webapp/phone-info.jsp @@ -20,7 +20,7 @@ <%@page import="java.text.SimpleDateFormat"%> <%@page import="de.haumacher.phoneblock.db.DB"%> <%@page import="de.haumacher.phoneblock.db.DBService"%> -<%@page import="de.haumacher.phoneblock.db.SpamReport"%> +<%@page import="de.haumacher.phoneblock.db.model.SpamReport"%> <%@page import="de.haumacher.phoneblock.db.Status"%> <%@page import="de.haumacher.phoneblock.db.Statistics"%> <%@page import="de.haumacher.phoneblock.util.JspUtil"%> diff --git a/phoneblock/src/test/java/de/haumacher/phoneblock/analysis/TestNumberAnalyzer.java b/phoneblock/src/test/java/de/haumacher/phoneblock/analysis/TestNumberAnalyzer.java index 26d9bb54..f7e40913 100644 --- a/phoneblock/src/test/java/de/haumacher/phoneblock/analysis/TestNumberAnalyzer.java +++ b/phoneblock/src/test/java/de/haumacher/phoneblock/analysis/TestNumberAnalyzer.java @@ -3,10 +3,12 @@ */ package de.haumacher.phoneblock.analysis; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +import de.haumacher.phoneblock.db.model.PhoneNumer; + /** * Test for {@link NumberAnalyzer}. */ diff --git a/phoneblock/src/test/java/de/haumacher/phoneblock/db/TestDB.java b/phoneblock/src/test/java/de/haumacher/phoneblock/db/TestDB.java index 467b86ab..822d70b8 100644 --- a/phoneblock/src/test/java/de/haumacher/phoneblock/db/TestDB.java +++ b/phoneblock/src/test/java/de/haumacher/phoneblock/db/TestDB.java @@ -132,7 +132,7 @@ public void testSpamReports() throws UnsupportedEncodingException, SQLException assertEquals(1001, _db.getLastSpamReport().longValue()); - List reports = _db.getLatestSpamReports(1001); + List reports = _db.getLatestSpamReports(1001); assertEquals(1, reports.size()); assertEquals("456", reports.get(0).getPhone()); } From a2c5a9553be920a2cd0ec5a44bd60a359ffa9bdf Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 21 Jan 2024 13:27:02 +0100 Subject: [PATCH 084/153] Fixed JSPs after class move. --- phoneblock/src/main/webapp/phone-info.jsp | 2 +- phoneblock/src/main/webapp/sitemap.jsp | 4 ++-- phoneblock/src/main/webapp/status.jsp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/phoneblock/src/main/webapp/phone-info.jsp b/phoneblock/src/main/webapp/phone-info.jsp index 9da06f23..98b3686c 100644 --- a/phoneblock/src/main/webapp/phone-info.jsp +++ b/phoneblock/src/main/webapp/phone-info.jsp @@ -11,7 +11,7 @@ <%@page import="de.haumacher.phoneblock.db.model.RatingInfo"%> <%@page import="de.haumacher.phoneblock.db.Ratings"%> <%@page import="de.haumacher.phoneblock.db.model.Rating"%> -<%@page import="de.haumacher.phoneblock.analysis.PhoneNumer"%> +<%@page import="de.haumacher.phoneblock.db.model.PhoneNumer"%> <%@page import="de.haumacher.phoneblock.analysis.NumberAnalyzer"%> <%@page import="java.util.Date"%> <%@page import="java.text.DateFormat"%> diff --git a/phoneblock/src/main/webapp/sitemap.jsp b/phoneblock/src/main/webapp/sitemap.jsp index a39d1e51..d5f8eb4c 100644 --- a/phoneblock/src/main/webapp/sitemap.jsp +++ b/phoneblock/src/main/webapp/sitemap.jsp @@ -8,7 +8,7 @@ %><%@page import="java.util.List" %><%@page import="de.haumacher.phoneblock.db.DB" %><%@page import="de.haumacher.phoneblock.db.DBService" -%><%@page import="de.haumacher.phoneblock.db.SpamReport" +%><%@page import="de.haumacher.phoneblock.db.model.SpamReport" %> @@ -81,7 +81,7 @@ String count = request.getParameter("count"); int pages = count == null ? 2000 : Integer.parseInt(count); long now = System.currentTimeMillis(); - List reports = db.getAll(pages); + List reports = db.getAll(pages); long oneWeekBefore = now - 1000L*60*60*24*7; for (SpamReport report : reports) { %> diff --git a/phoneblock/src/main/webapp/status.jsp b/phoneblock/src/main/webapp/status.jsp index 1eddf515..8c63ae42 100644 --- a/phoneblock/src/main/webapp/status.jsp +++ b/phoneblock/src/main/webapp/status.jsp @@ -5,7 +5,7 @@ <%@page import="de.haumacher.phoneblock.db.DBService"%> <%@page import="de.haumacher.phoneblock.db.Status"%> <%@page import="de.haumacher.phoneblock.db.Statistics"%> -<%@page import="de.haumacher.phoneblock.db.SpamReport"%> +<%@page import="de.haumacher.phoneblock.db.model.SpamReport"%> <%@page import="de.haumacher.phoneblock.util.JspUtil"%> <%@page import="java.util.Date"%> <%@page import="java.util.List"%> @@ -87,7 +87,7 @@ request.setAttribute("title", "Telefonnummern aktueller Werbeanrufer - PhoneBloc %> <% - List reports = DBService.getInstance().getLatestSpamReports(System.currentTimeMillis() - 60 * 60 * 1000); + List reports = DBService.getInstance().getLatestSpamReports(System.currentTimeMillis() - 60 * 60 * 1000); if (!reports.isEmpty()) { %>

Spam-Reports der letzten Stunde

From 83bb3f9474b04b34df03d0d61b9a28a1adab9b64 Mon Sep 17 00:00:00 2001 From: Bernhard Haumacher Date: Sun, 21 Jan 2024 13:31:05 +0100 Subject: [PATCH 085/153] Remember originally requested location before authentication. --- .../phoneblock/app/BasicLoginFilter.java | 33 ++++++++++++ .../phoneblock/app/FormLoginFilter.java | 54 +++++++++++++++++++ .../haumacher/phoneblock/app/LoginFilter.java | 37 ++++--------- .../phoneblock/app/LoginServlet.java | 27 ++++++++-- .../app/oauth/OAuthLoginServlet.java | 12 ++++- .../app/oauth/PhoneBlockConfigFactory.java | 23 ++++++++ phoneblock/src/main/webapp/login.jsp | 31 +++++++++-- 7 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/app/BasicLoginFilter.java create mode 100644 phoneblock/src/main/java/de/haumacher/phoneblock/app/FormLoginFilter.java diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/BasicLoginFilter.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/BasicLoginFilter.java new file mode 100644 index 00000000..082fe15d --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/BasicLoginFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.app; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import de.haumacher.phoneblock.callreport.CallReportServlet; +import de.haumacher.phoneblock.carddav.CardDavServlet; +import de.haumacher.phoneblock.util.ServletUtil; + +/** + * Filter requesting basic authentication. + * + * @see FormLoginFilter + */ +@WebFilter(urlPatterns = { + CardDavServlet.URL_PATTERN, + CallReportServlet.URL_PATTERN, +}) +public class BasicLoginFilter extends LoginFilter { + + @Override + protected void requestLogin(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException { + ServletUtil.sendAuthenticationRequest(response); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/FormLoginFilter.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/FormLoginFilter.java new file mode 100644 index 00000000..071c1a93 --- /dev/null +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/FormLoginFilter.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. + */ +package de.haumacher.phoneblock.app; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.haumacher.phoneblock.util.ServletUtil; + +/** + * Filter requesting basic authentication. + * + * @see BasicLoginFilter + */ +@WebFilter(urlPatterns = { + "/ab/*", +}) +public class FormLoginFilter extends LoginFilter { + + private static final Logger LOG = LoggerFactory.getLogger(FormLoginFilter.class); + + @Override + protected void requestLogin(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + String originalLocation = originalLocation(request); + LOG.info("Requesting login for resource: " + originalLocation); + request.setAttribute(LoginServlet.LOCATION_ATTRIBUTE, originalLocation); + request.getRequestDispatcher(LoginServlet.PATH).forward(request, response); + } + + private String originalLocation(HttpServletRequest request) { + StringBuilder location = new StringBuilder(); + location.append(request.getServletPath()); + String pathInfo = request.getPathInfo(); + if (pathInfo != null) { + location.append(pathInfo); + } + String query = request.getQueryString(); + if (query != null) { + location.append('?'); + location.append(query); + } + return location.toString(); + } + +} diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java index 3f1c86f3..b6b2b1e5 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginFilter.java @@ -1,43 +1,22 @@ -/* - * Copyright (c) 2022 Bernhard Haumacher et al. All Rights Reserved. - */ package de.haumacher.phoneblock.app; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import de.haumacher.phoneblock.callreport.CallReportServlet; -import de.haumacher.phoneblock.carddav.CardDavServlet; import de.haumacher.phoneblock.db.DBService; -import de.haumacher.phoneblock.util.ServletUtil; -/** - * Filter doing basic authentication. - */ -@WebFilter(urlPatterns = { - CardDavServlet.URL_PATTERN, - CallReportServlet.URL_PATTERN, - "/ab/*", -}) -public class LoginFilter implements Filter { +public abstract class LoginFilter implements Filter { public static final String AUTHENTICATED_USER_ATTR = "authenticated-user"; - @Override - public void init(FilterConfig filterConfig) throws ServletException { - // Ignore. - } - @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; @@ -63,9 +42,15 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha return; } } - - ServletUtil.sendAuthenticationRequest(resp); + + requestLogin(req, resp, chain); } + + /** + * Handles the request, if no authentication was provided. + */ + protected abstract void requestLogin(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException; + /** * The authenticated user of the given session. @@ -85,9 +70,5 @@ public static final void setAuthenticatedUser(HttpServletRequest req, String use req.getSession().setAttribute(AUTHENTICATED_USER_ATTR, userName); } - @Override - public void destroy() { - // Ignore. - } } diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginServlet.java index 99834213..64ba963a 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/LoginServlet.java @@ -19,14 +19,29 @@ /** * Servlet displaying information about a telephone number in the DB. */ -@WebServlet(urlPatterns = "/login") +@WebServlet(urlPatterns = LoginServlet.PATH) public class LoginServlet extends HttpServlet { + + /** + * Request attribute that save the original location that was requested before login. + * + *

+ * The location is a path relative to the context path of the application. + *

+ * + *

+ * The value is transmitted in the login request as additional parameter. + *

+ */ + public static final String LOCATION_ATTRIBUTE = "locationAfterLogin"; + + public static final String PATH = "/login"; private static final Logger LOG = LoggerFactory.getLogger(LoginServlet.class); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.sendRedirect(req.getContextPath() + "/login.jsp"); + req.getRequestDispatcher("/login.jsp").forward(req, resp); } @Override @@ -54,7 +69,13 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } LoginFilter.setAuthenticatedUser(req, authenticatedUser); - resp.sendRedirect(req.getContextPath() + SettingsServlet.PATH); + + String location = req.getParameter(LOCATION_ATTRIBUTE); + if (location == null) { + resp.sendRedirect(req.getContextPath() + SettingsServlet.PATH); + } else { + resp.sendRedirect(req.getContextPath() + location); + } } /** diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/OAuthLoginServlet.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/OAuthLoginServlet.java index d3eef9a1..ce6c29d4 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/OAuthLoginServlet.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/OAuthLoginServlet.java @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import de.haumacher.phoneblock.app.LoginFilter; +import de.haumacher.phoneblock.app.LoginServlet; import de.haumacher.phoneblock.app.RegistrationServlet; import de.haumacher.phoneblock.app.SettingsServlet; import de.haumacher.phoneblock.db.DB; @@ -74,6 +75,8 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se String extId = userProfile.getId(); + Optional location = sessionStore.get(context, LoginServlet.LOCATION_ATTRIBUTE); + DB db = DBService.getInstance(); String login = db.getLogin(clientName, extId); if (login == null) { @@ -93,11 +96,16 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se db.setEmail(login, email); } - RegistrationServlet.startSetup(req, resp, login, passwd); + if (location.isEmpty()) { + RegistrationServlet.startSetup(req, resp, login, passwd); + return; + } } else { LoginFilter.setAuthenticatedUser(req, login); - resp.sendRedirect(req.getContextPath() + SettingsServlet.PATH); } + + String path = location.isEmpty() ? SettingsServlet.PATH : (String) location.get(); + resp.sendRedirect(req.getContextPath() + path); } /** diff --git a/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/PhoneBlockConfigFactory.java b/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/PhoneBlockConfigFactory.java index 540bb13c..b082beb5 100644 --- a/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/PhoneBlockConfigFactory.java +++ b/phoneblock/src/main/java/de/haumacher/phoneblock/app/oauth/PhoneBlockConfigFactory.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; @@ -18,12 +19,18 @@ import org.pac4j.core.config.ConfigFactory; import org.pac4j.core.context.WebContext; import org.pac4j.core.context.WebContextFactory; +import org.pac4j.core.engine.DefaultSecurityLogic; +import org.pac4j.core.engine.savedrequest.DefaultSavedRequestHandler; +import org.pac4j.core.engine.savedrequest.SavedRequestHandler; +import org.pac4j.core.util.Pac4jConstants; import org.pac4j.jee.context.JEEContext; import org.pac4j.oauth.client.FacebookClient; import org.pac4j.oauth.client.Google2Client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.haumacher.phoneblock.app.LoginServlet; + /** * {@link ConfigFactory} for authenticating with external services. */ @@ -58,6 +65,22 @@ public Config build(final Object... parameters) { Config result = new Config(clients); result.setWebContextFactory(new ProxyAwareWebContextFactory()); + + DefaultSecurityLogic securityLogic = new DefaultSecurityLogic(); + SavedRequestHandler savedRequestHandler = new DefaultSavedRequestHandler() { + public void save(WebContext context, org.pac4j.core.context.session.SessionStore sessionStore) { + Optional locationHandle = context.getRequestParameter(LoginServlet.LOCATION_ATTRIBUTE); + if (locationHandle.isPresent()) { + String location = locationHandle.get(); + LOG.info("Saving requested location during OAuth authentication: " + location); + sessionStore.set(context, LoginServlet.LOCATION_ATTRIBUTE, location); + } + + super.save(context, sessionStore); + } + }; + securityLogic.setSavedRequestHandler(savedRequestHandler); + result.setSecurityLogic(securityLogic); return result; } diff --git a/phoneblock/src/main/webapp/login.jsp b/phoneblock/src/main/webapp/login.jsp index 3aea9701..f6435ec0 100644 --- a/phoneblock/src/main/webapp/login.jsp +++ b/phoneblock/src/main/webapp/login.jsp @@ -1,4 +1,8 @@ +<%@page import="java.net.URLEncoder"%> +<%@page import="java.net.URL"%> +<%@page import="de.haumacher.phoneblock.app.LoginServlet"%> +<%@page import="de.haumacher.phoneblock.util.JspUtil"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false"%> <% @@ -11,7 +15,7 @@ <% boolean error = request.getAttribute("error") != null; - String active = error ? "is-active" : ""; + String userActive = error ? "is-active" : ""; %> @@ -28,21 +32,38 @@ und welche Länge Deine Blocklist höchstens haben darf.

+ + <% + String location = (String) request.getAttribute(LoginServlet.LOCATION_ATTRIBUTE); + String locationParam; + if (location != null) { + locationParam = "&" + LoginServlet.LOCATION_ATTRIBUTE + "=" + URLEncoder.encode(location, "UTF-8"); + } else { + locationParam = ""; + } + %>