2013-03-15 11:12 UTC+0100 Viktor Szakats (harbour syenar.net)

* /harbour/* -> /*
    * moved whole Harbour source tree one level up to
      avoid single 'harbour' top dir
This commit is contained in:
vszakats
2013-03-15 11:13:30 +01:00
parent e064276c9e
commit a4a357a18b
4038 changed files with 5 additions and 0 deletions

752
extras/httpsrv/cgifunc.prg Normal file
View File

@@ -0,0 +1,752 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* uHTTPD (Micro HTTP server) cgi functions
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "error.ch"
#include "fileio.ch"
// #define HB_USE_HBTIP // Use functions from HBTIP - TOIMPLEMENT
#define CRLF ( Chr( 13 ) + Chr( 10 ) )
#xtranslate Throw( <oErr> ) => ( Eval( ErrorBlock(), <oErr> ), Break( <oErr> ) )
MEMVAR _SERVER, _GET, _POST, _COOKIE, _REQUEST, _HTTP_REQUEST
FUNCTION uhttpd_GetVars( cFields, cSeparator )
LOCAL hHashVars := { => }
LOCAL aField, cField, aFields
LOCAL cName, xValue
__defaultNIL( @cSeparator, "&" )
aFields := uhttpd_Split( cSeparator, cFields )
FOR EACH cField in aFields
aField := uhttpd_Split( "=", cField, 1 )
IF Len( aField ) == 1
hHashVars[ aField[ 1 ] ] := NIL
LOOP
ELSEIF Len( aField ) != 2
LOOP
ENDIF
cName := LTrim( aField[ 1 ] )
xValue := uhttpd_UrlDecode( aField[ 2 ] )
// Is it an array entry?
IF SubStr( cName, Len( cName ) - 1 ) == "[]"
cName := SubStr( cName, 1, Len( cName ) - 2 )
hHashVars[ cName ] := { xValue }
ELSE
// now check if variable already exists. If yes and I have already another element
// with same name, then I will change it to an array
IF hb_HPos( hHashVars, cName ) > 0
IF ! HB_ISARRAY( hHashVars[ cName ] )
// Transform it to array
hHashVars[ cName ] := { hHashVars[ cName ] }
ENDIF
AAdd( hHashVars[ cName ], xValue )
ELSE
hHashVars[ cName ] := xValue
ENDIF
ENDIF
NEXT
RETURN hHashVars
/*
uhttpd_SplitUrl( cUrl ) --> hUrl
(C) 2006 Francesco Saverio Giudice
Splits a valid URL into simple components and return them in a hash
it works like parse_url() PHP function
a URL string is something like this:
http://[username:password@]hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]]
Parameters:
cUrl - Valid URL string
Returns:
hUrl - Hash containing these keys:
SCHEME - protocol name
HOST - hostname
PORT - protocol port number
USER - username
PASS - password
PATH - path to directory and/or file
QUERY - part after question mark ?
FRAGMENT - part after hashmark #
*/
FUNCTION uhttpd_SplitUrl( cUrl )
LOCAL hUrl := { => }
LOCAL nPos, cTemp, cUserNamePassword, cHostnamePort
LOCAL cProto, cHost, cPort, nPort, cUser, cPass, cPath, cQuery, cFragment
LOCAL cUri
// Prevents case matching
hb_HCaseMatch( hUrl, .F. )
cTemp := cUrl
cUri := ""
// Starting with
// http://[username:password@]hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]]
// Read protocol
nPos := At( "://", cTemp )
IF nPos > 0
cProto := Left( cTemp, nPos - 1 )
// delete protocol from temp string
cTemp := SubStr( cTemp, nPos + 3 )
ELSE
cProto := ""
ENDIF
cUri += cProto + iif( ! Empty( cProto ), "://", "" )
// Now we have:
// [username:password@]hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]]
// Read username and password
nPos := At( "@", cTemp )
IF nPos > 0
cUserNamePassword := Left( cTemp, nPos - 1 )
// delete Username and Password from temp string
cTemp := SubStr( cTemp, nPos + 1 )
// Split username and password
nPos := At( ":", cUserNamePassword )
IF nPos > 0
cUser := Left( cUserNamePassword, nPos - 1 )
cPass := SubStr( cUserNamePassword, nPos + 1 )
ELSE
cUser := cUserNamePassword
cPass := ""
ENDIF
ELSE
cUser := ""
cPass := ""
ENDIF
// Now we have:
// hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]][#anchor]]
// Search for anchor using # char from right
nPos := RAt( "#", cTemp )
IF nPos > 0
cFragment := SubStr( cTemp, nPos + 1 )
// delete anchor from temp string
cTemp := SubStr( cTemp, 1, nPos - 1 )
ELSE
cFragment := ""
ENDIF
// Now we have:
// hostname[:port][/path[/file[.ext]][?arg1=[value][&arg2=[value]]]]
// Search for Query part using ? char from right
nPos := RAt( "?", cTemp )
IF nPos > 0
cQuery := SubStr( cTemp, nPos + 1 )
// delete query from temp string
cTemp := SubStr( cTemp, 1, nPos - 1 )
ELSE
cQuery := ""
ENDIF
// Now we have:
// hostname[:port][/path[/file[.ext]]
cUri += cTemp
// Search for Path part using / char from right
nPos := RAt( "/", cTemp )
IF nPos > 0
cPath := SubStr( cTemp, nPos )
// delete path from temp string
cTemp := SubStr( cTemp, 1, nPos - 1 )
ELSE
cPath := "/"
ENDIF
// Now we have:
// hostname[:port]
cHostnamePort := cTemp
// Searching port number
nPos := At( ":", cHostnamePort )
IF nPos > 0
cHost := Left( cHostnamePort, nPos - 1 )
cPort := SubStr( cHostnamePort, nPos + 1 )
nPort := Val( cPort )
IF nPort <= 0
nPort := -1
ENDIF
ELSE
cHost := cHostnamePort
nPort := -1
ENDIF
// Assemble hash
hUrl[ "SCHEME" ] := cProto
hUrl[ "HOST" ] := cHost
hUrl[ "PORT" ] := nPort
hUrl[ "USER" ] := cUser
hUrl[ "PASS" ] := cPass
hUrl[ "PATH" ] := cPath
hUrl[ "QUERY" ] := cQuery
hUrl[ "FRAGMENT" ] := cFragment
hUrl[ "URI" ] := cURI
// Prevents externals to add something else to this Hash
hb_HAutoAdd( hUrl, .F. )
RETURN hUrl
/*
uhttpd_SplitString( cString ) --> aLines
(C) 2006 Francesco Saverio Giudice
Splits a string into simple components and return them in an array
Parameters:
cString - Initial string
cDelim - Delimiter - default CRLF
lRemDelim - Remove delimiter from return values - default .T.
Returns:
aLines - Array with lines / fields for each element
Sample:
SplitString( "this=is=a=line=with=equals", "=" ) -> { "this", "is", "a", "line", "with", "equals" }
*/
FUNCTION uhttpd_SplitString( cString, cDelim, lRemDelim, nCount )
LOCAL nEOLPos
LOCAL cBuffer := cString
LOCAL aLines := {}, cLine
LOCAL nHowMany := 0
__defaultNIL( @cDelim, ( Chr( 13 ) + Chr( 10 ) ) )
__defaultNIL( @lRemDelim, .T. )
__defaultNIL( @nCount, -1 )
// WriteToLogFile( "Splitstring: " + CStr( cString ) )
DO WHILE ( nEOLPos := At( cDelim, cBuffer ) ) > 0
nHowMany++
IF lRemDelim
cLine := Left( cBuffer, nEOLPos - 1 )
ELSE
cLine := Left( cBuffer, ( nEOLPos + Len( cDelim ) ) - 1 )
ENDIF
// WriteToLogFile( "cBuffer, cDelim, nEOLPos, cLine: " + CStr( cBuffer ) + "," + CStr( cDelim ) + "," + CStr( nEOLPos ) + "," + CStr( cLine ) )
AAdd( aLines, cLine )
cBuffer := SubStr( cBuffer, nEOLPos + Len( cDelim ) )
IF nCount > -1
IF nHowMany >= nCount
EXIT
ENDIF
ENDIF
ENDDO
// Check last line
IF Len( cBuffer ) > 0
AAdd( aLines, cBuffer )
ENDIF
RETURN aLines
/************************************************************
* Encoding URL
*/
FUNCTION uhttpd_URLEncode( cString, lComplete )
#ifdef HB_USE_HBTIP
__defaultNIL( @lComplete, .T. )
RETURN TIPENCODERURL_ENCODE( cString, lComplete )
#else
LOCAL cRet := "", i, nVal, cChar
__defaultNIL( @lComplete, .T. )
FOR i := 1 TO Len( cString )
cChar := SubStr( cString, i, 1 )
DO CASE
CASE cChar == " "
cRet += "+"
CASE ( cChar >= "A" .AND. cChar <= "Z" ) .OR. ;
( cChar >= "a" .AND. cChar <= "z" ) .OR. ;
( cChar >= "0" .AND. cChar <= "9" ) .OR. ;
cChar == "." .OR. cChar == "," .OR. cChar == "&" .OR. ;
cChar == "/" .OR. cChar == ";" .OR. cChar == "_"
cRet += cChar
CASE iif( ! lComplete, cChar == ":" .OR. cChar == "?" .OR. cChar == "=", .F. )
cRet += cChar
OTHERWISE
nVal := Asc( cChar )
cRet += "%" + hb_NumToHex( nVal )
ENDCASE
NEXT
RETURN cRet
/************************************************************
* Decoding URL
*/
FUNCTION uhttpd_URLDecode( cString )
#ifdef HB_USE_HBTIP
RETURN TIPENCODERURL_DECODE( cString )
#else
LOCAL cRet := "", i, cChar
FOR i := 1 TO Len( cString )
cChar := SubStr( cString, i, 1 )
DO CASE
CASE cChar == "+"
cRet += " "
CASE cChar == "%"
i++
cRet += Chr( hb_HexToNum( SubStr( cString, i, 2 ) ) )
i++
OTHERWISE
cRet += cChar
ENDCASE
NEXT
RETURN cRet
#endif
/*
* DateToGMT( dDate, cTime, nDayToAdd ) --> cGMTDate
*
* dDate : default Date()
* cTime : default "00:00:00"
* nDayToAdd : default 0 - may be a negative number
*
* cGMTDate : The string return in form of "Sat, 31 Oct 2003 00:00:00 GMT"
*/
FUNCTION uhttpd_DateToGMT( dDate, cTime, nDayToAdd, nSecsToAdd )
LOCAL aDays := { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }
LOCAL aMonths := { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
__defaultNIL( @dDate, Date() )
__defaultNIL( @cTime, Time() )
__defaultNIL( @nDayToAdd, 0 )
__defaultNIL( @nSecsToAdd, 0 )
// TraceLog( "DateToGMT - StartingValue", dDate, cTime, nDayToAdd, nSecsToAdd )
cTime := uhttpd_AddSecondsToTime( cTime, nSecsToAdd, @nDayToAdd )
dDate += nDayToAdd
RETURN ;
aDays[ DoW( dDate ) ] + ", " + ;
StrZero( Day( dDate ), 2 ) + " " + ;
aMonths[ Month( dDate ) ] + " " + ;
StrZero( Year( dDate ), 4 ) + " " + ;
cTime + " GMT"
/*
* AddSecondsToTime( cTime, nSecsToAdd, @nDaysAdded ) --> cNewTime
*
* cTime : default "00:00:00"
* nSecsToAdd : default 0 - may be a negative number
* nDaysAdded : (out) return how many days add (or subtract) to actual date if numbers seconds is
* more than 86400 seconds (1 day)
*
* cNewTime : The new time string
*
* Rules: time is converted to seconds from midnight, then added of nSecsToAdd. Divided of 1 day and
* then reverted to Time string
*/
FUNCTION uhttpd_AddSecondsToTime( cTime, nSecsToAdd, nDaysAdded )
LOCAL nOneDaySeconds := 86400 // 24 * 60 * 60
LOCAL cNewTime, nSecs
__defaultNIL( @cTime, Time() )
__defaultNIL( @nSecsToAdd, 0 )
// nDaysAdded can be already valued, so below i add to this value
__defaultNIL( @nDaysAdded, 0 )
IF nSecsToAdd != 0
nSecs := Secs( cTime ) + nSecsToAdd
nDaysAdded += Int( nSecs / nOneDaySeconds ) // Attention! nDaysAdded can be already filled
nSecs := nSecs - nDaysAdded
cNewTime := TString( nSecs )
ELSE
cNewTime := cTime
ENDIF
RETURN cNewTime
FUNCTION uhttpd_TimeDiffAsSeconds( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
LOCAL aRetVal
__defaultNIL( @dDateEnd, Date() )
__defaultNIL( @cTimeEnd, Time() )
aRetVal := ft_Elapsed( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
RETURN aRetVal[ 4, 2 ]
FUNCTION uhttpd_OutputString( cString, aTranslate, lProtected )
LOCAL cHtml
__defaultNIL( @lProtected, .F. )
__defaultNIL( @aTranslate, { { '"', "&quot;" }, { " ", "&nbsp;" } } )
// TraceLog( "OutputString( cString, aTranslate, lProtected )", cString, aTranslate, lProtected )
IF lProtected
cHtml := uhttpd_HtmlSpecialChars( cString )
ELSE
cHtml := uhttpd_TranslateStrings( cString, aTranslate )
ENDIF
// TraceLog( "OutputString() = cHtml", cHtml )
RETURN cHtml
FUNCTION uhttpd_HtmlSpecialChars( cString, cQuote_style )
LOCAL aTranslations := { ;
{ "&", "&amp;" }, ;
{ "<", "&lt;" }, ;
{ ">", "&gt;" } }
RETURN uhttpd_HtmlConvertChars( cString, cQuote_style, aTranslations )
FUNCTION uhttpd_HtmlConvertChars( cString, cQuote_style, aTranslations )
__defaultNIL( @cQuote_style, "ENT_COMPAT" )
DO CASE
CASE cQuote_style == "ENT_COMPAT"
AAdd( aTranslations, { '"', "&quot;" } )
CASE cQuote_style == "ENT_QUOTES"
AAdd( aTranslations, { '"', "&quot;" } )
AAdd( aTranslations, { "'", "&#039;" } )
CASE cQuote_style == "ENT_NOQUOTES"
ENDCASE
RETURN uhttpd_TranslateStrings( cString, aTranslations )
FUNCTION uhttpd_CRLF2BR( cString )
LOCAL aTranslations := { ;
{ CRLF, "<br />" } }
RETURN uhttpd_TranslateStrings( cString, aTranslations )
FUNCTION uhttpd_TranslateStrings( cString, aTranslate )
LOCAL aTran
FOR EACH aTran IN aTranslate
IF aTran[ 1 ] $ cString
cString := StrTran( cString, aTran[ 1 ], aTran[ 2 ] )
ENDIF
NEXT
RETURN cString
FUNCTION uhttpd_StrStr( cString, cSearch )
LOCAL nPos := At( cSearch, cString )
LOCAL cVal := iif( nPos > 0, SubStr( cString, nPos ), NIL )
RETURN cVal
FUNCTION uhttpd_StrIStr( cString, cSearch )
RETURN uhttpd_StrStr( Upper( cSearch ), Upper( cString ) )
FUNCTION uhttpd_HtmlEntities( cString, cQuote_style )
LOCAL aTranslations := {}
LOCAL i
// ATTENTION, these chars are visible only with OEM font
FOR i := 160 TO 255
AAdd( aTranslations, { hb_BChar( i ), "&#" + Str( i, 3 ) + ";" } )
NEXT
RETURN uhttpd_HtmlConvertChars( cString, cQuote_style, aTranslations )
PROCEDURE uhttpd_Die( cError )
LOCAL oErr, lError
IF cError != NIL // THEN OutStd( cError )
#if 0
__OutDebug( "cError: ", cError )
IF ! oCGI:HeaderSent()
oCGI:WriteLN( CRLF2BR( cError ), CRLF2BR( CRLF() ) )
// oCGI:WriteLN( CRLF2BR( hb_DumpVar( TConfigure():hConfig ) ) )
ENDIF
#endif
// Generate Error
oErr := ErrorNew()
oErr:severity := ES_ERROR
oErr:genCode := EG_LIMIT
oErr:subSystem := "uhttpd_CGI"
oErr:subCode := 0
oErr:description := cError
oErr:canRetry := .F.
oErr:canDefault := .F.
oErr:fileName := ""
oErr:osCode := 0
lError := Eval( ErrorBlock(), oErr )
IF ! HB_ISLOGICAL( lError ) .OR. lError
__errInHandler()
ENDIF
Break( oErr )
// QUIT
ELSE
QUIT
ENDIF
RETURN
FUNCTION uhttpd_HTMLSpace( n )
RETURN Replicate( "&nbsp;", n ) // "&#32;"
PROCEDURE uhttpd_WriteToLogFile( cString, cLog, lCreate )
LOCAL nHandle, cSep
cSep := hb_ps()
// __defaultNIL( @cLog, AppFullPath() + cSep + "logfile.log" )
__defaultNIL( @cLog, cSep + "tmp" + cSep + "logfile.log" )
__defaultNIL( @lCreate, .F. )
IF cLog != NIL
IF ! lCreate .AND. hb_FileExists( cLog )
nHandle := FOpen( cLog, FO_READWRITE + FO_SHARED )
ELSE
nHandle := hb_FCreate( cLog, FC_NORMAL, FO_READWRITE + FO_SHARED )
// __OutDebug( "Create ", nHandle )
ENDIF
// cString := "PROCEDURE: " + ProcName( -2 ) + " " + cString
IF nHandle > 0
FSeek( nHandle, 0, FS_END )
FWrite( nHandle, cString )
FWrite( nHandle, CRLF )
FClose( nHandle )
ENDIF
ENDIF
RETURN
/*********************************************************************************/
FUNCTION uhttpd_SplitFileName( cFile )
LOCAL hFile
LOCAL cPath, cName, cExt, cDrive, cSep
hb_FNameSplit( cFile, @cPath, @cName, @cExt, @cDrive )
hFile := { ;
"FILE" => cFile, ;
"DRIVE" => cDrive, ;
"PATH" => cPath, ;
"NAME" => cName, ;
"EXT" => cExt, ;
"FULLPATH" => NIL, ;
"FULLNAME" => cName + cExt, ;
"UNC" => NIL }
cSep := hb_ps()
hFile:FULLPATH := iif( ! Empty( hFile:PATH ), iif( !( Right( hFile:PATH, Len( cSep ) ) == cSep ), hFile:PATH + cSep, hFile:PATH ), "" )
hFile:UNC := hFile:FULLPATH + hFile:FULLNAME
RETURN hFile
FUNCTION uhttpd_AppFullPath()
LOCAL hExeFile := uhttpd_SplitFileName( hb_argv( 0 ) )
LOCAL cPrgFullPath := hExeFile:FULLPATH
LOCAL cPath, cSep
cSep := hb_ps()
IF Right( cPrgFullPath, Len( cSep ) ) == cSep
cPath := SubStr( cPrgFullPath, 1, Len( cPrgFullPath ) - Len( cSep ) )
ELSE
cPath := cPrgFullPath
ENDIF
RETURN cPath
FUNCTION uhttpd_AppFullName()
LOCAL hExeFile := uhttpd_SplitFileName( hb_argv( 0 ) )
RETURN hExeFile:FULLNAME
FUNCTION uhttpd_CStrToVal( cExp, cType )
IF ! HB_ISSTRING( cExp )
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
ENDIF
SWITCH cType
CASE "C"
RETURN cExp
CASE "P"
RETURN hb_HexToNum( cExp )
CASE "D"
IF cExp[ 3 ] >= "0" .AND. cExp[ 3 ] <= "9" .AND. cExp[ 5 ] >= "0" .AND. cExp[ 5 ] <= "9"
RETURN hb_SToD( cExp )
ELSE
RETURN CToD( cExp )
ENDIF
CASE "L"
RETURN iif( cExp[ 1 ] == "T" .OR. cExp[ 1 ] == "Y" .OR. cExp[ 2 ] == "T" .OR. cExp[ 2 ] == "Y", .T., .F. )
CASE "N"
RETURN Val( cExp )
CASE "M"
RETURN cExp
CASE "U"
RETURN NIL
#if 0
CASE "A"
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
CASE "B"
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
CASE "O"
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
#endif
OTHERWISE
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
ENDSWITCH
RETURN NIL
FUNCTION uhttpd_GetField( cVar, cType )
LOCAL xVal
LOCAL nPos := hb_HPos( _Request, cVar )
IF nPos > 0 // cVar IN ::h_Request:Keys
xVal := hb_HValueAt( _Request, nPos ) // ::h_Request[ cVar ]
IF Empty( xVal )
xVal := NIL
ENDIF
IF cType != NIL .AND. cType $ "NLD"
xVal := uhttpd_CStrToVal( xVal, cType )
ENDIF
ENDIF
RETURN xVal
FUNCTION uhttpd_SetField( cVar, cVal )
LOCAL xVal := uhttpd_HGetValue( _Request, cVar )
_Request[ cVar ] := cVal
RETURN xVal
FUNCTION uhttpd_HGetValue( hHash, cKey )
LOCAL nPos
LOCAL xVal
IF hHash != NIL
xVal := iif( ( nPos := hb_HPos( hHash, cKey ) ) == 0, NIL, hb_HValueAt( hHash, nPos ) )
ENDIF
// RETURN iif( cKey $ hHash:Keys, hHash[ cKey ], NIL )
RETURN xVal

193
extras/httpsrv/cookie.prg Normal file
View File

@@ -0,0 +1,193 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* uHTTPD (Micro HTTP server) cookie functions
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbclass.ch"
MEMVAR _COOKIE
FUNCTION uhttpd_CookieNew( cDomain, cPath, nExpireDays, nExpireSecs )
RETURN uhttpd_Cookie():New( cDomain, cPath, nExpireDays, nExpireSecs )
CLASS uhttpd_Cookie
// Data for cookies
VAR aCookies INIT {} // Using an array to mantain order
VAR cDomain
VAR cPath INIT "/"
VAR cExpire
VAR lSecure INIT .F.
VAR lHttpOnly
VAR nExpireDays INIT 0
VAR nExpireSecs INIT 7200 // 1 hour - TODO set environment constant
VAR lCookiesSent INIT .F.
METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpOnly )
METHOD DeleteCookie( cCookieName, cDomain, cPath, lSecure )
METHOD DeleteAllCookies( cDomain, cPath, lSecure )
METHOD GetCookie( cCookieName )
METHOD IsCookie( cCookieName ) INLINE ::GetCookie( cCookieName ) != NIL
METHOD IsCookies() INLINE ! Empty( ::aaCookieToSet )
METHOD SetCookieDefaults( cDomain, cPath, nExpireDays, nExpireSecs )
ENDCLASS
// ------------------------------
METHOD SetCookieDefaults( cDomain, cPath, nExpireDays, nExpireSecs ) CLASS uhttpd_Cookie
IF cDomain != NIL
::cDomain := cDomain
ENDIF
IF cPath != NIL
::cPath := cPath
ENDIF
IF nExpireDays != NIL
::nExpireDays := nExpireDays
ENDIF
IF nExpireSecs != NIL
::nExpireSecs := nExpireSecs
ENDIF
RETURN NIL
METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpOnly ) CLASS uhttpd_Cookie
LOCAL cStr, nPos, nCookies
__defaultNIL( @cDomain, ::cDomain )
__defaultNIL( @cPath, ::cPath )
__defaultNIL( @lHttpOnly, .F. )
IF cExpires == NIL
cExpires := uhttpd_DateToGMT( Date(), Time(), ::nExpireDays, ::nExpireSecs )
ENDIF
::lHttpOnly := lHttpOnly
IF xValue != NIL
// Search if a cookie already exists
// case sensitive
IF ( nPos := AScan( ::aCookies, {| e | e[ 1 ] == cCookieName } ) ) > 0
::aCookies[ nPos ][ 2 ] := uhttpd_UrlEncode( hb_CStr( xValue ) )
ELSE
AAdd( ::aCookies, { cCookieName, uhttpd_UrlEncode( hb_CStr( xValue ) ) } )
ENDIF
ELSE
IF ( nPos := AScan( ::aCookies, {| e | e[ 1 ] == cCookieName } ) ) > 0
hb_ADel( ::aCookies, nPos, .T. )
ENDIF
ENDIF
// Rebuild cookie string as per RFC2616 (comma separated list)
cStr := ""
nCookies := Len( ::aCookies )
AEval( ::aCookies, {| e, i | cStr += e[ 1 ] + "=" + e[ 2 ] + iif( i < nCookies, ",", "" ) } )
// cStr := cCookieName + "=" + uhttpd_UrlEncode( hb_CStr( xValue ) )
IF cDomain != NIL
cStr += "; domain=" + cDomain
ENDIF
IF cPath != NIL
cStr += "; path=" + cPath
ENDIF
IF cExpires != NIL
cStr += "; expires=" + cExpires
ENDIF
IF HB_ISLOGICAL( lSecure ) .AND. lSecure
cStr += "; secure"
ENDIF
// Send the header
// uhttpd_SetHeader( "Set-Cookie", cStr, .F. )
uhttpd_SetHeader( "Set-Cookie", cStr )
RETURN NIL
METHOD DeleteCookie( cCookieName, cDomain, cPath, lSecure ) CLASS uhttpd_Cookie
LOCAL cExpires := uhttpd_DateToGMT( Date() - 1 ) // Setting date in the past delete cookie
::SetCookie( cCookieName, "", cDomain, cPath, cExpires, lSecure )
RETURN NIL
METHOD DeleteAllCookies( cDomain, cPath, lSecure ) CLASS uhttpd_Cookie
LOCAL cCookieName
FOR EACH cCookieName IN _COOKIE:Keys
// ::DeleteCookie( SubStr( cCookieName, 2 ), cDomain, cPath, lSecure )
::DeleteCookie( cCookieName, cDomain, cPath, lSecure )
NEXT
RETURN NIL
METHOD GetCookie( cCookieName ) CLASS uhttpd_Cookie
LOCAL cHeader, cRet
LOCAL nPos := 1
DO WHILE .T.
IF ( cHeader := uhttpd_GetHeader( "Set-Cookie", @nPos ) ) != NIL
IF cHeader == cCookieName
cRet := cHeader
EXIT
ELSE
nPos++
ENDIF
ELSE
EXIT
ENDIF
ENDDO
RETURN cRet

View File

@@ -0,0 +1,89 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Show Graphic Counter</title>
<meta http-equiv="Pragma" content="no-cache">
<link rel="stylesheet" type="text/css" href="/css/base.css" />
<script type="text/javascript" src="/js/ajax.js"></script>
<script type="text/javascript">
// <![CDATA[
var divpart;
/**
* Requests table data for a specific page.
*
* @param pageNum the page number to request data for
*/
function sendData( force )
{
var qstr = getquerystring();
if ( force || qstr.length > 4 )
{
//qstr = 'w1=' + escape(qstr); // NOTE: no '?' before querystring
//xmlPost('/cgi-bin/showcounter.hrb', qstr + "&sid=" + Math.random(), tableResponseHandler);
divpart = 'result';
updatepage( escape( qstr ) );
}
return false;
}
function getquerystring()
{
var form = document.forms[ 'f1' ];
var word = form.word.value;
//alert( 'qstr: ' + qstr );
return word;
}
function updatepage( str )
{
//document.getElementById( divpart ).innerHTML = str; /* "<img src='/counter/" + str + "' />"; */
document.getElementById( divpart ).innerHTML = "<img src='/cgi-bin/showcounter.hrb?w=" + str + "' />";
}
/**
* Handler for server's response to table requests.
* Table content is pulled from response XML and a HTML
* table is built. The table is then inserted into the
* 'tableSection' DIV.
*/
function tableResponseHandler()
{
// Make sure the request is loaded (readyState = 4)
if (req.readyState == 4)
{
// Make sure the status is "OK"
if (req.status == 200)
{
// shutdown Ajax loading progress
EndProgress();
// transform
//document.write( xsldoc );
updatepage( req.responseText );
}
else
{
EndProgress();
alert("There was a problem retrieving the XML data:\n" +
req.statusText);
}
}
}
// ]]>
</script>
</head>
<body>
This is a simple ajax test. Please type a number (at least 4 digits).
<form name="f1" onsubmit='JavaScript:sendData(true);return false'>
<p>Number: <input name="word" type="text" onkeyup='JavaScript:sendData(false);return false'>
<input value="Go" type="button" onclick='JavaScript:sendData(true)'></p>
<div id="result"></div>
</form>
Return to <a href="/">Main Page</a>
</body>
</html>

View File

@@ -0,0 +1,64 @@
body { color:black; font-size:10pt; font-family:Tahoma,Helvetica,Arial; }
a.pageSection:link { color: #ff0000; }
a.pageSection:visited { color: #0000ff; }
a.pageSection:hover { background: #66ff66; }
/* a:link { color: #ff0000; } */
/* a:visited { color: #0000ff; } */
/* a:hover { background: #66ff66; } */
A {
color: #0000FF;
text-decoration: none;
}
A:hover { color: #6699cc; text-decoration: underline; }
A.urls { color: #0A68B6; text-decoration: none; }
A.urls:hover { color: #6699cc; text-decoration: underline; }
A.tags { color: #008080; text-decoration: none; }
A.tags:hover { color: #6699cc; text-decoration: underline; }
table.pagetable td
{
padding: 3px;
}
table.datatable
{
width: 100%;
font-size: 12px;
font-family: Verdana;
}
table.datatable tr th
{
padding: 2px;
border-bottom:1px solid black;
text-align: left;
}
table.datatable tr td
{
padding: 2px;
border-bottom: 1px solid white;
vertical-align: top;
}
table.datatable tr.odd
{
background-color: #7FFFD4;
}
table.datatable tr.even
{
background-color: #F0F0F0;
}
table.datatable tr.blank
{
background-color: #FFFFFF;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Harbour uHTTPD Server example</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>Simple uHTTPD server demo</h1>
<br />
Examples:
<br />
<ul>
<li>
<a href="testajax.html">Test Ajax</a> (*)
</li>
<li>
<a href="testxmldb.html">Test Ajax XML Database</a> (*)
</li>
<li>
<a href="counter.html">Test Ajax Counter</a> (*) (**)
</li>
<li>
<a href="/serverstatus">Server Status</a>
</li>
<li>
<a href="/info">Alias to /cgi-bin/info.hrb page with server variables</a> (*)
</li>
<li>
<a href="postsample.html">Post method example</a>
</li>
<li>
<a href="/cookie">Cookie example</a> (*)
</li>
</ul>
<br />
(*) Before run these examples, please build files in /uhttpd/modules folder using 'hbmk2 modules.hbp'
<br />
(**) This example requires libgd package
</body>
</html>

View File

@@ -0,0 +1,257 @@
/*
Global data var declaration
*/
/**
* Open a connection to the specified URL, which is
* intended to provide an XML message. The specified data
* is sent to the server as parameters. This is the same as
* calling xmlOpen("POST", url, toSend, responseHandler).
*
* @param string url The URL to connect to.
* @param string toSend The data to send to the server; must be URL encoded.
* @param function responseHandler The Javascript function handling server response.
*/
function xmlPost(url, toSend, responseHandler)
{
StartProgress();
xmlOpen("POST", url, toSend, responseHandler);
}
/**
* Open a connection to the specified URL, which is
* intended to provide an XML message. No other data is
* sent to the server. This is the same as calling
* xmlOpen("GET", url, null, responseHandler).
*
* @param string url The URL to connect to.
* @param function responseHandler The Javascript function handling server response.
*/
function xmlGet(url, responseHandler)
{
StartProgress();
xmlOpen("GET", url, null, responseHandler);
}
/**
* Open a connection to the specified URL, which is
* intended to respond with an XML message.
*
* @param string method The connection method; either "GET" or "POST".
* @param string url The URL to connect to.
* @param string toSend The data to send to the server; must be URL encoded.
* @param function responseHandler The Javascript function handling server response.
*/
function xmlOpen(method, url, toSend, responseHandler)
{
req = null;
if (window.XMLHttpRequest)
{
// browser has native support for XMLHttpRequest object
req = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
// try XMLHTTP ActiveX (Internet Explorer) version
req = new ActiveXObject("Microsoft.XMLHTTP");
}
if(req)
{
req.onreadystatechange = responseHandler;
req.open(method, url, true);
req.setRequestHeader("content-type","application/x-www-form-urlencoded");
req.send(toSend);
}
else
{
alert('Your browser does not seem to support XMLHttpRequest.');
}
}
/**
* Gets the first child node of <code>parent</code> with the
* specified tag name.
*
* @param parent the parent XML DOM node to search
* @param tagName the tag name of the child node to search for
*/
function getNode(parent, tagName)
{
var i;
var max = parent.childNodes.length;
// Check each child node
for(i = 0; i < max; i++)
{
if(parent.childNodes[i].tagName)
{
if(parent.childNodes[i].tagName.toUpperCase() == tagName.toUpperCase())
{
// We found a matching child node; return it.
return parent.childNodes[i];
}
}
}
// One was not found; return null
return null;
}
/**
* Gets the first child node of <code>parent</code> with the
* specified tag name and whose value of the 'key' attribute
* is <code>key</code>.
*
* @param parent the parent XML DOM node to search
* @param tagName the tag name of the child nodes to search in
* @param key the value of the 'key' attribute to search on
*/
function getNodesWithKey(parent, tagName, key)
{
var i;
var cellNodes = parent.getElementsByTagName(tagName);
var max = cellNodes.length;
// Check each cell node for the specified value for
// the 'key' attribute
for(i = 0; i < max; i++)
{
if(cellNodes[i].getAttribute('key') == key)
{
// We found a matching cell node; return it.
return cellNodes[i];
}
}
// One was not found; return null
return null;
}
// ----- xslT functions --------------------------------------------------------------------
// Immediately try to load the xsl file asynchronously
var xsldocloaded = false;
var xsldoc;
function xslGet( xslfile )
{
if (window.XSLTProcessor)
{
// support Mozilla/Gecko based browsers
xsldoc = document.implementation.createDocument("", "", null);
xsldoc.addEventListener("load", onXslLoad, false);
xsldoc.load( xslfile );
}
else if(window.ActiveXObject)
{
// support Windows / ActiveX
xsldoc = new ActiveXObject("Microsoft.XMLDOM");
xsldoc.ondataavailable = onXslLoad;
xsldoc.load( xslfile );
}
}
function onXslLoad()
{
// flag that the xsl is loaded
xsldocloaded = true;
//alert( "xsl loaded: " + xsldocloaded )
}
// ----- xslT to HTML functions -----------
function combine_XLM_XSLT_HTML( xlm, xsl, html, html_id )
{
var swappableSection = html.getElementById( html_id );
if (window.XSLTProcessor)
{
// support Mozilla/Gecko based browsers
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet( xsl );
var outputXHTML = xsltProcessor.transformToFragment( xlm.responseXML, html );
//alert( outputXHTML );
swappableSection.innerHTML = "";
swappableSection.appendChild( outputXHTML );
}
else if(window.ActiveXObject)
{
// support Windows/ActiveX enabled browsers
var outputXHTML = xlm.responseXML.transformNode( xsl );
//alert( outputXHTML );
swappableSection.innerHTML = outputXHTML;
}
}
// ----- show or hide a progress indicator -----
var progress = false;
var progressTimer = null;
// show a progress indicator if it takes longer...
function StartProgress()
{
//alert( "progress = " + progress );
progress = true;
if (progressTimer != null)
window.clearTimeout(progressTimer);
progressTimer = window.setTimeout(ShowProgress, 220);
} // StartProgress
// hide any progress indicator soon.
function EndProgress()
{
progress = false;
if (progressTimer != null)
window.clearTimeout(progressTimer);
progressTimer = window.setTimeout(ShowProgress, 20);
} // EndProgress
// this function is called by a timer to show or hide a progress indicator
function ShowProgress()
{
//alert( "Showprogress = " + progress );
progressTimer = null;
var a = document.getElementById("AjaxProgressIndicator");
if (progress && (a != null)) {
// just display the existing object
a.style.top = document.documentElement.scrollTop + 2 + "px";
a.style.display = "";
} else if (progress) {
// find a relative link to the ajaxcore folder containing ajax.js
var path = "/images/"
//for (var n in document.scripts) {
// s = document.scripts[n].src;
// if ((s != null) && (s.length >= 7) && (s.substr(s.length -7).toLowerCase() == "ajax.js"))
// path = s.substr(0,s.length -7);
//} // for
// create new standard progress object
a = document.createElement("div");
a.id = "AjaxProgressIndicator";
a.style.position = "absolute";
a.style.right = "2px";
a.style.top = document.documentElement.scrollTop + 2 + "px";
a.style.width = "130px";
a.style.height = "16px"
a.style.padding = "2px";
a.style.verticalAlign = "bottom";
a.style.backgroundColor="#9FCDFF";
a.innerHTML = "<img style='vertical-align:bottom' src='" + path + "ajax-loader.gif?a'>&nbsp;please wait...";
document.body.appendChild(a);
} else if (a) {
a.style.display="none";
} // if
} // ShowProgress

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Harbour uHTTPD Server post example</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>Simple uHTTPD server method POST</h1>
<br />
<br />
<form name=test action="/info" method="post">
Type something: <input type="text" name="word">
<input type="submit">
</form>
Pressing button you will redirect to /info page. Look at POST and REQUEST values.
<br />You will see a "word" variable name.
<br />
<br />Return to <a href="/">Main Page</a>
</body>
</html>

View File

@@ -0,0 +1,56 @@
<html>
<head>
<title>Simple Ajax Example</title>
<script language="Javascript">
function xmlhttpPost(strURL)
{
var xmlHttpReq = false;
var self = this;
// Mozilla/Safari
if ( window.XMLHttpRequest )
{
self.xmlHttpReq = new XMLHttpRequest();
}
// IE
else if ( window.ActiveXObject )
{
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
self.xmlHttpReq.open('POST', strURL, true);
self.xmlHttpReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
self.xmlHttpReq.onreadystatechange = function()
{
if ( self.xmlHttpReq.readyState == 4 )
{
updatepage( self.xmlHttpReq.responseText );
}
}
self.xmlHttpReq.send( getquerystring() );
}
function getquerystring()
{
var form = document.forms[ 'f1' ];
var word = form.word.value;
qstr = 'w=' + escape(word); // NOTE: no '?' before querystring
return qstr;
}
function updatepage( str )
{
document.getElementById( "result" ).innerHTML = str;
}
</script>
</head>
<body>
This is a simple ajax test. Please type a string in input field and press GO button.
<form name="f1">
<p>word: <input name="word" type="text">
<input value="Go" type="button" onclick='JavaScript:xmlhttpPost("/cgi-bin/testajax.hrb")'></p>
<div id="result"></div>
</form>
Return to <a href="/">Main Page</a>
</body>
</html>

View File

@@ -0,0 +1,90 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Part 4 Example</title>
<meta http-equiv="Pragma" content="no-cache">
<link rel="stylesheet" type="text/css" href="/css/base.css" />
<script type="text/javascript" src="/js/ajax.js"></script>
<script type="text/javascript">
// <![CDATA[
var divpart;
/**
* Requests table data for a specific page.
*
* @param pageNum the page number to request data for
*/
function getTableData(pageNum)
{
xslGet( "/xsl/based.xsl" );
xmlGet( '/cgi-bin/tableservletdb.hrb?page=' + pageNum + "&sid=" + Math.random(), tableResponseHandler);
divpart = 'tableSection';
}
function getTablePages()
{
xslGet( "/xsl/basep.xsl" );
xmlGet( '/cgi-bin/tableservletdb.hrb?count=true' + "&sid=" + Math.random(), tableResponseHandler);
divpart = 'pageSection';
}
/**
* Handler for server's response to table requests.
* Table content is pulled from response XML and a HTML
* table is built. The table is then inserted into the
* 'tableSection' DIV.
*/
function tableResponseHandler()
{
// Make sure the request is loaded (readyState = 4)
if (req.readyState == 4)
{
// Make sure the status is "OK"
if (req.status == 200)
{
// shutdown Ajax loading progress
EndProgress();
// Make sure the XSL document is loaded
if (!xsldocloaded)
{
alert('Unable to transform data. XSL is not yet loaded.');
// break out of the function
return;
}
// transform
//document.write( xsldoc );
combine_XLM_XSLT_HTML( req, xsldoc, document, divpart );
xsldocloaded = null;
xsldoc = null;
}
else
{
alert("There was a problem retrieving the XML data:\n" +
req.statusText);
}
}
}
// ]]>
</script>
</head>
<body onload="getTablePages()">
<big>Simple XML servlet</big>
<br />
<br />Tested with IE6+, Firefox 2+ and Opera 9+. Not working with Google Chrome, Safari.
<br />Please note that tableservletdb.prg simulates a slow reply of 0.5 seconds.
If you want to check real speed please comment #define SIMULATE_SLOW_REPLY in source.
<br />Return to <a href="/">Main Page</a>
<br />
<br />Select a Page&nbsp;
<div style="border: 1px solid black; padding: 10px;" id="pageSection">&nbsp;</div>
<br />
<div style="border: 1px solid black; padding: 10px;" id="tableSection">&nbsp;</div>
</body>
</html>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" indent="no"/>
<xsl:variable name="numCols" select="count(table/header/cell)" />
<xsl:variable name="numToPad" select="23 - count(table/row)" />
<xsl:template match="/">
<!-- start Data Section table -->
<table cellspacing="0" cellpadding="0" class="datatable">
<tr>
<xsl:for-each select="table/header/cell">
<th><xsl:value-of select="." /></th>
</xsl:for-each>
</tr>
<xsl:if test="count(table/row) = 0">
<tr class="blank">
<td width="100%" colspan="{$numCols}" align="center" style="font-style: italic; padding:10px;">No Records Found</td>
</tr>
</xsl:if>
<xsl:for-each select="table/row">
<xsl:variable name="rowClass">
<xsl:choose>
<xsl:when test="position() mod 2">even</xsl:when>
<xsl:otherwise>odd</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr class="{$rowClass}">
<xsl:call-template name="buildCell">
<xsl:with-param name="rowNode" select="." />
</xsl:call-template>
</tr>
</xsl:for-each>
<xsl:call-template name="padding">
<xsl:with-param name="max_count" select="$numToPad"/>
<xsl:with-param name="counter" select="'0'"/>
</xsl:call-template>
</table>
<!-- end Data Section table -->
</xsl:template>
<xsl:template name="buildCell">
<xsl:param name="rowNode"/>
<xsl:for-each select="/table/header/cell">
<xsl:variable name="colName" select="@key" />
<td><xsl:value-of select="$rowNode/*[@key=$colName]" disable-output-escaping="yes"/>&#160;</td>
</xsl:for-each>
</xsl:template>
<xsl:template name="padding">
<xsl:param name="max_count"/>
<xsl:param name="counter"/>
<xsl:if test="$counter &lt; $max_count">
<tr class="blank">
<td colspan="{$numCols + 1}">&#160;</td>
</tr>
<xsl:call-template name="padding">
<xsl:with-param name="max_count" select="$max_count"/>
<xsl:with-param name="counter" select="$counter + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/">
<!-- start Data Section table -->
<table cellspacing="0" cellpadding="0" class="pagetable">
<tr>
<xsl:for-each select="pages/page">
<td>
<xsl:variable name="pagenumber"><xsl:value-of select="." /></xsl:variable>
<a class="pageSection" href="javascript: getTableData({$pagenumber});"><xsl:value-of select="$pagenumber" />
</a>
</td>
</xsl:for-each>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>

View File

@@ -0,0 +1,7 @@
#
# $Id$
#
-gh -w3
-ohome/cgi-bin/
modules/*.prg

View File

@@ -0,0 +1,111 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* uHTTPD cookie example
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://www.harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
MEMVAR _REQUEST
FUNCTION HRBMAIN()
LOCAL cHtml := ""
LOCAL cCookie := uhttpd_GetField( "mycookie" )
LOCAL cAction := uhttpd_GetField( "action" )
LOCAL oCookie
// hb_ToOutDebug( "cCookie = %s, cAction = %s\n\r", hb_ValToExp( cCookie ), cAction )
hb_default( @cCookie, "" )
hb_default( @cAction, "" )
// Sample page embedded
#pragma __cstream | cHtml += %s
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Harbour uHTTPD Server cookie example</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>Simple uHTTPD server cookie example</h1>
<br />
<br />
<form name=test action="/cgi-bin/cookie.hrb" method="post">
Type something: <input type="text" name="mycookie" value="<%COOKIE_VALUE%>">
<input type="submit">
<input type="hidden" name="action" value="gotoinfo">
</form>
Pressing button you will redirect to /info page. Look at COOKIE values.
<br />You will see a "mycookie" variable name.
<br />
<br />Return to <a href="/">Main Page</a>
</body>
</html>
#pragma __endtext
IF Empty( cAction )
// Set a simple cookie
oCookie := uhttpd_CookieNew( "localhost", "/", 1, 0 )
// cleaning previous cookie
oCookie:DeleteCookie( "mycookie" )
cHtml := StrTran( cHtml, "<%COOKIE_VALUE%>", cCookie )
ELSEIF cAction == "gotoinfo"
// Set a simple cookie
oCookie := uhttpd_CookieNew( "localhost", "/", 1, 0 )
oCookie:SetCookie( "mycookie", cCookie )
uhttpd_SetHeader( "Location", "/info" )
// uhttpd_Write( "cookie set <a href='/info'>Go to info page</a>" )
RETURN NIL
ENDIF
RETURN cHtml

View File

@@ -0,0 +1,142 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* uHTTPD info page
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://www.harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
MEMVAR _SERVER, _REQUEST, _GET, _POST, _COOKIE, _SESSION, _HTTP_REQUEST, _HTTP_RESPONSE
FUNCTION HRBMAIN()
LOCAL cHtml
cHtml := ShowServerInfo()
RETURN cHtml
STATIC FUNCTION ShowServerInfo()
LOCAL cHtml := ""
// LOCAL oCookie
cHtml += "<big>Server Info</big>"
// cHtml += "<br /><br />If it is first time you see this page reload it to see cookies<br /><br />"
cHtml += '<br /><br />Return to <a href="/">Main Page</a><br /><br />'
cHtml += DisplayVars( _Server , "SERVER Vars" )
cHtml += "<br />"
cHtml += DisplayVars( _HTTP_REQUEST , "HTTP Request Headers" )
cHtml += "<br />"
cHtml += DisplayVars( _HTTP_RESPONSE, "HTTP Response Headers" )
cHtml += "<br />"
cHtml += DisplayVars( _Get , "GET Vars" )
cHtml += "<br />"
cHtml += DisplayVars( _Post , "POST Vars" )
cHtml += "<br />"
cHtml += DisplayVars( _Cookie , "COOKIE Vars" )
cHtml += "<br />"
// cHtml += DisplayVars( _Files , "FILE Vars" )
// cHtml += "<br />"
cHtml += DisplayVars( _Request , "REQUEST Vars" )
cHtml += "<br />"
cHtml += DisplayVars( _Session , "SESSION Vars" )
cHtml += "<br />"
// Set a simple cookie
// oCookie := uhttpd_CookieNew( "localhost", "/", 1, 0 )
// oCookie:SetCookie( "samplecookie", "test" )
// oCookie:SetCookie( "samplecookie2", "test2" )
_SESSION[ "Session_Var1" ] := "Test1"
_SESSION[ "Session_Var2" ] := "Test2"
RETURN cHtml
STATIC FUNCTION DisplayVars( hHash, cTitle )
LOCAL cHtml := ""
cHtml += "<table width='90%' align='center' border='1'>"
cHtml += "<th colspan=2>" + hb_CStr( cTitle ) + "</th>"
cHtml += "<tr>"
cHtml += "<th width='20%'>KEY</th>"
cHtml += "<th width='80%'>VALUE</th>"
cHtml += "</tr>"
cHtml += DisplayHash( hHash )
cHtml += "</table>"
RETURN cHtml
STATIC FUNCTION DisplayHash( hHash )
LOCAL cHtml := ""
LOCAL cKey, cSubKey, xValue
FOR EACH cKey IN hHash:Keys
cHtml += "<tr>"
IF HB_ISHASH( hHash[ cKey ] )
cHtml += "<td>" + hb_CStr( cKey ) + "</td>"
cHtml += "<td>-------</td>"
FOR EACH cSubKey IN hHash[ cKey ]:Keys
xValue := hHash[ cKey ][ cSubKey ]
cHtml += "<tr>"
cHtml += "<td>" + hb_CStr( cSubKey ) + "</td>"
cHtml += "<td>" + iif( Empty( xValue ), "<i>no value</i>", hb_CStr( xValue ) ) + "</td>"
cHtml += "</tr>"
NEXT
ELSE
xValue := hHash[ cKey ]
cHtml += "<td>" + hb_CStr( cKey ) + "</td>"
cHtml += "<td>" + iif( Empty( xValue ), "<i>no value</i>", hb_CStr( xValue ) ) + "</td>"
ENDIF
cHtml += "</tr>"
NEXT
RETURN cHtml

View File

@@ -0,0 +1,199 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* simple image counter
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://www.harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#if defined( HBMK_HAS_HBGD )
MEMVAR _SERVER // defined in uHTTPD
MEMVAR _REQUEST // defined in uHTTPD
#define IMAGES_IN ".." + hb_ps() + ".." + hb_ps() + ".." + hb_ps() + "contrib" + hb_ps() + "hbgd" + hb_ps() + "tests" + hb_ps() + "digits" + hb_ps()
#define IMAGES_OUT ( _SERVER[ "DOCUMENT_ROOT" ] + hb_ps() + "counter" + hb_ps() )
#define DISPLAY_NUM 10
FUNCTION HRBMAIN()
LOCAL cHtml
IF hb_HHasKey( _REQUEST, "w" )
cHtml := CreateCounter( hb_ntos( Val( _REQUEST[ "w" ] ) ) )
IF ! Empty( cHtml )
uhttpd_SetHeader( "Content-Type", "image/gif" )
uhttpd_SetHeader( "Pragma", "no-cache" )
uhttpd_SetHeader( "Content-Disposition", "inline; filename=counter" + hb_ntos( hb_RandomInt( 100 ) ) + ".gif" )
uhttpd_Write( cHtml )
ELSE
uhttpd_SetHeader( "Content-Type", "text/html" )
uhttpd_Write( "<h1>Error: No image created</h1>" )
ENDIF
ELSE
uhttpd_SetHeader( "Content-Type", "text/html" )
uhttpd_Write( "<h1>Error: no parameters passed</h1>" )
ENDIF
RETURN .T.
STATIC FUNCTION CreateCounter( cValue, cBaseImage )
LOCAL oI, oIDigits, nWidth, nHeight, nDigits, nNumWidth, oTemp
#if 0
LOCAL black, white, blue, red, green, cyan, gray
LOCAL white
#endif
LOCAL aNumberImages := {}
LOCAL n, nValue
// A value if not passed
hb_default( @cValue, Str( hb_RandomInt( 1, 10 ^ DISPLAY_NUM ), DISPLAY_NUM ) )
hb_default( @cBaseImage, "57chevy.gif" )
IF ! hb_FileExists( IMAGES_IN + cBaseImage )
RETURN NIL
ENDIF
nValue := Val( cValue )
// Fix num lenght
IF nValue > 10 ^ DISPLAY_NUM
nValue := 10 ^ DISPLAY_NUM
ENDIF
cValue := StrZero( nValue, DISPLAY_NUM )
#if 0
? "Value = ", cValue
// Check output directory
IF ! hb_DirExists( IMAGES_OUT )
DirMake( IMAGES_OUT )
ENDIF
#endif
/* Load a digits image in memory from file */
oIDigits := GDImage():LoadFromGif( IMAGES_IN + cBaseImage )
/* Get single number images */
// Get dimensions
nWidth := oIDigits:Width()
nHeight := oIDigits:Height()
// Check base digits image
DO CASE
CASE nWidth % 10 == 0 // 0..9 digits
nDigits := 10
CASE nWidth % 11 == 0 // 0..9 :
nDigits := 11
CASE nWidth % 13 == 0 // 0..9 : am pm
nDigits := 13
OTHERWISE
uhttpd_Write( "Error on digits image" )
ENDCASE
nNumWidth := nWidth / nDigits
#if 0
? "nNumWidth, nWidth, nHeight, nDigits = ", nNumWidth, nWidth, nHeight, nDigits
#endif
/* extracts single digits */
FOR n := 1 TO nDigits
oTemp := oIDigits:Copy( ( n - 1 ) * nNumWidth, 0, nNumWidth, nHeight )
// Here I have to clone the image, otherwise on var destruction I loose
// the image in memory
AAdd( aNumberImages, oTemp:Clone() )
NEXT
/* Create counter image in memory */
oI := GDImage():New( nNumWidth * DISPLAY_NUM, nHeight ) // the counter
#if 0
? "Image dimensions: ", oI:Width(), oI:Height()
/* Allocate background */
white := oI:SetColor( 255, 255, 255 )
/* Allocate drawing color */
black := oI:SetColor( 0, 0, 0 )
blue := oI:SetColor( 0, 0, 255 )
red := oI:SetColor( 255, 0, 0 )
green := oI:SetColor( 0, 255, 0 )
cyan := oI:SetColor( 0, 255, 255 )
/* Draw rectangle */
oI:Rectangle( 0, 0, 200, 30,, blue )
#endif
/* Draw Digits */
FOR n := 1 TO Len( cValue )
// Retrieve the number from array in memory
oTemp := aNumberImages[ Val( SubStr( cValue, n, 1 ) ) + 1 ]:Clone()
// Save it to show the number for a position
// oTemp:SaveGif( IMAGES_OUT + "Pos_" + StrZero( n, 2 ) + ".gif" )
// Set the digit as tile that I have to use to fill position in counter
oI:SetTile( oTemp )
// Fill the position with the image digit
oI:Rectangle( ( n - 1 ) * nNumWidth, 0, ( n - 1 ) * nNumWidth + nNumWidth, nHeight, .T., gdTiled )
NEXT
#if 0
/* Write Final Counter Image */
oI:SaveGif( IMAGES_OUT + "counter" + StrZero( hb_RandomInt( 1, 99 ), 2 ) + ".gif" )
?
? "Look at " + IMAGES_OUT + " folder for output images"
?
#endif
RETURN oI:ToStringGif()
#endif

View File

@@ -0,0 +1,395 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* xml table servlet
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://www.harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbclass.ch"
#define TABLE_NAME_PATH ".." + hb_ps() + ".." + hb_ps() + ".." + hb_ps() + "tests" + hb_ps() + "test.dbf"
#define SIMULATE_SLOW_REPLY
MEMVAR _REQUEST // defined in uHTTPD
FUNCTION HRBMAIN()
LOCAL cXml, cPage, cCount, nCount
LOCAL oTM
LOCAL hGets := _REQUEST
hb_default( @hGets, { => } )
IF hb_HHasKey( hGets, "page" )
cPage := hGets[ "page" ]
oTM := TableManager():New()
IF oTM:Open()
oTM:Read()
cXml := oTM:getXmlData( Val( cPage ) )
oTM:Close()
ENDIF
ELSEIF hb_HHasKey( hGets, "count" )
cCount := hGets[ "count" ]
IF cCount == "true"
oTM := TableManager():New()
IF oTM:Open()
nCount := oTM:getLastRec()
cXml := oTM:getXmlCount( nCount )
oTM:Close()
ENDIF
ENDIF
ENDIF
IF ! Empty( cXml )
uhttpd_SetHeader( "Content-Type", "text/xml" )
// cache control
uhttpd_SetHeader( "Cache-Control", "no-cache, must-revalidate" )
uhttpd_SetHeader( "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" )
uhttpd_Write( cXml )
ELSE
uhttpd_SetHeader( "Content-Type", "text/xml" )
uhttpd_Write( '<?xml version="1.0" encoding="ISO-8859-1"?>' )
uhttpd_Write( "<pages><page>No Data</page></pages>" )
ENDIF
RETURN .T. // I Handle HTML Output
/*
TableManager
*/
CREATE CLASS TableManager
CLASS VAR ROWS_PER_PAGE INIT 23
VAR aData INIT {}
VAR cTable INIT TABLE_NAME_PATH
VAR lOpened INIT .F.
METHOD New()
METHOD Open()
METHOD Close() INLINE iif( ::lOpened, ( table->( dbCloseArea() ), ::lOpened := .F. ), )
METHOD Read()
METHOD getLastRec() INLINE table->( LastRec() )
METHOD getXmlData( page )
METHOD getXmlCount( ncount )
METHOD xmlEncode( input )
ENDCLASS
METHOD New() CLASS TableManager
RETURN Self
METHOD Open() CLASS TableManager
LOCAL cDBF := ::cTable
// hb_ToOutDebug( "CurPath = %s", hb_CurDrive() + hb_osDriveSeparator() + hb_ps() + CurDir() )
// hb_ToOutDebug( "before: cDBF = %s, Used() = %s\n", cDBF, Used() )
IF ! ::lOpened
CLOSE ALL
USE ( cDBF ) ALIAS table SHARED NEW
// hb_ToOutDebug( "after: cDBF = %s, Used() = %s\n", cDBF, Used() )
::lOpened := Used()
ENDIF
RETURN ::lOpened
METHOD Read() CLASS TableManager
LOCAL hMap, lOk := .F.
#ifdef SIMULATE_SLOW_REPLY
// force slow connection to simulate long reply
hb_idleSleep( 0.5 )
#endif
IF ::lOpened
table->( dbGoTop() )
// n := 0
DO WHILE table->( ! Eof() ) // .AND. ++n < 50
hMap := { => }
hMap[ "recno" ] := StrZero( table->( RecNo() ), 4 )
hMap[ "name" ] := RTrim( table->first ) + " " + RTrim( table->last )
hMap[ "address" ] := RTrim( table->street )
hMap[ "city" ] := RTrim( table->city )
hMap[ "state" ] := table->state
hMap[ "zip" ] := table->zip
AAdd( ::aData, hMap )
table->( dbSkip() )
ENDDO
lOk := .T.
ENDIF
RETURN lOK
/**
* Builds a <code>String</code> of XML representing the aData for the
* request table.
*
* For simplicity, we are using a hard-coded data set. In a production
* system, you may wish to use DAOs to query a database for specific table
* data. This may require additional parameters (e.g., the name of the
* table, which could be used to look up instructions on retrieving the
* necessary data).
*
* The returned XML will be formatted as follows:
* &lt;table&gt;<br />
* &lt;header&gt;<br />
* &lt;cell key="address"&gt;Address&lt;/cell&gt;<br />
* &lt;/header&gt;<br />
* &lt;row&gt;<br />
* &lt;cell key="name"&gt;Hank&lt;/cell&gt;<br />
* &lt;cell key="address"&gt;1B Something Street&lt;/cell&gt;<br />
* &lt;cell key="city"&gt;Marietta&lt;/cell&gt;<br />
* &lt;cell key="state"&gt;GA&lt;/cell&gt;<br />
* &lt;cell key="zip"&gt;30339&lt;/cell&gt;<br />
* &lt;/row&gt;<br />
* ...<br />
* &lt;/table&gt;
*
* @param page
* the page number to retrieve data for
* @return a <code>String</code> of XML representing data for the
* requested table
* @throws IllegalArgumentException
*/
METHOD getXmlData( page ) CLASS TableManager
LOCAL startIndex, stopIndex
LOCAL xml, i, map, key, cString
/*
* For simplicity, we are creating XML as a String. In a production
* system, you should create an XML document (org.w3c.dom.Document) to
* ensure compliance with the DOM Level 2 Core Specification.
*/
// Calculate the start and end indexes of the table data.
startIndex := ( page - 1 ) * ::ROWS_PER_PAGE
stopIndex := startIndex + ::ROWS_PER_PAGE
stopIndex := Min( Len( ::aData ), stopIndex )
// Check the validity of the page index.
IF startIndex < 0 .OR. startIndex >= stopIndex
// throw new IllegalArgumentException("Page index is out of bounds.");
ENDIF
xml := BasicXML():New()
xml:append( '<?xml version="1.0" encoding="ISO-8859-1"?>' )
// Add the opening <table> tag
xml:append( "<table>" )
// Add nodes describing the table columns
xml:append( "<header>" )
xml:append( '<cell key="recno">RecNo</cell>' )
xml:append( '<cell key="name">Name</cell>' )
xml:append( '<cell key="address">Address</cell>' )
xml:append( '<cell key="city">City</cell>' )
xml:append( '<cell key="state">State</cell>' )
xml:append( '<cell key="zip">Zip</cell>' )
xml:append( "</header>" )
// Add nodes for each row.
FOR i := startIndex + 1 TO stopIndex
map := ::aData[ i ]
// Add the opening <row> tag
xml:append( "<row>" )
// For each entry in the HashMap, add a node
// e.g., <address>123 four street</address>
FOR EACH KEY IN map:Keys
cString := '<cell key="' + key + '">'
cString += ::xmlEncode( hb_CStr( map[ key ] ) )
cString += "</cell>"
xml:append( cString )
NEXT
// Add the closing </row> tag
xml:append( "</row>" )
NEXT
// Add the closing </table> tag
xml:append( "</table>" )
RETURN xml:toString()
METHOD getXmlCount( nCount ) CLASS TableManager
LOCAL xml, n
LOCAL nPages := nCount / ::ROWS_PER_PAGE
IF Int( nPages ) < nPages
nPages ++
ENDIF
xml := BasicXML():New()
xml:append( '<?xml version="1.0" encoding="ISO-8859-1"?>' )
xml:append( "<pages>" )
FOR n := 1 TO nPages
xml:append( "<page>" + hb_ntos( n ) + "</page>" )
NEXT
xml:append( "</pages>" )
RETURN xml:toString()
/**
* Replaces characters commonly used in XML with symbolic representations
* such that they are interpretted correctly by XML parsers.
*
* @param input
* the string to encode.
* @return the encoded version of the specified string
*/
METHOD xmlEncode( input ) CLASS TableManager
LOCAL out, i, c
IF input == NIL
RETURN INPUT
ENDIF
// Go through the input string and replace the following
// characters:
// & &amp;
// ' &apos;
// " &quot;
// < &lt;
// > &gt;
// [any non-ascii character] &#[character code];
out := ""
FOR i := 1 TO Len( input )
c := SubStr( input, i, 1 )
SWITCH c
CASE "&"
out += "&amp;"
EXIT
CASE "'"
out += "&apos;"
EXIT
CASE '"'
out += "&quot;"
EXIT
CASE "<"
out += "&lt;"
EXIT
CASE ">"
out += "&gt;"
EXIT
#if 0
CASE " "
out += "&nbsp;"
EXIT
#endif
CASE Chr( 9 ) // E'\t'
CASE Chr( 13 ) // E'\r'
CASE Chr( 10 ) // E'\n'
out += c
EXIT
OTHERWISE
// All non-ascii
IF Asc( c ) <= 0x1F .OR. Asc( c ) >= 0x80
out += "&#x" + hb_NumToHex( Asc( c ) ) + ";"
ELSE
out += c
ENDIF
EXIT
ENDSWITCH
NEXT
RETURN out
CREATE CLASS BasicXML
VAR aData INIT {}
METHOD New() CONSTRUCTOR
METHOD append( cString ) INLINE AAdd( ::aData, cString )
METHOD ToString()
ENDCLASS
METHOD New() CLASS BasicXML
RETURN Self
METHOD ToString() CLASS BasicXML
LOCAL s := ""
AEval( ::aData, {| c | s += c + iif( Right( c, 1 ) == ">", hb_eol(), "" ) } )
RETURN s

View File

@@ -0,0 +1,66 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* simple ajax responder
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://www.harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
MEMVAR _REQUEST
FUNCTION HRBMAIN()
LOCAL cW
LOCAL cHtml := ""
IF hb_HHasKey( _REQUEST, "w" )
IF ! Empty( cW := _REQUEST[ "w" ] )
cHtml += "This is a reply from testajax : " + cW
ENDIF
ENDIF
RETURN cHtml

View File

@@ -0,0 +1,7 @@
#
# $Id$
#
@modules.hbp
hbgd.hbc

19
extras/httpsrv/readme.txt Normal file
View File

@@ -0,0 +1,19 @@
/*
* $Id$
*/
uHTTPD micro web server
Build it without GD: hbmk2 uhttpd.hbp modules.hbp
Build it with GD: hbmk2 uhttpdgd.hbp modulesg.hbp
[ This one needs GD lib. Please download it from:
http://www.libgd.org/ ]
To see accepted parameters run: uhttpd -?
Parameters can also be defined using uhttpd.ini file.
Once started connect to uhttpd using:
http://localhost:8082
to see default index page.
Francesco

890
extras/httpsrv/session.prg Normal file
View File

@@ -0,0 +1,890 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* uHTTPD (Micro HTTP server) session functions
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* www - http://harbour-project.org
*
* This program 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 2, or (at your option)
* any later version.
*
* This program 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 this software; see the file COPYING.txt. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbclass.ch"
#include "fileio.ch"
#include "directry.ch"
#xtranslate SetNewValueReturnOld( <p>, <v> ) => LOCAL xOld, xOld := <p>, iif( <v> != NIL, <p> := <v>, ), xOld
#xtranslate Default( <p>, <v> ) => ( <p> := iif( <p> == NIL, <v>, <p> ) )
MEMVAR _COOKIE, _SESSION, _REQUEST, _SERVER
#define MY_CRCKEY "UhTTpK3y76"
FUNCTION uhttpd_SessionNew( cSessionName, cSessionPath )
RETURN uhttpd_Session():New( cSessionName, cSessionPath )
CLASS uhttpd_Session
METHOD New( cSessionName, cSessionPath )
DESTRUCTOR DestroyObject()
METHOD Start( cSID )
METHOD IsRegistered()
METHOD CacheExpire( nTimeInMinutes ) INLINE SetNewValueReturnOld( ::nCache_Expire, nTimeInMinutes )
METHOD CacheLimiter( cNewLimiter )
METHOD GetCookieParams() INLINE { ::nCookie_LifeTime, ::cCookie_Path, ::cCookie_Domain, ::lCookie_Secure }
METHOD SetCookieParams( nLifeTime, cPath, cDomain, lSecure )
METHOD ID( cID ) INLINE SetNewValueReturnOld( ::cSID, cID )
METHOD Name( cName ) INLINE SetNewValueReturnOld( ::cName, cName )
METHOD RegenerateID()
METHOD SavePath( cPath ) INLINE SetNewValueReturnOld( ::cSavePath, cPath )
METHOD IsStarted() INLINE ( ::nActiveSessions > 0 )
METHOD UseOnlyCookies() INLINE ::lUse_Only_Cookies
METHOD UseTransSID() INLINE ::lUse_Only_Cookies
METHOD SaveCookie()
METHOD GetSessionVars( aHashVars, cFields, cSeparator )
METHOD GetVar( cVar ) INLINE uhttpd_HGetValue( _SESSION, cVar )
METHOD SetVar( cVar, xValue ) INLINE _SESSION[ cVar ] := xValue
METHOD SetSaveHandler( bOpen, bClose, bRead, bWrite, bDestroy, bGC )
METHOD Open( cPath, cName )
METHOD Close()
METHOD Read( cID )
METHOD Write( cID, cData )
METHOD Destroy()
METHOD GC( nMaxLifeTime )
METHOD SessionContainer( hHash ) INLINE SetNewValueReturnOld( _SESSION, hHash )
METHOD Encode()
METHOD Decode( cData )
HIDDEN:
VAR oCookie
VAR cSID
VAR cSavePath INIT "/tmp"
VAR cName // INIT "SESSIONID"
VAR lAuto_Start INIT .F. // .F. = no autostart
VAR nGc_Probability INIT 33 // Every 1/3 of checks i'll lunch Session GC
VAR nGc_MaxLifeTime INIT 1440 // seconds - Number of seconds after gc can delete a session
// VAR cSerialize_Handler INIT "HBHTMLLIB"
VAR nCookie_LifeTime INIT 3600 // 0 // Number of seconds to keep cookie, 0 = until browser is closed
VAR cCookie_Path INIT "/"
VAR cCookie_Domain
VAR lCookie_Secure INIT .F.
VAR lUse_Cookies INIT .T. // .T. = Use cookies to store session id on client side
VAR lUse_Only_Cookies INIT .F.
VAR cReferrer_Check // If is set check if referrer is equal to, if it isn't block
// VAR cEntropy_File
// VAR nEntropy_Lenght
VAR cCache_Limiter INIT "nocache" // Possible values are: none, nocache, private, private_no_expire, public
VAR nCache_Expire INIT 180 // in minutes, not checked if cCache_Limiter == none or nocache
VAR lUse_Trans_SID INIT .F. // .F. = no SID appended to URL
// Session Storage code blocks
VAR bOpen // INIT {| cPath, cName | ::SessionOpen( cPath, cName ) }
VAR bClose // INIT {|| ::SessionClose() }
VAR bRead // INIT {| cID | ::SessionRead( cID ) }
VAR bWrite // INIT {| cID, cData | ::SessionWrite( cID, cData ) }
VAR bDestroy // INIT {| cID | ::SessionDestroy( cID ) }
VAR bGC // INIT {| nMaxLifeTime | ::SessionGC( nMaxLifeTime ) }
VAR nFileRetry INIT 10 // How many time try to open / write / delete file in case of error
VAR nFileWait INIT 500 // How many milliseconds have to wait before retry
VAR nActiveSessions INIT 0
VAR lSessionActive INIT .F.
METHOD GenerateSID( cCRCKey )
METHOD CheckSID( cSID, cCRCKey )
METHOD SessionOpen( cPath, cName )
METHOD SessionClose()
METHOD SessionRead( cID )
METHOD SessionWrite( cID, cData )
METHOD SessionDestroy( cID )
METHOD SessionGC( nMaxLifeTime )
METHOD SendCacheLimiter()
ENDCLASS
// ------------------------------
METHOD New( cSessionName, cSessionPath ) CLASS uhttpd_Session
// hb_ToOutDebug( "cSessionName = %s, cSessionPath = %s\n\r", cSessionName, cSessionPath )
__defaultNIL( @cSessionName, "SESSION" )
__defaultNIL( @cSessionPath, ::cSavePath )
// ::cSID := ::GenerateSID()
// As default we will use FILES - this is FILE version
::bOpen := {| cPath, cName | ::SessionOpen( cPath, cName ) }
::bClose := {|| ::SessionClose() }
::bRead := {| cID | ::SessionRead( cID ) }
::bWrite := {| cID, cData | ::SessionWrite( cID, cData ) }
::bDestroy := {| cID | ::SessionDestroy( cID ) }
::bGC := {| nMaxLifeTime | ::SessionGC( nMaxLifeTime ) }
#if 0
// DBF version - we will store in a DBF - this only an example
::bOpen := {| cPath, cName | DBF_Session_Open( cPath, cName ) }
::bClose := {|| DBF_Session_Close() }
::bRead := {| cID | DBF_Session_Read( cID ) }
::bWrite := {| cID, cData | DBF_Session_Write( cID, cData ) }
::bDestroy := {| cID | DBF_Session_Destroy( cID ) }
::bGC := {| nMaxLifeTime | DBF_Session_GC( nMaxLifeTime ) }
#endif
::cName := cSessionName + "ID"
::cReferrer_Check := _SERVER[ "HTTP_REFERER" ]
::cSavePath := cSessionPath
::oCookie := uhttpd_CookieNew( ::cCookie_Domain, ::cCookie_Path )
RETURN Self
METHOD Start( cSID ) CLASS uhttpd_Session
LOCAL lSendCookie := .T.
LOCAL lDefine_SID := .T.
LOCAL xVal, nRand, nPos
LOCAL hUrl
IF cSID != NIL
::cSID := cSID
ENDIF
// hb_ToOutDebug( "cSID = %s, ::cSID = %s\n\r", cSID, ::cSID )
// TraceLog( "Active Sessions : " + hb_CStr( ::nActiveSessions ) )
IF ::nActiveSessions != 0
RETURN .F.
ENDIF
// Start checking ID from global vars
IF ( nPos := hb_HPos( _REQUEST, ::cName ) ) > 0
// ::cSID := ::oCGI:h_Request[ ::cName ]
::cSID := hb_HValueAt( _REQUEST, nPos )
IF HB_ISARRAY( ::cSID )
::cSID := ::cSID[ 1 ] // Get Only 1-st
ENDIF
lSendCookie := .F.
lDefine_SID := .F.
// ::oCGI:ToLogFile( "::cSID = " + hb_CStr( ::cSID ), "/pointtoit/tmp/log.txt" )
ENDIF
IF ! Empty( ::cSID ) .AND. ! ::CheckSID()
// Check if the SID is NOT valid, someone altered it
// ::oCGI:ToLogFile( "::cSID = " + hb_CStr( ::cSID ) + " SID is NOT valid, someone altered it", "/pointtoit/tmp/log.txt" )
::cSID := NIL // invalidate current SID, i'll generate a new one
lSendCookie := .T.
lDefine_SID := .T.
ENDIF
IF ! Empty( ::cSID ) .AND. ! Empty( ::cReferrer_Check )
// TODO: fix
// oUrl := TUrl():New( ::cReferrer_Check )
hUrl := uhttpd_SplitUrl( ::cReferrer_Check )
// hb_ToOutDebug( "hUrl = %s\n\r", hb_ValToExp( hUrl ) )
// Check whether the current request was referred to by
// an external site which invalidates the previously found ID
IF !( hUrl[ "HOST" ] == _SERVER[ "SERVER_NAME" ] )
::cSID := NIL // invalidate current SID, i'll generate a new one
lSendCookie := .T.
lDefine_SID := .T.
ENDIF
ENDIF
// Do we have an existing session ID?
IF Empty( ::cSID )
// Create new session ID
::cSID := ::GenerateSID()
ENDIF
// Is use_cookies set to false?
IF ! ::lUse_Cookies .AND. lSendCookie
lDefine_SID := .T.
lSendCookie := .F.
ENDIF
// Should we send a cookie?
IF lSendCookie
::oCookie:SetCookie( ::cName, ::cSID, ::cCookie_Domain, ::cCookie_Path, uhttpd_DateToGMT(,,, ::nCookie_LifeTime ), ::lCookie_Secure )
ENDIF
// Should we define the SID?
IF lDefine_SID
cSID := ::cName + "=" + ::cSID
_REQUEST[ ::cName ] := ::cSID
ENDIF
::nActiveSessions++
// Send caching headers
// Start session
IF ! ::Open( ::cSavePath, ::cName )
uhttpd_Die( "ERROR: Failed to open session file" )
ENDIF
// Read session data
IF !( ( xVal := ::Read( ::cSID ) ) == NIL )
// TraceLog( "Read session data - xVal", xVal )
// ::oCGI:ToLogFile( "xval = " + hb_CStr( xVal ), "/pointtoit/tmp/log.txt" )
// Decode session data
::Decode( xVal )
// ::oCGI:ToLogFile( "decoded", "/pointtoit/tmp/log.txt" )
ENDIF
// Send HTTP cache headers
::SendCacheLimiter()
// Check if we should clean up (call the garbage collection routines)
// TraceLog( "::nGc_probability = " + hb_CStr( ::nGc_probability ) )
IF ::nGc_probability > 0
nRand := hb_RandomInt( 1, 100 )
// TraceLog( "::nGc_probability - nRand = " + hb_CStr( nRand ) )
IF nRand <= ::nGc_Probability
::GC( ::nGc_MaxLifeTime )
ENDIF
ENDIF
RETURN .T.
METHOD Destroy() CLASS uhttpd_Session
IF ::nActiveSessions == 0
RETURN .F.
ENDIF
// Destroy session
IF ! Eval( ::bDestroy, ::cSID )
RETURN .F.
ENDIF
RETURN .T.
METHOD Close() CLASS uhttpd_Session
LOCAL cVal
// TraceLog( "Session Close() - oCGI:h_Session", DumpValue( oCGI:h_Session ) )
IF ::nActiveSessions == 0
RETURN .F.
ENDIF
// Encode session
cVal := ::Encode()
// Save session
IF ! ::Write( ::cSID, cVal )
uhttpd_Die( "Session could not be saved." )
ENDIF
// Close session
IF ! Eval( ::bClose )
uhttpd_Die( "Session could not be closed." )
ENDIF
::nActiveSessions--
RETURN .T.
METHOD Open( cPath, cName ) CLASS uhttpd_Session
RETURN Eval( ::bOpen, cPath, cName )
METHOD Read( cID ) CLASS uhttpd_Session
RETURN Eval( ::bRead, cID )
METHOD Write( cID, cData ) CLASS uhttpd_Session
RETURN Eval( ::bWrite, cID, cData )
METHOD GC( nMaxLifeTime ) CLASS uhttpd_Session
RETURN Eval( ::bGC, nMaxLifeTime )
METHOD IsRegistered() CLASS uhttpd_Session
LOCAL lRegistered := .F.
RETURN lRegistered
METHOD CacheLimiter( cNewLimiter ) CLASS uhttpd_Session
LOCAL cOldLimiter := ::cCache_Limiter
IF cNewLimiter != NIL
IF cNewLimiter $ "none/nocache/private/private_no_expire/public"
::cCache_Limiter := cNewLimiter
ELSE
uhttpd_Die( "ERROR: uhttpd_Session:CacheLimiter() - New Limiter is incorrect" )
ENDIF
ENDIF
RETURN cOldLimiter
METHOD SetCookieParams( nLifeTime, cPath, cDomain, lSecure ) CLASS uhttpd_Session
IF nLifeTime != NIL
::nCookie_LifeTime := nLifeTime
ENDIF
IF cPath != NIL
::cCookie_Path := cPath
ENDIF
IF cDomain != NIL
::cCookie_Domain := cDomain
ENDIF
IF lSecure != NIL
::lCookie_Secure := lSecure
ENDIF
RETURN NIL
METHOD RegenerateID() CLASS uhttpd_Session
::cSID := ::GenerateSID()
IF ::lUse_Cookies
::oCookie:SetCookie( ::cName, ::cSID, ::cCookie_Domain, ::cCookie_Path, uhttpd_DateToGMT(,,, ::nCookie_LifeTime ), ::lCookie_Secure )
ENDIF
RETURN ::cSID
METHOD SaveCookie() CLASS uhttpd_Session
LOCAL cExpires := uhttpd_DateToGMT( Date(), Time(),, ::nCookie_LifeTime )
LOCAL cKey
// oCGI:SetCookie( ::cName, ::cSID, ::cCookie_Domain, ::cCookie_Path, cExpires, ::lCookie_Secure )
FOR EACH cKey IN _SESSION:Keys
::oCookie:SetCookie( ::cName + "_" + cKey, _SESSION[ cKey ], ::cCookie_Domain, ::cCookie_Path, cExpires, ::lCookie_Secure )
NEXT
RETURN NIL
#if 0
METHOD ReadCookie() CLASS uhttpd_Session
oCGI:SetCookie( ::cName, ::cSID, ::cCookie_Domain, ::cCookie_Path, cExpires, ::lCookie_Secure )
RETURN NIL
#endif
METHOD GetSessionVars( aHashVars, cFields, cSeparator ) CLASS uhttpd_Session
LOCAL aNotSessionFlds := {}
LOCAL aField, cField, aFields
LOCAL cName, xValue
LOCAL cSessPrefix := ::cName + "_"
LOCAL cFieldsNotInSession := ""
LOCAL cSessVarName
__defaultNIL( @cSeparator, "&" )
aFields := hb_regexSplit( cSeparator, cFields )
FOR EACH cField in aFields
aField := hb_regexSplit( "=", cField, 2 )
IF Len( aField ) != 2
LOOP
ENDIF
cSessVarName := LTrim( aField[ 1 ] )
// cName := "_" + LTrim( aField[ 1 ] ) // ERROR ON VAR NAME WITH LEN 1. X
// cName := LTrim( aField[ 1 ] ) // ERROR ON VAR NAME WITH LEN 1. X
// TraceLog( "SESSION: cSessVarName, cSessPrefix, Left( cSessVarName, Len( cSessPrefix ) )", ;
// cSessVarName, cSessPrefix, Left( cSessVarName, Len( cSessPrefix ) ) )
IF Left( cSessVarName, Len( cSessPrefix ) ) == cSessPrefix // IF Left part of var is equal to session prefixname i.e. "SESSION"
cName := SubStr( cSessVarName, Len( cSessPrefix ) + 1 )
xValue := uhttpd_UrlDecode( aField[ 2 ] )
// TraceLog( "SESSION: cName, xValue", cName, xValue )
// TraceLog( "cName, xValue", cName, xValue )
// is it an array entry?
IF SubStr( cName, Len( cName ) - 1 ) == "[]"
cName := SubStr( cName, 1, Len( cName ) - 2 )
// aHashVars[ cName ] := { xValue }
aHashVars[ cName ] := { xValue }
// aHashVars:Keys( cName )
// __objSendMsg( aHashVars, "_" + cName, { xValue } ) // variant from Ron to handle 1 lenght name
ELSE
// aHashVars[ cName ] := xValue
aHashVars[ cName ] := xValue
// aHashVars:Keys( cName )
// __objSendMsg( aHashVars, "_" + cName, xValue ) // variant from Ron to handle 1 lenght name
ENDIF
// TraceLog( "aHashVars, cName, xValue", DumpValue( aHashVars ), cName, xValue )
ELSE
AAdd( aNotSessionFlds, aField )
ENDIF
NEXT
IF ! Empty( aNotSessionFlds )
FOR EACH aField IN aNotSessionFlds
cFieldsNotInSession += aField[ 1 ] + "=" + aField[ 2 ] + "&"
NEXT
// Delete last & char
cFieldsNotInSession := Left( cFieldsNotInSession, Len( cFieldsNotInSession ) - 1 )
ENDIF
// TraceLog( "SESSION: cFieldsNotInSession", cFieldsNotInSession )
RETURN cFieldsNotInSession
/*
* SID = 25 random chars + 5 CRC chars
*/
METHOD GenerateSID( cCRCKey ) CLASS uhttpd_Session
LOCAL cSID, nSIDCRC, cSIDCRC, n, cTemp
LOCAL nLenSID := 25
LOCAL cBaseKeys := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
LOCAL nLenKeys := Len( cBaseKeys )
LOCAL cRet
LOCAL nRand, nKey := 0
LOCAL nLenTemp
// LOCAL a := 0
// Max Lenght must to be 10
// __defaultNIL( @cCRCKey, "3InFoW4lL5" )
__defaultNIL( @cCRCKey, MY_CRCKEY )
/* Let's generate the sequence */
// cSID := Space( nLenSID )
cSID := ""
FOR n := 1 TO nLenSID - 5 // 5 = CRC Length
nRand := hb_RandomInt( 1, nLenKeys )
// cSID[ n ] := cBaseKeys[ nRand ]
cSID += SubStr( cBaseKeys, nRand, 1 )
nKey += nRand
NEXT
nSIDCRC := nKey * 51 // Max Value is 99603. a 5 chars number
cTemp := StrZero( nSIDCRC, 5 )
cSIDCRC := ""
nLenTemp := Len( cTemp )
FOR n := 1 TO nLenTemp
// cSIDCRC += cCRCKey[ Val( cTemp[ n ] ) + 1 ]
cSIDCRC += SubStr( cCRCKey, Val( SubStr( cTemp, n, 1 ) ) + 1, 1 )
// ::oCGI:ToLogFile( "cCRCKey = " + hb_CStr( SubStr( cCRCKey, Val( SubStr( cTemp, n, 1 ) ) + 1, 1 ) ), "/pointtoit/tmp/log.txt" )
NEXT
cRet := cSID + cSIDCRC
// ::oCGI:ToLogFile( "::GenerateSID() = " + hb_CStr( cSID ) + " " + hb_CStr( cSIDCRC ), "/pointtoit/tmp/log.txt" )
// TraceLog( "Generate SID: cRet, cSID, nSIDCRC, cTemp, cSIDCRC, nKey, a", cRet, cSID, nSIDCRC, cTemp, cSIDCRC, nKey, a )
RETURN cRet
METHOD CheckSID( cSID, cCRCKey ) CLASS uhttpd_Session
LOCAL nSIDCRC, cSIDCRC, n, cTemp
LOCAL nLenSID := 25
LOCAL cBaseKeys := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
LOCAL nRand, nKey := 0
LOCAL nLenTemp
LOCAL lOk
// LOCAL a := 0
__defaultNIL( @::cSID, ::RegenerateID() )
__defaultNIL( @cSID, ::cSID )
// Max Lenght must to be 10
__defaultNIL( @cCRCKey, MY_CRCKEY )
// hb_ToOutDebug( "cSID = %s, ::cSID = %s\n\r", hb_ValToExp( cSID ), hb_ValToExp( ::cSID ) )
IF ! Empty( cSID )
/* Calculate the key */
FOR n := 1 TO nLenSID - 5 // 5 = CRC Length
// nRand := At( cSID[ n ], cBaseKeys )
nRand := At( SubStr( cSID, n, 1 ), cBaseKeys )
nKey += nRand
NEXT
// Recalculate the CRC
nSIDCRC := nKey * 51 // Max Value is 99603. a 5 chars number
cTemp := StrZero( nSIDCRC, 5 )
cSIDCRC := ""
nLenTemp := Len( cTemp )
FOR n := 1 TO nLenTemp
// cSIDCRC += cCRCKey[ Val( cTemp[ n ] ) + 1 ]
cSIDCRC += SubStr( cCRCKey, Val( SubStr( cTemp, n, 1 ) ) + 1, 1 )
NEXT
lOk := ( Right( cSID, 5 ) == cSIDCRC )
// TraceLog( "Check SID: cRet, cSID, nSIDCRC, cTemp, cSIDCRC, nKey, a", cRet, cSID, nSIDCRC, cTemp, cSIDCRC, nKey, a )
// ::oCGI:ToLogFile( "::CheckSID() = " + hb_CStr( cSID ) + " " + hb_CStr( cSIDCRC ), "/pointtoit/tmp/log.txt" )
ENDIF
RETURN lOk
// ------------------------------
METHOD SetSaveHandler( bOpen, bClose, bRead, bWrite, bDestroy, bGC ) CLASS uhttpd_Session
IF bOpen != NIL
::bOpen := bOpen
ENDIF
IF bClose != NIL
::bClose := bClose
ENDIF
IF bRead != NIL
::bRead := bRead
ENDIF
IF bWrite != NIL
::bWrite := bWrite
ENDIF
IF bDestroy != NIL
::bDestroy := bDestroy
ENDIF
IF bGC != NIL
::bGC := bGC
ENDIF
RETURN NIL
METHOD SessionOpen( cPath, cName ) CLASS uhttpd_Session
// TraceLog( "SessionOpen() - cName", cName )
IF cPath != NIL
::cSavePath := cPath
ENDIF
IF cName != NIL
::cName := cName
ENDIF
RETURN .T.
METHOD SessionClose() CLASS uhttpd_Session
// TraceLog( "SessionClose()" )
// Nothing to do
RETURN .T.
METHOD SessionRead( cID ) CLASS uhttpd_Session
LOCAL nH
LOCAL cFile
LOCAL nFileSize
LOCAL cBuffer
LOCAL nRetry := 0
__defaultNIL( @cID, ::cSID )
cFile := ::cSavePath + hb_ps() + ::cName + "_" + cID
// TraceLog( "SessionRead: cFile", cFile )
IF hb_FileExists( cFile )
DO WHILE nRetry++ <= ::nFileRetry
IF ( nH := FOpen( cFile, FO_READ + FO_DENYWRITE ) ) != F_ERROR
nRetry := 0
DO WHILE nRetry++ <= ::nFileRetry
nFileSize := FSeek( nH, 0, FS_END )
FSeek( nH, 0, FS_SET )
cBuffer := Space( nFileSize )
IF FRead( nH, @cBuffer, nFileSize ) != nFileSize
// uhttpd_Die( "ERROR: On reading session file : " + cFile + ", File error : " + hb_CStr( FError() ) )
hb_idleSleep( ::nFileWait / 1000 )
LOOP
ENDIF
FClose( nH )
EXIT
ENDDO
ELSE
// uhttpd_Die( "ERROR: On opening session file : " + cFile + ", File error : " + hb_CStr( FError() ) )
hb_idleSleep( ::nFileWait / 1000 )
LOOP
ENDIF
EXIT
ENDDO
ENDIF
// TraceLog( "SessionRead() - cID, cFile, nFileSize, cBuffer", cID, cFile, nFileSize, cBuffer )
RETURN cBuffer
METHOD SessionWrite( cID, cData ) CLASS uhttpd_Session
LOCAL nH
LOCAL cFile
LOCAL nFileSize
LOCAL lOk := .F.
LOCAL nRetry := 0
// TraceLog( "SessionWrite() - cID, cData", cID, cData )
__defaultNIL( @cID, ::cSID )
__defaultNIL( @cData, "" )
nFileSize := Len( cData )
cFile := ::cSavePath + hb_ps() + ::cName + "_" + cID
// TraceLog( "SessionWrite() - cFile", cFile )
IF nFileSize > 0
DO WHILE nRetry++ <= ::nFileRetry
IF ( nH := hb_FCreate( cFile, FC_NORMAL, FO_READWRITE + FO_DENYWRITE ) ) != F_ERROR
IF FWrite( nH, @cData, nFileSize ) != nFileSize
uhttpd_Die( "ERROR: On writing session file : " + cFile + ", File error : " + hb_CStr( FError() ) )
ELSE
lOk := .T.
ENDIF
FClose( nH )
ELSE
// uhttpd_Die( "ERROR: On WRITING session file. I can not create session file : " + cFile + ", File error : " + hb_CStr( FError() ) )
hb_idleSleep( ::nFileWait / 1000 )
LOOP
ENDIF
EXIT
ENDDO
ELSE
// If session data is empty, I will delete the file if exist
// IF hb_FileExists( cFile )
// FErase( cFile )
// ENDIF
// Return that all is ok
lOk := .T.
ENDIF
RETURN lOk
METHOD SessionDestroy( cID ) CLASS uhttpd_Session
LOCAL cFile
LOCAL lOk
LOCAL nRetry := 0
// TraceLog( "SessionDestroy() - cID", cID )
__defaultNIL( @cID, ::cSID )
_SESSION := { => }
::oCookie:DeleteCookie( ::cName )
// TraceLog( "SessionDestroy() - cID, oCGI:h_Session", cID, DumpValue( oCGI:h_Session ) )
cFile := ::cSavePath + hb_ps() + ::cName + "_" + cID
lOk := .F.
DO WHILE nRetry++ <= ::nFileRetry
IF ( lOk := ( FErase( cFile ) == 0 ) )
EXIT
ELSE
hb_idleSleep( ::nFileWait / 1000 )
LOOP
ENDIF
ENDDO
#if 0
IF !( lOk := ( FErase( cFile ) == 0 ) )
uhttpd_Die( "ERROR: On deleting session file : " + cFile + ", File error : " + hb_CStr( FError() ) )
ELSE
#endif
IF lOk
// TraceLog( "SessionDestroy() - Sessione Eliminata - File " + cFile )
// Genero un nuovo SID
::RegenerateID()
ENDIF
RETURN lOk
METHOD SessionGC( nMaxLifeTime ) CLASS uhttpd_Session
// TraceLog( "SessionGC() - nMaxLifeTime", nMaxLifeTime )
// STATIC s_nStartTime
LOCAL nSecs
LOCAL aDir, aFile
__defaultNIL( @nMaxLifeTime, ::nGc_MaxLifeTime )
aDir := Directory( ::cSavePath + hb_ps() + ::cName + "_*.*" )
FOR EACH aFile IN aDir
nSecs := TimeDiffAsSeconds( aFile[ F_DATE ], Date(), aFile[ F_TIME ], Time() )
// TraceLog( "GC: aFile[ F_NAME ], aFile[ F_DATE ], Date(), aFile[ F_TIME ], Time(), nSecs, nMaxLifeTime", ;
// aFile[ F_NAME ], aFile[ F_DATE ], Date(), aFile[ F_TIME ], Time(), nSecs, nMaxLifeTime )
IF nSecs > nMaxLifeTime
// No error checking here, because if I cannot delete file now I will find it again on next loop
FErase( ::cSavePath + hb_ps() + aFile[ F_NAME ] )
ENDIF
NEXT
RETURN .T.
STATIC FUNCTION TimeDiffAsSeconds( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
LOCAL aRetVal
__defaultNIL( @dDateEnd, Date() )
__defaultNIL( @cTimeEnd, Time() )
aRetVal := ft_Elapsed( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
RETURN aRetVal[ 4, 2 ]
// ------------------------------
METHOD Encode() CLASS uhttpd_Session
LOCAL aSerial := {}
LOCAL cKey, xVal
IF Type( "_SESSION" ) == "H"
FOR EACH cKey IN _SESSION:Keys
xVal := _SESSION[ cKey ]
IF xVal != NIL
AAdd( aSerial, { cKey, xVal } )
ENDIF
NEXT
ENDIF
RETURN iif( ! Empty( aSerial ), hb_Serialize( aSerial ), NIL )
METHOD Decode( cData ) CLASS uhttpd_Session
LOCAL lOk := .T.
LOCAL cSerial := cData
LOCAL xVal, aElem
// LOCAL cKey
// TraceLog( "Decode - cSerial", cSerial )
// ::oCGI:ToLogFile( "Decode - cSerial = " + hb_CStr( cSerial ), "/pointtoit/tmp/log.txt" )
DO WHILE ( xVal := hb_Deserialize( @cSerial ) ) != NIL
// TraceLog( "Decode - xVal", DumpValue( xVal ) )
// ::oCGI:ToLogFile( "Decode - xVal = " + hb_CStr( xVal ) + ", ValType( xVal ) = " + ValType( xVal ), "/pointtoit/tmp/log.txt" )
SWITCH ValType( xVal )
#if 0
CASE "O"
// TraceLog( "Decode - xVal - Object", xVal )
IF xVal:className() == "TASSOCIATIVEARRAY"
// TraceLog( "Decode - xVal - Object - TAssociativeArray - Keys", xVal:Keys )
FOR EACH cKey IN xVal:Keys
// TraceLog( "Decode TassociativeArray - cKey, xVal:SendKey( cKey )", cKey, xVal:SendKey( cKey ) )
_SESSION:SendKey( cKey, xVal:SendKey( cKey ) )
NEXT
ENDIF
EXIT
#endif
CASE "A" // Le variabili sono conservate come array { VarName, Value }
// TraceLog( "Decode - xVal - Array", xVal )
// ::oCGI:ToLogFile( "Decode - xVal - Array = " + hb_CStr( xVal ) + ", Len = " + hb_CStr( Len( xVal ) ), "/pointtoit/tmp/log.txt" )
FOR EACH aElem IN xVal
// ::oCGI:ToLogFile( "Decode - aElem = " + hb_CStr( hb_ValToExp( aElem ) ), "/pointtoit/tmp/log.txt" )
_SESSION[ aElem[ 1 ] ] := aElem[ 2 ]
NEXT
EXIT
OTHERWISE
uhttpd_Die( "ERROR: On deserializing session data" )
lOk := .F.
EXIT
ENDSWITCH
ENDDO
RETURN lOk
METHOD SendCacheLimiter() CLASS uhttpd_Session
LOCAL dDate
DO CASE
CASE ::cCache_Limiter == "nocache"
// uhttpd_SetHeader( "Expires", "Thu, 19 Nov 1981 08:52:00 GMT" )
uhttpd_SetHeader( "Expires", uhttpd_DateToGMT( ,, -1, ) )
uhttpd_SetHeader( "Cache-Control", "no-cache" )
// uhttpd_SetHeader( "Cache-Control", "no-store, no-cache, must-revalidate" ) // HTTP/1.1
// uhttpd_SetHeader( "Cache-Control", "post-check=0, pre-check=0", .F. )
uhttpd_SetHeader( "Pragma", "no-cache" )
CASE ::cCache_Limiter == "private"
uhttpd_SetHeader( "Expires", "Thu, 19 Nov 1981 08:52:00 GMT" )
uhttpd_SetHeader( "Cache-Control", "private, max-age=" + hb_ntos( ::nCache_Expire * 60 ) )
IF hb_FGetDateTime( hb_argv( 0 ), @dDate )
uhttpd_SetHeader( "Last-Modified", uhttpd_DateToGMT( dDate ) )
ENDIF
CASE ::cCache_Limiter == "public"
uhttpd_SetHeader( "Expires", uhttpd_DateToGMT( ,,, ::nCache_Expire * 60 ) )
uhttpd_SetHeader( "Cache-Control", "public, max-age=" + hb_ntos( ::nCache_Expire * 60 ) )
IF hb_FGetDateTime( hb_argv( 0 ), @dDate )
uhttpd_SetHeader( "Last-Modified", uhttpd_DateToGMT( dDate ) )
ENDIF
OTHERWISE
uhttpd_Die( "ERROR: Caching method " + ::cCache_Limiter + " not implemented." )
ENDCASE
// __OutDebug( "Header cache '" + ::cCache_Limiter + "' inviato" )
RETURN NIL
PROCEDURE DestroyObject() CLASS uhttpd_Session
::Close()
// ::oCGI:ToLogFile( "Session destroyed" )
// ::oCGI := NIL
RETURN

15
extras/httpsrv/uhttpd.hbp Normal file
View File

@@ -0,0 +1,15 @@
#
# $Id$
#
-w3 -es2
-mt -gui
uhttpd.prg
cgifunc.prg
cookie.prg
session.prg
hbwin.hbc
hbnf.hbc

76
extras/httpsrv/uhttpd.ini Normal file
View File

@@ -0,0 +1,76 @@
#
# $Id$
#
# ------------------------------------
# Harbour Project source code:
# uHTTPD (Micro HTTP server) ini file
#
# Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
# www - http://harbour-project.org
# ------------------------------------
#
# uHTTPD ini file (defaults are commented)
#
# ------------------------------------
# --- server listen port
#Port = 8082
# --- console display rows
#Console-rows = 25
# --- console display cols
#Console-cols = 80
# --- application directory - macro $(APP_DIR) is application folder
#application_root = $(APP_DIR)
# --- document folder - macro $(APP_DIR) will be substitute with application_root
#document_root = $(APP_DIR)\home
# --- display folder content
#show_indexes = .f.
# --- default index files
#DirectoryIndex = index.html index.htm
[THREADS]
# --- how much a thread has to wait a connection before quit
#max_wait = 60
# --- how many threads have to run always
#start_num = 4
start_num = 10
# --- how many threads can be added to initial threads
# (over this number server replies with BUSY error)
#max_num = 20
[LOGFILES]
# --- path for access log
# $(APP_DIR) = to refer to application_root path
#access = $(APP_DIR)\logs\access.log
# --- path for error log
#error = $(APP_DIR)\logs\error.log
[SCRIPTALIASES]
# --- here put script aliases to real path
# you can use following macros:
# $(DOCROOT_DIR) = to refer to document_root path
# $(APP_DIR) = to refer to application_root path
# otherwise it will be a full filesystem path
/info = $(DOCROOT_DIR)/cgi-bin/info.hrb
/cookie = $(DOCROOT_DIR)/cgi-bin/cookie.hrb
[ALIASES]
# --- here put path aliases to real path
# you can use following macros:
# $(DOCROOT_DIR) = to refer to document_root path
# $(APP_DIR) = to refer to application_root path
# otherwise it will be a full filesystem path
# example:
#/images = $(APP_DIR)/images
# end

2889
extras/httpsrv/uhttpd.prg Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#
# $Id$
#
@uhttpd.hbp
hbgd.hbc