Page 1 of 1

Versioning and Downloading with HTTP and PHP

Posted: Fri Aug 08, 2008 8:29 am
by jamirokwai
Dear Board,

perhaps this is usefull for somebody. It's a little include (full source-code below) to
- check the version of an application
- download the newest version
- downloads and checks are count and driven via php-scripts, see below...

to be done is threaded download. Works really great on Mac OS X PB 4.30a1, but I
didn't try in on windows. I use this code inside my litte MlChck to check for a new
version and download on purpose, if wanted. Also to be done is using other
directories than the root...

Be aware, it heavily resides on the php-script. There are some error-checks, but
it's only a work in progress, and to be improved... I will put comments in later
versions of this .pbi

Have phun!


HTTP.pbi

Code: Select all

;- HTTP.pbi
; developed by Jörg Burbach
; - get version-string from PHP-script
; - download files from server

;- todo
; - put download into a thread
; - resume downloads
; - post content (including pictures, and such)
; - use directories (instead of / )

; - Errorcodes
#HTTP_OK               = 200
#HTTP_File_Not_Found   = 404
#HTTP_Server_Error     = 500
#HTTP_No_Connection    = 1
#HTTP_Server_Not_Found = 2
#HTTP_No_Size          = 3

#CLRF                        = Chr(13) + Chr(10)

#HTTP_Buffer_Length          = 2048
Global *HTTP_Buffer          = AllocateMemory(#HTTP_Buffer_Length)
Global *HTTP_Binary_Buffer   = 0
Global HTTP_LastAnswer$      = ""
Global HTTP_ErrorCode        = 0
Global HTTP_Content$         = ""
Global HTTP_Downloaded_Size  = 0  ; contains size of downloaded File when finished
Global HTTP_Downloaded_Name$ = "" ; conatins name of downloaded File when finished
Global HTTP_Arrival_Time     = 0  ; contains time left before download completed. If -1 -> finished

; Send Query to Server and waits for Answer

Procedure.l HTTP_Get_From_Server(Connection, Query$, Filedownload = 0) ; OK
 If Connection    = 0
  HTTP_ErrorCode  = #HTTP_No_Connection
 Else
  SendNetworkString(Connection, Query$)                                                ; Send to connected Server
  Result           = ReceiveNetworkData(Connection, *HTTP_Buffer, #HTTP_Buffer_Length) ; Bytes read
  Temp$            = PeekS(*HTTP_Buffer, Result)                                       ; save whole Answer
  Position         = FindString(Temp$, "HTTP/1.1 ", 0) + Len("HTTP/1.1 ")              ; find HTTP/1.1 xyz
  Laenge           = FindString(Temp$, " ", Position) - Position                       ;
  HTTP_ErrorCode   = Val(Mid(Temp$, Position, Laenge))                                 ; read Errorcode
  If FindString(Temp$, "This File Does Not Exist!", 0) <> 0
   ProcedureReturn #HTTP_File_Not_Found 
  EndIf  
  If HTTP_ErrorCode = #HTTP_OK                                                         ; no error (= 200)
   Position        = FindString(Temp$, "Content-Length: ", 0) + Len("Content-Length: "); find "Content-Length: "
   Laenge          = FindString(Temp$, #CLRF, Position) - Position                     ;
   If Position = 0 Or Laenge = 0                                                       ; no?
    HTTP_File_Len  = 1024 * 1024 * 2                                                   ; allocate 2mb buffer
   Else                                                                                ;
    HTTP_File_Len  = Val(Mid(Temp$, Position, Laenge))                                 ; else size was told
   EndIf                                                                               ;
   If HTTP_File_Len > Result
    *Back_Buffer = AllocateMemory(HTTP_File_Len)
    Speicher     = Result
    CopyMemory(*HTTP_Buffer, *Back_Buffer, Result)
    While Result <> 0 ;HTTP_Laenge > Speicher
     Result   = ReceiveNetworkData(Connection, *Back_Buffer + Speicher, HTTP_File_Len)
     Speicher = Speicher + Result
     HTTP_Downloaded_Size = Speicher
    Wend
    HTTP_LastAnswer$   = PeekS(*Back_Buffer, Speicher)
    If Filedownload 
     If *HTTP_Binary_Buffer 
      FreeMemory(*HTTP_Binary_Buffer)
     EndIf
     *HTTP_Binary_Buffer = AllocateMemory(HTTP_File_Len)
     CopyMemory(*Back_Buffer + Speicher - HTTP_File_Len, *HTTP_Binary_Buffer, HTTP_File_Len)
     Position = FindString(HTTP_LastAnswer$, "filename=", 0) + 10
     Laenge = FindString(HTTP_LastAnswer$, Chr(34), Position)
     HTTP_Downloaded_Name$ = Mid(HTTP_LastAnswer$, Position, Laenge - Position)
    EndIf
    FreeMemory(*Back_Buffer)
   Else
    HTTP_LastAnswer$   = PeekS(*HTTP_Buffer, Result)                                          ; save whole Answer
   EndIf
   HTTP_Content$ = Mid(HTTP_LastAnswer$, Len(HTTP_LastAnswer$) - HTTP_File_Len + 1, HTTP_File_Len) ; save Content only
   HTTP_ErrorCode = 0
  Else
   HTTP_ErrorCode = #HTTP_No_Size
  EndIf
 EndIf
 ProcedureReturn HTTP_ErrorCode 
EndProcedure

; Produces Query-String for Requesting Data of Webserver

Procedure.s HTTP_Make_Get(URL$, File$, Parameter$ = "0") ; OK
 If Parameter$ <> "0" : Param$ = "?" + Parameter$ : Else : Param$ = "" : EndIf
 Query$ = "GET /" + File$ + Param$ + " HTTP/1.1" + #CLRF
 Query$ + "Host: " + URL$ + #CLRF
 Query$ + "Connection: Close" + #CLRF + #CLRF
 ProcedureReturn Query$
EndProcedure 

; Produces Head-Query for requesting only information without data

Procedure.s HTTP_Make_Head(URL$, File$, Parameter$) ; OK
 If Parameter$ <> "0" : Param$ = "?" + Parameter$ : Else : Param$ = "" : EndIf
 Query$ = "HEAD /" + File$ + Param$ + " HTTP/1.1" + #CLRF
 Query$ + "Host: " + URL$ + #CLRF
 Query$ + "Connection: Close" + #CLRF + #CLRF
 ProcedureReturn Query$
EndProcedure

; Connect to HTTP-Server Server$ on a given Port, Default = 80
;  gives back Handle of Connection

Procedure.l HTTP_Connect_Server(Server$, Port = 80) ; OK
 Connection = OpenNetworkConnection(Server$, Port)
 If Connection  = 0
  HTTP_ErrorCode = #HTTP_Server_Not_Found
 Else
  HTTP_ErrorCode = #HTTP_OK
 EndIf
 ProcedureReturn Connection
EndProcedure

; Disconnect from Server using handle Connection

Procedure.l HTTP_Disconnect_Server(Connection) ; OK
 If Connection <> 0 
  ProcedureReturn CloseNetworkConnection(Connection)
 EndIf
EndProcedure

; Check Version from URL-File, gives back a string like "x.yy" or -1 if failed

Procedure.s HTTP_Get_File_Version(Server$, Port, Application$) ; OK
 Checker = HTTP_Connect_Server(Server$, Port)
 Query$ = HTTP_Make_Get(Server$, "version.php", "software=" + Application$)
 HTTP_Get_From_Server(Checker,Query$)
 HTTP_Disconnect_Server(Checker)
 ProcedureReturn HTTP_Content$
EndProcedure

; Download a certain File from a Server, see PHP-script for more information

Procedure.l HTTP_Get_File(Server$, Port, Application$)
 Checker = HTTP_Connect_Server(Server$, Port)
 Query$ = HTTP_Make_Get(Server$, "download.php", "download=" + Application$)
 If HTTP_Get_From_Server(Checker, Query$, 1) <> #HTTP_File_Not_Found
  HTTP_Disconnect_Server(Checker)
  ProcedureReturn #HTTP_OK
 EndIf
 ProcedureReturn #HTTP_File_Not_Found 
EndProcedure
HTTP_Demo.pb - this will check the newest version of MlChck and download it

Code: Select all

InitNetwork()
Debug http_Get_file_version("www.server.de", 80, "MlChck")
Debug http_Get_File("www.server.de", 80, "MlChck")
Debug "download complete: " + HTTP_Downloaded_Name$
CreateFile(0,HTTP_Downloaded_Name$)
WriteData(0, *HTTP_Binary_Buffer, HTTP_Downloaded_Size)
CloseFile(0)
version.php - really rudimentary, could be made better...

Code: Select all

<?php
if ($_GET["software"] == "MlChck") { echo "0.99"; } else { echo "-1"; }
?>
download.php - you will need to set your server-directories according to this script,
or simply change the script. Downloading will not work from here, because I shortened
the script. The directories simply don't exists :)

Code: Select all

<?php
function output_file($file,$name)
{
//do something on download abort/finish
//register_shutdown_function( 'function_name'  );
  if(!file_exists($file)) die('This File Does Not Exist!');
  $size = filesize($file);
  $name = rawurldecode($name);

  if (ereg('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
    $UserBrowser = "Opera";
  elseif (ereg('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
    $UserBrowser = "IE";
  else
    $UserBrowser = '';

/// important for download im most browser
  $mime_type = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ? 'application/octetstream' : 'application/octet-stream';
  @ob_end_clean(); /// decrease cpu usage extreme
  header('Content-Type: ' . $mime_type);
  header('Content-Disposition: attachment; filename="'.$name.'"');
  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  header('Accept-Ranges: bytes');
  header("Cache-control: private");
  header('Pragma: private');

/////  multipart-download and resume-download
  if(isset($_SERVER['HTTP_RANGE'])) {
    list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
    str_replace($range, "-", $range);
    $size2 = $size-1;
    $new_length = $size-$range;
    header("HTTP/1.1 206 Partial Content");
    header("Content-Length: $new_length");
    header("Content-Range: bytes $range$size2/$size");
  } else {
    $size2=$size-1;
    header("Content-Length: ".$size);
  }
  $chunksize = 1*(1024*1024);
  $bytes_send = 0;
  if ($file = fopen($file, 'r')) {
    if(isset($_SERVER['HTTP_RANGE'])) fseek($file, $range);
    while(!feof($file) and (connection_status()==0)) {
      $buffer = fread($file, $chunksize);
      print($buffer);//echo($buffer); // is also possible
      flush();
      $bytes_send += strlen($buffer);
      //sleep(1);//// decrease download speed
    }
    fclose($file);
  } else die('error can not open file');
  if(isset($new_length)) $size = $new_length;
  die();
}

// which file to download, which filename
output_file("downloads/" . $_GET["download"] . ".zip", $_GET["download"] . ".zip");
?>