diff --git a/assets/js/indigo.js b/assets/js/indigo.js index a520e50..727aa7f 100755 --- a/assets/js/indigo.js +++ b/assets/js/indigo.js @@ -654,6 +654,7 @@ var Olv = Olv || {}; onDataHrefClick: function(c) { if (a(c.target).attr("data-href")) { b.Net.go($(this).attr("data-href")); + return; } if (!c.isDefaultPrevented() && !a(c.target).closest("a,button").length) { var d = a(this); @@ -3782,33 +3783,38 @@ function updateTime() { var timestamp = timestamps.eq(i); var since = new Date().getTime() - timestamp.attr('time'); var time = new Date(since); + var timestampText; if(time < 1000) { - timestamp.text('Less than a second ago'); + timestampText = 'Less than a second ago'; } else if(time < 2000) { - timestamp.text('1 second ago'); + timestampText = '1 second ago'; } else if(time < 60000) { - timestamp.text(Math.floor(since / 1000) + ' seconds ago'); + timestampText = Math.floor(since / 1000) + ' seconds ago'; } else if(time < 120000) { - timestamp.text('1 minute ago'); + timestampText = '1 minute ago'; } else if(time < 3600000) { - timestamp.text(Math.floor(since / 1000 / 60) + ' minutes ago'); + timestampText = Math.floor(since / 1000 / 60) + ' minutes ago'; } else if(time < 7200000) { - timestamp.text('1 hour ago'); + timestampText = '1 hour ago'; } else if(time < 86400000) { - timestamp.text(Math.floor(since / 1000 / 60 / 60) + ' hours ago'); + timestampText = Math.floor(since / 1000 / 60 / 60) + ' hours ago'; } else if(time < 172800000) { - timestamp.text('1 day ago'); + timestampText = '1 day ago'; } else if(time < 345600000) { - timestamp.text(Math.floor(since / 1000 / 60 / 60 / 24) + ' days ago'); + timestampText = Math.floor(since / 1000 / 60 / 60 / 24) + ' days ago'; } else { var dateTime = new Date(parseInt(timestamp.attr('time'))); var mariosPrincessSex = dateTime.getHours() % 12; if(mariosPrincessSex == 0) { mariosPrincessSex = 12; } - timestamp.text((dateTime.getMonth() + 1).toString().padStart(2, "0") + "/" + dateTime.getDate().toString().padStart(2, "0") + "/" + dateTime.getFullYear() + " " + mariosPrincessSex + ":" + dateTime.getMinutes().toString().padStart(2, "0") + " " + (dateTime.getHours() >= 12 ? "PM" : "AM")); + timestampText = (dateTime.getMonth() + 1).toString().padStart(2, "0") + "/" + dateTime.getDate().toString().padStart(2, "0") + "/" + dateTime.getFullYear() + " " + mariosPrincessSex + ":" + dateTime.getMinutes().toString().padStart(2, "0") + " " + (dateTime.getHours() >= 12 ? "PM" : "AM"); timestamp.removeClass('update'); } + // only update the timestamp in the dom if it actually changed + if(timestamp[0].innerHTML != timestampText) { + timestamp.text(timestampText); + } } } updateTime(); diff --git a/assets/js/upload.js b/assets/js/upload.js index 089ee58..59ed9dc 100755 --- a/assets/js/upload.js +++ b/assets/js/upload.js @@ -84,96 +84,104 @@ function postFile(file, fileType, isDrawing, inputName) { } }); } -function init() { - $(".file-button").off().on("change", function(event) { - console.log(event); - var inputName = "image"; - if($(this).attr("id") !== undefined) inputName = $(this).attr("id"); - if(this.files.length) { - Olv.Form.toggleDisabled($("input.post-button"), true); - $("input[name=" + inputName + "]").siblings(".file-button").attr("disabled", "disabled"); - $("input[name=" + inputName + "]").siblings(".file-upload-button").text("Uploading..."); - var fileType = this.files[0].type; - var file = this.files[0]; - if(($(".file-upload-button").hasClass("for-avatar") || inputName === "icon") && fileType !== "image/gif") { - var img = new Image(); - img.src = URL.createObjectURL(file); - img.onload = function() { - var canvas = document.createElement("canvas"); - var ctx = canvas.getContext("2d"); - ctx.imageSmoothingQuality = "high"; - var size = 128, factor, startX, startY, resizeWidth, resizeHeight; - canvas.width = size; - canvas.height = size; - if(img.width > img.height) { - factor = img.width / img.height; - startX = (img.width - img.height) / 2; - startY = 0; - resizeWidth = size * factor; - resizeHeight = size; - } else if(img.height > img.width) { - factor = img.height / img.width; - startX = 0; - startY = (img.height - img.width) / 2; - resizeWidth = size; - resizeHeight = size * factor; - } else { - factor = 1; - startX = 0; - startY = 0; - resizeWidth = size; - resizeHeight = size; - } - ctx.drawImage(img, startX, startY, img.width, img.height, 0, 0, resizeWidth, resizeHeight); - canvas.toBlob(function(blob) { - postFile(blob, fileType, false, inputName); - }); + + +function handleChange(event) { + console.log(event); + var inputName = "image"; + if($(this).attr("id") !== undefined) inputName = $(this).attr("id"); + if(this.files.length) { + Olv.Form.toggleDisabled($("input.post-button"), true); + $("input[name=" + inputName + "]").siblings(".file-button").attr("disabled", "disabled"); + $("input[name=" + inputName + "]").siblings(".file-upload-button").text("Uploading..."); + var fileType = this.files[0].type; + var file = this.files[0]; + if(($(".file-upload-button").hasClass("for-avatar") || inputName === "icon") && fileType !== "image/gif") { + var img = new Image(); + img.src = URL.createObjectURL(file); + img.onload = function() { + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + ctx.imageSmoothingQuality = "high"; + var size = 128, factor, startX, startY, resizeWidth, resizeHeight; + canvas.width = size; + canvas.height = size; + if(img.width > img.height) { + factor = img.width / img.height; + startX = (img.width - img.height) / 2; + startY = 0; + resizeWidth = size * factor; + resizeHeight = size; + } else if(img.height > img.width) { + factor = img.height / img.width; + startX = 0; + startY = (img.height - img.width) / 2; + resizeWidth = size; + resizeHeight = size * factor; + } else { + factor = 1; + startX = 0; + startY = 0; + resizeWidth = size; + resizeHeight = size; } - } else { - postFile(file, fileType, false, inputName); + ctx.drawImage(img, startX, startY, img.width, img.height, 0, 0, resizeWidth, resizeHeight); + canvas.toBlob(function(blob) { + postFile(blob, fileType, false, inputName); + }); } - } else { - $("input[name=image]").val(""); - if(!$(".file-upload-button").hasClass("for-avatar")) { - $(".preview-container").css("display", "none"); - checkForm(); + } else { + postFile(file, fileType, false, inputName); } + } else { + $("input[name=image]").val(""); + if(!$(".file-upload-button").hasClass("for-avatar")) { + $(".preview-container").css("display", "none"); + checkForm(); } - }); - $(document).on("dragover dragenter", function(event) { - event.stopPropagation(); - event.preventDefault(); - event.originalEvent.dataTransfer.dropEffect = "copy"; - }); - $(document).on("drop paste", function(event) { - if($(this).siblings(".file-button").attr("disabled")) { - return; - } - var files; - switch(event.type) { - case "drop": - files = event.originalEvent.dataTransfer.files; - break; - case "paste": - if(event.originalEvent.clipboardData.files.length == 0) { - return; - } - files = event.originalEvent.clipboardData.files; - break; - default: + } +} + +function handleDropPaste(event) { + if($(this).siblings(".file-button").attr("disabled")) { + return; + } + var files; + switch(event.type) { + case "drop": + files = event.originalEvent.dataTransfer.files; + break; + case "paste": + if(event.originalEvent.clipboardData.files.length == 0) { return; - } - event.stopPropagation(); - event.preventDefault(); + } + files = event.originalEvent.clipboardData.files; + break; + default: + return; + } + event.stopPropagation(); + event.preventDefault(); - if(files[0].type.startsWith("image/") || files[0].type.startsWith("audio/") || files[0].type.startsWith("video/")) { - $(".file-button")[0].files = files; - $(".file-button").trigger("change"); - } else { - Olv.showMessage("Attachment upload failed", "You can only upload images, audio or videos."); - } - }); + if(files[0].type.startsWith("image/") || files[0].type.startsWith("audio/") || files[0].type.startsWith("video/")) { + $(".file-button")[0].files = files; + $(".file-button").trigger("change"); + } else { + Olv.showMessage("Attachment upload failed", "You can only upload images, audio or videos."); + } +} + +function handleDrag(event) { + event.stopPropagation(); + event.preventDefault(); + event.originalEvent.dataTransfer.dropEffect = "copy"; +} + +function init() { + $(".file-button").off().on("change", handleChange); + $(document).off("dragover dragenter").on("dragover dragenter", handleDrag); + $(document).off("drop paste").on("drop paste", handleDropPaste); } -$(document).off("ready, pjax:end", init).on("ready, pjax:end", init); +$(document).off("ready", init).off("pjax:end", init).on("ready", init).on("pjax:end", init); init(); diff --git a/config.json b/config.json index 01d61aa..c75c721 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,7 @@ "SocketOwner": "www-data", "Port": ":80", "GzipEnabled": true, + "CSRFProtectDisable": true, "SSL": { "Enabled": false, "Certificate": "cert.pem", diff --git a/handlers.go b/handlers.go index c419426..c1d291c 100755 --- a/handlers.go +++ b/handlers.go @@ -15,6 +15,7 @@ import ( "io" "io/ioutil" "path/filepath" + "mime" "mime/multipart" "net" "net/http" @@ -6281,7 +6282,15 @@ func uploadImage(w http.ResponseWriter, r *http.Request) { return } case "local": - imageFilePath := settings.ImageHost.ImageEndpoint + "/" + hash + filepath.Ext(handler.Filename) + fileExtension := filepath.Ext(handler.Filename) + if fileExtension == "" { + // if extension is not provided then use mime type + extensions, err := mime.ExtensionsByType(handler.Header.Get("Content-Type")) + if err == nil && len(extensions) != 0 { + fileExtension = extensions[0] // Use the first extension in the list + } + } + imageFilePath := settings.ImageHost.ImageEndpoint + "/" + hash + fileExtension outputFile, err := os.Create(imageFilePath) if err != nil { http.Error(w, "Could not create output file: " + err.Error(), http.StatusInternalServerError) diff --git a/main.go b/main.go index c5dbe5d..2ee583a 100755 --- a/main.go +++ b/main.go @@ -291,13 +291,17 @@ func main() { r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets")))) r.PathPrefix("/images/").Handler(http.StripPrefix("/images/", http.FileServer(http.Dir("images")))) - // Tell the http server to handle routing with the router we just made. + var handler http.Handler = r + + if !settings.CSRFProtectDisable { + handler = CSRF(r) + } if settings.GzipEnabled { - http.Handle("/", gziphandler.GzipHandler(CSRF(r))) - } else { - http.Handle("/", CSRF(r)) + handler = gziphandler.GzipHandler(handler) } + // Tell the http server to handle routing with the router we just made. + http.Handle("/", handler) // Tell the person who started this that we are starting the server. log.Printf("listening on " + settings.Port) @@ -308,10 +312,10 @@ func main() { unixListener, err := net.Listen("unix", settings.Port) if err != nil { - log.Fatal("cannot listen on unix socket:", err) + log.Fatal("cannot listen on unix socket: ", err) } - // hac + // set socket owner but only if the value is not blank if settings.SocketOwner != "" { socketUser, err := osUser.Lookup(settings.SocketOwner) if err != nil { diff --git a/types.go b/types.go index 1c46ad6..8917109 100755 --- a/types.go +++ b/types.go @@ -72,12 +72,16 @@ type community struct { type config struct { // if this is true, then it will listen on a unix socket instead of a tcp port ListenSocket bool + // this can be left blank if you are listening and accessing as the same user SocketOwner string // if listensocket is true then port is the socket Port string - // whether to enable gzip compression, unnecessary with a reverse proxy, absolutely necessary without one + // whether to enable gzip compression + // unnecessary with a reverse proxy, absolutely necessary without one GzipEnabled bool - SSL struct { + // will disable csrf protection which you probably want to do because it's annoying + CSRFProtectDisable bool + SSL struct { Enabled bool Certificate string Key string