mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Added paste expiration and simplified the view action.
This commit is contained in:
parent
e84752a9c0
commit
efe61f5112
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
@ -31,6 +32,19 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
Models.Paste paste = db.Pastes.Where(p => p.Url == url).FirstOrDefault();
|
||||
if (paste != null)
|
||||
{
|
||||
// Increment Views
|
||||
paste.Views += 1;
|
||||
db.Entry(paste).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
|
||||
// Check Expiration
|
||||
if (CheckExpiration(paste))
|
||||
{
|
||||
db.Pastes.Remove(paste);
|
||||
db.SaveChanges();
|
||||
return Redirect(Url.SubRouteUrl("error", "Error.Http404"));
|
||||
}
|
||||
|
||||
PasteViewModel model = new PasteViewModel();
|
||||
model.Url = url;
|
||||
model.Content = paste.Content;
|
||||
@ -38,37 +52,7 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
model.Syntax = paste.Syntax;
|
||||
model.DatePosted = paste.DatePosted;
|
||||
|
||||
// The paste has a password set
|
||||
if (!string.IsNullOrEmpty(paste.HashedPassword))
|
||||
{
|
||||
if (string.IsNullOrEmpty(password) || Helpers.SHA384.Hash(paste.Key, password) != paste.HashedPassword)
|
||||
{
|
||||
PasswordViewModel passModel = new PasswordViewModel();
|
||||
passModel.Url = url;
|
||||
passModel.CallingAction = Url.SubRouteUrl("paste", "Paste.View");
|
||||
// Redirect them to the password request page
|
||||
return View("~/Areas/Paste/Views/Paste/PasswordNeeded.cshtml", passModel);
|
||||
}
|
||||
// Now we decrypt the content
|
||||
byte[] data = Encoding.Unicode.GetBytes(paste.Content);
|
||||
byte[] ivBytes = Encoding.Unicode.GetBytes(paste.IV);
|
||||
byte[] keyBytes = AES.CreateKey(password, ivBytes, paste.KeySize);
|
||||
byte[] encData = AES.Decrypt(data, keyBytes, ivBytes);
|
||||
model.Content = Encoding.Unicode.GetString(encData);
|
||||
}
|
||||
|
||||
return View("~/Areas/Paste/Views/Paste/" + type + ".cshtml", model);
|
||||
}
|
||||
return Redirect(Url.SubRouteUrl("error", "Error.Http404"));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public ActionResult Raw(string url, string password)
|
||||
{
|
||||
Models.Paste paste = db.Pastes.Where(p => p.Url == url).FirstOrDefault();
|
||||
if (paste != null)
|
||||
{
|
||||
string content = paste.Content;
|
||||
byte[] data = Encoding.Unicode.GetBytes(paste.Content);
|
||||
|
||||
// The paste has a password set
|
||||
if (!string.IsNullOrEmpty(paste.HashedPassword))
|
||||
@ -77,59 +61,39 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
{
|
||||
PasswordViewModel passModel = new PasswordViewModel();
|
||||
passModel.Url = url;
|
||||
passModel.CallingAction = Url.SubRouteUrl("paste", "Paste.Raw");
|
||||
passModel.CallingAction = Url.SubRouteUrl("paste", "Paste.View", new { type = type });
|
||||
// Redirect them to the password request page
|
||||
return View("~/Areas/Paste/Views/Paste/PasswordNeeded.cshtml", passModel);
|
||||
}
|
||||
// Now we decrypt the content
|
||||
byte[] data = Encoding.Unicode.GetBytes(paste.Content);
|
||||
byte[] ivBytes = Encoding.Unicode.GetBytes(paste.IV);
|
||||
byte[] keyBytes = AES.CreateKey(password, ivBytes, paste.KeySize);
|
||||
byte[] encData = AES.Decrypt(data, keyBytes, ivBytes);
|
||||
content = Encoding.Unicode.GetString(encData);
|
||||
data = AES.Decrypt(data, keyBytes, ivBytes);
|
||||
model.Content = Encoding.Unicode.GetString(data);
|
||||
}
|
||||
|
||||
return Content(content, "text/plain");
|
||||
}
|
||||
return Redirect(Url.SubRouteUrl("error", "Error.Http404"));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
public ActionResult Download(string url, string password)
|
||||
{
|
||||
Models.Paste paste = db.Pastes.Where(p => p.Url == url).FirstOrDefault();
|
||||
if (paste != null)
|
||||
{
|
||||
byte[] fileData = Encoding.Unicode.GetBytes(paste.Content);
|
||||
|
||||
// The paste has a password set
|
||||
if (!string.IsNullOrEmpty(paste.HashedPassword))
|
||||
switch (type.ToLower())
|
||||
{
|
||||
if (string.IsNullOrEmpty(password) || Helpers.SHA384.Hash(paste.Key, password) != paste.HashedPassword)
|
||||
{
|
||||
PasswordViewModel passModel = new PasswordViewModel();
|
||||
passModel.Url = url;
|
||||
passModel.CallingAction = Url.SubRouteUrl("paste", "Paste.Download");
|
||||
// Redirect them to the password request page
|
||||
return View("~/Areas/Paste/Views/Paste/PasswordNeeded.cshtml", passModel);
|
||||
}
|
||||
// Now we decrypt the content
|
||||
byte[] data = Encoding.Unicode.GetBytes(paste.Content);
|
||||
byte[] ivBytes = Encoding.Unicode.GetBytes(paste.IV);
|
||||
byte[] keyBytes = AES.CreateKey(password, ivBytes, paste.KeySize);
|
||||
fileData = AES.Decrypt(data, keyBytes, ivBytes);
|
||||
case "full":
|
||||
return View("~/Areas/Paste/Views/Paste/Full.cshtml", model);
|
||||
case "simple":
|
||||
return View("~/Areas/Paste/Views/Paste/Simple.cshtml", model);
|
||||
case "raw":
|
||||
return Content(model.Content, "text/plain");
|
||||
case "download":
|
||||
//Create File
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = url,
|
||||
Inline = true
|
||||
};
|
||||
|
||||
Response.AppendHeader("Content-Disposition", cd.ToString());
|
||||
|
||||
return File(data, "text/plain");
|
||||
default:
|
||||
return View("~/Areas/Paste/Views/Paste/Full.cshtml", model);
|
||||
}
|
||||
|
||||
//Create File
|
||||
var cd = new System.Net.Mime.ContentDisposition
|
||||
{
|
||||
FileName = url,
|
||||
Inline = true
|
||||
};
|
||||
|
||||
Response.AppendHeader("Content-Disposition", cd.ToString());
|
||||
|
||||
return File(fileData, "text/plain");
|
||||
}
|
||||
return Redirect(Url.SubRouteUrl("error", "Error.Http404"));
|
||||
}
|
||||
@ -137,7 +101,7 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Paste([Bind(Include = "Content, Title, Syntax, Password, Hide")]PasteCreateViewModel model)
|
||||
public ActionResult Paste([Bind(Include = "Content, Title, Syntax, ExpireLength, ExpireUnit, Password, Hide")]PasteCreateViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
@ -146,6 +110,38 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
Models.Paste paste = db.Pastes.Create();
|
||||
paste.DatePosted = DateTime.Now;
|
||||
paste.Url = Utility.RandomString(Config.PasteConfig.UrlLength);
|
||||
paste.MaxViews = 0;
|
||||
paste.Views = -1;
|
||||
|
||||
// Figure out the expire date (null if 'never' or 'visit')
|
||||
if (model.ExpireLength.HasValue || model.ExpireUnit == "never")
|
||||
{
|
||||
switch (model.ExpireUnit)
|
||||
{
|
||||
case "never":
|
||||
break;
|
||||
case "view":
|
||||
paste.MaxViews = model.ExpireLength ?? 0;
|
||||
break;
|
||||
case "minute":
|
||||
paste.ExpireDate = paste.DatePosted.AddMinutes(model.ExpireLength ?? 1);
|
||||
break;
|
||||
case "hour":
|
||||
paste.ExpireDate = paste.DatePosted.AddHours(model.ExpireLength ?? 1);
|
||||
break;
|
||||
case "day":
|
||||
paste.ExpireDate = paste.DatePosted.AddDays(model.ExpireLength ?? 1);
|
||||
break;
|
||||
case "month":
|
||||
paste.ExpireDate = paste.DatePosted.AddMonths(model.ExpireLength ?? 1);
|
||||
break;
|
||||
case "year":
|
||||
paste.ExpireDate = paste.DatePosted.AddYears(model.ExpireLength ?? 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the hashed password if one is provided and encrypt stuff
|
||||
if (!string.IsNullOrEmpty(model.Password))
|
||||
@ -175,7 +171,7 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
db.Pastes.Add(paste);
|
||||
db.SaveChanges();
|
||||
|
||||
return Redirect(Url.SubRouteUrl("paste", "Paste.View", new { url = paste.Url, password = model.Password }));
|
||||
return Redirect(Url.SubRouteUrl("paste", "Paste.View", new { type = "Full", url = paste.Url, password = model.Password }));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -184,5 +180,15 @@ namespace Teknik.Areas.Paste.Controllers
|
||||
}
|
||||
return View("~/Areas/Paste/Views/Paste/Index.cshtml", model);
|
||||
}
|
||||
|
||||
private bool CheckExpiration(Models.Paste paste)
|
||||
{
|
||||
if (paste.ExpireDate != null && DateTime.Now >= paste.ExpireDate)
|
||||
return true;
|
||||
if (paste.MaxViews > 0 && paste.Views > paste.MaxViews)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@ namespace Teknik.Areas.Paste.Models
|
||||
|
||||
public string Syntax { get; set; }
|
||||
|
||||
public DateTime? ExpireDate { get; set; }
|
||||
|
||||
public string HashedPassword { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
@ -36,5 +38,9 @@ namespace Teknik.Areas.Paste.Models
|
||||
public int BlockSize { get; set; }
|
||||
|
||||
public bool Hide { get; set; }
|
||||
|
||||
public int MaxViews { get; set; }
|
||||
|
||||
public int Views { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -34,14 +34,14 @@ namespace Teknik.Areas.Paste
|
||||
"Paste.Raw", // Route name
|
||||
new List<string>() { "dev", "paste", "p" },
|
||||
"Raw/{url}/{password}", // URL with parameters
|
||||
new { controller = "Paste", action = "Raw", password = UrlParameter.Optional }, // Parameter defaults
|
||||
new { controller = "Paste", action = "ViewPaste", type = "Raw", password = UrlParameter.Optional }, // Parameter defaults
|
||||
new[] { typeof(Controllers.PasteController).Namespace }
|
||||
);
|
||||
context.MapSubdomainRoute(
|
||||
"Paste.Download", // Route name
|
||||
new List<string>() { "dev", "paste", "p" },
|
||||
"Download/{url}/{password}", // URL with parameters
|
||||
new { controller = "Paste", action = "Download", password = UrlParameter.Optional }, // Parameter defaults
|
||||
new { controller = "Paste", action = "ViewPaste", type = "Download", password = UrlParameter.Optional }, // Parameter defaults
|
||||
new[] { typeof(Controllers.PasteController).Namespace }
|
||||
);
|
||||
context.MapSubdomainRoute(
|
||||
|
@ -18,6 +18,11 @@ namespace Teknik.Areas.Paste.ViewModels
|
||||
|
||||
public string Syntax { get; set; }
|
||||
|
||||
[Range(1, int.MaxValue)]
|
||||
public int? ExpireLength { get; set; }
|
||||
|
||||
public string ExpireUnit { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public bool Hide { get; set; }
|
||||
|
@ -13,7 +13,7 @@
|
||||
<div class="container">
|
||||
<div class="row text-center">
|
||||
<div class="col-sm-12 text-center">
|
||||
<h2><b>@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)</b> <small>- Posted on @Model.DatePosted.ToString("dddd, MMMM d, yyyy") at @Model.DatePosted.ToString("h:mm:ss tt") - Format: @Model.Syntax</small></h2>
|
||||
<h2><b>@((string.IsNullOrEmpty(Model.Title)) ? "Untitled" : Model.Title)</b> <small>Posted on @Model.DatePosted.ToString("dddd, MMMM d, yyyy") at @Model.DatePosted.ToString("h:mm:ss tt") - Format: @Model.Syntax</small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="syntax" class="col-sm-2 col-sm-offset-1 control-label">Syntax</label>
|
||||
<div class="col-sm-4">
|
||||
<div class="col-sm-2">
|
||||
<select class="form-control" name="Syntax" id="syntax">
|
||||
<option value="auto-select">Auto Select</option>
|
||||
@foreach (KeyValuePair<string, string> format in Constants.HIGHLIGHTFORMATS)
|
||||
@ -36,6 +36,23 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="syntax" class="col-sm-2 col-sm-offset-1 control-label">Expires</label>
|
||||
<div class="col-sm-1">
|
||||
<input type="number" min="1" step="1" class="form-control" name="ExpireLength" id="expirelength" placeholder="1">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<select class="form-control" name="ExpireUnit" id="expireunit">
|
||||
<option value="never">Never</option>
|
||||
<option value="view">Views</option>
|
||||
<option value="minute">Minutes</option>
|
||||
<option value="hour">Hours</option>
|
||||
<option value="day">Days</option>
|
||||
<option value="month">Months</option>
|
||||
<option value="year">Years</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-2 col-sm-offset-1 control-label">Password</label>
|
||||
<div class="col-sm-4">
|
||||
@ -46,7 +63,7 @@
|
||||
<div class="col-sm-offset-3 col-sm-6">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="Hide" id="hide"> Hide from Public List
|
||||
<input type="checkbox" name="Hide" id="hide" value="true"> Hide from Public List
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user