Mailparser ignores attachments' headers - email-attachments

I'm sending an email with attachments, which look like
const attachment = { filename, path, size, headers: { uid: 'someId' } };
According to the Nodemailer's docs I can set attachment's headers in the same format as message headers.
At the receiver's side the email is parsed by simpleParser (from mailparser). Parsed email looks great, it has all attachment's info but the headers are empty {}. But the raw email source has the following:
----_NmP-30615c8ac620489d-Part_1
Content-Type: image/jpeg; name=attachment.jpg
Uid: someId
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=attachment.jpg
The uid header is there, but it is lost after parsing.
Also tried headers in the following format: headers: [{ header: 'uid', value: 'someId' }]. But it doesn't help.
How can I get that headers correctly? Or this can be the mailparser's bug?

After few hours of mailparser's source code researching I discovered that mailparser returns headers as Map, that's why it looks like Object {} which seems to be empty, but it's not.

Related

Flutter send streamed multipart request not finishing and causing segfault

I'm trying to send a multipart request via http.StreamedRequest. I am aware of http.MultipartRequest, however, it does not fit my needs (the API I'm trying to send the request to encodes arrays by sending the same attribute multiple times, which is not possible using the fields parameter of http.MultipartRequest since it is a Map<String, String>, which does not allow the same key to be inserted multiple times).
The file (with form-data name document) I'm trying to send is provided in form of a Uint8List (a .pdf file), in addition to a filename (in the following "some_filename.pdf", a pdf title (in the following "some_document_title") and an array of numbers (which I'm trying to add to the multipart request) with the attribute tags.
For instance, I want the array tags=[1, 2, 3], title="some_document_title" and file to be encoded as follows:
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="title"
Content-type: text/plain
some_document_title
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
1
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
2
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="tags"
Content-type: text/plain
3
--dart-http-boundary-rVI-TO32QXZlS.VAGKWpuhbt99woLKVVvjToGoT-MVOU1YGSpnQ
Content-Disposition: form-data; name="document"; filename="some_filename.pdf"
Content-type: application/octet-stream
After this part, I want to stream the file data.
The rough implementation looks like this:
Future<void> createDocument(Uint8List file, String filename, String title, List<int> tags) {
final StreamedRequest request = StreamedRequest('POST', Uri.parse('some_url'));
String boundary = "..."; // Generate random boundary string
String contentType = "application/octet-stream";
String body = "";
Map<String, String> fields = {
"title": title,
// Some more fields left out for readability
};
for (final fieldKey in fields.keys) {
body += _buildMultipartField(fieldKey, fields[fieldKey], boundary);
}
for (final tag in tags) {
body += _buildMultipartField('tags', tag.toString(), boundary);
}
// Prepare file part
body += "--$boundary" +
'\r\nContent-Disposition: form-data; name="document"; filename="$filename"' +
"\r\nContent-type: $contentType" +
"\r\n\r\n";
final closing = "\r\n--" + boundary + "--\r\n";
request.headers.addAll({
"Content-Type": "multipart/form-data; boundary=$boundary",
"Content-Length": "${body.length + closing.length + file.lengthInBytes}"
});
// Add current body
request.sink.add(utf8.encode(body));
Stream.fromIterable(file).listen((chunk) => request.sink.add([chunk]))
.onDone(() {
// Add closing to multipart request
request.sink.add(utf8.encode(closing));
request.sink.close();
});
final StreamdResponse response = await IOClient().send(request);
if (response.statusCode != 200) {
throw Error();
}
}
String _buildMultipartField(String fieldName, String value, String boundary) {
return '--$boundary' +
'\r\nContent-Disposition: form-data; name="$fieldName"' +
'\r\nContent-type: text/plain' +
'\r\n\r\n' +
value +
'\r\n';
}
Now, my current implementation produces the expected body, which is good, however, the streamed request does not seem to finish, and according to the DevTools Network page, the request is never sent.
Is the content-length relevant for the request to finish? I.e. is it possible that the request is not finishing because the server is still expecting data because the content-length is calculated incorrectly?
Also, I'm getting a segfault when hot restarting the application when the request is still pending (it never returns).
Any help is appreciated, thanks!

How to force Google Storage file to have `content-disposition: inline` instead of `attachment`?

I'm using GoogleApi.Storage.V1.Api.Objects.storage_objects_insert_simple to upload files to Google Storage. However, it seems that no matter what I do, the content-disposition HTTP response header is being set to attachment which forces browsers to download a file rather than displaying it in a browser tab.
Here's my usage:
{:ok, object} =
GoogleApi.Storage.V1.Api.Objects.storage_objects_insert_simple(
gconn,
"my-uploads-bucket",
"multipart",
%{
name: Path.basename(path),
contentType: content_type,
contentDisposition: "inline" # <-- this doesn't seem to have an effect
},
path
)
Does anyone know how to force the content disposition header to have a value of inline?
Update
After inspecting the value of object, I'm seeing the following:
%GoogleApi.Storage.V1.Model.Object{
acl: nil,
bucket: "my-uploads-bucket",
cacheControl: nil,
componentCount: nil,
contentDisposition: "inline", # <-- !!!
contentEncoding: nil,
contentLanguage: nil,
contentType: "image/png",
crc32c: "MGa01Q==",
...
However, after fetching a file via curl -I <url>, I see the following:
HTTP/2 200
...
content-type: image/png
content-disposition: attachment
...
It seems like my mistake here was in using the mediaLink field as the URL. This URL even has download as part of the URL: https://storage.googleapis.com/download/storage/....
If I instead use name field to build the URL, then it works as expected.
send_resp(
conn,
200,
"https://storage.googleapis.com/my-bucket/" <> object.name
)

Open API 3 - add headers on individual content-type in responses

I have my spec which have a path with a 200 response code, that response code can access multiple content-types, I want to add the Content-Disposition Header to one of those content-types.
Here's a sample:
openapi: '3.0.3'
info:
...
servers:
...
paths:
/examples:
...
get:
...
responses:
'200':
content:
application/json:
...
application/pdf:
encoding:
file:
headers:
Content-Disposition:
schema:
type: string
example: attachment; filename="name.pdf"
examples:
file:
summary: File
externalValue: https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
Here's the generated view:
no header
Here is an example where the header is added (for another endpoint)
responses:
'201':
description: Success
headers:
Location:
schema:
type: string
format: uri
description: The URI to the newly created example
And here's the generated view for that one:
with header
Am I doing something wrong?
encoding.<name>.headers is used to define headers for individual parts of a multipart/* request body, which is different from your scenario. Since your response is not multipart/*, the response headers must be defined in responses.<code>.headers.
However, OpenAPI does not have a way to vary response headers per media type. What you can do is define the Content-Disposition response header as optional and explain that it only applies to applicatioln/pdf responses.
paths:
/examples:
get:
responses:
'200':
description: ok
content:
application/pdf:
schema:
type: string
format: binary
headers:
Content-Disposition:
schema:
type: string
description: Used only with `application/pdf` responses
example: attachment; filename="name.pdf"

Paypal UNSUPPORTED_MEDIA_TYPE

I am trying to get an access token from paypal's authorization api.
When I make post request to the api I get UNSUPPORTED_MEDIA_TYPE i.e. 415 response.
Below is the snippet that I used.
const auth = await fetch(PAYPAL_OAUTH_API, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${ basicAuth }`
},
body: JSON.stringify({"grant_type": "client_credentials"})
});
I have fixed my issue by setting Content-Type to application/x-www-form-urlencoded.
My guess is paypal accepts only application/x-www-form-urlencoded for authorization api.
I ran into same issue, and the solution is following (using Postman):
Select POST
Add Token into Authorization, type is Bearer-Token
Select Content-Type: application/json in headers
Use RAW as body, and in TEXT dropdown, select JSON (application/JSON)
Copy body as raw object and change info accordingly.
Step 4 and 5 are what solved the error, you must send raw json object.

JSON encoded exceptions in TYPO3 extbase controller with JsonView

For my extbase-based TYPO3 CMS extension I created an ApiController with JsonView as view object. Returning values works like a charm, the correct header Content-type: application/json is set.
To return other responses like missing authorization messages or validation errors, I currently use:
$data = ["errors" => [
"status" => 401,
"message" => "Missing access token"
]];
$this->throwStatus($status, null, json_encode($data));
When I use $this->throwStatus() the header Content-type: text/html is set. Even if I manually set header("Content-type: application/json"); before using $this->throwStatus().
How can I create responses with the correct content type header?
Before you throw the status, try to set the headers in the response object:
$this->response->setHeader('Content-Type', 'application/json', true);
$this->response->sendHeaders();
If you are accessing your data through a dedicated pageType, you can set the header for this pageType in TypoScript:
myPageType.config.additionalHeaders {
10 {
header = Content-Type: application/json
replace = 1
}
}
I will add this to my post about the topic: https://usetypo3.com/json-view.html