<?php
/*
 Von mifritscher geschrieben. Steht unter der AGPL.
 
 Changelog:
 * r5:
   * traceroute etwas bremsen
   * Laufzeit angeben
 * r4:
   * Weitere Methoden fuers auslesen von Hostnames
 * r3:
   * svg schreiben
   * Hostname auslesen
   * Danach clustern
 * r2:
   * 3x versuchen dot auszufuehren (produziert manchmal 0 byte Dateien)
   * Graphlabel hinzugefuegt
   * .dot Datei in /var/www/html anlegen
 * r1:
   * Erste Versionierung.
*/

interface IPHelper {
    public function getLocalhost();
    public function getNumericDefault();
    public function getDummyAddress($index);
    public function isDummyAddress($address);
    public function getParameter();
    
    public function isFFFAddress($address);
    public function isTransferAddress($address);

    public function sanitizeIP($address);
}

class IPv4Helper implements IPHelper {
    public function getLocalhost() {
        return "127.0.0.1";
    }
    
    public function getNumericDefault() {
        return "0.0.0.0";
    }

    public function getDummyAddress($index) {
        return "0.0." . intval(($index % 65536) / 256) . '.' . ($index % 256);
    }

    public function isDummyAddress($address) {
        return (strpos($address,"0.0.") === 0);
    }

    public function getParameter() {
        return "-4";
    }

    public function isFFFAddress($address) {
        return strpos($address, '10.50') === 0 || strpos($address, '10.83') === 0;
    }

    public function isTransferAddress($address) {
        return strpos($address,".252.") > 0;
    }

    public function sanitizeIP($address) {
        return $address;
    }
}

class IPv6Helper implements IPHelper {
    public function getLocalhost() {
        return "::1";
    }

    public function getNumericDefault() {
        return "::";
    }

    public function getDummyAddress($index) {
        return "0000::".intval($index);
    }

    public function isDummyAddress($address) {
        return (strpos($address,"0000:") === 0);    
    }

    public function getParameter() {
        return "-6";
    }

    public function isFFFAddress($address) {
        return strpos($address, 'fd43:5602:29bd:') === 0;
    }

    public function isTransferAddress($address) {
        return strpos($address, "fd43:5602:29bd:ffff:") === 0;    
    }

    public function sanitizeIP($address) {
        return '[' . $address . ']';
    }
}

class Subnet {
    private $network;
    private $subnet;
    private $ipHelper;
    
    function __construct($def, $ipHelper) {
        $temp=explode('/', $def);
        $this->network=filter_var($temp[0], FILTER_VALIDATE_IP);
        $this->subnet=intval($temp[1]);
        $this->ipHelper=$ipHelper;
    }
    
    function getNetwork() {
        return $this->network;
    }

    function getSubnet() {
        return $this->subnet;
    }
}

class Node {
    private $ip;
    private $ipHelper;
    private $inRoutingTable;
    private $uplinks = array();
    private $downlinks = array();
    private $pingable = false;
    private $traceroutable = false;
    private $httpable = false;
    private $hostname = "";
    private $hostnameMethod = "";
    private $hostnameProtocol = "http";
    private $l3fffRouter = false;
    private $used = 0;
    
    function __construct($ip, $ipHelper, $inRoutingTable = false) {
        $this->ip=filter_var($ip, FILTER_VALIDATE_IP);
        $this->ipHelper=$ipHelper;
        $this->inRoutingTable = $inRoutingTable;
    }
    
    function getIP() {
        return $this->ip;
    }

    function getSanitizedIP() {
        if (strpos($this->ip,":") !== false) {
            return '[' . $this->ip . ']';
        }
        return $this->ip;
    }
    
    function setpingable() {
        $this->pingable=true;
    }

    function getpingable() {
        return $this->pingable;
    }
    
    function setTraceroutable() {
        $this->traceroutable=true;
    }
    
    function setHttpable() {
        $this->httpable=true;
    }
    
    function setHostname($hostname) {
        $this->hostname=trim(str_replace('"', '\"', substr(explode('\n',$hostname)[0], 0, 50)));
    }
    
    function setHostnameMethod($method) {
        $this->hostnameMethod=$method;
    }

    function setHostnameProtocol($protocol) {
        $this->hostnameProtocol=$protocol;
    }

    function setL3fffRouter() {
        $this->l3fffRouter=true;
    }

    function getHostname() {
        return $this->hostname;
    }
    
    function getHostnameMethod() {
        return $this->hostnameMethod;
    }

    function getHostnameProtocol() {
        return $this->hostnameProtocol;
    }

    function addUplink($node) {
        echo "Zu {$this->ip} wird der Uplink {$node->getIP()} hinzugefuegt\n";
        $this->uplinks[$node->getIP()] = $node;
    }

    function addDownlink($node) {
        echo "Zu {$this->ip} wird der Downlink {$node->getIP()} hinzugefuegt\n";
        $this->downlinks[$node->getIP()] = $node;
    }
    
    function incrUsed() {
       $this->used++;
    }
    
    function getused() {
        return $this->used;
    }
    
    function natSort() {
        uksort($this->uplinks, "strnatcmp");
        uksort($this->downlinks, "strnatcmp");
    }

    function getPrettyString() {
        $string = "";
        $string .= "==============================\n";
        $string .= "| Node: $this->ip\n|\n";
        $string .= "| Used: $this->used\n";
        $string .= "| Name: $this->hostname (Method: $this->hostnameMethod)\n";
        if (!$this->pingable) {
            $string .= "| is NOT pingable!\n";
        }
        if (!$this->traceroutable) {
            $string .= "| is NOT traceroutable!\n";
        }
        if (!$this->httpable) {
            $string .= "| is NOT httpable!\n";
        }
        if (!$this->inRoutingTable) {
            $string .= "| is NOT inRoutingTable!\n";
        }
        if (!empty($this->uplinks)) {
            $string .= "------------------------------\n";
            $string .= "| Uplinks (should be only one)\n";
            $i = 1;
            foreach ($this->uplinks as $uplink) {
                $string .= "| ";
                $string .= $i;
                $string .= ": ";
                $string .= $uplink->getIP();
                $string .= "\n";
                $i++;
            }
        }
        if (!empty($this->downlinks)) {
            $string .= "------------------------------\n";
            $string .= "| Downlinks\n";
            $i = 1;
            foreach ($this->downlinks as $downlink) {
                $string .= "| ";
                $string .= $i;
                $string .= ": ";
                $string .= $downlink->getIP();
                $string .= "\n";
                $i++;
            }
        }
        $string .= "==============================\n\n";

        return $string;   
    }
    
    function getDotNode() {
        if (!$this->filterForDot()) {
            return "";
        }
        $fillcolor="white";
        $string = "\"".$this->ip . "\" [label=\"{$this->ip}";
        if ($this->used > 0 && !$this->ipHelper->isDummyAddress($this->ip)) {
            $string .= " {$this->used}";
        }
/*
        if ($this->hostname) {
            $string .= " " . $this->hostname; // . ' ' . $this->hostnameMethod;
        }
*/
        $string .= "\"";
        if (!$this->ipHelper->isDummyAddress($this->ip)) {
            $sanitizedIP=$this->getSanitizedIP();
            $string .= " URL=\"{$this->hostnameProtocol}://$sanitizedIP\"";
        }
        if ($this->ip === $this->ipHelper->getNumericDefault()) {
            $string .= " color=green";        
        } else if ($this->ipHelper->isDummyAddress($this->ip)) {
            $string .= " color=gray";        
        } else if (!$this->pingable || !$this->traceroutable) {
            $string .= " color=red";
        } else if ($this->httpable) {
            if ($this->hostname) {
                $string .= " color=blue";
                if ($this->l3fffRouter) {
                    $fillcolor='lightblue';
                } else {
                    $fillcolor='lightgray';
                }
            } else {
                $string .= " color=cyan";
            }
        } else {
            $string .= " color=black";
        }
        if (!$this->inRoutingTable) {
            $string .= " style=dotted";
        }
        $string .= " style=filled fillcolor=$fillcolor";
        $string .= " shape=box";
        $string .= "]\n";
        
        return $string;
    }
    
    function getDotEdges() {
        $string="";
        foreach ($this->downlinks as $downlink) {
            if ($downlink->filterForDot() && !($this->ip==$this->ipHelper->getLocalhost() && $downlink->getIP()==$this->ipHelper->getLocalhost())) {
                $string.="\"" .$this->ip . "\" -> \"" . $downlink->getIP() . "\"";
                $string.=" [color=\"#".substr(md5($string),0,6)."\"]";
                $string.="\n";
            }
        }
        return $string;
    }
    
    //Reine Transfer Adressen, die zudem in keiner Route verwendet werden, rausfiltern.
    function filterForDot() {
        if ($this->ipHelper->isTransferAddress($this->ip) && $this->getUsed() == 0) {
            return false;
        }
        return true;
    }

}

class Host {
    private $name;
    private $nodes = array();

    function __construct($name) {
        $this->name = $name;
    }

   function addNode($node) {
        $this->nodes[] = $node;
   }

   function getDotCluster() {
        $string = "subgraph cluster_".str_replace("=","Z",base64_encode($this->name))." {\n";
        $string .= " label=\"{$this->name} " . count($this->nodes) . "\"\n";
        $string .= " style=filled fillcolor=floralwhite\n";
        foreach ($this->nodes as $node) {
            $ip = $node->getIP();
            $string .= "\"$ip\"\n";
        }
        $string .= "}\n";

        return $string;
   }
}

function getHTTP($url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 3);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    $data = curl_exec($ch);
    $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    # echo "$url - HTTPCODE: $httpcode\n";
    if (substr($httpcode,0,1) === "2") {
        return $data;
    } else {
        return;
    }
}

/*
$node=new Node("fd43:5602:29bd:29::5",new IPv6Helper());
aquireHostname($node,"https");
echo $node->getPrettyString();
die();
*/

function aquireHostname($node, $protocol) {
    $ip = $node->getSanitizedIP();
    //$ip = "127.0.0.1";
    // L3-Router
    // $ip = "10.50.139.1";
    // L4 Router, dessen Statusseite anscheinend haengt
    // $ip = "10.83.107.1";
    // GW, das nur ueber SSL den Namen preisgibt
    // $ip = "[fd43:5602:29bd:ffff::41]";
    // GW, das nur ueber SSL den Namen per /=jtsi preisgibt
    // $ip = "[fd43:5602:29bd:29::5]";
    // GW, das nur ueber SSL was verwertbares gibt - und auf / hoert (Was eigenes), allerdings mit " Dashboard" im Titel
    // $ip = "10.50.206.1";
    // hat in /gateway danach noch weitere Infos
    // $ip="10.83.252.95";

    // /gateway
    $response = getHTTP($protocol . '://' . $ip . '/gateway');
    if (!empty($response)) {
        // Das macht in der Tat 10.50.250.1 - das redirected auf simblebabel^^
        if (strpos($response,"<title>") !== false) {
            $start=strpos($response,'<title>');
            $end=strpos($response,'</title>');
            if ($start !== false && $end !== false) {
                $hostname = substr($response, $start+7, $end-$start-7);
                if (strpos($hostname,' ') === false) {
                    $node->setHostname($hostname);
                    $node->setHostnameMethod($protocol . ' /gateway - title');
                    $node->setHostnameProtocol($protocol);
                    return;
                }
            }
        } else {
            // Manche GWs haben danach noch Extrainfos
            $hostname=trim(explode("\n",$response)[0]);
            $node->setHostname($hostname);
            $node->setHostnameMethod($protocol . ' /gateway');
            $node->setHostnameProtocol($protocol);
            return;
        }
    }

    // /mrtg
    $response = getHTTP($protocol . '://' . $ip . '/mrtg');
    if (!empty($response)) {
        $start=strpos($response,'<H1>');
        $end=strpos($response,'</H1>');
        if ($start !== false && $end !== false) {
            $hostname = substr($response, $start+4, $end-$start-4);
            //fuer z.B. fff-has2 (10.83.252.43), der da "Willkomen auf fff-has" hat
            $hostname = explode(' ', $hostname);
            $hostname = $hostname[count($hostname)-1];
            $node->setHostname($hostname);
            $node->setHostnameMethod($protocol . ' /mrtg');
            $node->setHostnameProtocol($protocol);
            return;
        }
    }

    // /simpelbabel
    $response = getHTTP($protocol . '://' . $ip . '/simpelbabel');
    if (!empty($response)) {
        $start=strpos($response,'host');
        $end1=strpos($response,"\n",$start);
        $end2=strpos($response,"</td>",$start);
        $end=min($end1,$end2);
        if ($start !== false && $end !== false) {
            $hostname = substr($response, $start+4, $end-$start-4);
            $node->setHostname($hostname);
            $node->setHostnameMethod($protocol . ' /simpelbabel');
            $node->setHostnameProtocol($protocol);
            return;
        }
    }

    // /Simple-Babelweb
    $response = getHTTP($protocol . '://' . $ip . '/Simple-Babelweb');
    if (!empty($response)) {
        $start=strpos($response,'host');
        $end1=strpos($response,"\n", $start);
        $end2=strpos($response,"</td>", $start);
        $end=min($end1,$end2);
        if ($start !== false && $end !== false) {
            $hostname = substr($response, $start+4, $end-$start-4);
            $node->setHostname($hostname);
            $node->setHostnameMethod($protocol . ' /Simple-Babelweb');
            $node->setHostnameProtocol($protocol);
            return;
        }
    }

    // /cgi-bin/status.html
    $response = getHTTP($protocol . '://' . $ip . '/cgi-bin/status.html');
    if (!empty($response)) {
        $start=strpos($response,'<h1>');
        $end=strpos($response,'</h1>', $start);
        if ($start !== false && $end !== false) {
            $hostname = substr($response, $start+4, $end-$start-4);
            $node->setHostname($hostname);
            $node->setHostnameProtocol($protocol);
            if (strpos($response, '<h2>Freifunk Franken Router</h2>' === false)) {
                $node->setHostnameMethod($protocol . ' /cgi-bin/status.html');
            } else {
                $node->setHostnameMethod($protocol . ' /cgi-bin/status.html - Router');
                $node->setl3fffRouter();
            }
            return;
        }
    }

    // /
    $response = getHTTP($protocol . '://' . $ip . '/');
    if (!empty($response)) {
        if (strpos($response,"<title>") !== false) {
            $start=strpos($response,'<title>');
            $end=strpos($response,'</title>', $start);
            if ($start !== false && $end !== false) {
                $hostname = substr($response, $start+7, $end-$start-7);
                //Speziell fuer fblaese...
                if (strpos($hostname, 'nginx') === false) {
                    $hostname = str_replace('Welcome to ','',$hostname);
                    $hostname = str_replace('!','',$hostname);
                }
                //Speziell fuer byteturtles Dashboard...
                $hostname = str_replace(' Dashboard', '', $hostname);
                if (strpos($hostname,' ') === false) {
                    $node->setHostname($hostname);
                    $node->setHostnameMethod($protocol . ' / - title');
                    $node->setHostnameProtocol($protocol);
                    return;
                }
            }
        }
        $start=strpos($response,'<H1>');
        $end=strpos($response,'</H1>');
        if ($start !== false && $end !== false) {
            $hostname = substr($response, $start+4, $end-$start-4);
            //fuer z.B. fff-has2 (10.83.252.43), der da "Willkomen auf fff-has" hat
            $hostname = explode(' ', $hostname);
            $hostname = $hostname[count($hostname)-1];
            $node->setHostname($hostname);
            $node->setHostnameMethod($protocol . ' / - mrtg');
            $node->setHostnameProtocol($protocol);
            return;
        }
        //naila hat jtsi auf https laufen
        if (strpos($response,"domain: '") !== false) {
            $start=strpos($response,"domain: '");
            $end=strpos($response,"',",$start);
            if ($start !== false && $end !== false) {
                $hostname = substr($response, $start+9, $end-$start-9);
                $hostname = str_replace('chat.','',$hostname);
                if (strpos($hostname,' ') === false) {
                    $node->setHostname($hostname);
                    $node->setHostnameMethod($protocol . ' / - jitsi');
                    $node->setHostnameProtocol($protocol);
                    return;
                }
            }
        }
    }
}

function doTraceroute($node) {
    $output = array();
    $returnCode = -1;
    $ip = $node->getIP();
    $ipSanitized =  $node->getSanitizedIP();
    // 2 Versuche
    for ($i = 0; $i < 2; $i++) {
        exec("ping -n -c 1 -W 1.2 $ip", $traceroute, $returnCode);
        if ($returnCode === 0) {
            $node->setpingable();
            break;
        }
    }
    if ($i == 2) {
        echo "not pingable!\n";
    }

    // Manchmal haengt das deutlich mehr als eine Sekunde, wenn der Host nicht erreichbar ist.
    // Die Wahrscheinlichkeit, dass man nicht pingbar ist, aber http antwortet ist gering -> ueberspringen
    if ($node->getpingable()) {
        // 2 Versuche
        for ($i = 0; $i < 2; $i++) {
            $fp = @fsockopen($ipSanitized , 80, $timeout="1");
            if ($fp) {
                fclose($fp);
                $node->sethttpable();
                aquireHostname($node, 'http');
                break;
            }
        }
        if ($i == 2) {
            echo "not httpable!\n";
        }

        //Manche spucken nur ueber ssl was verwertbares aus
        if (empty($node->getHostname())) {
            // 2 Versuche
            for ($i = 0; $i < 2; $i++) {
                $fp = @fsockopen($ipSanitized , 443, $timeout="1");
                if ($fp) {
                    fclose($fp);
                    $node->sethttpable();
                    aquireHostname($node, 'https');
                    break;
                }
            }
        }
    } else {
        echo "not httpable (because of not pingable)!\n";
    }

    //Wenn pingbar und * drinne sind: 3x probieren
    $mintracelength=9999;
    $outputa = array();
    for ($i = 0; $i < 3; $i++) {
        exec("traceroute -n -w 1.1 -z 0.03 -q 1 -m 30 $ip", $outputa[$i]);
        // echo "Laenge des traces : " . count($outputa[$i]) . "\n";
        // >= - =, weil ja sein kann, dass beim vorherigen Durchlauf nur dieser Hop nicht wollte...
        if ($mintracelength >= count($outputa[$i])) {
            $mintracelength = count($outputa[$i]);
            $output = $outputa[$i];
        }
        if (!$node->getpingable() || strpos(implode($outputa[$i]),'*') === false) {
            break;
        }
    }
    $traceroute = array();
    foreach ($output as $string) {
        if (strpos($string,'hops')) {
            continue;
        }
        $string = preg_split("/[\s]+/",trim($string));
        $string = $string[1];
        $traceroute[] = $string;
    }
    //Von hinten durchgehen, alle * zum Schluss killen
    $count = count($traceroute) - 1;
    for (; $count >1; $count--) {
        if ($traceroute[$count] == "*" &&  $traceroute[$count-1] == "*" ) {
            unset($traceroute[$count]);
        } else {
            break;
        }
    }
    
    if ($traceroute[count($traceroute)-1] != "*") {
        $node->setTraceroutable();
    } else {
        echo "not traceable!\n";
    }

    return $traceroute;
}

function getGraph($ipHelperString) {
    $starttime = microtime(true);
    $ipHelper=null;
    
    if ($ipHelperString=="4") {
        $ipHelper = new IPv4Helper();
    } else if ($ipHelperString=="6") {
        $ipHelper = new IPv6Helper();
    } else {
        die("Know only 4 or 6");
    }
    
    $subnets=array();
    $nodes=array();

    //alle Routen bekommen
    $routeTable=array();
    exec('ip '.$ipHelper->getParameter().' route show table fff', $routeTable);

    $startNode = new Node($ipHelper->getLocalhost(), $ipHelper, true);
    $nodes[$startNode->getIP()] = $startNode;

    //Subnetze und Rechner(=Knoten) auslesen
    foreach ($routeTable as $route) {
        echo $route."\n";
        $route = explode(' ',$route)[0];
        echo $route."\n\n";
        if (!$ipHelper->isFFFAddress($route)) {
            continue;
        }
        
        if (strpos($route,'/')) {
            $subnet = new Subnet($route, $ipHelper);
            $subnets[$subnet->getNetwork()] = $subnet;
        } else {
            $node = new Node($route, $ipHelper, true);
            $nodes[$node->getIP()] = $node;
        }
    }

    print_r($subnets);
    print_r($nodes);

    //Durchscannen
    $index=0;
    $count=count($nodes);
    //Wird fuer Knoeten verwendet, die "mittendrin" ein * erzeugen
    $unknownIndex=0;
    //Will nur die originalen scannen, nicht die nachtraeglich eingefuegten (insb. nicht die 0.0.x.yer) Ja, php kopiert dabei das Array.
    $nodesOriginal = $nodes;
    foreach ($nodesOriginal as $node) {
        $index++;
        $ip = $node->getIP();
        echo "$index / $count : $ip\n";
        $traceroute = doTraceroute($node);
	echo $node->getHostname() . ' (' . $node->getHostnameMethod() . ")\n";
        print_r($traceroute);
        
        // Eintragen
        $currentNode = $startNode;
        $tracerouteIndex = 0;
        $tracerouteLength = count($traceroute);
        foreach ($traceroute as $ip) {
            $tracerouteIndex++;
            $currentNode->incrUsed();
            if ($ip == "*") {
                echo "Letzter?: " . $tracerouteIndex . "--" . $tracerouteLength . "\n";
                if ($tracerouteLength == $tracerouteIndex) {
                    //Muss dann der letzte Knoten sein -> also das Ziel des traceroutes. Den da "einschmuggeln". Dann sieht man, ueber welchen Pfad versucht wurde den zu erreichen.
                    $ip = $node->getIP();
                } else {
                    $unknownIndex++;
                    $ip = $ipHelper->getDummyAddress($unknownIndex);
                    $newNode = new Node($ip, $ipHelper, true);
                    $nodes[$ip] = $newNode;
                }
            }
            //Gibts den Node noch nicht? Dann: anlegen
            else if (empty($nodes[$ip])) {
                $newNode = new Node($ip, $ipHelper, true);
                $nodes[$ip] = $newNode;
            }
            //Up- und Downlink verdrahten
            $currentNode->addDownlink($nodes[$ip]);
            $nodes[$ip]->addUplink($currentNode);
            $currentNode = $nodes[$ip];
        }

/*
        if ($index == 20) {
            break;
        }
*/
    }

    //Sortieren anstossen
    foreach ($nodes as $node) {
        echo $node->natsort();
    }

    //Ausgeben
    foreach ($nodes as $node) {
        echo $node->getPrettyString();
    }

    //Clustern (nach Name)
    $hosts = array();
    foreach ($nodes as $node) {
        if (!empty($node->getHostname())) {
            if (!isset($hosts[$node->getHostname()])) {
                $hosts[$node->getHostname()] = new Host($node->getHostname());
            }
            $hosts[$node->getHostname()]->addNode($node);
        }
    }
    uksort($hosts, "strnatcmp");

    //Dot-Datei schreiben
    $neededTime = intval(microtime(true) - $starttime);
    $version = "r5";
    $title = "Generated flowchart of the IPv" . $ipHelperString . " network of freifunk franken. Generator version: $version. Generation time: " . date(DATE_RFC822) . ' Runtime: ' . $neededTime . 's';
    $fp = fopen('/var/www/html/data-'.$ipHelperString.'.dot', 'w');
    fwrite($fp, "digraph flowchart_IPv{$ipHelperString} {\n");
    fwrite($fp, "
    rankdir=LR
    overlap=false
    concentrate=true
    splines=ortho
    label=\"$title\"
    labelloc=top
    labeljust=left
    ");

    //Knoten
    foreach ($nodes as $node) {
        fwrite($fp, $node->getDotNode());
    }

    //Kanten
    foreach ($nodes as $node) {
        fwrite($fp, $node->getDotEdges());
    }

    //Cluster
    foreach ($hosts as $host) {
        fwrite($fp, $host->getDotCluster());
    }

    fwrite($fp, "}");

    fclose($fp);

    //dot ausfuehren
    //todo: produziert aus php raus meist ein 0 byte Bild - extern aufgerufen tuts.
    $result = array();
    for ($tries = 1; $tries <= 3; $tries++) {
        echo "Try $tries...\n";
        exec('dot -Tpng /var/www/html/data-'.$ipHelperString.'.dot >/var/www/html/graph-'.$ipHelperString.'.png', $array);
        foreach ($result as $res) {
            echo $res."\n";
        }
        if (filesize('/var/www/html/graph-'.$ipHelperString.'.png') > 0) {
            echo "...successfull\n";
            break;
        }
    }

    for ($tries = 1; $tries <= 3; $tries++) {
        echo "Try $tries...\n";
        exec('dot -Tpdf /var/www/html/data-'.$ipHelperString.'.dot >/var/www/html/graph-'.$ipHelperString.'.pdf', $array);
        foreach ($result as $res) {
            echo $res."\n";
        }
        if (filesize('/var/www/html/graph-'.$ipHelperString.'.pdf') > 0) {
            echo "...successfull\n";
            break;
        }
    }

    for ($tries = 1; $tries <= 3; $tries++) {
        echo "Try $tries...\n";
        exec('dot -Tsvg /var/www/html/data-'.$ipHelperString.'.dot >/var/www/html/graph-'.$ipHelperString.'.svg', $array);
        foreach ($result as $res) {
            echo $res."\n";
        }
        if (filesize('/var/www/html/graph-'.$ipHelperString.'.svg') > 0) {
            echo "...successfull\n";
            break;
        }
    }
}

getGraph("4");
getGraph("6");
?>

