Custom command line parameter - distinguish nonexistence from blank? [duplicate] - command-line

I am new to Inno Setup and I have already read the documentation. Now I know that Inno Setup can accept different/custom parameter and could be processed via Pascal script. But the problem is, I don't know how to write in Pascal.
I am hoping I could get help about the coding.
I'd like to pass /NOSTART parameter to my setup file which while tell the setup to disable(uncheck) the check mark on "Launch " and if /NOSTART is not provided, it it will enable(check) the check mark "Launch "
or if possible, that Launch page is not required and do everything via code.

Since you can't imperatively modify flags for section entries and directly accessing the RunList would be quite a dirty workaround, I'm using for this two postinstall entries, while one has no unchecked flag specified and the second one has. So, the first entry represents the checked launch check box and the second one unchecked launch check box. Which one is used is controlled by the Check parameter function, where is checked if a command line tail contains /NOSTART parameter.
Also, I've used a little more straightforward function for determining if a certain parameter is contained in the command line tail. It uses the CompareText function to compare text in a case insensitive way. You can replace it with CompareStr function, if you want to compare the parameter text in a case sensitive way. Here is the script:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
OutputDir=userdocs:Inno Setup Examples Output
[Run]
Filename: "calc.exe"; Description: "Launch calculator"; \
Flags: postinstall nowait skipifsilent; Check: LaunchChecked
Filename: "calc.exe"; Description: "Launch calculator"; \
Flags: postinstall nowait skipifsilent unchecked; Check: not LaunchChecked
[Code]
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
function LaunchChecked: Boolean;
begin
Result := not CmdLineParamExists('/NOSTART');
end;

and so a little research read and read .. i got my answer.
here's my code (except the "GetCommandLineParam")
[Code]
{
var
StartNow: Boolean;
}
function GetCommandLineParam(inParam: String): String;
var
LoopVar : Integer;
BreakLoop : Boolean;
begin
{ Init the variable to known values }
LoopVar :=0;
Result := '';
BreakLoop := False;
{ Loop through the passed in arry to find the parameter }
while ( (LoopVar < ParamCount) and
(not BreakLoop) ) do
begin
{ Determine if the looked for parameter is the next value }
if ( (ParamStr(LoopVar) = inParam) and
( (LoopVar+1) <= ParamCount )) then
begin
{ Set the return result equal to the next command line parameter }
Result := ParamStr(LoopVar+1);
{ Break the loop }
BreakLoop := True;
end;
{ Increment the loop variable }
LoopVar := LoopVar + 1;
end;
end;
{
function InitializeSetup(): Boolean;
var
NOSTART_Value : String;
begin
NOSTART_Value := GetCommandLineParam('/NOSTART');
if(NOSTART_Value = 'false') then
begin
StartNow := True
end
else
begin
StartNow := False
end;
Result := True;
end;
}
procedure CurStepChanged(CurStep: TSetupStep);
var
Filename: String;
ResultCode: Integer;
NOSTART_Value : String;
begin
if CurStep = ssDone then
begin
NOSTART_Value := GetCommandLineParam('/NOSTART');
if(NOSTART_Value = 'false') then
begin
Filename := ExpandConstant('{app}\{#MyAppExeName}');
Exec(Filename, '', '', SW_SHOW, ewNoWait, Resultcode);
end
end;
end;
a code update. Thanks to #TLama
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Break;
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
Filename: String;
ResultCode: Integer;
NOSTART_Value : String;
RunApp : Boolean;
begin
if CurStep = ssDone then
begin
RunApp := CmdLineParamExists('/START');
if(RunApp = True) then
begin
Filename := ExpandConstant('{app}\{#MyAppExeName}');
Exec(Filename, '', '', SW_SHOW, ewNoWait, Resultcode);
end
// NOSTART_Value := GetCommandLineParam('/START');
// if(NOSTART_Value = 'true') then
// begin
// Filename := ExpandConstant('{app}\{#MyAppExeName}');
// Exec(Filename, '', '', SW_SHOW, ewNoWait, Resultcode);
//end
end;
end;

How about the following, easy to read
; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Install Specialty Programs"
#define MyAppVersion "1.0"
#define MyAppPublisher ""
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{5}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={pf}\{#MyAppName}
DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputDir=P:\_Development\INNO Setup Files\Specialty File Install
OutputBaseFilename=Specialty File Install
Compression=lzma
SolidCompression=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "P:\_Development\INNO Setup Files\Specialty File Install\Files\0.0 - Steps.docx"; DestDir: "c:\support\Specialty Files"; Tasks: V00Step
[Tasks]
Name: "Office2013"; Description: "Running Office 2013"; Flags: checkablealone unchecked
Name: "Office2016"; Description: "Running Office 2016"; Flags: checkablealone unchecked
Name: "V00Step"; Description: "Steps To Follow (Read Me)"; Flags: exclusive
[Run]
Filename: "C:\Program Files (x86)\Microsoft Office\Office15\WINWORD.EXE"; Parameters: """c:\support\Specialty Files\0.0 - Steps.docx"""; Description: "Run if Office 2013 is installed"; Tasks: V00Step AND Office2013
Filename: "C:\Program Files (x86)\Microsoft Office\Office16\WINWORD.EXE"; Parameters: """c:\support\Specialty Files\0.0 - Steps.docx"""; Description: "Run if Office 2016 is installed"; Tasks: V00Step AND Office2016

Related

dsRESTConnection component won't allow me change the .username and .password after the first connect

In my Delphi 10.3.1 application server (which is a "DataSnap REST Application"), I have put some logging as follows:
procedure TWebModule1.DSAuthenticationManager1UserAuthenticate(
Sender: TObject; const Protocol, Context, User, Password: string;
var valid: Boolean; UserRoles: TStrings);
begin
valid := (User > '') and (Password > ''); // pass-through
if Valid then
begin
MyWebUtils.LogMessage('Authentication, ' + User + ', ' + Password +
', ' + protocol + ', ' + context);
TDSSessionManager.GetThreadSession.PutData('USERNAME', User);
end;
end;
In my client (on Android), which uses a dsRestConnection, I have a procedure to test the connection, which I call, for example in FormShow of the main form.
function TClientModuleRest.TestConnect(var ErrorMsg: String): Boolean;
var
returned: String;
begin
Result := True;
try
DSRestConnection.Username := 'Admin';
DSRestConnection.Password := 'Test';
DSRestConnection.TestConnection([toNoLoginPrompt]);
except
on e: exception do
begin
Result := False;
ErrorMsg := e.Message;
end
end;
end;
This works well.
The problem occurs when I call the first Server method.
function TClientModuleRest.ValidUser(username, password: String;
var Response: String): Integer;
var
Server: TServerMethodsRClient;
Valid: Boolean;
IniFile: TStringList;
FileName: TFileName;
begin
Result := 0;
DSRestConnection.UserName := username;
DSRestConnection.Password := password;
try
Server := TServerMethodsRClient.Create(DSRestConnection);
try
Valid := Server.ValidUser(username, password, Response);
if not Valid then
raise Exception.Create(response);
Result := 1;
finally
Server.Free;
end;
except
on e: exception do
begin
Response := e.Message;
Result := -1; // an error occured
end;
end;
end;
So the problem is that the setting of the .username and .password doesn't 'take'. In other words, my conclusion is that the .username and `.password can only be set before the first connect, and not thereafter.
The server log file shows that even though the ValidUser function was called with the username and password values that were passed to it, the authentication is receiving the username and password from the first (test) connect.
Is this by design? Or am I missing something? Thanks.

Sending e-mail with attachments with Indy

First of all, code.
procedure TMain.SendEmailIndy(
const SMTPServer: string;
const FromName, FromAddress: string;
const ToAddresses: string; //comma "," separated list of e-mail addresses
const CCAddresses: string; //comma "," separated list of e-mail addresses
const BCCAddresses: string; //comma "," separated list of e-mail addresses
const Subject: string;
const EmailBody: string;
const IsBodyHtml: Boolean);
var
smtp: TIdSMTP; // IdSmtp.pas
msg: TidMessage; // IdMessage.pas
builder: TIdCustomMessageBuilder; //IdMessageBuilder.pas
s: string;
emailAddress: string;
FileToSend: TIdAttachmentfile;
begin
msg := TidMessage.Create(nil);
try
if IsBodyHtml then begin
builder := TIdMessageBuilderHtml.Create;
TIdMessageBuilderHtml(builder).Html.Text := EmailBody
end else begin
builder := TIdMessageBuilderPlain.Create;
end;
if (Realization.AttachmentD.FileName <> '') then begin
msg.IsEncoded := true;
FileToSend := TIdAttachmentFile.Create(msg.MessageParts, Realization.AttachmentD.FileName);
FileToSend.FileName := Realization.AttachmentD.FileName;
//FileToSend.ContentDisposition := 'attachment';
FileToSend.ContentType := 'multipart/mixed';
ShowMessage('Sent: '+Realization.AttachmentD.FileName);
end;
msg.From.Name := FromName;
msg.From.Address := FromAddress;
msg.Subject := Subject;
//If the message is plaintext then we must fill the body outside of the PlainText email builder.
//(the PlainTextBuilder is unable to build plaintext e-mail)
if not IsBodyHtml then begin
msg.Body.Text := EmailBody;
end;
for s in ToAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then begin
with msg.recipients.Add do
begin
//Name := '<Name of recipient>';
Address := emailAddress;
end;
end;
end;
for s in CCAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then
msg.CCList.Add.Address := emailAddress;
end;
for s in BCCAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then
msg.BccList.Add.Address := emailAddress;
end;
smtp := TIdSMTP.Create(nil);
try
msg.Encoding := meMIME;
msg.ContentType := 'text/html';
msg.CharSet := 'UTF-8';
msg.ContentTransferEncoding:= 'quoted-printable';
smtp.Host := SMTPServer; // IP Address of SMTP server
Smtp.UseTLS := utNoTLSSupport;
smtp.Port := 587; //The default already is port 25 (the SMTP port)
smtp.Username := _GlobalData.EMail;
smtp.Password := _GlobalData.Password;
smtp.Connect;
try
smtp.Send(msg);
ShowMessage('Wiadomość wysłana.');
Realization.AttachmentD.FileName := '';
finally
smtp.Disconnect();
end;
finally
smtp.Free;
end;
finally
msg.Free;
end;
end;
I experience problems with sending e-mail message with attached file to it.
When I remove the following line from the code above, the message is sent without html message (e-mail body) that should be there:
FileToSend.ContentType := 'multipart/mixed';
However, when I leave this line in the code and try to send a message, I receive this message:
A policy-violation was found in an Email message you sent.
This Email scanner intercepted it and stopped the entire message
reaching its destination.
The policy-violation was reported to be:
Disallowed breakage found in header name - not valid email
Please contact your IT support personnel with any queries regarding this
policy.
Therefore my question is, how to send an e-mail with attached file properly.
You are misusing the TIdMessageBuilder... classes (TIdMessageBuilderHtml is perfectly capable of creating plain-text emails, but more importantly you are not calling TIdCustomMessageBuilder.FillMessage() to transfer the builder data into the TIdMessage).
You are not populating the TIdMessage correctly (for instance, you are not setting the TIdMessage.ContentType and TIdAttachmentFile.ContentType properties correctly when an attachment is present).
Try something more like this instead:
procedure TMain.SendEmailIndy(
const SMTPServer: string;
const FromName, FromAddress: string;
const ToAddresses: string; //comma separated list of e-mail addresses
const CCAddresses: string; //comma separated list of e-mail addresses
const BCCAddresses: string; //comma separated list of e-mail addresses
const Subject: string;
const EmailBody: string;
const IsBodyHtml: Boolean);
var
smtp: TIdSMTP; // IdSmtp.pas
msg: TidMessage; // IdMessage.pas
builder: TIdMessageBuilderHtml; //IdMessageBuilder.pas
begin
msg := TidMessage.Create(nil);
try
builder := TIdMessageBuilderHtml.Create;
try
if IsBodyHtml then
begin
builder.Html.Text := EmailBody;
builder.HtmlCharSet := 'utf-8';
builder.HtmlContentTransfer := 'quoted-printable';
end else
begin
builder.PlainText.Text := EmailBody;
builder.PlainTextCharSet := 'utf-8';
builder.PlainTextContentTransfer := 'quoted-printable';
end;
if Realization.AttachmentD.FileName <> '' then
begin
builder.Attachments.Add(Realization.AttachmentD.FileName);
ShowMessage('Sending: ' + Realization.AttachmentD.FileName);
end;
builder.FillMessage(msg);
finally
builder.Free;
end;
msg.From.Name := FromName;
msg.From.Address := FromAddress;
msg.Subject := Subject;
msg.Recipients.EmailAddresses := ToAddresses;
msg.CCList.EmailAddresses := CCAddresses;
msg.BccList.EmailAddresses := BCCAddresses;
smtp := TIdSMTP.Create(nil);
try
smtp.Host := SMTPServer; // IP Address of SMTP server
Smtp.UseTLS := utNoTLSSupport;
smtp.Port := 587; //The default already is port 25 (the SMTP port)
smtp.Username := _GlobalData.EMail;
smtp.Password := _GlobalData.Password;
smtp.AuthType := satDefault;
smtp.Connect;
try
smtp.Send(msg);
finally
smtp.Disconnect;
end;
finally
smtp.Free;
end;
finally
msg.Free;
end;
ShowMessage('Wiadomość wysłana.');
Realization.AttachmentD.FileName := '';
end;

Sending emails from the USA

My program uses Indy 10 TIdSMTP and TIdMessage to send error reports via email. The program is used mainly in Germany, but some clients are in the USA.
The tool works flawlessly in Germany, but the USA clients are not able to send anything at all. The output shows that the connection to the German email server (smtp.web.de) is made, but after that, an immediate disconnect follows. The exception "Socket error #10054 connection reset by peer" is thrown.
Here's some code:
procedure SendMail(const PI_sSenderAddress: string; const PI_arrReceiverAddresses, PI_arrCCAddresses, PI_arrBCCAddresses: array of string; const PI_sSubject, PI_sMailText: string; const PI_arrAttachments: array of string; const PI_sHost: string = ''; const PI_sUsername: string = ''; const PI_sPassword: string = ''; const PI_bUseTLS: boolean = False; const PI_nPort: integer = 25; const PI_nTimeoutSeconds: integer = 5 );
var
smtp: TIdSMTP;
Mail: TIdMessage;
i: integer;
arrAttachments: array of TIdAttachmentFile;
io: TIdSSLIOHandlerSocketOpenSSL;
begin
io := nil;
smtp := TIdSMTP.Create(Application);
try
smtp.Password := PI_sPassword;
smtp.Username := PI_sUsername;
smtp.Port := PI_nPort;
smtp.Host := PI_sHost;
if PI_bUseTLS then begin
io := TIdSSLIOHandlerSocketOpenSSL.Create(Application);
smtp.IOHandler := io;
end;
smtp.UseTLS := utUseRequireTLS;
if PI_sUserName <> '' then begin
smtp.AuthType := satDefault;
end else begin
smtp.AuthType := satNone;
end;
smtp.HeloName := Split(PI_sUsername, 1, '#');
mail := TIdMessage.Create( Application );
try
mail.Clear;
mail.From.Address := PI_sSenderAddress;
mail.From.Text := PI_sSenderAddress;
mail.Subject := PI_sSubject;
mail.Recipients.EMailAddresses := ArrayToStr( PI_arrReceiverAddresses, ',' ); //!! only one
mail.CCList.EMailAddresses := ArrayToStr( PI_arrCCAddresses, ',' );
mail.BccList.EMailAddresses := ArrayToStr( PI_arrBCCAddresses, ',' );
mail.ReceiptRecipient.Text := '';
mail.Body.Add( PI_sMailText );
mail.Date := now;
if Length(PI_arrAttachments) > 0 then begin
SetLength(arrAttachments, Length(PI_arrAttachments));
for i := 0 to High(PI_arrAttachments) do begin
arrAttachments[i] := TIdAttachmentFile.Create(mail.MessageParts, PI_arrAttachments[i]);
end;
end;
try
smtp.ConnectTimeout := PI_nTimeoutSeconds * 1000;
smtp.Connect;
if smtp.Connected then begin
smtp.Send(mail);
end;
finally
smtp.disconnect;
if Length(arrAttachments) > 0 then begin
for i := 0 to High(arrAttachments) do begin
arrAttachments[i].Free;
end;
end;
end;
finally
mail.Free;
end;
finally
smtp.free;
if Assigned(io) then io.Free;
end;
end;
I call it like this:
SendMail(
'myaccount#web.de',
['receiver#web.de'],
[],
[],
'Subject',
'Text',
[],
'smtp.web.de',
'myaccount#web.de',
'mypassword',
True,
587
);
Can someone help?
EDIT: I changed to a googlemail account, but getting the same error. Currently, i call it like this:
I call it like this:
SendMail(
'myaccount#gmail.com',
['receiver#web.de'],
[],
[],
'Subject',
'Text',
[],
'smtp.gmail.com',
'myaccount#gmail.com',
'mypassword',
True,
587
);

Save INI file in UTF-8 rather than ANSI in Inno Setup

I'm starting to use Inno Setup, and I have some problems with my INI file encoding.
I want to save user input in the INI file, and this input can contain accents.
I use Inno Setup Unicode, my setupScript.iss is UTF-8 encoded, and here is my code (a part) :
[INI]
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "ca.plafondAnnuel"; String: "{code:GetUser|Plafond}"
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "app.siren"; String: "{code:GetUser|Siren}"
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "app.adresse"; String: "{code:GetUser|Adresse}"
[Code]
var
UserPage: TInputQueryWizardPage;
ExamplePage : TInputOptionWizardPage;
ImmatriculationPage : TInputOptionWizardPage;
FakeElemIndex: Integer;
FakeElem: TCustomEdit;
AdresseTextarea: TNewMemo;
procedure InitializeWizard;
begin
UserPage := CreateInputQueryPage(wpWelcome,
'Configuration de l''application', '',
'Configurez ici votre application. Une fois installée, vous pourrez modifier ces valeurs.');
UserPage.Add('Siren :', False);
UserPage.Add('Plafond annuel (utilisé par les auto-entreprises, mettre 0 si vous ne souhaitez pas plafonner votre chiffre d''affaire.):', False);
FakeElemIndex := UserPage.Add('Votre adresse complète (telle qu''elle s''affichera sur les devis et factures, avec nom complet):', False);
FakeElem := UserPage.Edits[FakeElemIndex];
AdresseTextarea := TNewMemo.Create(WizardForm);
AdresseTextarea.Parent := FakeElem.Parent;
AdresseTextarea.SetBounds(FakeElem.Left, FakeElem.Top, FakeElem.Width, ScaleY(50));
// Hide the original single-line edit
FakeElem.Visible := False;
end;
function GetUser(Param: String): String;
begin
if Param = 'Adresse' then
Result := AdresseTextarea.Text
else if Param = 'Siren' then
Result := UserPage.Values[0]
else if Param = 'Plafond' then
Result := UserPage.Values[1];
end;
The value returned by getUser|Adresse in the [INI] part is not UTF-8 encoded: I open the INI file with Notepad++ and I see the file is UTF-8 encoded. But the value adresse is ANSI encoded (If I change the encoding of the file to ANSI, this value is readable)
Someone can help me understand how can I save this user input in UTF-8 ?
Thanks a lot !
The INI functions of Inno Setup ([INI] section and SetIni* functions) use internally the Windows API function WritePrivateProfileString.
This function does not support UTF-8 at all. All it supports is the ANSI encoding and UTF-16.
See How to read/write Chinese/Japanese characters from/to INI files?
So it's even questionable whether the target application will be able to read UTF-8-encoded INI file, if it relies on the Windows API function to read it.
Anyway, if you need the UTF-8, you would have to format the entries to INI format yourself and use SaveStringsToUTF8File function to write it.
The last option is to hack it by using the system call WritePrivateProfileString to write seemingly ANSI-encoded string, which will be in fact UTF-8-encoded.
For that you need to convert the string to UTF-8 in your code. You can use WideCharToMultiByte for that.
function WideCharToMultiByte(CodePage: UINT; dwFlags: DWORD;
lpWideCharStr: string; cchWideChar: Integer; lpMultiByteStr: AnsiString;
cchMultiByte: Integer; lpDefaultCharFake: Integer;
lpUsedDefaultCharFake: Integer): Integer;
external 'WideCharToMultiByte#kernel32.dll stdcall';
const
CP_UTF8 = 65001;
function GetStringAsUtf8(S: string): AnsiString;
var
Len: Integer;
begin
Len := WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, 0, 0, 0);
SetLength(Result, Len);
WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, Len, 0, 0);
end;
function WritePrivateProfileString(
lpAppName, lpKeyName, lpString, lpFileName: AnsiString): Integer;
external 'WritePrivateProfileStringA#kernel32.dll stdcall';
procedure CurStepChanged(CurStep: TSetupStep);
var
IniFileName: string;
begin
if CurStep = ssInstall then
begin
Log('Writting INI file');
if not ForceDirectories(ExpandConstant('{app}\www\conf')) then
begin
MsgBox('Error creating directory for INI file', mbError, MB_OK);
end
else
begin
IniFileName := ExpandConstant('{app}\www\conf\config.ini');
if (WritePrivateProfileString(
'Settings', 'ca.plafondAnnuel', GetStringAsUtf8(GetUser('Plafond')),
IniFileName) = 0) or
(WritePrivateProfileString(
'Settings', 'app.siren', GetStringAsUtf8(GetUser('Siren')),
IniFileName) = 0) or
(WritePrivateProfileString(
'Settings', 'app.adresse', GetStringAsUtf8(GetUser('Adresse')),
IniFileName) = 0) then
begin
MsgBox('Error writting the INI file', mbError, MB_OK);
end;
end;
end;
end;

Login to facebook with Indy

I would like to login to my facebook account with Indy. The version is 9.00.10 and I use OpenSSL with TIDHTTP and assigned a cookie manager to it. Everything works fine (I can send a POST request a GET, etc.)
I sniffed the actual login to facebook and I have the following information:
UserAgent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)
There are several POST parameters:
lsd = I have no idea what that is.
email = The actual facebook username/email.
pass = The password (unencrypted) --> I was shocked to see in clear text.
default_persistent = (0 or 1) for "keep me logged in"
timezone = Timezone code.
lgnrnd = I have no idea what that is.
lgnjs = I have no idea what that is.
locale = GEOIP location (e.x. en_US)
The post is made on https://www.facebook.com/login.php?login_attempt=1. However when I try to login it returns that I have entered an incorrect eMail. I'm sure I used the right eMail and Password.
Here is my code:
procedure TForm1.Button1Click(Sender: TObject);
var
TEST : STRING;
lParamList: TStringList;
i : Integer;
begin
lParamList := TStringList.Create;
lparamlist.Add('lsd=AVoBzJ5G');
lparamlist.Add('email=myeMail%40mysite.com');
lparamlist.Add('pass=mypass');
lparamlist.Add('default_persistent=0');
lparamlist.Add('timezone=240');
lparamlist.Add('lgnrnd=210302_FeQV');
lparamlist.Add('lgnjs=1367035381');
lparamlist.Add('locale=en_US');
IDHTTP1.Request.UserAgent := 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)';
Test := IdHTTP1.Get('https://www.facebook.com'); // To get the first cookies.
for i := 0 to IDHTTP1.CookieManager.CookieCollection.Count - 1 do begin
ShowMessage(IDHTTP1.CookieManager.CookieCollection.Items[i].CookieText); // Show me the cookies.
end;
TEST := IDHTTP1.Post('https://www.facebook.com/login.php?login_attempt=1', lParamList);
StrToFile ('text.html', test);
ShellExecute (0, 'open', 'text.html', '', '', SW_SHOW);
end;
I used the parameters that I got from LiveHTTPHeaders.
How would I successfully login to facebook with Indy?
EDIT: Tried this with XE2 and Indy 10 but I get the 'incorrect email' error:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdCookieManager, IdIOHandler,
IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdBaseComponent,
IdComponent, DateUtils, ShellAPI, IdTCPConnection, IdTCPClient, IdHTTP, Vcl.StdCtrls;
type
TForm1 = class(TForm)
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;
IdCookieManager1: TIdCookieManager;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function GetBetween (Str: String; StrStart : String; StrEnd : String) : String;
var
iPos : Integer;
BackUp : String;
begin
result := '';
iPos := Pos (StrStart, Str);
if iPos <> 0 then begin
Delete (Str, 1, iPos + Length (StrStart) - 1);
iPos := Pos (StrEnd, Str);
if iPos <> 0 then begin
result := Copy(Str,1, iPos - 1);
end;
end;
end;
function StrToFile(szFilePath:string; dwPosition:DWORD; szInput:string):Boolean;
var
hFile: DWORD;
dwSize: DWORD;
dwWritten: DWORD;
begin
Result := FALSE;
hFile := CreateFileW(PWideChar(szFilePath), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
if hFile <> INVALID_HANDLE_VALUE then
begin
dwSize := Length(szInput) * 2;
if dwSize > 0 then
begin
SetFilePointer(hFile, dwPosition, nil, FILE_BEGIN);
WriteFile(hFile, szInput[1], dwSize, dwWritten, nil);
if dwWritten = dwSize then
Result := TRUE;
end;
CloseHandle(hFile);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Response : String;
lparamList : TStringList;
begin
IDHTTP1.Request.UserAgent := 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)';
try
Response := IDHTTP1.Get('https://www.facebook.com/');
except
end;
lParamList := TStringList.Create;
lParamList.Add('lsd='+GetBetween (Response, 'name="lsd" value="', '"'));
lParamList.Add('eMail=myEmail#mySite.com');
lParamList.Add('pass=myPassword');
lParamList.Add('default_persistent'+GetBetween (Response, 'name="default_persistent" value="', '"'));
lParamList.Add('timezone=240');
lParamList.Add('lgnrnd='+GetBetween (Response, 'name="lgnrnd" value="', '"'));
lParamList.Add('lgnjs='+inttostr(DateTimeToUnix(Now)));
lParamList.Add('locale=en_US');
IDHTTP1.Request.Referer := 'https://www.facebook.com/';
try
Response := IDHTTP1.Post('https://www.facebook.com/login.php?login_attempt=1', lparamList);
except
end;
StrToFile ('test.html', 0, Response);
ShellExecute (0, 'open', 'test.html', '', '', SW_SHOW);
end;
end.
If the hoForceEncodeParams flag is enabled in the TIdHTTP.HTTPOptions property (which it is by default), then you need to fill the posted TStringList with un-encoded values. TIdHTTP.Post() will then encode the values for you when transmitting them.
Assuming the hoForceEncodeParams flag is enabled, lparamlist.Add('email=myeMail%40mysite.com'); would be transmitted as email=myeMail%2540mysite.com because the % character gets encoded as %25. Facebook would decode that as email=myeMail%40mysite.com and reject it as an invalid email.
You can either:
disable the hoForceEncodeParams flag so the TStringList values get transmitted as-is. You would then be responsible for encoding them manually.
leave the hoForceEncodeParams flag enabled and change lparamlist.Add('email=myeMail%40mysite.com'); to lparamlist.Add('email=myeMail#mysite.com'); instead. TIdHTTP.Post() in Indy 9 will then transmit it as email=myeMail#mysite.com because Indy 9 does not encode the # character. That may or may not work, depending on how lenient Facebook is.
If you upgrade to Indy 10, TIdHTTP.Post() will encode the # character as %40 as expected when the hoForceEncodeParams flag is enabled.
For anyone interested, the OP's code works just fine with the mobile versions of Facebook, so just replace www.facebook.com with touch/m.facebook.com.
Now if the OP would also be kind enough to share how he got the full version to work (at least the Cookies not enabled part), I'm sure we'll all be grateful.