MetaGer.php 44.5 KB
Newer Older
1
2
3
4
<?php
namespace App;

use App;
5
use Cache;
6
7
use Illuminate\Http\Request;
use Jenssegers\Agent\Agent;
Dominik Hebeler's avatar
Bugfix    
Dominik Hebeler committed
8
use LaravelLocalization;
9
use Log;
10
use Predis\Connection\ConnectionException;
11
use Redis;
12
13
14

class MetaGer
{
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    # Einstellungen für die Suche
    protected $fokus;
    protected $eingabe;
    protected $q;
    protected $category;
    protected $time;
    protected $page;
    protected $lang;
    protected $cache = "";
    protected $site;
    protected $hostBlacklist   = [];
    protected $domainBlacklist = [];
    protected $stopWords       = [];
    protected $phrases         = [];
    protected $engines         = [];
    protected $results         = [];
    protected $ads             = [];
Dominik Hebeler's avatar
Dominik Hebeler committed
32
    protected $products        = [];
33
34
35
    protected $warnings        = [];
    protected $errors          = [];
    protected $addedHosts      = [];
36
    protected $startCount      = 0;
37
    protected $canCache        = false;
38
39
40
41
42
43
44
45
46
    # Daten über die Abfrage
    protected $ip;
    protected $language;
    protected $agent;
    # Konfigurationseinstellungen:
    protected $sumaFile;
    protected $mobile;
    protected $resultCount;
    protected $sprueche;
47
    protected $domainsBlacklisted = [];
48
    protected $urlsBlacklisted    = [];
49
50
51
    protected $url;
    protected $languageDetect;

52
53
    public function __construct()
    {
54
        # Timer starten
55
        $this->starttime = microtime(true);
56
57

        # Versuchen Blacklists einzulesen
58
59
        if (file_exists(config_path() . "/blacklistDomains.txt") && file_exists(config_path() . "/blacklistUrl.txt")) {
            $tmp                      = file_get_contents(config_path() . "/blacklistDomains.txt");
60
            $this->domainsBlacklisted = explode("\n", $tmp);
61
62
63
            $tmp                      = file_get_contents(config_path() . "/blacklistUrl.txt");
            $this->urlsBlacklisted    = explode("\n", $tmp);
        } else {
64
            Log::warning("Achtung: Eine, oder mehrere Blacklist Dateien, konnten nicht geöffnet werden");
65
66
        }

67
        # Parser Skripte einhängen
68
69
70
71
        $dir = app_path() . "/Models/parserSkripte/";
        foreach (scandir($dir) as $filename) {
            $path = $dir . $filename;
            if (is_file($path)) {
72
                require_once $path;
73
74
75
            }
        }

76
        # Cachebarkeit testen
77
78
79
80
81
82
        try {
            Cache::has('test');
            $this->canCache = true;
        } catch (ConnectionException $e) {
            $this->canCache = false;
        }
83
    }
84

85
    # Erstellt aus den gesammelten Ergebnissen den View
86
87
88
    public function createView()
    {
        $viewResults = [];
89
        # Wir extrahieren alle notwendigen Variablen und geben Sie an unseren View:
90
        foreach ($this->results as $result) {
91
92
93
94
95
96
            $viewResults[] = get_object_vars($result);
        }

        # Wir müssen natürlich noch den Log für die durchgeführte Suche schreiben:
        $this->createLogs();

97
98
        if ($this->fokus === "bilder") {
            switch ($this->out) {
99
100
101
102
103
104
105
106
                case 'results':
                    return view('metager3bilderresults')
                        ->with('results', $viewResults)
                        ->with('eingabe', $this->eingabe)
                        ->with('mobile', $this->mobile)
                        ->with('warnings', $this->warnings)
                        ->with('errors', $this->errors)
                        ->with('metager', $this)
107
                        ->with('browser', (new Agent())->browser());
108
109
110
111
112
113
114
115
                default:
                    return view('metager3bilder')
                        ->with('results', $viewResults)
                        ->with('eingabe', $this->eingabe)
                        ->with('mobile', $this->mobile)
                        ->with('warnings', $this->warnings)
                        ->with('errors', $this->errors)
                        ->with('metager', $this)
116
                        ->with('browser', (new Agent())->browser());
117
            }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
        } else {
            switch ($this->out) {
                case 'results':
                    return view('metager3results')
                        ->with('results', $viewResults)
                        ->with('eingabe', $this->eingabe)
                        ->with('mobile', $this->mobile)
                        ->with('warnings', $this->warnings)
                        ->with('errors', $this->errors)
                        ->with('metager', $this)
                        ->with('browser', (new Agent())->browser());
                    break;
                case 'results-with-style':
                    return view('metager3')
                        ->with('results', $viewResults)
                        ->with('eingabe', $this->eingabe)
                        ->with('mobile', $this->mobile)
                        ->with('warnings', $this->warnings)
                        ->with('errors', $this->errors)
                        ->with('metager', $this)
                        ->with('suspendheader', "yes")
                        ->with('browser', (new Agent())->browser());
                    break;
                default:
                    return view('metager3')
                        ->with('eingabe', $this->eingabe)
                        ->with('mobile', $this->mobile)
                        ->with('warnings', $this->warnings)
                        ->with('errors', $this->errors)
                        ->with('metager', $this)
                        ->with('browser', (new Agent())->browser());
                    break;
            }
151
152
153
        }
    }

Phil Höfer's avatar
Phil Höfer committed
154
    public function prepareResults()
155
    {
Phil Höfer's avatar
Phil Höfer committed
156
        $engines = $this->engines;
157

Phil Höfer's avatar
Phil Höfer committed
158
159
        // combine
        $combinedResults = $this->combineResults($engines);
160
161
162
163

        # Wir bestimmen die Sprache eines jeden Suchergebnisses
        $this->results = $this->addLangCodes($this->results);

Phil Höfer's avatar
Phil Höfer committed
164
165
166
167
168
169
        // sort
        //$sortedResults = $this->sortResults($engines);
        // filter
        // augment (boost&adgoal)
        // authorize
        // misc (WiP)
170
171
        uasort($this->results, function ($a, $b) {
            if ($a->getRank() == $b->getRank()) {
172
                return 0;
173
174
            }

175
176
            return ($a->getRank() < $b->getRank()) ? 1 : -1;
        });
177

178
179
        # Validate Results
        $newResults = [];
180
181
        foreach ($this->results as $result) {
            if ($result->isValid($this)) {
182
                $newResults[] = $result;
183
184
            }

185
186
187
        }
        $this->results = $newResults;

188
189
190
191
192
193
        # Boost implementation
        $this->results = $this->parseBoost($this->results);

        #Adgoal Implementation
        $this->results = $this->parseAdgoal($this->results);

194
        $counter   = 0;
195
        $firstRank = 0;
196

197
        if (isset($this->startForwards)) {
198
            $this->startCount = $this->startForwards;
199
        } elseif (isset($this->startBackwards)) {
200
            $this->startCount = $this->startBackwards - count($this->results) - 1;
201
        } else {
202
203
204
            $this->startCount = 0;
        }

205
206
        foreach ($this->results as $result) {
            if ($counter === 0) {
207
                $firstRank = $result->rank;
208
209
            }

210
            $counter++;
211
            $result->number = $counter + $this->startCount;
212
213
214
215
            $confidence     = 0;
            if ($firstRank > 0) {
                $confidence = $result->rank / $firstRank;
            } else {
216
                $confidence = 0;
217
218
219
            }

            if ($confidence > 0.65) {
220
                $result->color = "#FF4000";
221
            } elseif ($confidence > 0.4) {
222
                $result->color = "#FF0080";
223
            } elseif ($confidence > 0.2) {
224
                $result->color = "#C000C0";
225
            } else {
226
                $result->color = "#000000";
227
228
            }

229
230
        }

231
        if (LaravelLocalization::getCurrentLocale() === "en") {
232
233
234
            $this->ads = [];
        }

235
        $this->validated = false;
236
        if (isset($this->password)) {
237
238
239
            # Wir bieten einen bezahlten API-Zugriff an, bei dem dementsprechend die Werbung ausgeblendet wurde:
            # Aktuell ist es nur die Uni-Mainz. Deshalb überprüfen wir auch nur diese.
            $password = getenv('mainz');
240
            $eingabe  = $this->eingabe;
241
            $password = md5($eingabe . $password);
242
243
            if ($this->password === $password) {
                $this->ads       = [];
244
                $this->products  = [];
245
246
247
                $this->validated = true;
            }
        }
248
249

        if (count($this->results) <= 0) {
250
            $this->errors[] = trans('metaGer.results.failed');
251
        }
252

253
        if ($this->canCache() && isset($this->next) && count($this->next) > 0 && count($this->results) > 0) {
254
            $page       = $this->page + 1;
255
            $this->next = [
256
257
258
                'page'          => $page,
                'startForwards' => $this->results[count($this->results) - 1]->number,
                'engines'       => $this->next,
259
260
            ];
            Cache::put(md5(serialize($this->next)), serialize($this->next), 60);
261
262
        } else {
            $this->next = [];
263
264
        }

265
    }
266

267
268
    private function addLangCodes($results)
    {
269
270
271
272
273
        # Wenn es keine Ergebnisse gibt, brauchen wir uns gar nicht erst zu bemühen
        if (sizeof($results) === 0) {
            return $results;
        }

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        # Bei der Spracheinstellung "all" wird nicht gefiltert
        if ($this->getLang() === "all") {
            return $results;
        } else {
            # Ansonsten müssen wir jedem Result einen Sprachcode hinzufügen
            $id          = 0;
            $langStrings = [];
            foreach ($results as $result) {
                # Wir geben jedem Ergebnis eine ID um später die Sprachcodes zuordnen zu können
                $result->id = $id;

                $langStrings["result_" . $id] = utf8_encode($result->getLangString());

                $id++;
            }
            # Wir schreiben die Strings in eine temporäre JSON-Datei,
            # Da das Array unter umständen zu groß ist für eine direkte Übergabe an das Skript
            $filename = "/tmp/" . getmypid();
            file_put_contents($filename, json_encode($langStrings));
            $langDetectorPath = app_path() . "/Models/lang.pl";
            $lang             = exec("echo '$filename' | $langDetectorPath");
            $lang             = json_decode($lang, true);

            # Wir haben nun die Sprachcodes der einzelnen Ergebnisse.
            # Diese müssen wir nur noch korrekt zuordnen, dann sind wir fertig.
            foreach ($lang as $key => $langCode) {
                # Prefix vom Key entfernen:
                $id = intval(str_replace("result_", "", $key));
                foreach ($this->results as $result) {
                    if ($result->id === $id) {
                        $result->langCode = $langCode;
                        break;
                    }
                }
            }
            return $results;
        }
    }

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
    /**
     * Diese Funktion überprüft, ob wir einen erweiterten Check auf Bots machen müssen.
     * Z.B.: Wurden wir von einem Bot (dessen Anfragen aus dem Tor-Netzwerk kamen) mit tausenden
     * Anfragen zu Telefonnummern überschwemmt. Bei diesen werden wir nun eine erweiterte Überprüfung
     * durchführen.
     * Für den Anfang werden wir alle Anfragen, die unter diese Kriterien fallen, nur noch beantworten, wenn
     * JavaScript ausgeführt wird. (Mal schauen ob und wie lange dies ausreicht)
     */
    public function doBotProtection($bot)
    {
        $hash = md5(date('YmdHi'));
        if (preg_match("/^\d+$/s", $this->getEingabe()) && $bot !== $hash) {
            return true;
        } else {
            return false;
        }

    }

Phil Höfer's avatar
Phil Höfer committed
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
    public function combineResults($engines)
    {
        foreach ($engines as $engine) {
            if (isset($engine->next)) {
                $this->next[] = $engine->next;
            }
            if (isset($engine->last)) {
                $this->last[] = $engine->last;
            }
            foreach ($engine->results as $result) {
                if ($result->valid) {
                    $this->results[] = $result;
                }
            }
            foreach ($engine->ads as $ad) {
                $this->ads[] = $ad;
            }
            foreach ($engine->products as $product) {
                $this->products[] = $product;
            }
        }

    }

356
357
    public function parseBoost($results)
    {
358
359
360
361
362
363
364
365
366
367
368
369
        foreach ($results as $result) {
            if (preg_match('/^(http[s]?\:\/\/)?(www.)?amazon\.de/', $result->anzeigeLink)) {
                if (preg_match('/\?/', $result->anzeigeLink)) {
                    $result->link .= '&tag=boostmg01-21';
                } else {
                    $result->link .= '?tag=boostmg01-21';
                }
                $result->partnershop = true;

            }
        }
        return $results;
370
    }
371

372
373
    public function parseAdgoal($results)
    {
374
        $publicKey  = getenv('adgoal_public');
375
        $privateKey = getenv('adgoal_private');
376
        if ($publicKey === false) {
377
378
379
            return $results;
        }
        $tldList = "";
380
381
        try {
            foreach ($results as $result) {
382
                $link = $result->anzeigeLink;
383
                if (strpos($link, "http") !== 0) {
384
385
386
387
388
389
390
391
392
393
                    $link = "http://" . $link;
                }
                $tldList .= parse_url($link, PHP_URL_HOST) . ",";
                $result->tld = parse_url($link, PHP_URL_HOST);
            }
            $tldList = rtrim($tldList, ",");

            # Hashwert
            $hash = md5("meta" . $publicKey . $tldList . "GER");

394
            # Query
395
396
            $query = urlencode($this->q);

397
            $link   = "https://api.smartredirect.de/api_v2/CheckForAffiliateUniversalsearchMetager.php?p=" . $publicKey . "&k=" . $hash . "&tld=" . $tldList . "&q=" . $query;
398
399
400
            $answer = json_decode(file_get_contents($link));

            # Nun müssen wir nur noch die Links für die Advertiser ändern:
401
            foreach ($answer as $el) {
402
                $hoster = $el[0];
403
                $hash   = $el[1];
404

405
406
                foreach ($results as $result) {
                    if ($hoster === $result->tld) {
407
408
                        # Hier ist ein Advertiser:
                        # Das Logo hinzufügen:
409
                        if ($result->image !== "") {
410
                            $result->logo = "https://img.smartredirect.de/logos_v2/60x30/" . $hash . ".gif";
411
                        } else {
412
                            $result->image = "https://img.smartredirect.de/logos_v2/120x60/" . $hash . ".gif";
413
414
                        }

415
416
417
                        # Den Link hinzufügen:
                        $publicKey = $publicKey;
                        $targetUrl = $result->anzeigeLink;
418
                        if (strpos($targetUrl, "http") !== 0) {
419
                            $targetUrl = "http://" . $targetUrl;
420
421
422
423
424
                        }

                        $gateHash            = md5($targetUrl . $privateKey);
                        $newLink             = "https://api.smartredirect.de/api_v2/ClickGate.php?p=" . $publicKey . "&k=" . $gateHash . "&url=" . urlencode($targetUrl) . "&q=" . $query;
                        $result->link        = $newLink;
425
426
427
428
                        $result->partnershop = true;
                    }
                }
            }
429
        } catch (\ErrorException $e) {
430
431
432
433
434
            return $results;
        }

        return $results;
    }
435

Karl's avatar
Karl committed
436
437
438
439
    /*
     * Die Erstellung der Suchmaschinen bis die Ergebnisse da sind mit Unterfunktionen
     */

440
441
    public function createSearchEngines(Request $request)
    {
442
        # Wenn es kein Suchwort gibt
443
        if (!$request->has("eingabe") || $this->q === "") {
444
            return;
445
        }
446

447
        $xml                  = simplexml_load_file($this->sumaFile);
448
        $sumas                = $xml->xpath("suma");
449
        $enabledSearchengines = [];
450
        $overtureEnabled      = false;
451
452
        $sumaCount            = 0;

Karl's avatar
Karl committed
453
        /* Erstellt die Liste der eingestellten Sumas
454
455
456
         * Der einzige Unterschied bei angepasstem Suchfokus ist,
         * dass nicht nach den Typen einer Suma,
         * sondern den im Request mitgegebenen Typen entschieden wird.
Karl's avatar
Karl committed
457
458
459
460
461
462
         * Ansonsten wird genau das selbe geprüft und gemacht:
         * Handelt es sich um spezielle Suchmaschinen die immer an sein müssen
         * Wenn es Overture ist vermerken dass Overture an ist
         * Suma Zähler erhöhen
         * Zu Liste hinzufügen
         */
463
        foreach ($sumas as $suma) {
464
            if (($this->sumaIsSelected($suma, $request)
465
                || (!$this->isBildersuche()
466
                    && $this->sumaIsAdsuche($suma, $overtureEnabled)))
467
468
469
                && (!$this->sumaIsDisabled($suma))) {
                if ($this->sumaIsOverture($suma)) {
                    $overtureEnabled = true;
470
                }
471
472
                if ($this->sumaIsNotAdsuche($suma)) {
                    $sumaCount += 1;
473
                }
474
                $enabledSearchengines[] = $suma;
475
476
477
478
479
            }
        }

        # Sonderregelung für alle Suchmaschinen, die zu den Minisuchern gehören. Diese können alle gemeinsam über einen Link abgefragt werden
        $subcollections = [];
480
481

        $tmp = [];
482
483
        foreach ($enabledSearchengines as $engine) {
            if (isset($engine['minismCollection'])) {
484
                $subcollections[] = $engine['minismCollection']->__toString();
485
            } else {
486
                $tmp[] = $engine;
487
488
            }

489
490
        }
        $enabledSearchengines = $tmp;
491
        if (sizeof($subcollections) > 0) {
492
            $enabledSearchengines[] = $this->loadMiniSucher($xml, $subcollections);
493
494
        }

495
        if ($sumaCount <= 0) {
496
            $this->errors[] = trans('metaGer.settings.noneSelected');
497
        }
498

499
        $engines = [];
500

501
        # Wenn eine Sitesearch durchgeführt werden soll, überprüfen wir ob überhaupt eine der Suchmaschinen eine Sitesearch unterstützt
502
        $siteSearchFailed = $this->checkCanNotSitesearch($enabledSearchengines);
503
504

        $typeslist = [];
505
        $counter   = 0;
506

507
508
        if ($this->requestIsCached($request)) {
            $engines = $this->getCachedEngines($request);
509
        } else {
510
            $engines = $this->actuallyCreateSearchEngines($enabledSearchengines, $siteSearchFailed);
511
        }
512

513
        # Wir starten alle Suchen
514
515
        foreach ($engines as $engine) {
            $engine->startSearch($this);
516
        }
517

518
519
        $this->adjustFocus($sumas, $enabledSearchengines);

520
521
522
523
524
        /* Wir warten auf die Antwort der Suchmaschinen
         * Die Verbindung steht zu diesem Zeitpunkt und auch unsere Requests wurden schon gesendet.
         * Wir zählen die Suchmaschinen, die durch den Cache beantwortet wurden:
         * $enginesToLoad zählt einerseits die Suchmaschinen auf die wir warten und andererseits
         * welche Suchmaschinen nicht rechtzeitig geantwortet haben.
525
526
         */

527
        $enginesToLoad = [];
528
529
530
531
532
533
        $canBreak      = false;
        foreach ($engines as $engine) {
            if ($engine->cached) {
                if ($overtureEnabled && ($engine->name === "overture" || $engine->name === "overtureAds")) {
                    $canBreak = true;
                }
534
535
            } else {
                $enginesToLoad[$engine->name] = false;
536
537
538
539
540
541
542
543
            }
        }

        $this->waitForResults($enginesToLoad, $overtureEnabled, $canBreak);

        $this->retrieveResults($engines);
    }

544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    # Spezielle Suchen und Sumas

    public function sumaIsSelected($suma, $request)
    {
        if ($this->fokus === "angepasst") {
            if ($request->has($suma["name"])) {
                return true;
            }
        } else {
            $types = explode(",", $suma["type"]);
            if (in_array($this->fokus, $types)) {
                return true;
            }
        }
        return false;
    }

    public function actuallyCreateSearchEngines($enabledSearchengines, $siteSearchFailed)
    {
        $engines = [];
        foreach ($enabledSearchengines as $engine) {

            # Wenn diese Suchmaschine gar nicht eingeschaltet sein soll
            if (!$siteSearchFailed
                && strlen($this->site) > 0
                && (!isset($engine['hasSiteSearch'])
                    || $engine['hasSiteSearch']->__toString() === "0")) {
                continue;
            }

            # Setze Pfad zu Parser
            $path = "App\Models\parserSkripte\\" . ucfirst($engine["package"]->__toString());

            # Prüfe ob Parser vorhanden
            if (!file_exists(app_path() . "/Models/parserSkripte/" . ucfirst($engine["package"]->__toString()) . ".php")) {
579
                Log::error("Konnte " . $engine["name"] . " nicht abfragen, da kein Parser existiert");
580
                $this->errors[] = trans('metaGer.engines.noParser', ['engine' => $engine["name"]]);
581
582
583
584
585
586
587
588
                continue;
            }

            # Es wird versucht die Suchengine zu erstellen
            $time = microtime();
            try {
                $tmp = new $path($engine, $this);
            } catch (\ErrorException $e) {
589
                Log::error("Konnte " . $engine["name"] . " nicht abfragen. " . var_dump($e));
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
                continue;
            }

            # Ausgabe bei Debug-Modus
            if ($tmp->enabled && isset($this->debug)) {
                $this->warnings[] = $tmp->service . "   Connection_Time: " . $tmp->connection_time . "    Write_Time: " . $tmp->write_time . " Insgesamt:" . ((microtime() - $time) / 1000);
            }

            # Wenn die neu erstellte Engine eingeschaltet ist, wird sie der Liste hinzugefügt
            if ($tmp->isEnabled()) {
                $engines[] = $tmp;
            }
        }
        return $engines;
    }

    public function isBildersuche()
    {
608
        return $this->fokus === "bilder";
609
610
611
612
    }

    public function sumaIsAdsuche($suma, $overtureEnabled)
    {
613
        $sumaName = $suma["name"]->__toString();
614
        return
615
616
617
618
            $sumaName === "qualigo"
            || $sumaName === "similar_product_ads"
            || (!$overtureEnabled && $sumaName === "overtureAds")
            || $sumaName == "rlvproduct";
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
    }

    public function sumaIsDisabled($suma)
    {
        return
        isset($suma['disabled'])
        && $suma['disabled']->__toString() === "1";
    }

    public function sumaIsOverture($suma)
    {
        return
        $suma["name"]->__toString() === "overture"
        || $suma["name"]->__toString() === "overtureAds";
    }

    public function sumaIsNotAdsuche($suma)
    {
        return
        $suma["name"]->__toString() !== "qualigo"
        && $suma["name"]->__toString() !== "similar_product_ads"
        && $suma["name"]->__toString() !== "overtureAds";
    }

    public function requestIsCached($request)
    {
        return
        $request->has('next')
        && Cache::has($request->input('next'))
        && unserialize(Cache::get($request->input('next')))['page'] > 1;
    }

    public function getCachedEngines($request)
    {
        $next       = unserialize(Cache::get($request->input('next')));
        $this->page = $next['page'];
        $engines    = $next['engines'];
        if (isset($next['startForwards'])) {
            $this->startForwards = $next['startForwards'];
        }
        if (isset($next['startBackwards'])) {
            $this->startBackwards = $next['startBackwards'];
        }
        return $engines;
    }

    public function loadMiniSucher($xml, $subcollections)
    {
        $minisucherEngine             = $xml->xpath('suma[@name="minism"]')[0];
        $subcollections               = urlencode("(" . implode(" OR ", $subcollections) . ")");
        $minisucherEngine["formData"] = str_replace("<<SUBCOLLECTIONS>>", $subcollections, $minisucherEngine["formData"]);
        $minisucherEngine["formData"] = str_replace("<<COUNT>>", sizeof($subcollections) * 10, $minisucherEngine["formData"]);
        return $minisucherEngine;
    }

674
    # Passt den Suchfokus an, falls für einen Fokus genau alle vorhandenen Sumas eingeschaltet sind
675
676
    public function adjustFocus($sumas, $enabledSearchengines)
    {
677
678
        # Findet für alle Foki die enthaltenen Sumas
        $foki = []; # [fokus][suma] => [suma]
679
        foreach ($sumas as $suma) {
680
            if ((!$this->sumaIsDisabled($suma)) && (!isset($suma['userSelectable']) || $suma['userSelectable']->__toString() === "1")) {
681
                if (isset($suma['type'])) {
682
683
684
                    # Wenn foki für diese Suchmaschine angegeben sind
                    $focuses = explode(",", $suma['type']->__toString());
                    foreach ($focuses as $foc) {
685
686
687
688
689
                        if (isset($suma['minismCollection'])) {
                            $foki[$foc][] = "minism";
                        } else {
                            $foki[$foc][] = $suma['name']->__toString();
                        }
690
                    }
691
                } else {
692
                    # Wenn keine foki für diese Suchmaschine angegeben sind
693
694
695
696
697
                    if (isset($suma['minismCollection'])) {
                        $foki["andere"][] = "minism";
                    } else {
                        $foki["andere"][] = $suma['name']->__toString();
                    }
698
699
700
701
                }
            }
        }

702
        # Findet die Namen der aktuell eingeschalteten Sumas
703
        $realEngNames = [];
704
        foreach ($enabledSearchengines as $realEng) {
705
            $nam = $realEng["name"]->__toString();
706
            if ($nam !== "qualigo" && $nam !== "overtureAds" && $nam !== "rlvproduct") {
707
708
709
                $realEngNames[] = $nam;
            }
        }
710

711
        # Anschließend werden diese beiden Listen verglichen (jeweils eine der Fokuslisten für jeden Fokus), um herauszufinden ob sie vielleicht identisch sind. Ist dies der Fall, so hat der Nutzer anscheinend Suchmaschinen eines kompletten Fokus eingestellt. Der Fokus wird dementsprechend angepasst.
712
        foreach ($foki as $fok => $engines) {
713
            $isFokus      = true;
714
            $fokiEngNames = [];
715
            foreach ($engines as $eng) {
716
717
                $fokiEngNames[] = $eng;
            }
718
            # Jede eingeschaltete Engine ist für diesen Fokus geeignet
719
            foreach ($fokiEngNames as $fen) {
720
721
                # Bei Bildersuchen ist uns egal, ob alle Suchmaschinen aus dem Suchfokus eingeschaltet sind, da wir sie eh als Bildersuche anzeigen müssen
                if (!in_array($fen, $realEngNames) && $fok !== "bilder") {
722
723
724
                    $isFokus = false;
                }
            }
725
            # Jede im Fokus erwartete Engine ist auch eingeschaltet
726
727
            foreach ($realEngNames as $ren) {
                if (!in_array($ren, $fokiEngNames)) {
728
729
730
                    $isFokus = false;
                }
            }
731
            # Wenn die Listen identisch sind, setze den Fokus um
732
            if ($isFokus) {
733
734
735
                $this->fokus = $fok;
            }
        }
736
    }
737

738
739
740
741
742
743
744
    public function checkCanNotSitesearch($enabledSearchengines)
    {
        if (strlen($this->site) > 0) {
            $enginesWithSite = 0;
            foreach ($enabledSearchengines as $engine) {
                if (isset($engine['hasSiteSearch']) && $engine['hasSiteSearch']->__toString() === "1") {
                    $enginesWithSite++;
745
                }
746
747
            }
            if ($enginesWithSite === 0) {
748
                $this->errors[] = trans('metaGer.sitesearch.failed', ['site' => $this->site, 'searchLink' => $this->generateSearchLink("web", false)]);
749
750
                return true;
            } else {
751
                $this->warnings[] = trans('metaGer.sitesearch.success', ['site' => $this->site]);
752
                return false;
753
754
            }
        }
755
        return false;
756
757
758
759
    }

    public function waitForResults($enginesToLoad, $overtureEnabled, $canBreak)
    {
760
        $loadedEngines = 0;
761
        $timeStart     = microtime(true);
762
763
764
765

        # Auf wie viele Suchmaschinen warten wir?
        $engineCount = count($enginesToLoad);

766
767
        while (true) {
            $time          = (microtime(true) - $timeStart) * 1000;
768
            $loadedEngines = intval(Redis::hlen('search.' . $this->getHashCode()));
769
            if ($overtureEnabled && (Redis::hexists('search.' . $this->getHashCode(), 'overture') || Redis::hexists('search.' . $this->getHashCode(), 'overtureAds'))) {
770
                $canBreak = true;
771
            }
772
773

            # Abbruchbedingung
774
            if ($time < 500) {
775
                if (($engineCount === 0 || $loadedEngines >= $engineCount) && $canBreak) {
776
                    break;
777
778
779
                }

            } elseif ($time >= 500 && $time < $this->time) {
780
                if (($engineCount === 0 || ($loadedEngines / ($engineCount * 1.0)) >= 0.8) && $canBreak) {
781
                    break;
782
783
784
                }

            } else {
785
786
787
788
                break;
            }
            usleep(50000);
        }
789
790
791
792
793
794
795

        # Wir haben nun so lange wie möglich gewartet. Wir registrieren nun noch die Suchmaschinen, die geanwortet haben.
        $answered = Redis::hgetall('search.' . $this->getHashCode());
        foreach ($answered as $key => $value) {
            $enginesToLoad[$key] = true;
        }
        $this->enginesToLoad = $enginesToLoad;
796
    }
797

798
799
800
    public function retrieveResults($engines)
    {
        # Von geladenen Engines die Ergebnisse holen
801
802
803
        foreach ($engines as $engine) {
            if (!$engine->loaded) {
                try {
804
                    $engine->retrieveResults($this);
805
                } catch (\ErrorException $e) {
806
807
808
809
                    Log::error($e);
                }
            }
        }
810

811
        # Nicht fertige Engines verwefen
812
813
        foreach ($engines as $engine) {
            if (!$engine->loaded) {
814
                $engine->shutdown();
815
            }
816
817
818
        }

        $this->engines = $engines;
819
820
    }

821
822
823
/*
 * Ende Suchmaschinenerstellung und Ergebniserhalt
 */
Karl's avatar
Karl committed
824

825
826
    public function parseFormData(Request $request)
    {
827
        # Sichert, dass der request in UTF-8 formatiert ist
828
829
830
831
832
833
834
835
        if ($request->input('encoding', '') !== "utf8") {
            # In früheren Versionen, als es den Encoding Parameter noch nicht gab, wurden die Daten in ISO-8859-1 übertragen
            $input = $request->all();
            foreach ($input as $key => $value) {
                $input[$key] = mb_convert_encoding("$value", "UTF-8", "ISO-8859-1");
            }
            $request->replace($input);
        }
836
        $this->url = $request->url();
837
        # Zunächst überprüfen wir die eingegebenen Einstellungen:
838
        # Fokus
839
        $this->fokus = $request->input('focus', 'web');
840
        # Suma-File
841
        if (App::isLocale("en")) {
842
            $this->sumaFile = config_path() . "/sumas.xml";
843
        } else {
844
845
            $this->sumaFile = config_path() . "/sumas.xml";
        }
846
        if (!file_exists($this->sumaFile)) {
847
            die(trans('metaGer.formdata.cantLoad'));
848
        }
849
        # Sucheingabe
850
        $this->eingabe = trim($request->input('eingabe', ''));
851
        if (strlen($this->eingabe) === 0) {
852
            $this->warnings[] = trans('metaGer.formdata.noSearch');
853
854
        }
        $this->q = $this->eingabe;
855
        # IP
856
        $this->ip = $request->ip();
857
        # Language
858
        if (isset($_SERVER['HTTP_LANGUAGE'])) {
859
            $this->language = $_SERVER['HTTP_LANGUAGE'];
860
        } else {
861
862
863
864
            $this->language = "";
        }
        # Category
        $this->category = $request->input('category', '');
865
        # Request Times
866
867
        $this->time = $request->input('time', 1000);
        # Page
868
        $this->page = 1;
869
870
        # Lang
        $this->lang = $request->input('lang', 'all');
871
872
        if ($this->lang !== "de" && $this->lang !== "en" && $this->lang !== "all") {
            $this->lang = "all";
873
        }
874
        $this->agent  = new Agent();
875
        $this->mobile = $this->agent->isMobile();
876
        # Sprüche
877
        $this->sprueche = $request->input('sprueche', 'off');
878
        if ($this->sprueche === "off") {
879
            $this->sprueche = true;
880
        } else {
881
            $this->sprueche = false;
882
        }
883
        # Theme
Phil Höfer's avatar
Phil Höfer committed
884
        $this->theme = preg_replace("/[^[:alnum:][:space:]]/u", '', $request->input('theme', 'default'));
885
886
887
        # Ergebnisse pro Seite:
        $this->resultCount = $request->input('resultCount', '20');
        # Manchmal müssen wir Parameter anpassen um den Sucheinstellungen gerecht zu werden:
888
889
        if ($request->has('dart')) {
            $this->time       = 10000;
890
            $this->warnings[] = trans('metaGer.formdata.dartEurope');
891
        }
892
893
        if ($this->time <= 500 || $this->time > 20000) {
            $this->time = 1000;
894
        }
895
896
897
898
899
900
901
902
903
        if ($request->has('minism') && ($request->has('fportal') || $request->has('harvest'))) {
            $input    = $request->all();
            $newInput = [];
            foreach ($input as $key => $value) {
                if ($key !== "fportal" && $key !== "harvest") {
                    $newInput[$key] = $value;
                }
            }
            $request->replace($newInput);
904
        }
905
906
        if (App::isLocale("en")) {
            $this->sprueche = "off";
907
        }
908
909
        if ($this->resultCount <= 0 || $this->resultCount > 200) {
            $this->resultCount = 1000;
910
        }
911
912
913
914
915
916
        if ($request->has('onenewspageAll') || $request->has('onenewspageGermanyAll')) {
            $this->time  = 5000;
            $this->cache = "cache";
        }
        if ($request->has('tab')) {
            if ($request->input('tab') === "off") {
917
                $this->tab = "_blank";
918
            } else {
919
920
                $this->tab = "_self";
            }
921
        } else {
922
923
            $this->tab = "_blank";
        }
924
        if ($request->has('password')) {
925
            $this->password = $request->input('password');
926
927
        }
        if ($request->has('quicktips')) {
928
            $this->quicktips = false;
929
        } else {
930
            $this->quicktips = true;
931
        }
932
        $this->out = $request->input('out', "html");
933
        # Standard output format html
934
        if ($this->out !== "html" && $this->out !== "json" && $this->out !== "results" && $this->out !== "results-with-style") {
935
            $this->out = "html";
936
        }
937
        $this->request = $request;
938
939
940
941
    }

    public function checkSpecialSearches(Request $request)
    {
942
943
944
945
946
        if ($request->has('site')) {
            $site = $request->input('site');
        } else {
            $site = "";
        }
947

948
949
950
951
        $this->searchCheckSitesearch($site);
        $this->searchCheckHostBlacklist();
        $this->searchCheckDomainBlacklist();
        $this->searchCheckPhrase();
Karl's avatar
Karl committed
952
        $this->searchCheckStopwords();
953
954
955
956

        if ($this->q === "") {
            $this->warnings[] = trans('metaGer.formdata.noSearch');
        }
957
958
    }

959
    public function searchCheckSitesearch($site)
960
    {
961
        if (preg_match("/(.*)\bsite:(\S+)(.*)/si", $this->q, $match)) {
962
963
964
            $this->site = $match[2];
            $this->q    = $match[1] . $match[3];
        }
965
966
        if ($site !== "") {
            $this->site = $site;
967
        }
968
    }
969

970
    public function searchCheckHostBlacklist()
971
    {
972
        while (preg_match("/(.*)(^|\s)-host:(\S+)(.*)/si", $this->q, $match)) {
973
            $this->hostBlacklist[] = $match[3];
974
            $this->q               = $match[1] . $match[4];
975
976
977
978
979
980
981
        }
        if (sizeof($this->hostBlacklist) > 0) {
            $hostString = "";
            foreach ($this->hostBlacklist as $host) {
                $hostString .= $host . ", ";
            }
            $hostString       = rtrim($hostString, ", ");
982
            $this->warnings[] = trans('metaGer.formdata.hostBlacklist', ['host' => $hostString]);
983
        }
984
    }
985

986
    public function searchCheckDomainBlacklist()
987
    {
988
        while (preg_match("/(.*)(^|\s)-domain:(\S+)(.*)/si", $this->q, $match)) {
989
            $this->domainBlacklist[] = $match[3];
990
            $this->q                 = $match[1] . $match[4];
991
992
993
994
995
996
997
        }
        if (sizeof($this->domainBlacklist) > 0) {
            $domainString = "";
            foreach ($this->domainBlacklist as $domain) {
                $domainString .= $domain . ", ";
            }
            $domainString     = rtrim($domainString, ", ");
998
            $this->warnings[] = trans('metaGer.formdata.domainBlacklist', ['domain' => $domainString]);
999
        }
1000
    }
1001

1002
    public function searchCheckStopwords()
1003
    {
1004
        while (preg_match("/(.*)(^|\s)-(\S+)(.*)/si", $this->q, $match)) {
1005
            $this->stopWords[] = $match[3];
1006
            $this->q           = $match[1] . $match[4];
1007
1008
1009
1010
1011
1012
1013
        }
        if (sizeof($this->stopWords) > 0) {
            $stopwordsString = "";
            foreach ($this->stopWords as $stopword) {
                $stopwordsString .= $stopword . ", ";
            }
            $stopwordsString  = rtrim($stopwordsString, ", ");
1014
            $this->warnings[] = trans('metaGer.formdata.stopwords', ['stopwords' => $stopwordsString]);