]> git.p6c8.net - jirafeau_project.git/blob - lib/functions.php
898b6fc41d8e0a3a38ac7205296fcb3385d63776
[jirafeau_project.git] / lib / functions.php
1 <?php
2 /*
3 * Jirafeau, your web file repository
4 * Copyright (C) 2008 Julien "axolotl" BERNARD <axolotl@magieeternelle.org>
5 * Copyright (C) 2012 Jerome Jutteau <j.jutteau@gmail.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /**
22 * Transform a string in a path by seperating each letters by a '/'.
23 * @return path finishing with a '/'
24 */
25 function
26 s2p ($s)
27 {
28 $p = '';
29 for ($i = 0; $i < strlen ($s); $i++)
30 $p .= $s{$i} . '/';
31 return $p;
32 }
33
34 /**
35 * Convert base 16 to base 64
36 * @returns A string based on 64 characters (0-9, a-z, A-Z, "-" and "_")
37 */
38 function
39 base_16_to_64 ($num)
40 {
41 $m = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
42 $hex2bin = ['0000', # 0
43 '0001', # 1
44 '0010', # 2
45 '0011', # 3
46 '0100', # 4
47 '0101', # 5
48 '0110', # 6
49 '0111', # 7
50 '1000', # 8
51 '1001', # 9
52 '1010', # a
53 '1011', # b
54 '1100', # c
55 '1101', # d
56 '1110', # e
57 '1111']; # f
58 $o = '';
59 $b = '';
60 $i = 0;
61 # Convert long hex string to bin.
62 $size = strlen ($num);
63 for ($i = 0; $i < $size; $i++)
64 $b .= $hex2bin{hexdec ($num{$i})};
65 # Convert long bin to base 64.
66 $size *= 4;
67 for ($i = $size - 6; $i >= 0; $i -= 6)
68 $o = $m{bindec (substr ($b, $i, 6))} . $o;
69 # Some few bits remaining ?
70 if ($i < 0 && $i > -6)
71 $o = $m{bindec (substr ($b, 0, $i + 6))} . $o;
72 return $o;
73 }
74
75 function
76 jirafeau_human_size ($octets)
77 {
78 $u = array ('B', 'KB', 'MB', 'GB', 'TB');
79 $o = max ($octets, 0);
80 $p = min (floor (($o ? log ($o) : 0) / log (1024)), count ($u) - 1);
81 $o /= pow (1024, $p);
82 return round ($o, 1) . $u[$p];
83 }
84
85 function
86 jirafeau_clean_rm_link ($link)
87 {
88 $p = s2p ("$link");
89 if (file_exists (VAR_LINKS . $p . $link))
90 unlink (VAR_LINKS . $p . $link);
91 $parse = VAR_LINKS . $p;
92 $scan = array();
93 while (file_exists ($parse)
94 && ($scan = scandir ($parse))
95 && count ($scan) == 2 // '.' and '..' folders => empty.
96 && basename ($parse) != basename (VAR_LINKS))
97 {
98 rmdir ($parse);
99 $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
100 }
101 }
102
103 function
104 jirafeau_clean_rm_file ($md5)
105 {
106 $p = s2p ("$md5");
107 if (file_exists (VAR_FILES . $p . $md5))
108 unlink (VAR_FILES . $p . $md5);
109 if (file_exists (VAR_FILES . $p . $md5 . '_count'))
110 unlink (VAR_FILES . $p . $md5 . '_count');
111 $parse = VAR_FILES . $p;
112 $scan = array();
113 while (file_exists ($parse)
114 && ($scan = scandir ($parse))
115 && count ($scan) == 2 // '.' and '..' folders => empty.
116 && basename ($parse) != basename (VAR_FILES))
117 {
118 rmdir ($parse);
119 $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
120 }
121 }
122
123 /**
124 * transforms a php.ini string representing a value in an integer
125 * @param $value the value from php.ini
126 * @returns an integer for this value
127 */
128 function jirafeau_ini_to_bytes ($value)
129 {
130 $modifier = substr ($value, -1);
131 $bytes = substr ($value, 0, -1);
132 switch (strtoupper ($modifier))
133 {
134 case 'P':
135 $bytes *= 1024;
136 case 'T':
137 $bytes *= 1024;
138 case 'G':
139 $bytes *= 1024;
140 case 'M':
141 $bytes *= 1024;
142 case 'K':
143 $bytes *= 1024;
144 default:
145 break;
146 }
147 return $bytes;
148 }
149
150 /**
151 * gets the maximum upload size according to php.ini
152 * @returns the maximum upload size string
153 */
154 function
155 jirafeau_get_max_upload_size ()
156 {
157 return jirafeau_human_size(
158 min (jirafeau_ini_to_bytes (ini_get ('post_max_size')),
159 jirafeau_ini_to_bytes (ini_get ('upload_max_filesize'))));
160 }
161
162 /**
163 * gets a string explaining the error
164 * @param $code the error code
165 * @returns a string explaining the error
166 */
167 function
168 jirafeau_upload_errstr ($code)
169 {
170 switch ($code)
171 {
172 case UPLOAD_ERR_INI_SIZE:
173 case UPLOAD_ERR_FORM_SIZE:
174 return t('Your file exceeds the maximum authorized file size. ');
175 break;
176
177 case UPLOAD_ERR_PARTIAL:
178 case UPLOAD_ERR_NO_FILE:
179 return
180 t
181 ('Your file was not uploaded correctly. You may succeed in retrying. ');
182 break;
183
184 case UPLOAD_ERR_NO_TMP_DIR:
185 case UPLOAD_ERR_CANT_WRITE:
186 case UPLOAD_ERR_EXTENSION:
187 return t('Internal error. You may not succeed in retrying. ');
188 break;
189
190 default:
191 break;
192 }
193 return t('Unknown error. ');
194 }
195
196 /** Remove link and it's file
197 * @param $link the link's name (hash)
198 */
199
200 function
201 jirafeau_delete_link ($link)
202 {
203 $l = jirafeau_get_link ($link);
204 if (!count ($l))
205 return;
206
207 jirafeau_clean_rm_link ($link);
208
209 $md5 = $l['md5'];
210 $p = s2p ("$md5");
211
212 $counter = 1;
213 if (file_exists (VAR_FILES . $p . $md5. '_count'))
214 {
215 $content = file (VAR_FILES . $p . $md5. '_count');
216 $counter = trim ($content[0]);
217 }
218 $counter--;
219
220 if ($counter >= 1)
221 {
222 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
223 fwrite ($handle, $counter);
224 fclose ($handle);
225 }
226
227 if ($counter == 0)
228 jirafeau_clean_rm_file ($md5);
229 }
230
231 /**
232 * Delete a file and it's links.
233 */
234 function
235 jirafeau_delete_file ($md5)
236 {
237 $count = 0;
238 /* Get all links files. */
239 $stack = array (VAR_LINKS);
240 while (($d = array_shift ($stack)) && $d != NULL)
241 {
242 $dir = scandir ($d);
243
244 foreach ($dir as $node)
245 {
246 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
247 preg_match ('/\.tmp/i', "$node"))
248 continue;
249
250 if (is_dir ($d . $node))
251 {
252 /* Push new found directory. */
253 $stack[] = $d . $node . '/';
254 }
255 elseif (is_file ($d . $node))
256 {
257 /* Read link informations. */
258 $l = jirafeau_get_link (basename ($node));
259 if (!count ($l))
260 continue;
261 if ($l['md5'] == $md5)
262 {
263 $count++;
264 jirafeau_delete_link ($node);
265 }
266 }
267 }
268 }
269 jirafeau_clean_rm_file ($md5);
270 return $count;
271 }
272
273 /**
274 * handles an uploaded file
275 * @param $file the file struct given by $_FILE[]
276 * @param $one_time_download is the file a one time download ?
277 * @param $key if not empty, protect the file with this key
278 * @param $time the time of validity of the file
279 * @param $ip uploader's ip
280 * @returns an array containing some information
281 * 'error' => information on possible errors
282 * 'link' => the link name of the uploaded file
283 * 'delete_link' => the link code to delete file
284 */
285 function
286 jirafeau_upload ($file, $one_time_download, $key, $time, $ip)
287 {
288 if (empty ($file['tmp_name']) || !is_uploaded_file ($file['tmp_name']))
289 {
290 return (array(
291 'error' =>
292 array ('has_error' => true,
293 'why' => jirafeau_upload_errstr ($file['error'])),
294 'link' => '',
295 'delete_link' => ''));
296 }
297
298 /* array representing no error */
299 $noerr = array ('has_error' => false, 'why' => '');
300
301 /* file informations */
302 $md5 = md5_file ($file['tmp_name']);
303 $name = trim ($file['name']);
304 $mime_type = $file['type'];
305 $size = $file['size'];
306
307 /* does file already exist ? */
308 $rc = false;
309 $p = s2p ("$md5");
310 if (file_exists (VAR_FILES . $p . $md5))
311 {
312 $rc = unlink ($file['tmp_name']);
313 }
314 elseif ((file_exists (VAR_FILES . $p) || @mkdir (VAR_FILES . $p, 0755, true))
315 && move_uploaded_file ($file['tmp_name'], VAR_FILES . $p . $md5))
316 {
317 $rc = true;
318 }
319 if (!$rc)
320 {
321 return (array(
322 'error' =>
323 array ('has_error' => true,
324 'why' => t('Internal error during file creation.')),
325 'link' =>'',
326 'delete_link' => ''));
327 }
328
329 /* increment or create count file */
330 $counter = 0;
331 if (file_exists (VAR_FILES . $p . $md5 . '_count'))
332 {
333 $content = file (VAR_FILES . $p . $md5. '_count');
334 $counter = trim ($content[0]);
335 }
336 $counter++;
337 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
338 fwrite ($handle, $counter);
339 fclose ($handle);
340
341 /* Create delete code. */
342 $delete_link_code = 0;
343 for ($i = 0; $i < 8; $i++)
344 $delete_link_code .= dechex (rand (0, 16));
345
346 /* md5 password or empty */
347 $password = '';
348 if (!empty ($key))
349 $password = md5 ($key);
350
351 /* create link file */
352 $link_tmp_name = VAR_LINKS . $md5 . rand (0, 10000) . ' .tmp';
353 $handle = fopen ($link_tmp_name, 'w');
354 fwrite ($handle,
355 $name . NL. $mime_type . NL. $size . NL. $password . NL. $time .
356 NL . $md5. NL . ($one_time_download ? 'O' : 'R') . NL.date ('U') .
357 NL. $ip . NL. $delete_link_code . NL);
358 fclose ($handle);
359 $md5_link = base_16_to_64 (md5_file ($link_tmp_name));
360 $l = s2p ("$md5_link");
361 if (!@mkdir (VAR_LINKS . $l, 0755, true) ||
362 !rename ($link_tmp_name, VAR_LINKS . $l . $md5_link))
363 {
364 if (file_exists ($link_tmp_name))
365 unlink ($link_tmp_name);
366
367 $counter--;
368 if ($counter >= 1)
369 {
370 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
371 fwrite ($handle, $counter);
372 fclose ($handle);
373 }
374 else
375 {
376 jirafeau_clean_rm_file ($md5_link);
377 }
378 return (array(
379 'error' =>
380 array ('has_error' => true,
381 'why' => t('Internal error during file creation. ')),
382 'link' =>'',
383 'delete_link' => ''));
384 }
385 return (array ('error' => $noerr,
386 'link' => $md5_link,
387 'delete_link' => $delete_link_code));
388 }
389
390 /**
391 * tells if a mime-type is viewable in a browser
392 * @param $mime the mime type
393 * @returns a boolean telling if a mime type is viewable
394 */
395 function
396 jirafeau_is_viewable ($mime)
397 {
398 if (!empty ($mime))
399 {
400 /* Actually, verify if mime-type is an image or a text. */
401 $viewable = array ('image', 'text');
402 $decomposed = explode ('/', $mime);
403 return in_array ($decomposed[0], $viewable);
404 }
405 return false;
406 }
407
408
409 // Error handling functions.
410 //! Global array that contains all registered errors.
411 $error_list = array ();
412
413 /**
414 * Adds an error to the list of errors.
415 * @param $title the error's title
416 * @param $description is a human-friendly description of the problem.
417 */
418 function
419 add_error ($title, $description)
420 {
421 global $error_list;
422 $error_list[] = '<p>' . $title. '<br />' . $description. '</p>';
423 }
424
425 /**
426 * Informs whether any error has been registered yet.
427 * @return true if there are errors.
428 */
429 function
430 has_error ()
431 {
432 global $error_list;
433 return !empty ($error_list);
434 }
435
436 /**
437 * Displays all the errors.
438 */
439 function
440 show_errors ()
441 {
442 if (has_error ())
443 {
444 global $error_list;
445 echo '<div class="error">';
446 foreach ($error_list as $error)
447 {
448 echo $error;
449 }
450 echo '</div>';
451 }
452 }
453
454 function check_errors ()
455 {
456 if (file_exists (JIRAFEAU_ROOT . 'install.php')
457 && !file_exists (JIRAFEAU_ROOT . 'lib/config.local.php'))
458 {
459 header('Location: install.php');
460 exit;
461 }
462
463 /* check if the destination dirs are writable */
464 $writable = is_writable (VAR_FILES) && is_writable (VAR_LINKS);
465
466 /* Checking for errors. */
467 if (!is_writable (VAR_FILES))
468 add_error (t('The file directory is not writable!'), VAR_FILES);
469
470 if (!is_writable (VAR_LINKS))
471 add_error (t('The link directory is not writable!'), VAR_LINKS);
472
473 /* Check if the install.php script is still in the directory. */
474 if (file_exists (JIRAFEAU_ROOT . 'install.php'))
475 add_error (t('Installer script still present'),
476 t('Please make sure to delete the installer script ' .
477 '"install.php" before continuing.'));
478 }
479
480 /**
481 * Read link informations
482 * @return array containing informations.
483 */
484 function
485 jirafeau_get_link ($hash)
486 {
487 $out = array ();
488 $link = VAR_LINKS . s2p ("$hash") . $hash;
489
490 if (!file_exists ($link))
491 return $out;
492
493 $c = file ($link);
494 $out['file_name'] = trim ($c[0]);
495 $out['mime_type'] = trim ($c[1]);
496 $out['file_size'] = trim ($c[2]);
497 $out['key'] = trim ($c[3], NL);
498 $out['time'] = trim ($c[4]);
499 $out['md5'] = trim ($c[5]);
500 $out['onetime'] = trim ($c[6]);
501 $out['upload_date'] = trim ($c[7]);
502 $out['ip'] = trim ($c[8]);
503 $out['link_code'] = trim ($c[9]);
504
505 return $out;
506 }
507
508 /**
509 * List files in admin interface.
510 */
511 function
512 jirafeau_admin_list ($name, $file_hash, $link_hash)
513 {
514 echo '<fieldset><legend>';
515 if (!empty ($name))
516 echo t('Filename') . ": $name ";
517 if (!empty ($file_hash))
518 echo t('file') . ": $file_hash ";
519 if (!empty ($link_hash))
520 echo t('link') . ": $link_hash ";
521 if (empty ($name) && empty ($file_hash) && empty ($link_hash))
522 echo t('List all files');
523 echo '</legend>';
524 echo '<table>';
525 echo '<tr>';
526 echo '<td>' . t('Filename') . '</td>';
527 echo '<td>' . t('Type') . '</td>';
528 echo '<td>' . t('Size') . '</td>';
529 echo '<td>' . t('Expire') . '</td>';
530 echo '<td>' . t('Onetime') . '</td>';
531 echo '<td>' . t('Upload date') . '</td>';
532 echo '<td>' . t('Origin') . '</td>';
533 echo '<td>' . t('Action') . '</td>';
534 echo '</tr>';
535
536 /* Get all links files. */
537 $stack = array (VAR_LINKS);
538 while (($d = array_shift ($stack)) && $d != NULL)
539 {
540 $dir = scandir ($d);
541 foreach ($dir as $node)
542 {
543 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
544 preg_match ('/\.tmp/i', "$node"))
545 continue;
546 if (is_dir ($d . $node))
547 {
548 /* Push new found directory. */
549 $stack[] = $d . $node . '/';
550 }
551 elseif (is_file ($d . $node))
552 {
553 /* Read link informations. */
554 $l = jirafeau_get_link ($node);
555 if (!count ($l))
556 continue;
557
558 /* Filter. */
559 if (!empty ($name) && !preg_match ("/$name/i", $l['file_name']))
560 continue;
561 if (!empty ($file_hash) && $file_hash != $l['md5'])
562 continue;
563 if (!empty ($link_hash) && $link_hash != $node)
564 continue;
565 /* Print link informations. */
566 echo '<tr>';
567 echo '<td>' .
568 '<form action = "admin.php" method = "post">' .
569 '<input type = "hidden" name = "action" value = "download"/>' .
570 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
571 '<input type = "submit" value = "' . $l['file_name'] . '" />' .
572 '</form>';
573 echo '</td>';
574 echo '<td>' . $l['mime_type'] . '</td>';
575 echo '<td>' . jirafeau_human_size ($l['file_size']) . '</td>';
576 echo '<td>' . ($l['time'] == -1 ? '' : strftime ('%c', $l['time'])) .
577 '</td>';
578 echo '<td>' . $l['onetime'] . '</td>';
579 echo '<td>' . strftime ('%c', $l['upload_date']) . '</td>';
580 echo '<td>' . $l['ip'] . '</td>';
581 echo '<td>' .
582 '<form action = "admin.php" method = "post">' .
583 '<input type = "hidden" name = "action" value = "delete_link"/>' .
584 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
585 '<input type = "submit" value = "' . t('Del link') . '" />' .
586 '</form>' .
587 '<form action = "admin.php" method = "post">' .
588 '<input type = "hidden" name = "action" value = "delete_file"/>' .
589 '<input type = "hidden" name = "md5" value = "' . $l['md5'] . '"/>' .
590 '<input type = "submit" value = "' . t('Del file and links') . '" />' .
591 '</form>' .
592 '</td>';
593 echo '</tr>';
594 }
595 }
596 }
597 echo '</table></fieldset>';
598 }
599
600 /**
601 * Clean expired files.
602 * @return number of cleaned files.
603 */
604 function
605 jirafeau_admin_clean ()
606 {
607 $count = 0;
608 /* Get all links files. */
609 $stack = array (VAR_LINKS);
610 while (($d = array_shift ($stack)) && $d != NULL)
611 {
612 $dir = scandir ($d);
613
614 foreach ($dir as $node)
615 {
616 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
617 preg_match ('/\.tmp/i', "$node"))
618 continue;
619
620 if (is_dir ($d . $node))
621 {
622 /* Push new found directory. */
623 $stack[] = $d . $node . '/';
624 }
625 elseif (is_file ($d . $node))
626 {
627 /* Read link informations. */
628 $l = jirafeau_get_link (basename ($node));
629 if (!count ($l))
630 continue;
631 $p = s2p ($l['md5']);
632 if ($l['time'] > 0 && $l['time'] < time () || // expired
633 !file_exists (VAR_FILES . $p . $l['md5']) || // invalid
634 !file_exists (VAR_FILES . $p . $l['md5'] . '_count')) // invalid
635 {
636 jirafeau_delete_link ($node);
637 $count++;
638 }
639 }
640 }
641 }
642 return $count;
643 }
644 ?>

patrick-canterino.de