2009-06-15 18:47 UTC+0200 Viktor Szakats (harbour.01 syenar.hu)

* utils/hbmk2/hbmk2.prg
    - Deleted hb_DirBase() DJGPP hack after Przemek's fix.
    - Deleted gcc compiler command line hack after Przemek's fix.

  * contrib/hbxbp/xbp.ch
    + Added extra protection for Windows-only debug line.

  * contrib/hbxbp/apig.ch
    * Minor in comment.

  * contrib/rddado/adordd.prg
    * Minor formatting.
    * Changed it to use non-legacy OLE interface.
      PLEASE REVIEW & TEST.

  * contrib/hbwin/legacy.prg
    + Added support for TOleAuto():cClassName var.
    + Added RTE generation in case the OLE object cannot be created.
      PLEASE REVIEW & TEST.

  + contrib/rddado/tests/test.mdb
    + Added. It's generated by access2.prg to avoid any problems.

  * contrib/rddado/tests/access1.prg
    * Minor formatting.
    ; TOFIX: Does't work:
      ---
      Error BASE/3012  Argument error: OPEN
      Called from WIN_OLEAUTO:OPEN(0)
      Called from ADO_OPEN(0)
      Called from DBUSEAREA(0)
      Called from MAIN(11)
      ---

  - examples/uhttpd
  + examples/httpsrv
  - examples/httpsrv/uhttpd.ini
  + examples/httpsrv/httpsrv.ini
  - examples/httpsrv/uhttpdgd.hbp
  + examples/httpsrv/httpsrvg.hbp
  - examples/httpsrv/uhttpdc.c
  + examples/httpsrv/httpsrvc.c
  - examples/httpsrv/uhttpd.prg
  + examples/httpsrv/httpsrv.prg
  * examples/httpsrv/cookie.prg
  * examples/httpsrv/cgifunc.prg
  * examples/httpsrv/session.prg
  * examples/httpsrv/readme.txt
    * Renamed uhttpd to httpsrv.
      NOTE: If there are better names proposed I can rename 
            it to anything else. Mindaugas's new uhttpd will 
            be name uhttpd2 to avoid any ambiguity.
This commit is contained in:
Viktor Szakats
2009-06-15 18:02:57 +00:00
parent 69941dbb06
commit 91bd1aee41
35 changed files with 245 additions and 154 deletions

View File

@@ -0,0 +1,864 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* HTTPSRV (Micro HTTP server) cgi functions
*
* 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. 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.
*
*/
#ifdef __XHARBOUR__
#include "hbcompat.ch"
#else
//#include "xhb.ch"
#include "common.ch"
#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> ) )
#define HB_IHASH() HB_HSETCASEMATCH( {=>}, FALSE )
MEMVAR _SERVER, _GET, _POST, _COOKIE, _REQUEST, _HTTP_REQUEST
FUNCTION uhttpd_GetVars( cFields, cSeparator )
LOCAL hHashVars := hb_Hash()
LOCAL aField, cField, aFields
LOCAL cName, xValue
DEFAULT cSeparator TO "&"
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 := hb_Hash()
LOCAL nPos, cTemp, cUserNamePassword, cHostnamePort
LOCAL cProto, cHost, cPort, nPort, cUser, cPass, cPath, cQuery, cFragment
LOCAL cUri
// Prevents case matching
hb_HSetCaseMatch( hUrl, FALSE )
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
hb_hSet( hUrl, "SCHEME" , cProto )
hb_hSet( hUrl, "HOST" , cHost )
hb_hSet( hUrl, "PORT" , nPort )
hb_hSet( hUrl, "USER" , cUser )
hb_hSet( hUrl, "PASS" , cPass )
hb_hSet( hUrl, "PATH" , cPath )
hb_hSet( hUrl, "QUERY" , cQuery )
hb_hSet( hUrl, "FRAGMENT", cFragment )
hb_hSet( hUrl, "URI" , cURI )
// Prevents externals to add something else to this Hash
hb_HSetAutoAdd( hUrl, FALSE )
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 TRUE
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
DEFAULT cDelim TO ( CHR(13) + CHR(10) )
DEFAULT lRemDelim TO TRUE
DEFAULT nCount TO -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
DEFAULT lComplete TO TRUE
RETURN TIPENCODERURL_ENCODE( cString, lComplete )
#else
LOCAL cRet := "", i, nVal, cChar
DEFAULT lComplete TO TRUE
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 == '=', FALSE )
cRet += cChar
OTHERWISE
nVal := Asc( cChar )
cRet += "%" + hb_NumToHex( nVal )
ENDCASE
NEXT
RETURN cRet
#endif
/************************************************************
* 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 "Saturday, 31-Oct-03 00:00:00 GMT"
*/
FUNCTION uhttpd_DateToGMT( dDate, cTime, nDayToAdd, nSecsToAdd )
LOCAL cStr
LOCAL cOldDateFormat := Set( _SET_DATEFORMAT, "dd-mm-yy" )
LOCAL nDay, nMonth, nYear, nDoW
LOCAL aDays := { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }
LOCAL aMonths := { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }
DEFAULT dDate TO DATE()
DEFAULT cTime TO TIME()
DEFAULT nDayToAdd TO 0
DEFAULT nSecsToAdd TO 0
//Tracelog( "DateToGMT - StartingValue", dDate, cTime, nDayToAdd, nSecsToAdd )
cTime := uhttpd_AddSecondsToTime( cTime, nSecsToAdd, @nDayToAdd )
dDate += nDayToAdd
nDay := Day( dDate )
nMonth := Month( dDate )
nYear := Year( dDate)
nDoW := Dow( dDate )
cStr := aDays[ nDow ] + ", " + StrZero( nDay, 2 ) + "-" + aMonths[ nMonth ] + "-" + ;
Right( StrZero( nYear, 4 ), 2 ) + " " + cTime + " GMT"
//Tracelog( "DateToGMT", cStr )
Set( _SET_DATEFORMAT, cOldDateFormat )
RETURN cStr
/*
* 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
DEFAULT cTime TO TIME()
DEFAULT nSecsToAdd TO 0
DEFAULT nDaysAdded TO 0 // nDaysAdded can be already valued, so below i add to this value
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
DEFAULT dDateEnd TO DATE()
DEFAULT cTimeEnd TO TIME()
aRetVal := FT_ELAPSED( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
RETURN aRetVal[ 4, 2 ]
FUNCTION uhttpd_OutputString( cString, aTranslate, lProtected )
LOCAL cHtml
DEFAULT lProtected TO FALSE
DEFAULT aTranslate TO { { '"', '&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 )
DEFAULT cQuote_style TO "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 := { ; // ATTENTION, this chars are visible only with OEM font
// { ' ', '&#160;' } ,; // &nbsp; Nonbreaking space
// { '­', '&#161;' } ,; // &iexcl; Inverted exclamation
// { '½', '&#162;' } ,; // &cent; Cent sign
// { 'œ', '&#163;' } ,; // &pound; Pound sterling
// { 'Ï', '&#164;' } ,; // &curren; General currency sign
// { '¾', '&#165;' } ,; // &yen; Yen sign
// { 'Ý', '&#166;' } ,; // &brvbar; or &brkbar; Broken vertical bar
// { 'õ', '&#167;' } ,; // &sect; Section sign
// { 'ù', '&#168;' } ,; // &uml; or &die; Diresis / Umlaut
// { '¸', '&#169;' } ,; // &copy; Copyright
// { '¦', '&#170;' } ,; // &ordf; Feminine ordinal
// { '®', '&#171;' } ,; // &laquo; Left angle quote, guillemet left
// { 'ª', '&#172;' } ,; // &not Not sign
// { 'ð', '&#173;' } ,; // &shy; Soft hyphen
// { '©', '&#174;' } ,; // &reg; Registered trademark
// { 'î', '&#175;' } ,; // &macr; or &hibar; Macron accent
// { 'ø', '&#176;' } ,; // &deg; Degree sign
// { 'ñ', '&#177;' } ,; // &plusmn; Plus or minus
// { 'ý', '&#178;' } ,; // &sup2; Superscript two
// { 'ü', '&#179;' } ,; // &sup3; Superscript three
// { 'ï', '&#180;' } ,; // &acute; Acute accent
// { 'æ', '&#181;' } ,; // &micro; Micro sign
// { 'ô', '&#182;' } ,; // &para; Paragraph sign
// { 'ú', '&#183;' } ,; // &middot; Middle dot
// { '÷', '&#184;' } ,; // &cedil; Cedilla
// { 'û', '&#185;' } ,; // &sup1; Superscript one
// { '§', '&#186;' } ,; // &ordm; Masculine ordinal
// { '¯', '&#187;' } ,; // &raquo; Right angle quote, guillemet right
// { '¬', '&#188;' } ,; // &frac14; Fraction one-fourth
// { '«', '&#189;' } ,; // &frac12; Fraction one-half
// { 'ó', '&#190;' } ,; // &frac34; Fraction three-fourths
// { '¨', '&#191;' } ,; // &iquest; Inverted question mark
// { '·', '&#192;' } ,; // &Agrave; Capital A, grave accent
// { 'µ', '&#193;' } ,; // &Aacute; Capital A, acute accent
// { '¶', '&#194;' } ,; // &Acirc; Capital A, circumflex
// { 'Ç', '&#195;' } ,; // &Atilde; Capital A, tilde
// { 'Ž', '&#196;' } ,; // &Auml; Capital A, diresis / umlaut
// { '<27>', '&#197;' } ,; // &Aring; Capital A, ring
// { '', '&#198;' } ,; // &AElig; Capital AE ligature
// { '€', '&#199;' } ,; // &Ccedil; Capital C, cedilla
// { 'Ô', '&#200;' } ,; // &Egrave; Capital E, grave accent
// { '<27>', '&#201;' } ,; // &Eacute; Capital E, acute accent
// { 'Ò', '&#202;' } ,; // &Ecirc; Capital E, circumflex
// { 'Ó', '&#203;' } ,; // &Euml; Capital E, diresis / umlaut
// { 'Þ', '&#204;' } ,; // &Igrave; Capital I, grave accent
// { 'Ö', '&#205;' } ,; // &Iacute; Capital I, acute accent
// { '×', '&#206;' } ,; // &Icirc; Capital I, circumflex
// { 'Ø', '&#207;' } ,; // &Iuml; Capital I, diresis / umlaut
// { 'Ñ', '&#208;' } ,; // &ETH; Capital Eth, Icelandic
// { '¥', '&#209;' } ,; // &Ntilde; Capital N, tilde
// { 'ã', '&#210;' } ,; // &Ograve; Capital O, grave accent
// { 'à', '&#211;' } ,; // &Oacute; Capital O, acute accent
// { 'â', '&#212;' } ,; // &Ocirc; Capital O, circumflex
// { 'å', '&#213;' } ,; // &Otilde; Capital O, tilde
// { '™', '&#214;' } ,; // &Ouml; Capital O, diresis / umlaut
// { 'ž', '&#215;' } ,; // &times; Multiply sign
// { '<27>', '&#216;' } ,; // &Oslash; Capital O, slash
// { 'ë', '&#217;' } ,; // &Ugrave; Capital U, grave accent
// { 'é', '&#218;' } ,; // &Uacute; Capital U, acute accent
// { 'ê', '&#219;' } ,; // &Ucirc; Capital U, circumflex
// { 'š', '&#220;' } ,; // &Uuml; Capital U, diresis / umlaut
// { 'í', '&#221;' } ,; // &Yacute; Capital Y, acute accent
// { 'è', '&#222;' } ,; // &THORN; Capital Thorn, Icelandic
// { 'á', '&#223;' } ,; // &szlig; Small sharp s, German sz
// { '…', '&#224;' } ,; // &agrave; Small a, grave accent
// { ' ', '&#225;' } ,; // &aacute; Small a, acute accent
// { 'ƒ', '&#226;' } ,; // &acirc; Small a, circumflex
// { 'Æ', '&#227;' } ,; // &atilde; Small a, tilde
// { '„', '&#228;' } ,; // &auml; Small a, diresis / umlaut
// { '†', '&#229;' } ,; // &aring; Small a, ring
// { '', '&#230;' } ,; // &aelig; Small ae ligature
// { '‡', '&#231;' } ,; // &ccedil; Small c, cedilla
// { 'Š', '&#232;' } ,; // &egrave; Small e, grave accent
// { '', '&#233;' } ,; // &eacute; Small e, acute accent
// { 'ˆ', '&#234;' } ,; // &ecirc; Small e, circumflex
// { '‰', '&#235;' } ,; // &euml; Small e, diresis / umlaut
// { '<27>', '&#236;' } ,; // &igrave; Small i, grave accent
// { '¡', '&#237;' } ,; // &iacute; Small i, acute accent
// { 'Œ', '&#238;' } ,; // &icirc; Small i, circumflex
// { '', '&#239;' } ,; // &iuml; Small i, diresis / umlaut
// { 'Ð', '&#240;' } ,; // &eth; Small eth, Icelandic
// { '¤', '&#241;' } ,; // &ntilde; Small n, tilde
// { '•', '&#242;' } ,; // &ograve; Small o, grave accent
// { '¢', '&#243;' } ,; // &oacute; Small o, acute accent
// { '“', '&#244;' } ,; // &ocirc; Small o, circumflex
// { 'ä', '&#245;' } ,; // &otilde; Small o, tilde
// { '”', '&#246;' } ,; // &ouml; Small o, diresis / umlaut
// { 'ö', '&#247;' } ,; // &divide; Division sign
// { '', '&#248;' } ,; // &oslash; Small o, slash
// { '—', '&#249;' } ,; // &ugrave; Small u, grave accent
// { '£', '&#250;' } ,; // &uacute; Small u, acute accent
// { '', '&#251;' } ,; // &ucirc; Small u, circumflex
// { '<27>', '&#252;' } ,; // &uuml; Small u, diresis / umlaut
// { 'ì', '&#253;' } ,; // &yacute; Small y, acute accent
// { 'ç', '&#254;' } ,; // &thorn; Small thorn, Icelandic
// { '˜', '&#255;' } ; // &yuml; Small y, diresis / umlaut
// }
LOCAL aTranslations := {}
LOCAL i
FOR i := 160 TO 255
aAdd( aTranslations, { Chr( 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 )
//__OutDebug( "cError: ", cError )
//IF !oCGI:HeaderSent()
// oCGI:WriteLN( CRLF2BR( cError ), CRLF2BR( CRLF() ) )
// //oCGI:WriteLN( CRLF2BR( hb_dumpVar(TConfigure():hConfig) ) )
//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;"
/* FROM FT LIB */
STATIC FUNCTION FT_ELAPSED(dStart, dEnd, cTimeStart, cTimeEnd)
LOCAL nTotalSec, nCtr, nConstant, nTemp, aRetVal[4,2]
IF ! ( VALTYPE(dStart) $ 'DC' )
dStart := DATE()
ELSEIF VALTYPE(dStart) == 'C'
cTimeStart := dStart
dStart := DATE()
ENDIF
IF ! ( VALTYPE(dEnd) $ 'DC' )
dEnd := DATE()
ELSEIF VALTYPE(dEnd) == 'C'
cTimeEnd := dEnd
dEnd := DATE()
ENDIF
IF( VALTYPE(cTimeStart) != 'C', cTimeStart := '00:00:00', )
IF( VALTYPE(cTimeEnd) != 'C', cTimeEnd := '00:00:00', )
nTotalSec := (dEnd - dStart) * 86400 + ;
VAL(cTimeEnd) * 3600 + ;
VAL(SUBSTR(cTimeEnd,AT(':', cTimeEnd)+1,2)) * 60 + ;
IF(RAT(':', cTimeEnd) == AT(':', cTimeEnd), 0, ;
VAL(SUBSTR(cTimeEnd,RAT(':', cTimeEnd)+1))) - ;
VAL(cTimeStart) * 3600 - ;
VAL(SUBSTR(cTimeStart,AT(':', cTimeStart)+1,2)) * 60 - ;
IF(RAT(':', cTimeStart) == AT(':', cTimeStart), 0, ;
VAL(SUBSTR(cTimeStart,RAT(':', cTimeStart)+1)))
nTemp := nTotalSec
FOR nCtr = 1 to 4
nConstant := IF(nCtr == 1, 86400, IF(nCtr == 2, 3600, IF( nCtr == 3, 60, 1)))
aRetVal[nCtr,1] := INT(nTemp/nConstant)
aRetval[nCtr,2] := nTotalSec / nConstant
nTemp -= aRetVal[nCtr,1] * nConstant
NEXT
RETURN aRetVal
PROCEDURE uhttpd_WriteToLogFile( cString, cLog, lCreate )
LOCAL nHandle, cSep
cSep := HB_OsPathSeparator()
//DEFAULT cLog TO AppFullPath() + cSep + "logfile.log"
DEFAULT cLog TO cSep + "tmp" + cSep + "logfile.log"
DEFAULT lCreate TO FALSE
IF cLog <> NIL
IF !lCreate .AND. FILE( 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, 2 )
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_OsPathSeparator()
WITH OBJECT hFile
:FULLPATH := IIF( !Empty( :PATH ), IIF( !( Right( :PATH, Len( cSep ) ) == cSep ), :PATH + cSep, :PATH ), "" )
:UNC := :FULLPATH + :FULLNAME
END
RETURN hFile
FUNCTION uhttpd_AppFullPath()
LOCAL hExeFile := uhttpd_SplitFileName( HB_ARGV(0) )
LOCAL cPrgFullPath := hExeFile:FULLPATH
LOCAL cPath, cSep
cSep := HB_OsPathSeparator()
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 ValType( cExp ) != 'C'
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 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
/*
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 } ) )
*/
OTHERWISE
Throw( ErrorNew( "CSTR", 0, 3101, ProcName(), "Argument error", { cExp, cType } ) )
END
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 IN hHash:Keys, hHash[ cKey ], NIL )
RETURN xVal

View File

@@ -0,0 +1,181 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* HTTPSRV (Micro HTTP server) cookie functions
*
* 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. 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 "common.ch"
#include "hbclass.ch"
#command IF <lexpr> THEN <*statement*> =>;
IF (<lexpr>) ; <statement> ; END
#command IF <lexpr> THEN <statement1> ELSE <statement2> =>;
IF (<lexpr>) ; <statement1> ; ELSE ; <statement2> ; END
MEMVAR _COOKIE
FUNCTION uhttpd_CookieNew( cDomain, cPath, nExpireDays, nExpireSecs )
RETURN uhttpd_Cookie():New( cDomain, cPath, nExpireDays, nExpireSecs )
CLASS uhttpd_Cookie
// Data for cookies
DATA aCookies INIT {} // Using an array to mantain order
DATA cDomain
DATA cPath INIT "/"
DATA cExpire
DATA lSecure INIT FALSE
DATA lHttpOnly
DATA nExpireDays INIT 0
DATA nExpireSecs INIT 7200 // 1 hour - TODO set environment constant
DATA lCookiesSent INIT FALSE
METHOD SetCookie()
METHOD DeleteCookie()
METHOD DeleteAllCookies()
METHOD GetCookie()
METHOD IsCookie( cCookieName ) INLINE ::GetCookie( cCookieName ) != NIL
METHOD IsCookies() INLINE !Empty( ::aaCookieToSet )
METHOD SetCookieDefaults()
ENDCLASS
// ------------------------------ ***************************** -----------------------------------
METHOD SetCookieDefaults( cDomain, cPath, nExpireDays, nExpireSecs ) CLASS uhttpd_Cookie
IF cDomain <> NIL THEN ::cDomain := cDomain
IF cPath <> NIL THEN ::cPath := cPath
IF nExpireDays <> NIL THEN ::nExpireDays := nExpireDays
IF nExpireSecs <> NIL THEN ::nExpireSecs := nExpireSecs
RETURN NIL
METHOD SetCookie( cCookieName, xValue, cDomain, cPath, cExpires, lSecure, lHttpOnly ) CLASS uhttpd_Cookie
LOCAL cStr, nPos, nCookies
DEFAULT cDomain TO ::cDomain
DEFAULT cPath TO ::cPath
DEFAULT cExpires TO uhttpd_DateToGMT( Date(), Time(), ::nExpireDays, ::nExpireSecs )
DEFAULT lHttpOnly TO FALSE
::lHttpOnly := lHttpOnly
IF !Empty( xValue )
// 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 ValType( lSecure ) == "L" .AND. lSecure
cStr += "; secure"
ENDIF
// Send the header
//uhttpd_SetHeader( "Set-Cookie", cStr, FALSE )
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 TRUE
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,66 @@
body {font-family:Tahoma,Helvetica, Arial;font-size:10pt;color:black;}
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-family: Verdana;
font-size: 12px;
}
table.datatable tr th
{
border-bottom:1px solid black;
padding: 2px;
text-align: left;
}
table.datatable tr td
{
vertical-align: top;
padding: 2px;
border-bottom: 1px solid white;
}
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: 8.9 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 bldhrb.bat
<br />
(**) This example requires LIBGD
</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,75 @@
#
# $Id$
#
# ------------------------------------
# Harbour Project source code:
# HTTPSRV (Micro HTTP server) ini file
#
# Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
# www - http://www.harbour-project.org
# ------------------------------------
#
# HTTPSRV 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* HTTPSRV (Micro HTTP server) [C helper functions]
*
* Copyright 2009 Francesco Saverio Giudice <info / at / fsgiudice.com>
* Copyright 2008 Mindaugas Kavaliauskas (dbtopas at dbtopas.lt)
* www - http://www.harbour-project.org
*
* Credits:
* Based on first version posted from Mindaugas Kavaliauskas on
* developers NG on December 15th, 2008 whom give my thanks to have
* shared initial work.
* Francesco.
*
* 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. 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.
*
*/
#define HB_OS_WIN_USED
#include "hbapi.h"
#include "hbdate.h"
#if !defined( HB_OS_WIN )
#include <time.h>
#endif
#if defined( HB_OS_WIN )
BOOL win_SysRefresh( int iMsec )
{
int iQuit = ( int ) FALSE;
HANDLE hDummyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
/* Begin the operation and continue until it is complete
or until the user clicks the mouse or presses a key. */
while( MsgWaitForMultipleObjects( 1, &hDummyEvent, FALSE, ( iMsec == 0 ? INFINITE : ( ULONG ) iMsec ), QS_ALLINPUT | QS_ALLPOSTMESSAGE) == WAIT_OBJECT_0 + 1 )
{
MSG msg;
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
switch( msg.message )
{
case WM_CLOSE:
{
iQuit = 1;
goto stopLoop;
}
case WM_QUIT:
{
iQuit = ( int ) msg.wParam;
goto stopLoop;
}
#if 0
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_KEYUP:
/* Perform any required cleanup. */
break;
/* exit; */
#endif
default:
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
if( ! iQuit )
{
goto stopLoop;
}
}
stopLoop:
CloseHandle( hDummyEvent );
return iQuit;
}
HB_FUNC( WIN_SYSREFRESH )
{
hb_retni( win_SysRefresh( hb_parni( 1 ) ) );
}
#else
HB_FUNC( WIN_SYSREFRESH )
{
hb_retni( ( int ) FALSE );
}
#endif
HB_FUNC( HB_UTCOFFSET )
{
char * szRet = ( char * ) hb_xgrab( 6 );
int nLen;
#if defined(HB_OS_WIN)
{
TIME_ZONE_INFORMATION tzInfo;
if( GetTimeZoneInformation( &tzInfo ) == TIME_ZONE_ID_INVALID )
tzInfo.Bias = 0;
else
tzInfo.Bias = -tzInfo.Bias;
hb_snprintf( szRet, 6, "%+03d%02d",
( int )( tzInfo.Bias / 60 ),
( int )( tzInfo.Bias % 60 > 0 ? - tzInfo.Bias % 60 : tzInfo.Bias % 60 ) );
nLen = strlen( szRet );
}
#else
{
struct tm tmTime;
time_t current;
time( &current );
# if defined( HB_HAS_LOCALTIME_R )
localtime_r( &current, &tmTime );
# else
tmTime = *localtime( &current );
# endif
nLen = strftime( szRet, 6, "%z", &tmTime );
}
#endif
if( nLen < 6 )
szRet = ( char * ) hb_xrealloc( szRet, nLen + 1 );
hb_retclen_buffer( szRet, nLen );
}

View File

@@ -0,0 +1,12 @@
#
# $Id$
#
# httpsrv with GD support
@httpd.hbp
-DGD_SUPPORT
-lhbgd -lhbct
-lbgd{win}
-lgd{!win}
-L/opt/local/lib{darwin}

View File

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

View File

@@ -0,0 +1,116 @@
/*
* $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. 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 "common.ch"
#include "hbclass.ch"
MEMVAR _REQUEST
#xcommand TEXT INTO <v> => #pragma __cstream|<v>+=%s
//#pragma escapedstrings = on
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 )
DEFAULT cCookie TO ""
DEFAULT cAction TO ""
// Sample page embedded
TEXT INTO cHtml
<!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>
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,144 @@
/*
* $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. 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.
*
*/
/*
Show internal variables.
Call it with: /info
*/
#include "common.ch"
#include "hbclass.ch"
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,225 @@
/*
* $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. 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( GD_SUPPORT )
MEMVAR _SERVER // defined in uHTTPD
MEMVAR _REQUEST // defined in uHTTPD
#include "common.ch"
//#include "xhb.ch"
#include "gd.ch"
#ifdef __PLATFORM__UNIX
#define IMAGES_IN "../../hbgd/tests/digits/"
#define IMAGES_OUT ( _SERVER[ "DOCUMENT_ROOT" ] + "/counter/" )
#else
#define IMAGES_IN "..\..\hbgd\tests\digits\"
#define IMAGES_OUT ( _SERVER[ "DOCUMENT_ROOT" ] + "\counter\" )
#endif
#define DISPLAY_NUM 10
FUNCTION HRBMAIN()
LOCAL cHtml
//LOCAL cBaseImage
IF HB_HHasKey( _REQUEST, "w" )
cHtml := CreateCounter( AllTrim( Str( Val( _REQUEST[ "w" ] ) ) ) )
//hb_ToOutDebug( hb_sprintf( "CreateCounter = %s", cHtml ) )
IF !Empty( cHtml )
uhttpd_SetHeader( "Content-Type", "image/gif" )
uhttpd_SetHeader( "Pragma", "no-cache" )
uhttpd_SetHeader( "Content-Disposition", "inline; filename=counter" + LTrim( Str( 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 TRUE
STATIC FUNCTION CreateCounter( cValue, cBaseImage )
LOCAL oI, oIDigits, nWidth, nHeight, nDigits, nNumWidth, oTemp
//LOCAL black, white, blue, red, green, cyan, gray
//LOCAL white
LOCAL aNumberImages := {}
LOCAL n, nValue
//LOCAL cFile
// A value if not passed
DEFAULT cValue TO Str( hb_RandomInt( 1, 10^DISPLAY_NUM ), DISPLAY_NUM )
DEFAULT cBaseImage TO "57chevy.gif"
IF !File( IMAGES_IN + cBaseImage )
//hb_ToOutDebug( "ERROR: Base Image File '" + IMAGES_IN + cBaseImage + "' not found" )
//THROW( "ERROR: Base Image File '" + IMAGES_IN + cBaseImage + "' not found" )
RETURN NIL
ENDIF
nValue := Val( cValue )
// Fix num lenght
IF nValue > 10^DISPLAY_NUM
nValue := 10^DISPLAY_NUM
ENDIF
cValue := StrZero( nValue, DISPLAY_NUM )
//? "Value = ", cValue
// To set fonts run this command:
// for windows: SET GDFONTPATH=c:\windows\fonts
// per linux : export GDFONTPATH=/usr/share/fonts/default/TrueType
// SET GDFONTPATH=c:\windows\fonts
//IF GetEnv( "GDFONTPATH" ) == ""
// ? "Please set GDFONTPATH"
// ? "On Windows: SET GDFONTPATH=c:\windows\fonts"
// ? "On Linux : export GDFONTPATH=/usr/share/fonts/default/TrueType"
// ?
//ENDIF
// Check output directory
/*
IF !ISDirectory( IMAGES_OUT )
DirMake( IMAGES_OUT )
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
//? "nNumWidth, nWidth, nHeight, nDigits = ", nNumWidth, nWidth, nHeight, nDigits
/* extracts single digits */
FOR n := 1 TO nDigits
oTemp := oIDigits:Copy( (n - 1) * nNumWidth, 0, nNumWidth, nHeight )
//oTemp:SaveGif( IMAGES_OUT + StrZero( n-1, 2 ) + ".gif" )
// 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
//? "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 )
/* 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, TRUE, gdTiled )
NEXT
/* Write Final Counter Image */
//cFile := "counter" + StrZero( hb_RandomInt( 1, 99 ), 2 ) + ".gif"
//oI:SaveGif( IMAGES_OUT + cFile )
/* Destroy images in memory */
// Class does it automatically
//?
//? "Look at " + IMAGES_OUT + " folder for output images"
//?
//RETURN cFile
RETURN oI:ToStringGif()
#endif

View File

@@ -0,0 +1,404 @@
/*
* $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. 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 "common.ch"
#include "hbclass.ch"
#define CRLF ( chr(13)+chr(10) )
#ifdef __PLATFORM__WINDOWS
#define TABLE_NAME_PATH "..\..\..\..\tests\test.dbf"
#else
#define TABLE_NAME_PATH "../../../../tests/test.dbf"
#endif
#define SIMULATE_SLOW_REPLY
MEMVAR _REQUEST // defined in uHTTPD
FUNCTION HRBMAIN()
LOCAL cXml, cPage, cCount, nCount
LOCAL oTM
LOCAL hGets
hGets := _REQUEST
DEFAULT hGets TO hb_Hash()
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 TRUE // I Handle HTML Output
/*
TableManager
*/
CLASS TableManager
CLASSVAR ROWS_PER_PAGE INIT 23
VAR aData INIT {}
VAR cTable INIT TABLE_NAME_PATH
VAR lOpened INIT FALSE
METHOD New()
METHOD Open()
METHOD Close() INLINE IIF( ::lOpened, ( table->( dbCloseArea() ), ::lOpened := FALSE ), )
METHOD Read()
METHOD getLastRec() INLINE table->( LastRec() )
METHOD getXmlData()
METHOD getXmlCount()
METHOD xmlEncode( input )
ENDCLASS
METHOD New() CLASS TableManager
RETURN Self
METHOD Open() CLASS TableManager
LOCAL cDBF := ::cTable
IF !::lOpened
CLOSE ALL
USE ( cDBF ) ALIAS table SHARED NEW
//hb_ToOutDebug( "cDBF = %s, Used() = %s\n", cDBF, Used() )
::lOpened := USED()
ENDIF
RETURN ::lOpened
METHOD Read() CLASS TableManager
LOCAL hMap, lOk := FALSE
#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 := hb_Hash()
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 := TRUE
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>" + LTrim( Str( 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
//case ' '
// out += "&nbsp;"
// exit
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
end
NEXT
RETURN out
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 ) == ">", CRLF, "" ) } )
RETURN s

View File

@@ -0,0 +1,69 @@
/*
* $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. 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 "common.ch"
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,24 @@
/*
* $Id$
*/
HTTPSRV micro web server
Build it without GD: hbmk2 httpsrv.hbp
Build it with GD: hbmk2 httpsrvd.hbp
[ This one needs bgd.dll. Please download it from:
http://www.libgd.org/releases/gd-latest-win32.zip ]
Add -DUSE_HB_INET to command line if you want to use Harbour's
built-in socket functions.
To see accepted parameters run: httpsrv -?
Parameters can also be defined using httpsrv.ini file.
Before starting please build modules using: hbmk2 modules.hbp
Once started connect to httpsrv using:
http://localhost:8082
to see default index page.
Francesco

View File

@@ -0,0 +1,875 @@
/*
* $Id$
*/
/*
* Harbour Project source code:
* HTTPSRV (Micro HTTP server) session functions
*
* 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. 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.
*
*/
#ifdef __XHARBOUR__
#include "hbcompat.ch"
#else
#include "common.ch"
#include "hbclass.ch"
#include "fileio.ch"
#include "directry.ch"
#command IF <lexpr> THEN <*statement*> =>;
IF (<lexpr>) ; <statement> ; END
#command IF <lexpr> THEN <statement1> ELSE <statement2> =>;
IF (<lexpr>) ; <statement1> ; ELSE ; <statement2> ; END
#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()
DESTRUCTOR DestroyObject()
METHOD Start()
METHOD IsRegistered()
METHOD CacheExpire( nTimeInMinutes ) INLINE SetNewValueReturnOld( ::nCache_Expire, nTimeInMinutes )
METHOD CacheLimiter()
METHOD GetCookieParams() INLINE { ::nCookie_LifeTime, ::cCookie_Path, ::cCookie_Domain, ::lCookie_Secure }
METHOD SetCookieParams()
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()
METHOD GetVar( cVar ) INLINE uhttpd_HGetValue( _SESSION, cVar )
METHOD SetVar( cVar, xValue ) INLINE _SESSION[ cVar ] := xValue
METHOD SetSaveHandler()
METHOD Open( cPath, cName )
METHOD Close()
METHOD Read( cID )
METHOD Write( cID, cData )
METHOD Destroy( cID )
METHOD GC( nMaxLifeTime )
METHOD SessionContainer( hHash ) INLINE SetNewValueReturnOld( _SESSION, hHash )
METHOD Encode() // INLINE HB_Serialize( _SESSION )
METHOD Decode()
HIDDEN:
DATA oCookie
DATA cSID
DATA cSavePath INIT "/tmp"
DATA cName // INIT "SESSIONID"
DATA lAuto_Start INIT FALSE // FALSE = no autostart
DATA nGc_Probability INIT 33 // Every 1/3 of checks i'll lunch Session GC
DATA nGc_MaxLifeTime INIT 1440 // seconds - Number of seconds after gc can delete a session
// DATA cSerialize_Handler INIT "HBHTMLLIB"
DATA nCookie_LifeTime INIT 3600 //0 // Number of seconds to keep cookie, 0 = until browser is closed
DATA cCookie_Path INIT "/"
DATA cCookie_Domain
DATA lCookie_Secure INIT FALSE
DATA lUse_Cookies INIT TRUE // TRUE = Use cookies to store session id on client side
DATA lUse_Only_Cookies INIT FALSE
DATA cReferrer_Check // If is set check if referrer is equal to, if it isn't block
// DATA cEntropy_File
// DATA nEntropy_Lenght
DATA cCache_Limiter INIT "nocache" // Possible values are: none, nocache, private, private_no_expire, public
DATA nCache_Expire INIT 180 // in minutes, not checked if cCache_Limiter == none or nocache
DATA lUse_Trans_SID INIT FALSE // FALSE = no SID appended to URL
// Session Storage code blocks
DATA bOpen //INIT {|cPath, cName| ::SessionOpen( cPath, cName ) }
DATA bClose //INIT {|| ::SessionClose() }
DATA bRead //INIT {|cID| ::SessionRead( cID ) }
DATA bWrite //INIT {|cID, cData| ::SessionWrite( cID, cData ) }
DATA bDestroy //INIT {|cID| ::SessionDestroy( cID ) }
DATA bGC //INIT {|nMaxLifeTime| ::SessionGC( nMaxLifeTime ) }
DATA nFileRetry INIT 10 // How many time try to open / write / delete file in case of error
DATA nFileWait INIT 500 // How many milliseconds have to wait before retry
DATA nActiveSessions INIT 0
DATA lSessionActive INIT FALSE
METHOD GenerateSID()
METHOD CheckSID()
METHOD SessionOpen()
METHOD SessionClose()
METHOD SessionRead()
METHOD SessionWrite()
METHOD SessionDestroy()
METHOD SessionGC()
METHOD SendCacheLimiter()
ENDCLASS
// ------------------------------ ***************************** -----------------------------------
METHOD New( cSessionName, cSessionPath ) CLASS uhttpd_Session
//hb_ToOutDebug( "cSessionName = %s, cSessionPath = %s\n\r", cSessionName, cSessionPath )
DEFAULT cSessionName TO "SESSION"
DEFAULT cSessionPath TO ::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 ) }
/*
// 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 ) }
*/
::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 := TRUE
LOCAL lDefine_SID := TRUE
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 FALSE
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 := FALSE
lDefine_SID := FALSE
//::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 := TRUE
lDefine_SID := TRUE
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 ) )
//IF !( oUrl:cServer == _SERVER[ "SERVER_NAME" ] )
IF !( hUrl[ "HOST" ] == _SERVER[ "SERVER_NAME" ] )
::cSID := NIL // invalidate current SID, i'll generate a new one
lSendCookie := TRUE
lDefine_SID := TRUE
ENDIF
// // Check whether the current request was referred to by
// // an external site which invalidates the previously found ID
// $url = parse_url($GLOBALS['HTTP_REFERER']);
// if (trim($url['host']) != $GLOBALS['SERVER_NAME']) {
// unset($session->id);
// $send_cookie = true;
// $define_sid = true;
// }
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 := TRUE
lSendCookie := FALSE
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 TRUE
METHOD Destroy() CLASS uhttpd_Session
IF ::nActiveSessions == 0
RETURN FALSE
ENDIF
// Destroy session
IF !Eval( ::bDestroy, ::cSID )
RETURN FALSE
ENDIF
RETURN TRUE
METHOD Close() CLASS uhttpd_Session
LOCAL cVal
//TraceLog( "Session Close() - oCGI:h_Session", DumpValue( oCGI:h_Session ) )
IF ::nActiveSessions == 0
RETURN FALSE
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 TRUE
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 := FALSE
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 THEN ::nCookie_LifeTime := nLifeTime
IF cPath <> NIL THEN ::cCookie_Path := cPath
IF cDomain <> NIL THEN ::cCookie_Domain := cDomain
IF lSecure <> NIL THEN ::lCookie_Secure := lSecure
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
//METHOD ReadCookie()
// oCGI:SetCookie( ::cName, ::cSID, ::cCookie_Domain, ::cCookie_Path, cExpires, ::lCookie_Secure )
//RETURN NIL
METHOD GetSessionVars( aHashVars, cFields, cSeparator ) CLASS uhttpd_Session
LOCAL aNotSessionFlds := {}
LOCAL aField, cField, aFields
LOCAL cName, xValue
LOCAL cSessPrefix := ::cName + "_"
LOCAL cFieldsNotInSession := ""
LOCAL cSessVarName
DEFAULT cSeparator TO "&"
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
//DEFAULT cCRCKey TO "3InFoW4lL5" // Max Lenght must to be 10
DEFAULT cCRCKey TO MY_CRCKEY // Max Lenght must to be 10
/* 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
DEFAULT ::cSID TO ::RegenerateID()
DEFAULT cSID TO ::cSID
DEFAULT cCRCKey TO MY_CRCKEY // Max Lenght must to be 10
//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 THEN ::bOpen := bOpen
IF bClose <> NIL THEN ::bClose := bClose
IF bRead <> NIL THEN ::bRead := bRead
IF bWrite <> NIL THEN ::bWrite := bWrite
IF bDestroy <> NIL THEN ::bDestroy := bDestroy
IF bGC <> NIL THEN ::bGC := bGC
RETURN NIL
METHOD SessionOpen( cPath, cName ) CLASS uhttpd_Session
//TraceLog( "SessionOpen() - cName", cName )
IF cPath <> NIL THEN ::cSavePath := cPath
IF cName <> NIL THEN ::cName := cName
RETURN TRUE
METHOD SessionClose() CLASS uhttpd_Session
//TraceLog( "SessionClose()" )
// Nothing to do
RETURN TRUE
METHOD SessionRead( cID ) CLASS uhttpd_Session
LOCAL nH
LOCAL cFile
LOCAL nFileSize
LOCAL cBuffer
LOCAL nRetry := 0
DEFAULT cID TO ::cSID
cFile := ::cSavePath + HB_OSPathSeparator() + ::cName + "_" + cID
//TraceLog( "SessionRead: cFile", cFile )
IF File( cFile )
DO WHILE nRetry++ <= ::nFileRetry
IF ( nH := FOpen( cFile, FO_READ + FO_DENYWRITE ) ) <> -1
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 := FALSE
LOCAL nRetry := 0
//TraceLog( "SessionWrite() - cID, cData", cID, cData )
DEFAULT cID TO ::cSID
DEFAULT cData TO ""
nFileSize := Len( cData )
cFile := ::cSavePath + HB_OSPathSeparator() + ::cName + "_" + cID
//TraceLog( "SessionWrite() - cFile", cFile )
IF nFileSize > 0
DO WHILE nRetry++ <= ::nFileRetry
IF ( nH := hb_FCreate( cFile, FC_NORMAL, FO_READWRITE + FO_DENYWRITE ) ) <> -1
IF ( FWrite( nH, @cData, nFileSize ) ) <> nFileSize
uhttpd_Die( "ERROR: On writing session file : " + cFile + ", File error : " + hb_cStr( FError() ) )
ELSE
lOk := TRUE
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 File( cFile )
// FErase( cFile )
//ENDIF
// Return that all is ok
lOk := TRUE
ENDIF
RETURN lOk
METHOD SessionDestroy( cID ) CLASS uhttpd_Session
LOCAL cFile
LOCAL lOk
LOCAL nRetry := 0
//TraceLog( "SessionDestroy() - cID", cID )
DEFAULT cID TO ::cSID
_SESSION := hb_Hash()
::oCookie:DeleteCookie( ::cName )
//TraceLog( "SessionDestroy() - cID, oCGI:h_Session", cID, DumpValue( oCGI:h_Session ) )
cFile := ::cSavePath + HB_OSPathSeparator() + ::cName + "_" + cID
lOk := FALSE
DO WHILE nRetry++ <= ::nFileRetry
IF ( lOk := ( FErase( cFile ) == 0 ) )
EXIT
ELSE
hb_idleSleep( ::nFileWait / 1000 )
LOOP
ENDIF
ENDDO
//IF !( lOk := ( FErase( cFile ) == 0 ) )
// uhttpd_Die( "ERROR: On deleting session file : " + cFile + ", File error : " + hb_cStr( FError() ) )
//ELSE
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 nStartTime
LOCAL nSecs
LOCAL aDir, aFile
DEFAULT nMaxLifeTime TO ::nGc_MaxLifeTime
aDir := Directory( ::cSavePath + HB_OSPathSeparator() + ::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_OSPathSeparator() + aFile[ F_NAME ] )
ENDIF
NEXT
RETURN TRUE
STATIC FUNCTION TimeDiffAsSeconds( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
LOCAL aRetVal
DEFAULT dDateEnd TO DATE()
DEFAULT cTimeEnd TO TIME()
aRetVal := FT_ELAPSED( dDateStart, dDateEnd, cTimeStart, cTimeEnd )
RETURN aRetVal[ 4, 2 ]
// Nanforum ELAPSED
STATIC FUNCTION FT_ELAPSED(dStart, dEnd, cTimeStart, cTimeEnd)
LOCAL nTotalSec, nCtr, nConstant, nTemp, aRetVal[4,2]
IF ! ( VALTYPE(dStart) $ 'DC' )
dStart := DATE()
ELSEIF VALTYPE(dStart) == 'C'
cTimeStart := dStart
dStart := DATE()
ENDIF
IF ! ( VALTYPE(dEnd) $ 'DC' )
dEnd := DATE()
ELSEIF VALTYPE(dEnd) == 'C'
cTimeEnd := dEnd
dEnd := DATE()
ENDIF
IF( VALTYPE(cTimeStart) != 'C', cTimeStart := '00:00:00', )
IF( VALTYPE(cTimeEnd) != 'C', cTimeEnd := '00:00:00', )
nTotalSec := (dEnd - dStart) * 86400 + ;
VAL(cTimeEnd) * 3600 + ;
VAL(SUBSTR(cTimeEnd,AT(':', cTimeEnd)+1,2)) * 60 + ;
IF(RAT(':', cTimeEnd) == AT(':', cTimeEnd), 0, ;
VAL(SUBSTR(cTimeEnd,RAT(':', cTimeEnd)+1))) - ;
VAL(cTimeStart) * 3600 - ;
VAL(SUBSTR(cTimeStart,AT(':', cTimeStart)+1,2)) * 60 - ;
IF(RAT(':', cTimeStart) == AT(':', cTimeStart), 0, ;
VAL(SUBSTR(cTimeStart,RAT(':', cTimeStart)+1)))
nTemp := nTotalSec
FOR nCtr = 1 to 4
nConstant := IF(nCtr == 1, 86400, IF(nCtr == 2, 3600, IF( nCtr == 3, 60, 1)))
aRetVal[nCtr,1] := INT(nTemp/nConstant)
aRetval[nCtr,2] := nTotalSec / nConstant
nTemp -= aRetVal[nCtr,1] * nConstant
NEXT
RETURN aRetVal
// -------------------------------*************************-----------------------------------------
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 THEN aAdd( aSerial, { cKey, xVal } )
NEXT
ENDIF
RETURN IIF( !Empty( aSerial ), HB_Serialize( aSerial ), NIL )
METHOD Decode( cData ) CLASS uhttpd_Session
LOCAL lOk := TRUE
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 )
//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
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 := FALSE
EXIT
END
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", FALSE )
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=' + LTrim( Str( ::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=' + LTrim( Str( ::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

View File

@@ -0,0 +1,432 @@
/*
* $Id$
*/
#include "hbapi.h"
#include "hbapiitm.h"
#if defined( HB_OS_WIN )
#define _WINSOCKAPI_ /* Prevents inclusion of winsock.h in windows.h */
#define HB_SOCKET_T SOCKET
#include <winsock2.h>
#include <windows.h>
/*
Function naming:
The intention of this library is to be as close as possible to the original
socket implementation. This supposed to be valid for function names also,
but some of the names are very platform dependent, ex., WSA*() functions.
select() function name is reserved for standard Harbour's function, so,
socket_*() prefix was used:
socket_init() - WSAStartup()
socket_exit() - WSACleanup()
socket_error() - WSALastError()
socket_select() - select()
Finally I renamed all functions to have socket_*() prefix to be more "prefix
compatible" and not to occupy a general function names like send(), bind(),
accept(), listen(), etc.:
socket_create() - socket()
socket_close() - closesocket()
socket_shutdown() - shutdown()
socket_bind() - bind()
socket_listen() - listen()
socket_accept() - accept()
socket_send() - send()
socket_recv() - recv()
socket_recv() - recv()
socket_getsockname() - getsockname()
socket_getpeername() - getpeername()
Types mapping:
SOCKET
UINT_PTR in Windows, let's map it to pointer type, and INVALID_SOCKET value to NIL
struct sockaddr
It is not only IP addresses, also can be IPX, etc. All network-host byte order
conversion should be hidden from Harbour API. So, let's map to:
{ adress_familly, ... }
AF_INET: { AF_INET, cAddr, nPort }
other: { AF_?, cAddressDump }
*/
#ifdef hb_parnidef
#undef hb_parnidef
#endif
static int hb_parnidef( int iParam, int iValue )
{
return HB_ISNUM( iParam ) ? hb_parni( iParam ) : iValue;
}
static SOCKET hb_parsocket( int iParam )
{
return HB_ISPOINTER( iParam ) ? ( SOCKET ) hb_parptr( 1 ) : INVALID_SOCKET;
}
static void hb_retsocket( SOCKET hSocket )
{
if( hSocket == INVALID_SOCKET )
hb_ret();
else
hb_retptr( ( void* ) hSocket );
}
static SOCKET hb_itemGetSocket( PHB_ITEM pItem )
{
return HB_IS_POINTER( pItem ) ? ( SOCKET ) hb_itemGetPtr( pItem ) : INVALID_SOCKET;
}
static PHB_ITEM hb_itemPutSocket( PHB_ITEM pItem, SOCKET hSocket )
{
if( ! pItem )
pItem = hb_itemNew( NULL );
if( hSocket == INVALID_SOCKET )
hb_itemClear( pItem );
else
hb_itemPutPtr( pItem, ( void* ) hSocket );
return pItem;
}
static void hb_itemGetSockaddr( PHB_ITEM pItem, struct sockaddr* sa )
{
memset( sa, 0, sizeof( struct sockaddr ) );
if( HB_IS_ARRAY( pItem ) )
{
sa->sa_family = hb_arrayGetNI( pItem, 1 );
if( sa->sa_family == AF_INET )
{
( ( struct sockaddr_in* ) sa)->sin_addr.S_un.S_addr = inet_addr( hb_arrayGetCPtr( pItem, 2 ) );
( ( struct sockaddr_in* ) sa)->sin_port = htons( hb_arrayGetNI( pItem, 3 ) );
}
else
{
ULONG ulLen = hb_arrayGetCLen( pItem, 2 );
if( ulLen > sizeof( sa->sa_data ) )
ulLen = sizeof( sa->sa_data );
memcpy( sa->sa_data, hb_arrayGetCPtr( pItem, 2 ), ulLen );
}
}
}
static PHB_ITEM hb_itemPutSockaddr( PHB_ITEM pItem, const struct sockaddr* saddr )
{
pItem = hb_itemNew( pItem );
if( saddr->sa_family == AF_INET )
{
hb_arrayNew( pItem, 3 );
hb_arraySetNI( pItem, 1, saddr->sa_family );
hb_arraySetC( pItem, 2, inet_ntoa( ( ( struct sockaddr_in* ) saddr )->sin_addr ) );
hb_arraySetNI( pItem, 3, ntohs( ( ( struct sockaddr_in* ) saddr )->sin_port ) );
}
else
{
hb_arrayNew( pItem, 2 );
hb_arraySetNI( pItem, 1, saddr->sa_family );
hb_arraySetCL( pItem, 2, saddr->sa_data, sizeof( saddr->sa_data ) );
}
return pItem;
}
HB_FUNC ( SOCKET_INIT )
{
WSADATA wsad;
hb_retni( WSAStartup( hb_parnidef( 1, 257 ), &wsad ) );
hb_storclen( (char*) &wsad, sizeof( WSADATA ), 2 );
}
HB_FUNC ( SOCKET_EXIT )
{
hb_retni( WSACleanup() );
}
HB_FUNC ( SOCKET_ERROR )
{
hb_retni( WSAGetLastError() );
}
HB_FUNC ( SOCKET_CREATE )
{
hb_retsocket( socket( hb_parnidef( 1, PF_INET ),
hb_parnidef( 2, SOCK_STREAM ),
hb_parnidef( 3, IPPROTO_TCP ) ) );
}
HB_FUNC ( SOCKET_CLOSE )
{
hb_retni( closesocket( hb_parsocket( 1 ) ) );
}
HB_FUNC ( SOCKET_BIND )
{
struct sockaddr sa;
hb_itemGetSockaddr( hb_param( 2, HB_IT_ANY ), &sa );
hb_retni( bind( hb_parsocket( 1 ), &sa, sizeof( struct sockaddr ) ) );
}
HB_FUNC ( SOCKET_LISTEN )
{
hb_retni( listen( hb_parsocket( 1 ), hb_parnidef( 2, 10 ) ) );
}
HB_FUNC ( SOCKET_ACCEPT )
{
struct sockaddr saddr;
int iSize = sizeof( struct sockaddr );
hb_retsocket( accept( hb_parsocket( 1 ), &saddr, &iSize ) );
if( HB_ISBYREF( 2 ) )
{
PHB_ITEM pItem = hb_itemPutSockaddr( NULL, &saddr );
hb_itemParamStoreForward( 2, pItem );
hb_itemRelease( pItem );
}
}
HB_FUNC ( SOCKET_SHUTDOWN )
{
hb_retni( shutdown( hb_parsocket( 1 ), hb_parnidef( 2, SD_BOTH ) ) );
}
HB_FUNC ( SOCKET_RECV )
{
int iLen, iRet;
char* pBuf;
iLen = hb_parni( 3 );
if( iLen > 65536 || iLen <= 0 )
iLen = 4096;
pBuf = ( char* ) hb_xgrab( ( ULONG ) iLen );
iRet = recv( hb_parsocket( 1 ), pBuf, iLen, hb_parnidef( 4, 0 ) );
hb_retni( iRet );
hb_storclen( pBuf, iRet > 0 ? iRet : 0, 2 );
hb_xfree( pBuf );
}
HB_FUNC ( SOCKET_SEND )
{
hb_retni( send( hb_parsocket( 1 ), hb_parc( 2 ), hb_parclen( 2 ), hb_parni( 3, 0 ) ) );
}
HB_FUNC ( SOCKET_SELECT )
{
fd_set setread, setwrite, seterror;
BOOL bRead = 0, bWrite = 0, bError = 0;
struct timeval tv;
SOCKET socket, maxsocket;
PHB_ITEM pArray, pItem;
ULONG ulLen, ulIndex, ulCount;
LONG lTimeout;
int iRet;
FD_ZERO( &setread );
FD_ZERO( &setwrite );
FD_ZERO( &seterror );
maxsocket = (SOCKET) 0;
pArray = hb_param( 1, HB_IT_ARRAY );
if( pArray )
{
ulLen = hb_arrayLen( pArray );
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
bRead = 1;
FD_SET( socket, &setread );
if( socket > maxsocket )
maxsocket = socket;
}
}
}
pArray = hb_param( 2, HB_IT_ARRAY );
if( pArray )
{
ulLen = hb_arrayLen( pArray );
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
bWrite = 1;
FD_SET( socket, &setwrite );
if( socket > maxsocket )
maxsocket = socket;
}
}
}
pArray = hb_param( 3, HB_IT_ARRAY );
if( pArray )
{
ulLen = hb_arrayLen( pArray );
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
bError = 1;
FD_SET( socket, &seterror );
if( socket > maxsocket )
maxsocket = socket;
}
}
}
/* Default forever */
lTimeout = HB_ISNUM( 4 ) ? hb_parnl( 4 ) : -1;
if( lTimeout == -1 )
{
iRet = select( maxsocket + 1, bRead ? &setread : NULL, bWrite ? &setwrite: NULL,
bError ? &seterror : NULL, NULL );
}
else
{
tv.tv_sec = lTimeout / 1000;
tv.tv_usec = ( lTimeout % 1000 ) * 1000;
iRet = select( maxsocket + 1, bRead ? &setread : NULL, bWrite ? &setwrite: NULL,
bError ? &seterror : NULL, &tv );
}
pArray = hb_param( 1, HB_IT_ARRAY );
if( pArray && HB_ISBYREF( 1 ) )
{
ulLen = hb_arrayLen( pArray );
pItem = hb_itemNew( NULL );
hb_arrayNew( pItem, ulLen );
ulCount = 0;
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
if( FD_ISSET( socket, &setread ) )
{
hb_arraySetForward( pItem, ++ulCount, hb_itemPutSocket( NULL, socket ) );
}
}
}
hb_itemParamStoreForward( 1, pItem );
}
pArray = hb_param( 2, HB_IT_ARRAY );
if( pArray && HB_ISBYREF( 2 ) )
{
ulLen = hb_arrayLen( pArray );
pItem = hb_itemNew( NULL );
hb_arrayNew( pItem, ulLen );
ulCount = 0;
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
if( FD_ISSET( socket, &setwrite ) )
{
hb_arraySetForward( pItem, ++ulCount, hb_itemPutSocket( NULL, socket ) );
}
}
}
hb_itemParamStoreForward( 2, pItem );
}
pArray = hb_param( 3, HB_IT_ARRAY );
if( pArray && HB_ISBYREF( 3 ) )
{
ulLen = hb_arrayLen( pArray );
pItem = hb_itemNew( NULL );
hb_arrayNew( pItem, ulLen );
ulCount = 0;
for( ulIndex = 1; ulIndex <= ulLen; ulIndex++ )
{
socket = hb_itemGetSocket( hb_arrayGetItemPtr( pArray, ulIndex ) );
if( socket != INVALID_SOCKET )
{
if( FD_ISSET( socket, &seterror ) )
{
hb_arraySetForward( pItem, ++ulCount, hb_itemPutSocket( NULL, socket ) );
}
}
}
hb_itemParamStoreForward( 3, pItem );
}
hb_retni( iRet );
}
HB_FUNC ( SOCKET_GETSOCKNAME )
{
struct sockaddr saddr;
int iSize = sizeof( struct sockaddr );
hb_retni( getsockname( hb_parsocket( 1 ), &saddr, &iSize ) );
if( HB_ISBYREF( 2 ) )
{
PHB_ITEM pItem = hb_itemPutSockaddr( NULL, &saddr );
hb_itemParamStoreForward( 2, pItem );
hb_itemRelease( pItem );
}
}
HB_FUNC ( SOCKET_GETPEERNAME )
{
struct sockaddr saddr;
int iSize = sizeof( struct sockaddr );
hb_retni( getpeername( hb_parsocket( 1 ), &saddr, &iSize ) );
if( HB_ISBYREF( 2 ) )
{
PHB_ITEM pItem = hb_itemPutSockaddr( NULL, &saddr );
hb_itemParamStoreForward( 2, pItem );
hb_itemRelease( pItem );
}
}
HB_FUNC ( CONNECT )
{
struct sockaddr sa;
hb_itemGetSockaddr( hb_param( 2, HB_IT_ANY ), &sa );
hb_retni( connect( hb_parsocket( 1 ), &sa, sizeof( struct sockaddr ) ) );
}
#endif

View File

@@ -0,0 +1,8 @@
#
# $Id$
#
# Use -DUSE_HB_INET if you want to turn on Harbour internet socket.
# It's always on on non-Windows systems.
-mt -gui uhttpd.prg cgifunc.prg cookie.prg session.prg uhttpdc.c socket.c