Attachments.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. <?php
  2. /**
  3. * This file handles the uploading and creation of attachments
  4. * as well as the auto management of the attachment directories.
  5. *
  6. * Simple Machines Forum (SMF)
  7. *
  8. * @package SMF
  9. * @author Simple Machines
  10. *
  11. * @copyright 2012 Simple Machines
  12. * @license http://www.simplemachines.org/about/smf/license.php BSD
  13. *
  14. * @version 2.1 Alpha 1
  15. */
  16. if (!defined('SMF'))
  17. die('Hacking attempt...');
  18. function automanage_attachments_check_directory()
  19. {
  20. global $boarddir, $modSettings, $context;
  21. // Not pretty, but since we don't want folders created for every post. It'll do unless a better solution can be found.
  22. if (empty($modSettings['automanage_attachments']))
  23. return;
  24. elseif (!isset($_FILES) && !isset($doit))
  25. return;
  26. elseif (isset($_FILES))
  27. foreach ($_FILES['attachment']['tmp_name'] as $dummy)
  28. if (!empty($dummy))
  29. {
  30. $doit = true;
  31. break;
  32. }
  33. if (!isset($doit))
  34. return;
  35. $year = date('Y');
  36. $month = date('m');
  37. $day = date('d');
  38. $rand = md5(mt_rand());
  39. $rand1 = $rand[1];
  40. $rand = $rand[0];
  41. if (!empty($modSettings['attachment_basedirectories']) && !empty($modSettings['use_subdirectories_for_attachments']))
  42. {
  43. if (!is_array($modSettings['attachment_basedirectories']))
  44. $modSettings['attachment_basedirectories'] = unserialize($modSettings['attachment_basedirectories']);
  45. $base_dir = array_search($modSettings['basedirectory_for_attachments'], $modSettings['attachment_basedirectories']);
  46. }
  47. else
  48. $base_dir = 0;
  49. if ($modSettings['automanage_attachments'] == 1)
  50. {
  51. if (!isset($modSettings['last_attachments_directory']))
  52. $modSettings['last_attachments_directory'] = array();
  53. if (!is_array($modSettings['last_attachments_directory']))
  54. $modSettings['last_attachments_directory'] = unserialize($modSettings['last_attachments_directory']);
  55. if (!isset($modSettings['last_attachments_directory'][$base_dir]))
  56. $modSettings['last_attachments_directory'][$base_dir] = 0;
  57. }
  58. $basedirectory = (!empty($modSettings['use_subdirectories_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : $boarddir);
  59. //Just to be sure: I don't want directory separators at the end
  60. $sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR;
  61. $basedirectory = rtrim($basedirectory, $sep);
  62. switch ($modSettings['automanage_attachments'])
  63. {
  64. case 1:
  65. $updir = $basedirectory . DIRECTORY_SEPARATOR . 'attachments_' . (isset($modSettings['last_attachments_directory'][$base_dir]) ? $modSettings['last_attachments_directory'][$base_dir] : 0);
  66. break;
  67. case 2:
  68. $updir = $basedirectory . DIRECTORY_SEPARATOR . $year;
  69. break;
  70. case 3:
  71. $updir = $basedirectory . DIRECTORY_SEPARATOR . $year . DIRECTORY_SEPARATOR . $month;
  72. break;
  73. case 4:
  74. $updir = $basedirectory . DIRECTORY_SEPARATOR . (empty($modSettings['use_subdirectories_for_attachments']) ? 'attachments-' : 'random_') . $rand;
  75. break;
  76. case 5:
  77. $updir = $basedirectory . DIRECTORY_SEPARATOR . (empty($modSettings['use_subdirectories_for_attachments']) ? 'attachments-' : 'random_') . $rand . DIRECTORY_SEPARATOR . $rand1;
  78. break;
  79. default :
  80. $updir = '';
  81. }
  82. if (!is_array($modSettings['attachmentUploadDir']))
  83. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  84. if (!in_array($updir, $modSettings['attachmentUploadDir']) && !empty($updir))
  85. $outputCreation = automanage_attachments_create_directory($updir);
  86. elseif (in_array($updir, $modSettings['attachmentUploadDir']))
  87. $outputCreation = true;
  88. if ($outputCreation)
  89. {
  90. $modSettings['currentAttachmentUploadDir'] = array_search($updir, $modSettings['attachmentUploadDir']);
  91. $context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
  92. updateSettings(array(
  93. 'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'],
  94. ));
  95. }
  96. return $outputCreation;
  97. }
  98. function automanage_attachments_create_directory($updir)
  99. {
  100. global $modSettings, $initial_error, $context, $boarddir;
  101. $tree = mama_get_directory_tree_elements($updir);
  102. $count = count($tree);
  103. $directory = mama_init_dir($tree, $count);
  104. if ($directory === false)
  105. {
  106. // Maybe it's just the folder name
  107. $tree = mama_get_directory_tree_elements($boarddir . DIRECTORY_SEPARATOR . $updir);
  108. $count = count($tree);
  109. $directory = mama_init_dir($tree, $count);
  110. if ($directory === false)
  111. return false;
  112. }
  113. $directory .= DIRECTORY_SEPARATOR . array_shift($tree);
  114. while (!is_dir($directory) || $count != -1)
  115. {
  116. if (!is_dir($directory))
  117. {
  118. if (!@mkdir($directory,0755))
  119. {
  120. $context['dir_creation_error'] = 'attachments_no_create';
  121. return false;
  122. }
  123. }
  124. $directory .= DIRECTORY_SEPARATOR . array_shift($tree);
  125. $count--;
  126. }
  127. if (!is_writable($directory))
  128. {
  129. chmod($directory, 0755);
  130. if (!is_writable($directory))
  131. {
  132. chmod($directory, 0775);
  133. if (!is_writable($directory))
  134. {
  135. chmod($directory, 0777);
  136. if (!is_writable($directory))
  137. {
  138. $context['dir_creation_error'] = 'attachments_no_write';
  139. return false;
  140. }
  141. }
  142. }
  143. }
  144. // Everything seems fine...let's create the .htaccess
  145. if (!file_exists($directory . DIRECTORY_SEPARATOR . '.htaccess'))
  146. secureDirectory($updir, true);
  147. $sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR;
  148. $updir = rtrim($updir, $sep);
  149. // Only update if it's a new directory
  150. if (!in_array($updir, $modSettings['attachmentUploadDir']))
  151. {
  152. $modSettings['currentAttachmentUploadDir'] = max(array_keys($modSettings['attachmentUploadDir'])) +1;
  153. $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] = $updir;
  154. updateSettings(array(
  155. 'attachmentUploadDir' => serialize($modSettings['attachmentUploadDir']),
  156. 'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'],
  157. ), true);
  158. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  159. }
  160. $context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
  161. return true;
  162. }
  163. function automanage_attachments_by_space()
  164. {
  165. global $modSettings, $boarddir, $context;
  166. if (!isset($modSettings['automanage_attachments']) || (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] != 1))
  167. return;
  168. $basedirectory = (!empty($modSettings['use_subdirectories_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : $boarddir);
  169. //Just to be sure: I don't want directory separators at the end
  170. $sep = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? '\/' : DIRECTORY_SEPARATOR;
  171. $basedirectory = rtrim($basedirectory, $sep);
  172. // Get the current base directory
  173. if (!empty($modSettings['use_subdirectories_for_attachments']) && !empty($modSettings['attachment_basedirectories']))
  174. {
  175. $base_dir = array_search($modSettings['basedirectory_for_attachments'], $modSettings['attachment_basedirectories']);
  176. $base_dir = !empty($modSettings['automanage_attachments']) ? $base_dir : 0;
  177. }
  178. else
  179. $base_dir = 0;
  180. // Get the last attachment directory for that base directory
  181. if (empty($modSettings['last_attachments_directory'][$base_dir]))
  182. $modSettings['last_attachments_directory'][$base_dir] = 0;
  183. // And increment it.
  184. $modSettings['last_attachments_directory'][$base_dir]++;
  185. $updir = $basedirectory . DIRECTORY_SEPARATOR . 'attachments_' . $modSettings['last_attachments_directory'][$base_dir];
  186. if (automanage_attachments_create_directory($updir))
  187. {
  188. $modSettings['currentAttachmentUploadDir'] = array_search($updir, $modSettings['attachmentUploadDir']);
  189. updateSettings(array(
  190. 'last_attachments_directory' => serialize($modSettings['last_attachments_directory']),
  191. 'currentAttachmentUploadDir' => $modSettings['currentAttachmentUploadDir'],
  192. ));
  193. $modSettings['last_attachments_directory'] = unserialize($modSettings['last_attachments_directory']);
  194. return true;
  195. }
  196. else
  197. return false;
  198. }
  199. function mama_get_directory_tree_elements ($directory)
  200. {
  201. /*
  202. In Windows server both \ and / can be used as directory separators in paths
  203. In Linux (and presumably *nix) servers \ can be part of the name
  204. So for this reasons:
  205. * in Windows we need to explode for both \ and /
  206. * while in linux should be safe to explode only for / (aka DIRECTORY_SEPARATOR)
  207. */
  208. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
  209. $tree = preg_split('#[\\\/]#', $directory);
  210. else
  211. {
  212. if (substr($directory, 0, 1)!=DIRECTORY_SEPARATOR)
  213. return false;
  214. $tree = explode(DIRECTORY_SEPARATOR, trim($directory,DIRECTORY_SEPARATOR));
  215. }
  216. return $tree;
  217. }
  218. function mama_init_dir (&$tree, &$count)
  219. {
  220. $directory = '';
  221. // If on Windows servers the first part of the path is the drive (e.g. "C:")
  222. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
  223. {
  224. //Better be sure that the first part of the path is actually a drive letter...
  225. //...even if, I should check this in the admin page...isn't it?
  226. //...NHAAA Let's leave space for users' complains! :P
  227. if (preg_match('/^[a-z]:$/i',$tree[0]))
  228. $directory = array_shift($tree);
  229. else
  230. return false;
  231. $count--;
  232. }
  233. return $directory;
  234. }
  235. function processAttachments()
  236. {
  237. global $context, $modSettings, $smcFunc, $txt, $user_info;
  238. // Make sure we're uploading to the right place.
  239. if (!empty($modSettings['automanage_attachments']))
  240. automanage_attachments_check_directory();
  241. if (!is_array($modSettings['attachmentUploadDir']))
  242. $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
  243. $context['attach_dir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
  244. // Is the attachments folder actualy there?
  245. if (!empty($context['dir_creation_error']))
  246. $initial_error = $context['dir_creation_error'];
  247. elseif (!is_dir($context['attach_dir']))
  248. {
  249. $initial_error = 'attach_folder_warning';
  250. log_error(sprintf($txt['attach_folder_admin_warning'], $context['attach_dir']), 'critical');
  251. }
  252. if (!isset($initial_error) && !isset($context['attachments']))
  253. {
  254. // If this isn't a new post, check the current attachments.
  255. if (isset($_REQUEST['msg']))
  256. {
  257. $request = $smcFunc['db_query']('', '
  258. SELECT COUNT(*), SUM(size)
  259. FROM {db_prefix}attachments
  260. WHERE id_msg = {int:id_msg}
  261. AND attachment_type = {int:attachment_type}',
  262. array(
  263. 'id_msg' => (int) $_REQUEST['msg'],
  264. 'attachment_type' => 0,
  265. )
  266. );
  267. list ($context['attachments']['quantity'], $context['attachments']['total_size']) = $smcFunc['db_fetch_row']($request);
  268. $smcFunc['db_free_result']($request);
  269. }
  270. else
  271. $context['attachments'] = array(
  272. 'quantity' => 0,
  273. 'total_size' => 0,
  274. );
  275. }
  276. // Hmm. There are still files in session.
  277. $ignore_temp = false;
  278. if (!empty($_SESSION['temp_attachments']['post']['files']) && count($_SESSION['temp_attachments']) > 1)
  279. {
  280. // Let's try to keep them. But...
  281. $ignore_temp = true;
  282. // If new files are being added. We can't ignore those
  283. foreach ($_FILES['attachment']['tmp_name'] as $dummy)
  284. if (!empty($dummy))
  285. {
  286. $ignore_temp = false;
  287. break;
  288. }
  289. // Need to make space for the new files. So, bye bye.
  290. if (!$ignore_temp)
  291. {
  292. foreach ($_SESSION['temp_attachments'] as $attachID => $attachment)
  293. if (strpos($attachID, 'post_tmp_' . $user_info['id']) !== false)
  294. unlink($attachment['tmp_name']);
  295. $context['we_are_history'] = $txt['error_temp_attachments_flushed'];
  296. $_SESSION['temp_attachments'] = array();
  297. }
  298. }
  299. if (!isset($_FILES['attachment']['name']))
  300. $_FILES['attachment']['tmp_name'] = array();
  301. if (!isset($_SESSION['temp_attachments']))
  302. $_SESSION['temp_attachments'] = array();
  303. // Remember where we are at. If it's anywhere at all.
  304. if (!$ignore_temp)
  305. $_SESSION['temp_attachments']['post'] = array(
  306. 'msg' => !empty($_REQUEST['msg']) ? $_REQUEST['msg'] : 0,
  307. 'last_msg' => !empty($_REQUEST['last_msg']) ? $_REQUEST['last_msg'] : 0,
  308. 'topic' => !empty($topic) ? $topic : 0,
  309. 'board' => !empty($board) ? $board : 0,
  310. );
  311. // If we have an itital error, lets just display it.
  312. if (!empty($initial_error))
  313. {
  314. $_SESSION['temp_attachments']['initial_error'] = $initial_error;
  315. // And delete the files 'cos they ain't going nowhere.
  316. foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy)
  317. if (file_exists($_FILES['attachment']['tmp_name'][$n]))
  318. unlink($_FILES['attachment']['tmp_name'][$n]);
  319. $_FILES['attachment']['tmp_name'] = array();
  320. }
  321. // Loop through $_FILES['attachment'] array and move each file to the current attachments folder.
  322. foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy)
  323. {
  324. if ($_FILES['attachment']['name'][$n] == '')
  325. continue;
  326. // First, let's first check for PHP upload errors.
  327. $errors = array();
  328. if (!empty($_FILES['attachment']['error'][$n]))
  329. {
  330. if ($_FILES['attachment']['error'][$n] == 2)
  331. $errors[] = array('file_too_big', array($modSettings['attachmentSizeLimit']));
  332. elseif ($_FILES['attachment']['error'][$n] == 6)
  333. log_error($_FILES['attachment']['name'][$n] . ': ' . $txt['php_upload_error_6'], 'critical');
  334. else
  335. log_error($_FILES['attachment']['name'][$n] . ': ' . $txt['php_upload_error_' . $_FILES['attachment']['error'][$n]]);
  336. if (empty($errors))
  337. $errors[] = 'attach_php_error';
  338. }
  339. // Try to move and rename the file before doing any more checks on it.
  340. $attachID = 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand());
  341. $destName = $context['attach_dir'] . '/' . $attachID;
  342. if (empty($errors))
  343. {
  344. $_SESSION['temp_attachments'][$attachID] = array(
  345. 'name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])),
  346. 'tmp_name' => $destName,
  347. 'size' => $_FILES['attachment']['size'][$n],
  348. 'type' => $_FILES['attachment']['type'][$n],
  349. 'id_folder' => $modSettings['currentAttachmentUploadDir'],
  350. 'errors' => array(),
  351. );
  352. // Move the file to the attachments folder with a temp name for now.
  353. if (@move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName))
  354. @chmod($destName, 0644);
  355. else
  356. {
  357. $_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_timeout';
  358. if (file_exists($_FILES['attachment']['tmp_name'][$n]))
  359. unlink($_FILES['attachment']['tmp_name'][$n]);
  360. }
  361. }
  362. else
  363. {
  364. $_SESSION['temp_attachments'][$attachID] = array(
  365. 'name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])),
  366. 'tmp_name' => $destName,
  367. 'errors' => $errors,
  368. );
  369. if (file_exists($_FILES['attachment']['tmp_name'][$n]))
  370. unlink($_FILES['attachment']['tmp_name'][$n]);
  371. }
  372. // If there's no errors to this pont. We still do need to apply some addtional checks before we are finished.
  373. if (empty($_SESSION['temp_attachments'][$attachID]['errors']))
  374. attachmentChecks($attachID);
  375. }
  376. // Mod authors, finally a hook to hang an alternate attachment upload system upon
  377. // Upload to the current attachment folder with the file name $attachID or 'post_tmp_' . $user_info['id'] . '_' . md5(mt_rand())
  378. // Populate $_SESSION['temp_attachments'][$attachID] with the following:
  379. // name => The file name
  380. // tmp_name => Path to the temp file ($context['attach_dir'] . '/' . $attachID).
  381. // size => File size (required).
  382. // type => MIME type (optional if not available on upload).
  383. // id_folder => $modSettings['currentAttachmentUploadDir']
  384. // errors => An array of errors (use the index of the $txt variable for that error).
  385. // Template changes can be done using "integrate_upload_template".
  386. call_integration_hook('integrate_attachment_upload', array());
  387. }
  388. /**
  389. * Performs various checks on an uploaded file.
  390. * - Requires that $_SESSION['temp_attachments'][$attachID] be properly populated.
  391. *
  392. * @param $attachID
  393. */
  394. function attachmentChecks($attachID)
  395. {
  396. global $modSettings, $context, $sourcedir, $smcFunc;
  397. // No data or missing data .... Not necessarily needed, but in case a mod author missed something.
  398. if ( empty($_SESSION['temp_attachments'][$attachID]))
  399. $errror = '$_SESSION[\'temp_attachments\'][$attachID]';
  400. elseif (empty($attachID))
  401. $errror = '$attachID';
  402. elseif (empty($context['attachments']))
  403. $errror = '$context[\'attachments\']';
  404. elseif (empty($context['attach_dir']))
  405. $errror = '$context[\'attach_dir\']';
  406. // Let's get their attention.
  407. if (!empty($error))
  408. fatal_lang_error('attach_check_nag', 'debug', array($error));
  409. // These are the only valid image types for SMF.
  410. $validImageTypes = array(
  411. 1 => 'gif',
  412. 2 => 'jpeg',
  413. 3 => 'png',
  414. 5 => 'psd',
  415. 6 => 'bmp',
  416. 7 => 'tiff',
  417. 8 => 'tiff',
  418. 9 => 'jpeg',
  419. 14 => 'iff'
  420. );
  421. // Just in case this slipped by the first checks, we stop it here and now
  422. if ($_SESSION['temp_attachments'][$attachID]['size'] == 0)
  423. {
  424. $_SESSION['temp_attachments'][$attachID]['errors'][] = 'attach_0_byte_file';
  425. return false;
  426. }
  427. // First, the dreaded security check. Sorry folks, but this should't be avoided
  428. $size = @getimagesize($_SESSION['temp_attachments'][$attachID]['tmp_name']);
  429. if (isset($validImageTypes[$size[2]]))
  430. {
  431. require_once($sourcedir . '/Subs-Graphics.php');
  432. if (!checkImageContents($_SESSION['temp_attachments'][$attachID]['tmp_name'], !empty($modSettings['attachment_image_paranoid'])))
  433. {
  434. // It's bad. Last chance, maybe we can re-encode it?
  435. if (empty($modSettings['attachment_image_reencode']) || (!reencodeImage($_SESSION['temp_attachments'][$attachID]['tmp_name'], $size[2])))
  436. {
  437. // Nothing to do: not allowed or not successful re-encoding it.
  438. $_SESSION['temp_attachments'][$attachID]['errors'][] = 'bad_attachment';
  439. return false;
  440. }
  441. // Success! However, successes usually come for a price:
  442. // we might get a new format for our image...
  443. $old_format = $size[2];
  444. $size = @getimagesize($attachmentOptions['tmp_name']);
  445. if (!(empty($size)) && ($size[2] != $old_format))
  446. {
  447. if (isset($validImageTypes[$size[2]]))
  448. $_SESSION['temp_attachments'][$attachID]['type'] = 'image/' . $validImageTypes[$size[2]];
  449. }
  450. }
  451. }
  452. // Is there room for this sucker?
  453. if (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit']))
  454. {
  455. // Check the folder size and count. If it hasn't been done already.
  456. if (empty($context['dir_size']) || empty($context['dir_files']))
  457. {
  458. $request = $smcFunc['db_query']('', '
  459. SELECT COUNT(*), SUM(size)
  460. FROM {db_prefix}attachments
  461. WHERE id_folder = {int:folder_id}',
  462. array(
  463. 'folder_id' => (empty($modSettings['currentAttachmentUploadDir']) ? 1 : $modSettings['currentAttachmentUploadDir']),
  464. )
  465. );
  466. list ($context['dir_files'], $context['dir_size']) = $smcFunc['db_fetch_row']($request);
  467. $smcFunc['db_free_result']($request);
  468. }
  469. $context['dir_size'] += $_SESSION['temp_attachments'][$attachID]['size'];
  470. $context['dir_files']++;
  471. // Are we about to run out of room? Let's notify the admin then.
  472. if (empty($modSettings['attachment_full_notified']) && empty($modSettings['attachmentDirSizeLimit']) && $modSettings['attachmentDirSizeLimit'] > 4000 && $context['dir_size'] > ($modSettings['attachmentDirSizeLimit'] - 2000) * 1024
  473. || (empty($modSettings['attachmentDirFileLimit']) && $modSettings['attachmentDirFileLimit'] * .95 < $context['dir_files'] && $modSettings['attachmentDirFileLimit'] > 500))
  474. {
  475. require_once($sourcedir . '/Subs-Admin.php');
  476. emailAdmins('admin_attachments_full');
  477. updateSettings(array('attachment_full_notified' => 1));
  478. }
  479. // // No room left.... What to do now???
  480. if (!empty($modSettings['attachmentDirFileLimit']) && $context['dir_files'] + 2 > $modSettings['attachmentDirFileLimit']
  481. || (!empty($modSettings['attachmentDirFileLimit']) && $context['dir_size'] > $modSettings['attachmentDirSizeLimit'] * 1024))
  482. {
  483. if (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] == 1)
  484. {
  485. // Move it to the new folder if we can.
  486. if (automanage_attachments_by_space())
  487. {
  488. rename($_SESSION['temp_attachments'][$attachID]['tmp_name'], $context['attach_dir'] . '/' . $attachID);
  489. $_SESSION['temp_attachments'][$attachID]['tmp_name'] = $context['attach_dir'] . '/' . $attachID;
  490. $_SESSION['temp_attachments'][$attachID]['id_folder'] = $modSettings['currentAttachmentUploadDir'];
  491. $context['dir_size'] = $_SESSION['temp_attachments'][$attachID]['size'];
  492. $context['dir_files'] = 1;
  493. }
  494. // Or, let the user know that it ain't gonna happen.
  495. else
  496. {
  497. if (isset($context['dir_creation_error']))
  498. $_SESSION['temp_attachments'][$attachID]['errors'][] = $context['dir_creation_error'];
  499. else
  500. $_SESSION['temp_attachments'][$attachID]['errors'][] = 'ran_out_of_space';
  501. }
  502. }
  503. else
  504. $_SESSION['temp_attachments'][$attachID]['errors'][] = 'ran_out_of_space';
  505. }
  506. }
  507. // Is the file too big?
  508. $context['attachments']['total_size'] += $_SESSION['temp_attachments'][$attachID]['size'];
  509. if (!empty($modSettings['attachmentSizeLimit']) && $_SESSION['temp_attachments'][$attachID]['size'] > $modSettings['attachmentSizeLimit'] * 1024)
  510. $_SESSION['temp_attachments'][$attachID]['errors'][] = array('file_too_big', array(comma_format($modSettings['attachmentSizeLimit'], 0)));
  511. // Check the total upload size for this post...
  512. if (!empty($modSettings['attachmentPostLimit']) && $context['attachments']['total_size'] > $modSettings['attachmentPostLimit'] * 1024)
  513. $_SESSION['temp_attachments'][$attachID]['errors'][] = array('attach_max_total_file_size', array(comma_format($modSettings['attachmentPostLimit'], 0), comma_format($modSettings['attachmentPostLimit'] - (($context['attachments']['total_size'] - $_SESSION['temp_attachments'][$attachID]['size']) / 1024), 0)));
  514. // Have we reached the maximum number of files we are allowed?
  515. $context['attachments']['quantity']++;
  516. // Set a max limit if none exists
  517. if (empty($modSettings['attachmentNumPerPostLimit']) && $context['attachments']['quantity'] >= 50)
  518. $modSettings['attachmentNumPerPostLimit'] = 50;
  519. if (!empty($modSettings['attachmentNumPerPostLimit']) && $context['attachments']['quantity'] > $modSettings['attachmentNumPerPostLimit'])
  520. $_SESSION['temp_attachments'][$attachID]['errors'][] = array('attachments_limit_per_post', array($modSettings['attachmentNumPerPostLimit']));
  521. // File extension check
  522. if (!empty($modSettings['attachmentCheckExtensions']))
  523. {
  524. $allowed = explode(',', strtolower($modSettings['attachmentExtensions']));
  525. foreach ($allowed as $k => $dummy)
  526. $allowed[$k] = trim($dummy);
  527. if (!in_array(strtolower(substr(strrchr($_SESSION['temp_attachments'][$attachID]['name'], '.'), 1)), $allowed))
  528. {
  529. $allowed_extensions = strtr(strtolower($modSettings['attachmentExtensions']), array(',' => ', '));
  530. $_SESSION['temp_attachments'][$attachID]['errors'][] = array('cant_upload_type', array($allowed_extensions));
  531. }
  532. }
  533. // Undo the math if there's an error
  534. if (!empty($_SESSION['temp_attachments'][$attachID]['errors']))
  535. {
  536. if (isset($context['dir_size']))
  537. $context['dir_size'] -= $_SESSION['temp_attachments'][$attachID]['size'];
  538. if (isset($context['dir_files']))
  539. $context['dir_files']--;
  540. $context['attachments']['total_size'] -= $_SESSION['temp_attachments'][$attachID]['size'];
  541. $context['attachments']['quantity']--;
  542. return false;
  543. }
  544. return true;
  545. }
  546. /**
  547. * Create an attachment, with the given array of parameters.
  548. * - Adds any addtional or missing parameters to $attachmentOptions.
  549. * - Renames the temporary file.
  550. * - Creates a thumbnail if the file is an image and the option enabled.
  551. *
  552. * @param array $attachmentOptions
  553. */
  554. function createAttachment(&$attachmentOptions)
  555. {
  556. global $modSettings, $sourcedir, $smcFunc, $context;
  557. global $txt, $boarddir;
  558. require_once($sourcedir . '/Subs-Graphics.php');
  559. // These are the only valid image types for SMF.
  560. $validImageTypes = array(
  561. 1 => 'gif',
  562. 2 => 'jpeg',
  563. 3 => 'png',
  564. 5 => 'psd',
  565. 6 => 'bmp',
  566. 7 => 'tiff',
  567. 8 => 'tiff',
  568. 9 => 'jpeg',
  569. 14 => 'iff'
  570. );
  571. // If this is an image we need to set a few additional parameters.
  572. $size = @getimagesize($attachmentOptions['tmp_name']);
  573. list ($attachmentOptions['width'], $attachmentOptions['height']) = $size;
  574. // If it's an image get the mime type right.
  575. if (empty($attachmentOptions['mime_type']) && $attachmentOptions['width'])
  576. {
  577. // Got a proper mime type?
  578. if (!empty($size['mime']))
  579. $attachmentOptions['mime_type'] = $size['mime'];
  580. // Otherwise a valid one?
  581. elseif (isset($validImageTypes[$size[2]]))
  582. $attachmentOptions['mime_type'] = 'image/' . $validImageTypes[$size[2]];
  583. }
  584. // Get the hash if no hash has been given yet.
  585. if (empty($attachmentOptions['file_hash']))
  586. $attachmentOptions['file_hash'] = getAttachmentFilename($attachmentOptions['name'], false, null, true);
  587. // Assuming no-one set the extension let's take a look at it.
  588. if (empty($attachmentOptions['fileext']))
  589. {
  590. $attachmentOptions['fileext'] = strtolower(strrpos($attachmentOptions['name'], '.') !== false ? substr($attachmentOptions['name'], strrpos($attachmentOptions['name'], '.') + 1) : '');
  591. if (strlen($attachmentOptions['fileext']) > 8 || '.' . $attachmentOptions['fileext'] == $attachmentOptions['name'])
  592. $attachmentOptions['fileext'] = '';
  593. }
  594. $smcFunc['db_insert']('',
  595. '{db_prefix}attachments',
  596. array(
  597. 'id_folder' => 'int', 'id_msg' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8',
  598. 'size' => 'int', 'width' => 'int', 'height' => 'int',
  599. 'mime_type' => 'string-20', 'approved' => 'int',
  600. ),
  601. array(
  602. (int) $attachmentOptions['id_folder'], (int) $attachmentOptions['post'], $attachmentOptions['name'], $attachmentOptions['file_hash'], $attachmentOptions['fileext'],
  603. (int) $attachmentOptions['size'], (empty($attachmentOptions['width']) ? 0 : (int) $attachmentOptions['width']), (empty($attachmentOptions['height']) ? '0' : (int) $attachmentOptions['height']),
  604. (!empty($attachmentOptions['mime_type']) ? $attachmentOptions['mime_type'] : ''), (int) $attachmentOptions['approved'],
  605. ),
  606. array('id_attach')
  607. );
  608. $attachmentOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
  609. // @todo Add an error here maybe?
  610. if (empty($attachmentOptions['id']))
  611. return false;
  612. // Now that we have the attach id, let's rename this sucker and finish up.
  613. $attachmentOptions['destination'] = getAttachmentFilename(basename($attachmentOptions['name']), $attachmentOptions['id'], $attachmentOptions['id_folder'], false, $attachmentOptions['file_hash']);
  614. rename($attachmentOptions['tmp_name'], $attachmentOptions['destination']);
  615. // If it's not approved then add to the approval queue.
  616. if (!$attachmentOptions['approved'])
  617. $smcFunc['db_insert']('',
  618. '{db_prefix}approval_queue',
  619. array(
  620. 'id_attach' => 'int', 'id_msg' => 'int',
  621. ),
  622. array(
  623. $attachmentOptions['id'], (int) $attachmentOptions['post'],
  624. ),
  625. array()
  626. );
  627. if (empty($modSettings['attachmentThumbnails']) || (empty($attachmentOptions['width']) && empty($attachmentOptions['height'])))
  628. return true;
  629. // Like thumbnails, do we?
  630. if (!empty($modSettings['attachmentThumbWidth']) && !empty($modSettings['attachmentThumbHeight']) && ($attachmentOptions['width'] > $modSettings['attachmentThumbWidth'] || $attachmentOptions['height'] > $modSettings['attachmentThumbHeight']))
  631. {
  632. if (createThumbnail($attachmentOptions['destination'], $modSettings['attachmentThumbWidth'], $modSettings['attachmentThumbHeight']))
  633. {
  634. // Figure out how big we actually made it.
  635. $size = @getimagesize($attachmentOptions['destination'] . '_thumb');
  636. list ($thumb_width, $thumb_height) = $size;
  637. if (!empty($size['mime']))
  638. $thumb_mime = $size['mime'];
  639. elseif (isset($validImageTypes[$size[2]]))
  640. $thumb_mime = 'image/' . $validImageTypes[$size[2]];
  641. // Lord only knows how this happened...
  642. else
  643. $thumb_mime = '';
  644. $thumb_filename = $attachmentOptions['name'] . '_thumb';
  645. $thumb_size = filesize($attachmentOptions['destination'] . '_thumb');
  646. $thumb_file_hash = getAttachmentFilename($thumb_filename, false, null, true);
  647. $thumb_path = $attachmentOptions['destination'] . '_thumb';
  648. // We should check the file size and count here since thumbs are added to the existing totals.
  649. if (!empty($modSettings['automanage_attachments']) && $modSettings['automanage_attachments'] == 1 && !empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit']))
  650. {
  651. $context['dir_size'] = isset($context['dir_size']) ? $context['dir_size'] += $thumb_size : $context['dir_size'] = 0;
  652. $context['dir_files'] = isset($context['dir_files']) ? $context['dir_files']++ : $context['dir_files'] = 0;
  653. // If the folder is full, try to create a new one and move the thumb to it.
  654. if ($context['dir_size'] > $modSettings['attachmentDirSizeLimit'] * 1024 || $context['dir_files'] + 2 > $modSettings['attachmentDirFileLimit'])
  655. {
  656. if (automanage_attachments_by_space())
  657. {
  658. rename($thumb_path, $context['attach_dir'] . '/' . $thumb_filename);
  659. $thumb_path = $context['attach_dir'] . '/' . $thumb_filename;
  660. $context['dir_size'] = $thumb_size;
  661. $context['dir_files'] = 1;
  662. }
  663. }
  664. }
  665. // If a new folder has been already created. Gotta move this thumb there then.
  666. if ($modSettings['currentAttachmentUploadDir'] != $attachmentOptions['id_folder'])
  667. {
  668. rename($thumb_path, $context['attach_dir'] . '/' . $thumb_filename);
  669. $thumb_path = $context['attach_dir'] . '/' . $thumb_filename;
  670. }
  671. // To the database we go!
  672. $smcFunc['db_insert']('',
  673. '{db_prefix}attachments',
  674. array(
  675. 'id_folder' => 'int', 'id_msg' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-40', 'fileext' => 'string-8',
  676. 'size' => 'int', 'width' => 'int', 'height' => 'int', 'mime_type' => 'string-20', 'approved' => 'int',
  677. ),
  678. array(
  679. $modSettings['currentAttachmentUploadDir'], (int) $attachmentOptions['post'], 3, $thumb_filename, $thumb_file_hash, $attachmentOptions['fileext'],
  680. $thumb_size, $thumb_width, $thumb_height, $thumb_mime, (int) $attachmentOptions['approved'],
  681. ),
  682. array('id_attach')
  683. );
  684. $attachmentOptions['thumb'] = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
  685. if (!empty($attachmentOptions['thumb']))
  686. {
  687. $smcFunc['db_query']('', '
  688. UPDATE {db_prefix}attachments
  689. SET id_thumb = {int:id_thumb}
  690. WHERE id_attach = {int:id_attach}',
  691. array(
  692. 'id_thumb' => $attachmentOptions['thumb'],
  693. 'id_attach' => $attachmentOptions['id'],
  694. )
  695. );
  696. rename($thumb_path, getAttachmentFilename($thumb_filename, $attachmentOptions['thumb'], $modSettings['currentAttachmentUploadDir'], false, $thumb_file_hash));
  697. }
  698. }
  699. }
  700. return true;
  701. }
  702. ?>