function attach_init(&$a) { if (argc() < 2) { notice(t('Item not available.') . EOL); return; } $r = attach_by_hash(argv(1), argc() > 2 ? intval(argv(2)) : 0); if (!$r['success']) { notice($r['message'] . EOL); return; } $c = q("select channel_address from channel where channel_id = %d limit 1", intval($r['data']['uid'])); if (!$c) { return; } $unsafe_types = array('text/html', 'text/css', 'application/javascript'); if (in_array($r['data']['filetype'], $unsafe_types)) { header('Content-type: text/plain'); } else { header('Content-type: ' . $r['data']['filetype']); } header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"'); if ($r['data']['flags'] & ATTACH_FLAG_OS) { $istream = fopen('store/' . $c[0]['channel_address'] . '/' . $r['data']['data'], 'rb'); $ostream = fopen('php://output', 'wb'); if ($istream && $ostream) { pipe_streams($istream, $ostream); fclose($istream); fclose($ostream); } } else { echo $r['data']['data']; } killme(); }
function post() { $hash = $_POST['hash']; $time = $_POST['time']; $sig = $_POST['signature']; $resource = $_POST['resource']; $revision = intval($_POST['revision']); if (!$hash) { killme(); } $channel = channelx_by_hash($hash); if (!$channel || !$time || !$sig) { killme(); } $slop = intval(get_pconfig($channel['channel_id'], 'system', 'getfile_time_slop')); if ($slop < 1) { $slop = 3; } $d1 = datetime_convert('UTC', 'UTC', "now + {$slop} minutes"); $d2 = datetime_convert('UTC', 'UTC', "now - {$slop} minutes"); if ($time > $d1 || $time < $d2) { logger('time outside allowable range'); killme(); } if (!rsa_verify($hash . '.' . $time, base64url_decode($sig), $channel['channel_pubkey'])) { logger('verify failed.'); killme(); } $r = attach_by_hash($resource, $revision); if (!$r['success']) { notice($r['message'] . EOL); return; } $unsafe_types = array('text/html', 'text/css', 'application/javascript'); if (in_array($r['data']['filetype'], $unsafe_types)) { header('Content-type: text/plain'); } else { header('Content-type: ' . $r['data']['filetype']); } header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"'); if (intval($r['data']['os_storage'])) { $fname = dbunescbin($r['data']['data']); if (strpos($fname, 'store') !== false) { $istream = fopen($fname, 'rb'); } else { $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname, 'rb'); } $ostream = fopen('php://output', 'wb'); if ($istream && $ostream) { pipe_streams($istream, $ostream); fclose($istream); fclose($ostream); } } else { echo dbunescbin($r['data']['data']); } killme(); }
function sync_files($channel, $files) { require_once 'include/attach.php'; if ($channel && $files) { foreach ($files as $f) { if (!$f) { continue; } $fetch_url = $f['fetch_url']; $oldbase = dirname($fetch_url); $original_channel = $f['original_channel']; if (!($fetch_url && $original_channel)) { continue; } if ($f['attach']) { $attachment_stored = false; foreach ($f['attach'] as $att) { convert_oldfields($att, 'data', 'content'); if ($att['deleted']) { attach_delete($channel, $att['hash']); continue; } $attach_exists = false; $x = attach_by_hash($att['hash']); logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG); logger('sync_files duplicate check: att=' . print_r($att, true), LOGGER_DEBUG); logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x, true), LOGGER_DEBUG); if ($x['success']) { $attach_exists = true; $attach_id = $x[0]['id']; } $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); unset($att['id']); $att['aid'] = $channel['channel_account_id']; $att['uid'] = $channel['channel_id']; // check for duplicate folder names with the same parent. // If we have a duplicate that doesn't match this hash value // change the name so that the contents won't be "covered over" // by the existing directory. Use the same logic we use for // duplicate files. if (strpos($att['filename'], '.') !== false) { $basename = substr($att['filename'], 0, strrpos($att['filename'], '.')); $ext = substr($att['filename'], strrpos($att['filename'], '.')); } else { $basename = $att['filename']; $ext = ''; } $r = q("select filename from attach where ( filename = '%s' OR filename like '%s' ) and folder = '%s' and hash != '%s' ", dbesc($basename . $ext), dbesc($basename . '(%)' . $ext), dbesc($att['folder']), dbesc($att['hash'])); if ($r) { $x = 1; do { $found = false; foreach ($r as $rr) { if ($rr['filename'] === $basename . '(' . $x . ')' . $ext) { $found = true; break; } } if ($found) { $x++; } } while ($found); $att['filename'] = $basename . '(' . $x . ')' . $ext; } else { $att['filename'] = $basename . $ext; } // end duplicate detection // @fixme - update attachment structures if they are modified rather than created $att['content'] = $newfname; // Note: we use $att['hash'] below after it has been escaped to // fetch the file contents. // If the hash ever contains any escapable chars this could cause // problems. Currently it does not. dbesc_array($att); if ($attach_exists) { logger('sync_files attach exists: ' . print_r($att, true), LOGGER_DEBUG); $str = ''; foreach ($att as $k => $v) { if ($str) { $str .= ","; } $str .= " `" . $k . "` = '" . $v . "' "; } $r = dbq("update `attach` set " . $str . " where id = " . intval($attach_id)); } else { logger('sync_files attach does not exists: ' . print_r($att, true), LOGGER_DEBUG); $r = dbq("INSERT INTO attach (`" . implode("`, `", array_keys($att)) . "`) VALUES ('" . implode("', '", array_values($att)) . "')"); } // is this a directory? if ($att['filetype'] === 'multipart/mixed' && $att['is_dir']) { os_mkdir($newfname, STORAGE_DEFAULT_PERMISSIONS, true); $attachment_stored = true; continue; } else { // it's a file // for the sync version of this algorithm (as opposed to 'offline import') // we will fetch the actual file from the source server so it can be // streamed directly to disk and avoid consuming PHP memory if it's a huge // audio/video file or something. $time = datetime_convert(); $parr = array('hash' => $channel['channel_hash'], 'time' => $time, 'resource' => $att['hash'], 'revision' => 0, 'signature' => base64url_encode(rsa_sign($channel['channel_hash'] . '.' . $time, $channel['channel_prvkey']))); $store_path = $newfname; $fp = fopen($newfname, 'w'); if (!$fp) { logger('failed to open storage file.', LOGGER_NORMAL, LOG_ERR); continue; } $redirects = 0; $x = z_post_url($fetch_url, $parr, $redirects, array('filep' => $fp)); fclose($fp); if ($x['success']) { $attachment_stored = true; } continue; } } } if (!$attachment_stored) { // @TODO should we queue this and retry or delete everything or what? logger('attachment store failed', LOGGER_NORMAL, LOG_ERR); } if ($f['photo']) { foreach ($f['photo'] as $p) { unset($p['id']); $p['aid'] = $channel['channel_account_id']; $p['uid'] = $channel['channel_id']; convert_oldfields($p, 'data', 'content'); convert_oldfields($p, 'scale', 'imgscale'); convert_oldfields($p, 'size', 'filesize'); convert_oldfields($p, 'type', 'mimetype'); // if this is a profile photo, undo the profile photo bit // for any other photo which previously held it. if ($p['photo_usage'] == PHOTO_PROFILE) { $e = q("update photo set photo_usage = %d where photo_usage = %d\n\t\t\t\t\t\t\tand resource_id != '%s' and uid = %d ", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), dbesc($p['resource_id']), intval($channel['channel_id'])); } // same for cover photos if ($p['photo_usage'] == PHOTO_COVER) { $e = q("update photo set photo_usage = %d where photo_usage = %d\n\t\t\t\t\t\t\tand resource_id != '%s' and uid = %d ", intval(PHOTO_NORMAL), intval(PHOTO_COVER), dbesc($p['resource_id']), intval($channel['channel_id'])); } if ($p['imgscale'] === 0 && $p['os_storage']) { $p['content'] = $store_path; } else { $p['content'] = base64_decode($p['content']); } $exists = q("select * from photo where resource_id = '%s' and imgscale = %d and uid = %d limit 1", dbesc($p['resource_id']), intval($p['imgscale']), intval($channel['channel_id'])); dbesc_array($p); if ($exists) { $str = ''; foreach ($p as $k => $v) { if ($str) { $str .= ","; } $str .= " `" . $k . "` = '" . $v . "' "; } $r = dbq("update `photo` set " . $str . " where id = " . intval($exists[0]['id'])); } else { $r = dbq("INSERT INTO photo (`" . implode("`, `", array_keys($p)) . "`) VALUES ('" . implode("', '", array_values($p)) . "')"); } } } if ($f['item']) { sync_items($channel, $f['item'], ['channel_address' => $original_channel, 'url' => $oldbase]); } } } }