* Renamed project file to .pas for easier file type recognition
This commit is contained in:
366
restemplate.pas
Normal file
366
restemplate.pas
Normal file
@@ -0,0 +1,366 @@
|
||||
{
|
||||
This file is part of restemplate.
|
||||
|
||||
Copyright (C) 2015 Andreas Schneider
|
||||
|
||||
restemplate is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
sqlvision is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with restemplate. If not, see <http://www.gnu.org/licenses/>.
|
||||
}
|
||||
|
||||
program restemplate;
|
||||
|
||||
{$mode objfpc}
|
||||
|
||||
{.$define use_synapse}
|
||||
{$define use_fclweb}
|
||||
|
||||
uses
|
||||
SysUtils, Classes, strutils, IniFiles,
|
||||
{$ifdef use_synapse}httpsend, ssl_openssl,{$endif}
|
||||
{$ifdef use_fclweb}fphttpclient,{$endif}
|
||||
JTemplate,
|
||||
fpjson, jsonparser,
|
||||
DOM, XMLRead, XMLWrite;
|
||||
|
||||
var
|
||||
data: TextFile;
|
||||
line: String;
|
||||
parser: TJTemplateParser;
|
||||
{$ifdef use_synapse}http: THTTPSend;{$endif}
|
||||
{$ifdef use_fclweb}http: TFPHTTPClient;{$endif}
|
||||
method, url: String;
|
||||
content: TStringList;
|
||||
commandMode: Boolean;
|
||||
configDir, templateDir: String;
|
||||
templateFile, templateName: String;
|
||||
sessionIni: TIniFile;
|
||||
beautify: Boolean;
|
||||
|
||||
type
|
||||
TContentType = (ctOther, ctJSON, ctXML);
|
||||
|
||||
procedure CmdAskUser(AName: String);
|
||||
var
|
||||
value, default: String;
|
||||
begin
|
||||
Write(AName);
|
||||
default := sessionIni.ReadString(templateName, AName, '');
|
||||
if default <> '' then
|
||||
Write(' [', default, ']: ')
|
||||
else
|
||||
Write(': ');
|
||||
|
||||
ReadLn(value);
|
||||
|
||||
if value = '' then
|
||||
value := default;
|
||||
|
||||
parser.Fields.Add(AName, value);
|
||||
|
||||
sessionIni.WriteString(templateName, AName, value);
|
||||
end;
|
||||
|
||||
procedure CmdHeader(AHeader: String);
|
||||
var
|
||||
i: Integer;
|
||||
name, value: String;
|
||||
begin
|
||||
parser.Content := AHeader;
|
||||
parser.Replace;
|
||||
AHeader := parser.Content;
|
||||
|
||||
{$ifdef use_synapse}
|
||||
http.Headers.Add(AHeader);
|
||||
{$endif}
|
||||
|
||||
{$ifdef use_fclweb}
|
||||
i := 1;
|
||||
while (i < Length(AHeader)) and (AHeader[i] <> ':') do
|
||||
Inc(i);
|
||||
|
||||
name := Trim(Copy(AHeader, 1, i - 1));
|
||||
value := Trim(Copy(AHeader, i + 1, Length(AHeader)));
|
||||
|
||||
http.AddHeader(name, value);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function IdentifyContentType(AString: String; out ContentType: TContentType): Boolean;
|
||||
begin
|
||||
if Pos('application/json', AString) > 0 then
|
||||
ContentType := ctJSON
|
||||
else if Pos('application/xml', AString) > 0 then
|
||||
ContentType := ctXML
|
||||
else
|
||||
ContentType := ctOther;
|
||||
|
||||
Result := (ContentType <> ctOther);
|
||||
end;
|
||||
|
||||
procedure ProcessCall(AURL: String);
|
||||
var
|
||||
s: String;
|
||||
{$ifdef use_fclweb}
|
||||
request, response: TStream;
|
||||
{$endif}
|
||||
jsonParser: TJSONParser;
|
||||
jsonData: TJSONData;
|
||||
contentType: TContentType;
|
||||
xmlDoc: TXMLDocument;
|
||||
begin
|
||||
parser.Content := AURL;
|
||||
parser.Replace;
|
||||
AURL := parser.Content;
|
||||
writeln('Calling ', AURL);
|
||||
|
||||
{$ifdef use_synapse}
|
||||
if content.Count > 0 then
|
||||
begin
|
||||
// Variable replacement
|
||||
parser.Content := content.Text;
|
||||
parser.Replace;
|
||||
content.Text .= parser.Content;
|
||||
|
||||
content.SaveToStream(http.Document);
|
||||
end;
|
||||
|
||||
if http.HTTPMethod(method, AURL) then
|
||||
begin
|
||||
writeln;
|
||||
writeln('Status: ', http.ResultCode);
|
||||
writeln;
|
||||
writeln('Headers:');
|
||||
for s in http.Headers do
|
||||
writeln(' ', s);
|
||||
writeln;
|
||||
content.LoadFromStream(http.Document);
|
||||
writeln(content.Text);
|
||||
end else
|
||||
begin
|
||||
ExitCode := 2;
|
||||
writeln;
|
||||
writeln('FAILED! Last Socket Error: ', http.Sock.SocksLastError);
|
||||
end;
|
||||
{$endif}
|
||||
|
||||
{$ifdef use_fclweb}
|
||||
response := TMemoryStream.Create;
|
||||
request := nil;
|
||||
|
||||
if content.Count > 0 then
|
||||
begin
|
||||
request := TMemoryStream.Create;
|
||||
|
||||
// Variable replacement
|
||||
parser.Content := content.Text;
|
||||
parser.Replace;
|
||||
content.Text := parser.Content;
|
||||
|
||||
content.SaveToStream(request);
|
||||
request.Position := 0;
|
||||
http.RequestBody := request;
|
||||
end;
|
||||
|
||||
try
|
||||
http.HTTPMethod(method, AURL, response, []);
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
writeln;
|
||||
writeln('Failed! ', E.Message);
|
||||
Halt(1);
|
||||
end;
|
||||
end;
|
||||
|
||||
writeln;
|
||||
writeln('Status: ', http.ResponseStatusCode, ' (', http.ResponseStatusText, ')');
|
||||
writeln;
|
||||
writeln('Headers:');
|
||||
for s in http.ResponseHeaders do
|
||||
writeln(' ', s);
|
||||
writeln;
|
||||
|
||||
response.Position := 0;
|
||||
|
||||
if beautify and IdentifyContentType(http.GetHeader(http.ResponseHeaders, 'Content-Type'), contentType) then
|
||||
begin
|
||||
case contentType of
|
||||
ctJSON:
|
||||
begin
|
||||
jsonParser := TJSONParser.Create(response);
|
||||
jsonData := jsonParser.Parse;
|
||||
writeln(jsonData.FormatJSON);
|
||||
jsonData.Free;
|
||||
jsonParser.Free;
|
||||
end;
|
||||
ctXML:
|
||||
begin
|
||||
ReadXMLFile(xmlDoc, response);
|
||||
response.Size := 0;
|
||||
WriteXMLFile(xmlDoc, response);
|
||||
response.Position := 0;
|
||||
content.LoadFromStream(response);
|
||||
writeln(content.Text);
|
||||
end;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
content.LoadFromStream(response);
|
||||
writeln(content.Text);
|
||||
end;
|
||||
|
||||
response.Free;
|
||||
request.Free;
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
procedure CmdBasicAuth(AData: String);
|
||||
var
|
||||
separator: Char;
|
||||
i: Integer;
|
||||
begin
|
||||
separator := AData[1];
|
||||
i := 2;
|
||||
while (i < Length(AData)) and (AData[i] <> separator) do
|
||||
Inc(i);
|
||||
|
||||
http.UserName := Copy(AData, 2, i - 2);
|
||||
http.Password := Copy(AData, i + 1, Length(AData));
|
||||
end;
|
||||
|
||||
function ProcessCommand(ALine: String): Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
if AnsiStartsStr('Ask ', ALine) then
|
||||
begin
|
||||
Result := True;
|
||||
CmdAskUser(Copy(ALine, 5, Length(ALine)));
|
||||
end else
|
||||
if AnsiStartsStr('Header ', ALine) then
|
||||
begin
|
||||
Result := True;
|
||||
CmdHeader(Copy(ALine, 8, Length(ALine)));
|
||||
end else
|
||||
if AnsiStartsStr('Method ', ALine) then
|
||||
begin
|
||||
Result := True;
|
||||
method := Copy(ALine, 8, Length(ALine));
|
||||
end else
|
||||
// We don't include the space here, since we also interpret this following
|
||||
// char as separator for username and password! (In case the username itself
|
||||
// contains a space.)
|
||||
if AnsiStartsStr('BasicAuth', ALine) then
|
||||
begin
|
||||
Result := True;
|
||||
CmdBasicAuth(Copy(ALine, 10, Length(ALine)));
|
||||
end else
|
||||
if ALine = 'Beautify' then
|
||||
begin
|
||||
Result := True;
|
||||
beautify := True;
|
||||
end else
|
||||
if AnsiStartsStr('Call ', ALine) then
|
||||
begin
|
||||
Result := True;
|
||||
url := Copy(ALine, 6, Length(ALine));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure ListProfiles;
|
||||
var
|
||||
sr: TSearchRec;
|
||||
begin
|
||||
Writeln('Known profiles:');
|
||||
if FindFirst(templateDir + '*.rest', faAnyFile, sr) = 0 then
|
||||
begin
|
||||
repeat
|
||||
writeln(' ', Copy(sr.Name, 1, Length(sr.Name) - 5));
|
||||
until FindNext(sr) <> 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
configDir := GetAppConfigDir(False);
|
||||
templateDir := configDir + 'templates' + PathDelim;
|
||||
|
||||
templateFile := templateDir + ParamStr(1) + '.rest';
|
||||
|
||||
if ParamCount <> 1 then
|
||||
begin
|
||||
Writeln('Usage: ', ExtractFileName(ParamStr(0)), ' <profile or file>');
|
||||
Writeln;
|
||||
ListProfiles;
|
||||
Halt(0);
|
||||
end;
|
||||
|
||||
if FileExists(ParamStr(1)) then
|
||||
begin
|
||||
AssignFile(data, ParamStr(1));
|
||||
templateName := ExtractFileName(ParamStr(1));
|
||||
if AnsiEndsStr('.rest', templateName) then
|
||||
templateName := Copy(templateName, 1, Length(templateName) - 5);
|
||||
end else
|
||||
if FileExists(templateFile) then
|
||||
begin
|
||||
AssignFile(data, templateFile);
|
||||
templateName := ParamStr(1);
|
||||
end else
|
||||
begin
|
||||
writeln('Template not found!');
|
||||
Halt(1);
|
||||
end;
|
||||
Reset(data);
|
||||
|
||||
sessionIni := TIniFile.Create(configDir + 'session.ini');
|
||||
|
||||
parser := TJTemplateParser.Create;
|
||||
content := TStringList.Create;
|
||||
beautify := False; //By default, we don't want to manipulate output.
|
||||
|
||||
{$ifdef use_synapse}
|
||||
http := THTTPSend.Create;
|
||||
{$endif}
|
||||
|
||||
{$ifdef use_fclweb}
|
||||
http := TFPHttpClient.Create(nil);
|
||||
{$endif}
|
||||
|
||||
commandMode := True;
|
||||
|
||||
try
|
||||
while not EOF(data) do
|
||||
begin
|
||||
ReadLn(data, line);
|
||||
if commandMode and (line <> '') and (line[1] <> '#') and
|
||||
not ProcessCommand(line) then
|
||||
begin
|
||||
commandMode := False;
|
||||
end;
|
||||
|
||||
if not commandMode then
|
||||
content.Add(line);
|
||||
end;
|
||||
|
||||
if url <> '' then
|
||||
ProcessCall(url);
|
||||
|
||||
finally
|
||||
sessionIni.Free;
|
||||
parser.Free;
|
||||
http.Free;
|
||||
content.Free;
|
||||
|
||||
CloseFile(data);
|
||||
end;
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user