fileUpload.lib.php 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * FILE UPLOAD LIBRARY
  5. *
  6. * This is the file upload library for Chamilo.
  7. * Include/require it in your code to use its functionality.
  8. *
  9. * @package chamilo.library
  10. * @todo test and reorganise
  11. */
  12. /**
  13. * Code
  14. */
  15. require_once api_get_path(LIBRARY_PATH).'document.lib.php';
  16. /**
  17. * Changes the file name extension from .php to .phps
  18. * Useful for securing a site.
  19. *
  20. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  21. * @param - file_name (string) name of a file
  22. * @return - the filenam phps'ized
  23. */
  24. function php2phps($file_name) {
  25. return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
  26. }
  27. /**
  28. * Renames .htaccess & .HTACCESS to htaccess.txt
  29. *
  30. * @param string $filename
  31. * @return string
  32. */
  33. function htaccess2txt($filename) {
  34. return str_replace(array('.htaccess', '.HTACCESS'), array('htaccess.txt', 'htaccess.txt'), $filename);
  35. }
  36. /**
  37. * This function executes our safety precautions
  38. * more functions can be added
  39. *
  40. * @param string $filename
  41. * @return string
  42. * @see php2phps()
  43. * @see htaccess2txt()
  44. */
  45. function disable_dangerous_file($filename) {
  46. return htaccess2txt(php2phps($filename));
  47. }
  48. /**
  49. * This function generates a unique name for a file on a given location
  50. * filenames are changed to name_#.ext
  51. *
  52. * @param string $path
  53. * @param string $name
  54. * @return new unique name
  55. */
  56. function unique_name($path, $name) {
  57. $ext = substr(strrchr($name, '.'), 0);
  58. $name_no_ext = substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
  59. $n = 0;
  60. $unique = '';
  61. while (file_exists($path . $name_no_ext . $unique . $ext)) {
  62. $unique = '_' . ++$n;
  63. }
  64. return $name_no_ext . $unique . $ext;
  65. }
  66. /**
  67. * Returns the name without extension, used for the title
  68. *
  69. * @param string $name
  70. * @return name without the extension
  71. */
  72. function get_document_title($name) {
  73. // If they upload .htaccess...
  74. $name = disable_dangerous_file($name);
  75. $ext = substr(strrchr($name, '.'), 0);
  76. return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
  77. }
  78. /**
  79. * This function checks if the upload succeeded
  80. *
  81. * @param array $uploaded_file ($_FILES)
  82. * @return true if upload succeeded
  83. */
  84. function process_uploaded_file($uploaded_file, $show_output = true) {
  85. // Checking the error code sent with the file upload.
  86. if (isset($uploaded_file['error'])) {
  87. switch ($uploaded_file['error']) {
  88. case 1:
  89. // The uploaded file exceeds the upload_max_filesize directive in php.ini.
  90. if ($show_output) {
  91. Display::display_error_message(get_lang('UplExceedMaxServerUpload').ini_get('upload_max_filesize'));
  92. }
  93. return false;
  94. case 2:
  95. // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
  96. // Not used at the moment, but could be handy if we want to limit the size of an upload (e.g. image upload in html editor).
  97. $max_file_size = intval($_POST['MAX_FILE_SIZE']);
  98. if ($show_output) {
  99. Display::display_error_message(get_lang('UplExceedMaxPostSize'). format_file_size($max_file_size));
  100. }
  101. return false;
  102. case 3:
  103. // The uploaded file was only partially uploaded.
  104. if ($show_output) {
  105. Display::display_error_message(get_lang('UplPartialUpload').' '.get_lang('PleaseTryAgain'));
  106. }
  107. return false;
  108. case 4:
  109. // No file was uploaded.
  110. if ($show_output) {
  111. Display::display_error_message(get_lang('UplNoFileUploaded').' '. get_lang('UplSelectFileFirst'));
  112. }
  113. return false;
  114. }
  115. }
  116. if (!file_exists($uploaded_file['tmp_name'])) {
  117. // No file was uploaded.
  118. if ($show_output) {
  119. Display::display_error_message(get_lang('UplUploadFailed'));
  120. }
  121. return false;
  122. }
  123. if (file_exists($uploaded_file['tmp_name'])) {
  124. $filesize = filesize($uploaded_file['tmp_name']);
  125. if (empty($filesize)) {
  126. // No file was uploaded.
  127. if ($show_output) {
  128. Display::display_error_message(get_lang('UplUploadFailedSizeIsZero'));
  129. }
  130. return false;
  131. }
  132. }
  133. $course_id = api_get_course_id();
  134. //Checking course quota if we are in a course
  135. if (!empty($course_id)) {
  136. $max_filled_space = DocumentManager::get_course_quota();
  137. // Check if there is enough space to save the file
  138. if (!DocumentManager::enough_space($uploaded_file['size'], $max_filled_space)) {
  139. if ($show_output) {
  140. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  141. }
  142. return false;
  143. }
  144. }
  145. // case 0: default: We assume there is no error, the file uploaded with success.
  146. return true;
  147. }
  148. /**
  149. * This function does the save-work for the documents.
  150. * It handles the uploaded file and adds the properties to the database
  151. * If unzip=1 and the file is a zipfile, it is extracted
  152. * If we decide to save ALL kinds of documents in one database,
  153. * we could extend this with a $type='document', 'scormdocument',...
  154. *
  155. * @param array $_course
  156. * @param array $uploaded_file ($_FILES)
  157. * @param string $base_work_dir
  158. * @param string $upload_path
  159. * @param int $user_id
  160. * @param int $to_group_id, 0 for everybody
  161. * @param int $to_user_id, NULL for everybody
  162. * @param int $unzip 1/0
  163. * @param string $what_if_file_exists overwrite, rename or warn if exists (default)
  164. * @param boolean Optional output parameter. So far only use for unzip_uploaded_document function. If no output wanted on success, set to false.
  165. * @return path of the saved file
  166. */
  167. function handle_uploaded_document(
  168. $_course,
  169. $uploaded_file,
  170. $base_work_dir,
  171. $upload_path,
  172. $user_id,
  173. $to_group_id = 0,
  174. $to_user_id = null,
  175. $unzip = 0,
  176. $what_if_file_exists = '',
  177. $output = true
  178. ) {
  179. if (!$user_id) {
  180. die('Not a valid user.');
  181. }
  182. // Strip slashes
  183. $uploaded_file['name'] = stripslashes($uploaded_file['name']);
  184. // Add extension to files without one (if possible)
  185. $uploaded_file['name'] = add_ext_on_mime($uploaded_file['name'], $uploaded_file['type']);
  186. $current_session_id = api_get_session_id();
  187. // Just in case process_uploaded_file is not called
  188. $max_filled_space = DocumentManager::get_course_quota();
  189. // Check if there is enough space to save the file
  190. if (!DocumentManager::enough_space($uploaded_file['size'], $max_filled_space)) {
  191. if ($output) {
  192. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  193. }
  194. return false;
  195. }
  196. // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
  197. if ($unzip == 1 && preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
  198. return unzip_uploaded_document(
  199. $uploaded_file,
  200. $upload_path,
  201. $base_work_dir,
  202. $max_filled_space,
  203. $output,
  204. $to_group_id
  205. );
  206. //display_message('Unzipping file');
  207. } elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploaded_file['name']))) { // We can only unzip ZIP files (no gz, tar,...)
  208. if ($output) {
  209. Display::display_error_message(get_lang('UplNotAZip')." ".get_lang('PleaseTryAgain'));
  210. }
  211. return false;
  212. } else {
  213. // Clean up the name, only ASCII characters should stay. (and strict)
  214. $clean_name = replace_dangerous_char($uploaded_file['name'], 'strict');
  215. // No "dangerous" files
  216. $clean_name = disable_dangerous_file($clean_name);
  217. // Checking file extension
  218. if (!filter_extension($clean_name)) {
  219. if ($output) {
  220. Display::display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
  221. }
  222. return false;
  223. } else {
  224. // If the upload path differs from / (= root) it will need a slash at the end
  225. if ($upload_path != '/') {
  226. $upload_path = $upload_path.'/';
  227. }
  228. $file_path = $upload_path.$clean_name;
  229. // Full path to where we want to store the file with trailing slash
  230. $where_to_save = $base_work_dir.$upload_path;
  231. // At least if the directory doesn't exist, tell so
  232. if (!is_dir($where_to_save)) {
  233. if (!mkdir($where_to_save, api_get_permissions_for_new_directories())) {
  234. if ($output) {
  235. Display::display_error_message(get_lang('DestDirectoryDoesntExist').' ('.$upload_path.')');
  236. }
  237. return false;
  238. }
  239. }
  240. // Full path of the destination
  241. $store_path = $where_to_save.$clean_name;
  242. // Name of the document without the extension (for the title)
  243. $document_name = get_document_title($uploaded_file['name']);
  244. // Size of the uploaded file (in bytes)
  245. $file_size = $uploaded_file['size'];
  246. $files_perm = api_get_permissions_for_new_files();
  247. $doc_path = '/'.$clean_name;
  248. $docId = DocumentManager :: get_document_id($_course, $doc_path, $current_session_id);
  249. // What to do if the target file exists
  250. switch ($what_if_file_exists) {
  251. // Overwrite the file if it exists
  252. case 'overwrite':
  253. // Check if the target file exists, so we can give another message
  254. $file_exists = file_exists($store_path);
  255. if (moveUploadedFile($uploaded_file, $store_path)) {
  256. chmod($store_path, $files_perm);
  257. if ($file_exists && $docId) {
  258. // UPDATE DATABASE
  259. $document_id = DocumentManager::get_document_id($_course, $file_path);
  260. if (is_numeric($document_id)) {
  261. // Update file size
  262. update_existing_document($_course, $document_id, $uploaded_file['size']);
  263. // Update document item_property
  264. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentUpdated', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  265. // Redo visibility
  266. api_set_default_visibility(TOOL_DOCUMENT, $document_id);
  267. } else {
  268. // There might be cases where the file exists on disk but there is no registration of that in the database
  269. // In this case, and if we are in overwrite mode, overwrite and create the db record
  270. $document_id = add_document($_course, $file_path, 'file', $file_size, $document_name, null, 0, true, null, $current_session_id);
  271. if ($document_id) {
  272. // Put the document in item_property update
  273. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  274. // Redo visibility
  275. api_set_default_visibility(TOOL_DOCUMENT, $document_id);
  276. }
  277. }
  278. // If the file is in a folder, we need to update all parent folders
  279. item_property_update_on_folder($_course, $upload_path, $user_id);
  280. // Display success message with extra info to user
  281. if ($output) {
  282. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path .' '. get_lang('UplFileOverwritten'), false);
  283. }
  284. return $file_path;
  285. } else {
  286. // Put the document data in the database
  287. $document_id = add_document($_course, $file_path, 'file', $file_size, $document_name, null, 0, true, null, $current_session_id);
  288. if ($document_id) {
  289. // Put the document in item_property update
  290. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  291. // Redo visibility
  292. api_set_default_visibility(TOOL_DOCUMENT, $document_id);
  293. }
  294. // If the file is in a folder, we need to update all parent folders
  295. item_property_update_on_folder($_course, $upload_path, $user_id);
  296. // Display success message to user
  297. if ($output){
  298. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path, false);
  299. }
  300. return $file_path;
  301. }
  302. } else {
  303. if ($output) {
  304. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  305. }
  306. return false;
  307. }
  308. break;
  309. // Rename the file if it exists
  310. case 'rename':
  311. if ($docId) {
  312. $new_name = unique_name($where_to_save, $clean_name);
  313. } else {
  314. $new_name = $clean_name;
  315. }
  316. $store_path = $where_to_save.$new_name;
  317. $new_file_path = $upload_path.$new_name;
  318. if (moveUploadedFile($uploaded_file, $store_path)) {
  319. chmod($store_path, $files_perm);
  320. // Put the document data in the database
  321. $document_id = add_document($_course, $new_file_path, 'file', $file_size, $document_name, null, 0, true);
  322. if ($document_id) {
  323. // Update document item_property
  324. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  325. // Redo visibility
  326. api_set_default_visibility(TOOL_DOCUMENT, $document_id);
  327. }
  328. // If the file is in a folder, we need to update all parent folders
  329. item_property_update_on_folder($_course, $upload_path, $user_id);
  330. // Display success message to user
  331. if ($output){
  332. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.get_lang('UplFileSavedAs').$new_file_path, false);
  333. }
  334. return $new_file_path;
  335. } else {
  336. if ($output) {
  337. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  338. }
  339. return false;
  340. }
  341. break;
  342. default:
  343. // Only save the file if it doesn't exist or warn user if it does exist
  344. if (file_exists($store_path) && $docId) {
  345. if ($output) {
  346. Display::display_error_message($clean_name.' '.get_lang('UplAlreadyExists'));
  347. }
  348. } else {
  349. if (moveUploadedFile($uploaded_file, $store_path)) {
  350. chmod($store_path, $files_perm);
  351. // Put the document data in the database
  352. $document_id = add_document($_course, $file_path, 'file', $file_size, $document_name, null, 0, true);
  353. if ($document_id) {
  354. // Update document item_property
  355. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, $to_user_id, null, null, $current_session_id);
  356. // Redo visibility
  357. api_set_default_visibility(TOOL_DOCUMENT, $document_id);
  358. }
  359. // If the file is in a folder, we need to update all parent folders
  360. item_property_update_on_folder($_course,$upload_path,$user_id);
  361. // Display success message to user
  362. if ($output){
  363. Display::display_confirmation_message(get_lang('UplUploadSucceeded').'<br />'.$file_path, false);
  364. }
  365. return $file_path;
  366. } else {
  367. if ($output) {
  368. Display::display_error_message(get_lang('UplUnableToSaveFile'));
  369. }
  370. return false;
  371. }
  372. }
  373. break;
  374. }
  375. }
  376. }
  377. }
  378. /**
  379. * @param string $file
  380. * @param string $storePath
  381. * @return bool
  382. */
  383. function moveUploadedFile($file, $storePath)
  384. {
  385. $handleFromFile = isset($file['from_file']) && $file['from_file'] ? true : false;
  386. if ($handleFromFile) {
  387. return file_exists($file['tmp_name']);
  388. } else {
  389. return move_uploaded_file($file['tmp_name'], $storePath);
  390. }
  391. }
  392. /**
  393. * Checks if there is enough place to add a file on a directory
  394. * on the base of a maximum directory size allowed
  395. * deprecated: use enough_space instead!
  396. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  397. * @param - file_size (int) - size of the file in byte
  398. * @param - dir (string) - Path of the directory
  399. * whe the file should be added
  400. * @param - max_dir_space (int) - maximum size of the diretory in byte
  401. * @return - boolean true if there is enough space,
  402. * boolean false otherwise
  403. *
  404. * @see - enough_size() uses dir_total_space() function
  405. */
  406. function enough_size($file_size, $dir, $max_dir_space)
  407. {
  408. // If the directory is the archive directory, safely ignore the size limit
  409. if (api_get_path(SYS_ARCHIVE_PATH) == $dir) {
  410. return true;
  411. }
  412. if ($max_dir_space) {
  413. $already_filled_space = dir_total_space($dir);
  414. if (($file_size + $already_filled_space) > $max_dir_space) {
  415. return false;
  416. }
  417. }
  418. return true;
  419. }
  420. /**
  421. * Computes the size already occupied by a directory and is subdirectories
  422. *
  423. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  424. * @param - dir_path (string) - size of the file in byte
  425. * @return - int - return the directory size in bytes
  426. */
  427. function dir_total_space($dir_path) {
  428. $save_dir = getcwd();
  429. chdir($dir_path) ;
  430. $handle = opendir($dir_path);
  431. $sumSize = 0;
  432. $dirList = array();
  433. while ($element = readdir($handle)) {
  434. if ( $element == '.' || $element == '..') {
  435. continue; // Skip the current and parent directories
  436. }
  437. if (is_file($element)) {
  438. $sumSize += filesize($element);
  439. }
  440. if (is_dir($element)) {
  441. $dirList[] = $dir_path.'/'.$element;
  442. }
  443. }
  444. closedir($handle) ;
  445. if (sizeof($dirList) > 0) {
  446. foreach ($dirList as $j) {
  447. $sizeDir = dir_total_space($j); // Recursivity
  448. $sumSize += $sizeDir;
  449. }
  450. }
  451. chdir($save_dir); // Return to initial position
  452. return $sumSize;
  453. }
  454. /**
  455. * Tries to add an extension to files without extension
  456. * Some applications on Macintosh computers don't add an extension to the files.
  457. * This subroutine try to fix this on the basis of the MIME type sent
  458. * by the browser.
  459. *
  460. * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
  461. * We don't have solution for this kind of situation
  462. *
  463. * @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
  464. * @author - Bert Vanderkimpen
  465. * @param - file_name (string) - Name of the file
  466. * @param - file_type (string) - Type of the file
  467. * @return - file_name (string)
  468. */
  469. function add_ext_on_mime($file_name, $file_type) {
  470. // Check whether the file has an extension AND whether the browser has sent a MIME Type
  471. if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
  472. // Build a "MIME-types / extensions" connection table
  473. static $mime_type = array();
  474. $mime_type[] = 'application/msword'; $extension[] = '.doc';
  475. $mime_type[] = 'application/rtf'; $extension[] = '.rtf';
  476. $mime_type[] = 'application/vnd.ms-powerpoint'; $extension[] = '.ppt';
  477. $mime_type[] = 'application/vnd.ms-excel'; $extension[] = '.xls';
  478. $mime_type[] = 'application/pdf'; $extension[] = '.pdf';
  479. $mime_type[] = 'application/postscript'; $extension[] = '.ps';
  480. $mime_type[] = 'application/mac-binhex40'; $extension[] = '.hqx';
  481. $mime_type[] = 'application/x-gzip'; $extension[] = 'tar.gz';
  482. $mime_type[] = 'application/x-shockwave-flash'; $extension[] = '.swf';
  483. $mime_type[] = 'application/x-stuffit'; $extension[] = '.sit';
  484. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  485. $mime_type[] = 'application/zip'; $extension[] = '.zip';
  486. $mime_type[] = 'application/x-tar'; $extension[] = '.tar';
  487. $mime_type[] = 'text/html'; $extension[] = '.html';
  488. $mime_type[] = 'text/plain'; $extension[] = '.txt';
  489. $mime_type[] = 'text/rtf'; $extension[] = '.rtf';
  490. $mime_type[] = 'img/gif'; $extension[] = '.gif';
  491. $mime_type[] = 'img/jpeg'; $extension[] = '.jpg';
  492. $mime_type[] = 'img/png'; $extension[] = '.png';
  493. $mime_type[] = 'audio/midi'; $extension[] = '.mid';
  494. $mime_type[] = 'audio/mpeg'; $extension[] = '.mp3';
  495. $mime_type[] = 'audio/x-aiff'; $extension[] = '.aif';
  496. $mime_type[] = 'audio/x-pn-realaudio'; $extension[] = '.rm';
  497. $mime_type[] = 'audio/x-pn-realaudio-plugin'; $extension[] = '.rpm';
  498. $mime_type[] = 'audio/x-wav'; $extension[] = '.wav';
  499. $mime_type[] = 'video/mpeg'; $extension[] = '.mpg';
  500. $mime_type[] = 'video/mpeg4-generic'; $extension[] = '.mp4';
  501. $mime_type[] = 'video/quicktime'; $extension[] = '.mov';
  502. $mime_type[] = 'video/x-msvideo'; $extension[] = '.avi';
  503. $mime_type[] = 'video/x-ms-wmv'; $extension[] = '.wmv';
  504. $mime_type[] = 'video/x-flv'; $extension[] = '.flv';
  505. $mime_type[] = 'image/svg+xml'; $extension[] = '.svg';
  506. $mime_type[] = 'image/svg+xml'; $extension[] = '.svgz';
  507. $mime_type[] = 'video/ogg'; $extension[] = '.ogv';
  508. $mime_type[] = 'audio/ogg'; $extension[] = '.oga';
  509. $mime_type[] = 'application/ogg'; $extension[] = '.ogg';
  510. $mime_type[] = 'application/ogg'; $extension[] = '.ogx';
  511. $mime_type[] = 'application/x-freemind'; $extension[] = '.mm';
  512. $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12'; $extension[] = '.docm';
  513. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; $extension[] = '.docx';
  514. $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12'; $extension[] = '.dotm';
  515. $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template'; $extension[] = '.dotx';
  516. $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12'; $extension[] = '.potm';
  517. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template'; $extension[] = '.potx';
  518. $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12'; $extension[] = '.ppam';
  519. $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'; $extension[] = '.ppsm';
  520. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow'; $extension[] = '.ppsx';
  521. $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12'; $extension[] = '.pptm';
  522. $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'; $extension[] = '.pptx';
  523. $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12'; $extension[] = '.xlam';
  524. $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12'; $extension[] = '.xlsb';
  525. $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12'; $extension[] = '.xlsm';
  526. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $extension[] = '.xlsx';
  527. $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12'; $extension[] = '.xltm';
  528. $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template'; $extension[] = '.xltx';
  529. // Test on PC (files with no extension get application/octet-stream)
  530. //$mime_type[] = 'application/octet-stream'; $extension[] = '.ext';
  531. // Check whether the MIME type sent by the browser is within the table
  532. foreach ($mime_type as $key => & $type) {
  533. if ($type == $file_type) {
  534. $file_name .= $extension[$key];
  535. break;
  536. }
  537. }
  538. unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
  539. }
  540. return $file_name;
  541. }
  542. /**
  543. *
  544. * @author Hugues Peeters <hugues.peeters@claroline.net>
  545. *
  546. * @param array $uploaded_file - follows the $_FILES Structure
  547. * @param string $base_work_dir - base working directory of the module
  548. * @param string $upload_path - destination of the upload.
  549. * This path is to append to $base_work_dir
  550. * @param int $max_filled_space - amount of bytes to not exceed in the base
  551. * working directory
  552. *
  553. * @return boolean true if it succeds, false otherwise
  554. */
  555. function treat_uploaded_file($uploaded_file, $base_work_dir, $upload_path, $max_filled_space, $uncompress = '') {
  556. $uploaded_file['name'] = stripslashes($uploaded_file['name']);
  557. if (!enough_size($uploaded_file['size'], $base_work_dir, $max_filled_space)) {
  558. return api_failure::set_failure('not_enough_space');
  559. }
  560. if ($uncompress == 'unzip' && preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
  561. return unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space);
  562. } else {
  563. $file_name = trim($uploaded_file['name']);
  564. // CHECK FOR NO DESIRED CHARACTERS
  565. $file_name = replace_dangerous_char($file_name, 'strict');
  566. // TRY TO ADD AN EXTENSION TO FILES WITOUT EXTENSION
  567. $file_name = add_ext_on_mime($file_name, $uploaded_file['type']);
  568. // HANDLE PHP FILES
  569. $file_name = ($file_name);
  570. // COPY THE FILE TO THE DESIRED DESTINATION
  571. if (move_uploaded_file($uploaded_file['tmp_name'], $base_work_dir.$upload_path.'/'.$file_name)) {
  572. set_default_settings($upload_path, $file_name);
  573. }
  574. return true;
  575. }
  576. }
  577. /**
  578. * Manages all the unzipping process of an uploaded file
  579. *
  580. * @author Hugues Peeters <hugues.peeters@claroline.net>
  581. *
  582. * @param array $uploaded_file - follows the $_FILES Structure
  583. * @param string $upload_path - destination of the upload.
  584. * This path is to append to $base_work_dir
  585. * @param string $base_work_dir - base working directory of the module
  586. * @param int $max_filled_space - amount of bytes to not exceed in the base
  587. * working directory
  588. *
  589. * @return boolean true if it succeeds false otherwise
  590. */
  591. function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space) {
  592. $zip_file = new PclZip($uploaded_file['tmp_name']);
  593. // Check the zip content (real size and file extension)
  594. if (file_exists($uploaded_file['tmp_name'])) {
  595. $zip_content_array = $zip_file->listContent();
  596. $ok_scorm = false;
  597. $realFileSize = 0;
  598. foreach ($zip_content_array as & $this_content) {
  599. if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
  600. return api_failure::set_failure('php_file_in_zip_file');
  601. } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
  602. $ok_scorm = true;
  603. } elseif (stristr($this_content['filename'], 'LMS')) {
  604. $ok_plantyn_scorm1 = true;
  605. } elseif (stristr($this_content['filename'], 'REF')) {
  606. $ok_plantyn_scorm2 = true;
  607. } elseif (stristr($this_content['filename'], 'SCO')) {
  608. $ok_plantyn_scorm3 = true;
  609. } elseif (stristr($this_content['filename'], 'AICC')) {
  610. $ok_aicc_scorm = true;
  611. }
  612. $realFileSize += $this_content['size'];
  613. }
  614. if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
  615. $ok_scorm = true;
  616. }
  617. if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
  618. return api_failure::set_failure('not_scorm_content');
  619. }
  620. if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
  621. return api_failure::set_failure('not_enough_space');
  622. }
  623. // It happens on Linux that $upload_path sometimes doesn't start with '/'
  624. if ($upload_path[0] != '/' && substr($base_work_dir,-1,1) != '/') {
  625. $upload_path = '/'.$upload_path;
  626. }
  627. if ($upload_path[strlen($upload_path) - 1] == '/') {
  628. $upload_path=substr($upload_path, 0, -1);
  629. }
  630. /* Uncompressing phase */
  631. /*
  632. The first version, using OS unzip, is not used anymore
  633. because it does not return enough information.
  634. We need to process each individual file in the zip archive to
  635. - add it to the database
  636. - parse & change relative html links
  637. */
  638. if (PHP_OS == 'Linux' && ! get_cfg_var('safe_mode') && false) { // *** UGent, changed by OC ***
  639. // Shell Method - if this is possible, it gains some speed
  640. exec("unzip -d \"".$base_work_dir.$upload_path."/\"".$uploaded_file['name']." " .$uploaded_file['tmp_name']);
  641. } else {
  642. // PHP method - slower...
  643. $save_dir = getcwd();
  644. chdir($base_work_dir.$upload_path);
  645. $unzippingState = $zip_file->extract();
  646. for ($j=0; $j < count($unzippingState); $j++) {
  647. $state = $unzippingState[$j];
  648. // Fix relative links in html files
  649. $extension = strrchr($state['stored_filename'], '.');
  650. }
  651. if ($dir = @opendir($base_work_dir.$upload_path)) {
  652. while ($file = readdir($dir)) {
  653. if ($file != '.' && $file != '..') {
  654. $filetype = 'file';
  655. if (is_dir($base_work_dir.$upload_path.'/'.$file)) $filetype = 'folder';
  656. $safe_file = replace_dangerous_char($file, 'strict');
  657. @rename($base_work_dir.$upload_path.'/'.$file,$base_work_dir.$upload_path.'/'.$safe_file);
  658. set_default_settings($upload_path, $safe_file,$filetype);
  659. }
  660. }
  661. closedir($dir);
  662. } else {
  663. error_log('Could not create directory '.$base_work_dir.$upload_path.' to unzip files');
  664. }
  665. chdir($save_dir); // Back to previous dir position
  666. }
  667. }
  668. return true;
  669. }
  670. /**
  671. * Manages all the unzipping process of an uploaded document
  672. * This uses the item_property table for properties of documents
  673. *
  674. * @author Hugues Peeters <hugues.peeters@claroline.net>
  675. * @author Bert Vanderkimpen
  676. *
  677. * @param array $uploaded_file - follows the $_FILES Structure
  678. * @param string $upload_path - destination of the upload.
  679. * This path is to append to $base_work_dir
  680. * @param string $base_work_dir - base working directory of the module
  681. * @param int $max_filled_space - amount of bytes to not exceed in the base
  682. * working directory
  683. * @param boolean Output switch. Optional. If no output not wanted on success, set to false.
  684. *
  685. * @return boolean true if it succeeds false otherwise
  686. */
  687. function unzip_uploaded_document(
  688. $uploaded_file,
  689. $upload_path,
  690. $base_work_dir,
  691. $max_filled_space,
  692. $output = true,
  693. $to_group_id = 0
  694. ) {
  695. global $_course;
  696. global $_user;
  697. global $to_user_id;
  698. global $to_group_id;
  699. $zip_file = new PclZip($uploaded_file['tmp_name']);
  700. // Check the zip content (real size and file extension)
  701. $zip_content_array = (array)$zip_file->listContent();
  702. $real_filesize = 0;
  703. foreach ($zip_content_array as & $this_content) {
  704. $real_filesize += $this_content['size'];
  705. }
  706. if (!DocumentManager::enough_space($real_filesize, $max_filled_space)) {
  707. Display::display_error_message(get_lang('UplNotEnoughSpace'));
  708. return false;
  709. }
  710. // It happens on Linux that $upload_path sometimes doesn't start with '/'
  711. if ($upload_path[0] != '/') {
  712. $upload_path='/'.$upload_path;
  713. }
  714. /* Uncompressing phase */
  715. // Get into the right directory
  716. $save_dir = getcwd();
  717. chdir($base_work_dir.$upload_path);
  718. // We extract using a callback function that "cleans" the path
  719. $unzipping_state = $zip_file->extract(PCLZIP_CB_PRE_EXTRACT, 'clean_up_files_in_zip', PCLZIP_OPT_REPLACE_NEWER);
  720. // Add all documents in the unzipped folder to the database
  721. add_all_documents_in_folder_to_database($_course, $_user['user_id'], $base_work_dir ,$upload_path == '/' ? '' : $upload_path, $to_group_id);
  722. return true;
  723. }
  724. /**
  725. * This function is a callback function that is used while extracting a zipfile
  726. * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract
  727. *
  728. * @param $p_event
  729. * @param $p_header
  730. * @return 1 (If the function returns 1, then the extraction is resumed)
  731. */
  732. function clean_up_files_in_zip($p_event, &$p_header) {
  733. $res = clean_up_path($p_header['filename']);
  734. return $res;
  735. }
  736. /**
  737. * This function cleans up a given path
  738. * by eliminating dangerous file names and cleaning them
  739. *
  740. * @param string $path
  741. * @return $path
  742. * @see disable_dangerous_file()
  743. * @see replace_dangerous_char()
  744. */
  745. function clean_up_path(&$path) {
  746. // Split the path in folders and files
  747. $path_array = explode('/', $path);
  748. // Clean up every foler and filename in the path
  749. foreach ($path_array as $key => & $val) {
  750. // We don't want to lose the dots in ././folder/file (cfr. zipfile)
  751. if ($val != '.') {
  752. $val = disable_dangerous_file(replace_dangerous_char($val));
  753. }
  754. }
  755. // Join the "cleaned" path (modified in-place as passed by reference)
  756. $path = implode('/', $path_array);
  757. $res = filter_extension($path);
  758. return $res;
  759. }
  760. /**
  761. * Checks if the file is dangerous, based on extension and/or mimetype.
  762. * The list of extensions accepted/rejected can be found from
  763. * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include')
  764. * @param string filename passed by reference. The filename will be modified if filter rules say so! (you can include path but the filename should look like 'abc.html')
  765. * @return int 0 to skip file, 1 to keep file
  766. */
  767. function filter_extension(&$filename) {
  768. if (substr($filename, -1) == '/') {
  769. return 1; // Authorize directories
  770. }
  771. $blacklist = api_get_setting('upload_extensions_list_type');
  772. if ($blacklist != 'whitelist') { // if = blacklist
  773. $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
  774. $skip = api_get_setting('upload_extensions_skip');
  775. $ext = strrchr($filename, '.');
  776. $ext = substr($ext, 1);
  777. if (empty($ext)) {
  778. return 1; // We're in blacklist mode, so accept empty extensions
  779. }
  780. if (in_array(strtolower($ext), $extensions)) {
  781. if ($skip == 'true') {
  782. return 0;
  783. } else {
  784. $new_ext = api_get_setting('upload_extensions_replace_by');
  785. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  786. return 1;
  787. }
  788. } else {
  789. return 1;
  790. }
  791. } else {
  792. $extensions = split(';', strtolower(api_get_setting('upload_extensions_whitelist')));
  793. $skip = api_get_setting('upload_extensions_skip');
  794. $ext = strrchr($filename, '.');
  795. $ext = substr($ext, 1);
  796. if (empty($ext)) {
  797. return 1; // Accept empty extensions
  798. }
  799. if (!in_array(strtolower($ext), $extensions)) {
  800. if ($skip == 'true') {
  801. return 0;
  802. } else {
  803. $new_ext = api_get_setting('upload_extensions_replace_by');
  804. $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
  805. return 1;
  806. }
  807. } else {
  808. return 1;
  809. }
  810. }
  811. }
  812. /**
  813. * Adds a new document to the database
  814. *
  815. * @param array $_course
  816. * @param string $path
  817. * @param string $filetype
  818. * @param int $filesize
  819. * @param string $title
  820. * @param string $comment
  821. * @param int $readonly
  822. * @param bool $save_visibility
  823. * @param int $group_id
  824. * @param int $session_id Session ID, if any
  825. * @return id if inserted document
  826. */
  827. function add_document(
  828. $_course,
  829. $path,
  830. $filetype,
  831. $filesize,
  832. $title,
  833. $comment = null,
  834. $readonly = 0,
  835. $save_visibility = true,
  836. $group_id = null,
  837. $session_id = 0
  838. ) {
  839. $session_id = intval($session_id);
  840. if (empty($session_id)) {
  841. $session_id = api_get_session_id();
  842. }
  843. $readonly = intval($readonly);
  844. $comment = Database::escape_string($comment);
  845. $path = Database::escape_string($path);
  846. $filetype = Database::escape_string($filetype);
  847. $filesize = intval($filesize);
  848. $title = Database::escape_string(htmlspecialchars($title));
  849. $c_id = $_course['real_id'];
  850. $table_document = Database::get_course_table(TABLE_DOCUMENT);
  851. $sql = "INSERT INTO $table_document (c_id, path, filetype, size, title, comment, readonly, session_id)
  852. VALUES ($c_id, '$path','$filetype','$filesize','$title', '$comment', $readonly, $session_id)";
  853. if (Database::query($sql)) {
  854. $document_id = Database::insert_id();
  855. if ($document_id) {
  856. if ($save_visibility) {
  857. api_set_default_visibility($document_id, TOOL_DOCUMENT, $group_id);
  858. }
  859. }
  860. return $document_id;
  861. } else {
  862. return false;
  863. }
  864. }
  865. /**
  866. * Updates an existing document in the database
  867. * as the file exists, we only need to change the size
  868. *
  869. * @param array $_course
  870. * @param int $document_id
  871. * @param int $filesize
  872. * @param int $readonly
  873. * @return boolean true /false
  874. */
  875. function update_existing_document($_course, $document_id, $filesize, $readonly = 0) {
  876. $document_table = Database::get_course_table(TABLE_DOCUMENT);
  877. $document_id = intval($document_id);
  878. $filesize = intval($filesize);
  879. $readonly = intval($readonly);
  880. $course_id = $_course['real_id'];
  881. $sql = "UPDATE $document_table SET size = '$filesize' , readonly = '$readonly'
  882. WHERE c_id = $course_id AND id = $document_id";
  883. if (Database::query($sql)) {
  884. return true;
  885. } else {
  886. return false;
  887. }
  888. }
  889. /**
  890. * This function updates the last_edit_date, last edit user id on all folders in a given path
  891. *
  892. * @param array $_course
  893. * @param string $path
  894. * @param int $user_id
  895. */
  896. function item_property_update_on_folder($_course, $path, $user_id) {
  897. //display_message("Start update_lastedit_on_folder");
  898. // If we are in the root, just return... no need to update anything
  899. if ($path == '/') {
  900. return;
  901. }
  902. $user_id = intval($user_id);
  903. // If the given path ends with a / we remove it
  904. $endchar = substr($path, strlen($path) - 1, 1);
  905. if ($endchar == '/') {
  906. $path = substr($path, 0, strlen($path) - 1);
  907. }
  908. $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
  909. // Get the time
  910. $time = date('Y-m-d H:i:s', time());
  911. // Det all paths in the given path
  912. // /folder/subfolder/subsubfolder/file
  913. // if file is updated, subsubfolder, subfolder and folder are updated
  914. $exploded_path = explode('/', $path);
  915. $course_id = api_get_course_int_id();
  916. $newpath = '';
  917. foreach ($exploded_path as $key => & $value) {
  918. // We don't want a slash before our first slash
  919. if ($key != 0) {
  920. $newpath .= '/'.$value;
  921. //echo 'path= '.$newpath.'<br />';
  922. // Select ID of given folder
  923. $folder_id = DocumentManager::get_document_id($_course, $newpath);
  924. if ($folder_id) {
  925. $sql = "UPDATE $TABLE_ITEMPROPERTY SET lastedit_date='$time',lastedit_type='DocumentInFolderUpdated', lastedit_user_id='$user_id'
  926. WHERE c_id = $course_id AND tool='".TOOL_DOCUMENT."' AND ref='$folder_id'";
  927. Database::query($sql);
  928. }
  929. }
  930. }
  931. }
  932. /**
  933. * Returns the directory depth of the file.
  934. *
  935. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  936. * @param path+filename eg: /main/document/document.php
  937. * @return The directory depth
  938. */
  939. function get_levels($filename) {
  940. $levels=explode('/', $filename);
  941. if (empty($levels[count($levels) - 1])) {
  942. unset($levels[count($levels) - 1]);
  943. }
  944. return count($levels);
  945. }
  946. /**
  947. * Adds file to document table in database
  948. * deprecated: use file_set_default_settings instead
  949. *
  950. * @author Olivier Cauberghe <olivier.cauberghe@ugent.be>
  951. * @param path,filename
  952. * action: Adds an entry to the document table with the default settings.
  953. */
  954. function set_default_settings($upload_path, $filename, $filetype = 'file') {
  955. global $dbTable,$_configuration;
  956. global $default_visibility;
  957. if (!$default_visibility) {
  958. $default_visibility = 'v';
  959. }
  960. $filetype = Database::escape_string($filetype);
  961. $upload_path = str_replace('\\', '/', $upload_path);
  962. $upload_path = str_replace('//', '/', $upload_path);
  963. if ($upload_path == '/') {
  964. $upload_path='';
  965. } elseif (!empty($upload_path) && $upload_path[0] != '/') {
  966. $upload_path="/$upload_path";
  967. }
  968. $endchar = substr($filename, strlen($filename) - 1, 1);
  969. if ($endchar == '/') {
  970. $filename = substr($filename, 0, -1);
  971. }
  972. // $dbTable already has backticks!
  973. //$query = "select count(*) as bestaat from $dbTable where path='$upload_path/$filename'";
  974. $query = "select count(*) as bestaat from $dbTable where path='$upload_path/$filename'";
  975. $result = Database::query($query);
  976. $row = Database::fetch_array($result);
  977. if ($row['bestaat'] > 0) {
  978. //$query = "update $dbTable set path='$upload_path/$filename',visibility='$default_visibility', filetype='$filetype' where path='$upload_path/$filename'";
  979. $query = "UPDATE $dbTable SET path='$upload_path/$filename',visibility='$default_visibility', filetype='$filetype' where path='$upload_path/$filename'";
  980. } else {
  981. //$query = "INSERT INTO $dbTable (path,visibility,filetype) VALUES('$upload_path/$filename','$default_visibility','$filetype')";
  982. $query = "INSERT INTO $dbTable (path,visibility,filetype) VALUES('$upload_path/$filename','$default_visibility','$filetype')";
  983. }
  984. Database::query($query);
  985. }
  986. /**
  987. * Retrieves the image path list in a html file
  988. *
  989. * @author Hugues Peeters <hugues.peeters@claroline.net>
  990. * @param string $html_file
  991. * @return array - images path list
  992. */
  993. function search_img_from_html($html_file) {
  994. $img_path_list = array();
  995. if (!$fp = fopen($html_file, 'r')) {
  996. return ;
  997. }
  998. // Aearch and store occurences of the <img> tag in an array
  999. $size_file = (filesize($html_file) === 0) ? 1 : filesize($html_file);
  1000. if (isset($fp) && $fp !== false) {
  1001. $buffer = fread($fp, $size_file);
  1002. if (strlen($buffer) >= 0 && $buffer !== false) {
  1003. //
  1004. } else {
  1005. die('<center>Can not read file.</center>');
  1006. }
  1007. } else {
  1008. die('<center>Can not read file.</center>');
  1009. }
  1010. $matches = array();
  1011. if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
  1012. $img_tag_list = $matches[0];
  1013. }
  1014. fclose ($fp);
  1015. unset($buffer);
  1016. // Search the image file path from all the <IMG> tag detected
  1017. if (sizeof($img_tag_list) > 0) {
  1018. foreach ($img_tag_list as & $this_img_tag) {
  1019. if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
  1020. $img_path_list[] = $matches[1];
  1021. }
  1022. }
  1023. $img_path_list = array_unique($img_path_list); // Remove duplicate entries
  1024. }
  1025. return $img_path_list;
  1026. }
  1027. /**
  1028. * Creates a new directory trying to find a directory name
  1029. * that doesn't already exist
  1030. * (we could use unique_name() here...)
  1031. *
  1032. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1033. * @author Bert Vanderkimpen
  1034. * @param array $_course current course information
  1035. * @param int $user_id current user id
  1036. * @param string $desiredDirName complete path of the desired name
  1037. * @param string The visible name of the directory
  1038. * @param int Visibility (0 for invisible, 1 for visible, 2 for deleted)
  1039. * @return string actual directory name if it succeeds,
  1040. * boolean false otherwise
  1041. */
  1042. function create_unexisting_directory(
  1043. $_course,
  1044. $user_id,
  1045. $session_id,
  1046. $to_group_id,
  1047. $to_user_id,
  1048. $base_work_dir,
  1049. $desired_dir_name,
  1050. $title = null,
  1051. $visibility = null
  1052. ) {
  1053. $nb = '';
  1054. // add numerical suffix to directory if another one of the same number already exists
  1055. while (file_exists($base_work_dir.$desired_dir_name.$nb)) {
  1056. $nb += 1;
  1057. }
  1058. if ($title == null) {
  1059. $title = basename($desired_dir_name);
  1060. }
  1061. $course_id = $_course['real_id'];
  1062. if (mkdir($base_work_dir.$desired_dir_name.$nb, api_get_permissions_for_new_directories(), true)) {
  1063. // Check if pathname already exists inside document table
  1064. $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
  1065. $sql = "SELECT path FROM $tbl_document WHERE c_id = $course_id AND path='".$desired_dir_name.$nb."'";
  1066. $rs = Database::query($sql);
  1067. if (Database::num_rows($rs) == 0) {
  1068. $document_id = add_document($_course, $desired_dir_name.$nb, 'folder', 0, $title, null, 0, true, $to_group_id);
  1069. if ($document_id) {
  1070. // Update document item_property
  1071. if ($visibility !== '') {
  1072. $visibilities = array(0 => 'invisible', 1 => 'visible', 2 => 'delete');
  1073. api_item_property_update(
  1074. $_course,
  1075. TOOL_DOCUMENT,
  1076. $document_id,
  1077. $visibilities[$visibility],
  1078. $user_id,
  1079. $to_group_id,
  1080. $to_user_id,
  1081. null,
  1082. null,
  1083. $session_id
  1084. );
  1085. } else {
  1086. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'FolderCreated', $user_id, $to_group_id, $to_user_id, null, null, $session_id);
  1087. }
  1088. return $desired_dir_name.$nb;
  1089. }
  1090. } else {
  1091. //This means the folder NOT exist in the filesystem (now this was created) but there is a record in the Database
  1092. return $desired_dir_name.$nb;
  1093. }
  1094. } else {
  1095. return false;
  1096. }
  1097. }
  1098. /**
  1099. * Handles uploaded missing images
  1100. *
  1101. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1102. * @author Bert Vanderkimpen
  1103. * @param array $_course
  1104. * @param array $uploaded_file_collection - follows the $_FILES Structure
  1105. * @param string $base_work_dir
  1106. * @param string $missing_files_dir
  1107. * @param int $user_id
  1108. * @param int $max_filled_space
  1109. */
  1110. function move_uploaded_file_collection_into_directory($_course, $uploaded_file_collection, $base_work_dir, $missing_files_dir, $user_id, $to_group_id, $to_user_id, $max_filled_space) {
  1111. $number_of_uploaded_images = count($uploaded_file_collection['name']);
  1112. for ($i = 0; $i < $number_of_uploaded_images; $i++) {
  1113. $missing_file['name'] = $uploaded_file_collection['name'][$i];
  1114. $missing_file['type'] = $uploaded_file_collection['type'][$i];
  1115. $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
  1116. $missing_file['error'] = $uploaded_file_collection['error'][$i];
  1117. $missing_file['size'] = $uploaded_file_collection['size'][$i];
  1118. $upload_ok = process_uploaded_file($missing_file);
  1119. if ($upload_ok) {
  1120. $new_file_list[] = handle_uploaded_document($_course, $missing_file, $base_work_dir, $missing_files_dir, $user_id, $to_group_id, $to_user_id, $max_filled_space, 0, 'overwrite');
  1121. }
  1122. unset($missing_file);
  1123. }
  1124. return $new_file_list;
  1125. }
  1126. /**
  1127. * Opens the old html file and replace the src path into the img tag
  1128. * This also works for files in subdirectories.
  1129. * @param $original_img_path is an array
  1130. * @param $new_img_path is an array
  1131. */
  1132. function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file) {
  1133. global $_course;
  1134. // Open the file
  1135. $fp = fopen($html_file, 'r');
  1136. $buffer = fread($fp, filesize($html_file));
  1137. $new_html_content = '';
  1138. // Fix the image tags
  1139. for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb ; $i++) {
  1140. $replace_what = $original_img_path[$i];
  1141. // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
  1142. $exploded_file_path = explode('/', $new_img_path[$i]);
  1143. $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
  1144. //$message .= "Element [$i] <b>" . $replace_what . "</b> replaced by <b>" . $replace_by . "</b><br />"; //debug
  1145. //api_display_debug_info($message);
  1146. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1147. }
  1148. $new_html_content .= $buffer;
  1149. @fclose($fp);
  1150. // Write the resulted new file
  1151. if (!$fp = fopen($html_file, 'w')) {
  1152. return;
  1153. }
  1154. if (!fwrite($fp, $new_html_content)) {
  1155. return;
  1156. }
  1157. }
  1158. /**
  1159. * Creates a file containing an html redirection to a given url
  1160. *
  1161. * @author Hugues Peeters <hugues.peeters@claroline.net>
  1162. * @param string $file_path
  1163. * @param string $url
  1164. * @return void
  1165. */
  1166. function create_link_file($file_path, $url) {
  1167. $file_content = '<html>'
  1168. .'<head>'
  1169. .'<meta http-equiv="refresh" content="1;url='.$url.'">'
  1170. .'</head>'
  1171. .'<body>'
  1172. .'</body>'
  1173. .'</html>';
  1174. if (file_exists($file_path)) {
  1175. if (!($fp = fopen ($file_path, 'w'))) {
  1176. return false;
  1177. }
  1178. return fwrite($fp, $file_content);
  1179. }
  1180. }
  1181. /**
  1182. * Opens html file $full_file_name;
  1183. * Parses the hyperlinks; and
  1184. * Writes the result back in the html file.
  1185. *
  1186. * @author Roan Embrechts
  1187. * @version 0.1
  1188. */
  1189. function api_replace_links_in_html($upload_path, $full_file_name) {
  1190. // Open the file
  1191. if (file_exists($full_file_name)) {
  1192. $fp = fopen($full_file_name, 'r');
  1193. $buffer = fread ($fp, filesize ($full_file_name));
  1194. // Parse the contents
  1195. $new_html_content = api_replace_links_in_string($upload_path, $buffer);
  1196. // Write the result
  1197. $fp = fopen($full_file_name, 'w');
  1198. fwrite($fp, $new_html_content);
  1199. }
  1200. }
  1201. /**
  1202. deprecated: use api_replace_parameter instead
  1203. Parse the buffer string provided as parameter
  1204. Replace the a href tags so they are displayed correctly.
  1205. - works for files in root and subdirectories
  1206. - replace relative hyperlinks to use showinframes.php?file= ...
  1207. - add target="_self" to all absolute hyperlinks
  1208. - leave local anchors untouched (e.g. #CHAPTER1)
  1209. - leave links with download.php and showinframes.php untouched
  1210. @author Roan Embrechts
  1211. @version 0.6
  1212. */
  1213. function api_replace_links_in_string($upload_path, $buffer) {
  1214. // Search for hyperlinks
  1215. $matches = array();
  1216. if (preg_match_all('/<a[\s]*href[^<]*>/i', $buffer, $matches)) {
  1217. $tag_list = $matches[0];
  1218. }
  1219. // Search the filepath of all detected <a href> tags
  1220. if (sizeof($tag_list) > 0) {
  1221. $file_path_list = array();
  1222. $href_list = array();
  1223. foreach ($tag_list as & $this_tag) {
  1224. /* Match case insensitive, the stuff between the two ~ :
  1225. a href = <exactly one quote><one or more non-quotes><exactly one ">
  1226. e.g. a href="www.google.be", A HREF = "info.html"
  1227. to match ["] escape the " or else PHP interprets it
  1228. [\"]{1} --> matches exactly one "
  1229. + 1 or more (like * is 0 or more)
  1230. [\s]* matches whitespace
  1231. $matches contains captured subpatterns
  1232. the only one here is ([^\"]+) --> matches[1]
  1233. */
  1234. if (preg_match("~a href[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
  1235. $file_path_list[] = $matches[1]; // older
  1236. $href_list[] = $matches[0]; // to also add target="_self"
  1237. }
  1238. }
  1239. }
  1240. // Replace the original hyperlinks by the correct ones
  1241. for ($count = 0; $count < sizeof($href_list); $count++) {
  1242. $replace_what[$count] = $href_list[$count];
  1243. $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
  1244. $is_local_anchor = strpos($replace_what[$count], "#");
  1245. if (!$is_absolute_hyperlink && !$is_local_anchor) {
  1246. // This is a relative hyperlink
  1247. if ((strpos($replace_what[$count], 'showinframes.php') === false) && (strpos($replace_what[$count], 'download.php') === false)) {
  1248. // Fix the link to use showinframes.php
  1249. $replace_by[$count] = 'a href = "showinframes.php?file='.$upload_path.'/'.$file_path_list[$count].'" target="_self"';
  1250. } else {
  1251. // URL has been already fixed, leave it as is
  1252. $replace_by[$count] = $replace_what[$count];
  1253. }
  1254. } elseif ($is_absolute_hyperlink) {
  1255. $replace_by[$count] = 'a href="'.$file_path_list[$count].'" target ="_self"';
  1256. } else {
  1257. // Don't change anything
  1258. $replace_by[$count] = $replace_what[$count];
  1259. }
  1260. //Display::display_normal_message('Link replaced by ' . $replace_by[$count]); // debug
  1261. }
  1262. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1263. return $buffer;
  1264. }
  1265. /**
  1266. EXPERIMENTAL - function seems to work, needs more testing
  1267. @param $upload_path is the path where the document is stored, like "/archive/"
  1268. if it is the root level, the function expects "/"
  1269. otherwise "/path/"
  1270. This function parses all tags with $param_name parameters.
  1271. so the tags are displayed correctly.
  1272. --------------
  1273. Algorithm v1.0
  1274. --------------
  1275. given a string and a parameter,
  1276. * OK find all tags in that string with the specified parameter (like href or src)
  1277. * OK for every one of these tags, find the src|href|... part to edit it
  1278. * OK change the src|href|... part to use download.php (or showinframes.php)
  1279. * OK do some special stuff for hyperlinks
  1280. Exceptions
  1281. * OK if download.php or showinframes.php is already in the tag, leave it alone
  1282. * OK if mailto is in the tag, leave it alone
  1283. * OK if the src|href param contains http://, it's absolute --> leave it alone
  1284. Special for hyperlinks (a href...)
  1285. * OK add target="_self"
  1286. * OK use showinframes.php instead of download.php
  1287. @author Roan Embrechts
  1288. @version 1.1
  1289. */
  1290. function api_replace_parameter($upload_path, $buffer, $param_name = 'src') {
  1291. // Search for tags with $param_name as a parameter
  1292. /*
  1293. // [\s]* matches whitespace
  1294. // [\"=a-z] matches ", = and a-z
  1295. // ([\s]*[a-z]*)* matches all whitespace and normal alphabet
  1296. // characters a-z combinations but seems too slow
  1297. // perhaps ([\s]*[a-z]*) a maximum number of times ?
  1298. // [\s]*[a-z]*[\s]* matches many tags
  1299. // the ending "i" means to match case insensitive (a matches a and A)
  1300. */
  1301. $matches = array();
  1302. if (preg_match_all('/<[a-z]+[^<]*'.$param_name.'[^<]*>/i', $buffer, $matches)) {
  1303. $tag_list = $matches[0];
  1304. }
  1305. // Search the filepath of parameter $param_name in all detected tags
  1306. if (sizeof($tag_list) > 0) {
  1307. $file_path_list = array();
  1308. $href_list = array();
  1309. foreach ($tag_list as & $this_tag) {
  1310. //Display::display_normal_message(htmlentities($this_tag)); //debug
  1311. if ( preg_match("~".$param_name."[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
  1312. $file_path_list[] = $matches[1]; // older
  1313. $href_list[] = $matches[0]; // to also add target="_self"
  1314. }
  1315. }
  1316. }
  1317. // Replace the original tags by the correct ones
  1318. for ($count = 0; $count < sizeof($href_list); $count++) {
  1319. $replace_what[$count] = $href_list[$count];
  1320. $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
  1321. $is_local_anchor = strpos($replace_what[$count], '#');
  1322. if (!$is_absolute_hyperlink && !$is_local_anchor) {
  1323. if ((strpos($replace_what[$count], 'showinframes.php') === false)
  1324. && (strpos($replace_what[$count], 'download.php') === false)
  1325. && (strpos($replace_what[$count], 'mailto') === false)) {
  1326. // Fix the link to use download.php or showinframes.php
  1327. if (preg_match("/<a([\s]*[\"\/:'=a-z0-9]*){5}href[^<]*>/i", $tag_list[$count])) {
  1328. $replace_by[$count] = " $param_name =\"showinframes.php?file=" . $upload_path.$file_path_list[$count]."\" target=\"_self\" ";
  1329. } else {
  1330. $replace_by[$count] = " $param_name =\"download.php?doc_url=" . $upload_path.$file_path_list[$count]."\" ";
  1331. }
  1332. } else {
  1333. // "mailto" or url already fixed, leave as is
  1334. //$message .= "Already fixed or contains mailto: ";
  1335. $replace_by[$count] = $replace_what[$count];
  1336. }
  1337. } elseif ($is_absolute_hyperlink) {
  1338. //$message .= "Absolute hyperlink, don't change, add target=_self: ";
  1339. $replace_by[$count] = " $param_name=\"" . $file_path_list[$count] . "\" target =\"_self\"";
  1340. } else {
  1341. // Don't change anything
  1342. //$message .= "Local anchor, don't change: ";
  1343. $replace_by[$count] = $replace_what[$count];
  1344. }
  1345. //$message .= "In tag $count, <b>" . htmlentities($tag_list[$count])
  1346. // . "</b>, parameter <b>" . $replace_what[$count] . "</b> replaced by <b>" . $replace_by[$count] . "</b><br>"; //debug
  1347. }
  1348. //if ($message) api_display_debug_info($message); //debug
  1349. $buffer = str_replace($replace_what, $replace_by, $buffer);
  1350. return $buffer;
  1351. }
  1352. /**
  1353. * Checks the extension of a file, if it's .htm or .html
  1354. * we use search_img_from_html to get all image paths in the file
  1355. *
  1356. * @param string $file
  1357. * @return array paths
  1358. * @see check_for_missing_files() uses search_img_from_html()
  1359. */
  1360. function check_for_missing_files($file) {
  1361. if (strrchr($file, '.') == '.htm' || strrchr($file, '.') == '.html') {
  1362. $img_file_path = search_img_from_html($file);
  1363. return $img_file_path;
  1364. }
  1365. return false;
  1366. }
  1367. /**
  1368. * This function builds a form that asks for the missing images in a html file
  1369. * maybe we should do this another way?
  1370. *
  1371. * @param array $missing_files
  1372. * @param string $upload_path
  1373. * @param string $file_name
  1374. * @return string the form
  1375. */
  1376. function build_missing_files_form($missing_files, $upload_path, $file_name) {
  1377. // Do we need a / or not?
  1378. $added_slash = ($upload_path == '/') ? '' : '/';
  1379. $folder_id = DocumentManager::get_document_id(api_get_course_info(), $upload_path);
  1380. // Build the form
  1381. $form = "<p><strong>".get_lang('MissingImagesDetected')."</strong></p>"
  1382. ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">"
  1383. // Related_file is the path to the file that has missing images
  1384. ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />"
  1385. ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />"
  1386. ."<input type=\"hidden\" name=\"id\" value=\"".$folder_id."\" />"
  1387. ."<table border=\"0\">";
  1388. foreach ($missing_files as & $this_img_file_path) {
  1389. $form .= "<tr>"
  1390. ."<td>".basename($this_img_file_path)." : </td>"
  1391. ."<td>"
  1392. ."<input type=\"file\" name=\"img_file[]\"/>"
  1393. ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
  1394. ."</td>"
  1395. ."</tr>";
  1396. }
  1397. $form .= "</table>"
  1398. ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
  1399. ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Ok')."\" class=\"save\">".get_lang('Ok')."</button>"
  1400. ."</form>";
  1401. return $form;
  1402. }
  1403. /**
  1404. * This recursive function can be used during the upgrade process form older versions of Chamilo
  1405. * It crawls the given directory, checks if the file is in the DB and adds it if it's not
  1406. *
  1407. * @param string $base_work_dir
  1408. * @param string $current_path, needed for recursivity
  1409. */
  1410. function add_all_documents_in_folder_to_database($_course, $user_id, $base_work_dir, $current_path = '', $to_group_id = 0) {
  1411. $current_session_id = api_get_session_id();
  1412. $path = $base_work_dir.$current_path;
  1413. // Open dir
  1414. $handle = opendir($path);
  1415. if (is_dir($path)) {
  1416. // Run trough
  1417. while ($file = readdir($handle)) {
  1418. if ($file == '.' || $file == '..') continue;
  1419. $completepath = "$path/$file";
  1420. // Directory?
  1421. if (is_dir($completepath)) {
  1422. $title = get_document_title($file);
  1423. $safe_file = replace_dangerous_char($file);
  1424. @rename($path.'/'.$file, $path.'/'.$safe_file);
  1425. // If we can't find the file, add it
  1426. if (!DocumentManager::get_document_id($_course, $current_path.'/'.$safe_file)) {
  1427. $document_id = add_document($_course, $current_path.'/'.$safe_file, 'folder', 0, $title);
  1428. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, null, null, null, $current_session_id);
  1429. //echo $current_path.'/'.$safe_file.' added!<br />';
  1430. }
  1431. // Recursive
  1432. add_all_documents_in_folder_to_database($_course,$user_id,$base_work_dir,$current_path.'/'.$safe_file, $to_group_id);
  1433. } else {
  1434. //Rename
  1435. $safe_file = disable_dangerous_file(replace_dangerous_char($file, 'strict'));
  1436. @rename($base_work_dir.$current_path.'/'.$file, $base_work_dir.$current_path.'/'.$safe_file);
  1437. $document_id = DocumentManager::get_document_id($_course, $current_path.'/'.$safe_file);
  1438. if (!$document_id) {
  1439. $title = get_document_title($file);
  1440. $size = filesize($base_work_dir.$current_path.'/'.$safe_file);
  1441. $document_id = add_document($_course, $current_path.'/'.$safe_file, 'file', $size, $title);
  1442. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', $user_id, $to_group_id, null, null, null, $current_session_id);
  1443. //echo $current_path.'/'.$safe_file.' added!<br />';
  1444. } else {
  1445. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentUpdated', $user_id, $to_group_id, null, null, null, $current_session_id);
  1446. }
  1447. }
  1448. }
  1449. }
  1450. }