]> git.p6c8.net - jirafeau.git/blob - lib/functions.php
better array construction for base_16_to_64 construction
[jirafeau.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 $o = '';
43 $b = '';
44 $i = 0;
45 $size = strlen ($num);
46 for ($i = 0; $i < $size; $i++)
47 $b .= base_convert ($num{$i}, 16, 2);
48 $size = strlen ($b);
49 for ($i = $size - 6; $i >= 0; $i -= 6)
50 $o = $m{bindec (substr ($b, $i, 6))} . $o;
51 if ($i < 0 && $i > -6)
52 $o = $m{bindec (substr ($b, 0, $i + 6))} . $o;
53 return $o;
54 }
55
56 function
57 jirafeau_human_size ($octets)
58 {
59 $u = array ('B', 'KB', 'MB', 'GB', 'TB');
60 $o = max ($octets, 0);
61 $p = min (floor (($o ? log ($o) : 0) / log (1024)), count ($u) - 1);
62 $o /= pow (1024, $p);
63 return round ($o, 1) . $u[$p];
64 }
65
66 function
67 jirafeau_clean_rm_link ($link)
68 {
69 $p = s2p ("$link");
70 if (file_exists (VAR_LINKS . $p . $link))
71 unlink (VAR_LINKS . $p . $link);
72 $parse = VAR_LINKS . $p;
73 $scan = array();
74 while (file_exists ($parse)
75 && ($scan = scandir ($parse))
76 && count ($scan) == 2 // '.' and '..' folders => empty.
77 && basename ($parse) != basename (VAR_LINKS))
78 {
79 rmdir ($parse);
80 $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
81 }
82 }
83
84 function
85 jirafeau_clean_rm_file ($md5)
86 {
87 $p = s2p ("$md5");
88 if (file_exists (VAR_FILES . $p . $md5))
89 unlink (VAR_FILES . $p . $md5);
90 if (file_exists (VAR_FILES . $p . $md5 . '_count'))
91 unlink (VAR_FILES . $p . $md5 . '_count');
92 $parse = VAR_FILES . $p;
93 $scan = array();
94 while (file_exists ($parse)
95 && ($scan = scandir ($parse))
96 && count ($scan) == 2 // '.' and '..' folders => empty.
97 && basename ($parse) != basename (VAR_FILES))
98 {
99 rmdir ($parse);
100 $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
101 }
102 }
103
104 /**
105 * transforms a php.ini string representing a value in an integer
106 * @param $value the value from php.ini
107 * @returns an integer for this value
108 */
109 function jirafeau_ini_to_bytes ($value)
110 {
111 $modifier = substr ($value, -1);
112 $bytes = substr ($value, 0, -1);
113 switch (strtoupper ($modifier))
114 {
115 case 'P':
116 $bytes *= 1024;
117 case 'T':
118 $bytes *= 1024;
119 case 'G':
120 $bytes *= 1024;
121 case 'M':
122 $bytes *= 1024;
123 case 'K':
124 $bytes *= 1024;
125 default:
126 break;
127 }
128 return $bytes;
129 }
130
131 /**
132 * gets the maximum upload size according to php.ini
133 * @returns the maximum upload size string
134 */
135 function
136 jirafeau_get_max_upload_size ()
137 {
138 return jirafeau_human_size(
139 min (jirafeau_ini_to_bytes (ini_get ('post_max_size')),
140 jirafeau_ini_to_bytes (ini_get ('upload_max_filesize'))));
141 }
142
143 /**
144 * gets a string explaining the error
145 * @param $code the error code
146 * @returns a string explaining the error
147 */
148 function
149 jirafeau_upload_errstr ($code)
150 {
151 switch ($code)
152 {
153 case UPLOAD_ERR_INI_SIZE:
154 case UPLOAD_ERR_FORM_SIZE:
155 return t('Your file exceeds the maximum authorized file size. ');
156 break;
157
158 case UPLOAD_ERR_PARTIAL:
159 case UPLOAD_ERR_NO_FILE:
160 return
161 t
162 ('Your file was not uploaded correctly. You may succeed in retrying. ');
163 break;
164
165 case UPLOAD_ERR_NO_TMP_DIR:
166 case UPLOAD_ERR_CANT_WRITE:
167 case UPLOAD_ERR_EXTENSION:
168 return t('Internal error. You may not succeed in retrying. ');
169 break;
170
171 default:
172 break;
173 }
174 return t('Unknown error. ');
175 }
176
177 /** Remove link and it's file
178 * @param $link the link's name (hash)
179 */
180
181 function
182 jirafeau_delete_link ($link)
183 {
184 $l = jirafeau_get_link ($link);
185 if (!count ($l))
186 return;
187
188 jirafeau_clean_rm_link ($link);
189
190 $md5 = $l['md5'];
191 $p = s2p ("$md5");
192
193 $counter = 1;
194 if (file_exists (VAR_FILES . $p . $md5. '_count'))
195 {
196 $content = file (VAR_FILES . $p . $md5. '_count');
197 $counter = trim ($content[0]);
198 }
199 $counter--;
200
201 if ($counter >= 1)
202 {
203 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
204 fwrite ($handle, $counter);
205 fclose ($handle);
206 }
207
208 if ($counter == 0)
209 jirafeau_clean_rm_file ($md5);
210 }
211
212 /**
213 * Delete a file and it's links.
214 */
215 function
216 jirafeau_delete_file ($md5)
217 {
218 $count = 0;
219 /* Get all links files. */
220 $stack = array (VAR_LINKS);
221 while (($d = array_shift ($stack)) && $d != NULL)
222 {
223 $dir = scandir ($d);
224
225 foreach ($dir as $node)
226 {
227 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
228 preg_match ('/\.tmp/i', "$node"))
229 continue;
230
231 if (is_dir ($d . $node))
232 {
233 /* Push new found directory. */
234 $stack[] = $d . $node . '/';
235 }
236 elseif (is_file ($d . $node))
237 {
238 /* Read link informations. */
239 $l = jirafeau_get_link (basename ($node));
240 if (!count ($l))
241 continue;
242 if ($l['md5'] == $md5)
243 {
244 $count++;
245 jirafeau_delete_link ($node);
246 }
247 }
248 }
249 }
250 jirafeau_clean_rm_file ($md5);
251 return $count;
252 }
253
254 /**
255 * handles an uploaded file
256 * @param $file the file struct given by $_FILE[]
257 * @param $one_time_download is the file a one time download ?
258 * @param $key if not empty, protect the file with this key
259 * @param $time the time of validity of the file
260 * @param $ip uploader's ip
261 * @returns an array containing some information
262 * 'error' => information on possible errors
263 * 'link' => the link name of the uploaded file
264 * 'delete_link' => the link code to delete file
265 */
266 function
267 jirafeau_upload ($file, $one_time_download, $key, $time, $ip)
268 {
269 if (empty ($file['tmp_name']) || !is_uploaded_file ($file['tmp_name']))
270 {
271 return (array(
272 'error' =>
273 array ('has_error' => true,
274 'why' => jirafeau_upload_errstr ($file['error'])),
275 'link' => '',
276 'delete_link' => ''));
277 }
278
279 /* array representing no error */
280 $noerr = array ('has_error' => false, 'why' => '');
281
282 /* file informations */
283 $md5 = md5_file ($file['tmp_name']);
284 $name = trim ($file['name']);
285 $mime_type = $file['type'];
286 $size = $file['size'];
287
288 /* does file already exist ? */
289 $rc = false;
290 $p = s2p ("$md5");
291 if (file_exists (VAR_FILES . $p . $md5))
292 {
293 $rc = unlink ($file['tmp_name']);
294 }
295 elseif ((file_exists (VAR_FILES . $p) || @mkdir (VAR_FILES . $p, 0755, true))
296 && move_uploaded_file ($file['tmp_name'], VAR_FILES . $p . $md5))
297 {
298 $rc = true;
299 }
300 if (!$rc)
301 {
302 return (array(
303 'error' =>
304 array ('has_error' => true,
305 'why' => t('Internal error during file creation.')),
306 'link' =>'',
307 'delete_link' => ''));
308 }
309
310 /* increment or create count file */
311 $counter = 0;
312 if (file_exists (VAR_FILES . $p . $md5 . '_count'))
313 {
314 $content = file (VAR_FILES . $p . $md5. '_count');
315 $counter = trim ($content[0]);
316 }
317 $counter++;
318 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
319 fwrite ($handle, $counter);
320 fclose ($handle);
321
322 /* Create delete code. */
323 $delete_link_code = 0;
324 for ($i = 0; $i < 8; $i++)
325 $delete_link_code .= dechex (rand (0, 16));
326
327 /* md5 password or empty */
328 $password = '';
329 if (!empty ($key))
330 $password = md5 ($key);
331
332 /* create link file */
333 $link_tmp_name = VAR_LINKS . $md5 . rand (0, 10000) . ' .tmp';
334 $handle = fopen ($link_tmp_name, 'w');
335 fwrite ($handle,
336 $name . NL. $mime_type . NL. $size . NL. $password . NL. $time .
337 NL . $md5. NL . ($one_time_download ? 'O' : 'R') . NL.date ('U') .
338 NL. $ip . NL. $delete_link_code . NL);
339 fclose ($handle);
340 $md5_link = base_16_to_64 (md5_file ($link_tmp_name));
341 $l = s2p ("$md5_link");
342 if (!@mkdir (VAR_LINKS . $l, 0755, true) ||
343 !rename ($link_tmp_name, VAR_LINKS . $l . $md5_link))
344 {
345 if (file_exists ($link_tmp_name))
346 unlink ($link_tmp_name);
347
348 $counter--;
349 if ($counter >= 1)
350 {
351 $handle = fopen (VAR_FILES . $p . $md5. '_count', 'w');
352 fwrite ($handle, $counter);
353 fclose ($handle);
354 }
355 else
356 {
357 jirafeau_clean_rm_file ($md5_link);
358 }
359 return (array(
360 'error' =>
361 array ('has_error' => true,
362 'why' => t('Internal error during file creation. ')),
363 'link' =>'',
364 'delete_link' => ''));
365 }
366 return (array ('error' => $noerr,
367 'link' => $md5_link,
368 'delete_link' => $delete_link_code));
369 }
370
371 /**
372 * tells if a mime-type is viewable in a browser
373 * @param $mime the mime type
374 * @returns a boolean telling if a mime type is viewable
375 */
376 function
377 jirafeau_is_viewable ($mime)
378 {
379 if (!empty ($mime))
380 {
381 /* Actually, verify if mime-type is an image or a text. */
382 $viewable = array ('image', 'text');
383 $decomposed = explode ('/', $mime);
384 return in_array ($decomposed[0], $viewable);
385 }
386 return false;
387 }
388
389
390 // Error handling functions.
391 //! Global array that contains all registered errors.
392 $error_list = array ();
393
394 /**
395 * Adds an error to the list of errors.
396 * @param $title the error's title
397 * @param $description is a human-friendly description of the problem.
398 */
399 function
400 add_error ($title, $description)
401 {
402 global $error_list;
403 $error_list[] = '<p>' . $title. '<br />' . $description. '</p>';
404 }
405
406 /**
407 * Informs whether any error has been registered yet.
408 * @return true if there are errors.
409 */
410 function
411 has_error ()
412 {
413 global $error_list;
414 return !empty ($error_list);
415 }
416
417 /**
418 * Displays all the errors.
419 */
420 function
421 show_errors ()
422 {
423 if (has_error ())
424 {
425 global $error_list;
426 echo '<div class="error">';
427 foreach ($error_list as $error)
428 {
429 echo $error;
430 }
431 echo '</div>';
432 }
433 }
434
435 /**
436 * Read link informations
437 * @return array containing informations.
438 */
439 function
440 jirafeau_get_link ($hash)
441 {
442 $out = array ();
443 $link = VAR_LINKS . s2p ("$hash") . $hash;
444
445 if (!file_exists ($link))
446 return $out;
447
448 $c = file ($link);
449 $out['file_name'] = trim ($c[0]);
450 $out['mime_type'] = trim ($c[1]);
451 $out['file_size'] = trim ($c[2]);
452 $out['key'] = trim ($c[3], NL);
453 $out['time'] = trim ($c[4]);
454 $out['md5'] = trim ($c[5]);
455 $out['onetime'] = trim ($c[6]);
456 $out['upload_date'] = trim ($c[7]);
457 $out['ip'] = trim ($c[8]);
458 $out['link_code'] = trim ($c[9]);
459
460 return $out;
461 }
462
463 /**
464 * List files in admin interface.
465 */
466 function
467 jirafeau_admin_list ($name, $file_hash, $link_hash)
468 {
469 echo '<fieldset><legend>';
470 if (!empty ($name))
471 echo t('Filename') . ": $name ";
472 if (!empty ($file_hash))
473 echo t('file') . ": $file_hash ";
474 if (!empty ($link_hash))
475 echo t('link') . ": $link_hash ";
476 if (empty ($name) && empty ($file_hash) && empty ($link_hash))
477 echo t('List all files');
478 echo '</legend>';
479 echo '<table>';
480 echo '<tr>';
481 echo '<td>' . t('Filename') . '</td>';
482 echo '<td>' . t('Type') . '</td>';
483 echo '<td>' . t('Size') . '</td>';
484 echo '<td>' . t('Expire') . '</td>';
485 echo '<td>' . t('Onetime') . '</td>';
486 echo '<td>' . t('Upload date') . '</td>';
487 echo '<td>' . t('Origin') . '</td>';
488 echo '<td>' . t('Action') . '</td>';
489 echo '</tr>';
490
491 /* Get all links files. */
492 $stack = array (VAR_LINKS);
493 while (($d = array_shift ($stack)) && $d != NULL)
494 {
495 $dir = scandir ($d);
496 foreach ($dir as $node)
497 {
498 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
499 preg_match ('/\.tmp/i', "$node"))
500 continue;
501 if (is_dir ($d . $node))
502 {
503 /* Push new found directory. */
504 $stack[] = $d . $node . '/';
505 }
506 elseif (is_file ($d . $node))
507 {
508 /* Read link informations. */
509 $l = jirafeau_get_link ($node);
510 if (!count ($l))
511 continue;
512
513 /* Filter. */
514 if (!empty ($name) && !preg_match ("/$name/i", $l['file_name']))
515 continue;
516 if (!empty ($file_hash) && $file_hash != $l['md5'])
517 continue;
518 if (!empty ($link_hash) && $link_hash != $node)
519 continue;
520 /* Print link informations. */
521 echo '<tr>';
522 echo '<td>' .
523 '<form action = "admin.php" method = "post">' .
524 '<input type = "hidden" name = "action" value = "download"/>' .
525 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
526 '<input type = "submit" value = "' . $l['file_name'] . '" />' .
527 '</form>';
528 echo '</td>';
529 echo '<td>' . $l['mime_type'] . '</td>';
530 echo '<td>' . jirafeau_human_size ($l['file_size']) . '</td>';
531 echo '<td>' . ($l['time'] == -1 ? '' : strftime ('%c', $l['time'])) .
532 '</td>';
533 echo '<td>' . $l['onetime'] . '</td>';
534 echo '<td>' . strftime ('%c', $l['upload_date']) . '</td>';
535 echo '<td>' . $l['ip'] . '</td>';
536 echo '<td>' .
537 '<form action = "admin.php" method = "post">' .
538 '<input type = "hidden" name = "action" value = "delete_link"/>' .
539 '<input type = "hidden" name = "link" value = "' . $node . '"/>' .
540 '<input type = "submit" value = "' . t('Del link') . '" />' .
541 '</form>' .
542 '<form action = "admin.php" method = "post">' .
543 '<input type = "hidden" name = "action" value = "delete_file"/>' .
544 '<input type = "hidden" name = "md5" value = "' . $l['md5'] . '"/>' .
545 '<input type = "submit" value = "' . t('Del file and links') . '" />' .
546 '</form>' .
547 '</td>';
548 echo '</tr>';
549 }
550 }
551 }
552 echo '</table></fieldset>';
553 }
554
555 /**
556 * Clean expired files.
557 * @return number of cleaned files.
558 */
559 function
560 jirafeau_admin_clean ()
561 {
562 $count = 0;
563 /* Get all links files. */
564 $stack = array (VAR_LINKS);
565 while (($d = array_shift ($stack)) && $d != NULL)
566 {
567 $dir = scandir ($d);
568
569 foreach ($dir as $node)
570 {
571 if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0 ||
572 preg_match ('/\.tmp/i', "$node"))
573 continue;
574
575 if (is_dir ($d . $node))
576 {
577 /* Push new found directory. */
578 $stack[] = $d . $node . '/';
579 }
580 elseif (is_file ($d . $node))
581 {
582 /* Read link informations. */
583 $l = jirafeau_get_link (basename ($node));
584 if (!count ($l))
585 continue;
586 $p = s2p ($l['md5']);
587 if ($l['time'] > 0 && $l['time'] < time () || // expired
588 !file_exists (VAR_FILES . $p . $l['md5']) || // invalid
589 !file_exists (VAR_FILES . $p . $l['md5'] . '_count')) // invalid
590 {
591 jirafeau_delete_link ($node);
592 $count++;
593 }
594 }
595 }
596 }
597 return $count;
598 }
599 ?>

patrick-canterino.de