diff --git a/hp-tipping-point/tpToIdea.py b/hp-tipping-point/tpToIdea.py
new file mode 100644
index 0000000000000000000000000000000000000000..3610e169db4ae5b2f728a496a6453d3740bb076d
--- /dev/null
+++ b/hp-tipping-point/tpToIdea.py
@@ -0,0 +1,522 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2018 Cesnet z.s.p.o
+# Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
+
+import json
+from uuid import uuid4
+import re
+import socket
+import optparse
+import sys
+import os
+import signal
+import resource
+import os.path as pth
+import atexit
+import time
+from datetime import datetime
+import logging
+from collections import OrderedDict
+
+
+class FileWatcher(object):
+
+    def __init__(self, filename, tail=True):
+        self.filename = filename
+        self.open()
+        self.line_buffer = ""
+        if tail and self.f:
+            self.f.seek(0, os.SEEK_END)
+
+    def open(self):
+        try:
+            self.f = open(self.filename, "r")
+            st = os.fstat(self.f.fileno())
+            self.inode, self.size = st.st_ino, st.st_size
+        except IOError:
+            self.f = None
+            self.inode = -1
+            self.size = -1
+
+    def _check_reopen(self):
+        try:
+            st = os.stat(self.filename)
+            cur_inode, cur_size = st.st_ino, st.st_size
+        except OSError as e:
+            cur_inode = -1
+            cur_size = -1
+        if cur_inode != self.inode or cur_size < self.size:
+            self.close()
+            self.open()
+
+    def readline(self):
+        if not self.f:
+            self.open()
+            if not self.f:
+                return self.line_buffer
+        res = self.f.readline()
+        if not res:
+            self._check_reopen()
+            if not self.f:
+                return self.line_buffer
+            res = self.f.readline()
+        if not res.endswith("\n"):
+            self.line_buffer += res
+        else:
+            res = self.line_buffer + res
+            self.line_buffer = ""
+            return res
+
+    def close(self):
+        try:
+            if self.f:
+                self.f.close()
+        except IOError:
+            pass
+        self.inode = -1
+        self.size = -1
+
+    def __repr__(self):
+        return '%s("%s")' % (type(self).__name__, self.filename)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+        return False
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        return self.readline()
+
+
+class Filer(object):
+
+    def __init__(self, directory):
+        self.basedir = self._ensure_path(directory)
+        self.tmp = self._ensure_path(pth.join(self.basedir, "tmp"))
+        self.incoming = self._ensure_path(pth.join(self.basedir, "incoming"))
+        self.hostname = socket.gethostname()
+        self.pid = os.getpid()
+
+    def _ensure_path(self, p):
+        try:
+            os.mkdir(p)
+        except OSError:
+            if not pth.isdir(p):
+                raise
+        return p
+
+    def _get_new_name(self, fd=None):
+        (inode, device) = os.fstat(fd)[1:3] if fd else (0, 0)
+        return "%s.%d.%f.%d.%d" % (
+            self.hostname, self.pid, time.time(), device, inode)
+
+    def create_unique_file(self):
+        # First find and open name unique within tmp
+        tmpname = None
+        while not tmpname:
+            tmpname = self._get_new_name()
+            try:
+                fd = os.open(pth.join(self.tmp, tmpname), os.O_CREAT | os.O_RDWR | os.O_EXCL)
+            except OSError as e:
+                if e.errno != errno.EEXIST:
+                    raise   # other errors than duplicates should get noticed
+                tmpname = None
+        # Now we know the device/inode, rename to make unique within system
+        newname = self._get_new_name(fd)
+        os.rename(pth.join(self.tmp, tmpname), pth.join(self.tmp, newname))
+        nf = os.fdopen(fd, "w")
+        return nf, newname
+
+    def publish_file(self, short_name):
+        os.rename(pth.join(self.tmp, short_name), pth.join(self.incoming, short_name))
+
+
+class IdeaGen(object):
+
+    tp_to_idea = {
+        1: {
+              1: ["Attempt.Exploit"],
+              2: ["Attempt.Exploit"],
+              3: ["Attempt.Exploit"],
+              4: ["Attempt.Exploit"],
+              5: ["Attempt.Exploit"],
+              6: ["Attempt.Exploit"],
+            255: ["Attempt.Exploit"]
+        },
+
+        2: {
+              1: ["Malware.Worm"],
+              2: ["Malware.Virus"],
+              3: ["Malware.Trojan"],
+              4: ["Intrusion.Botnet"],
+              5: ["Fraud.Phishing"],
+            255: ["Malware"]
+        },
+
+        3: {
+              1: ["Availability.DDoS"],
+              2: ["Availability.DDoS"],
+              3: ["Availability.DDoS"],
+            255: ["Availability.DDoS"]
+        },
+
+        4: {
+              1: ["Other"],
+              2: ["Other"],
+              3: ["Other"],
+              4: ["Other"],
+              5: ["Other"],
+              6: ["Attempt.Login"],
+              7: ["Malware.Spyware"],
+            255: ["Other"]
+        },
+
+        5: {
+              1: ["Recon.Scanning"],
+              2: ["Attempt.Exploit"],
+              3: ["Attempt.Exploit"],
+              4: ["Recon.Scanning", "Attempt.Exploit"],
+            255: ["Attempt.Exploit"]
+        },
+
+        6: {
+              1: ["Anomaly.Protocol"],
+              2: ["Anomaly.Traffic"],
+              3: ["Anomaly.Application"],
+            255: ["Anomaly"]
+        },
+
+        7: {
+              1: ["Anomaly.Traffic"],
+              2: ["Anomaly.Application"],
+            255: ["Anomaly.Traffic"]
+        },
+
+        8: {
+              1: ["Other"],
+              2: ["Other"],
+            255: ["Other"]
+        },
+    }
+
+    def __init__(self, name, test=False, other=False):
+        self.name = name
+        self.test = test
+        self.other = other
+
+    def convert_category(self, category, id_taxonomy):
+        '''
+        converts category from record to IDEA category
+        :param category: TippingPoint category description
+        :param id_taxonomy: TippingPoint taxonomy id
+        :return: if category or incident is empty or is not important for saving it return None, otherwise return
+                 converted category
+        '''
+        if not (category and id_taxonomy):
+            return None
+        tp_cat_maj = id_taxonomy >> 24
+        tp_cat_min = id_taxonomy >> 16 & 0b11111111
+        tp_proto = id_taxonomy >> 8 & 0b11111111
+        tp_platf = id_taxonomy & 0b11111111
+        try:
+            category = IdeaGen.tp_to_idea[tp_cat_maj][tp_cat_min]
+        except KeyError:
+            category = ["Other"]
+
+        return category
+
+    def gen_event_idea(self, timestamp, category, id_taxonomy, cve, filter_name, proto, src_ip, src_port,
+                       dest_ip, dest_port, conn_count, url, severity, orig_data):
+        '''
+        put every piece of record together into IDEA message
+        :return: new IDEA message
+        '''
+
+        if (category == ["Other"]) and not self.other:
+            return None
+
+        event = OrderedDict([
+            ("Format","IDEA0"),
+            ("ID", str(uuid4())),
+            ("DetectTime", datetime.fromtimestamp(timestamp / 1000).isoformat() + 'Z'),
+            ("Category", category + (["Test"] if self.test else [])),
+        ])
+        if cve:
+            event['Ref'] = ['urn:cve:'.format(i) for i in cve]
+        if conn_count and int(conn_count):
+            event['ConnCount'] = int(conn_count)
+        source = OrderedDict()
+        target = OrderedDict()
+        if src_ip:
+            # TippingPoint vSMS bugfix: Remove excessive spaces occasionally included inside the IPv6 address
+            src_ip = src_ip.replace(" ", "")
+            af = "IP4" if not ':' in src_ip else "IP6"
+            source[af] = [src_ip]
+        if src_port and int(src_port):
+            source['Port'] = [int(src_port)]
+        if proto:
+            source['Proto'] = [proto]
+        if dest_ip and (dest_ip != "0.0.0.0"):
+            # TippingPoint vSMS bugfix: Remove excessive spaces occasionally included inside the IPv6 address
+            dest_ip = dest_ip.replace(" ", "")
+            af = "IP4" if not ':' in dest_ip else "IP6"
+            target[af] = [dest_ip]
+            if dest_port and int(dest_port):
+                target['Port'] = [int(dest_port)]
+            if proto:
+                target['Proto'] = [proto]
+            if url:
+                target['URL'] = url
+        if source:
+            event['Source'] = [source]
+        if target:
+            event['Target'] = [target]
+        if orig_data:
+            event['Attach'] = [OrderedDict([
+                               ('Type', ["OrigData"]),
+                               ('Content', orig_data.strip())
+            ])]
+        event['Node'] = [OrderedDict([
+                            ('Name', self.name),
+                            ('Type', ["Datagram", "Content", "Protocol", "Signature", "Policy", "Heuristic"]),
+                            ('SW', ["TippingPoint_NX_NGIPS"])
+                        ])]
+        return event
+
+
+def daemonize(
+        work_dir=None, chroot_dir=None,
+        umask=None, uid=None, gid=None,
+        pidfile=None, files_preserve=[], signals={}):
+    # Dirs, limits, users
+    if chroot_dir is not None:
+        os.chdir(chroot_dir)
+        os.chroot(chroot_dir)
+    if umask is not None:
+        os.umask(umask)
+    if work_dir is not None:
+        os.chdir(work_dir)
+    if gid is not None:
+        os.setgid(gid)
+    if uid is not None:
+        os.setuid(uid)
+    # Doublefork, split session
+    if os.fork() > 0:
+        os._exit(0)
+    try:
+        os.setsid()
+    except OSError:
+        pass
+    if os.fork() > 0:
+        os._exit(0)
+    # Setup signal handlers
+    for (signum, handler) in signals.items():
+        signal.signal(signum, handler)
+    # Close descriptors
+    descr_preserve = set(f.fileno() for f in files_preserve)
+    maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
+    if maxfd == resource.RLIM_INFINITY:
+        maxfd = 65535
+    for fd in range(maxfd, 3, -1):  # 3 means omit stdin, stdout, stderr
+        if fd not in descr_preserve:
+            try:
+                os.close(fd)
+            except Exception:
+                pass
+    # Redirect stdin, stdout, stderr to /dev/null
+    devnull = os.open(os.devnull, os.O_RDWR)
+    for fd in range(3):
+        os.dup2(devnull, fd)
+    # PID file
+    if pidfile is not None:
+        pidd = os.open(pidfile, os.O_RDWR | os.O_CREAT | os.O_EXCL | os.O_TRUNC)
+        os.write(pidd, str(os.getpid())+"\n")
+        os.close(pidd)
+        # Define and setup atexit closure
+
+        @atexit.register
+        def unlink_pid():
+            try:
+                os.unlink(pidfile)
+            except Exception:
+                pass
+
+
+def get_args():
+    optp = optparse.OptionParser(
+        usage="usage: %prog [options] logfile ...",
+        description="Watch TippingPoint logfiles and generate Idea events into directory")
+    optp.add_option(
+        "-n", "--name",
+        default=None,
+        dest="name",
+        type="string",
+        action="store",
+        help="Warden client name")
+    optp.add_option(
+        "--test",
+        default=False,
+        dest="test",
+        action="store_true",
+        help="Add \"Test\" category")
+    optp.add_option(
+        "--other",
+        default=False,
+        dest="other",
+        action="store_true",
+        help="Send events having \"Other\" category (usually nonmalicious)")
+    optp.add_option(
+        "-o", "--oneshot",
+        default=False,
+        dest="oneshot",
+        action="store_true",
+        help="process files and quit (do not daemonize)")
+    optp.add_option(
+        "--poll",
+        default=1,
+        dest="poll",
+        type="int",
+        action="store",
+        help="log file polling interval")
+    optp.add_option(
+        "-d", "--dir",
+        default=None,
+        dest="dir",
+        type="string",
+        action="store",
+        help="Target directory (mandatory)")
+    optp.add_option(
+        "-p", "--pid",
+        default=pth.join("/var/run", pth.splitext(pth.basename(sys.argv[0]))[0] + ".pid"),
+        dest="pid",
+        type="string",
+        action="store",
+        help="create PID file with this name (default: %default)")
+    optp.add_option(
+        "-u", "--uid",
+        default=None,
+        dest="uid",
+        type="int",
+        action="store",
+        help="user id to run under")
+    optp.add_option(
+        "-g", "--gid",
+        default=None,
+        dest="gid",
+        type="int",
+        action="store",
+        help="group id to run under")
+    optp.add_option(
+        "--origdata",
+        default=False,
+        dest="origdata",
+        action="store_true",
+        help="Store original report to IDEA message")
+    return optp
+
+
+def not_empty(test_string):
+    # tests if string is not empty
+    return None if test_string.strip() in ["", "null"] else test_string
+
+
+def save_events(event, filer):
+    f, name = filer.create_unique_file()
+    with f:
+        f.write(json.dumps(event, ensure_ascii=True))
+    filer.publish_file(name)
+    
+
+def process_data(line, filer, origdata, idea_gen):
+    '''
+    takes one record, parse it to parameters, give it to Ideagen and writes to file
+    :param line: one record
+    :param idea_file: where output goes
+    :param origdata: if true, write original record to IDEA message
+    '''
+    row = line.split("|")
+    category = idea_gen.convert_category(category=row[1], id_taxonomy=int(row[2]))
+    timestamp = row[0].split(" ")[-1]
+    cve = [i for i in row[3].split(",") if i not in ("null", "")]
+    odata = "|".join([timestamp] + row[1:])
+    if category and not_empty(row[0][-14:-3]):
+        idea_event = idea_gen.gen_event_idea(timestamp=int(timestamp), category=category, id_taxonomy=int(row[2]),
+                                             cve=cve, filter_name=not_empty(row[4]), proto=not_empty(row[5]),
+                                             src_ip=not_empty(row[6]), src_port=not_empty(row[7]), dest_ip=not_empty(row[8]), dest_port=not_empty(row[9]),
+                                             conn_count=not_empty(row[10]), severity=not_empty(row[11]), url=not_empty(row[12]),
+                                             orig_data=odata if origdata else False)
+        if idea_event:
+            save_events(idea_event, filer)
+
+
+running_flag = True
+reload_flag = False
+
+
+def terminate_me(signum, frame):
+    global running_flag
+    running_flag = False
+
+
+def reload_me(signum, frame):
+    global reload_flag
+    reload_flag = True
+
+
+def main():
+    global running_flag
+    global reload_flag
+    logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG, filename='tipping_point_log.log', filemode='w')
+    optp = get_args()
+    opts, args = optp.parse_args()
+    if not args or opts.name is None or opts.dir is None:
+        optp.print_help()
+        sys.exit()
+    if opts.oneshot:
+        signal.signal(signal.SIGINT, terminate_me)
+        signal.signal(signal.SIGTERM, terminate_me)
+        files = [open(arg) for arg in args]
+    else:
+        daemonize(
+            pidfile=opts.pid,
+            uid=opts.uid,
+            gid=opts.gid,
+            signals={
+                signal.SIGINT: terminate_me,
+                signal.SIGTERM: terminate_me,
+                signal.SIGHUP: reload_me
+            })
+        files = [FileWatcher(arg) for arg in args]
+    filer = Filer(opts.dir)
+    idea_gen = IdeaGen(opts.name, opts.test, opts.other)
+    while running_flag:
+        for log_file in files:
+            while True:
+                line = log_file.readline()
+                if line is None or not line.strip():
+                    logging.info("no line")
+                    break
+                logging.info("readline")
+                process_data(line, filer, opts.origdata, idea_gen)
+            if not running_flag:
+                break
+            if reload_flag:
+                for f in files:
+                    f.close()
+                    f.open()
+                reload_flag = False
+        if opts.oneshot:
+            break
+        else:
+            time.sleep(opts.poll)
+            
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file