-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgemini.class.php
158 lines (136 loc) · 4.08 KB
/
gemini.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
151
152
153
154
155
156
157
158
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
// Based on:
// gemini://glasgow.social/gemini-php
class Gemini {
function __construct($config) {
if (empty($config['certificate_file'])) {
die('Missing certificate file. Edit config.php\n');
}
$this->ip = '0';
$this->port = '1965';
$this->data_dir = 'hosts/';
$this->default_host_dir = 'default/';
$this->default_index_file = 'index.gmi';
$this->logging = 1;
$this->log_file = "logs/gemini-php.log";
$this->log_sep = "\t";
$settings = array(
'ip', 'port',
'data_dir',
'default_host_dir',
'default_index_file',
'certificate_file',
'certificate_passphrase'
);
foreach ($settings as $setting_key) {
if (!empty($config[$setting_key])) {
$this->$setting_key = $config[$setting_key];
}
}
// Append the required filepath slashes if they're missing
if (substr($this->data_dir, -1) != "/") {
$this->data_dir .= "/";
}
if (substr($this->default_host_dir, -1) != "/") {
$this->default_host_dir .= "/";
}
if ($this->logging) {
if (!file_exists($this->log_file)) {
$this->log_to_file("Log created", null, null, null, null);
}
if (!is_writable($this->log_file)) {
die("{$this->log_file} is not writable.\n");
}
}
if (!is_readable($this->certificate_file)) {
die("Certificate file {$this->certificate_file} not readable.\n");
}
}
function parse_request($request) {
$url = trim($request); // Strip <CR><LF> from the end
return parse_url($url);
}
function get_valid_hosts() {
$dirs = array_map('basename', glob($this->data_dir . '*', GLOB_ONLYDIR));
return $dirs;
}
function get_status_code($filepath) {
if (is_file($filepath) and file_exists($filepath)) {
return '20';
}
if (!file_exists($filepath)) {
//echo("File $filepath doesn't exist\n");
return '51';
}
return '50';
}
function get_mime_type($filepath) {
$type = mime_content_type($filepath);
// We need a way to detect gemini file types, which PHP doesn't
// so.. if it ends with gemini (or if it has no extension), assume
$path_parts = pathinfo($filepath);
if (empty($path_parts['extension'])
or $path_parts['extension'] === 'gemini'
or $path_parts['extension'] === 'gmi'
) {
$type = 'text/gemini';
}
return $type;
}
/**
* Gets the full file path (assumes directory structure based on host)
*
* This function determines where the requested file is stored
* based on the hostname supplied in the request from the client.
* If no host is supplied, the default directory is assumed.
*
* @param array $url An array returned by the parse_request() method
*
* @return string
*/
function get_filepath($url) {
$hostname = '';
if (!is_array($url)) {
return false;
}
if (!empty($url['host'])) {
$hostname = $url['host'];
}
$valid_hosts = $this->get_valid_hosts();
if (!in_array($hostname, $valid_hosts)) {
$hostname = 'default';
}
// Kristall Browser is adding "__" to the end of the filenames
// wtf am I missing?
// also removing ".." to mitigate against directory traversal
$url['path'] = str_replace(array('..', '__'), '', $url['path']);
// Force an index file to be appended if a filename is missing
if (empty($url['path'])) {
$url['path'] = '/' . $this->default_index_file;
} elseif(substr($url['path'], -1) === '/') {
$url['path'] .= $this->default_index_file;
}
$valid_data_dir = dirname(__FILE__) . '/' . $this->data_dir;
$return_path = $this->data_dir.$hostname.$url['path'];
if (is_link($return_path)) {
return $return_path;
}
// Check the real path is in the data_dir (path traversal sanity check)
if (substr(realpath($return_path), 0, strlen($valid_data_dir)) === $valid_data_dir) {
return $return_path;
}
return false;
}
function log_to_file($ip, $status_code, $meta, $filepath, $filesize) {
$ts = date("Y-m-d H:i:s", strtotime('now'));
$this->log_sep;
$str = $ts.$this->log_sep . $ip . $this->log_sep
. $status_code . $this->log_sep
. $meta.$this->log_sep . $filepath . $this->log_sep
. $filesize . "\n";
file_put_contents($this->log_file, $str, FILE_APPEND);
}
}