Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Obsolete text field "attachment name..." #979

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 61 additions & 12 deletions netbout-web/src/main/java/com/netbout/rest/bout/TkAttach.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@
import eu.medsea.mimeutil.detector.MagicMimeMimeDetector;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.takes.Request;
import org.takes.Response;
import org.takes.Take;
Expand All @@ -63,6 +65,7 @@
* @since 2.14
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
*/
@SuppressWarnings("PMD.ExcessiveImports")
final class TkAttach implements Take {

/**
Expand Down Expand Up @@ -93,17 +96,15 @@ final class TkAttach implements Take {

@Override
public Response act(final Request req) throws IOException {
final Request file = new RqMultipart.Smart(
final RqMultipart.Smart form = new RqMultipart.Smart(
new RqMultipart.Base(req)
).single("file");
);
final Request file = form.single("file");
final String name = this.filename(form, file);
final Bout bout = new RqBout(this.base, req).bout();
final String name = this.name(file);
final File temp = File.createTempFile("netbout", "bin");
try {
try (OutputStream os = new FileOutputStream(temp)) {
IOUtils.copy(file.body(), os);
}
this.validate(temp, name);
this.copyAndValidate(file, temp, name);
final StringBuilder msg = new StringBuilder(Tv.HUNDRED);
if (new Attachments.Search(bout.attachments()).exists(name)) {
msg.append(
Expand Down Expand Up @@ -137,12 +138,43 @@ public Response act(final Request req) throws IOException {
}

/**
* Extracts file name.
* Reads the filename either from the name field or from the uploaded file.
* @todo #865:30min write a test for this method. It should make sure that
* if there is a name part in the form and it is not empty then it is
* taken and if not, the name of the uploaded file is used.
* @param form From
* @param file File
* @return File name
* @return String name
* @throws IOException If fails
*/
private String name(final Request file) throws IOException {
private String filename(final RqMultipart.Smart form, final Request file)
throws IOException {
final String name;
final Iterable<Request> requests = form.part("name");
if (requests.iterator().hasNext()) {
name = IOUtils.toString(
requests.iterator().next().body(),
StandardCharsets.UTF_8
);
} else {
name = "";
}
final String filename;
if (StringUtils.isBlank(name)) {
filename = this.nameFromFile(file);
} else {
filename = name;
}
return filename;
}

/**
* Extracts name from file part.
* @param file File
* @return String name
* @throws IOException If fails
*/
private String nameFromFile(final Request file) throws IOException {
final Matcher matcher = TkAttach.FILE_NAME_PATTERN.matcher(
new RqHeaders.Smart(
new RqHeaders.Base(file)
Expand All @@ -155,6 +187,23 @@ private String name(final Request file) throws IOException {
return URLDecoder.decode(matcher.group(5), CharEncoding.UTF_8);
}

/**
* Copy the multipart data to a file and validate it.
* @param src Multipart request
* @param dst File
* @param name Filename
* @throws IOException If copy or validate fails
*/
private void copyAndValidate(final Request src, final File dst,
final String name) throws IOException {
Files.copy(
src.body(),
dst.toPath(),
StandardCopyOption.REPLACE_EXISTING
);
this.validate(dst, name);
}

/**
* Checks some limitations on attachment.
* @param attach Temportary file with attachment
Expand Down
12 changes: 12 additions & 0 deletions netbout-web/src/main/js/bout.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@

/*globals $:false, document:false, window:false */

function cleanFilename(name) {
/* jslint forbidds [^a-z], but it's needed here */
/*jslint regexp: false*/
var cleaned = name.split(/[\/\\]/).pop()
.replace(/[^a-zA-Z\.\-0-9]+/g, "-")
.replace(/[\-]+/g, "-")
.replace(/^-/, "")
.replace(/-$/, "");
/*jslint regexp: true*/
return cleaned;
}

function escapeHTML(txt) {
"use strict";
return txt.replace(/&/g, '&amp;')
Expand Down
2 changes: 1 addition & 1 deletion netbout-web/src/main/xsl/bout.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@
enctype="multipart/form-data">
<fieldset>
<input id="file-name" name="name" autocomplete="off" placeholder="attachment name..." size="30" maxlength="50"/>
<input id="file-binary" name="file" type="file"/>
<input id="file-binary" name="file" type="file" onchange="$('#file-name').val(cleanFilename($(this).val()))" />
<label for="file-submit"/>
<input id="file-submit" type="submit" value="Upload"/>
</fieldset>
Expand Down
48 changes: 48 additions & 0 deletions netbout-web/src/test/java/com/netbout/rest/bout/TkAttachTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,54 @@ public void ignoresWrongFile() throws Exception {
}
}

/**
* Take attachement name from name field if value is set.
* @throws Exception If there is some problem inside
*/
@Test
public void takeAttachementNameFromField() throws Exception {
final MkBase base = new MkBase();
final String urn = "urn:test:4";
final User user = base.user(new URN(urn));
user.aliases().add("jeff4");
final Alias alias = user.aliases().iterate().iterator().next();
final long number = alias.inbox().start();
final Bout bout = alias.inbox().bout(number);
bout.friends().invite(alias.name());
final String name = "test.jpg";
final String filename = "tt.jpg";
final RqWithAuth request = new RqWithAuth(
urn,
new RqMultipart.Fake(
TkAttachTest.fake(number),
new RqWithHeaders(
TkAttachTest.body(""),
String.format(TkAttachTest.POST_URL, number),
//@checkstyle LineLengthCheck (1 line)
String.format("Content-Disposition: form-data; filename=\"%s\"; name=\"file\"", filename),
"Content-Type: application/json"
),
new RqWithHeaders(
TkAttachTest.body(name),
String.format(TkAttachTest.POST_URL, number),
"Content-Disposition: form-data; name=\"name\""
)
)
);
try {
new FkBout(".++", new TkAttach(base)).route(request);
} catch (final RsForward response) {
MatcherAssert.assertThat(
response,
new HmRsStatus(HttpURLConnection.HTTP_SEE_OTHER)
);
}
MatcherAssert.assertThat(
bout.messages().iterate().iterator().next().text(),
Matchers.containsString(String.format("attachment \"%s\"", name))
);
}

/**
* Creates fake request for the provided bout number.
* @param number Bout number
Expand Down