Download File Using Synapse Compiling Error lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Mattze96 · 2013-08-03 10:51 Hi, i want to create a programm launcher, that updates my programm when an update is avaible, but i get errors, when compiling my project. I have just a simple form with a button which should start the download. In line 61 at HTTPGetResult.Document.SaveToFile(TargetFile); i get an "Ilegall qualifier" Error. Im wondering because ive copied this function from Synapse lazarus wiki (advanced http): http://wiki.freepa scal.org/Synapse#Downloading_files_from_an_HTTP_server Here is my Code: Code: [Select]unit Unit1; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, httpsend; type   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   private     { private declarations }   public     { public declarations }     function DownloadHTTP(URL: string; FileName: string): boolean;   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } function DownloadHTTP(URL, TargetFile: string): Boolean; // Download file; retry if necessary. // Could use Synapse HttpGetBinary, but that doesn't deal // with result codes (i.e. it happily downloads a 404 error document) const   MaxRetries = 3; var   HTTPGetResult: Boolean;   HTTPSender: THTTPSend;   RetryAttempt: Integer; begin   Result := False;   RetryAttempt := 1;   HTTPSender := THTTPSend.Create;   try     try       // Try to get the file       HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);       while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do       begin         Sleep(500 * RetryAttempt);         HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);         RetryAttempt := RetryAttempt + 1;       end;       // If we have an answer from the server, check if the file       // was sent to us.       case HTTPSender.Resultcode of         100..299:           begin             HTTPGetResult.Document.SaveToFile(TargetFile);             Result := True;           end; //informational, success         300..399: Result := False; // redirection. Not implemented, but could be.         400..499: Result := False; // client error; 404 not found etc         500..599: Result := False; // internal server error         else Result := False; // unknown code       end;     except       // We don't care for the reason for this error; the download failed.       Result := False;     end;   finally     HTTPSender.Free;   end; end; procedure TForm1.Button1Click(Sender: TObject); begin end; end. best regards matthias ---------------------------------------------------------------------- kamischi · 2013-08-03 11:21 I haven't tested, but since HTTPGetResult is a boolean, HTTPGetResult.Document.SaveToFile is an error. I haven't tested it, but from checking the class declaration of HTTPSend, i think that HTTPSender should be used, in other words, replace   HTTPGetResult.Document.SaveToFile(TargetFile); by   HTTPSender.Document.SaveToFile(TargetFile); If this works, please update the wiki pages. ---------------------------------------------------------------------- Leledumbo · 2013-08-03 11:23 QuoteIn line 61 at HTTPGetResult.Document.SaveToFile(TargetFile); i get an "Ilegall qualifier" Error Quotevar   HTTPGetResult: Boolean; Clear enough? The example is wrong then, I think it should be HTTPSender instead of HTTPGetResult. ---------------------------------------------------------------------- Mattze96 · 2013-08-04 11:33 Hi, thanks, the change HTTPSender.Document.... works fine and it downloads files. But I've got a sourceforge.net page and i want, that my launcher downloads the newest version. Example:(https://sourceforge.net/projects/drteach/files/latest/downloa d) http://wiki.freepascal.org/Download_from_SourceForge I need a working function that gets the real URL of a special sourceforgelink so that my DownloadHTTP function can download the file. But I get again some Errors and i don't know how to fix them. I copied the whole Synapse-library into my project folder. I hope my question is clear and here is my code: Errorlog: Code: [Select]Hint: Start of reading config file /etc/fpc.cfg Hint: End of reading config file /etc/fpc.cfg Free Pascal Compiler version 2.6.2 [2013/03/17] for x86_64 Copyright (c) 1993-2012 by Florian Klaempfl and others Target OS: Linux for x86-64 Compiling project1.lpr Compiling unit1.pas unit1.pas(20,28) Hint: Parameter "Sender" not used unit1.pas(129,15) Error: Identifier not found "PosEx" unit1.pas(133,30) Error: Identifier not found "PosEx" unit1.pas(140,16) Error: Identifier not found "RPos" unit1.pas(143,28) Error: Identifier not found "RPosEx" unit1.pas(167,15) Error: Identifier not found "infoln" unit1.pas(168,15) Error: Identifier not found "infoln" unit1.pas(204,13) Error: Identifier not found "infoln" unit1.pas(222,39) Error: Duplicate identifier "Refresh" unit1.pas(222,39) Hint: Identifier already defined in unit CONTROLS at line 1505 unit1.pas(245,18) Error: Identifier not found "PosEx" unit1.pas(246,17) Error: Identifier not found "infoln" unit1.pas(247,17) Error: Identifier not found "infoln" unit1.pas(259) Fatal: There were 11 errors compiling module, stopping Code: [Select]unit Unit1; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,   ComCtrls, Menus, httpsend; type   { TForm1 }   TForm1 = class(TForm)     Button1: TButton;     CheckBox1: TCheckBox;     ProgressBar1: TProgressBar;     StatusBar1: TStatusBar;     procedure Button1Click(Sender: TObject);   private     { private declarations }   public     { public declarations }     function DownloadHTTP(URL: string; TargetFile: string): boolean;     function SourceForgeURL(URL: string): string;     function SFDirectLinkURL(URL: string; Document: TMemoryStream): string;   end; var   Form1: TForm1; implementation {$R *.lfm} { TForm1 } //function DownloadHTTP(URL, TargetFile: string): Boolean; //end; procedure TForm1.Button1Click(Sender: TObject); var sucess: Boolean; var url: String; begin   url:=SourceForgeURL('https://sourceforge.net/projects/drteach/files/ latest/download');   sucess:=DownloadHTTP(url, 'testfile');   if sucess then ShowMessage('Sucess'); end; function TForm1.DownloadHTTP(URL: string; TargetFile: string): boolean;  // Download file; retry if necessary. // Could use Synapse HttpGetBinary, but that doesn't deal // with result codes (i.e. it happily downloads a 404 error document) const   MaxRetries = 3; var   HTTPGetResult: Boolean;   HTTPSender: THTTPSend;   RetryAttempt: Integer; begin   Result := False;   //URL:=SourceForgeURL(URL);   RetryAttempt := 1;   HTTPSender := THTTPSend.Create;   try     try       // Try to get the file       HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);       while (HTTPGetResult = False) and (RetryAttempt < MaxRetries) do       begin         Sleep(500 * RetryAttempt);         HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);         RetryAttempt := RetryAttempt + 1;       end;       // If we have an answer from the server, check if the file       // was sent to us.       case HTTPSender.Resultcode of         100..299:           begin             HTTPSender.Document.SaveToFile(TargetFile);             Result := True;           end; //informational, success         300..399: Result := False; // redirection. Not implemented, but could be.         400..499: Result := False; // client error; 404 not found etc         500..599: Result := False; // internal server error         else Result := False; // unknown code       end;     except       // We don't care for the reason for this error; the download failed.       Result := False;     end;   finally     HTTPSender.Free;   end; end; function TForm1.SourceForgeURL(URL: string): string; // Detects sourceforge download and tries to deal with // redirection, and extracting direct download link. // Thanks to // Ocye: http://lazarus.freepascal.org/index.php/topic,13425.msg70575. html#msg70575 const   SFProjectPart = '//sourceforge.net/projects/';   SFFilesPart = '/files/';   SFDownloadPart ='/download'; var   HTTPSender: THTTPSend;   i, j: integer;   FoundCorrectURL: boolean;   SFDirectory: string; //Sourceforge directory   SFDirectoryBegin: integer;   SFFileBegin: integer;   SFFilename: string; //Sourceforge name of file   SFProject: string;   SFProjectBegin: integer; begin   // Detect SourceForge download; e.g. from URL   //          1         2         3         4         5         6       7         8         9   // 12345578901234567890123455789012345678901234557890123456789012345 57890123456789012345578901234567890   // http://sourceforge.net/projects/base64decoder/files/base64decoder /version%202.0/b64util.zip/download   //                                 ^^^project^^^       ^^^directory............^^^ ^^^file^^^   FoundCorrectURL:=true; //Assume not a SF download   i:=Pos(SFProjectPart, URL);   if i>0 then   begin     // Possibly found project; now extract project, directory and filename parts.     SFProjectBegin:=i+Length(SFProjectPart);     j := PosEx(SFFilesPart, URL, SFProjectBegin);     if (j>0) then     begin       SFProject:=Copy(URL, SFProjectBegin, j-SFProjectBegin);       SFDirectoryBegin:=PosEx(SFFilesPart, URL, SFProjectBegin)+Length(SFFilesPart);       if SFDirectoryBegin>0 then       begin         // Find file         // URL might have trailing arguments... so: search for first         // /download coming up from the right, but it should be after         // /files/         i:=RPos(SFDownloadPart, URL);         // Now look for previous / so we can make out the file         // This might perhaps be the trailing / in /files/         SFFileBegin:=RPosEx('/',URL,i-1)+1;         if SFFileBegin>0 then         begin           SFFilename:=Copy(URL,SFFileBegin, i-SFFileBegin);           //Include trailing /           SFDirectory:=Copy(URL, SFDirectoryBegin, SFFileBegin- SFDirectoryBegin);           FoundCorrectURL:=false;         end;       end;     end;   end;   if not FoundCorrectURL then   begin     try       // Rewrite URL if needed for Sourceforge download redirection       // Detect direct link in HTML body and get URL from that       HTTPSender := THTTPSend.Create;       //Who knows, this might help:       HTTPSender.UserAgent:='curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0 OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18';       while not FoundCorrectURL do       begin         HTTPSender.HTTPMethod('GET', URL);         infoln('debug: headers:');         infoln(HTTPSender.Headers.Text);         case HTTPSender.Resultcode of           301, 302, 307:             begin               for i := 0 to HTTPSender.Headers.Count - 1 do                 if (Pos('Location: ', HTTPSender.Headers.Strings[i]) > 0) or                   (Pos('location: ', HTTPSender.Headers.Strings[i]) > 0) then                 begin                   j := Pos('use_mirror=', HTTPSender.Headers.Strings[i]);                   if j > 0 then                     URL :=                       'http://' + RightStr(HTTPSender.Headers.Strings[i],                       length(HTTPSender.Headers.Strings[i]) - j - 10) +                       '.dl.sourceforge.net/project/' +                       SFProject + '/' + SFDirectory + SFFilename                   else                     URL:=StringReplace(                       HTTPSender.Headers.Strings[i], 'Location: ', '', []);                   HTTPSender.Clear;//httpsend                   FoundCorrectURL:=true;                   break; //out of rewriting loop               end;             end;           100..200:             begin               //Assume a sourceforge timer/direct link page               URL:=SFDirectLinkURL(URL, HTTPSender.Document); //Find out               FoundCorrectURL:=true; //We're done by now             end;           500: raise Exception.Create('No internet connection available');             //Internal Server Error ('+aURL+')');           else             raise Exception.Create('Download failed with error code ' +               IntToStr(HTTPSender.ResultCode) + ' (' + HTTPSender.ResultString + ')');         end;//case       end;//while       infoln('debug: resulting url after sf redir: *' + URL + '*');     finally       HTTPSender.Free;     end;   end;   result:=URL; end; function TForm1.SFDirectLinkURL(URL: string; Document: TMemoryStream): string;   { Transform this part of the body: into a valid URL: http://downloads.sourceforge.net/project/base64decoder/base64decoder/v ersion%202.0/b64util.zip?r=&ts=1329648745&use_mirror=kent } const   Refresh='0 then       begin         URLStart:=AnsiPos(URLMarker, HTMLBody[Counter])+Length(URLMarker);         if URLStart>RefreshStart then         begin           // Look for closing "           URL:=Copy(HTMLBody[Counter],             URLStart,             PosEx('"',HTMLBody[Counter],URLStart+1)-URLStart);           infoln('debug: new url after sf noscript:');           infoln(URL);           break;         end;       end;     end;   finally     HTMLBody.Free;   end;   result:=URL; end; end. ---------------------------------------------------------------------- Mattze96 · 2013-08-04 12:50 Update: The solution is not very important. I've just setup my own server ---------------------------------------------------------------------- kamischi · 2013-08-04 13:52 PosEx is from the unit StrUtils. Add "StrUtils" to your Uses Part of the program should fix the compile error. no idea, where Infoln comes from. ---------------------------------------------------------------------- BigChimp · 2013-08-04 14:08 The infoln call is in my fpcup tool to output diagnostic info. Seems somebody copied some code from fpcup instead of the wiki article (fine by me though). ---------------------------------------------------------------------- Mattze96 · 2013-08-04 14:08 Well thank you. StrUtils are working. These Errors are left: Code: [Select]Hint: Start of reading config file /etc/fpc.cfg Hint: End of reading config file /etc/fpc.cfg Free Pascal Compiler version 2.6.2 [2013/03/17] for x86_64 Copyright (c) 1993-2012 by Florian Klaempfl and others Target OS: Linux for x86-64 Compiling project1.lpr Compiling unit1.pas unit1.pas(20,28) Hint: Parameter "Sender" not used unit1.pas(167,15) Error: Identifier not found "infoln" unit1.pas(168,15) Error: Identifier not found "infoln" unit1.pas(204,13) Error: Identifier not found "infoln" unit1.pas(222,39) Error: Duplicate identifier "Refresh" unit1.pas(222,39) Hint: Identifier already defined in unit CONTROLS at line 1505 unit1.pas(246,17) Error: Identifier not found "infoln" unit1.pas(247,17) Error: Identifier not found "infoln" unit1.pas(259) Fatal: There were 6 errors compiling module, stopping ---------------------------------------------------------------------- BigChimp · 2013-08-04 14:12 Yes, you ()@#*$@*#$. If you rip out somebody's code from somewhere you have to take care to also add any called functions. If you don't understand that - what about adding this (off the top of my head, might not compile): Code: [Select]function infoln(Message: string): boolean; begin   result:=true; end; ---------------------------------------------------------------------- Mattze96 · 2013-08-04 14:29 Sorry and thank you very much. It's working now. Sorry the infoln() function was not mention in your post: http://lazar us.freepascal.org/index.php/topic,13425.msg70575.html#msg70575 or i did not found it. http://wiki.freepascal.org/Download_from_SourceForge maybe you can add it? Sorry again and thanks. matthias ---------------------------------------------------------------------- BigChimp · 2013-08-04 14:42 Quote from: Mattze96 on August 04, 2013, 04:29:16 pmSorry the infoln() function was not mention in your post: http://lazarus.freepascal.org/i ndex.php/topic,13425.msg70575.html#msg70575 or i did not found it. I think you didn't find it - it was mentioned: Quote from: BigChimp on February 19, 2012, 09:33:30 amDemo project attached; I currently have this code (using Synapse, infoln is basically writeln): Quotehttp://wiki.freepascal.org/Download_from_SourceForge No infoln there that I could find, so no changes needed. If any are needed, see below... Quote from: Mattze96 on August 04, 2013, 04:29:16 pmmaybe you can add it? So after posting the initial code (with instructions) on the forum, writing the code snippet on the wiki you are also asking to perform further modifications? Sorry, but that is your task, not mine. I'm very happy to give free support, help others as I have been helped, post articles etc but there is a limit somewhere. Quote from: Mattze96 on August 04, 2013, 04:29:16 pmSorry again and thanks. No problem - it wasn't personal, just that a lot of posts/people just take, take, take without giving back (or even help you to help them by providing the right information). Glad you got your code working. ---------------------------------------------------------------------- Miyamoto · 2013-08-10 10:10 This example uses the same instruction but i get another error Code: [Select]uses httpsend, ... function DownloadHTTP(URL, TargetFile: string): Boolean; var   HTTPGetResult: Boolean;   HTTPSender: THTTPSend; begin   Result := False;   HTTPSender := THTTPSend.Create;   try     HTTPGetResult := HTTPSender.HTTPMethod('GET', URL);     if HTTPSender.ResultCode = 100..299 then begin       HTTPGetResult.Document.SaveToFile(TargetFile);       Result := True;     end;   finally     HTTPSender.Free;   end; end; When compiling it says: then expected but .. found on this line     if HTTPSender.ResultCode = 100..299 then begin I don't understand why the 'Advanced version' uses: Code: [Select]        300..399: Result := False; // redirection. Not implemented, but could be.         400..499: Result := False; // client error; 404 not found etc         500..599: Result := False; // internal server error         else Result := False; // unknown code instead of: else  Result := False;    http://wiki.freepascal.org/Synapse#From_an_HTTP_server ---------------------------------------------------------------------- Jurassic Pork · 2013-08-10 10:42 hello, Miyamoto look at    this message  or this one ---------------------------------------------------------------------- eric · 2013-08-10 11:47 Code: [Select]if HTTPSender.ResultCode = 100..299 then begin should surely be: Code: [Select]if (HTTPSender.ResultCode in [100..299]) then begin