1<?php 2 3// Load dependencies 4require_once __DIR__ . '/../vendor/autoload.php'; 5 6// Check arguments 7if (empty($argv[1])) 8{ 9 exit(_('Configured hostname required as argument!') . PHP_EOL); 10} 11 12// Check cert exists 13if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/cert.pem')) 14{ 15 exit( 16 sprintf( 17 _('Certificate for host "%s" not found!') . PHP_EOL, 18 $argv[1] 19 ) 20 ); 21} 22 23// Check key exists 24if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/key.rsa')) 25{ 26 exit( 27 sprintf( 28 _('Key for host "%s" not found!') . PHP_EOL, 29 $argv[1] 30 ) 31 ); 32} 33 34// Check host configured 35if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/config.json')) 36{ 37 exit( 38 sprintf( 39 _('Host "%s" not configured!') . PHP_EOL, 40 $argv[1] 41 ) 42 ); 43} 44 45// Check data directory exist 46if (!is_dir(__DIR__ . '/../host/' . $argv[1] . '/data')) 47{ 48 exit( 49 sprintf( 50 _('Data directory "%s" not found!') . PHP_EOL, 51 $argv[1] 52 ) 53 ); 54} 55 56// Init config 57$config = json_decode( 58 file_get_contents( 59 __DIR__ . '/../host/' . $argv[1] . '/config.json' 60 ) 61); 62 63// Init memory 64$memory = new \Yggverse\Cache\Memory( 65 $config->memcached->server->host, 66 $config->memcached->server->port, 67 $config->memcached->server->host. 68 $config->memcached->server->port, 69 $config->memcached->server->timeout 70); 71 72$memory->flush(); 73 74// Init filesystem 75$filesystem = new \Yggverse\Gemini\Dokuwiki\Filesystem( 76 sprintf( 77 __DIR__ . '/../host/' . $argv[1] . '/data' 78 ) 79); 80 81// Init reader 82$reader = new \Yggverse\Gemini\Dokuwiki\Reader(); 83 84// Init helper 85$helper = new \Yggverse\Gemini\Dokuwiki\Helper( 86 $filesystem, 87 $reader 88); 89 90// Init search server 91$manticore = new \Manticoresearch\Client( 92 [ 93 'host' => $config->manticore->server->host, 94 'port' => $config->manticore->server->port, 95 ] 96); 97 98// Init search index 99$index = $manticore->index( 100 $config->manticore->index->document->name 101); 102 103$index->drop(true); 104$index->create( 105 [ 106 'uri' => 107 [ 108 'type' => 'text' 109 ], 110 'name' => 111 [ 112 'type' => 'text' 113 ], 114 'data' => 115 [ 116 'type' => 'text' 117 ] 118 ], 119 (array) $config->manticore->index->document->settings 120); 121 122foreach ($filesystem->getList() as $path) 123{ 124 if (!str_ends_with($path, $config->manticore->index->extension)) 125 { 126 continue; 127 } 128 129 if ($uri = $filesystem->getPageUriByPath($path)) 130 { 131 if ($data = $filesystem->getDataByPath($path)) 132 { 133 $gemini = $reader->toGemini( 134 $data 135 ); 136 137 $index->addDocument( 138 [ 139 'uri' => $uri, 140 'name' => (string) $reader->getH1( 141 $gemini 142 ), 143 'data' => (string) $reader->toGemini( 144 $gemini 145 ) 146 ], 147 crc32( 148 $uri 149 ) 150 ); 151 } 152 } 153} 154 155// Init server 156$server = new \Yggverse\TitanII\Server(); 157 158$server->setCert( 159 __DIR__ . '/../host/' . $argv[1] . '/cert.pem' 160); 161 162$server->setKey( 163 __DIR__ . '/../host/' . $argv[1] . '/key.rsa' 164); 165 166$server->setHandler( 167 function (\Yggverse\TitanII\Request $request): \Yggverse\TitanII\Response 168 { 169 global $config; 170 global $memory; 171 global $index; 172 global $filesystem; 173 global $reader; 174 global $helper; 175 176 $response = new \Yggverse\TitanII\Response(); 177 178 $response->setCode( 179 $config->gemini->response->default->code 180 ); 181 182 $response->setMeta( 183 $config->gemini->response->default->meta 184 ); 185 186 // Route begin 187 switch ($request->getPath()) 188 { 189 // Static routes 190 case null: 191 case false: 192 case '': 193 194 $response->setCode( 195 30 196 ); 197 198 $response->setMeta( 199 sprintf( 200 'gemini://%s%s/', 201 $config->gemini->server->host, 202 $config->gemini->server->port == 1965 ? null : ':' . $config->gemini->server->port 203 ) 204 ); 205 206 return $response; 207 208 break; 209 210 case '/search': 211 212 // Search query request 213 if (empty($request->getQuery())) 214 { 215 $response->setMeta( 216 'text/plain' 217 ); 218 219 $response->setCode( 220 10 221 ); 222 223 return $response; 224 } 225 226 // Prepare query 227 $query = trim( 228 urldecode( 229 $request->getQuery() 230 ) 231 ); 232 233 // Do search 234 $results = $index->search( 235 @\Manticoresearch\Utils::escape( 236 $query 237 ) 238 )->get(); 239 240 // Init page content 241 $lines = [ 242 PHP_EOL 243 ]; 244 245 // Append page title 246 $lines[] = sprintf( 247 '# %s - %s', 248 $config->string->search, 249 $query 250 ); 251 252 // Append search results 253 if ($total = $results->getTotal()) 254 { 255 $lines[] = sprintf( 256 '%s: %d', 257 $config->string->found, 258 $total 259 ); 260 261 $lines[] = sprintf( 262 '## %s', 263 $config->string->results 264 ); 265 266 foreach($results as $result) 267 { 268 $lines[] = sprintf( 269 '=> /%s %s', 270 $result->get('uri'), 271 $result->get('name') 272 ); 273 } 274 } 275 276 // Nothing found 277 else 278 { 279 $lines[] = $config->string->nothing; 280 } 281 282 // Append actions 283 $lines[] = sprintf( 284 '## %s', 285 $config->string->actions 286 ); 287 288 // Append search link 289 $lines[] = sprintf( 290 '=> /search %s', 291 $config->string->search 292 ); 293 294 // Append homepage link 295 $lines[] = sprintf( 296 '=> / %s', 297 $config->string->main 298 ); 299 300 // Append source link 301 $lines[] = sprintf( 302 '=> %s %s', 303 $config->dokuwiki->url->source, 304 $config->string->source 305 ); 306 307 // Append about info 308 $lines[] = $config->string->about; 309 310 // Append aliases 311 if ($config->dokuwiki->url->alias) 312 { 313 $lines[] = sprintf( 314 '## %s', 315 $config->string->alias 316 ); 317 318 foreach ($config->dokuwiki->url->alias as $base => $name) 319 { 320 $lines[] = sprintf( 321 '=> %s %s', 322 $base, 323 $name 324 ); 325 } 326 } 327 328 // Build content 329 $response->setContent( 330 implode( 331 PHP_EOL, 332 $lines 333 ) 334 ); 335 336 return $response; 337 338 break; 339 340 default: 341 342 // Parse request 343 preg_match('/^\/([^\/]*)$/', $request->getPath(), $matches); 344 345 $uri = isset($matches[1]) ? $matches[1] : ''; 346 347 // File request, get page content 348 if ($path = $filesystem->getPagePathByUri($uri)) 349 { 350 // Check for cached results 351 if ($content = $memory->get($path)) 352 { 353 $response->setContent( 354 $content 355 ); 356 357 return $response; 358 } 359 360 // Define base URL 361 $reader->setMacros( 362 '~URL:base~', 363 sprintf( 364 'gemini://%s%s/', 365 $request->getHost(), 366 $config->gemini->server->port == 1965 ? null : ':' . $config->gemini->server->port 367 ) 368 ); 369 370 // Define index menu 371 $menu = []; 372 373 // Append index sections 374 if ($sections = $helper->getChildrenSectionLinksByUri($uri)) 375 { 376 // Append header 377 $menu[] = sprintf( 378 '### %s', 379 $config->string->sections 380 ); 381 382 // Append sections 383 foreach ($sections as $section) 384 { 385 $menu[] = $section; 386 } 387 } 388 389 // Get children pages 390 if ($pages = $helper->getChildrenPageLinksByUri($uri)) 391 { 392 // Append header 393 $menu[] = sprintf( 394 '### %s', 395 $config->string->pages 396 ); 397 398 // Append pages 399 foreach ($pages as $page) 400 { 401 $menu[] = $page; 402 } 403 } 404 405 // Set macros value 406 if ($menu) 407 { 408 $reader->setRule( 409 '/\{\{indexmenu>:([^\}]+)\}\}/i', 410 implode( 411 PHP_EOL, 412 $menu 413 ) 414 ); 415 } 416 417 // Convert 418 $gemini = $reader->toGemini( 419 $filesystem->getDataByPath( 420 $path 421 ) 422 ); 423 424 $lines = [ 425 $gemini 426 ]; 427 428 // Get page links 429 if ($links = $reader->getLinks($gemini)) 430 { 431 $lines[] = sprintf( 432 '## %s', 433 $config->string->links 434 ); 435 436 foreach ($links as $link) 437 { 438 $lines[] = sprintf( 439 '=> %s', 440 $link 441 ); 442 } 443 } 444 445 // Append actions header 446 $lines[] = sprintf( 447 '## %s', 448 $config->string->actions 449 ); 450 451 // Append search link 452 $lines[] = sprintf( 453 '=> /search %s', 454 $config->string->search 455 ); 456 457 // Append homepage link 458 $lines[] = sprintf( 459 '=> / %s', 460 $config->string->main 461 ); 462 463 // Append source link 464 $lines[] = sprintf( 465 '=> %s/%s %s', 466 $config->dokuwiki->url->source, 467 $uri, 468 $config->string->source 469 ); 470 471 // Append about info 472 $lines[] = $config->string->about; 473 474 // Append aliases 475 if ($config->dokuwiki->url->alias) 476 { 477 $lines[] = sprintf( 478 '## %s', 479 $config->string->alias 480 ); 481 482 foreach ($config->dokuwiki->url->alias as $base => $name) 483 { 484 $lines[] = sprintf( 485 '=> %s/%s %s', 486 $base, 487 $uri, 488 $name 489 ); 490 } 491 } 492 493 // Merge lines 494 $content = implode( 495 PHP_EOL, 496 $lines 497 ); 498 499 // Cache results 500 $memory->set( 501 $path, 502 $content 503 ); 504 505 // Response 506 $response->setContent( 507 $content 508 ); 509 510 return $response; 511 } 512 513 // File not found, request directory for minimal navigation 514 else if ($path = $filesystem->getDirectoryPathByUri($uri)) 515 { 516 // Check for cached results 517 if ($content = $memory->get($path)) 518 { 519 $response->setContent( 520 $content 521 ); 522 523 return $response; 524 } 525 526 // Init home page content 527 $lines = [ 528 PHP_EOL 529 ]; 530 531 // Build header 532 $h1 = []; 533 534 $segments = []; 535 536 foreach ((array) explode(':', $uri) as $segment) 537 { 538 $segments[] = $segment; 539 540 // Find section index if exists 541 if ($file = $filesystem->getPagePathByUri(implode(':', $segments) . ':' . $segment)) 542 { 543 $h1[] = $reader->getH1( 544 $reader->toGemini( 545 $filesystem->getDataByPath( 546 $file 547 ) 548 ) 549 ); 550 } 551 552 // Find section page if exists 553 else if ($file = $filesystem->getPagePathByUri(implode(':', $segments))) 554 { 555 $h1[] = $reader->getH1( 556 $reader->toGemini( 557 $filesystem->getDataByPath( 558 $file 559 ) 560 ) 561 ); 562 } 563 564 // Reset title of undefined segment 565 else 566 { 567 $h1[] = null; 568 } 569 } 570 571 // Append header 572 $lines[] = sprintf( 573 '# %s', 574 empty($h1[0]) ? $config->string->welcome : implode(' ', $h1) 575 ); 576 577 // Get children sections 578 if ($sections = $helper->getChildrenSectionLinksByUri($uri)) 579 { 580 // Append header 581 $lines[] = sprintf( 582 '## %s', 583 $config->string->sections 584 ); 585 586 // Append sections 587 foreach ($sections as $section) 588 { 589 $lines[] = $section; 590 } 591 } 592 593 // Get children pages 594 if ($pages = $helper->getChildrenPageLinksByUri($uri)) 595 { 596 // Append header 597 $lines[] = sprintf( 598 '## %s', 599 $config->string->pages 600 ); 601 602 // Append pages 603 foreach ($pages as $page) 604 { 605 $lines[] = $page; 606 } 607 } 608 609 // Append about info 610 $lines[] = sprintf( 611 '## %s', 612 $config->string->actions 613 ); 614 615 // Append search link 616 $lines[] = sprintf( 617 '=> /search %s', 618 $config->string->search 619 ); 620 621 // Append source link 622 $lines[] = sprintf( 623 '=> %s %s', 624 $config->dokuwiki->url->source, 625 $config->string->source 626 ); 627 628 // Append about info 629 $lines[] = $config->string->about; 630 631 // Append aliases 632 if ($config->dokuwiki->url->alias) 633 { 634 $lines[] = sprintf( 635 '## %s', 636 $config->string->alias 637 ); 638 639 foreach ($config->dokuwiki->url->alias as $base => $name) 640 { 641 $lines[] = sprintf( 642 '=> %s/%s %s', 643 $base, 644 $uri, 645 $name 646 ); 647 } 648 } 649 650 // Merge lines 651 $content = implode( 652 PHP_EOL, 653 $lines 654 ); 655 656 // Cache results 657 $memory->set( 658 $path, 659 $content 660 ); 661 662 // Response 663 $response->setContent( 664 $content 665 ); 666 667 return $response; 668 } 669 670 // Media request 671 else if ($path = $filesystem->getMediaPathByUri($uri)) 672 { 673 if ($mime = $filesystem->getMimeByPath($path)) 674 { 675 if ($data = $filesystem->getDataByPath($path)) 676 { 677 // Set MIME 678 $response->setMeta( 679 $mime 680 ); 681 682 // Append data 683 $response->setContent( 684 $data 685 ); 686 687 // Response 688 return $response; 689 } 690 } 691 } 692 } 693 694 // Not found 695 $response->setCode( 696 51 697 ); 698 699 $response->setMeta( 700 $config->string->nothing 701 ); 702 703 return $response; 704 } 705); 706 707// Start server 708echo sprintf( 709 _('Server "%s" started on %s:%d') . PHP_EOL, 710 $argv[1], 711 $config->gemini->server->host, 712 $config->gemini->server->port 713); 714 715$server->start( 716 $config->gemini->server->host, 717 $config->gemini->server->port 718);