134 lines
5.1 KiB
Diff
134 lines
5.1 KiB
Diff
|
From b0669e837daefeaff482a04d6dc15df1c6ebc0f0 Mon Sep 17 00:00:00 2001
|
||
|
From: Robert Scheck <robert-scheck@users.noreply.github.com>
|
||
|
Date: Sat, 24 Apr 2021 09:22:26 +0200
|
||
|
Subject: [PATCH] Convert FRITZ!Box XML phone book into Baresip contacts
|
||
|
(#1382)
|
||
|
|
||
|
---
|
||
|
.github/workflows/tools.yml | 14 ++++++
|
||
|
tools/fritzbox2baresip | 94 +++++++++++++++++++++++++++++++++++++
|
||
|
2 files changed, 108 insertions(+)
|
||
|
create mode 100644 .github/workflows/tools.yml
|
||
|
create mode 100755 tools/fritzbox2baresip
|
||
|
|
||
|
diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml
|
||
|
new file mode 100644
|
||
|
index 000000000..6cc62d606
|
||
|
--- /dev/null
|
||
|
+++ b/.github/workflows/tools.yml
|
||
|
@@ -0,0 +1,14 @@
|
||
|
+name: Tools
|
||
|
+
|
||
|
+on: [push, pull_request]
|
||
|
+
|
||
|
+jobs:
|
||
|
+ build:
|
||
|
+ runs-on: ubuntu-latest
|
||
|
+
|
||
|
+ steps:
|
||
|
+ - uses: actions/checkout@v2
|
||
|
+ - name: pylint
|
||
|
+ uses: cclauss/GitHub-Action-for-pylint@0.7.0
|
||
|
+ with:
|
||
|
+ args: "pylint tools/fritzbox2baresip"
|
||
|
diff --git a/tools/fritzbox2baresip b/tools/fritzbox2baresip
|
||
|
new file mode 100755
|
||
|
index 000000000..cba91630f
|
||
|
--- /dev/null
|
||
|
+++ b/tools/fritzbox2baresip
|
||
|
@@ -0,0 +1,94 @@
|
||
|
+#!/usr/bin/python3
|
||
|
+#
|
||
|
+# Copyright (c) 2021, Robert Scheck <robert@fedoraproject.org>
|
||
|
+#
|
||
|
+# All rights reserved.
|
||
|
+#
|
||
|
+# Redistribution and use in source and binary forms, with or without
|
||
|
+# modification, are permitted provided that the following conditions
|
||
|
+# are met:
|
||
|
+#
|
||
|
+# 1. Redistributions of source code must retain the above copyright
|
||
|
+# notice, this list of conditions and the following disclaimer.
|
||
|
+# 2. Redistributions in binary form must reproduce the above copyright
|
||
|
+# notice, this list of conditions and the following disclaimer in the
|
||
|
+# documentation and/or other materials provided with the distribution.
|
||
|
+# 3. Neither the name of the copyright holder nor the names of its
|
||
|
+# contributors may be used to endorse or promote products derived
|
||
|
+# from this software without specific prior written permission.
|
||
|
+#
|
||
|
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
+#
|
||
|
+
|
||
|
+"""Convert FRITZ!Box XML phone book into Baresip contacts"""
|
||
|
+
|
||
|
+import sys
|
||
|
+import xml.etree.ElementTree
|
||
|
+
|
||
|
+def fail(msg):
|
||
|
+ """Print failure message to STDERR and end with non-zero exit code"""
|
||
|
+ print(msg, file=sys.stderr)
|
||
|
+ sys.exit(1)
|
||
|
+
|
||
|
+def usage():
|
||
|
+ """Handle mandatory and optional command line arguments"""
|
||
|
+ if len(sys.argv) not in range(2, 5):
|
||
|
+ fail(f"Usage: {sys.argv[0]} <FRITZ!Box XML> [<Baresip contacts>] "
|
||
|
+ "[<FRITZ!Box IP>]")
|
||
|
+
|
||
|
+ return sys.argv[1], \
|
||
|
+ sys.argv[2] if len(sys.argv) >= 3 else None, \
|
||
|
+ f"@{sys.argv[3]}" if len(sys.argv) == 4 else "@fritz.box"
|
||
|
+
|
||
|
+def convert(entries, src, host="@fritz.box"):
|
||
|
+ """Convert FRITZ!Box XML phone book into Baresip contacts"""
|
||
|
+ try:
|
||
|
+ types = {'home': '\N{house building}',
|
||
|
+ 'work': '\N{briefcase}',
|
||
|
+ 'mobile': '\N{mobile phone}'}
|
||
|
+ tree = xml.etree.ElementTree.parse(src)
|
||
|
+ for contact in tree.iter('contact'):
|
||
|
+ realname = contact.findtext("./person/realName")
|
||
|
+ for ntype in [*types]:
|
||
|
+ number = contact.findtext("./telephony/number"
|
||
|
+ f"[@type='{ntype}']")
|
||
|
+ if number is not None:
|
||
|
+ entries.append(f"{types[ntype]} {realname} <sip:{number}"
|
||
|
+ f"{'' if '@' in number else host}>")
|
||
|
+ except FileNotFoundError:
|
||
|
+ fail(f"Error: File '{src}' does not exist or can not be read!")
|
||
|
+ except xml.etree.ElementTree.ParseError:
|
||
|
+ fail(f"Error: File '{src}' is no FRITZ!Box XML phone book or damaged!")
|
||
|
+
|
||
|
+def write(entries, src=None, dst=None):
|
||
|
+ """Write Baresip contacts to file or STDOUT"""
|
||
|
+ try:
|
||
|
+ dst = None if dst == '-' else dst
|
||
|
+ sys.stdout.close = lambda: None
|
||
|
+ with (open(dst, 'w') if dst else sys.stdout) as contacts:
|
||
|
+ contacts.write("# SIP contacts\n")
|
||
|
+ contacts.write("# Source: "
|
||
|
+ f"{'(unknown)' if src is None else src }\n")
|
||
|
+ contacts.write('\n'.join(entries) + '\n')
|
||
|
+ except PermissionError:
|
||
|
+ fail(f"Error: File '{dst}' can not be created or written to!")
|
||
|
+
|
||
|
+def main():
|
||
|
+ """Handle command line arguments, convert phone book and write result"""
|
||
|
+ entries = []
|
||
|
+ src, dst, host = usage()
|
||
|
+ convert(entries, src, host)
|
||
|
+ write(entries, src, dst)
|
||
|
+
|
||
|
+if __name__ == "__main__":
|
||
|
+ main()
|