Esempio n. 1
0
        public IActionResult DownloadData(string file, bool decrypt)
        {
            if (_config.UploadConfig.DownloadEnabled)
            {
                Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
                if (upload != null)
                {
                    // Check Expiration
                    if (UploadHelper.CheckExpiration(upload))
                    {
                        DeleteFile(upload);
                        return(Json(new { error = new { message = "File Does Not Exist" } }));
                    }

                    string subDir   = upload.FileName[0].ToString();
                    string filePath = Path.Combine(_config.UploadConfig.UploadDirectory, subDir, upload.FileName);
                    if (System.IO.File.Exists(filePath))
                    {
                        // Notify the client the content length we'll be outputting
                        Response.Headers.Add("Content-Length", upload.ContentLength.ToString());

                        // Create content disposition
                        var cd = new System.Net.Mime.ContentDisposition
                        {
                            FileName = upload.Url,
                            Inline   = true
                        };

                        // Set the content type of this response
                        Response.Headers.Add("Content-Type", upload.ContentType);

                        Response.Headers.Add("Content-Disposition", cd.ToString());

                        // Read in the file
                        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

                        // If the IV is set, and Key is set, then decrypt it while sending
                        if (decrypt && !string.IsNullOrEmpty(upload.Key) && !string.IsNullOrEmpty(upload.IV))
                        {
                            byte[] keyBytes = Encoding.UTF8.GetBytes(upload.Key);
                            byte[] ivBytes  = Encoding.UTF8.GetBytes(upload.IV);

                            return(new BufferedFileStreamResult(upload.ContentType, (response) => ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fs, false, keyBytes, ivBytes), (int)upload.ContentLength, _config.UploadConfig.ChunkSize), false));
                        }
                        else // Otherwise just send it
                        {
                            // Send the file
                            return(new BufferedFileStreamResult(upload.ContentType, (response) => ResponseHelper.StreamToOutput(response, true, fs, (int)upload.ContentLength, _config.UploadConfig.ChunkSize), false));
                        }
                    }
                }
                return(Json(new { error = new { message = "File Does Not Exist" } }));
            }
            return(Json(new { error = new { message = "Downloads are disabled" } }));
        }
Esempio n. 2
0
        public async Task <IActionResult> Download(string file)
        {
            if (_config.UploadConfig.DownloadEnabled)
            {
                ViewBag.Title = "Download " + file;
                string   fileName       = string.Empty;
                string   url            = string.Empty;
                string   key            = string.Empty;
                string   iv             = string.Empty;
                string   contentType    = string.Empty;
                long     contentLength  = 0;
                bool     premiumAccount = false;
                DateTime dateUploaded   = new DateTime();

                Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
                if (upload != null)
                {
                    // Check Expiration
                    if (UploadHelper.CheckExpiration(upload))
                    {
                        DeleteFile(upload);
                        return(new StatusCodeResult(StatusCodes.Status404NotFound));
                    }

                    upload.Downloads += 1;
                    _dbContext.Entry(upload).State = EntityState.Modified;
                    _dbContext.SaveChanges();

                    fileName      = upload.FileName;
                    url           = upload.Url;
                    key           = upload.Key;
                    iv            = upload.IV;
                    contentType   = upload.ContentType;
                    contentLength = upload.ContentLength;
                    dateUploaded  = upload.DateUploaded;
                    if (User.Identity.IsAuthenticated)
                    {
                        IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, User.Identity.Name);

                        premiumAccount = userInfo.AccountType == AccountType.Premium;
                    }
                    if (!premiumAccount && upload.User != null)
                    {
                        IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, upload.User.Username);

                        premiumAccount = userInfo.AccountType == AccountType.Premium;
                    }
                }
                else
                {
                    return(new StatusCodeResult(StatusCodes.Status404NotFound));
                }

                // We don't have the key, so we need to decrypt it client side
                if (string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv))
                {
                    DownloadViewModel model = new DownloadViewModel();
                    model.CurrentSub    = Subdomain;
                    model.FileName      = file;
                    model.ContentType   = contentType;
                    model.ContentLength = contentLength;
                    model.IV            = iv;
                    model.Decrypt       = true;

                    return(View(model));
                }
                else if (!premiumAccount && _config.UploadConfig.MaxDownloadSize < contentLength)
                {
                    // We want to force them to the dl page due to them being over the max download size for embedded content
                    DownloadViewModel model = new DownloadViewModel();
                    model.CurrentSub    = Subdomain;
                    model.FileName      = file;
                    model.ContentType   = contentType;
                    model.ContentLength = contentLength;
                    model.Decrypt       = false;

                    return(View(model));
                }
                else // We have the key, so that means server side decryption
                {
                    // Check for the cache
                    bool   isCached      = false;
                    string modifiedSince = Request.Headers["If-Modified-Since"];
                    if (!string.IsNullOrEmpty(modifiedSince))
                    {
                        DateTime modTime = new DateTime();
                        bool     parsed  = DateTime.TryParse(modifiedSince, out modTime);
                        if (parsed)
                        {
                            if ((modTime - dateUploaded).TotalSeconds <= 1)
                            {
                                isCached = true;
                            }
                        }
                    }

                    if (isCached)
                    {
                        return(new StatusCodeResult(StatusCodes.Status304NotModified));
                    }
                    else
                    {
                        string subDir    = fileName[0].ToString();
                        string filePath  = Path.Combine(_config.UploadConfig.UploadDirectory, subDir, fileName);
                        long   startByte = 0;
                        long   endByte   = contentLength - 1;
                        long   length    = contentLength;
                        if (System.IO.File.Exists(filePath))
                        {
                            #region Range Calculation
                            // Are they downloading it by range?
                            bool byRange = !string.IsNullOrEmpty(Request.Headers["Range"]); // We do not support ranges

                            // check to see if we need to pass a specified range
                            if (byRange)
                            {
                                long     anotherStart = startByte;
                                long     anotherEnd   = endByte;
                                string[] arr_split    = Request.Headers["Range"].ToString().Split(new char[] { '=' });
                                string   range        = arr_split[1];

                                // Make sure the client hasn't sent us a multibyte range
                                if (range.IndexOf(",") > -1)
                                {
                                    // (?) Shoud this be issued here, or should the first
                                    // range be used? Or should the header be ignored and
                                    // we output the whole content?
                                    Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength);

                                    return(new StatusCodeResult(StatusCodes.Status416RequestedRangeNotSatisfiable));
                                }

                                // If the range starts with an '-' we start from the beginning
                                // If not, we forward the file pointer
                                // And make sure to get the end byte if spesified
                                if (range.StartsWith("-"))
                                {
                                    // The n-number of the last bytes is requested
                                    anotherStart = startByte - Convert.ToInt64(range.Substring(1));
                                }
                                else
                                {
                                    arr_split    = range.Split(new char[] { '-' });
                                    anotherStart = Convert.ToInt64(arr_split[0]);
                                    long temp = 0;
                                    anotherEnd = (arr_split.Length > 1 && Int64.TryParse(arr_split[1].ToString(), out temp)) ? Convert.ToInt64(arr_split[1]) : contentLength;
                                }

                                /* Check the range and make sure it's treated according to the specs.
                                 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
                                 */
                                // End bytes can not be larger than $end.
                                anotherEnd = (anotherEnd > endByte) ? endByte : anotherEnd;
                                // Validate the requested range and return an error if it's not correct.
                                if (anotherStart > anotherEnd || anotherStart > contentLength - 1 || anotherEnd >= contentLength)
                                {
                                    Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength);

                                    return(new StatusCodeResult(StatusCodes.Status416RequestedRangeNotSatisfiable));
                                }
                                startByte = anotherStart;
                                endByte   = anotherEnd;

                                length = endByte - startByte + 1; // Calculate new content length

                                // Ranges are response of 206
                                Response.StatusCode = 206;
                            }
                            #endregion

                            // Set Last Modified
                            Response.GetTypedHeaders().LastModified = dateUploaded;

                            // We accept ranges
                            Response.Headers.Add("Accept-Ranges", "0-" + contentLength);

                            // Notify the client the byte range we'll be outputting
                            Response.Headers.Add("Content-Range", "bytes " + startByte + "-" + endByte + "/" + contentLength);

                            // Notify the client the content length we'll be outputting
                            Response.Headers.Add("Content-Length", length.ToString());

                            // Set the content type of this response
                            Response.Headers.Add("Content-Type", contentType);

                            // Create content disposition
                            var cd = new System.Net.Mime.ContentDisposition
                            {
                                FileName = url,
                                Inline   = true
                            };

                            Response.Headers.Add("Content-Disposition", cd.ToString());

                            // Read in the file
                            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

                            // Reset file stream to starting position (or start of range)
                            fs.Seek(startByte, SeekOrigin.Begin);

                            try
                            {
                                // If the IV is set, and Key is set, then decrypt it while sending
                                if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(iv))
                                {
                                    byte[] keyBytes = Encoding.UTF8.GetBytes(key);
                                    byte[] ivBytes  = Encoding.UTF8.GetBytes(iv);

                                    return(new BufferedFileStreamResult(contentType, async(response) => await ResponseHelper.StreamToOutput(response, true, new AesCounterStream(fs, false, keyBytes, ivBytes), (int)length, _config.UploadConfig.ChunkSize), false));
                                }
                                else // Otherwise just send it
                                {
                                    // Send the file
                                    return(new BufferedFileStreamResult(contentType, async(response) => await ResponseHelper.StreamToOutput(response, true, fs, (int)length, _config.UploadConfig.ChunkSize), false));
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogWarning(ex, "Error in Download: {url}", new { url });
                            }
                        }
                    }
                    return(new StatusCodeResult(StatusCodes.Status404NotFound));
                }
            }
            return(new StatusCodeResult(StatusCodes.Status403Forbidden));
        }