Files
harbour-core/harbour/contrib/tip/client.prg
Viktor Szakats 799edeb1e5 2007-10-01 23:50 UTC+0100 Viktor Szakats (harbour.01 syenar.hu)
* contrib/tip/Makefile
   * contrib/tip/makefile.bc
   + contrib/tip/makefile.vc
   + contrib/tip/make_vc.bat
   * contrib/tip/tip.ch
   * contrib/tip/utils.c
   * contrib/tip/base64x.c
   * contrib/tip/encmthd.c
   - contrib/tip/hbhex2n.c
   * contrib/tip/cgi.prg
   * contrib/tip/client.prg
   * contrib/tip/ftpcln.prg
   * contrib/tip/httpcln.prg
   * contrib/tip/mail.prg
   * contrib/tip/thtml.prg
   * contrib/tip/url.prg
     + Code converted to not require xhb lib.
     % Now compiled with -kM switch to avoid macro expansion 
       of strings. This is important here, as "&" char is used 
       quite often in the code, so expect a somewhat better 
       performance.
     - Removed hex number conversion functions, as compatible 
       versions are now available in Harbour with am hb_ prefix.
     + C code update to use Harbour API calls only.
     + Added VC build script.
     ! Fixed two C warnings.
     ! Standardized SVN header in one file.
     ! Set std SVN keywords and eol-style.
     ; Please test it.

   * include/hbextern.ch
   * source/rtl/hbinet.c
   * contrib/xhb/xhb.ch
   * contrib/xhb/xhbfunc.c
     * Renamed hb_IsInetSocket() to hb_inetIsSocket() to 
       keep all inet related functions in one namespace.
2007-10-01 22:17:21 +00:00

603 lines
13 KiB
Plaintext

/*
* $Id$
*/
/*
* xHarbour Project source code:
* TIP Class oriented Internet protocol library
*
* Copyright 2003 Giancarlo Niccolai <gian@niccolai.ws>
*
* 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.
*
*/
/* 2004-01-13
Enhaced tip cliente to conenct to secure smtp servers by Luiz Rafael Culik
*/
/* 2007-03-29, Hannes Ziegler
Adapted all :new() method(s) so that tIPClient becomes the
abstract super class for TIpClientFtp, TIpClientHttp, TIpClientPop and TIpClientSmtp
Added Methods :INetErrorDesc(), :lastErrorCode() and :lastErrorMessage()
Removed method :data() since it calls an undeclared method :getOk()
:data() is used in TIpClientSmtp
Fixed bug in :readToFile()
*/
/* 2007-06-01, Toninho@fwi
Added data ::nWrite to work like ::nRead
*/
#include "hbclass.ch"
#include "error.ch"
#include "fileio.ch"
#include "tip.ch"
#include "common.ch"
/**
* Inet Client class
*/
CLASS tIPClient
CLASSDATA bInitSocks INIT .F.
CLASSDATA cCRLF INIT HB_InetCRLF()
DATA oUrl // url to wich to connect
DATA oCredentials // credential needed to access the service
DATA nStatus // basic status
DATA SocketCon
Data lTrace
Data nHandle
/* Input stream length */
DATA nLength
/* Input stream data read by the app*/
DATA nRead
/* Last physical read amount */
DATA nLastRead
DATA nDefaultPort
DATA nConnTimeout
DATA bInitialized
DATA cReply
DATA nAccessMode
DATA nWrite
DATA nLastWrite
DATA bEof
DATA isOpen INIT .F.
/** Gauge control; it can be a codeblock or a function pointer. */
DATA exGauge
DATA Cargo
METHOD New( oUrl, lTrace, oCredentials )
METHOD Open()
METHOD Read( iLen )
METHOD ReadToFile( cFile, nMode, nSize )
METHOD Write( cData, iLen, bCommit )
METHOD Commit()
METHOD WriteFromFile( cFile )
METHOD Reset()
METHOD Close()
/* METHOD Data( cData ) */ // commented: calls undeclared METHOD :getOk
METHOD lastErrorCode() INLINE ::nLastError
METHOD lastErrorMessage(SocketCon) INLINE ::INetErrorDesc(SocketCon)
PROTECTED:
DATA nLastError INIT 0
/* Methods to log data if needed */
METHOD InetRecv( SocketCon, cStr1, len)
METHOD InetRecvLine( SocketCon, nLen, size )
METHOD InetRecvAll( SocketCon, cStr1, len )
METHOD InetCount( SocketCon )
METHOD InetSendAll( SocketCon, cData, nLen )
METHOD InetErrorCode(SocketCon)
METHOD InetErrorDesc(SocketCon)
METHOD InetConnect( cServer, nPort, SocketCon )
METHOD Log()
ENDCLASS
METHOD New( oUrl, lTrace, oCredentials ) CLASS tIPClient
LOCAL oErr
Default lTrace to .F.
IF .not. ::bInitSocks
HB_InetInit()
::bInitSocks := .T.
ENDIF
IF HB_IsString( oUrl )
oUrl := tUrl():New( oUrl )
ENDIF
IF .NOT. oURL:cProto $ "ftp,http,pop,smtp"
oErr := ErrorNew()
oErr:Args := { Self, oURL:cProto }
oErr:CanDefault := .F.
oErr:CanRetry := .F.
oErr:CanSubstitute := .T.
oErr:Description := "unsupported protocol"
oErr:GenCode := EG_UNSUPPORTED
oErr:Operation := ::className()+":new()"
oErr:Severity := ES_ERROR
oErr:SubCode := 1081
oErr:SubSystem := "BASE"
Eval( ErrorBlock(), oErr )
ENDIF
::oUrl := oUrl
::oCredentials := oCredentials
::nStatus := 0
::bInitialized := .F.
::nWrite := 0
::nLastWrite := 0
::nLength := -1
::nRead := 0
::nLastRead := 0
::bEof := .F.
::lTrace := lTrace
RETURN self
METHOD Open( cUrl ) CLASS tIPClient
LOCAL nPort
IF HB_IsString( cUrl )
::oUrl := tUrl():New( cUrl )
ENDIF
IF ::oUrl:nPort == -1
nPort := ::nDefaultPort
ELSE
nPort := ::oUrl:nPort
ENDIF
::SocketCon := HB_InetCreate()
HB_InetTimeout( ::SocketCon, ::nConnTimeout )
::InetConnect( ::oUrl:cServer, nPort, ::SocketCon )
IF ::InetErrorCode( ::SocketCon ) != 0
RETURN .F.
ENDIF
::isOpen := .T.
RETURN .T.
METHOD Close() CLASS tIPClient
local nRet:=-1
IF .not. Empty( ::SocketCon )
nRet := HB_InetClose( ::SocketCon )
::SocketCon:=nil
::isOpen := .F.
ENDIF
RETURN(nRet)
METHOD Reset() CLASS tIPClient
::bInitialized := .F.
::bEof := .F.
RETURN .T.
METHOD Commit() CLASS tIPClient
RETURN .T.
METHOD Read( nLen ) CLASS tIPClient
LOCAL cStr0, cStr1
IF ::nLength > 0 .and. ::nLength == ::nRead
RETURN NIL
ENDIF
IF Empty( nLen ) .or. nLen < 0 .or.( ::nLength > 0 .and. nLen > ::nLength - ::nRead )
nLen := ::nLength - ::nRead
ENDIF
IF Empty( nLen ) .or. nLen < 0
// read till end of stream
cStr1 := Space( 1024 )
cStr0 := ""
::nLastRead := ::InetRecv( ::SocketCon, @cStr1, 1024 )
DO WHILE ::nLastRead > 0
::nRead += ::nLastRead
cStr0 += Substr( cStr1, 1, ::nLastRead )
::nLastRead := ::InetRecv( ::SocketCon, @cStr1, 1024 )
ENDDO
::bEof := .T.
ELSE
// read an amount of data
cStr0 := Space( nLen )
// S.R. if len of file is less than 1024 HB_InetRecvAll return 0
// ::nLastRead := HB_InetRecvAll( ::SocketCon, @cStr0, nLen )
::InetRecvAll( ::SocketCon, @cStr0, nLen )
::nLastRead := ::InetCount( ::SocketCon )
::nRead += ::nLastRead
IF ::nLastRead != nLen
::bEof := .T.
cStr0 := Substr( cStr0, 1, ::nLastRead )
// S.R. RETURN NIL
ENDIF
IF ::nRead == ::nLength
::bEof := .T.
ENDIF
ENDIF
RETURN cStr0
METHOD ReadToFile( cFile, nMode, nSize ) CLASS tIPClient
LOCAL nFout
LOCAL cData
LOCAL nSent
IF Empty ( nMode )
nMode := FC_NORMAL
ENDIF
nSent := 0
IF !Empty( ::exGauge )
HB_ExecFromArray( ::exGauge, { nSent, nSize, Self } )
ENDIF
::nRead := 0
::nStatus := 1
DO WHILE ::InetErrorCode( ::SocketCon ) == 0 .and. .not. ::bEof
cData := ::Read( 1024 )
IF cData == NIL
IF nFout != NIL
Fclose( nFout )
ENDIF
IF ::InetErrorCode( ::SocketCon ) > 0
RETURN .F.
ELSE
RETURN .T.
ENDIF
ENDIF
IF nFout == NIL
nFout := Fcreate( cFile, nMode )
IF nFout < 0
::nStatus := 0
RETURN .F.
ENDIF
ENDIF
IF Fwrite( nFout, cData ) < 0
Fclose( nFout )
RETURN .F.
ENDIF
nSent += Len( cData )
IF !Empty( ::exGauge )
HB_ExecFromArray( ::exGauge, { nSent, nSize, Self } )
ENDIF
ENDDO
IF nSent > 0
::Commit()
Endif
::nStatus := 2
Fclose( nFout )
RETURN .T.
METHOD WriteFromFile( cFile ) CLASS tIPClient
LOCAL nFin
LOCAL cData
LOCAL nLen
LOCAL nSize, nSent
::nWrite := 0
::nStatus := 0
nFin := Fopen( cFile, FO_READ )
IF nFin < 0
RETURN .F.
ENDIF
nSize := FSeek( nFin, 0, 2 )
FSeek( nFin, 0 )
// allow initialization of the gauge
nSent := 0
IF ! Empty( ::exGauge )
HB_ExecFromArray( ::exGauge, {nSent, nSize, Self} )
ENDIF
::nStatus := 1
cData := Space( 1024 )
nLen := Fread( nFin, @cData, 1024 )
DO WHILE nLen > 0
IF ::Write( @cData, nLen ) != nLen
Fclose( nFin )
RETURN .F.
ENDIF
nSent += nLen
IF ! Empty( ::exGauge )
HB_ExecFromArray( ::exGauge, {nSent, nSize, Self} )
ENDIF
nLen := Fread( nFin, @cData, 1024 )
ENDDO
// it may happen that the file has lenght 0
IF nSent > 0
::Commit()
ENDIF
::nStatus := 2
Fclose( nFin )
RETURN .T.
/*
HZ: METHOD :getOk() is not declared in TIpClient
METHOD Data( cData ) CLASS tIPClient
::InetSendall( ::SocketCon, "DATA" + ::cCRLF )
IF .not. ::GetOk()
RETURN .F.
ENDIF
::InetSendall(::SocketCon, cData + ::cCRLF + "." + ::cCRLF )
RETURN ::GetOk()
*/
METHOD Write( cData, nLen, bCommit ) CLASS tIPClient
IF Empty( nLen )
nLen := Len( cData )
ENDIF
::nLastWrite := ::InetSendall( ::SocketCon, cData , nLen )
IF .not. Empty( bCommit ) .and. bCommit
::Commit()
ENDIF
::nWrite += ::nLastWrite
RETURN ::nLastWrite
METHOD InetSendAll( SocketCon, cData, nLen ) CLASS tIPClient
Local nRet
IF Empty( nLen )
nLen := Len( cData )
ENDIF
nRet := HB_InetSendAll( SocketCon, cData, nLen )
if ::lTrace
::Log( SocketCon, nlen, cData, nRet )
endif
Return nRet
METHOD InetCount( SocketCon ) CLASS tIPClient
Local nRet
nRet := HB_InetCount( SocketCon )
if ::lTrace
::Log( SocketCon, nRet )
endif
Return nRet
METHOD InetRecv( SocketCon, cStr1, len ) CLASS tIPClient
Local nRet
nRet := HB_InetRecv( SocketCon, @cStr1, len )
if ::lTrace
::Log( SocketCon, "", len, iif( nRet >= 0, cStr1, nRet ) )
endif
Return nRet
METHOD InetRecvLine( SocketCon, nLen, size ) CLASS tIPClient
Local cRet
cRet := HB_InetRecvLine( SocketCon, @nLen, size )
if ::lTrace
::Log( SocketCon, "", size, cRet )
endif
Return cRet
METHOD InetRecvAll( SocketCon, cStr1, len ) CLASS tIPClient
Local nRet
nRet := HB_InetRecvAll( SocketCon, @cStr1, len )
if ::lTrace
::Log( SocketCon, "", len, iif( nRet >= 0, cStr1, nRet ) )
endif
Return nRet
METHOD InetErrorCode( SocketCon ) CLASS tIPClient
Local nRet
::nLastError := nRet := HB_InetErrorCode( SocketCon )
if ::lTrace
::Log( SocketCon, nRet )
endif
Return nRet
METHOD InetErrorDesc( SocketCon ) CLASS tIPClient
LOCAL cMsg := ""
DEFAULT SocketCon TO ::SocketCon
IF .not. Empty( SocketCon )
cMsg := HB_InetErrorDesc( SocketCon )
ENDIF
RETURN cMsg
/* BROKEN, should test number of parameters and act accordingly, see doc\inet.txt */
METHOD InetConnect( cServer, nPort, SocketCon ) CLASS tIPClient
HB_InetConnect( cServer, nPort, SocketCon )
if ::lTrace
::Log( cServer, nPort, SocketCon )
endif
Return Nil
/* Called from another method with list of parameters and, as last parameter, return code
of function being logged.
Example, I want to log MyFunc( a, b, c ) which returns m,
::Log( a, b, c, m )
*/
METHOD Log( ... ) CLASS tIPClient
LOCAL xVar
LOCAL cMsg := DToS( Date() ) + "-" + Time() + Space( 2 ) + ;
SubStr( ProcName( 1 ), Rat( ":", ProcName( 1 ) ) ) +;
"( "
for each xVar in hb_aParams()
// Preserves CRLF on result
if xVar:__enumIndex() < PCount()
cMsg += StrTran( StrTran( AllTrim( hb_CStr( xVar ) ), Chr( 13 ) ), Chr( 10 ) )
else
cMsg += hb_CStr( xVar )
endif
cMsg += iif ( xVar:__enumIndex() < PCount() - 1, ", ", "" )
if xVar:__enumIndex() == PCount() - 1
cMsg += " )" + hb_OsNewLine() + ">> "
elseif xVar:__enumIndex() == PCount()
cMsg += " <<" + hb_OsNewLine() + hb_OsNewLine()
endif
next
fWrite( ::nHandle, cMsg )
RETURN Self