/** * Deletes all the attachments a given language code holds. Well, not quite! It deletes all references, and any attachments have through it, run out of references. * * @param ID_TEXT The arbitrary type that the attached is for (e.g. download) * @param ID_TEXT The id in the set of the arbitrary types that the attached is for * @param ?object The database connection to use (NULL: standard site connection) */ function delete_comcode_attachments($type, $id, $connection = NULL) { if (is_null($connection)) { $connection = $GLOBALS['SITE_DB']; } require_lang('comcode'); $refs = $connection->query_select('attachment_refs', array('a_id', 'id'), array('r_referer_type' => $type, 'r_referer_id' => $id)); $connection->query_delete('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id)); foreach ($refs as $ref) { // Was that the last reference to this attachment? (if so -- delete attachment) $test = $connection->query_value_null_ok('attachment_refs', 'id', array('a_id' => $ref['a_id'])); if (is_null($test)) { _delete_attachment($ref['a_id'], $connection); } } }
/** * Delete attachments solely used by the specified hook. * * @param ID_TEXT The hook * @param ?object The database connection to use (NULL: standard site connection) */ function delete_attachments($type, $connection = NULL) { if (is_null($connection)) { $connection = $GLOBALS['SITE_DB']; } require_code('attachments2'); require_code('attachments3'); // Clear any de-referenced attachments $before = $connection->query_select('attachment_refs', array('a_id', 'id'), array('r_referer_type' => $type)); foreach ($before as $ref) { // Delete reference (as it's not actually in the new comcode!) $connection->query_delete('attachment_refs', array('id' => $ref['id']), '', 1); // Was that the last reference to this attachment? (if so -- delete attachment) $test = $connection->query_value_null_ok('attachment_refs', 'id', array('a_id' => $ref['a_id'])); if (is_null($test)) { _delete_attachment($ref['a_id'], $connection); } } }
/** * Get an array containing new comcode, and tempcode. The function wraps the normal comcode_to_tempcode function. The function will do attachment management, including deleting of attachments that have become unused due to editing of some comcode and removing of the reference. * * @param LONG_TEXT The unparsed comcode that references the attachments * @param ID_TEXT The type the attachment will be used for (e.g. download) * @param ID_TEXT The ID the attachment will be used for * @param boolean Whether we are only previewing the attachments (i.e. don't store them!) * @param ?object The database connection to use (NULL: standard site connection) * @param ?boolean Whether to insert it as an admin (any comcode parsing will be carried out with admin privileges) (NULL: autodetect) * @param ?MEMBER The member to use for ownership permissions (NULL: current member) * @return array A map containing 'comcode' (after substitution for tying down the new attachments) and 'tempcode' */ function do_comcode_attachments($original_comcode, $type, $id, $previewing_only = false, $connection = NULL, $insert_as_admin = NULL, $for_member = NULL) { require_lang('comcode'); global $COMCODE_ATTACHMENTS; unset($COMCODE_ATTACHMENTS[$id]); // In case we have some kind of conflict if (is_null($connection)) { $connection = $GLOBALS['SITE_DB']; } if ($for_member !== NULL) { $member = $for_member; if (is_null($insert_as_admin)) { $insert_as_admin = false; } } else { if (function_exists('get_member')) { $member = get_member(); if (is_null($insert_as_admin)) { $insert_as_admin = false; } } else { $member = 0; if (is_null($insert_as_admin)) { $insert_as_admin = true; } } } $comcode_text = substr($original_comcode, 0, 8) != '<comcode'; // Handle data URLs for attachment embedding if (function_exists('imagecreatefromstring')) { $matches = array(); $matches2 = array(); $num_matches = preg_match_all('#<img[^<>]*src="data:image/\\w+;base64,([^"]*)"[^<>]*>#', $original_comcode, $matches); $num_matches2 = preg_match_all('#\\[img[^\\[\\]]*\\]data:image/\\w+;base64,([^"]*)\\[/img\\]#', $original_comcode, $matches2); for ($i = 0; $i < $num_matches2; $i++) { $matches[0][$num_matches] = $matches2[0][$i]; $matches[1][$num_matches] = $matches2[1][$i]; $num_matches++; } for ($i = 0; $i < $num_matches; $i++) { if (strpos($original_comcode, $matches[0][$i]) !== false) { $data = @base64_decode($matches[1][$i]); if ($data !== false && function_exists('imagepng')) { $image = @imagecreatefromstring($data); if ($image !== false) { do { $new_filename = uniqid('', true) . '.png'; $new_path = get_custom_file_base() . '/uploads/attachments/' . $new_filename; } while (file_exists($new_path)); imagepng($image, $new_path); $attachment_id = $GLOBALS['SITE_DB']->query_insert('attachments', array('a_member_id' => get_member(), 'a_file_size' => strlen($data), 'a_url' => 'uploads/attachments/' . $new_filename, 'a_thumb_url' => '', 'a_original_filename' => basename($new_filename), 'a_num_downloads' => 0, 'a_last_downloaded_time' => time(), 'a_description' => '', 'a_add_time' => time()), true); $GLOBALS['SITE_DB']->query_insert('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id, 'a_id' => $attachment_id)); $original_comcode = str_replace($matches[0][$i], '[attachment type="inline" thumb="0"]' . strval($attachment_id) . '[/attachment]', $original_comcode); } } } } } global $ATTACHMENTS_ALREADY_REFERENCED; $old_already = $ATTACHMENTS_ALREADY_REFERENCED; $ATTACHMENTS_ALREADY_REFERENCED = array(); $before = $connection->query_select('attachment_refs', array('a_id', 'id'), array('r_referer_type' => $type, 'r_referer_id' => $id)); foreach ($before as $ref) { $ATTACHMENTS_ALREADY_REFERENCED[$ref['a_id']] = 1; } $has_one = false; $may_have_one = false; foreach ($_POST as $key => $value) { if (preg_match('#^hidFileID\\_#i', $key) != 0) { require_code('uploads'); $may_have_one = is_swf_upload(); } } if ($may_have_one) { require_code('uploads'); is_swf_upload(true); require_code('comcode_from_html'); $original_comcode = preg_replace_callback('#<input [^>]*class="ocp_keep_ui_controlled" [^>]*title="([^"]*)" [^>]*type="text" [^>]*value="[^"]*"[^>]*/?' . '>#siU', 'debuttonise', $original_comcode); } $myfile = mixed(); foreach ($_FILES as $key => $file) { $matches = array(); if (($may_have_one && is_swf_upload() || is_uploaded_file($file['tmp_name'])) && preg_match('#file(\\d+)#', $key, $matches) != 0) { $has_one = true; $atype = post_param('attachmenttype' . $matches[1], ''); $is_extract = preg_match('#\\[attachment [^\\]]*type="\\w+_extract"[^\\]]*\\]new_' . $matches[1] . '\\[/#', $original_comcode) != 0 || preg_match('#<attachment [^>]*type="\\w+_extract"[^>]*>new_' . $matches[1] . '</#', $original_comcode) != 0; if (substr($atype, -8) == '_extract' || $is_extract) { require_code('uploads'); require_code('files'); require_code('files2'); $thumb = preg_match('#\\[(attachment|attachment_safe) [^\\]]*thumb="1"[^\\]]*\\]new_' . $matches[1] . '\\[/#', $original_comcode) != 0 || preg_match('#<(attachment|attachment_safe) [^>]*thumb="1"[^>]*>new_' . $matches[1] . '</#', $original_comcode) != 0; $arcext = get_file_extension($_FILES[$key]['name']); if ($arcext == 'tar' || $arcext == 'zip') { if ($arcext == 'tar') { require_code('tar'); $myfile = tar_open($file['tmp_name'], 'rb'); $dir = tar_get_directory($myfile, true); } elseif ($arcext == 'zip') { if (!function_exists('zip_open') && get_option('unzip_cmd') == '') { warn_exit(do_lang_tempcode('ZIP_NOT_ENABLED')); } if (!function_exists('zip_open')) { require_code('m_zip'); $mzip = true; } else { $mzip = false; } $myfile = zip_open($file['tmp_name']); if (is_integer($myfile)) { require_code('failure'); warn_exit(zip_error($myfile, $mzip)); } $dir = array(); while (($zip_entry = zip_read($myfile)) !== false) { $dir[] = array('zip_entry' => $zip_entry, 'path' => zip_entry_name($zip_entry), 'size' => zip_entry_filesize($zip_entry)); } } if (count($dir) > 100) { require_code('site'); attach_message(do_lang_tempcode('TOO_MANY_FILES_TO_EXTRACT'), 'warn'); } else { foreach ($dir as $entry) { if (substr($entry['path'], -1) == '/') { continue; } // Ignore folders $_file = preg_replace('#\\..*\\.#', '.', basename($entry['path'])); if (!check_extension($_file, false, NULL, true)) { continue; } if (should_ignore_file($entry['path'], IGNORE_ACCESS_CONTROLLERS | IGNORE_HIDDEN_FILES)) { continue; } $place = get_custom_file_base() . '/uploads/attachments/' . $_file; $i = 2; // Hunt with sensible names until we don't get a conflict while (file_exists($place)) { $_file = strval($i) . basename($entry['path']); $place = get_custom_file_base() . '/uploads/attachments/' . $_file; $i++; } $i = 2; $_file_thumb = basename($entry['path']); $place_thumb = get_custom_file_base() . '/uploads/attachments_thumbs/' . $_file_thumb; // Hunt with sensible names until we don't get a conflict while (file_exists($place_thumb)) { $_file_thumb = strval($i) . basename($entry['path']); $place_thumb = get_custom_file_base() . '/uploads/attachments_thumbs/' . $_file_thumb; $i++; } if ($arcext == 'tar') { $file_details = tar_get_file($myfile, $entry['path'], false, $place); } elseif ($arcext == 'zip') { zip_entry_open($myfile, $entry['zip_entry']); $file_details = array('size' => $entry['size']); $out_file = @fopen($place, 'wb') or intelligent_write_error($place); $more = mixed(); do { $more = zip_entry_read($entry['zip_entry']); if ($more !== false) { if (fwrite($out_file, $more) < strlen($more)) { warn_exit(do_lang_tempcode('COULD_NOT_SAVE_FILE')); } } } while ($more !== false && $more != ''); fclose($out_file); zip_entry_close($entry['zip_entry']); } $description = do_lang('EXTRACTED_FILE'); if (strpos($entry['path'], '/') !== false) { $description = do_lang('EXTRACTED_FILE_PATH', dirname($entry['path'])); } // Thumbnail $thumb_url = ''; require_code('images'); if (is_image($_file)) { $gd = get_option('is_on_gd') == '1' && function_exists('imagetypes'); if ($gd) { require_code('images'); if (!is_saveable_image($_file)) { $ext = '.png'; } else { $ext = '.' . get_file_extension($_file); } $thumb_url = 'uploads/attachments_thumbs/' . $_file_thumb; convert_image(get_custom_base_url() . '/uploads/attachments/' . $_file, $place_thumb, -1, -1, intval(get_option('thumb_width')), true, NULL, false, true); if ($connection->connection_write != $GLOBALS['SITE_DB']->connection_write) { $thumb_url = get_custom_base_url() . '/' . $thumb_url; } } else { $thumb_url = 'uploads/attachments/' . $_file; } } $url = 'uploads/attachments/' . $_file; if (addon_installed('galleries')) { require_code('images'); if (is_video($url) && $connection->connection_read == $GLOBALS['SITE_DB']->connection_read) { require_code('transcoding'); $url = transcode_video($url, 'attachments', 'a_url', 'a_original_filename', NULL, NULL); } } $attachment_id = $connection->query_insert('attachments', array('a_member_id' => get_member(), 'a_file_size' => $file_details['size'], 'a_url' => $url, 'a_thumb_url' => $thumb_url, 'a_original_filename' => basename($entry['path']), 'a_num_downloads' => 0, 'a_last_downloaded_time' => time(), 'a_description' => $description, 'a_add_time' => time()), true); $connection->query_insert('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id, 'a_id' => $attachment_id)); if ($comcode_text) { $original_comcode .= chr(10) . chr(10) . '[attachment type="' . comcode_escape(str_replace('_extract', '', $atype)) . '" description="' . comcode_escape($description) . '" thumb="' . ($thumb ? '1' : '0') . '"]' . strval($attachment_id) . '[/attachment]'; } else { require_code('comcode_xml'); //$original_comcode.=chr(10).chr(10).'<attachment type="'.comcode_escape(str_replace('_extract','',$atype)).'" thumb="'.($thumb?'1':'0').'"><attachmentDescription>'.comcode_text__to__comcode_xml($description).'</attachmentDescription>'.strval($attachment_id).'</attachment>'; Would go in bad spot } } } if ($arcext == 'tar') { tar_close($myfile); } elseif ($arcext == 'zip') { zip_close($myfile); } } } else { if (strpos($original_comcode, ']new_' . $matches[1] . '[/attachment]') === false && strpos($original_comcode, '>new_' . $matches[1] . '</attachment>') === false && strpos($original_comcode, ']new_' . $matches[1] . '[/attachment_safe]') === false && strpos($original_comcode, '>new_' . $matches[1] . '</attachment_safe>') === false) { if (preg_match('#\\]\\d+\\[/attachment\\]#', $original_comcode) == 0 && preg_match('#>\\d+</attachment>#', $original_comcode) == 0) { if ($comcode_text) { $original_comcode .= chr(10) . chr(10) . '[attachment]new_' . $matches[1] . '[/attachment]'; } else { //$original_comcode.=chr(10).chr(10).'<attachment>new_'.$matches[1].'</attachment>'; Would go in bad spot } } } } } } global $LAX_COMCODE; $temp = $LAX_COMCODE; if ($has_one) { $LAX_COMCODE = true; } // We don't want a simple syntax error to cause us to lose our attachments $tempcode = comcode_to_tempcode($original_comcode, $member, $insert_as_admin, 60, $id, $connection, false, false, false, false, false, NULL, $for_member); $LAX_COMCODE = $temp; $ATTACHMENTS_ALREADY_REFERENCED = $old_already; /*if ((array_key_exists($id,$COMCODE_ATTACHMENTS)) && (array_key_exists(0,$COMCODE_ATTACHMENTS[$id]))) { $original_comcode=$COMCODE_ATTACHMENTS[$id][0]['comcode']; }*/ $new_comcode = $original_comcode; if (array_key_exists($id, $COMCODE_ATTACHMENTS)) { $ids_present = array(); for ($i = 0; $i < count($COMCODE_ATTACHMENTS[$id]); $i++) { $attachment = $COMCODE_ATTACHMENTS[$id][$i]; // If it's a new one, we need to change the comcode to reference the ID we made for it if ($attachment['type'] == 'new') { $marker = $attachment['marker']; // echo $marker.'!'.$new_comcode; $a_id = $attachment['id']; $old_length = strlen($new_comcode); // Search backwards from $marker $tag_end_start = $marker - strlen('[/' . $attachment['tag_type'] . ']'); // </attachment> would be correct if it is Comcode-XML, but they have the same length, so it's irrelevant $tag_start_end = $tag_end_start; while ($tag_start_end > 1 && (!isset($new_comcode[$tag_start_end - 1]) || $new_comcode[$tag_start_end - 1] != ']' && $new_comcode[$tag_start_end - 1] != '>')) { $tag_start_end--; } $param_keep = substr($new_comcode, 0, $tag_start_end - 1); $end_keep = substr($new_comcode, $tag_end_start); if ($comcode_text) { $new_comcode = $param_keep; if (strpos(substr($param_keep, strrpos($param_keep, '[')), ' type=') === false) { $new_comcode .= ' type="' . comcode_escape($attachment['attachmenttype']) . '"'; } if (strpos(substr($param_keep, strrpos($param_keep, '[')), ' description=') === false) { $new_comcode .= ' description="' . comcode_escape($attachment['description']) . '"'; } $new_comcode .= ']' . strval($a_id) . $end_keep; } else { require_code('comcode_xml'); $new_comcode = $param_keep; if (strpos(substr($param_keep, strrpos($param_keep, '<')), ' type=') === false) { $new_comcode .= ' type="' . comcode_escape($attachment['attachmenttype']); } $new_comcode .= '">'; if (strpos(substr($param_keep, strrpos($param_keep, '<')), ' description=') === false) { require_code('comcode_xml'); $new_comcode .= '<attachmentDescription>' . comcode_text__to__comcode_xml($attachment['description'], true) . '</attachmentDescription>'; } $new_comcode .= strval($a_id) . $end_keep; } // echo $new_comcode.'<br />!<br />'; // Update other attachment markers $dif = strlen($new_comcode) - $old_length; for ($j = $i + 1; $j < count($COMCODE_ATTACHMENTS[$id]); $j++) { // echo $COMCODE_ATTACHMENTS[$id][$i]['marker'].'!'; $COMCODE_ATTACHMENTS[$id][$j]['marker'] += $dif; } if (!is_null($type)) { $connection->query_insert('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id, 'a_id' => $a_id)); } } else { // (Re-)Reference it $connection->query_delete('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id, 'a_id' => $attachment['id']), '', 1); $connection->query_insert('attachment_refs', array('r_referer_type' => $type, 'r_referer_id' => $id, 'a_id' => $attachment['id'])); } $ids_present[] = $attachment['id']; } if (!$previewing_only && get_value('disable_attachment_cleanup') !== '1') { // Clear any de-referenced attachments foreach ($before as $ref) { if (!in_array($ref['a_id'], $ids_present) && strpos($new_comcode, 'attachment.php?id=') === false && !multi_lang()) { // Delete reference (as it's not actually in the new comcode!) $connection->query_delete('attachment_refs', array('id' => $ref['id']), '', 1); // Was that the last reference to this attachment? (if so -- delete attachment) $test = $connection->query_value_null_ok('attachment_refs', 'id', array('a_id' => $ref['a_id'])); if (is_null($test)) { require_code('attachments3'); _delete_attachment($ref['a_id'], $connection); } } } } } return array('comcode' => $new_comcode, 'tempcode' => $tempcode); }