I need to make a program that generates a password that is saved in a text file format in a specific destination I set and the user needs to open the .txt to get the password to 'unlock' another program.
I have already got the code to generate the password in the string sPass and now I need to use the SaveToFile function to save it into the text file I created called Password.txt but I cannot find the general form to use the SaveTo File Function in Delphi and I do not know where to put the sPass and Password.txt in the function.
It should be something like : SaveToFile(...) but I do not know how to save sPass in Password.txt
Edit :
Just one more question, how do you delete what is previously stored in Password.txt before you add the string to it so that Password.txt is blank before the string is added ? Thanks
The Modern Modern way is to use TFile.WriteAllText in IOUtils (Delphi 2010 and up)
procedure WriteAllText(const Path: string; const Contents: string);
overload; static;
Creates a new file, writes the specified string to the file, and then
closes the file. If the target file already exists, it is overwritten.
The modern way is to create a stringlist and save that to file.
procedure MakeAStringlistAndSaveThat;
var
MyText: TStringlist;
begin
MyText:= TStringlist.create;
try
MyText.Add('line 1');
MyText.Add('line 2');
MyText.SaveToFile('c:\folder\filename.txt');
finally
MyText.Free
end; {try}
end;
Note that Delphi already has a related class that does everything you want: TInifile.
It stores values and keys in a key = 'value' format.
passwordlist:= TInifile.Create;
try
passwordlist.LoadFromFile('c:\folder\passwords.txt');
//Add or replace a password for `user1`
passwordlist.WriteString('sectionname','user1','topsecretpassword');
passwordlist.SaveToFile('c:\folder\passwords.txt');
finally
passwordlist.Free;
end; {try}
Warning
Note that saving unecrypted passwords in a textfile is a security-leak. It's better to hash your passwords using a hashfunction, see: Password encryption in Delphi
For tips on how to save passwords in a secure way.
You can use the TFileStream class to save a string to a file:
uses
Classes;
procedure StrToFile(const FileName, SourceString : string);
var
Stream : TFileStream;
begin
Stream:= TFileStream.Create(FileName, fmCreate);
try
Stream.WriteBuffer(Pointer(SourceString)^, Length(SourceString));
finally
Stream.Free;
end;
end;
and to read
function FileToStr(const FileName : string):string;
var
Stream : TFileStream;
begin
Stream:= TFileStream.Create(FileName, fmOpenRead);
try
SetLength(Result, Stream.Size);
Stream.Position:=0;
Stream.ReadBuffer(Pointer(Result)^, Stream.Size);
finally
Stream.Free;
end;
end;
Fastest and simplest way, no need to declare any variables:
with TStringList.Create do
try
Add(SomeString);
SaveToFile('c:\1.txt');
finally
Free;
end;
Related
My scenario is currently that I'd like the possibility for users to skip installation of certain files that a user might have specified via a command line argument at install time.
The idea would be for the user to specify a text file and ideally during installation, the installer would check if the file currently being copied is listed in the supplied text file and decide depending on that.
My [Files] section atm is referencing full directories: would the best approach be to list all files individually and make use of the Check parameter or is there a different approach available? There seems to be no event function that would lend itself for this task.
Thanks
Markus
No need to list all files individually. The Check function is called for each file individually, even when the Source is a wildcard.
Use the CurrentFilename function to tell what file is being processed at the moment.
Note that the Check function is called multiple times for each file.
[Files]
Source: "C:\path\*.*"; DestDir: "{app}"; Check: AllowFile
[Code]
var
Whitelist: TStringList;
function AllowFile: boolean;
var
FileName: string;
begin
FileName := ExtractFileName(ExpandConstant(CurrentFileName));
Result := (Whitelist = nil) or (Whitelist.IndexOf(Uppercase(FileName)) >= 0);
if Result then
Log(Format('Allowing "%s"', [FileName]))
else
Log(Format('Skipping "%s"', [FileName]));
end;
function InitializeSetup(): Boolean;
var
WhitelistFile: string;
Lines: TArrayOfString;
I: Integer;
begin
WhitelistFile := ExpandConstant('{param:Whitelist}');
if WhitelistFile = '' then
begin
Log('No whitelist specified, allowing all files');
end
else
begin
Whitelist := TStringList.Create;
Whitelist.Sorted := True;
LoadStringsFromFile(WhitelistFile, Lines);
for I := 0 to GetArrayLength(Lines) - 1 do
Whitelist.Add(Uppercase(Lines[I]));
Log(Format('Loaded %d entries to whitelist from "%s"', [
Whitelist.Count, WhitelistFile]));
end;
Result := True;
end;
Is there a way to disallow user input if it contains spaces only?
I already tried this solution:
Inno Setup - Create User Input Query Page with input length and format limit and use the input
But, I don't want that solution because it disable -space- completely.
E.g. if input in text field is "my name" it will return error because -space- is not allowed.
Use the same code as in:
Inno Setup - Create User Input Query Page with input length and format limit and use the input
Just use this implementation of ValidateInput:
function ValidateInput(Sender: TWizardPage): Boolean;
begin
Result := True;
if Trim(Page.Values[0]) = '' then
begin
MsgBox('Input cannot be empty.', mbError, MB_OK);
Result := False;
end;
end;
The Trim function is the key.
I need that my program be protected with a dynamic password that includes the current date.
I need just the month or the day or the hour or the minute.
I tried this code to include the day into the password:
[Setup]
Password=Password!{code:DateTime|0}
[Code]
function DateTime (Param: string): string;
begin
Result := GetDateTimeString('dd', #0, #0);
end;
But it's not working.
Regards.
The Password directive cannot contain any constants, let alone scripted constants.
So your script makes the password be literally Password!{code:DateTime|0}.
Instead, use CheckPassword event function:
[Code]
function CheckPassword(Password: String): Boolean;
begin
Result := (Password = ('Password!' + GetDateTimeString('dd', #0, #0)));
end;
Though safer than comparing against a literal string (which can be seen in .exe binary) is comparing a checksum.
See How to do a dynamic password in Inno Setup?
I need a StringHelper which saves a string to a file:
var
s: string;
begin
s := 'Some text';
s.SaveTo('C:\MyText.txt');
end;
Unfortunately, this is not possible. Is it possible to add such a StringHelper?
It is possible to add such a helper. For instance:
type
TMyStringHelper = record helper for string
procedure SaveTo(const FileName: string);
end;
The downside to doing so is that this will replace the string helper that is provided by the RTL. If you don't use it, that won't matter. If you do use it, then that's a problem that cannot readily be overcome.
You could look at this a different way. Instead of trying to use a helper on the string type, you could use TFile.WriteAllText instead.
TFile.WriteAllText(FileName, 'Some text', TEncoding.UTF8);
Obviously you can use a different encoding if you prefer.
I'm using Lazarus to build a simple app that builds Outlook signatures based on a template. The idea is to extract the template (a ZIP file), and replace variables within the files it contains.
For example, I may want to replace {fullname} with the name provided by the user.
I am currently using the implementation below, but it seems to be ineffective. The file is read and written to, but it appears the replacements are not being made. I have tested to see if my implementation of TFileStream is not correct, but using WriteAnsiString to append dummy text onto the end of the output file works.
Please would you kindly have a look at my code below and let me know what I may have done wrong, or if there are any better alternatives to StringReplace? I am aware that one can use TStringList - however, doing so breaks line endings. As memos and rich edits use TStringList, using those won't help either.
Update:
I have seen this, but using AnsiString makes no difference. If I'm not mistaken, FPC uses it by default anyway, instead of UnicodeString.
Update 2:
Indeed, AnsiString is the default. Using a unicode string (which makes the replacements work) adds ? to the beginning and end of the file. Why would it do that?
function multiStringReplace(const s: string; search, replace : array of string; flags : tReplaceFlags): string;
var c : cardinal;
begin
assert(length(search) = length(replace), 'Array lengths differ.');
result := s;
for c := low(search) to high(search) do
result := stringReplace(result, search[c], replace[c], flags);
end;
procedure fileReplaceString(const fileName: string; search, replace: array of string);
var
fs: tFileStream;
s: string;
begin
fs := tFileStream.create(fileName, fmOpenRead or fmShareDenyNone);
try
setLength(s, fs.size);
fs.readBuffer(s[1], fs.size);
finally
fs.free();
end;
s := multiStringReplace(s, search, replace, [rfReplaceAll, rfIgnoreCase]);
fs := tFileStream.create(fileName, fmOpenWrite);
try
fs.writeBuffer(s[1], length(s));
finally
fs.free();
end;
end;
Usage:
fileReplaceString(currentFile, ['{fullname}'], ['Full Name']);
Thanks to Abelisto's comment above, it appears the issue is due to the fact that Outlook saves the three files it creates with different encodings. To get around it, I simply used convertEncoding and guessEncoding from lconvencoding, as below:
uses
lconvencoding;
// Read string
s := convertEncoding(
multiStringReplace(s, search, replace, [rfReplaceAll, rfIgnoreCase]),
guessEncoding(s), encodingAnsi
);
// Write modified and converted string back to file
encodingAnsi appears to be the best conversion, at least in my case. Converting to UTF8 (with or without BOM) caused a bit of a headache with certain characters, specifically EmDash or EnDash.