Skip to content

Commit

Permalink
Fixes #11 and use semantic versioning
Browse files Browse the repository at this point in the history
- when there is monitoring, but no capture, indicate so in `fritzcap.py` main script
- new threading base class `ExceptionLoggingThread` that logs exceptions during `run`: all thread classes now descend from `ExceptionLoggingThread`
- in `call_monitor`, when there is no `capture_monitor`: prevent exceptions and ensure the intended logic still works
- semantic versioning http://semver.org/

```
2017-07-09 10:33:21,889 - FritzCap version 2.3.1 started.
2017-07-09 10:33:21,889 - Note: -m or --monitor_calls without -c or --capture_files does monitoring without capture.
2017-07-09 10:33:21,890 - Connect    to the call monitor service on 192.168.124.23:1012.
2017-07-09 10:33:21,897 - Connected  to the call monitor service on 192.168.124.23:1012.
2017-07-09 10:33:37,816 - Ring       (ID:0, ActiveCalls.:1, Caller:xxxxxxxxxx, DialedNumber:yyyyyyyyyy, LinePort:SIP0)
2017-07-09 10:33:43,075 - Disconnect (ID:0, ActiveCalls.:0, Caller:xxxxxxxxxx, DialedNumber:yyyyyyyyyy, LinePort:SIP0)
2017-07-09 10:33:58,084 - Connection to the call monitor service on 192.168.124.23:1012 stopped.
2017-07-09 10:33:58,084 - FritzCap version 2.3.1 finished.
```
  • Loading branch information
jpluimers committed Jul 9, 2017
1 parent 35039f1 commit 9404308
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 59 deletions.
87 changes: 51 additions & 36 deletions core/call_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@

from log import Log
from capture_monitor import CaptureMonitor
from exception_logging_thread import ExceptionLoggingThread

class CallMonitor(threading.Thread):
class CallMonitor(ExceptionLoggingThread):

def __init__(self, capture_monitor, box_name, call_service_port):
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)
self._stop = threading.Event()

self.capture_monitor = capture_monitor
Expand All @@ -70,7 +71,7 @@ def init_connection(self):

self.logger.info("Connected to the call monitor service on %s:%s." % (self.box_name,self.call_service_port))

def run(self):
def run_logic(self):
self.logger.debug("Thread started.")
callers_count = 0 # Set the count of currently led calls to 0.
call_id_map = {}
Expand Down Expand Up @@ -103,8 +104,8 @@ def run(self):
sline = line.split(";")

event_time = datetime.datetime.now()
# parse the oryginal telnet time. Example: 13.01.11 21:48:31
oryginal_event_time = time.strptime(sline[0], "%d.%m.%y %H:%M:%S") # 13.01.11 21:48:31
# parse the original telnet time. Example: 13.01.11 21:48:31
original_event_time = time.strptime(sline[0], "%d.%m.%y %H:%M:%S") # 13.01.11 21:48:31

self.logger.debug("Telnet:'"+line+"'")

Expand All @@ -120,19 +121,25 @@ def run(self):
if not me:
me = "Unknown"

self.capture_monitor.set_data("callevent.name", command)
self.capture_monitor.set_data("acalls.number", callers_count)
self.capture_monitor.set_data("lineport.name", sline[5])
if (self.capture_monitor is None):
me_numbername = me
callpartner_numbername = call_partner

self.capture_monitor.set_data("tocall", oryginal_event_time)
self.capture_monitor.set_data("tcall", event_time)
self.capture_monitor.set_callnumber("caller", call_partner)
self.capture_monitor.set_callnumber("dialed", me)
self.capture_monitor.set_callnumber("me", me)
self.capture_monitor.set_callnumber("callpartner", call_partner)
else:
self.capture_monitor.set_data("callevent.name", command)
self.capture_monitor.set_data("acalls.number", callers_count)
self.capture_monitor.set_data("lineport.name", sline[5])

self.capture_monitor.set_data("tocall", original_event_time)
self.capture_monitor.set_data("tcall", event_time)
self.capture_monitor.set_callnumber("caller", call_partner)
self.capture_monitor.set_callnumber("dialed", me)
self.capture_monitor.set_callnumber("me", me)
self.capture_monitor.set_callnumber("callpartner", call_partner)

me_numbername = self.capture_monitor.get_call_numbername(me)
callpartner_numbername = self.capture_monitor.get_call_numbername(call_partner)

me_numbername = self.capture_monitor.get_call_numbername(me)
callpartner_numbername = self.capture_monitor.get_call_numbername(call_partner)
self.logger.info("Ring (ID:%s, ActiveCalls.:%s, Caller:%s, DialedNumber:%s, LinePort:%s)" % (sline[2],callers_count,callpartner_numbername,me_numbername,sline[5]))
call_id_map[sline[2]] = [callpartner_numbername,me_numbername,sline[5]]

Expand All @@ -147,37 +154,45 @@ def run(self):
if not call_partner:
call_partner = "Unknown"

self.capture_monitor.set_data("callevent.name", command)
self.capture_monitor.set_data("acalls.number", callers_count)
self.capture_monitor.set_data("lineport.name", sline[6])
self.capture_monitor.set_data("tocall", oryginal_event_time)
self.capture_monitor.set_data("tcall", event_time)
self.capture_monitor.set_callnumber("caller", me)
self.capture_monitor.set_callnumber("dialed", call_partner)
self.capture_monitor.set_callnumber("me", me)
self.capture_monitor.set_callnumber("callpartner", call_partner)

me_numbername = self.capture_monitor.get_call_numbername(me)
callpartner_numbername = self.capture_monitor.get_call_numbername(call_partner)
if (self.capture_monitor is None):
me_numbername = self.capture_monitor.get_call_numbername(me)
callpartner_numbername = self.capture_monitor.get_call_numbername(call_partner)
else:
self.capture_monitor.set_data("callevent.name", command)
self.capture_monitor.set_data("acalls.number", callers_count)
self.capture_monitor.set_data("lineport.name", sline[6])
self.capture_monitor.set_data("tocall", original_event_time)
self.capture_monitor.set_data("tcall", event_time)
self.capture_monitor.set_callnumber("caller", me)
self.capture_monitor.set_callnumber("dialed", call_partner)
self.capture_monitor.set_callnumber("me", me)
self.capture_monitor.set_callnumber("callpartner", call_partner)

me_numbername = self.capture_monitor.get_call_numbername(me)
callpartner_numbername = self.capture_monitor.get_call_numbername(call_partner)

self.logger.info("Call (ID:%s, ActiveCalls.:%s, Caller:%s, DialedNumber:%s, LinePort:%s)" % (sline[2],callers_count,me_numbername,callpartner_numbername,sline[6]))
call_id_map[sline[2]] = [me_numbername,callpartner_numbername,sline[6]]
elif command == "CONNECT": # Conversation started.
self.capture_monitor.set_data("toconn", oryginal_event_time)
self.capture_monitor.set_data("tconn", event_time)
if (self.capture_monitor is not None):
self.capture_monitor.set_data("toconn", original_event_time)
self.capture_monitor.set_data("tconn", event_time)
self.logger.info("Connect (ID:%s, ActiveCalls.:%s, Caller:%s, DialedNumber:%s, LinePort:%s)" % (sline[2],callers_count,call_id_map[sline[2]][0],call_id_map[sline[2]][1],call_id_map[sline[2]][2]))
continue # Don't increase currently led calls, because already done with CALL/RING.
elif command == "DISCONNECT": # Call was ended.
callers_count -= 1 # Decrease the count of currently led calls.

self.capture_monitor.set_data("todisc", oryginal_event_time)
self.capture_monitor.set_data("tdisc", event_time)
self.capture_monitor.set_data("acalls.number", callers_count)
if (self.capture_monitor is not None):
self.capture_monitor.set_data("todisc", original_event_time)
self.capture_monitor.set_data("tdisc", event_time)
self.capture_monitor.set_data("acalls.number", callers_count)

self.logger.info("Disconnect (ID:%s, ActiveCalls.:%s, Caller:%s, DialedNumber:%s, LinePort:%s)" % (sline[2],callers_count,call_id_map[sline[2]][0],call_id_map[sline[2]][1],call_id_map[sline[2]][2]))
if (callers_count < 0):
self.logger.warning("There is more stopped calls than started. Data corrupt or program started while calling. Normalize ActiveCalls to '0'")
callers_count = 0;
self.capture_monitor.set_data("acalls.number", callers_count)
if (self.capture_monitor is not None):
self.capture_monitor.set_data("acalls.number", callers_count)
else:
continue

Expand All @@ -190,10 +205,10 @@ def run(self):
self.capture_monitor.stop_capture();

self._stop.set()
self.capture_monitor.stop()
if (self.capture_monitor is not None):
self.capture_monitor.stop()
self.logger.debug("Thread stopped.")


def stop (self):
self.logger.debug("Received signal to stop the thread.")
self._stop.set()
Expand Down
7 changes: 4 additions & 3 deletions core/capfile_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@
from log import Log
from pcap_parse import PcapParser
from g711_decoder import G711Decoder
from exception_logging_thread import ExceptionLoggingThread

class CapfileWorker(threading.Thread):
class CapfileWorker(ExceptionLoggingThread):

def __init__(self, worker_id, decode_work_queue):
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)
self.decode_work_queue = decode_work_queue
self.worker_id = worker_id
self._stop = threading.Event()

self.logger = Log().getLogger()
self.logger.debug("CapfileWorker(worker_id:%s, decode_work_queue:%s)." % (worker_id, decode_work_queue))

def run(self):
def run_logic(self):
self.logger.debug("Thread started.")
while not self._stop.isSet():
try:
Expand Down
7 changes: 4 additions & 3 deletions core/capture_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@
from log import Log
from string_helper import StringHelper
from tracer import Tracer
from exception_logging_thread import ExceptionLoggingThread

class CaptureMonitor(threading.Thread):
class CaptureMonitor(ExceptionLoggingThread):

state_started = False
next_stop_time = 0
next_start_time = 0
cap_file_path = ""
def __init__(self, decode_work_queue, data_map, box_name, username, password, protocol, cap_folder, cap_file, cap_interface, login_required, default_login, sid_challenge, sid_login, start_str, stop_str, after_capture_time):
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)
self._stop = threading.Event()

self.decode_work_queue = decode_work_queue
Expand Down Expand Up @@ -82,7 +83,7 @@ def __init__(self, decode_work_queue, data_map, box_name, username, password, pr
self.logger = Log().getLogger()
self.logger.debug("CaptureMonitor(decode_work_queue:'%s', data_map:'%s', box_name:'%s', username:'%s', password:'%s', protocol:'%s', cap_folder:'%s', cap_file:'%s', cap_interface:'%s', login_required:'%s', default_login:'%s', sid_challenge:'%s', sid_login:'%s', start_str:'%s', stop_str:'%s', after_capture_time:'%s')" % (decode_work_queue,data_map,box_name,username,password,protocol,cap_folder,cap_file,cap_interface,login_required,default_login,sid_challenge,sid_login,start_str,stop_str,after_capture_time))

def run(self):
def run_logic(self):
self.logger.debug("Thread started.")

if self.login_required:
Expand Down
51 changes: 51 additions & 0 deletions core/exception_logging_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
#################################################################################
# Simple thread that logs exceptions in the run for issue resolving purposes.
##################################################################################
# Copyright (c) 2017, Jeroen Wiert Pluimers (https://wiert.me)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of the <organization> 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 <COPYRIGHT HOLDER> 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.
##################################################################################

import threading

from log import Log

class ExceptionLoggingThread(threading.Thread):

def __init__(self):
threading.Thread.__init__(self)
self.logger = Log().getLogger()
self.logger.debug("ExceptionLoggingThread().")

def run_logic(self):
self.logger.debug("Thread started.")

def run(self):
try:
self.run_logic()
except:
self.logger.exception('Exception in `run`')
raise
7 changes: 4 additions & 3 deletions core/interfaces_dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@

from HTMLParser import HTMLParser
from htmlentitydefs import name2codepoint
from exception_logging_thread import ExceptionLoggingThread

## https://docs.python.org/2/library/htmlparser.html
class CaptureLuaHtmlParser(HTMLParser):
Expand Down Expand Up @@ -120,10 +121,10 @@ def handle_decl(self, data):
pass


class InterfacesDumper(threading.Thread):
class InterfacesDumper(ExceptionLoggingThread):

def __init__(self, box_name, username, password, protocol, login_required, default_login, sid_challenge, sid_login):
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)
self._stop = threading.Event()

self.box_name = box_name
Expand All @@ -139,7 +140,7 @@ def __init__(self, box_name, username, password, protocol, login_required, defau
self.logger = Log().getLogger()
self.logger.debug("InterfacesDumper(box_name:'%s', username:'%s', password:'%s', protocol:'%s', login_required:'%s', default_login:'%s', sid_challenge:'%s', sid_login:'%s')" % (box_name,username,password,protocol,login_required,default_login,sid_challenge,sid_login))

def run(self):
def run_logic(self):
self.logger.debug("Thread started.")

if self.login_required:
Expand Down
2 changes: 1 addition & 1 deletion core/string_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class StringHelper():

def __init__(self):
self.logger = Log().getLogger()
self.logger.debug("SytemInputFileReader(decode_work_queue:'%s')" % (decode_work_queue))
self.logger.debug("StringHelper()")

def parse_string(data_str, data_map):
datetime_parse_str = ""
Expand Down
7 changes: 4 additions & 3 deletions core/sysinput_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,19 @@

import logging
from log import Log
from exception_logging_thread import ExceptionLoggingThread

class SytemInputFileReader(threading.Thread):
class SytemInputFileReader(ExceptionLoggingThread):

def __init__(self, decode_work_queue):
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)
self._stop = threading.Event()
self.decode_work_queue = decode_work_queue

self.logger = Log().getLogger()
self.logger.debug("SytemInputFileReader(decode_work_queue:'%s')" % (decode_work_queue))

def run(self):
def run_logic(self):
self.logger.debug("Thread started.")
for line in sys.stdin:
line = line.strip()
Expand Down
10 changes: 5 additions & 5 deletions core/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
##################################################################################

import threading, urllib, ctypes, platform
import urllib, ctypes, platform

from log import Log
from exception_logging_thread import ExceptionLoggingThread

THREAD_SET_INFORMATION = 0x20
THREAD_PRIORITY_ABOVE_NORMAL = 1


''' Tracer runs in a separate thread'''
class Tracer(threading.Thread):
class Tracer(ExceptionLoggingThread):
def __init__(self, url, filename):
self.url = url
self.i = 0
self.filename = filename
threading.Thread.__init__(self)
ExceptionLoggingThread.__init__(self)

self.logger = Log().getLogger()

Expand All @@ -57,7 +57,7 @@ def monitor(self, n1, n2, n3):
self.i += 1
self.i %= 4

def run(self):
def run_logic(self):
if platform.system() == "Windows":
w32 = ctypes.windll.kernel32
tid = w32.GetCurrentThreadId()
Expand Down
11 changes: 7 additions & 4 deletions fritzcap.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def signal_handler(signum, stack):
main_args = parser.add_argument_group('main arguments')
ext_args = parser.add_argument_group('extended defaults arguments')

fritzcap_version = '2.3'
fritzcap_version = '2.3.1'
parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + fritzcap_version)

main_args.add_argument('-c', '--capture_files', default=None, action='store_true', help='capture file/s. If the monitor option is not set, only one file will be captured')
Expand Down Expand Up @@ -179,10 +179,10 @@ def signal_handler(signum, stack):
if (args.__contains__(key)):
value = args.__getattribute__(key)

if value is None and config.has_option("settings", key) and config.get("settings", key):
if (value is None) and config.has_option("settings", key) and config.get("settings", key):
value = config.get("settings", key)

if value is None and default_value:
if (value is None) and default_value:
value = default_value

args.__setattr__(key,value)
Expand Down Expand Up @@ -213,7 +213,7 @@ def signal_handler(signum, stack):


# take the password data from the command line
if (args.capture_files or args.show_interfaces) and args.password is None and login_required:
if (args.capture_files or args.show_interfaces) and (args.password is None) and login_required:
platform_system = platform.system()
if (platform_system == "Windows"):
signal.signal(signal.SIGINT, signal_handler)
Expand Down Expand Up @@ -275,6 +275,9 @@ def signal_handler(signum, stack):
######################################
if (args.monitor_calls):
nothing_to_do = False
if (capture_monitor is None):
logger.info("Note: -m or --monitor_calls without -c or --capture_files does monitoring without capture.")

call_monitor = CallMonitor(capture_monitor, args.box_name, args.call_service_port)
all_threads.insert(0, call_monitor)
elif (args.capture_files):
Expand Down
2 changes: 1 addition & 1 deletion repair_cap_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def check_data(data, filepos, last_time_sec):

printed_line2 = "counter:%-10s, byte_counter:0x%-6X = %-6s, stime:%s, ts_usec:%-7s, incl_len:0x%-6X = %-6s, orig_len:0x%-6X = %-6s, data:" % (counter,nextpos,nextpos,cap_time2, ts_usec2, incl_len2, incl_len2, orig_len2, orig_len2)

print "The Oryginal is OK, but the next of oryginal seems not to be correct. Go Back..."
print "The original is OK, but the next of original seems not to be correct. Go Back..."
print "\t\t"+printed_line
print "\t\t"+printed_line2
print ""
Expand Down

0 comments on commit 9404308

Please sign in to comment.