]> git.p6c8.net - jirafeau_project.git/blob - lib/functions.php
e48534ed49782c90d1a7696904f44e7189b8d119
[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 /**
455 * Read link informations
456 * @return array containing informations.
457 */
458 function
459 jirafeau_get_link ($hash)
460 {
461 $out = array ();
462 $link = VAR_LINKS . s2p ("$hash") . $hash;
463
464 if (!file_exists ($link))
465 return $out;
466
467 $c = file ($link);
468 $out['file_name'] = trim ($c[0]);
469 $out['mime_type'] = trim ($c[1]);
470 $out['file_size'] = trim ($c[2]);
471 $out['key'] = trim ($c[3], NL);
472 $out['time'] = trim ($c[4]);
473 $out['md5'] = trim ($c[5]);
474 $out['onetime'] = trim ($c[6]);
475 $out['upload_date'] = trim ($c[7]);
476 $out['ip'] = trim ($c[8]);
477 $out['link_code'] = trim ($c[9]);
478
479 return $out;
480 }
481
482 /**
483 * List files in admin interface.
484 */
485 function
486 jirafeau_admin_list ($name, $file_hash, $link_hash)
487 {
488 echo '<fieldset><legend>';
489 if (!empty ($name))
490 echo t('Filename') . ": $name ";
491 if (!empty ($file_hash))
492 echo t('file') . ": $file_hash ";
493 if (!empty ($link_hash))
494 echo t('link') . ": $link_hash ";
495 if (empty ($name) && empty ($file_hash) && empty ($link_hash))
496 echo t('List all files');
497 echo '</legend>';
498 echo '<table>';
499 echo '<tr>';
500 echo '<td>' . t('Filename') . '</td>';
501 echo '<td>' . t('Type') . '</td>';
502 echo '<td>' . t('Size') . '</td>';
503 echo '<td>' . t('Expire') . '</td>';
504 echo '<td>' . t('Onetime') . '</td>';
505 echo '<td>' . t('Upload date') . '</td>';
506 echo '<td>' . t('Origin') . '</td>';
507 echo '<td>' . t('Action') . '</td>';
508 echo '</tr>';
509
510 /* Get all links files. */
511 $stack = array (VAR_LINKS);
512 while (($d = array_shift ($stack)) && $d != NULL)
513 {
514 $dir = scandir ($d);
515 foreach ($dir as $node)
516 {
517 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
518 preg_match ('/\.tmp/i', "$node"))
519 continue;
520 if (is_dir ($d . $node))
521 {
522 /* Push new found directory. */
523 $stack[] = $d . $node . '/';
524 }
525 elseif (is_file ($d . $node))
526 {
527 /* Read link informations. */
528 $l = jirafeau_get_link ($node);
529 if (!count ($l))
530 continue;
531
532 /* Filter. */
533 if (!empty ($name) && !preg_match ("/$name/i", $l['file_name']))
534 continue;
535 if (!empty ($file_hash) && $file_hash != $l['md5'])
536 continue;
537 if (!empty ($link_hash) && $link_hash != $node)
538 continue;
539 /* Print link informations. */
540 echo '<tr>';
541 echo '<td>' .
542 '<form action = "admin.php" method = "post">' .
543 '<input type = "hidden" name = "action" value = "download"/>' .
544 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
545 '<input type = "submit" value = "' . $l['file_name'] . '" />' .
546 '</form>';
547 echo '</td>';
548 echo '<td>' . $l['mime_type'] . '</td>';
549 echo '<td>' . jirafeau_human_size ($l['file_size']) . '</td>';
550 echo '<td>' . ($l['time'] == -1 ? '' : strftime ('%c', $l['time'])) .
551 '</td>';
552 echo '<td>' . $l['onetime'] . '</td>';
553 echo '<td>' . strftime ('%c', $l['upload_date']) . '</td>';
554 echo '<td>' . $l['ip'] . '</td>';
555 echo '<td>' .
556 '<form action = "admin.php" method = "post">' .
557 '<input type = "hidden" name = "action" value = "delete_link"/>' .
558 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
559 '<input type = "submit" value = "' . t('Del link') . '" />' .
560 '</form>' .
561 '<form action = "admin.php" method = "post">' .
562 '<input type = "hidden" name = "action" value = "delete_file"/>' .
563 '<input type = "hidden" name = "md5" value = "' . $l['md5'] . '"/>' .
564 '<input type = "submit" value = "' . t('Del file and links') . '" />' .
565 '</form>' .
566 '</td>';
567 echo '</tr>';
568 }
569 }
570 }
571 echo '</table></fieldset>';
572 }
573
574 /**
575 * Clean expired files.
576 * @return number of cleaned files.
577 */
578 function
579 jirafeau_admin_clean ()
580 {
581 $count = 0;
582 /* Get all links files. */
583 $stack = array (VAR_LINKS);
584 while (($d = array_shift ($stack)) && $d != NULL)
585 {
586 $dir = scandir ($d);
587
588 foreach ($dir as $node)
589 {
590 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
591 preg_match ('/\.tmp/i', "$node"))
592 continue;
593
594 if (is_dir ($d . $node))
595 {
596 /* Push new found directory. */
597 $stack[] = $d . $node . '/';
598 }
599 elseif (is_file ($d . $node))
600 {
601 /* Read link informations. */
602 $l = jirafeau_get_link (basename ($node));
603 if (!count ($l))
604 continue;
605 $p = s2p ($l['md5']);
606 if ($l['time'] > 0 && $l['time'] < time () || // expired
607 !file_exists (VAR_FILES . $p . $l['md5']) || // invalid
608 !file_exists (VAR_FILES . $p . $l['md5'] . '_count')) // invalid
609 {
610 jirafeau_delete_link ($node);
611 $count++;
612 }
613 }
614 }
615 }
616 return $count;
617 }
618 ?>

patrick-canterino.de