root/dev/common/includes/class.parser.php @ 378

Revision 378, 26.6 KB (checked in by kovell, 13 years ago)

API feed caches date properly. Rank mod has missing TopLists?. Parser has better spelling.

Line 
1<?php
2require_once("class.alliance.php");
3require_once("class.corp.php");
4require_once("class.pilot.php");
5require_once("class.kill.php");
6require_once("class.item.php");
7require_once("class.parser.translate.php");
8
9class Parser
10{
11    function uchr ($codes) { //converts characterset code-pages to ascii-compatible types
12        if (is_scalar($codes)) $codes= func_get_args();
13        $str= '';
14        foreach ($codes as $code) $str.= html_entity_decode('&#'.$code.';',ENT_NOQUOTES,'UTF-8');
15        return $str;
16    }
17
18    function Parser($killmail)
19    {
20        if(function_exists('mb_convert_encoding')) $killmail = mb_convert_encoding($killmail, "UTF-8");
21        $this->error_ = array();
22        $this->killmail_ = trim(str_replace("\r", '', $killmail));
23        $this->returnmail = false;
24
25        $pos = 0;
26        $russian = $this->uchr(1042).$this->uchr(1086).$this->uchr(1074).$this->uchr(1083).$this->uchr(1077).$this->uchr(1095)
27                .$this->uchr(1077).$this->uchr(1085).$this->uchr(1086).' '.$this->uchr(1087).$this->uchr(1080).$this->uchr(1088)
28                .$this->uchr(1072).$this->uchr(1090).$this->uchr(1086).$this->uchr(1074).':'; //involved party
29
30        //Fraktion added for mails that originate from griefwatch / battle-clinic that do nothing with this info
31        if (strpos($this->killmail_, 'Beteiligte Parteien:') || (strpos($this->killmail_, 'Fraktion:')))
32        {
33            $this->preparse('german');
34        } 
35        elseif (strpos($this->killmail_, $russian)) 
36        { 
37            $this->preparse('russian');
38           
39        }
40        elseif (strpos($this->killmail_, 'System Security Level:'))
41        {
42            // this converts the killmail internally from pre-rmr to kali format
43            $this->preparse('prermr');
44        }
45
46        //get the mail's timestamp - if older than QR, then preparse for scrambler translation
47        $timestamp = substr($this->killmail_, 0, 16);
48        $timestamp = str_replace('.', '-', $timestamp);
49
50        $apoc_release = '2009-03-10 03:00:00';
51
52        if(strtotime($timestamp) < strtotime($apoc_release))
53        {
54            $this->preparse('apoc');
55        }
56
57        $qr_release = '2008-11-11 00:00:00';
58
59        if(strtotime($timestamp) < strtotime($qr_release))
60        {
61            $this->preparse('preqr');
62        }
63
64        if (strpos($this->killmail_, '**** Truncated - mail is too large ****') > 0)
65        {
66            $this->killmail_ = str_replace('**** Truncated - mail is too large ****', '', $this->killmail_);
67        }
68
69        // Parser fix, since some killmails don't have a final blow, they would break the KB.
70        //On mails without final blow info, the first name on the list becomes the final blow holder
71        if (strpos($this->killmail_, 'laid the final blow') < 1)
72        {
73            $this->needs_final_blow_ = 1;
74        }
75    }
76
77    function parse($checkauth = true)
78    {
79        // header section
80        $involvedpos = strpos($this->killmail_, "Involved parties:");
81        if($involvedpos == 0)
82        {
83            $this->error("Mail lacks Involved parties header.");
84            return 0;
85        }
86
87        $header = substr($this->killmail_, 0, $involvedpos);
88        $timestamp = substr($header, 0, 16);
89
90        $victim = explode("\n", trim(substr($this->killmail_, 0, $involvedpos)));
91        $upper_limit =  count($victim);
92       
93        $victimname = "Unknown"; //lovely default values
94        $factionname = "None";
95        $alliancename = "None";
96        $corpname = "Unknown";
97        $shipname = "Unknown";
98        $systemname = "Unknown";
99        $systemsec = "0.0";
100        $dmgtaken = '0';
101        $this->dmgtaken = '0';
102        $pos = 0;
103        $moon = "";
104       
105        for($counter = 0; $counter <= $upper_limit; $counter++)
106        {
107            if(preg_match("/Victim: (.*)/", $victim[$counter], $matches)) 
108            {
109                if($matches[1])
110                    $victimname = $matches[1];
111            } 
112            elseif (preg_match("/Corp: (.*)/", $victim[$counter], $matches)) 
113            {
114                 if($matches[1])
115                    $corpname = $matches[1];
116            }
117            elseif (preg_match("/Alliance: (.*)/", $victim[$counter], $matches)) 
118            {
119                if($matches[1])
120                    $alliancename = $matches[1];
121            }
122            elseif (preg_match("/Faction:\s*(.*)/", $victim[$counter], $matches)) 
123            {
124                if($matches[1])
125                    $factionname = $matches[1];
126            }
127            elseif (preg_match("/Destroyed: (.*)/", $victim[$counter], $matches)) 
128            {
129                if($matches[1])
130                    $shipname = $matches[1];
131            } 
132            elseif (preg_match("/System: (.*)/", $victim[$counter], $matches)) 
133            {
134                if($matches[1])
135                                {
136                    $systemname = $matches[1];
137                        // fix for unanchored destroyed POS modules - replace $moon with $systemname
138                    // as that's all we have to go on. Placed here as $moon is filled before
139                    //systemname is known - Captain Thunk
140                    if ((strcmp($moon, 'Unknown') == 0) && ($pos == 1))
141                        {
142                                $moon = $matches[1];
143                                $victimname = $matches[1];
144                    }
145                                }
146                        }
147            elseif (preg_match("/Security: (.*)/", $victim[$counter], $matches)) 
148            {
149                if($matches[1])
150                    $systemsec = $matches[1];
151            } 
152            elseif (preg_match("/Damage Taken: (.*)/", $victim[$counter], $matches)) 
153            {
154                if($matches[1])
155                {
156                    $dmgtaken = $matches[1];
157                    $this->dmgtaken = $dmgtaken;
158                }
159            } 
160            elseif (preg_match("/Moon: (.*)/", $victim[$counter], $matches)) 
161            {
162                if($matches[1])
163                {
164                    $moon = $matches[1];
165                    $victimname = $matches[1];
166                    $pos = 1;
167                }
168                else
169                { //if the system is valid, it will pick this up, provided it features after
170                  //the moon is listed - which is unlikely unless the mail format
171                  //drastically changes... again :)
172                    $moon = 'Unknown';
173                    $victimname = 'Unknown';
174                    $pos = 1;
175                }
176            }
177        }
178       
179        //report the errors for the things that make sense.
180        //we need pilot names, corp names, ship types, and the system to be sure
181        //the rest aren't required but for completeness, you'd want them in :)
182        if (strcmp($victimname, 'Unknown') == 0)
183        {
184            $this->error('Victim has no name.');
185            unset($victimname); //we unset the variable so that it fails the next check
186        }
187       
188        if (strcmp($corpname, 'Unknown') == 0)
189        {
190            $this->error('Victim has no corp.');
191            unset($corpname);
192        }
193           
194        if (strcmp($shipname, 'Unknown') == 0)
195        {
196            $this->error('Victim has no ship type.');
197            unset($shipname);
198        }
199       
200        if (strcmp($systemname, 'Unknown') == 0)
201        {
202            $this->error('Killmail lacks solar system information.');
203            unset($systemname);
204        }
205       
206        if ($pos == 1)
207        {
208                $victimname = $moon;
209        }
210       
211        if (!isset($timestamp) || 
212                !isset($factionname) ||
213                !isset($alliancename) ||
214                !isset($corpname) ||
215                !isset($victimname) ||
216                !isset($shipname) ||
217                !isset($systemname) ||
218                !isset($systemsec))
219            return 0;
220
221        if ($checkauth)
222            $authorized = false;
223        else $authorized = true;
224
225        // populate/update database
226        $alliance = new Alliance();
227        $alliance->add($alliancename);
228        $corp = new Corporation();
229        $corp->add($corpname, $alliance, $timestamp);
230        $victim = new Pilot();
231        $victim->add($victimname, $corp, $timestamp);
232        $system = new SolarSystem();
233        $system->lookup($systemname);
234        if (!$system->getID())
235        {
236            $this->error('System not found.', $systemname);
237        }
238        $ship = new Ship();
239        $ship->lookup($shipname);
240        if (!$ship->getID())
241        {
242            $this->error('Ship not found.', $shipname);
243        }
244        $kill = new Kill();
245        $kill->setTimeStamp($timestamp);
246        $kill->setVictimID($victim->getID());
247        $kill->setVictimCorpID($corp->getID());
248        $kill->setVictimAllianceID($alliance->getID());
249                $kill->setVictimShip($ship);
250        $kill->setSolarSystem($system);
251        if ($dmgtaken)
252        {
253            $kill->set('dmgtaken', $dmgtaken);
254        }
255
256        if (ALLIANCE_ID != 0 && $alliance->getID() == ALLIANCE_ID)
257        {
258            $authorized = true;
259        }
260        elseif (CORP_ID != 0)
261        {
262            $corps = explode(",", CORP_ID);
263            foreach($corps as $checkcorp)
264            {
265                if ($corp->getID() == $checkcorp)
266                    $authorized = true;
267            }
268        }
269
270        // involved parties section
271        $end = strpos($this->killmail_, "Destroyed items:");
272        if ($end == 0)
273        {
274            $end = strpos($this->killmail_, "Dropped items:");
275            if ($end == 0)
276            { //try to parse to the end of the mail in the event sections are missing
277                $end = strlen($this->killmail_);
278            }
279        }
280        $involved = explode("\n", trim(substr($this->killmail_, strpos($this->killmail_, "Involved parties:") + 17, $end - (strpos($this->killmail_, "Involved parties:") + 17))));     
281
282        $ipilot_count = 0; //allows us to be a bit more specific when errors strike
283        $i = 0;
284
285        $order = 0;
286        while ($i < count($involved))
287        {             
288            $iparts = count($involved);
289            $finalblow = 0;
290
291            while($i < $iparts) {
292                $ipilot_count++;
293               
294                $ipname = "Unknown";
295                $ianame = "None";
296                $ifname = "None";
297                $icname = "None";
298                $isname = "Unknown";
299                $iwname = "Unknown";
300                $idmgdone = '0';
301                $secstatus = "0.0";
302               
303               
304                while($involved[$i] == '')
305                { //compensates for multiple blank lines between involved parties
306                    $i++;
307                    if($i > count($involved))
308                    {
309                        $this->error("Involved parties section prematurely ends.");
310                        return 0;
311
312                    }
313                }
314
315                for($counter = $i; $counter <= $iparts; $counter++)
316                {
317                    if(preg_match("/Name: (.*)/", $involved[$counter], $matches)) 
318                    {
319                        if($matches[1])
320                        {
321                            if(stristr($involved[$counter], '/'))
322                            {
323                                $slash = strpos($involved[$counter], '/');
324                                $name = trim(substr($involved[$counter], 5, $slash-5));
325                                $corporation = trim(substr($involved[$counter], $slash+1, strlen($involved[$counter])- $shash+1));
326
327                                //alliance lookup for warp disruptors - normal NPCs aren't to be bundled uin
328                                $crp = new Corporation();
329                                $crp->lookup($corporation);
330                                if($crp->getID() > 0 && stristr($name, ' warp '))
331                                {
332                                    $al = $crp->getAlliance();
333                                    $ianame = $al->getName();
334                                }
335
336                                //now if the corp bit has final blow info, note it
337                                preg_match("/(.*) \\(laid the final blow\\)/", $corporation, $matched);
338                                if($matched[1])
339                                {
340                                    $finalblow = 1;
341                                    $iwname = $name;
342                                    $end = strpos($corporation, '(') -1;
343                                    $corporation = substr($corporation, 0, $end);
344                                }
345                                else 
346                                {
347                                    $finalblow = 0;
348                                    $iwname = $name;
349                                }
350                                $ipname = $name;
351                                $icname = $corporation;
352                            }
353                            else
354                            {
355                                $ipname = $matches[1];
356                                preg_match("/(.*) \\(laid the final blow\\)/", $ipname, $matches);
357                                if ($matches[1])
358                                {
359                                    $ipname = $matches[1];
360                                    $finalblow = 1;
361                                }
362                                else $finalblow = 0;
363                            }
364                        }
365                    }
366                    else if(preg_match("/Alliance: (.*)/", $involved[$counter], $matches)) 
367                    {
368                        if($matches[1])
369                            $ianame = $matches[1];
370                    }
371                    else if(preg_match("/Faction: (.*)/", $involved[$counter], $matches)) 
372                    {
373                        if($matches[1])
374                            $ifname = $matches[1];
375                    }
376                    else if(preg_match("/Corp: (.*)/", $involved[$counter], $matches)) 
377                    {
378                        if($matches[1])
379                            $icname = $matches[1];
380                    }
381                    else if(preg_match("/Ship: (.*)/", $involved[$counter], $matches)) 
382                    {
383                        if($matches[1])
384                            $isname = $matches[1];
385                    }
386                    else if(preg_match("/Weapon: (.*)/", $involved[$counter], $matches)) 
387                    {
388                        if($matches[1])
389                            $iwname = $matches[1];
390                    }
391                    else if(preg_match("/Security: (.*)/", $involved[$counter], $matches)) 
392                    {
393                        if($matches[1])
394                            $secstatus = $matches[1];
395                    }
396                    else if(preg_match("/Damage Done: (.*)/", $involved[$counter], $matches)) 
397                    {
398                        if($matches[1])
399                            $idmgdone = $matches[1];
400                    }
401                    else if($involved[$counter] == '')
402                    { //allows us to process the involved party. This is the empty line after the
403                      //involved party section
404                        $counter++;
405                        $i = $counter;
406                        break; 
407                    }
408                    else { //skip over this entry, it could read anything, we don't care. Handy if/when
409                           //new mail fields get added and we aren't handling them yet.
410                        //$counter++;
411                        //$i = $counter;
412                    }
413                   
414                    if ($this->needs_final_blow_)
415                    {
416                        $finalblow = 1;
417                        $this->needs_final_blow_ = 0;
418                    }
419                }
420
421                $ialliance = new Alliance();
422                $ialliance->add($ianame);
423               
424                $icorp = new Corporation();
425                if (strcmp($icname, 'None') == 0)
426                {
427                    $this->error('Involved party has no corp. (Party No. '.$ipilot_count.')');
428                }
429                else
430                {   //don't add corp, because pilots have to be in corps.
431                    $icorp->add($icname, $ialliance, $kill->getTimeStamp());
432                }
433
434                $ipilot = new Pilot(); 
435               
436                if (strcmp($ipname, 'Unknown') == 0)
437                {
438                    if (preg_match("/Mobile/", $iwname) || preg_match("/Control Tower/", $iwname))
439                    { //for involved parties parsed that lack a pilot, but are actually POS or mobile warp disruptors
440                        $ipname = $iwname;
441                        $ipilot->add($ipname, $icorp, $timestamp); 
442                    }
443                    else $this->error('Involved party has no name. (Party No. '.$ipilot_count.')');
444                }
445                else
446                {
447                    //don't add pilot if the pilot's unknown or dud
448                    $ipilot->add($ipname, $icorp, $timestamp); 
449                }
450
451                $iship = new Ship();
452                $iship->lookup($isname);
453                if (!$iship->getID())
454                {
455                    $this->error('Ship not found.', $isname);
456                }
457
458                $iweapon = new Item();
459                $iweapon->lookup($iwname);
460                if (strcmp($iwname, 'Unknown') == 0)
461                {
462                    $this->error('No weapon found for pilot "'.$ipname .'"');
463                } elseif (!$iweapon->getID())
464                {
465                    $this->error('Weapon not found.', $iwname);
466                }
467
468                if (ALLIANCE_ID != 0 && $ialliance->getID() == ALLIANCE_ID)
469                {
470                    $authorized = true;
471                }
472                elseif (CORP_ID != 0)
473                {
474                    $corps = explode(",", CORP_ID);
475                    foreach($corps as $corp)
476                    {
477                        if ($icorp->getID() == $corp)
478                            $authorized = true;
479                    }
480                }
481                if (!$authorized)
482                {
483                    if ($string = config::get('post_permission'))
484                    {
485                        if ($string == 'all')
486                        {
487                            $authorized = true;
488                        }
489                        else
490                        {
491                            $tmp = explode(',', $string);
492                            foreach ($tmp as $item)
493                            {
494                                if (!$item)
495                                {
496                                    continue;
497                                }
498                                $typ = substr($item, 0, 1);
499                                $id = substr($item, 1);
500                                if ($typ == 'a')
501                                {
502                                    if ($ialliance->getID() == $id || $kill->getVictimAllianceID() == $id)
503                                    {
504                                        $authorized = true;
505                                        break;
506                                    }
507                                }
508                                elseif ($typ == 'c')
509                                {
510                                    if ($icorp->getID() == $id || $kill->getVictimCorpID() == $id)
511                                    {
512                                        $authorized = true;
513                                        break;
514                                    }
515                                }
516                                elseif ($typ == 'p')
517                                {
518                                    if ($ipilot->getID() == $id || $kill->getVictimID() == $id)
519                                    {
520                                        $authorized = true;
521                                        break;
522                                    }
523                                }
524                            }
525                        }
526                    }
527                }
528
529                $iparty = new InvolvedParty($ipilot->getID(), $icorp->getID(),
530                    $ialliance->getID(), $secstatus, $iship, $iweapon);
531                if ($dmgtaken)
532                {
533                    $iparty->dmgdone_ = $idmgdone;
534                }
535                $kill->addInvolvedParty($iparty);
536
537                if ($finalblow == 1)
538                {
539                    $kill->setFBPilotID($ipilot->getID());
540                    $kill->setFBCorpID($icorp->getID());
541                    $kill->setFBAllianceID($ialliance->getID());
542                }
543            } 
544        }
545
546        // destroyed items section
547        $destroyedpos = strpos($this->killmail_, "Destroyed items:");
548
549        if ($destroyedpos)
550        {
551            $endpos = strlen($this->killmail_) - $destroyedpos + 16;
552            $pos = strpos($this->killmail_, "Dropped items:");
553            if ($pos === false)
554            {
555                $pos = strlen($this->killmail_);
556            }
557            $endpos = $pos - $destroyedpos - 16;
558
559            $destroyed = explode("\n", trim(substr($this->killmail_, $destroyedpos + 16, $endpos)));
560            #var_dump($destroyed); exit;
561            $destroyed_items = $this->scanForItems($destroyed);
562            foreach ($destroyed_items as $item)
563            {
564                $ditem = new DestroyedItem($item['item'], $item['quantity'], $item['location']);
565                $kill->addDestroyedItem($ditem);
566            }
567        }
568
569        $startpos = strpos($this->killmail_, "Dropped items:");
570        if ($startpos)
571        {
572            $endpos = strlen($this->killmail_) - $startpos + 14;
573
574            $dropped = explode("\n", trim(substr($this->killmail_, $startpos + 14, $endpos)));
575            #var_dump($dropped); exit;
576
577            $dropped_items = $this->scanForItems($dropped);
578            foreach ($dropped_items as $item)
579            {
580                $ditem = new DroppedItem($item['item'], $item['quantity'], $item['location']);
581                $kill->addDroppedItem($ditem);
582            }
583        }
584
585        if (!$authorized)
586        {
587            return -2;
588        }
589        if ($this->getError())
590        {
591            return 0;
592        }
593
594        if ($this->returnmail)
595        {
596            return $kill;
597        }
598        $id = $kill->add();
599        if ($id == -1)
600        {
601            $this->dupeid_ = $kill->dupeid_;
602        }
603
604        return $id;
605    }
606
607    function scanForItems($destroyed)
608    {
609        $i = 0;
610        $num = count($destroyed);
611        while ($i < $num)
612        {
613            $destroyed[$i] = trim($destroyed[$i]);
614
615            $itemname = substr($destroyed[$i], 0, strlen($destroyed[$i]));
616            //API mod will return null when it can't lookup an item, so filter these
617            if($itemname == '(Cargo)' || (strpos($itemname, ', Qty:') === 0))
618            {
619                $this->error('Item name missing, yet item has quantity. If you have used the API mod for this kill, you are missing items from your dabase.',$itemname);
620                $i++;
621                continue; //continue to get rest of the mail's possible errors
622            }
623
624            if ($destroyed[$i] == "")
625            {
626                $i++;
627                continue;
628            }
629
630            if ($destroyed[$i] == "Empty.")
631            {
632                $container = false;
633                $i++;
634                continue;
635            }
636
637            $qtypos = 0;
638            $locpos = 0;
639            $itemname = "";
640            $quantity = "";
641            $location = "";
642
643            $qtypos = strpos($destroyed[$i], ", Qty: ");
644            $locpos = strrpos($destroyed[$i], "(");
645
646            if ($container && $locpos != false)
647            {
648                $container = false;
649            }
650            if (strpos($destroyed[$i], "Container"))
651            {
652                $container = true;
653            }
654            if ($qtypos <= 0 && !$locpos)
655            {
656                $itemlen = strlen($destroyed[$i]);
657                if ($container) $location = "Cargo";
658            }
659            if ($qtypos > 0 && !$locpos)
660            {
661                $itemlen = $qtypos;
662                $qtylen = strlen($destroyed[$i]) - $qtypos;
663                if ($container) $location = "Cargo";
664            }
665            if ($locpos > 0 && $qtypos <= 0)
666            {
667                $itemlen = $locpos - 1;
668                $qtylen = 0;
669                $loclen = strlen($destroyed[$i]) - $locpos - 2;
670                if (!$locpos) $container = false;
671            }
672            if ($locpos > 0 && $qtypos > 0)
673            {
674                $itemlen = $qtypos;
675                $qtylen = $locpos - $qtypos - 7;
676                $loclen = strlen($destroyed[$i]) - $locpos - 2;
677                if (!$locpos) $container = false;
678            }
679
680            $itemname = substr($destroyed[$i], 0, $itemlen);
681            if ($qtypos) $quantity = substr($destroyed[$i], $qtypos + 6, $qtylen);
682            if ($locpos) $location = substr($destroyed[$i], $locpos + 1, $loclen);
683
684            if ($quantity == "")
685            {
686                $quantity = 1;
687            }
688
689            $item = new Item();
690            $item->lookup(trim($itemname));
691            if (!$item->getID())
692            {
693                $this->error('Item not found.', trim($itemname));
694            }
695            if ($location == 'In Container')
696            {
697                $location = 'Cargo';
698            }
699
700            $items[] = array('item' => $item, 'quantity' => $quantity, 'location' => $location);
701            $i++;
702        }
703
704        return $items;
705    }
706
707    function error($message, $debugtext = null)
708    {
709        $this->error_[] = array($message, $debugtext);
710    }
711
712    function getError()
713    {
714        if (count($this->error_))
715        {
716            return $this->error_;
717        }
718        return false;
719    }
720
721    function preparse($set)
722    {
723        $translate = new Translate($set);
724        $this->killmail_ = $translate->getTranslation($this->killmail_);
725    }
726}
727//Currently maintained by FriedRoadKill
728?>
Note: See TracBrowser for help on using the browser.