This article is part of Managing bug report in issue tracker series.
Jira allows you to submit reports anonymously without using API. This is an alternative method to submit bug reports. It is called Issue Collector in Jira. Issue collectors are available for both Jira bug tracker ("just Jira", "Jira Software") as well as Jira Service Desk ("Jira Service Management").
Note: anonymous submitting may be disabled - please, refer to your Jira configuration and documentation.
Warning: each sent report will be tracked individually - as new issue in Jira.
Below are examples for setting up anonymous HTTP upload for Jira.
Creating Issue Collector
1. | Open properties/settings of your project in Jira; |
2. | Find "Issue collectors" item in project settings' menu; |
3. | Click on "Add issue collector": |
4. | Enter arbitrary name and description; |
5. | Set the "Issue Type" option to "Bug" (or "Incident", or "Problem"); |
6. | Set the "Reporter" option to any account. This account will be used by default to create new bugs; |
7. | [Optionally] Check the "Attempt to match submitter email address" option; |
8. | Disable the "Collects the environment data of the user" option; |
9. | Set the "Trigger" option to "Custom"; |
10. | Set the "Issue collector form" option to "Custom" and select at least "Description" and "Attach file". Optionally check "Components", "Environment", and "Affects versions". You may also check additional fields if you think you can supply values for these fields (see below); |
11. | Click the "Submit" button to create new issue collector. |
New Issue Collector was created
12. | Copy the provided HTML code for embedding; |
13. | Create new empty text file; |
14. | Paste the following text: |
<html>
<body>
-- place the copied HTML code here --
<input
type="button"
id="myCustomTrigger"
name="myCustomTrigger"
value="Test Submit Bug">
</body>
</html>
15. | Save text file as Test.html anywhere; |
16. | Double-click on the saved Test.html file to open it in a web browser; |
17. | You should see a single "Test Submit Bug" button. Click on it: |
Testing Bug Submission Form
Inspecting the Web-Form
Right click on any element in the form and select "Inspect" or "View Source" command from the popup menu of your web browser;
The form that you see should be loaded as a nested frame inside:
<iframe id="atlwdg-frame" scrolling="no" frameborder="0"
src="https://your-address.atlassian.net/rest/collectors/1.0/template/form/your-collector-id?os_authType=none">
Ignore content of the Test.html file (including the mentioned tag), find the nested frame and work with it.
The frame should have a <form> tag like this:
<form id="jic-collector-form" class="aui "
action="https://your-address.atlassian.net/rest/collectors/1.0/template/custom/your-collector-id"
method="POST">
Copy an URL for the "action" property. This URL will be used as a value for the "URL" property of the "HTTP upload" settings in EurekaLog. Also, copy / remember the ID for the collector.
Scroll down the form and find these tags:
<input type="hidden" title="projectKey" value="your-project-key">
<input type="hidden" title="issueType" value="your-issue-id">
<input type="hidden" title="collectorId" value="your-collector-id">
<input type="hidden" name="pid" value="your-project-id">
<select class="select hidden multi-select-select"
id="components" multiple="multiple"
name="components"
size="3"
data-remove-null-options="true"
data-submit-input-val="true"
data-input-text=""
data-create-permission="true"
style="display: none;">
<option title="Your-Component-1 " value="Your-Component-1-ID">
Your-Component-1
</option>
<option title="Your-Component-2 " value="Your-Component-2-ID">
Your-Component-2
</option>
...
</select>
<select class="select hidden multi-select-select"
id="versions"
multiple="multiple"
name="versions"
size="5"
data-remove-null-options="true"
data-submit-input-val="true"
data-input-text=" "
data-create-permission="true"
style="display: none;">
<optgroup label="Released Versions">
<option value="Your-Version-1-ID">
Your-Version-1
</option>
<option value="Your-Version-2-ID">
Your-Version-2
</option>
...
</optgroup>
<optgroup label="Unreleased Versions">
<option value="Your-Version-3-ID">
Your-Version-3
</option>
<option value="Your-Version-4-ID">
Your-Version-4
</option>
...
</optgroup>
</select>
Tags for components and versions will not be shown if you did not select the corresponding options when creating issue collector.
Save or write down all IDs. These will be used later (see below).
Setting up the HTTP Upload method
First, start with specifying HTTP upload method:
Typical HTTP upload setup for submitting report anonymously in Jira
Paste the URL that you have retrieved on the previous step while inspecting HTML for the web form.
Additionally, specify HTTP headers:
Origin: https://your-account.atlassian.net
Content-Type: application/x-www-form-urlencoded;charset=utf-8
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: iframe
Referer: https://your-account.atlassian.net/rest/collectors/1.0/template/form/your-collector-id?os_authType=none
Alternatively, you may set up these options at run-time:
uses
ESend, ESendWebHTTP, EModules, EConsts, ETypes;
const
GCollectorID = 'your-collector-id';
GAccountID = 'your-account';
initialization
// Set up HTTP options
CurrentEurekaLogOptions.SendHTTPURL := GAccountID +
'.atlassian.net/rest/collectors/1.0/template/custom/' + GCollectorID;
CurrentEurekaLogOptions.SendHTTPPort := 443;
CurrentEurekaLogOptions.SendHTTPSSL := True;
CurrentEurekaLogOptions.SendHTTPHeaders :=
'Origin: https://' + GAccountID + '.atlassian.net' + sLineBreak +
'Content-Type: application/x-www-form-urlencoded;charset=utf-8' + sLineBreak +
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,' +
'*/*;q=0.8,application/signed-exchange;v=b3;q=0.9' + sLineBreak +
'Sec-Fetch-Site: same-origin' + sLineBreak +
'Sec-Fetch-Mode: navigate' + sLineBreak +
'Sec-Fetch-User: ?1' + sLineBreak +
'Sec-Fetch-Dest: iframe' + sLineBreak +
'Referer: https://' + GAccountID +
'.atlassian.net/rest/collectors/1.0/template/form/' + GCollectorID +
'?os_authType=none';
// Register sender (if not already registered)
if Pos(TELHTTPSender.ClassName, CurrentEurekaLogOptions.SenderClasses) <= 0 then
CurrentEurekaLogOptions.AddSenderClass(TELHTTPSender.ClassName, 0);
end.
You can also add fixed HTTP fields. In other words, fields that will not change at run-time:
screenshot=
_tricky_=
webInfo=
projectKey=your-project-key
issueType=your-issue-id
pid=your-project-id
collectorId=your-collector-id
versions=your-version-id
components=your-component-id
(versions and components are optional fields)
This is only an example. Actual field names and values depend on your actual Jira collector's code. Please, study source code of your bug submission HTML form to learn which fields should be included.
Alternatively, you may set up these options at run-time - see below.
Setting up POST data
Second, you need to supply custom fields. Use OnCustomWebFieldsRequest event handler:
uses
Registry, EEvents, ETypes, EConsts, EClasses, ESend, ESendWeb,
ESendWebHTTP, ESysInfo, EWebTools, EEncoding, EFileMemory, EJSON;
const
GCollectorID = 'your-collector-id';
GAccountID = 'your-account';
GPID = 'your-project-id';
GProjectKey = 'your-project-key';
GIssueID = 'your-issue-id';
GVersionID = 'your-version-id';
GComponentID = 'your-component-id';
// Defines HTTP fields for web-form
procedure SetCustomFields(const ACustom: Pointer; AExceptionInfo: TEurekaExceptionInfo;
ASender: TObject; AWebFields: TStrings; var ACallNextHandler: Boolean);
function ComposeTitle(const AOptions: TEurekaModuleOptions): String;
var
BugAppVersion: String;
BugType: String;
BugID: String;
begin
BugAppVersion := AOptions.CustomField[sifBugAppVersion];
BugType := AOptions.CustomField[sifBugType];
BugID := AOptions.CustomField[sifBugID];
if BugAppVersion <> '' then
Result := Format('%s (Bug %s; v%s)', [BugType, BugID, BugAppVersion])
else
Result := Format('%s (Bug %s)', [BugType, BugID]);
end;
function ComposeMessage(const AOptions: TEurekaModuleOptions): String;
var
BugText: String;
ReproduceText: String;
begin
BugText := AOptions.CustomField[sifBugText];
ReproduceText := AOptions.CustomField[sifStepsToReproduce];
if ReproduceText <> '' then
Result := BugText + sLineBreak + sLineBreak + ReproduceText
else
Result := BugText;
end;
var
Options: TEurekaModuleOptions;
AttachedFiles: TStringList;
Token: String;
begin
Token := GetCollectorToken(ASender);
Options := nil;
AttachedFiles := TStringList.Create;
try
if ASender is TELUniversalSender then
begin
Options := TELUniversalSender(ASender).Options;
AttachedFiles.Assign(TELUniversalSender(ASender).AttachedFiles);
end
else
Options := TEurekaModuleOptions.Create('');
// security field:
AWebFields.Values['atl_token'] := Token;
// hidden fields:
AWebFields.Values['screenshot'] := '';
AWebFields.Values['_tricky_'] := '';
AWebFields.Values['webInfo'] := '';
// dynamic fields:
AWebFields.Values['summary'] := ComposeTitle(Options);
AWebFields.Values['description'] := ComposeMessage(Options);
AWebFields.Values['fullname'] := GetUserFullName;
AWebFields.Values['email'] := Options.CustomField[sifUserEMail];
AWebFields.Values['environment'] :=
Format('Computer: %s' + sLineBreak +
'OS: %s' + sLineBreak +
'Build: %s',
[Options.CustomField[sifMachineID],
Options.CustomField[sifOSType],
Options.CustomField[sifOSBuild]]);
AWebFields.Values['filetoconvert'] :=
UploadFilesForJiraCollector(ASender, AttachedFiles, Token);
// editable fields - refer to page's source
// this is only an example!
// remove any field that you have already entered in
// HTTP upload's options
AWebFields.Values['projectKey'] := GProjectKey;
AWebFields.Values['issueType'] := GIssueID;
AWebFields.Values['pid'] := GPID;
AWebFields.Values['collectorId'] := GCollectorID;
AWebFields.Values['versions'] := GVersionID; // optional
AWebFields.Values['components'] := GComponentID; // optional
finally
FreeAndNil(AttachedFiles);
if not (ASender is TELUniversalSender) then
FreeAndNil(Options);
end;
end;
initialization
// Define HTML content
RegisterEventCustomWebFieldsRequest(nil, SetCustomFields);
end.
Note: study submission form's page source to get information on field names and values. The code above is just an example!
Retrieving Security Token
Use this function:
// Retrieves random generated 'atlassian.xsrf.token' cookie
function GetCollectorToken(const ASender: TObject): String;
function Query(const AConnect: HINTERNET): String;
function OpenRequest(const AConnect: HINTERNET): HINTERNET;
const
Headers = 'Accept: text/html,application/xhtml+xml,' +
'application/xml;q=0.9,*/*;q=0.8,' +
'application/signed-exchange;v=b3;q=0.9' + sLineBreak +
'Sec-Fetch-Site: cross-site' + sLineBreak +
'Sec-Fetch-Mode: navigate' + sLineBreak +
'Sec-Fetch-Dest: iframe' + sLineBreak;
const
SECURITY_FLAG_IGNORE_REVOCATION = $80;
var
dwOpenRequestFlags, dwBuffLen, dwFlags: DWord;
bResult: Boolean;
begin
dwOpenRequestFlags := (
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP or
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS or
INTERNET_FLAG_KEEP_CONNECTION or
INTERNET_FLAG_NO_AUTO_REDIRECT or
INTERNET_FLAG_NO_UI or
INTERNET_FLAG_RELOAD or
SECURITY_FLAG_IGNORE_REVOCATION or
SECURITY_FLAG_IGNORE_UNKNOWN_CA or
INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
INTERNET_FLAG_NO_CACHE_WRITE or
// SSL Settings:
INTERNET_FLAG_SECURE or
INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID);
Result := HttpOpenRequest(AConnect, 'GET',
'/rest/collectors/1.0/template/form/' + GCollectorID +
'?os_authType=none', nil, nil, nil, dwOpenRequestFlags, nil);
CheckWinInetError(Result = nil);
// SSL options:
dwBuffLen := SizeOf(dwFlags);
InternetQueryOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwBuffLen);
dwFlags := (dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA);
InternetSetOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags));
// Remove old headers
HttpAddRequestHeaders(Result, 'Accept: ' + sLineBreak + 'Content-Type: ' + sLineBreak,
DWord(-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);
// Add new headers
bResult := HttpAddRequestHeaders(Result, PChar(Headers), DWord(-1), HTTP_ADDREQ_FLAG_ADD);
CheckWinInetError(not bResult);
end;
procedure SendRequestBody(const ARequest: HINTERNET);
var
BufferIn: INTERNET_BUFFERS;
bResult: Boolean;
begin
FillChar(BufferIn, SizeOf(BufferIn), 0);
BufferIn.dwStructSize := SizeOf(INTERNET_BUFFERS);
BufferIn.dwBufferTotal := 0;
bResult := HttpSendRequestEx(ARequest, @BufferIn, nil, 0, 0);
CheckWinInetError(not bResult);
HttpEndRequest(ARequest, nil, 0, 0);
end;
function ReadResponse(const ARequest: HINTERNET): String;
var
Sz: Cardinal;
Ind: Cardinal;
ErrCode: TSysErrorCode;
Buffer: String;
Cookies: TStringList;
begin
Sleep(10);
Sz := SizeOf(ErrCode);
ErrCode := TSysErrorCode(0);
Ind := 0;
if HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
@ErrCode, Sz, Ind) and
(ErrCode div 100 <> 2) then
raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode));
Sz := 1024;
SetLength(Buffer, Sz);
ErrCode := TSysErrorCode(0);
Ind := 0;
if HttpQueryInfo(ARequest, HTTP_QUERY_SET_COOKIE, Pointer(Buffer), Sz, Ind) then
SetLength(Buffer, Sz div SizeOf(Char))
else
Finalize(Buffer);
Cookies := TStringList.Create;
try
Cookies.Text := Buffer;
Result := Trim(Cookies.Values['atlassian.xsrf.token']);
if Pos(';', Result) > 0 then
begin
SetLength(Result, Pos(';', Result) - 1);
Result := Trim(Result);
end;
finally
FreeAndNil(Cookies);
end;
end;
procedure CloseRequest(var ARequest: HINTERNET);
begin
if Assigned(ARequest) then
begin
InternetCloseHandle(ARequest);
ARequest := nil;
end;
end;
var
Request: HINTERNET;
begin
Request := OpenRequest(AConnect);
try
SendRequestBody(Request);
Result := ReadResponse(Request);
finally
CloseRequest(Request);
end;
end;
var
Internet, Connect: HINTERNET;
UserAgent: String;
begin
if not InitWebTools then
begin
Result := '';
Exit;
end;
try
if ASender is ESendWeb.TELWebSender then
UserAgent := TELWebSender(ASender).UserAgent
else
UserAgent := EUserAgent;
Internet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
CheckWinInetError(Internet = nil);
try
Connect := InternetConnect(Internet, GAccountID + '.atlassian.net',
443, nil, nil, INTERNET_SERVICE_HTTP,
INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE, 0);
CheckWinInetError(Connect = nil);
try
Result := Query(Connect);
finally
InternetCloseHandle(Connect);
end;
finally
InternetCloseHandle(Internet);
end;
finally
DoneWebTools;
end;
end;
Uploading Bug Report file
Use this function:
// Uploads file to the collector
function UploadFilesForJiraCollector(const ASender: TObject;
const AAttachedFiles: TStrings; const AToken: String): String;
function Query(const AConnect: HINTERNET; const AFile, AToken: String): String;
var
FS: TMemoryMappedStream;
function OpenRequest(const AConnect: HINTERNET;
const AFile, AToken: String): HINTERNET;
const
DefHeaders = 'Accept: */*' + sLineBreak +
'Sec-Fetch-Site: same-origin' + sLineBreak +
'Sec-Fetch-Mode: cors' + sLineBreak +
'Sec-Fetch-Dest: empty' + sLineBreak +
'Referer: https://' + GAccountID +
'.atlassian.net/rest/collectors/1.0/template/form/' +
GCollectorID + '?os_authType=none' + sLineBreak;
const
SECURITY_FLAG_IGNORE_REVOCATION = $80;
var
dwOpenRequestFlags, dwBuffLen, dwFlags: DWord;
Headers, Ext: String;
Reg: TRegistry;
ContentType: String;
bResult: Boolean;
begin
dwOpenRequestFlags := (
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP or
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS or
INTERNET_FLAG_KEEP_CONNECTION or
INTERNET_FLAG_NO_AUTO_REDIRECT or
INTERNET_FLAG_NO_UI or
INTERNET_FLAG_RELOAD or
SECURITY_FLAG_IGNORE_REVOCATION or
SECURITY_FLAG_IGNORE_UNKNOWN_CA or
INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID or
INTERNET_FLAG_NO_CACHE_WRITE or
// SSL Settings:
INTERNET_FLAG_SECURE or
INTERNET_FLAG_IGNORE_CERT_CN_INVALID or
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID);
Ext := AnsiLowerCase(ExtractFileExt(AFile));
if Ext = '.el' then
ContentType := 'text/plain'
else
if Ext = '.elf' then
ContentType := 'text/plain'
else
if Ext = '.elp' then
ContentType := 'application/zip'
else
if Ext = '.elx' then
ContentType := 'text/xml'
else
if Ext = '.elj' then
ContentType := 'application/json'
else
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CLASSES_ROOT;
if Reg.OpenKey('\' + Ext, False) then
ContentType := Trim(Reg.ReadString('Content Type'))
else
ContentType := '';
if ContentType = '' then
ContentType := 'application/octet-stream';
finally
FreeAndNil(Reg);
end;
end;
Headers := DefHeaders + 'Content-Type: ' + ContentType + sLineBreak;
Result := HttpOpenRequest(AConnect, 'POST',
PChar(Format('/rest/collectors/1.0/tempattachment/' + GCollectorID +
'?filename=%s&size=%d&atl_token=%s&formToken=null&projectId=' + GPID,
[URLEncode(ExtractFileName(AFile)), FS.Size, AToken])),
nil, nil, nil, dwOpenRequestFlags, nil);
CheckWinInetError(Result = nil);
// SSL options:
dwBuffLen := SizeOf(dwFlags);
InternetQueryOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwBuffLen);
dwFlags := (dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA);
InternetSetOption(Result, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, SizeOf(dwFlags));
// Remove old headers
HttpAddRequestHeaders(Result, 'Accept: ' + sLineBreak + 'Content-Type: ' + sLineBreak,
DWord(-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);
// Add new headers
bResult := HttpAddRequestHeaders(Result, PChar(Headers), DWord(-1), HTTP_ADDREQ_FLAG_ADD);
CheckWinInetError(not bResult);
end;
procedure SendRequestBody(const ARequest: HINTERNET);
var
BufferIn: INTERNET_BUFFERS;
TotalSize: Integer;
dwBytesWritten: Cardinal;
bResult: Boolean;
begin
TotalSize := FS.Size;
FillChar(BufferIn, SizeOf(BufferIn), 0);
BufferIn.dwStructSize := SizeOf(INTERNET_BUFFERS);
BufferIn.dwBufferTotal := TotalSize;
bResult := HttpSendRequestEx(ARequest, @BufferIn, nil, 0, 0);
CheckWinInetError(not bResult);
try
if TotalSize > 0 then
begin
bResult := InternetWriteFile(ARequest, FS.Memory, Cardinal(TotalSize), dwBytesWritten);
CheckWinInetError(not bResult);
end;
finally
HttpEndRequest(ARequest, nil, 0, 0);
end;
end;
function ReadResponse(const ARequest: HINTERNET): String;
const
ERROR_INTERNET_TIMEOUT = 12002;
ERROR_INTERNET_INCORRECT_HANDLE_STATE = 12019;
var
Read: Cardinal;
Data, Buf: RawByteString;
Sz: Cardinal;
Ind: Cardinal;
ErrCode, SavedErrCode: TSysErrorCode;
bResult: Boolean;
begin
Sleep(10);
Read := 10240;
Sz := SizeOf(Read);
Ind := 0;
SetLastError(TSysErrorCode(0));
if (not HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_CONTENT_LENGTH,
@Read, Sz, Ind)) or (Read = 0) then
begin
SavedErrCode := GetLastError;
if SavedErrCode = TSysErrorCode(12150) { Header Not Found } then
begin
// "Transfer-encoding: chunked"
Read := 10240;
end
else
begin
Sz := SizeOf(ErrCode);
ErrCode := TSysErrorCode(0);
Ind := 0;
if not HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
@ErrCode, Sz, Ind) then
begin
SetLastError(SavedErrCode);
CheckWinInetError(True);
end;
if ErrCode div 100 <> 2 then
raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode))
else
begin
Result := '';
Exit;
end;
end;
end;
SetLength(Buf, Read);
Data := '';
repeat
Read := 0;
bResult := InternetReadFile(ARequest, Pointer(Buf), Length(Buf), Read);
CheckWinInetError(not bResult);
if Read > 0 then
Data := Data + Copy(Buf, 1, Read);
until Read = 0;
if Data = '' then
begin
Sz := SizeOf(ErrCode);
ErrCode := TSysErrorCode(0);
Ind := 0;
if HttpQueryInfo(ARequest, HTTP_QUERY_FLAG_NUMBER or HTTP_QUERY_STATUS_CODE,
@ErrCode, Sz, Ind) and
(ErrCode div 100 <> 2) then
raise EHTTPError.Create(0, Cardinal(ErrCode), ReadErrorMessage(ARequest, ErrCode));
end;
Result := UTF8ToString(Data);
end;
procedure CloseRequest(var ARequest: HINTERNET);
begin
if Assigned(ARequest) then
begin
InternetCloseHandle(ARequest);
ARequest := nil;
end;
end;
var
Request: HINTERNET;
JSON: IJSONValues;
begin
FS := TMemoryMappedStream.Create(AFile, GENERIC_READ);
try
Request := OpenRequest(AConnect, AFile, AToken);
try
SendRequestBody(Request);
FreeAndNil(FS);
JSON := JSONCreate(ReadResponse(Request));
Result := JSON['id'];
finally
CloseRequest(Request);
end;
finally
FreeAndNil(FS);
end;
end;
var
Internet, Connect: HINTERNET;
UserAgent: String;
begin
if (AAttachedFiles = nil) or
(AAttachedFiles.Count = 0) or
(not FileExists(AAttachedFiles[0])) or
(not InitWebTools) then
begin
Result := '';
Exit;
end;
try
if ASender is ESendWeb.TELWebSender then
UserAgent := TELWebSender(ASender).UserAgent
else
UserAgent := EUserAgent;
Internet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
CheckWinInetError(Internet = nil);
try
Connect := InternetConnect(Internet, GAccountID + '.atlassian.net',
443, nil, nil, INTERNET_SERVICE_HTTP,
INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE, 0);
CheckWinInetError(Connect = nil);
try
Result := Query(Connect, AAttachedFiles[0], AToken);
finally
InternetCloseHandle(Connect);
end;
finally
InternetCloseHandle(Internet);
end;
finally
DoneWebTools;
end;
end;
Other helpers routines
procedure CheckWinInetError(const AError: Boolean);
var
ErrorCode: TSysErrorCode;
ErrorMessage: String;
begin
if AError then
begin
ErrorCode := GetLastError;
ErrorMessage := FindWinINetErrorMessage(Cardinal(ErrorCode));
if ErrorMessage = '' then
ErrorMessage := SysErrorMessage(ErrorCode);
raise EWinInetError.Create(0, Cardinal(ErrorCode), ErrorMessage);
end;
end;
function ReadErrorMessage(const ARequest: HINTERNET; const ErrCode: TSysErrorCode): String;
var
Sz: Cardinal;
Ind: Cardinal;
Headers: String;
Str: TStringList;
X: Integer;
begin
Sz := 10240;
SetLength(Result, Sz div SizeOf(Char));
Ind := 0;
if HttpQueryInfo(ARequest, HTTP_QUERY_STATUS_TEXT, Pointer(Result), Sz, Ind) then
begin
SetLength(Result, Sz div SizeOf(Char));
Result := Trim(Result);
end
else
Result := '';
if (ErrCode = TSysErrorCode(301)) or (ErrCode = TSysErrorCode(302)) then
begin
Sz := 10240;
SetLength(Headers, Sz div SizeOf(Char));
Ind := 0;
if HttpQueryInfo(ARequest, HTTP_QUERY_RAW_HEADERS_CRLF, Pointer(Headers), Sz, Ind) then
begin
SetLength(Headers, Sz div SizeOf(Char));
Headers := Trim(Headers);
Str := TStringList.Create;
try
Str.Text := AnsiLowerCase(StringReplace(Headers, ': ', '=', [rfReplaceAll])); // Do Not Localize
for X := 0 to Str.Count - 1 do
if Str.Names[X] = 'location' then // Do Not Localize
begin
Result := Trim(Result + sLineBreak + Str.ValueFromIndex[X]);
Break;
end;
finally
FreeAndNil(Str);
end;
end;
end;
if Result = '' then
Result := HTTPErrorMessage(Cardinal(ErrCode))
else
Result := HTTPErrorMessage(Cardinal(ErrCode), Result);
end;
See also:
Send feedback...
|
Build date: 2024-09-30
Last edited: 2023-08-09
|
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation.
We do not use your e-mail address for any other purpose.
We will remove your e-mail address from our system after the issue you are reporting has been resolved.
While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback.
After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.
Permanent link to this article: https://www.eurekalog.com/help/eurekalog/jira_and_http_upload.php
|
|