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

patrick-canterino.de