Browse Source

Add $ENV->convert, update readme and config template, add more Twig templates, etc.

biotorrents 3 years ago
parent
commit
f397d945f4

+ 1
- 0
.gitignore View File

@@ -33,3 +33,4 @@ __MACOSX/
33 33
 /static/common/badges/**
34 34
 /static/styles/**/*.css
35 35
 /static/styles/assets/fonts/**
36
+fonts.tgz

+ 0
- 63
classes/charts.class.php View File

@@ -168,66 +168,3 @@ class PIE_CHART extends GOOGLE_CHARTS
168 168
         $this->URL .= "&chl=".implode('|', $Labels).'&chd=e:'.implode('', $Data);
169 169
     }
170 170
 }
171
-
172
-/*
173
-class LOG_BAR_GRAPH extends GOOGLE_CHARTS
174
-{
175
-    // todo: Finish
176
-    public function __construct($Base, $Width, $Height, $Options = [])
177
-    {
178
-        parent::__construct('lc', $Width, $Height, $Options);
179
-    }
180
-
181
-    public function color($Color)
182
-    {
183
-        $this->URL .= '&chco='.$Color.'&chm=B,'.$Color.'50,0,0,0';
184
-    }
185
-
186
-    public function generate()
187
-    {
188
-        $Max = max($this->Data);
189
-        $Min = ((isset($this->Options['Break'])) ? $Min = min($this->Data) : 0);
190
-        $Data = [];
191
-
192
-        foreach ($this->Data as $Value) {
193
-            $Data[] = $this->encode((($Value - $Min) / ($Max - $Min)) * 4095);
194
-        }
195
-        $this->URL .= "&chxt=y,x&chxs=0,h&chxl=1:|".implode('|', $this->Labels).'&chxr=0,'.$Min.','.($Max-$Min).'&chd=e:'.implode('', $Data);
196
-    }
197
-}
198
-*/
199
-
200
-/*
201
-class POLL_GRAPH extends GOOGLE_CHARTS
202
-{
203
-    public function __construct()
204
-    {
205
-        $this->URL .= '?cht=bhg';
206
-    }
207
-
208
-    public function add($Label, $Data)
209
-    {
210
-        if ($Label !== false) {
211
-            $this->Labels[] = Format::cut_string($Label, 35);
212
-        }
213
-        $this->Data[] = $Data;
214
-    }
215
-
216
-    public function generate()
217
-    {
218
-        $Count = count($this->Data);
219
-        $Height = (30 * $Count) + 20;
220
-        $Max = max($this->Data);
221
-        $Sum = array_sum($this->Data);
222
-        $Increment = ($Max / $Sum) * 25; // * 100% / 4divisions
223
-        $Data = [];
224
-        $Labels = [];
225
-
226
-        foreach ($this->Data as $Key => $Value) {
227
-            $Data[] = $this->encode(($Value / $Max) * 4095);
228
-            $Labels[] = '@t'.str_replace(array(' ', ','), array('+', '\,'), $this->Labels[$Key]).',000000,1,'.round((($Key + 1) / $Count) - (12 / $Height), 2).':0,12';
229
-        }
230
-        $this->URL .= "&chbh=25,0,5&chs=214x$Height&chl=0%|".round($Increment, 1)."%|".round($Increment * 2, 1)."%|".round($Increment * 3, 1)."%|".round($Increment * 4, 1)."%&chm=".implode('|', $Labels).'&chd=e:'.implode('', $Data);
231
-    }
232
-}
233
-*/

+ 32
- 24
classes/config.template.php View File

@@ -1,3 +1,4 @@
1
+
1 2
 <?php
2 3
 declare(strict_types=1);
3 4
 
@@ -19,7 +20,7 @@ declare(strict_types=1);
19 20
  *   $LongArray = [];
20 21
  *   ENV::setPub(
21 22
  *     'CONFIG',
22
- *     new RecursiveArrayObject($LongArray)
23
+ *     $ENV->convert($LongArray)
23 24
  *   );
24 25
  *
25 26
  *   $ENV = ENV::go();
@@ -32,9 +33,8 @@ declare(strict_types=1);
32 33
  *
33 34
  *   var_dump(
34 35
  *     $ENV->dedupe(
35
- *       $ENV->CATS->SEQ->Platforms,
36
- *       $ENV->CATS->IMG->Platforms->toArray(),
37
- *       [$MapVectorFormats, $MapRasterFormats, $PlainFormats]
36
+ *       $ENV->META->Formats->Sequences,
37
+ *       $ENV->META->Formats->Proteins->toArray()
38 38
  *     )
39 39
  *   );
40 40
  */
@@ -42,6 +42,8 @@ declare(strict_types=1);
42 42
 # Initialize
43 43
 require_once 'env.class.php';
44 44
 $ENV = ENV::go();
45
+
46
+# Basic info
45 47
 ENV::setPub('PHP_MIN', '7.4.0');
46 48
 ENV::setPub('DEV', true);
47 49
 
@@ -132,7 +134,7 @@ $TechSupport = [
132 134
 ];
133 135
 ENV::setPub(
134 136
     'HELP',
135
-    new RecursiveArrayObject($TechSupport)
137
+    $ENV->convert($TechSupport)
136 138
 );
137 139
 
138 140
 
@@ -180,9 +182,9 @@ ENV::setPriv('SQLPORT', 3306);
180 182
 #ENV::setPriv('SQLSOCK', '/var/run/mysqld/mysqld.sock');
181 183
 
182 184
 # TLS client certs
183
-ENV::setPriv('SQL_CERT', "/var/www/sql-keys/client-cert.pem");
184
-ENV::setPriv('SQL_KEY', "/var/www/sql-keys/client-key.pem");
185
-ENV::setPriv('SQL_CA', "/var/www/sql-keys/ca.pem");
185
+ENV::setPriv('SQL_CERT', "$ENV->WEB_ROOT/sql-keys/client-cert.pem");
186
+ENV::setPriv('SQL_KEY', "$ENV->WEB_ROOT/sql-keys/client-key.pem");
187
+ENV::setPriv('SQL_CA', "$ENV->WEB_ROOT/sql-keys/ca.pem");
186 188
 
187 189
  # Production
188 190
  if (!$ENV->DEV) {
@@ -251,7 +253,7 @@ if (!$ENV->DEV) {
251 253
     ];
252 254
     ENV::setPub(
253 255
         'ANNOUNCE_URLS',
254
-        new RecursiveArrayObject($AnnounceURLs)
256
+        $ENV->convert($AnnounceURLs)
255 257
     );
256 258
 }
257 259
 
@@ -270,7 +272,7 @@ else {
270 272
     ];
271 273
     ENV::setPub(
272 274
         'ANNOUNCE_URLS',
273
-        new RecursiveArrayObject($AnnounceURLs)
275
+        $ENV->convert($AnnounceURLs)
274 276
     );
275 277
 }
276 278
 
@@ -378,6 +380,10 @@ ENV::setPub('FEATURE_SEND_EMAIL', true);
378 380
 # (should only be used for initial setup)
379 381
 ENV::setPub('FEATURE_SET_ENC_KEY_PUBLIC', false);
380 382
 
383
+# Attempt to support the Seqhash algorithm
384
+# https://blog.libredna.org/post/seqhash/
385
+ENV::setPub('FEATURE_SEQHASH', true);
386
+
381 387
 
382 388
 /**
383 389
  * Settings
@@ -592,7 +598,7 @@ $AutomatedBadgeIDs = [
592 598
 ];
593 599
 ENV::setPub(
594 600
     'AUTOMATED_BADGE_IDS',
595
-    new RecursiveArrayObject($AutomatedBadgeIDs)
601
+    $ENV->convert($AutomatedBadgeIDs)
596 602
 );
597 603
 
598 604
 
@@ -822,11 +828,13 @@ $DB = [
822 828
     'picture' => ['name' => 'Picture', 'desc' => 'A meaningful picture, e.g., the specimen or a thumbnail'],
823 829
 
824 830
     # From the non-renamed `torrents` table
825
-    'version' => ['name' => 'Version', 'desc' => 'Start with 0.1.0', 'note' => 'Please see <a href="https://semver.org target=" _blank">Semantic Versioning</a>'],
831
+    'version' => ['name' => 'Version', 'desc' => 'Start with 0.1.0', 'note' => 'Please see <a href="https://semver.org" target="_blank">Semantic Versioning</a>'],
832
+    'license' => ['name' => 'License', 'desc' => '', 'note' => 'Please see <a href="http://www.dcc.ac.uk/resources/how-guides/license-research-data" target="_blank">How to License Research Data</a>'],
833
+    'mirrors' => ['name' => 'Mirrors', 'desc' => 'Up to two FTP/HTTP addresses that either point directly to a file, or for multi-file torrents, to the enclosing folder'],
826 834
 ];
827 835
 ENV::setPub(
828 836
     'DB',
829
-    new RecursiveArrayObject($DB)
837
+    $ENV->convert($DB)
830 838
 );
831 839
 
832 840
 
@@ -947,7 +955,7 @@ $META = [
947 955
             'Binary',
948 956
             'Text',
949 957
         ],
950
-    ], # End $this->META->Platforms
958
+    ], # End $ENV->META->Platforms
951 959
 
952 960
     /**
953 961
      * 1.
@@ -1172,7 +1180,7 @@ $META = [
1172 1180
             'Jupyter'      => ['ipynb'],
1173 1181
             'Ontology'     => ['cgif', 'cl', 'clif', 'csv', 'htm', 'html', 'kif', 'obo', 'owl', 'rdf', 'rdfa', 'rdfs', 'rif', 'tsv', 'xcl', 'xht', 'xhtml', 'xml'],
1174 1182
         ],
1175
-    ], # End $this->META->Formats
1183
+    ], # End $ENV->META->Formats
1176 1184
 
1177 1185
 
1178 1186
     /**
@@ -1268,7 +1276,7 @@ $META = [
1268 1276
             'Velocity',
1269 1277
             'Weight',
1270 1278
         ],
1271
-    ], # End $this->META->Scopes
1279
+    ], # End $ENV->META->Scopes
1272 1280
 
1273 1281
     /**
1274 1282
      * 1.
@@ -1294,11 +1302,11 @@ $META = [
1294 1302
         'OpenMTA',
1295 1303
         'Public Domain',
1296 1304
         'Unspecified',
1297
-    ], # End $this->META->Licenses
1305
+    ], # End $ENV->META->Licenses
1298 1306
 ];
1299 1307
 ENV::setPub(
1300 1308
     'META',
1301
-    new RecursiveArrayObject($META)
1309
+    $ENV->convert($META)
1302 1310
 );
1303 1311
 
1304 1312
 
@@ -1322,16 +1330,16 @@ $CATS = [
1322 1330
         'Icon' => "$CatIcons/sequences.png",
1323 1331
         'Description' => "For data that's ACGT, ACGU, amino acid letters on disk.",
1324 1332
         'Platforms' => $ENV->META->Platforms->Sequences,
1325
-        'Formats' => [
1326
-            $ENV->META->Formats->Sequences,
1327
-            $ENV->META->Formats->Proteins,
1328
-            $ENV->META->Formats->Plain,
1333
+        'Formats' => array_merge([
1329 1334
             /*
1330 1335
             'Sequences' => $ENV->META->Formats->Sequences,
1331 1336
             'Proteins' => $ENV->META->Formats->Proteins,
1332 1337
             'Plain' => $ENV->META->Formats->Plain,
1333 1338
             */
1334
-        ],
1339
+            $ENV->META->Formats->Sequences,
1340
+            $ENV->META->Formats->Proteins,
1341
+            $ENV->META->Formats->Plain,
1342
+        ]),
1335 1343
     ],
1336 1344
 
1337 1345
     2 => [
@@ -1481,7 +1489,7 @@ $CATS = [
1481 1489
 ];
1482 1490
 ENV::setPub(
1483 1491
     'CATS',
1484
-    new RecursiveArrayObject($CATS)
1492
+    $ENV->convert($CATS)
1485 1493
 );
1486 1494
 
1487 1495
 

+ 44
- 39
classes/env.class.php View File

@@ -43,19 +43,13 @@ class ENV
43 43
     # Prevents multiple instances
44 44
     public function __clone()
45 45
     {
46
-        return trigger_error(
47
-            'clone() not allowed',
48
-            E_USER_ERROR
49
-        );
46
+        return error('clone() not allowed.');
50 47
     }
51 48
 
52 49
     # Prevents unserializing
53 50
     public function __wakeup()
54 51
     {
55
-        return trigger_error(
56
-            'wakeup() not allowed',
57
-            E_USER_ERROR
58
-        );
52
+        return error('wakeup() not allowed.');
59 53
     }
60 54
 
61 55
     # $this->key returns public->key
@@ -112,11 +106,40 @@ class ENV
112 106
     }
113 107
 
114 108
 
109
+    /**
110
+     * convert
111
+     *
112
+     * Take a mixed input and returns a RecursiveArrayObject.
113
+     * This function is the sausage grinder, so to speak.
114
+     */
115
+    public function convert($obj)
116
+    {
117
+        switch (gettype($obj)) {
118
+            case 'string':
119
+                $out = json_decode($obj, true);
120
+                return (json_last_error() === JSON_ERROR_NONE)
121
+                    ? new RecursiveArrayObject($out)
122
+                    : error('json_last_error_msg(): ' . json_last_error_msg());
123
+                break;
124
+            
125
+            case 'array':
126
+            case 'object':
127
+                return new RecursiveArrayObject($obj);
128
+            
129
+            default:
130
+                return error('$ENV->convert() expects a JSON string, array, or object.');
131
+                break;
132
+        }
133
+    }
134
+
135
+
115 136
     /**
116 137
      * toArray
138
+     *
139
+     * Takes an object and returns an array.
117 140
      * @see https://ben.lobaugh.net/blog/567/php-recursively-convert-an-object-to-an-array
118 141
      */
119
-    public function toArray($obj)
142
+    public function toArray(object $obj)
120 143
     {
121 144
         if (is_object($obj)) {
122 145
             $obj = (array) $obj;
@@ -135,30 +158,6 @@ class ENV
135 158
     }
136 159
 
137 160
 
138
-    /**
139
-     * fromJson
140
-     *
141
-     * @param string $JSON Valid JavaScript object string
142
-     * @return RecursiveArrayObject Not stdClass as in json_decode()
143
-     */
144
-
145
-    public function fromJson($str)
146
-    {
147
-        if (!is_string($str) || is_empty($str)) {
148
-            error('$ENV->fromJson() expects a string.');
149
-        }
150
-
151
-        $json = json_decode($str, true);
152
-        
153
-        if (json_last_error() === JSON_ERROR_NONE) {
154
-            # Decode to array and construct RAO
155
-            return new RecursiveArrayObject($json);
156
-        } else {
157
-            error('Unable to parse JSON in $ENV->fromJson().');
158
-        }
159
-    }
160
-
161
-
162 161
     /**
163 162
      * dedupe
164 163
      *
@@ -181,18 +180,24 @@ class ENV
181 180
     /**
182 181
      * flatten
183 182
      *
184
-     * Takes an $ENV node (Recursive ArrayObject)
183
+     * Takes an $ENV node or array of them
185 184
      * and flattens out the multi-dimensionality.
186
-     * Returns a single, non-deduplicated array.
185
+     * It returns a flat array with keys intact.
187 186
      */
188
-    public function flatten($arr)
187
+    public function flatten($arr, int $lvl = null)
189 188
     {
189
+        if (!is_array($arr) && !is_object($arr)) {
190
+            return error('$ENV->flatten() expects an array or object, got ' . gettype($arr));
191
+        }
192
+
190 193
         $new = array();
191 194
 
192 195
         foreach ($arr as $k => $v) {
193
-            if (is_object($v)) {
196
+            /*
197
+             if (is_object($v)) {
194 198
                 $v = $this->toArray($v);
195 199
             }
200
+            */
196 201
     
197 202
             if (is_array($v)) {
198 203
                 $new = array_merge($new, $this->flatten($v));
@@ -212,7 +217,7 @@ class ENV
212 217
      * Maps a callback (or default) to an object.
213 218
      *
214 219
      * Example output:
215
-     * $Hashes = $ENV->map('md5', $ENV->CATS->SEQ);
220
+     * $Hashes = $ENV->map('md5', $ENV->CATS->{6});
216 221
      *
217 222
      * var_dump($Hashes);
218 223
      * object(RecursiveArrayObject)#324 (1) {
@@ -240,7 +245,7 @@ class ENV
240 245
      * @param object $obj Object to operate on
241 246
      * @return object $RAO Mapped RecursiveArrayObject
242 247
      */
243
-    public function map($fn = '', $obj = null)
248
+    public function map(string $fn = '', object $obj = null)
244 249
     {
245 250
         # Set a default function if desired
246 251
         if (empty($fn) && !is_object($fn)) {

+ 0
- 44
classes/input.class.php View File

@@ -1,44 +0,0 @@
1
-<?php
2
-declare(strict_types=1);
3
-
4
-/**
5
- * Input class
6
- * 
7
- * An attempt to normalize and secure form inputs.
8
- */
9
-
10
-class Input {
11
-
12
-    /**
13
-     * passphrase
14
-     */
15
-    function passphrase(
16
-        string $Name = 'password',
17
-        string $ID = 'password',
18
-        string $Placeholder = 'Passphrase',
19
-        bool $Advice = false) {
20
-        $ENV = ENV::go();
21
-
22
-        # Input validation
23
-        if (!is_string($Name) || empty($Name)) {
24
-            error("Expected non-empty string, got \$Name = $Name in Input::passphrase.");
25
-        }
26
-
27
-        if (!empty($Advice) && $Advice !== true || $Advice !== false) {
28
-            error("Expected true|false, got \$Advice = $Advice in Input::passphrase.");
29
-        }
30
-
31
-        $Field = <<<HTML
32
-        <input type="password" name="$Name" id="$ID" placeholder="$Placeholder"
33
-          minlength="$ENV->PW_MIN" maxlength="$ENV->PW_MAX"
34
-          class="inputtext" autocomplete="off" required="required" />
35
-HTML;
36
-
37
-if ($Advice) {
38
-    return $Field . $ENV->PW_ADVICE;
39
-} else {
40
-    return $Field;
41
-}
42
-
43
-    }
44
-}

+ 21
- 6
classes/security.class.php View File

@@ -39,23 +39,38 @@ class Security
39 39
     {
40 40
         $ENV = ENV::go();
41 41
 
42
+        # Bad PHP version
43
+        if (version_compare(PHP_VERSION, $ENV->PHP_MIN, '<')) {
44
+            error("Gazelle requires PHP > $ENV->PHP_MIN.");
45
+        }
46
+
42 47
         # short_open_tag
43 48
         if (!ini_get('short_open_tag')) {
44
-            error('short_open_tag != On in php.ini');
49
+            error('short_open_tag != On in php.ini.');
45 50
         }
46 51
 
47 52
         # apcu
48 53
         if (!extension_loaded('apcu')) {
49
-            error('APCu extension not loaded');
54
+            error('APCu extension php-apcu not loaded.');
50 55
         }
51 56
 
52
-        # Bad PHP version
53
-        if (version_compare($ENV->PHP_MIN, '7.4.0', '<')) {
54
-            error("Gazelle requires PHP > $ENV->PHP_MIN.");
57
+        # mbstring
58
+        if (!extension_loaded('mbstring')) {
59
+            error('Multibyte string extension php-mbstring not loaded.');
60
+        }
61
+
62
+        # memcache
63
+        if (!extension_loaded('memcache')) {
64
+            error('memcached extension php-memcache not loaded.');
65
+        }
66
+
67
+        # blake3
68
+        if ($ENV->FEATURE_SEQHASH && !extension_loaded('blake3')) {
69
+            error('Please install and enable the php-blake3 extension.');
55 70
         }
56 71
 
57 72
         # Deal with dumbasses
58
-        if (isset($_REQUEST['info_hash']) && isset($_REQUEST['peer_id'])) {
73
+        if (isset($_REQUEST['info_hash']) || isset($_REQUEST['peer_id'])) {
59 74
             error(
60 75
                 'd14:failure reason40:Invalid .torrent, try downloading again.e',
61 76
                 $NoHTML = true,

+ 24
- 46
classes/torrent_form.class.php View File

@@ -444,7 +444,7 @@ HTML;
444 444
         $Version = display_str($Torrent['Version']);
445 445
 
446 446
         echo $Twig->render(
447
-            'torrent_form/identifier.html',
447
+            'torrent_form/version.html',
448 448
             [
449 449
               'db' => $ENV->DB->version,
450 450
               'version' => $Version,
@@ -583,7 +583,7 @@ HTML;
583 583
             'torrent_form/year.html',
584 584
             [
585 585
             'db' => $ENV->DB->year,
586
-            'location' => $TorrentYear,
586
+            'year' => $TorrentYear,
587 587
           ]
588 588
         );
589 589
 
@@ -699,7 +699,7 @@ HTML;
699 699
                 $trID = 'media_tr',
700 700
                 $Label = 'Platform',
701 701
                 $Torrent = $Torrent,
702
-                $Media = $ENV->META->Platforms->Sequences
702
+                $Media = $ENV->CATS->{1}->Platforms
703 703
             );
704 704
             
705 705
 
@@ -710,10 +710,7 @@ HTML;
710 710
                 $trID = 'media_graphs_tr',
711 711
                 $Label = 'Platform',
712 712
                 $Torrent = $Torrent,
713
-                $Media = $ENV->flatten([
714
-                    $ENV->META->Platforms->Graphs,
715
-                    $ENV->META->Platforms->Sequences
716
-                ])
713
+                $Media = $ENV->CATS->{2}->Platforms
717 714
             );
718 715
             
719 716
 
@@ -724,10 +721,7 @@ HTML;
724 721
                 $trID = 'media_scalars_vectors_tr',
725 722
                 $Label = 'Platform',
726 723
                 $Torrent = $Torrent,
727
-                $Media = $ENV->flatten([
728
-                    $ENV->META->Platforms->Graphs,
729
-                    $ENV->META->Platforms->Images
730
-                ])
724
+                $Media = $ENV->CATS->{5}->Platforms
731 725
             );
732 726
 
733 727
 
@@ -738,7 +732,7 @@ HTML;
738 732
                 $trID = 'media_images_tr',
739 733
                 $Label = 'Platform',
740 734
                 $Torrent = $Torrent,
741
-                $Media = $ENV->META->Platforms->Images
735
+                $Media = $ENV->CATS->{8}->Platforms
742 736
             );
743 737
 
744 738
 
@@ -749,7 +743,7 @@ HTML;
749 743
                 $trID = 'media_documents_tr',
750 744
                 $Label = 'Platform',
751 745
                 $Torrent = $Torrent,
752
-                $Media = $ENV->META->Platforms->Documents
746
+                $Media = $ENV->CATS->{11}->Platforms
753 747
             );
754 748
 
755 749
 
@@ -760,7 +754,7 @@ HTML;
760 754
                 $trID = 'media_machine_data_tr',
761 755
                 $Label = 'Platform',
762 756
                 $Torrent = $Torrent,
763
-                $Media = $ENV->META->Platforms->Raw
757
+                $Media = $ENV->CATS->{12}->Platforms
764 758
             );
765 759
         } # fi NewTorrent
766 760
         else {
@@ -826,7 +820,7 @@ HTML;
826 820
             $trID = 'container_tr',
827 821
             $Label = 'Format',
828 822
             $Torrent = $Torrent,
829
-            $FileTypes = array_merge(($ENV->CATS->{1}->Formats))
823
+            $FileTypes = $ENV->CATS->{1}->Formats
830 824
         );
831 825
         
832 826
 
@@ -837,7 +831,7 @@ HTML;
837 831
             $trID = 'container_graphs_tr',
838 832
             $Label = 'Format',
839 833
             $Torrent = $Torrent,
840
-            $FileTypes = array_merge($this->GraphXmlFormats, $this->GraphTxtFormats, $this->SeqFormats, $this->ProtFormats, $this->PlainFormats)
834
+            #$FileTypes = array_column($ENV->META, $Formats)
841 835
         );
842 836
 
843 837
 
@@ -1030,21 +1024,13 @@ HTML;
1030 1024
             $TorrentImage = display_str($Torrent['Image']);
1031 1025
             $Disabled = $this->Disabled;
1032 1026
 
1033
-            echo <<<HTML
1034
-            <tr id="cover_tr">
1035
-            <td>
1036
-              <label for="image">
1037
-                Picture
1038
-              </label>
1039
-            </td>
1040
-            
1041
-            <td>
1042
-              <input type="text" id="image" name="image" size="60"
1043
-                placeholder="A meaningful picture, e.g., the specimen or a thumbnail"
1044
-                value="$TorrentImage" $Disabled? />
1045
-            </td>
1046
-          </tr>
1047
-HTML;
1027
+            echo $Twig->render(
1028
+                'torrent_form/picture.html',
1029
+                [
1030
+                    'db' => $ENV->DB->picture,
1031
+                    'picture' => $TorrentImage,
1032
+                ]
1033
+            );
1048 1034
         }
1049 1035
 
1050 1036
 
@@ -1056,21 +1042,13 @@ HTML;
1056 1042
          */
1057 1043
         if (!$this->DisabledFlag && $this->NewTorrent) {
1058 1044
             $TorrentMirrors = display_str($Torrent['Mirrors']);
1059
-            echo <<<HTML
1060
-            <tr id="mirrors_tr">
1061
-              <td>
1062
-                <label for="mirrors">
1063
-                  Mirrors
1064
-                </label>
1065
-              </td>
1066
-              
1067
-              <td>
1068
-                <!-- Needs to be all on one line -->
1069
-                <textarea rows="2" name="mirrors" id="mirrors"
1070
-                  placeholder="Up to two FTP/HTTP addresses that either point directly to a file, or for multi-file torrents, to the enclosing folder">$TorrentMirrors</textarea>
1071
-              </td>
1072
-            </tr>
1073
-HTML;
1045
+            echo $Twig->render(
1046
+                'torrent_form/mirrors.html',
1047
+                [
1048
+                  'db' => $ENV->DB->mirrors,
1049
+                  'mirrors' => $TorrentMirrors,
1050
+              ]
1051
+            );
1074 1052
         }
1075 1053
 
1076 1054
 

+ 5
- 2
classes/twig.class.php View File

@@ -60,13 +60,16 @@ class Twig
60 60
     private static function factory(): \Twig\Environment
61 61
     {
62 62
         $ENV = ENV::go();
63
+
64
+        # https://twig.symfony.com/doc/3.x/api.html
63 65
         $Twig = new \Twig\Environment(
64 66
             new \Twig\Loader\FilesystemLoader("$ENV->SERVER_ROOT/templates"),
65 67
             [
66 68
                 'auto_reload' => true,
67
-                'autoescape' => 'html',
68
-                'cache' => "$ENV->WEB_ROOT/cache/twig"
69
+                'autoescape' => 'name',
70
+                'cache' => "$ENV->WEB_ROOT/cache/twig",
69 71
                 #'debug' => DEBUG_MODE,
72
+                'strict_variables' => true,
70 73
         ]
71 74
         );
72 75
 

+ 39
- 20
readme.md View File

@@ -1,9 +1,11 @@
1 1
 # BioTorrents.de Gazelle
2 2
 
3 3
 This software is twice removed from the original
4
-[What.cd Gazelle](https://github.com/WhatCD/Gazelle):
5
-it's based on the security hardened PHP7 fork
4
+[What.cd Gazelle](https://github.com/WhatCD/Gazelle).
5
+It's based on the security hardened PHP7 fork
6 6
 [Oppaitime Gazelle](https://git.oppaiti.me/Oppaitime/Gazelle).
7
+It shares several features with
8
+[Orpheus Gazelle](https://github.com/OPSnet/Gazelle).
7 9
 The goal is to organize a functional database with pleasant interfaces,
8 10
 and render insightful views using data from robust external sources.
9 11
 
@@ -23,6 +25,7 @@ BioTorrents.de supports an array of
23 25
 with the appropriate bold/italic glyphs and monospace.
24 26
 These options are available to every theme.
25 27
 Font Awesome 5 is also universally available.
28
+[Download the fonts](https://docs.biotorrents.de/dl/fonts.tgz).
26 29
 
27 30
 ## Markdown support
28 31
 
@@ -36,30 +39,46 @@ Support for the default Gazelle recursive regex BBcode parser.
36 39
 ## $ENV recursive singleton
37 40
 
38 41
 [The site configuration](classes/config.template.php)
39
-is being migrated to a format govered by
40
-[the ENV special class](classes/env.class.php)
41
-for modified ArrayObjects.
42
-This is useful for several reasons:
43
-
44
-- prevents multiple configs loaded in memory;
45
-- ensures the config's immutability;
46
-- doesn't pollute the constants table;
47
-- allows public (echoed) and private (accessed) values;
48
-- supports large, nested static metadata structures;
49
-- able to scope access to the function level;
50
-- easy to extend ENV with new class methods;
51
-- good interoperability potential with JSON; and
52
-- native PHP ArrayObject support with Array compatibility.
42
+is being migrated to a format govered by the
43
+[ENV special class](classes/env.class.php)
44
+for modified recursive ArrayObjects.
53 45
 
54 46
 ## Twig template system
55 47
 
56
-Similar to ENV,
57
-[the Twig interface](classes/twig.class.php)
48
+Similar to ENV, the
49
+[Twig interface](classes/twig.class.php)
58 50
 operates as a singleton because it's an external module with its own cache.
59 51
 Twig provides a security benefit by escaping rendered output,
60 52
 and a secondary benefit of clarifying the PHP running the site sections.
61
-Several custom filters are available from
62
-[Orpheus Gazelle](https://github.com/OPSnet/Gazelle).
53
+Several custom filters are available from OPS.
54
+
55
+## Active data minimization
56
+
57
+BioTorrents.de has
58
+[real lawyer-vetted policies](templates/legal).
59
+In the process of matching the tech to the legal word,
60
+we dropped support for a number of compromising features:
61
+
62
+- Bitcoin, PayPal, and currency exchange API and system calls;
63
+- Bitcoin addresses, user donation history, and similar metadata; and
64
+- IP address and geolocation, email address, passphrase, and passkey history.
65
+
66
+Besides that, BioTorrents has several passive developments in progress:
67
+
68
+- prepare all queries with parameterized statements;
69
+- declare strict mode at the top of every PHP and JS file;
70
+- check strict equality and strong typing, including function arguments;
71
+- run all files through generic formatters such as PHP-CS-Fixer; and
72
+- move all external libraries to uncomplicated package management.
73
+
74
+## Minor changes
75
+
76
+- Database crypto bumped up to AES-256
77
+- Good subresource integrity support
78
+- Configurable HTTP status code errors
79
+- Integrated diceware passphrase generator
80
+- TLS database connections
81
+- Semantic HTML5 themes (WIP)
63 82
 
64 83
 # Changelog: WCD → OT
65 84
 

+ 14
- 3
sections/login/login.php View File

@@ -1,6 +1,9 @@
1 1
 <?php
2 2
 declare(strict_types=1);
3 3
 
4
+$ENV = ENV::go();
5
+$Twig = Twig::go();
6
+
4 7
 View::show_header('Login'); ?>
5 8
 
6 9
 <p class="center mouseless">
@@ -47,7 +50,15 @@ if (!$Banned) { ?>
47 50
 
48 51
     <tr>
49 52
       <td>
50
-        <?= Input::passphrase() ?>
53
+        <?=
54
+        $Twig->render('input/passphrase.html', [
55
+          'name' => 'password',
56
+          'id' => 'password',
57
+          'placeholder' => 'Passphrase',
58
+          'pw_min' => $ENV->PW_MIN,
59
+          'pw_max' => $ENV->PW_MAX,
60
+          'advice' => false,
61
+        ]) ?>
51 62
       </td>
52 63
 
53 64
       <td>
@@ -76,8 +87,8 @@ else { ?>
76 87
 
77 88
 if ($Attempts > 0) { ?>
78 89
 <p class="center">
79
-  Forgot your password?
80
-  <a href="login.php?act=recover" class="tooltip" title="Recover your password">Reset it here!</a>
90
+  Forgot your passphrase?
91
+  <a href="login.php?act=recover" class="tooltip" title="Recover your passphrase">Reset it here!</a>
81 92
 </p>
82 93
 
83 94
 <?php

+ 14
- 11
sections/login/recover_step2.php View File

@@ -2,8 +2,9 @@
2 2
 declare(strict_types=1);
3 3
 
4 4
 $ENV = ENV::go();
5
-View::show_header('Recover Password', 'validate,password_validate');
5
+$Twig = Twig::go();
6 6
 
7
+View::show_header('Recover Password', 'validate,password_validate');
7 8
 echo '<h2>Reset your password</h2>';
8 9
 
9 10
 if (empty($PassWasReset)) {
@@ -22,11 +23,12 @@ if (empty($PassWasReset)) {
22 23
       </td>
23 24
 
24 25
       <td>
25
-        <?= Input::passphrase($Name = 'password', $ID='new_pass_1', $Placeholder = 'New passphrase') ?>
26
-        <!--
27
-        <input type="password" minlength="15" name="password" id="new_pass_1" class="inputtext" size="40"
28
-          placeholder="New Password" pattern=".{15,307200}" required style="width: 250px !important;">
29
-        -->
26
+        <?=
27
+        $Twig->render('input/passphrase.html', [
28
+          'name' => 'password',
29
+          'id' => 'new_pass_1',
30
+          'placeholder' => 'New passphrase'
31
+        ]) ?>
30 32
       </td>
31 33
     </tr>
32 34
 
@@ -35,11 +37,12 @@ if (empty($PassWasReset)) {
35 37
         <strong id="pass_match"></strong>
36 38
       </td>
37 39
       <td>
38
-        <?= Input::passphrase($Name = 'verifypassword', $ID='new_pass_2', $Placeholder = 'Confirm passphrase') ?>
39
-        <!--
40
-        <input type="password" minlength="15" name="verifypassword" id="new_pass_2" class="inputtext" size="40"
41
-          placeholder="Confirm Password" pattern=".{15,307200}" required style="width: 250px !important;">
42
-        -->
40
+      <?=
41
+        $Twig->render('input/passphrase.html', [
42
+          'name' => 'verifypassword',
43
+          'id' => 'new_pass_2',
44
+          'placeholder' => 'Confirm passphrase'
45
+        ]) ?>
43 46
       </td>
44 47
     </tr>
45 48
 

+ 24
- 0
templates/input/passphrase.html View File

@@ -0,0 +1,24 @@
1
+<input
2
+  type="password"
3
+  name="{{ name|default('password') }}"
4
+  id="{{ id|default('password') }}"
5
+  placeholder="{{ placeholder|default('passphrase') }}"
6
+  minlength="{{ pw_min|default('15') }}"
7
+  maxlength="{{ pw_max|default('10000') }}"
8
+  pattern=".{ {{ pw_min }},{{ pw_max }} }"
9
+  class="inputtext"
10
+  autocomplete="off"
11
+  required="required"
12
+/>
13
+
14
+<strong id="pass_match"></strong>
15
+
16
+{% if advice == true %}
17
+<p>
18
+  Any password {{ pw_min }} characters or longer is accepted, but a strong password
19
+  <ul>
20
+    <li>is a pass<em>phrase</em> of mixed case with many small words,</li>
21
+    <li>that contains complex characters including Unicode and emoji.</li>
22
+  </ul>
23
+</p>
24
+{% endif %}

+ 14
- 0
templates/torrent_form/mirrors.html View File

@@ -0,0 +1,14 @@
1
+<tr id="mirrors_tr">
2
+  <td>
3
+    <label for="mirrors">{{ db.name }}</label>
4
+  </td>
5
+
6
+  <td>
7
+    <textarea
8
+      rows="2"
9
+      name="mirrors"
10
+      id="mirrors"
11
+      {# Needs to be all on one line #}
12
+      placeholder="{{ db.desc }}">{{ mirrors }}</textarea>
13
+  </td>
14
+</tr>

+ 16
- 0
templates/torrent_form/picture.html View File

@@ -0,0 +1,16 @@
1
+<tr id="cover_tr">
2
+  <td>
3
+    <label for="image">{{db.name }}</label>
4
+  </td>
5
+
6
+  <td>
7
+    <input
8
+      type="text"
9
+      id="image"
10
+      name="image"
11
+      size="60"
12
+      placeholder="{{ db.desc }}"
13
+      value="{{ picture }}"
14
+    />
15
+  </td>
16
+</tr>

+ 2
- 2
templates/torrent_form/version.html View File

@@ -10,10 +10,10 @@
10 10
       name="version"
11 11
       size="12"
12 12
       pattern="\d+\.*\d*\.*\d*"
13
-      placeholder="{{ db.desc}}"
13
+      placeholder="{{ db.desc }}"
14 14
       value="{{ version }}"
15 15
     />
16 16
 
17
-    <p>{{ note }}</p>
17
+    <p>{{ db.note|raw }}</p>
18 18
   </td>
19 19
 </tr>

Loading…
Cancel
Save