Import Jabber GPL'ed protocol (beware, it crashes but it's too late to find out why).
This commit is contained in:
commit
0706699184
|
@ -0,0 +1,7 @@
|
||||||
|
SubDir TOP ;
|
||||||
|
|
||||||
|
# Include all the components
|
||||||
|
SubInclude TOP libs ;
|
||||||
|
SubInclude TOP protocols ;
|
||||||
|
|
||||||
|
UninstallTarget $(CAYA_DIRECTORY) ;
|
|
@ -0,0 +1,71 @@
|
||||||
|
# The directories used by the build.
|
||||||
|
BUILD_DIR = [ FDirName $(TOP) build ] ;
|
||||||
|
JAM_DIR = [ FDirName $(BUILD_DIR) jam ] ;
|
||||||
|
SCRIPTS_DIR = [ FDirName $(JAM_DIR) scripts ] ;
|
||||||
|
GENERATED_DIR = [ FDirName $(TOP) generated ] ;
|
||||||
|
DISTRO_DIR = [ FDirName $(TOP) generated distro ] ;
|
||||||
|
FULL_DISTRO_DIR = [ FDirName $(TOP) generated fulldistro ] ;
|
||||||
|
PACKAGE_DIR = [ FDirName $(GENERATED_DIR) packages ] ;
|
||||||
|
PACKAGE_OBJECT_DIR = [ FDirName $(PACKAGE_DIR) objects ] ;
|
||||||
|
|
||||||
|
##-------------------------------------------------------------------
|
||||||
|
## Defines
|
||||||
|
##-------------------------------------------------------------------
|
||||||
|
VERSION = 0.0.1 ;
|
||||||
|
DEFINES += VERSION=\"\\\"$(VERSION)\\\"\" ;
|
||||||
|
DEFINES += BUILD_DATE=\"\\\"$(JAMDATE)\\\"\" ;
|
||||||
|
CHGRP = ;
|
||||||
|
CHOWN = ;
|
||||||
|
|
||||||
|
# Include BuildConfig generated by configure
|
||||||
|
{
|
||||||
|
local buildConfig = [ Glob $(GENERATED_DIR) : BuildConfig ] ;
|
||||||
|
if $(buildConfig) {
|
||||||
|
LOCATE on BuildConfig = $(GENERATED_DIR) ;
|
||||||
|
include BuildConfig ;
|
||||||
|
} else {
|
||||||
|
Exit "Run ./configure first!" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include UserBuildConfig
|
||||||
|
{
|
||||||
|
local userBuildConfig = [ Glob $(JAM_DIR) : UserBuildConfig ] ;
|
||||||
|
if $(userBuildConfig) {
|
||||||
|
LOCATE on UserBuildConfig = $(JAM_DIR) ;
|
||||||
|
include UserBuildConfig ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache files for header scanning and jamfile caching.
|
||||||
|
HCACHEFILE = header_cache ;
|
||||||
|
JCACHEFILE = jamfile_cache ;
|
||||||
|
LOCATE on $(HCACHEFILE) $(JCACHEFILE) = $(GENERATED_DIR) ;
|
||||||
|
|
||||||
|
# Perform configuration checks
|
||||||
|
include [ FDirName $(JAM_DIR) CheckRules ] ;
|
||||||
|
CheckGccPlatform ;
|
||||||
|
CheckCaya ;
|
||||||
|
if ! $(HAVE_CAYA) {
|
||||||
|
Echo "** Caya library is needed!" ;
|
||||||
|
Exit 1 ;
|
||||||
|
}
|
||||||
|
CheckOpenSSL ;
|
||||||
|
if ! $(HAVE_OPENSSL) {
|
||||||
|
Echo "** MSN, Jabber, GoogleTalk and Facebook protocols are disabled for lack of OpenSSL" ;
|
||||||
|
}
|
||||||
|
CheckLibYahoo2 ;
|
||||||
|
if ! $(HAVE_LIBYAHOO2) {
|
||||||
|
Echo "** Yahoo protocol is disabled for lack of libyahoo2" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Include jam scripts
|
||||||
|
include [ FDirName $(JAM_DIR) HelperRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) ConfigRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) OverriddenJamRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) MainBuildRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) FileRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) InstallRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) PackageRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) DistroRules ] ;
|
||||||
|
include [ FDirName $(JAM_DIR) BuildSettings ] ;
|
|
@ -0,0 +1,113 @@
|
||||||
|
# BuildSettings
|
||||||
|
#
|
||||||
|
# Setup global variables.
|
||||||
|
|
||||||
|
# C and C++ flags
|
||||||
|
if $(OSPLAT) = PPC {
|
||||||
|
# filter out -nosyspath
|
||||||
|
CFLAGS = [ FFilter $(CFLAGS) : -nosyspath ] ;
|
||||||
|
C++FLAGS = [ FFilter $(C++FLAGS) : -nosyspath ] ;
|
||||||
|
LINKFLAGS += -warn -export pragma ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use copyattr for copying.
|
||||||
|
CP = copyattr --data ;
|
||||||
|
|
||||||
|
# Default paths for bison and flex:
|
||||||
|
BISON = bison ;
|
||||||
|
LEX = flex ;
|
||||||
|
|
||||||
|
# mkdir shall not fail, if the directory already exists.
|
||||||
|
MKDIR = mkdir -p ;
|
||||||
|
|
||||||
|
# by default we do not strip and do not build tests:
|
||||||
|
STRIP_APPS ?= 0 ;
|
||||||
|
BUILD_TESTS ?= 0 ;
|
||||||
|
|
||||||
|
# Enable debugging by default
|
||||||
|
DEBUG ?= 1 ;
|
||||||
|
|
||||||
|
rule SetUpSubDirBuildSettings
|
||||||
|
{
|
||||||
|
# SetUpSubDirBuildSettings <dir> ;
|
||||||
|
#
|
||||||
|
# Sets up the compiler flags and defines based on the WARNINGS, DEBUG, and
|
||||||
|
# OPTIMIZE variables. Also sets the locations for the targets (objects,
|
||||||
|
# libraries and executables).
|
||||||
|
#
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
#
|
||||||
|
local dir = $(1) ;
|
||||||
|
|
||||||
|
# warnings settings
|
||||||
|
if $(WARNINGS) != 0 {
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
CCFLAGS += -Wall -Wno-multichar -Wpointer-arith
|
||||||
|
-Wmissing-prototypes -Wcast-align -Wsign-compare ;
|
||||||
|
C++FLAGS += -Wall -Wno-multichar -Wno-ctor-dtor-privacy -Woverloaded-virtual
|
||||||
|
-Wconversion -Wpointer-arith -Wcast-align
|
||||||
|
-Wsign-compare -Wno-reorder -Wno-unknown-pragmas ;
|
||||||
|
} else {
|
||||||
|
CCFLAGS += -w on -requireprotos ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local gccString = ;
|
||||||
|
if $(IS_GCC4_PLATFORM) {
|
||||||
|
gccString += gcc4 ;
|
||||||
|
} else {
|
||||||
|
gccString += gcc2 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
local binModeString = ;
|
||||||
|
if $(DEBUG) && $(DEBUG) != 0 {
|
||||||
|
binModeString += debug ;
|
||||||
|
} else {
|
||||||
|
binModeString += release ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# debugging settings
|
||||||
|
if $(DEBUG) && $(DEBUG) != 0 {
|
||||||
|
OPTIMIZE = 0 ;
|
||||||
|
STRIP_APPS = 0 ;
|
||||||
|
DEFINES += DEBUG=$(DEBUG) BM_REF_DEBUGGING ;
|
||||||
|
CCFLAGS += -g ;
|
||||||
|
C++FLAGS += -g -fno-inline ;
|
||||||
|
LINKFLAGS += -g ;
|
||||||
|
}
|
||||||
|
|
||||||
|
DISTRO_DIR = [ FDirName $(TOP) generated distro-$(OS:L)-$(OSPLAT:L)-$(gccString)-$(binModeString) ] ;
|
||||||
|
OBJECTS_DIR = [ FDirName $(TOP) generated objects-$(OS:L)-$(OSPLAT:L)-$(gccString)-$(binModeString) ] ;
|
||||||
|
|
||||||
|
# optimization settings
|
||||||
|
if $(OPTIMIZE) = 0 {
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
OPTIM = -O0 ;
|
||||||
|
} else {
|
||||||
|
OPTIM = -O0 ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
OPTIM ?= -O3 -fstrict-aliasing ;
|
||||||
|
} else {
|
||||||
|
OPTIM ?= -O7 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# setup objects location
|
||||||
|
local objdir = [ FDirName $(OBJECTS_DIR) $(dir[2-]) ] ;
|
||||||
|
SEARCH_SOURCE += $(objdir) ;
|
||||||
|
LOCATE_SOURCE = $(objdir) ;
|
||||||
|
LOCATE_TARGET = $(objdir) ;
|
||||||
|
|
||||||
|
# setup main targets location
|
||||||
|
LOCATE_MAIN_TARGET ?= [ FDirName $(DISTRO_DIR) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# The LOCATE_MAIN_TARGET variable shall be reset for each subdirectory.
|
||||||
|
AUTO_SET_UP_CONFIG_VARIABLES += LOCATE_MAIN_TARGET ;
|
||||||
|
|
||||||
|
# Add the rules setting up the build settings for a subdirectory to the
|
||||||
|
# rules invoked by SubDir.
|
||||||
|
SUBDIRRULES += SetUpSubDirBuildSettings ;
|
|
@ -0,0 +1,106 @@
|
||||||
|
# CheckRules
|
||||||
|
#
|
||||||
|
# Common checks.
|
||||||
|
|
||||||
|
rule CheckGccPlatform
|
||||||
|
{
|
||||||
|
# CheckGccPlatform ;
|
||||||
|
# Detects if we are using gcc4 and set IS_GCC4_PLATFORM according.
|
||||||
|
|
||||||
|
# First find out which gcc version the platform uses.
|
||||||
|
IS_GCC4_PLATFORM = ;
|
||||||
|
if $(OS) = HAIKU {
|
||||||
|
# Only Haiku might use gcc 4. We use the existence of a libstdc++.r4.so in
|
||||||
|
# /boot/develop/lib/x86 to judge whether this is a BeOS compatible and thus
|
||||||
|
# gcc 2 platform. This is not entirely correct, but should be good enough
|
||||||
|
# for the time being.
|
||||||
|
local haveLibStdC++.R4 = [ Glob /boot/develop/lib/x86 : libstdc++.r4.so ] ;
|
||||||
|
if ! $(haveLibStdC++.R4) {
|
||||||
|
IS_GCC4_PLATFORM = 1 ;
|
||||||
|
Echo Using GCC4 platform ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule CheckOpenSSL
|
||||||
|
{
|
||||||
|
# CheckOpenSSL ;
|
||||||
|
# Check for OpenSSL and defined HAVE_OPENSSL according, it also defines
|
||||||
|
# OPENSSL_INCLUDE_DIR and OPENSSL_LIBRARY_DIR with location of respectively
|
||||||
|
# include and library files.
|
||||||
|
|
||||||
|
HAVE_OPENSSL = ;
|
||||||
|
OPENSSL_INCLUDE_DIR = ;
|
||||||
|
OPENSSL_LIBRARY_DIR = ;
|
||||||
|
|
||||||
|
local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/openssl : ssl.h ] ;
|
||||||
|
if $(haveHeaders) {
|
||||||
|
OPENSSL_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/openssl ;
|
||||||
|
|
||||||
|
local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libssl.so ] ;
|
||||||
|
if $(haveLibs) {
|
||||||
|
OPENSSL_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ;
|
||||||
|
|
||||||
|
Echo OpenSSL Headers: $(OPENSSL_INCLUDE_DIR) ;
|
||||||
|
Echo OpenSSL Libs: $(OPENSSL_LIBRARY_DIR) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAVE_OPENSSL = $(haveLibs) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule CheckLibYahoo2
|
||||||
|
{
|
||||||
|
# CheckLibYahoo2
|
||||||
|
# Check for LibYahoo2 and defined HAVE_LIBYAHOO2 according, it also defines
|
||||||
|
# LIBYAHOO2_INCLUDE_DIR and LIBYAHOO2_LIBRARY_DIR with location of respectively
|
||||||
|
# include and library files.
|
||||||
|
|
||||||
|
HAVE_LIBYAHOO2 = ;
|
||||||
|
LIBYAHOO2_INCLUDE_DIR = ;
|
||||||
|
LIBYAHOO2_LIBRARY_DIR = ;
|
||||||
|
|
||||||
|
local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 : yahoo2.h ] ;
|
||||||
|
if $(haveHeaders) {
|
||||||
|
LIBYAHOO2_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 ;
|
||||||
|
|
||||||
|
local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libyahoo2.so ] ;
|
||||||
|
if $(haveLibs) {
|
||||||
|
LIBYAHOO2_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ;
|
||||||
|
|
||||||
|
Echo Yahoo Headers: $(LIBYAHOO2_INCLUDE_DIR) ;
|
||||||
|
Echo Yahoo Libs: $(LIBYAHOO2_LIBRARY_DIR) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAVE_LIBYAHOO2 = $(haveLibs) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule CheckCaya
|
||||||
|
{
|
||||||
|
# CheckCaya
|
||||||
|
# Check for Caya and defined HAVE_CAYA according, it also defines
|
||||||
|
# CAYA_INCLUDE_DIR and CAYA_LIBRARY_DIR with location of respectively
|
||||||
|
# include and library files.
|
||||||
|
|
||||||
|
HAVE_CAYA = ;
|
||||||
|
CAYA_INCLUDE_DIR = ;
|
||||||
|
CAYA_LIBRARY_DIR = ;
|
||||||
|
|
||||||
|
local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/caya : CayaProtocol.h ] ;
|
||||||
|
if $(haveHeaders) {
|
||||||
|
CAYA_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/caya ;
|
||||||
|
|
||||||
|
# local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libinfopopper.so ] ;
|
||||||
|
# if $(haveLibs) {
|
||||||
|
# CAYA_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ;
|
||||||
|
|
||||||
|
Echo Caya Headers: $(CAYA_INCLUDE_DIR) ;
|
||||||
|
# Echo Caya Libs: $(CAYA_LIBRARY_DIR) ;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# HAVE_CAYA = $(haveLibs) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAVE_CAYA = $(haveHeaders) ;
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
# ConfigRules
|
||||||
|
#
|
||||||
|
# Contains rules providing the config variable feature. It allows to set the
|
||||||
|
# values for certain variables for subdirectories in a central place. That is
|
||||||
|
# one can, for instance, specify in a file like UserBuildConfig for which
|
||||||
|
# directories to enable debugging, warnings, set special defines, compiler
|
||||||
|
# flags and the like without needing to edit the Jamfiles for the respective
|
||||||
|
# dirs.
|
||||||
|
|
||||||
|
rule ConfigObject
|
||||||
|
{
|
||||||
|
# ConfigObject <dir> ;
|
||||||
|
#
|
||||||
|
# Private rule. Returns the dummy object on which the config variables are
|
||||||
|
# set for a given subdir.
|
||||||
|
#
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
#
|
||||||
|
local config = __config__ ;
|
||||||
|
local grist = [ FGrist root $(1) ] ;
|
||||||
|
return $(config:G=$(grist)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule SetConfigVar
|
||||||
|
{
|
||||||
|
# SetConfigVar <var> : <dir> : <value> [ : <scope> ] ;
|
||||||
|
#
|
||||||
|
# Sets a config variable for a specified directory to the given value.
|
||||||
|
#
|
||||||
|
# <var>: The name of the variable to be set.
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
# <value>: The value to which the variable shall be set.
|
||||||
|
# <scope>: Either "global" or "local". The former implies that the variable
|
||||||
|
# value shall also be used for subdirectories (recursively), if
|
||||||
|
# for them the variable has not been set. The latter has the same
|
||||||
|
# effect regarding subdirs as if the variable for the directory
|
||||||
|
# is not set. Defaults to "global".
|
||||||
|
#
|
||||||
|
local var = $(1[1]) ;
|
||||||
|
local config = [ ConfigObject $(2) ] ;
|
||||||
|
local scope = $(4) ;
|
||||||
|
if ! $(scope) {
|
||||||
|
scope = global ;
|
||||||
|
}
|
||||||
|
$(var) on $(config) = $(3) ;
|
||||||
|
__set_$(var) on $(config) = $(scope) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule AppendToConfigVar
|
||||||
|
{
|
||||||
|
# AppendToConfigVar <var> : <dir> : <value> [ : <scope> ] ;
|
||||||
|
#
|
||||||
|
# Appends a value to a config variable for a specified directory. Shortcut
|
||||||
|
# for
|
||||||
|
# SetConfigVar <var> : <dir> : [ ConfigVar <var> : <dir> ] <value
|
||||||
|
# : <scope> ;
|
||||||
|
#
|
||||||
|
# <var>: The name of the variable to be set.
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
# <value>: The value which to append to the variables current value.
|
||||||
|
# <scope>: Either "global" or "local". The former implies that the variable
|
||||||
|
# value shall also be used for subdirectories (recursively), if
|
||||||
|
# for them the variable has not been set. The latter has the same
|
||||||
|
# effect regarding subdirs as if the variable for the directory
|
||||||
|
# is not set. Defaults to "global".
|
||||||
|
#
|
||||||
|
SetConfigVar $(1) : $(2) : [ ConfigVar $(1) : $(2) ] $(3) : $(4) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule ConfigVar
|
||||||
|
{
|
||||||
|
# ConfigVar <var> : <dir> [ : <scope> ] ;
|
||||||
|
#
|
||||||
|
# Returns the value of a configuration variable for a given subdir.
|
||||||
|
# If the variable is not set for the subdir, the rule is invoked
|
||||||
|
# recursively for the parent directory with the scope "global". When
|
||||||
|
# the root is reached without yielding a value, the value of the global
|
||||||
|
# variable <var> is returned.
|
||||||
|
#
|
||||||
|
# <var>: The name of the variable whose value shall be returned.
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
# <scope>: If not given any scope passed to SetConfigVar for the given
|
||||||
|
# directory will be accepted, otherwise it must match the scope
|
||||||
|
# passed to SetConfigVar.
|
||||||
|
#
|
||||||
|
local var = $(1[1]) ;
|
||||||
|
local dir = $(2) ;
|
||||||
|
local config = [ ConfigObject $(dir) ] ;
|
||||||
|
local scope = $(3) ;
|
||||||
|
local varScope = [ on $(config) return $(__set_$(var)) ] ;
|
||||||
|
if ( ! $(scope) && $(varScope) )
|
||||||
|
|| ( $(scope) && $(scope) = $(varScope) )
|
||||||
|
|| ! $(dir) {
|
||||||
|
on $(config) return $($(var)) ;
|
||||||
|
} else {
|
||||||
|
dir = [ FReverse $(dir) ] ;
|
||||||
|
return [ ConfigVar $(var) : [ FReverse $(dir[2-]) ] : global ] ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule SetUpConfigVars {
|
||||||
|
# SetUpConfigVars <dir> ;
|
||||||
|
#
|
||||||
|
# Sets the global variables defined in AUTO_SET_UP_CONFIG_VARIABLES to the
|
||||||
|
# values specified for the subdirectory <dir>.
|
||||||
|
#
|
||||||
|
# <dir>: Parameters as passed to the SubDir rule, i.e. the name of the
|
||||||
|
# TOP variable and the subdir tokens.
|
||||||
|
#
|
||||||
|
local dir = $(1) ;
|
||||||
|
|
||||||
|
# Backup the global variable value on first invocation, otherwise restore
|
||||||
|
# them, so that ConfigVar returns the right values for not explicity set
|
||||||
|
# local variables.
|
||||||
|
local var ;
|
||||||
|
if ! $(__config_var_backup__) {
|
||||||
|
__config_var_backup__ = true ;
|
||||||
|
for var in $(AUTO_SET_UP_CONFIG_VARIABLES) {
|
||||||
|
__config_var_backup_$(var)__ = $($(var)) ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for var in $(AUTO_SET_UP_CONFIG_VARIABLES) {
|
||||||
|
$(var) = $(__config_var_backup_$(var)__) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set the variables to their configured values.
|
||||||
|
for var in $(AUTO_SET_UP_CONFIG_VARIABLES) {
|
||||||
|
$(var) = [ ConfigVar $(var) : $(dir) ] ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the SetUpConfigVars rule to the rules that are invoked at the end of the
|
||||||
|
# SubDir rule. Prepend it, so that the variables are set up before any other
|
||||||
|
# rule is invoked.
|
||||||
|
SUBDIRRULES = SetUpConfigVars $(SUBDIRRULES) ;
|
||||||
|
|
||||||
|
# Some config variables that should be set up automatically for subdirs.
|
||||||
|
AUTO_SET_UP_CONFIG_VARIABLES +=
|
||||||
|
CCFLAGS C++FLAGS DEBUG DEFINES HDRS LINKFLAGS OPTIM OPTIMIZE
|
||||||
|
SYSHDRS WARNINGS
|
||||||
|
;
|
|
@ -0,0 +1,35 @@
|
||||||
|
# DistroRules
|
||||||
|
#
|
||||||
|
# Rules to archive binary distributions.
|
||||||
|
|
||||||
|
rule Distro
|
||||||
|
{
|
||||||
|
local target = $(1) ;
|
||||||
|
|
||||||
|
NotFile $(target) ;
|
||||||
|
Always $(target) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions Distro
|
||||||
|
{
|
||||||
|
echo "== Making distro $(DISTRO_DIR) ==" ;
|
||||||
|
mimeset $(DISTRO_DIR) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
Depends fulldistro : distro ;
|
||||||
|
|
||||||
|
rule FullDistro
|
||||||
|
{
|
||||||
|
local target = $(1) ;
|
||||||
|
|
||||||
|
NotFile $(target) ;
|
||||||
|
Always $(target) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions FullDistro
|
||||||
|
{
|
||||||
|
echo "== Making full distro $(FULL_DISTRO_DIR) ==" ;
|
||||||
|
rm -rf $(FULL_DISTRO_DIR) ;
|
||||||
|
cp -a $(DISTRO_DIR) $(FULL_DISTRO_DIR) ;
|
||||||
|
mimeset $(FULL_DISTRO_DIR) ;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
# FileRules
|
||||||
|
#
|
||||||
|
# Rules for files and symbolic links.
|
||||||
|
|
||||||
|
rule SymLink
|
||||||
|
{
|
||||||
|
# SymLink <target> : <source> : <makeDefaultDependencies> ;
|
||||||
|
# Links <target> to <source>.
|
||||||
|
# <source> is the exact link contents. No binding is done.
|
||||||
|
# <makeDefaultDependencies> If true, <target> will be made a dependency
|
||||||
|
# of the `all' pseudo target, i.e. it will be made by default, and removed
|
||||||
|
# on `jam clean'.
|
||||||
|
|
||||||
|
local target = $(1) ;
|
||||||
|
local source = $(2) ;
|
||||||
|
local makeDefaultDependencies = $(3) ;
|
||||||
|
if ! $(makeDefaultDependencies) {
|
||||||
|
makeDefaultDependencies = true ;
|
||||||
|
}
|
||||||
|
LINKCONTENTS on $(target) = $(source) ;
|
||||||
|
SymLink1 $(target) ;
|
||||||
|
if $(makeDefaultDependencies) = true {
|
||||||
|
LocalDepends files : $(target) ;
|
||||||
|
LocalClean clean : $(target) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actions SymLink1
|
||||||
|
{
|
||||||
|
$(RM) "$(1)" && $(LN) -s "$(LINKCONTENTS)" "$(1)"
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
# HelperRules
|
||||||
|
#
|
||||||
|
# Helper rules without side effects.
|
||||||
|
|
||||||
|
rule FFilter
|
||||||
|
{
|
||||||
|
# FFilter <list> : <excludes> ;
|
||||||
|
#
|
||||||
|
# Removes all occurrences of <excludes> in <list>.
|
||||||
|
|
||||||
|
local list = $(1) ;
|
||||||
|
local excludes = $(2) ;
|
||||||
|
local newList ;
|
||||||
|
local item ;
|
||||||
|
for item in $(list) {
|
||||||
|
local skip ;
|
||||||
|
local exclude ;
|
||||||
|
for exclude in $(excludes) {
|
||||||
|
if $(item) = $(exclude) {
|
||||||
|
skip = true ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ! $(skip) {
|
||||||
|
newList += $(item) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $(newList) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule FSplitPath
|
||||||
|
{
|
||||||
|
# FSplitPath <path> ;
|
||||||
|
#
|
||||||
|
# Decomposes a path into its components.
|
||||||
|
#
|
||||||
|
# <path>: The path to be decomposed.
|
||||||
|
#
|
||||||
|
local path = $(1:G=) ;
|
||||||
|
|
||||||
|
local components ;
|
||||||
|
# $(path:D) for "/" is "/". Therefore the second condition.
|
||||||
|
while $(path:D) && $(path:D) != $(path)
|
||||||
|
{
|
||||||
|
# Note: $(path:B) returns "." for "..", but $(path:D=) is fine.
|
||||||
|
components = $(path:D=) $(components) ;
|
||||||
|
path = $(path:D) ;
|
||||||
|
}
|
||||||
|
components = $(components) ;
|
||||||
|
# Use this to return initial /
|
||||||
|
#components = $(path) $(components) ;
|
||||||
|
return $(components) ;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
# InstallRules
|
||||||
|
#
|
||||||
|
# Missing rules for installation.
|
||||||
|
|
||||||
|
rule InstallSymLink
|
||||||
|
{
|
||||||
|
# InstallSymlink linkname : source ;
|
||||||
|
|
||||||
|
LocalDepends install : $(>) ;
|
||||||
|
LocalDepends install : $(<) ;
|
||||||
|
LocalClean uninstall : $(<) ;
|
||||||
|
|
||||||
|
NoCare $(>) ;
|
||||||
|
InstallSymLink1 $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions InstallSymLink1
|
||||||
|
{
|
||||||
|
$(RM) $(<) && $(LN) -s $(>) $(<)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule UninstallTarget
|
||||||
|
{
|
||||||
|
# UninstallTarget target ;
|
||||||
|
|
||||||
|
LocalClean uninstall : $(<) ;
|
||||||
|
}
|
|
@ -0,0 +1,380 @@
|
||||||
|
# MainBuildRules
|
||||||
|
#
|
||||||
|
# Rules that specify what to build and how to do it.
|
||||||
|
|
||||||
|
rule AddResources
|
||||||
|
{
|
||||||
|
# AddResources <name> : <resourcefiles> ;
|
||||||
|
#
|
||||||
|
# Adds resources to the application.
|
||||||
|
#
|
||||||
|
# <name>: Name of the application.
|
||||||
|
# <resourcefiles>: List of resource files. Grist will be set.
|
||||||
|
#
|
||||||
|
local resfiles ;
|
||||||
|
local file ;
|
||||||
|
for file in $(2) {
|
||||||
|
if ! $(file:G) {
|
||||||
|
file = [ FGristFiles $(file) ] ;
|
||||||
|
}
|
||||||
|
resfiles += $(file) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEARCH on $(resfile) += $(SEARCH_SOURCE) ;
|
||||||
|
|
||||||
|
for file in $(resfiles) {
|
||||||
|
if $(file:S) = .rdef {
|
||||||
|
local rdef = $(file) ;
|
||||||
|
file = $(rdef:S=.rsrc) ;
|
||||||
|
ResComp $(file) : $(rdef) ;
|
||||||
|
}
|
||||||
|
RESFILES on $(1) += $(file) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Application
|
||||||
|
{
|
||||||
|
# Application <name> : <sources> : <libraries> : <res> ;
|
||||||
|
#
|
||||||
|
# Creates an application from sources.
|
||||||
|
#
|
||||||
|
# <name>: Name of the application. Grist is allowed.
|
||||||
|
# <sources>: List of source files. Grist will be set.
|
||||||
|
# <libraries>: List of libraries to link against.
|
||||||
|
# <res>: List of resource files. Grist will be set.
|
||||||
|
#
|
||||||
|
local app = $(1) ;
|
||||||
|
local sources = $(2) ;
|
||||||
|
local libs = $(3) ;
|
||||||
|
local res = $(4) ;
|
||||||
|
|
||||||
|
AddResources $(app) : $(res) ;
|
||||||
|
Main $(app) : $(sources) ;
|
||||||
|
MakeLocate $(app) : $(LOCATE_MAIN_TARGET) ;
|
||||||
|
LinkAgainst $(app) : $(libs) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions Strip
|
||||||
|
{
|
||||||
|
strip "$(1)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule AddOn
|
||||||
|
{
|
||||||
|
# AddOn <name> : <sources> : <libraries> : <res> ;
|
||||||
|
#
|
||||||
|
# Creates an add-on from sources.
|
||||||
|
#
|
||||||
|
# <name>: Name of the add-on. Grist is allowed.
|
||||||
|
# <sources>: List of source files. Grist will be set.
|
||||||
|
# <libraries>: List of libraries to link against.
|
||||||
|
# <res>: List of resource files. Grist will be set.
|
||||||
|
#
|
||||||
|
SharedLibrary $(1) : $(2) : $(3) : $(4) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule SharedLibrary
|
||||||
|
{
|
||||||
|
# SharedLibrary <name> : <sources> : <libraries> : <res> ;
|
||||||
|
#
|
||||||
|
# Creates a shared library from sources.
|
||||||
|
#
|
||||||
|
# <name>: Name of the shared library. Grist is allowed.
|
||||||
|
# <sources>: List of source files. Grist will be set.
|
||||||
|
# <libraries>: List of libraries to link against.
|
||||||
|
# <res>: List of resource files. Grist will be set.
|
||||||
|
#
|
||||||
|
local lib = $(1) ;
|
||||||
|
local sources = $(2) ;
|
||||||
|
local libs = $(3) ;
|
||||||
|
local res = $(4) ;
|
||||||
|
|
||||||
|
AddResources $(lib) : $(res) ;
|
||||||
|
Main $(lib) : $(sources) ;
|
||||||
|
MakeLocate $(lib) : $(LOCATE_MAIN_TARGET) ;
|
||||||
|
local linkFlags ;
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
linkFlags = -nostart -Xlinker -soname=\"$(lib)\" -Xlinker --no-undefined ;
|
||||||
|
} else {
|
||||||
|
linkFlags = -xms ;
|
||||||
|
}
|
||||||
|
LINKFLAGS on $(lib) = [ on $(lib) return $(LINKFLAGS) ] $(linkFlags) ;
|
||||||
|
LinkAgainst $(lib) : $(libs) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule StaticLibrary
|
||||||
|
{
|
||||||
|
# StaticLibrary <name> : <sources> ;
|
||||||
|
#
|
||||||
|
# Creates a static library from sources.
|
||||||
|
#
|
||||||
|
# <name>: Name of the static library. Grist is allowed.
|
||||||
|
# <source>: List of source files. Grist will be set.
|
||||||
|
#
|
||||||
|
local lib = $(1) ;
|
||||||
|
Library $(lib) : $(2) ;
|
||||||
|
MakeLocate $(lib) : $(LOCATE_MAIN_TARGET) ;
|
||||||
|
|
||||||
|
# If KEEPOBJS is set, Library doesn't make the library depend on `lib'.
|
||||||
|
if $(KEEPOBJS) {
|
||||||
|
Depends lib : $(lib) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule LinkAgainst
|
||||||
|
{
|
||||||
|
# LinkAgainst <name> : <libs> ;
|
||||||
|
#
|
||||||
|
# Adds libraries to the list of libraries a (Main) target shall be linked
|
||||||
|
# against.
|
||||||
|
#
|
||||||
|
# <name>: The name of the target for which to add libraries.
|
||||||
|
# <libs>: The libraries (actually arbitrary shared objects and static
|
||||||
|
# libraries) to be added. Valid elements are e.g. "be" or
|
||||||
|
# "libopenbeos.so" or "/boot/.../libfoo.so". If the basename starts
|
||||||
|
# with "lib" or the thingy has a dirname or grist, it is added to
|
||||||
|
# the NEEDLIBS variable (i.e. the file will be bound!), otherwise
|
||||||
|
# it is prefixed "-l" and added to LINKLIBS. If you want to specify
|
||||||
|
# a target that isn't a library and also has neither grist nor a
|
||||||
|
# dirname, you can prepend "<nogrist>" as grist; it will be
|
||||||
|
# stripped by this rule.
|
||||||
|
#
|
||||||
|
for i in $(>)
|
||||||
|
{
|
||||||
|
local isfile = ;
|
||||||
|
if $(i:D) || $(i:G) {
|
||||||
|
isfile = true ;
|
||||||
|
if $(i:G) = <nogrist> {
|
||||||
|
i = $(i:G=) ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch $(i:B)
|
||||||
|
{
|
||||||
|
# XXX: _APP_ and _KERNEL_ should not be needed for ELF.
|
||||||
|
case _APP_ : isfile = true ;
|
||||||
|
case _KERNEL_ : isfile = true ;
|
||||||
|
case lib* : isfile = true ;
|
||||||
|
case * : isfile = ;
|
||||||
|
}
|
||||||
|
if ! $(isfile) && ( $(i:S) = .so || $(i:S) = .o || $(i:S) = .a ) {
|
||||||
|
isfile = true ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if $(isfile) {
|
||||||
|
NEEDLIBS on $(1) = [ on $(1) return $(NEEDLIBS) ] $(i) ;
|
||||||
|
Depends $(1) : $(i) ;
|
||||||
|
} else {
|
||||||
|
LINKLIBS on $(1) = [ on $(1) return $(LINKLIBS) ] -l$(i) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule XRes
|
||||||
|
{
|
||||||
|
# XRes <target> : <resource files> ;
|
||||||
|
#
|
||||||
|
# Adds resources to a file.
|
||||||
|
#
|
||||||
|
# <target>: The files to which resources shall be added.
|
||||||
|
# <resource files>: The resource files.
|
||||||
|
#
|
||||||
|
if $(2)
|
||||||
|
{
|
||||||
|
Depends $(1) : $(2) ;
|
||||||
|
XRes1 $(1) : $(2) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule ResComp
|
||||||
|
{
|
||||||
|
# ResComp <resource file> : <rdef file> ;
|
||||||
|
#
|
||||||
|
# Creates a binary resource file from a rdef script.
|
||||||
|
#
|
||||||
|
# <resource file>: The resource file. Grist is required.
|
||||||
|
# <rdef file>: The rdef script. Grist is required.
|
||||||
|
#
|
||||||
|
local defines ;
|
||||||
|
|
||||||
|
on $(1) {
|
||||||
|
defines = $(DEFINES) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINES on $(1) = $(defines) ;
|
||||||
|
CCDEFS on $(1) = [ FDefines $(defines) ] ;
|
||||||
|
HDRS on $(1) = [ on $(1) FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ]
|
||||||
|
$(HDRS_INCLUDES_SEPARATOR) ;
|
||||||
|
RCHDRS on $(1) = [ FRcIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ;
|
||||||
|
|
||||||
|
SEARCH on $(2) += $(SEARCH_SOURCE) ;
|
||||||
|
MakeLocate $(1) : $(LOCATE_TARGET) ;
|
||||||
|
Depends $(1) : $(2) $(RC) ;
|
||||||
|
LocalClean clean : $(1) ;
|
||||||
|
ResComp1 $(1) : $(RC) $(2) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: We pipe the input files into the preprocessor, since *.rdef files are
|
||||||
|
# considered linker scripts, and thus we can use preprocessor features.
|
||||||
|
actions ResComp1
|
||||||
|
{
|
||||||
|
cat "$(2[2-])" | $(CC) -E $(CCDEFS) $(HDRS) - | egrep -v '^#' | $(2[1]) $(RCHDRS) --auto-names -o "$(1)" -
|
||||||
|
}
|
||||||
|
|
||||||
|
actions XRes1
|
||||||
|
{
|
||||||
|
xres -o "$(1)" "$(2)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions MimeSet
|
||||||
|
{
|
||||||
|
mimeset -f "$(1)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule LexC++
|
||||||
|
{
|
||||||
|
Depends $(1) : $(2) ;
|
||||||
|
MakeLocate $(1) : $(LOCATE_SOURCE) ;
|
||||||
|
Clean clean : $(1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions LexC++
|
||||||
|
{
|
||||||
|
$(LEX) -i -P$(<:B) -o$(1) $(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Bison
|
||||||
|
{
|
||||||
|
local _h ;
|
||||||
|
|
||||||
|
_h = $(1:S=.h) ;
|
||||||
|
|
||||||
|
MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ;
|
||||||
|
|
||||||
|
Depends $(<) : $(>) ;
|
||||||
|
BisonC++ $(<) : $(>) ;
|
||||||
|
Clean clean : $(<) $(_h) ;
|
||||||
|
|
||||||
|
# make sure someone includes $(_h) else it will be
|
||||||
|
# a deadly independent target
|
||||||
|
|
||||||
|
Includes $(<) : $(_h) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions BisonC++
|
||||||
|
{
|
||||||
|
$(BISON) -v -d -p $(2:B) -o $(1) $(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Rez
|
||||||
|
{
|
||||||
|
Depends $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PreCompile
|
||||||
|
{
|
||||||
|
# PreCompile <hdr> : <src>
|
||||||
|
#
|
||||||
|
# precompiles the given src (a headerfile) into the specified header.
|
||||||
|
#
|
||||||
|
local _hdr = $(1) ;
|
||||||
|
local _src = $(2) ;
|
||||||
|
MakeLocate $(_hdr) : $(LOCATE_TARGET) ;
|
||||||
|
PreComp $(_hdr) : $(_src) ;
|
||||||
|
Clean clean : $(_hdr) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PreComp
|
||||||
|
{
|
||||||
|
Depends $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions PreComp
|
||||||
|
{
|
||||||
|
mwcc -precompile $(<) -lang cplus "$(>)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule SubDirSysHdrs
|
||||||
|
{
|
||||||
|
# SubDirSysHdrs <dirs> ;
|
||||||
|
#
|
||||||
|
# Adds directories to the system include search paths for the current
|
||||||
|
# subdirectory. Counterpart of SubDirHdrs which adds non-system include
|
||||||
|
# search paths.
|
||||||
|
#
|
||||||
|
# <dirs>: The directories to be added to the current subdir's system
|
||||||
|
# include search paths.
|
||||||
|
#
|
||||||
|
SUBDIRSYSHDRS += [ FDirName $(1) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule ObjectSysHdrs
|
||||||
|
{
|
||||||
|
# SubDirSysHdrs <sources or objects> : <dirs> ;
|
||||||
|
#
|
||||||
|
# Adds directories to the system include search paths for the given
|
||||||
|
# sources or objects. Counterpart of ObjectHdrs which adds non-system
|
||||||
|
# include search paths.
|
||||||
|
#
|
||||||
|
# NOTE: This rule must be invoked *after* the rule that generates the
|
||||||
|
# objects.
|
||||||
|
#
|
||||||
|
# <sources or objects>: The targets for which to add system include
|
||||||
|
# search paths.
|
||||||
|
# <dirs>: The directories to be added to the given objects' system
|
||||||
|
# include search paths.
|
||||||
|
#
|
||||||
|
|
||||||
|
local s ;
|
||||||
|
for s in [ FGristFiles $(<:S=$(SUFOBJ)) ] {
|
||||||
|
SYSHDRS on $(s) += $(>) ;
|
||||||
|
CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ]
|
||||||
|
$(HDRS_INCLUDES_SEPARATOR) [ on $(s) FSysIncludes $(SYSHDRS) ] ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# FSysIncludes <dirs> ;
|
||||||
|
#
|
||||||
|
# Counterpart of FIncludes for system include search paths.
|
||||||
|
#
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
rule FSysIncludes { return -I$(<) ; }
|
||||||
|
} else {
|
||||||
|
rule FSysIncludes { return "-i "$(<) ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# FRcIncludes <dirs> ;
|
||||||
|
#
|
||||||
|
# Counterpart of FIncludes for *.rdef scripts.
|
||||||
|
#
|
||||||
|
rule FRcIncludes
|
||||||
|
{
|
||||||
|
return "-I "$(<) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Variable referring to the STL.
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
if $(IS_GCC4_PLATFORM) = 1 {
|
||||||
|
TARGET_LIBSTDC++ = stdc++ ;
|
||||||
|
} else {
|
||||||
|
TARGET_LIBSTDC++ = stdc++.r4 ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TARGET_LIBSTDC++ = mslcpp_4_0 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rule CreateSVNRevisionFile file
|
||||||
|
{
|
||||||
|
# CreateSVNRevisionFile <file>
|
||||||
|
|
||||||
|
local svnEntries = <svn>entries ;
|
||||||
|
SEARCH on $(svnEntries) = [ FDirName $(TOP) .svn ] ;
|
||||||
|
Depends $(file) : $(svnEntries) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions CreateSVNRevisionFile
|
||||||
|
{
|
||||||
|
(LANG=C svn info $(TOP) 2> /dev/null || echo Revision: 0) |
|
||||||
|
grep Revision | awk '{printf $2}' > $(1)
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
# OverriddenJamRules
|
||||||
|
#
|
||||||
|
# Jam rules that need to be overridden for good reasons.
|
||||||
|
|
||||||
|
# Overridden to allow for spaces in file names and to support resources.
|
||||||
|
# Also set the on target LINKFLAGS variable to prevent later changes to
|
||||||
|
# the global variable from having an effect on the setting for the target.
|
||||||
|
rule Link
|
||||||
|
{
|
||||||
|
local dbg = [ on $(1) return $(DEBUG) ] ;
|
||||||
|
if $(STRIP_APPS) && $(STRIP_APPS) != 0 && (!$(dbg) || $(dbg) = 0) {
|
||||||
|
# strip app if requested so and if not in debug mode!
|
||||||
|
Strip $(1) ;
|
||||||
|
}
|
||||||
|
# Note: RESFILES must be set before invocation.
|
||||||
|
MODE on $(1) = $(EXEMODE) ;
|
||||||
|
on $(1) XRes $(1) : $(RESFILES) ;
|
||||||
|
Chmod $(1) ;
|
||||||
|
MimeSet $(1) ;
|
||||||
|
LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions Link bind NEEDLIBS
|
||||||
|
{
|
||||||
|
$(LINK) $(LINKFLAGS) -o "$(1)" $(UNDEFS) "$(2)" "$(NEEDLIBS)" $(LINKLIBS)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Overridden to allow for spaces in file names.
|
||||||
|
actions Chmod1
|
||||||
|
{
|
||||||
|
$(CHMOD) "$(MODE)" "$(1)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Overridden to allow for spaces in file names.
|
||||||
|
actions piecemeal together existing Clean
|
||||||
|
{
|
||||||
|
$(RM) -rf "$(>)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Changes to rules for sake of discrimination between system and non-system
|
||||||
|
# headers.
|
||||||
|
|
||||||
|
if $(OSPLAT) = X86 {
|
||||||
|
if $(IS_GCC4_PLATFORM) = 1 {
|
||||||
|
HDRS_INCLUDES_SEPARATOR = -iquote- ;
|
||||||
|
} else {
|
||||||
|
HDRS_INCLUDES_SEPARATOR = -I- ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HDRS_INCLUDES_SEPARATOR = -i- ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Cc
|
||||||
|
{
|
||||||
|
Depends $(<) : $(>) ;
|
||||||
|
|
||||||
|
# If the compiler's -o flag doesn't work, relocate the .o
|
||||||
|
|
||||||
|
if $(RELOCATE)
|
||||||
|
{
|
||||||
|
CcMv $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Just to clarify here: this sets the per-target CCFLAGS to
|
||||||
|
# be the current value of (global) CCFLAGS and SUBDIRCCFLAGS.
|
||||||
|
# CCHDRS and CCDEFS must be reformatted each time for some
|
||||||
|
# compiles (VMS, NT) that malign multiple -D or -I flags.
|
||||||
|
|
||||||
|
CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) $(OPTIM) ;
|
||||||
|
|
||||||
|
CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ]
|
||||||
|
$(HDRS_INCLUDES_SEPARATOR) [ on $(<) FSysIncludes $(SYSHDRS) ] ;
|
||||||
|
CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule C++
|
||||||
|
{
|
||||||
|
Depends $(<) : $(>) ;
|
||||||
|
|
||||||
|
if $(RELOCATE)
|
||||||
|
{
|
||||||
|
CcMv $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) $(OPTIM) ;
|
||||||
|
|
||||||
|
CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ]
|
||||||
|
$(HDRS_INCLUDES_SEPARATOR) [ on $(<) FSysIncludes $(SYSHDRS) ] ;
|
||||||
|
CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Object
|
||||||
|
{
|
||||||
|
# locate object and search for source, if wanted
|
||||||
|
|
||||||
|
Clean clean : $(<) ;
|
||||||
|
|
||||||
|
MakeLocate $(<) : $(LOCATE_TARGET) ;
|
||||||
|
SEARCH on $(>) = $(SEARCH_SOURCE) ;
|
||||||
|
|
||||||
|
# Save HDRS for -I$(HDRS) on compile.
|
||||||
|
# We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers
|
||||||
|
# in the .c file's directory, but generated .c files (from
|
||||||
|
# yacc, lex, etc) are located in $(LOCATE_TARGET), possibly
|
||||||
|
# different from $(SEARCH_SOURCE).
|
||||||
|
|
||||||
|
HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ;
|
||||||
|
SYSHDRS on $(<) = $(SUBDIRSYSHDRS) $(SYSHDRS) ;
|
||||||
|
|
||||||
|
# handle #includes for source: Jam scans for headers with
|
||||||
|
# the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE)
|
||||||
|
# with the scanned file as the target and the found headers
|
||||||
|
# as the sources. HDRSEARCH is the value of SEARCH used for
|
||||||
|
# the found header files. Finally, if jam must deal with
|
||||||
|
# header files of the same name in different directories,
|
||||||
|
# they can be distinguished with HDRGRIST.
|
||||||
|
|
||||||
|
# $(SEARCH_SOURCE:E) is where cc first looks for #include
|
||||||
|
# "foo.h" files. If the source file is in a distant directory,
|
||||||
|
# look there. Else, look in "" (the current directory).
|
||||||
|
|
||||||
|
HDRRULE on $(>) = HdrRule ;
|
||||||
|
HDRSCAN on $(>) = $(HDRPATTERN) ;
|
||||||
|
HDRSEARCH on $(>) =
|
||||||
|
$(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(SYSHDRS) $(STDHDRS) ;
|
||||||
|
|
||||||
|
HDRGRIST on $(>) = $(HDRGRIST) ;
|
||||||
|
|
||||||
|
# propagate target specific-defines
|
||||||
|
|
||||||
|
DEFINES on $(<) += $(DEFINES) ;
|
||||||
|
|
||||||
|
# if source is not .c, generate .c with specific rule
|
||||||
|
|
||||||
|
switch $(>:S)
|
||||||
|
{
|
||||||
|
case .asm : As $(<) : $(>) ;
|
||||||
|
case .c : Cc $(<) : $(>) ;
|
||||||
|
case .C : C++ $(<) : $(>) ;
|
||||||
|
case .cc : C++ $(<) : $(>) ;
|
||||||
|
case .cpp : C++ $(<) : $(>) ;
|
||||||
|
case .f : Fortran $(<) : $(>) ;
|
||||||
|
case .l : Cc $(<) : $(<:S=.c) ;
|
||||||
|
LexC++ $(<:S=.c) : $(>) ;
|
||||||
|
case .s : As $(<) : $(>) ;
|
||||||
|
case .y : Cc $(<) : $(<:S=.c) ;
|
||||||
|
Bison $(<:S=.c) : $(>) ;
|
||||||
|
case * : UserObject $(<) : $(>) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule ObjectHdrs
|
||||||
|
{
|
||||||
|
local s ;
|
||||||
|
for s in [ FGristFiles $(<:S=$(SUFOBJ)) ] {
|
||||||
|
HDRS on $(s) += $(>) ;
|
||||||
|
CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ]
|
||||||
|
$(HDRS_INCLUDES_SEPARATOR) [ on $(s) FSysIncludes $(SYSHDRS) ] ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Override Jam 2.5rc3 MakeLocate and MkDir to deal more intelligently
|
||||||
|
# with grist set on the supplied directory name.
|
||||||
|
rule MakeLocate
|
||||||
|
{
|
||||||
|
if $(2[1])
|
||||||
|
{
|
||||||
|
local dir = $(2[1]) ;
|
||||||
|
if ! $(dir:G) {
|
||||||
|
dir = $(dir:G=dir) ;
|
||||||
|
}
|
||||||
|
LOCATE on $(1) = $(dir:G=) ;
|
||||||
|
Depends $(1) : $(dir) ;
|
||||||
|
MkDir $(dir) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule MkDir
|
||||||
|
{
|
||||||
|
# If dir exists, don't update it
|
||||||
|
# Do this even for $(DOT).
|
||||||
|
|
||||||
|
local dir = $(<) ;
|
||||||
|
if ! $(dir:G) {
|
||||||
|
dir = $(dir:G=dir) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoUpdate $(dir) ;
|
||||||
|
|
||||||
|
if $(dir:G=) != $(DOT) && ! $($(dir:G=)-mkdir) {
|
||||||
|
local s ;
|
||||||
|
|
||||||
|
# Cheesy gate to prevent multiple invocations on same dir
|
||||||
|
# MkDir1 has the actions
|
||||||
|
# Arrange for jam dirs
|
||||||
|
|
||||||
|
$(dir:G=)-mkdir = true ;
|
||||||
|
MkDir1 $(dir) ;
|
||||||
|
Depends dirs : $(dir) ;
|
||||||
|
|
||||||
|
# Recursively make parent directories.
|
||||||
|
# $(dir:P) = $(dir)'s parent, & we recurse until root
|
||||||
|
|
||||||
|
s = $(dir:P) ; # parent keeps grist
|
||||||
|
|
||||||
|
if $(s:G=) && $(s) != $(dir) {
|
||||||
|
Depends $(dir) : $(s) ;
|
||||||
|
MkDir $(s) ;
|
||||||
|
} else if $(s) {
|
||||||
|
NotFile $(s) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add SUBDIRSYSHDRS to the variables that shall be reset automatically by the
|
||||||
|
# SubDir rule.
|
||||||
|
SUBDIRRESET += SYSHDRS ;
|
|
@ -0,0 +1,247 @@
|
||||||
|
# PackageRules
|
||||||
|
#
|
||||||
|
# Rules to create archives for binary distribution
|
||||||
|
|
||||||
|
rule Copy
|
||||||
|
{
|
||||||
|
if $(2) {
|
||||||
|
SEARCH on $(2) += $(SEARCH_SOURCE) ;
|
||||||
|
Depends $(1) : $(COPYATTR) $(2) ;
|
||||||
|
Copy1 $(1) : $(COPYATTR) $(2) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actions Copy1
|
||||||
|
{
|
||||||
|
"$(2[1])" -d "$(2[2-])" "$(1)"
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Packages
|
||||||
|
{
|
||||||
|
local packagenames = $(1) ;
|
||||||
|
local packagefiles = $(2) ;
|
||||||
|
local path = $(3) ;
|
||||||
|
for name in $(packagenames) {
|
||||||
|
Package $(name) : $(packagefiles) : $(path) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rule FPackageConfigSubPath
|
||||||
|
{
|
||||||
|
# FPackageConfigSubPath <packagename>
|
||||||
|
#
|
||||||
|
local packagename = $(1) ;
|
||||||
|
|
||||||
|
local configSubPath ;
|
||||||
|
on $(packagename) {
|
||||||
|
configSubPath = $(OS:L) $(OSPLAT:L) ;
|
||||||
|
|
||||||
|
if $(DEBUG) = 0 {
|
||||||
|
configSubPath += release ;
|
||||||
|
} else {
|
||||||
|
configSubPath += debug_$(DEBUG) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $(configSubPath) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule Package
|
||||||
|
{
|
||||||
|
local packagename = $(1) ;
|
||||||
|
local packagefiles = $(2) ;
|
||||||
|
local path = $(3) ;
|
||||||
|
|
||||||
|
local configSubPath = [ FPackageConfigSubPath $(packagename) ] ;
|
||||||
|
#local packagezip = $(packagename:S=.zip:G=_packages) ;
|
||||||
|
local packagezip = $(packagename:S=-$(VERSION).zip:G=_packages) ;
|
||||||
|
local targetDir = [ FDirName $(PACKAGE_DIR) $(configSubPath) ] ;
|
||||||
|
local packagedir = [ FDirName $(targetDir) $(packagename) ] ;
|
||||||
|
|
||||||
|
local installscript = install.sh ;
|
||||||
|
local packageinstallscript = $(installscript:G=_packages!$(packagename)) ;
|
||||||
|
local installzip = install.zip ;
|
||||||
|
local packageinstallzip = $(installzip:G=_packages!$(packagename)) ;
|
||||||
|
|
||||||
|
local packageobjectdir = [ FDirName $(PACKAGE_OBJECT_DIR)
|
||||||
|
$(configSubPath) $(packagename) ] ;
|
||||||
|
local packagefiledir = [ FDirName $(packageobjectdir) $(path) ] ;
|
||||||
|
local packagefileinstallzip
|
||||||
|
= $(installzip:G=_package_objects!$(packagename)) ;
|
||||||
|
|
||||||
|
# add the files to the install.zip
|
||||||
|
local packagefilegrist = [ FGrist _package_files $(packagename) $(path) ] ;
|
||||||
|
for file in $(packagefiles) {
|
||||||
|
if $(path[0]) = "boot" {
|
||||||
|
local packagefile = $(file:G=$(packagefilegrist)) ;
|
||||||
|
MakeLocate $(packagefile) : $(packagefiledir) ;
|
||||||
|
Copy $(packagefile) : $(file) ;
|
||||||
|
Clean cleanPackages : $(packagefile) ;
|
||||||
|
PackageInstallZip $(packagefileinstallzip) : $(packagefile) ;
|
||||||
|
} else {
|
||||||
|
local packagefile = $(file:G=_packages!$(packagename)) ;
|
||||||
|
MakeLocate $(packagefile) : $(packagedir) ;
|
||||||
|
Copy $(packagefile) : [ FGristFiles $(file) ] ;
|
||||||
|
Clean cleanPackages : $(packagefile) ;
|
||||||
|
Depends $(packagezip) : $(packagefile) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# general setup for this package -- only on first invocation
|
||||||
|
if ! $(_setup_$(packagename)) {
|
||||||
|
_setup_$(packagename) = true ;
|
||||||
|
|
||||||
|
NotFile $(packagename) ;
|
||||||
|
LocalDepends packages : $(packagename) ;
|
||||||
|
|
||||||
|
MakeLocate $(packagezip) : $(targetDir) ;
|
||||||
|
MakeLocate $(packageinstallscript) : $(packagedir) ;
|
||||||
|
MakeLocate $(packageinstallzip) : $(packagedir) ;
|
||||||
|
MakeLocate $(packagefileinstallzip) : $(packageobjectdir) ;
|
||||||
|
|
||||||
|
PackageInstallScript $(packageinstallscript) : $(packagedir) ;
|
||||||
|
LinkInstallZip $(packageinstallzip) : $(packagefileinstallzip) ;
|
||||||
|
Depends $(packagename) : $(packagezip) ;
|
||||||
|
PackageZip $(packagezip) : $(packagedir)
|
||||||
|
: $(packageinstallscript) $(packageinstallzip) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PackageSymLink
|
||||||
|
{
|
||||||
|
# PackageSymLink <packageName> : <symlink path components>
|
||||||
|
# : <symlink target>
|
||||||
|
#
|
||||||
|
local packagename = $(1) ;
|
||||||
|
local symlinkPath = $(2) ;
|
||||||
|
local symlinkTarget = $(3) ;
|
||||||
|
|
||||||
|
local configSubPath = [ FPackageConfigSubPath $(packagename) ] ;
|
||||||
|
|
||||||
|
local symlinkDir = [ FReverse $(symlinkPath) ] ;
|
||||||
|
local symlink = $(symlinkDir[1]) ;
|
||||||
|
symlinkDir = [ FReverse $(symlinkDir[2-]) ] ;
|
||||||
|
local symlinkGrist = [ FGrist _package $(packagename) $(symlinkDir) ] ;
|
||||||
|
symlink = $(symlink:G=$(symlinkGrist)) ;
|
||||||
|
|
||||||
|
if $(symlinkDir[1]) = boot {
|
||||||
|
local installzip = install.zip ;
|
||||||
|
local packagefileinstallzip
|
||||||
|
= $(installzip:G=_package_objects!$(packagename)) ;
|
||||||
|
|
||||||
|
local packageobjectdir = [ FDirName $(PACKAGE_OBJECT_DIR)
|
||||||
|
$(configSubPath) $(packagename) ] ;
|
||||||
|
symlinkDir = [ FDirName $(packageobjectdir) $(symlinkDir) ] ;
|
||||||
|
|
||||||
|
PackageInstallZip $(packagefileinstallzip) : $(symlink) ;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#local packagezip = $(packagename:S=.zip:G=_packages) ;
|
||||||
|
local packagezip = $(packagename:S=-$(VERSION).zip:G=_packages) ;
|
||||||
|
|
||||||
|
local packagedir = [ FDirName $(PACKAGE_DIR) $(configSubPath)
|
||||||
|
$(packagename) ] ;
|
||||||
|
symlinkDir = [ FDirName $(packagedir) $(symlinkDir) ] ;
|
||||||
|
|
||||||
|
Depends $(packagezip) : $(symlink) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeLocate $(symlink) : $(symlinkDir) ;
|
||||||
|
SymLink $(symlink) : $(symlinkTarget) : false ;
|
||||||
|
Clean cleanPackages : $(symlink) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PackageDriverSymLink
|
||||||
|
{
|
||||||
|
# PackageDriverSymLink <packageName> : <devRelativeSymlinkComponents> ;
|
||||||
|
# <packageName>: Package name.
|
||||||
|
# <devRelativeSymlinkComponents>: Path components relative to the
|
||||||
|
# /boot/home/config/add-ons/kernel/drivers/dev directory, e.g.
|
||||||
|
# "graphics mga.driver" (no quotation, of course).
|
||||||
|
#
|
||||||
|
local packageName = $(1) ;
|
||||||
|
local symlinkComponents = $(2) ;
|
||||||
|
|
||||||
|
# construct the symlink contents
|
||||||
|
local symlinkPath = [ FReverse $(symlinkComponents) ] ;
|
||||||
|
symlinkPath = bin $(symlinkPath[1]) ;
|
||||||
|
|
||||||
|
for i in $(symlinkComponents) {
|
||||||
|
symlinkPath = $(DOTDOT) $(symlinkPath) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageSymLink $(packageName)
|
||||||
|
: boot home config add-ons kernel drivers dev $(symlinkComponents)
|
||||||
|
: [ FDirName $(symlinkPath) ] ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PackageZip
|
||||||
|
{
|
||||||
|
local dir = $(2:G=dir) ;
|
||||||
|
Depends $(1) : $(dir) $(3) ;
|
||||||
|
Clean cleanPackages : $(1) ;
|
||||||
|
PackageZip1 $(1) : $(dir) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions together PackageZip1 {
|
||||||
|
cd "$(2:P)" ;
|
||||||
|
zip -rq "$(1:BS)" "$(2:BS)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PackageInstallScript
|
||||||
|
{
|
||||||
|
MakeLocate $(1) : $(2) ;
|
||||||
|
Clean cleanPackages : $(1) ;
|
||||||
|
PackageInstallScript1 $(1) : $(2:G=dir) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions together PackageInstallScript1
|
||||||
|
{
|
||||||
|
echo '#!/bin/sh
|
||||||
|
base=`dirname "$0"`
|
||||||
|
cd "$base"
|
||||||
|
if [ -n "$TTY" ]
|
||||||
|
then
|
||||||
|
unzip -d / install.zip
|
||||||
|
else
|
||||||
|
response=`alert "Would you like to automatically overwrite existing files, or receive a prompt?" "Overwrite" "Prompt"`
|
||||||
|
if [ $response == "Overwrite" ]
|
||||||
|
then
|
||||||
|
unzip -od / install.zip
|
||||||
|
alert "Finished installing" "Thanks"
|
||||||
|
else
|
||||||
|
if [ -e /boot/beos/apps/Terminal ]
|
||||||
|
then
|
||||||
|
terminal=/boot/beos/apps/Terminal
|
||||||
|
else
|
||||||
|
terminal=`query Terminal | head -1`
|
||||||
|
fi
|
||||||
|
$terminal -t "installer" /bin/sh "$0"
|
||||||
|
fi
|
||||||
|
fi' > "$(1)" ;
|
||||||
|
chmod 755 "$(1)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule PackageInstallZip
|
||||||
|
{
|
||||||
|
Depends $(1) : $(2) ;
|
||||||
|
Clean cleanPackages : $(1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions together PackageInstallZip
|
||||||
|
{
|
||||||
|
cd "$(1:P)" ;
|
||||||
|
zip -rqy "$(1:BS)" boot ;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule LinkInstallZip
|
||||||
|
{
|
||||||
|
Depends $(1) : $(2) ;
|
||||||
|
Clean cleanPackages : $(1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
actions together LinkInstallZip
|
||||||
|
{
|
||||||
|
ln -sf "`pwd`/$(2)" "$(1)" ;
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
# UserBuildConfig
|
||||||
|
#
|
||||||
|
# Sample of a UserBuildConfig file. It is a central place where the user can
|
||||||
|
# set variables affecting certain aspects of the build, such as debug,
|
||||||
|
# warnings and optimization settings.
|
||||||
|
#
|
||||||
|
# The following variables can be set:
|
||||||
|
#
|
||||||
|
# BUILD_TESTS - If not empty, all tests (TestBeam) will be build, too.
|
||||||
|
# Default is 0, i.e. no tests.
|
||||||
|
# CCFLAGS, C++FLAGS - Flags passed to the C/C++ compiler.
|
||||||
|
# DEBUG - If not empty, will turn on debugging, i.e. will
|
||||||
|
# add respective C/C++ compiler and linker flags and
|
||||||
|
# the CPP DEBUG macro.
|
||||||
|
# DEFINES - CPP macros to be defined, e.g. something like
|
||||||
|
# `SPECIAL_FEATURE' or `CACHE_SIZE=1024'.
|
||||||
|
# HDRS - List of directories to be added to the local include
|
||||||
|
# search paths.
|
||||||
|
# LINKFLAGS - Flags passed to the linker.
|
||||||
|
# LOCATE_MAIN_TARGET - Directory where the main targets (i.e. applications,
|
||||||
|
# libraries shall be placed). Should usually not be
|
||||||
|
# tampered with by the user.
|
||||||
|
# OPTIM - Optimization specific flags passed to the C/C++
|
||||||
|
# compiler. Usually you want to use OPTIMIZE instead.
|
||||||
|
# OPTIMIZE - If not set to `0', will turn on optimization, i.e.
|
||||||
|
# will set the respective C/C++ compiler flags
|
||||||
|
# (i.e. the OPTIM variable).
|
||||||
|
# STRIP_APPS - if not set to '0', will cause all generated apps to
|
||||||
|
# be stripped. Default is '0', i.e. no stripping
|
||||||
|
# SYSHDRS - List of directories to be added to the system include
|
||||||
|
# search paths.
|
||||||
|
# WARNINGS - If not set to `0', will turn on warnings, i.e. will
|
||||||
|
# set the respective C/C++ compiler flags.
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
|
||||||
|
# Globally turn off debugging:
|
||||||
|
#
|
||||||
|
# DEBUG = 0 ;
|
||||||
|
|
||||||
|
# Globally activate debugging:
|
||||||
|
#
|
||||||
|
# DEBUG = 1 ;
|
||||||
|
|
||||||
|
# ... e.g. like this, for the `add-ons/catalogs' directory and all its
|
||||||
|
# subdirectories.
|
||||||
|
#
|
||||||
|
# SetConfigVar WARNINGS : TOP add-ons catalogs : 1 ;
|
||||||
|
|
||||||
|
# Turn on debugging for the the directory `Languages' and all its subdirectories.
|
||||||
|
#
|
||||||
|
# SetConfigVar DEBUG : TOP Languages : 1 ;
|
||||||
|
|
||||||
|
# Turn off optimization for the `rez' directory and all its subdirectories.
|
||||||
|
#
|
||||||
|
# SetConfigVar OPTIMIZE : TOP rez : 0 ;
|
||||||
|
|
||||||
|
# Define the CPP macro INSANE_DEBUGGING_LEVEL to the value `100' in the
|
||||||
|
# `lpe' directory, but NOT in its subdirectories (note the use of the
|
||||||
|
# optional fourth parameter `local', which works for both SetConfigVar and
|
||||||
|
# AppendToConfigVar).
|
||||||
|
#
|
||||||
|
# AppendToConfigVar DEFINES : TOP lpe : INSANE_DEBUGGING_LEVEL=100 : local ;
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright 2009, Pier Luigi Fiorini.
|
||||||
|
# Distributed under the terms of the MIT License.
|
||||||
|
#
|
||||||
|
# Authors:
|
||||||
|
# Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
|
||||||
|
#
|
||||||
|
|
||||||
|
current_dir=`pwd`
|
||||||
|
defines=""
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
jambin=`which jam`
|
||||||
|
rcbin=`which rc`
|
||||||
|
xresbin=`which xres`
|
||||||
|
settypebin=`which settype`
|
||||||
|
mimesetbin=`which mimeset`
|
||||||
|
setversionbin=`which setversion`
|
||||||
|
copyattrbin=`which copyattr`
|
||||||
|
|
||||||
|
# Check operating system
|
||||||
|
platform=`uname -s`
|
||||||
|
release=`uname -r`
|
||||||
|
echo -n "Checking operating system... "
|
||||||
|
case "$platform" in
|
||||||
|
BeOS)
|
||||||
|
case "$release" in
|
||||||
|
4.*)
|
||||||
|
echo "*** BeOS R4 is not supported!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
5.*)
|
||||||
|
echo "*** BeOS R5 is not supported!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
6.*)
|
||||||
|
echo "*** Zeta is not supported!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "*** Unsupported BeOS platform!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
Haiku)
|
||||||
|
defines="HAIKU_TARGET_PLATFORM_HAIKU=1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "*** Unsupported $platform operating system!"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo "$platform $release"
|
||||||
|
|
||||||
|
# Check whether jam exists
|
||||||
|
echo -n "Checking whether jam exists... "
|
||||||
|
if [ -z "$jambin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
echo "*** Caya requires jam, please read our Build.txt file."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for rc
|
||||||
|
echo -n "Checking for rc... "
|
||||||
|
if [ -z "$rcbin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $rcbin
|
||||||
|
|
||||||
|
# Check for xres
|
||||||
|
echo -n "Checking for xres..."
|
||||||
|
if [ -z "$xresbin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $xresbin
|
||||||
|
|
||||||
|
# Check for settype
|
||||||
|
echo -n "Checking for settype..."
|
||||||
|
if [ -z "$settypebin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $settypebin
|
||||||
|
|
||||||
|
# Check for mimeset
|
||||||
|
echo -n "Checking for mimeset..."
|
||||||
|
if [ -z "$mimesetbin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $mimesetbin
|
||||||
|
|
||||||
|
# Check for setverion
|
||||||
|
echo -n "Checking for setversion..."
|
||||||
|
if [ -z "$setversionbin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $setversionbin
|
||||||
|
|
||||||
|
# Check for copyattr
|
||||||
|
echo -n "Checking for copyattr..."
|
||||||
|
if [ -z "$copyattrbin" ]; then
|
||||||
|
echo "not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo $copyattrbin
|
||||||
|
|
||||||
|
# Create the build configuration
|
||||||
|
mkdir -p $current_dir/generated
|
||||||
|
cat > $current_dir/generated/BuildConfig << EOF
|
||||||
|
RC = ${rcbin} ;
|
||||||
|
XRES = ${xresbin} ;
|
||||||
|
SETTYPE = ${settypebin} ;
|
||||||
|
MIMESET = ${mimesetbin} ;
|
||||||
|
SETVERSION = ${setversionbin} ;
|
||||||
|
COPYATTR = ${copyattrbin} ;
|
||||||
|
|
||||||
|
COMMON_DIRECTORY = $(finddir B_COMMON_DIRECTORY) ;
|
||||||
|
COMMON_BIN_DIRECTORY = $(finddir B_COMMON_BIN_DIRECTORY) ;
|
||||||
|
COMMON_INCLUDE_DIRECTORY = $(finddir B_COMMON_DIRECTORY)/include ;
|
||||||
|
COMMON_LIB_DIRECTORY = $(finddir B_COMMON_LIB_DIRECTORY) ;
|
||||||
|
COMMON_SERVERS_DIRECTORY = $(finddir B_COMMON_SERVERS_DIRECTORY) ;
|
||||||
|
COMMON_ADDONS_DIRECTORY = $(finddir B_COMMON_ADDONS_DIRECTORY) ;
|
||||||
|
COMMON_DEVELOP_DIRECTORY = $(finddir B_COMMON_DEVELOP_DIRECTORY) ;
|
||||||
|
USER_CONFIG_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY) ;
|
||||||
|
USER_INCLUDE_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY)/include ;
|
||||||
|
SYSTEM_DIRECTORY = $(finddir B_SYSTEM_DIRECTORY) ;
|
||||||
|
SYSTEM_LIB_DIRECTORY = $(finddir B_SYSTEM_LIB_DIRECTORY) ;
|
||||||
|
BEOS_PREFERENCES_DIRECTORY = $(finddir B_BEOS_PREFERENCES_DIRECTORY) ;
|
||||||
|
PREFERENCES_DIRECTORY = $(finddir B_PREFERENCES_DIRECTORY) ;
|
||||||
|
USER_PREFERENCES_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY)/be/Preferences ;
|
||||||
|
APPS_DIRECTORY = $(finddir B_APPS_DIRECTORY) ;
|
||||||
|
CAYA_DIRECTORY = $(finddir B_APPS_DIRECTORY)/Caya ;
|
||||||
|
|
||||||
|
DEFINES += ${defines} ;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Configuration done."
|
|
@ -0,0 +1,4 @@
|
||||||
|
SubDir TOP libs ;
|
||||||
|
|
||||||
|
# Include all the components.
|
||||||
|
SubInclude TOP libs libgloox ;
|
|
@ -0,0 +1,106 @@
|
||||||
|
SubDir TOP libs libgloox ;
|
||||||
|
|
||||||
|
SubDirSysHdrs [ FDirName $(TOP) ] ;
|
||||||
|
SubDirSysHdrs [ FDirName $(TOP) libs ] ;
|
||||||
|
SubDirSysHdrs [ FDirName $(OPENSSL_INCLUDE_DIR) ] ;
|
||||||
|
|
||||||
|
SEARCH_SOURCE += [ FDirName $(TOP) libs ] ;
|
||||||
|
|
||||||
|
StaticLibrary libgloox.a :
|
||||||
|
adhoc.cpp
|
||||||
|
amp.cpp
|
||||||
|
annotations.cpp
|
||||||
|
attention.cpp
|
||||||
|
base64.cpp
|
||||||
|
bookmarkstorage.cpp
|
||||||
|
capabilities.cpp
|
||||||
|
chatstate.cpp
|
||||||
|
chatstatefilter.cpp
|
||||||
|
client.cpp
|
||||||
|
clientbase.cpp
|
||||||
|
component.cpp
|
||||||
|
compressiondefault.cpp
|
||||||
|
compressionzlib.cpp
|
||||||
|
connectionbosh.cpp
|
||||||
|
connectionhttpproxy.cpp
|
||||||
|
connectionsocks5proxy.cpp
|
||||||
|
connectiontcpbase.cpp
|
||||||
|
connectiontcpclient.cpp
|
||||||
|
connectiontcpserver.cpp
|
||||||
|
connectiontls.cpp
|
||||||
|
connectiontlsserver.cpp
|
||||||
|
dataform.cpp
|
||||||
|
dataformfield.cpp
|
||||||
|
dataformfieldcontainer.cpp
|
||||||
|
dataformitem.cpp
|
||||||
|
dataformreported.cpp
|
||||||
|
delayeddelivery.cpp
|
||||||
|
disco.cpp
|
||||||
|
dns.cpp
|
||||||
|
error.cpp
|
||||||
|
eventdispatcher.cpp
|
||||||
|
featureneg.cpp
|
||||||
|
flexoff.cpp
|
||||||
|
gloox.cpp
|
||||||
|
gpgencrypted.cpp
|
||||||
|
gpgsigned.cpp
|
||||||
|
inbandbytestream.cpp
|
||||||
|
instantmucroom.cpp
|
||||||
|
iq.cpp
|
||||||
|
jid.cpp
|
||||||
|
lastactivity.cpp
|
||||||
|
logsink.cpp
|
||||||
|
md5.cpp
|
||||||
|
message.cpp
|
||||||
|
messageevent.cpp
|
||||||
|
messageeventfilter.cpp
|
||||||
|
messagefilter.cpp
|
||||||
|
messagesession.cpp
|
||||||
|
mucmessagesession.cpp
|
||||||
|
mucroom.cpp
|
||||||
|
mutex.cpp
|
||||||
|
nickname.cpp
|
||||||
|
nonsaslauth.cpp
|
||||||
|
oob.cpp
|
||||||
|
parser.cpp
|
||||||
|
prep.cpp
|
||||||
|
presence.cpp
|
||||||
|
privacyitem.cpp
|
||||||
|
privacymanager.cpp
|
||||||
|
privatexml.cpp
|
||||||
|
pubsubevent.cpp
|
||||||
|
pubsubitem.cpp
|
||||||
|
pubsubmanager.cpp
|
||||||
|
receipt.cpp
|
||||||
|
registration.cpp
|
||||||
|
rosteritem.cpp
|
||||||
|
rostermanager.cpp
|
||||||
|
search.cpp
|
||||||
|
sha.cpp
|
||||||
|
shim.cpp
|
||||||
|
simanager.cpp
|
||||||
|
siprofileft.cpp
|
||||||
|
socks5bytestream.cpp
|
||||||
|
socks5bytestreammanager.cpp
|
||||||
|
socks5bytestreamserver.cpp
|
||||||
|
softwareversion.cpp
|
||||||
|
stanza.cpp
|
||||||
|
stanzaextensionfactory.cpp
|
||||||
|
subscription.cpp
|
||||||
|
tag.cpp
|
||||||
|
tlsdefault.cpp
|
||||||
|
tlsgnutlsbase.cpp
|
||||||
|
tlsgnutlsclient.cpp
|
||||||
|
tlsgnutlsclientanon.cpp
|
||||||
|
tlsgnutlsserveranon.cpp
|
||||||
|
tlsopensslbase.cpp
|
||||||
|
tlsopensslclient.cpp
|
||||||
|
tlsopensslserver.cpp
|
||||||
|
tlsschannel.cpp
|
||||||
|
uniquemucroom.cpp
|
||||||
|
util.cpp
|
||||||
|
vcard.cpp
|
||||||
|
vcardmanager.cpp
|
||||||
|
vcardupdate.cpp
|
||||||
|
xhtmlim.cpp
|
||||||
|
;
|
|
@ -0,0 +1,472 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "adhoc.h"
|
||||||
|
#include "adhochandler.h"
|
||||||
|
#include "adhoccommandprovider.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* cmdActionStringValues[] =
|
||||||
|
{
|
||||||
|
"execute", "cancel", "prev", "next", "complete"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string actionString( Adhoc::Command::Action action )
|
||||||
|
{
|
||||||
|
return util::lookup2( action, cmdActionStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* cmdStatusStringValues[] =
|
||||||
|
{
|
||||||
|
"executing", "completed", "canceled"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string statusString( Adhoc::Command::Status status )
|
||||||
|
{
|
||||||
|
return util::lookup( status, cmdStatusStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* cmdNoteStringValues[] =
|
||||||
|
{
|
||||||
|
"info", "warn", "error"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const std::string noteString( Adhoc::Command::Note::Severity sev )
|
||||||
|
{
|
||||||
|
return util::lookup( sev, cmdNoteStringValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Adhoc::Command::Note ----
|
||||||
|
Adhoc::Command::Note::Note( const Tag* tag )
|
||||||
|
: m_severity( InvalidSeverity )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "note" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_severity = (Severity)util::deflookup( tag->findAttribute( "type" ), cmdNoteStringValues, Info );
|
||||||
|
m_note = tag->cdata();
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Adhoc::Command::Note::tag() const
|
||||||
|
{
|
||||||
|
if( m_note.empty() || m_severity == InvalidSeverity )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* n = new Tag( "note", m_note );
|
||||||
|
n->addAttribute( TYPE, noteString( m_severity ) );
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
// ---- ~Adhoc::Command::Note ----
|
||||||
|
|
||||||
|
// ---- Adhoc::Command ----
|
||||||
|
Adhoc::Command::Command( const std::string& node, Adhoc::Command::Action action,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_form( form ), m_action( action ),
|
||||||
|
m_status( InvalidStatus ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( InvalidAction ), m_status( status ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid,
|
||||||
|
Adhoc::Command::Action action,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( action ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
Action executeAction, int allowedActions,
|
||||||
|
DataForm* form )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ),
|
||||||
|
m_form( form ), m_action( executeAction ), m_status( status ), m_actions( allowedActions )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::Command( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtAdhocCommand ), m_form( 0 ), m_actions( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_sessionid = tag->findAttribute( "sessionid" );
|
||||||
|
m_status = (Status)util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues );
|
||||||
|
|
||||||
|
Tag* a = tag->findChild( "actions" );
|
||||||
|
if( a )
|
||||||
|
{
|
||||||
|
// Multi-stage response
|
||||||
|
m_action = (Action)util::deflookup2( a->findAttribute( "action" ), cmdActionStringValues, Complete );
|
||||||
|
if( a->hasChild( "prev" ) )
|
||||||
|
m_actions |= Previous;
|
||||||
|
if( a->hasChild( "next" ) )
|
||||||
|
m_actions |= Next;
|
||||||
|
if( a->hasChild( "complete" ) )
|
||||||
|
m_actions |= Complete;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_action = (Action)util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute );
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConstTagList& l = tag->findTagList( "/command/note" );
|
||||||
|
ConstTagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
m_notes.push_back( new Note( (*it) ) );
|
||||||
|
|
||||||
|
Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA );
|
||||||
|
if( x )
|
||||||
|
m_form = new DataForm( x );
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::Command::~Command()
|
||||||
|
{
|
||||||
|
util::clearList( m_notes );
|
||||||
|
delete m_form;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Adhoc::Command::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Adhoc::Command::tag() const
|
||||||
|
{
|
||||||
|
if( m_node.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* c = new Tag( "command" );
|
||||||
|
c->setXmlns( XMLNS_ADHOC_COMMANDS );
|
||||||
|
c->addAttribute( "node", m_node );
|
||||||
|
if( m_actions != 0 )
|
||||||
|
{
|
||||||
|
// Multi-stage command response
|
||||||
|
|
||||||
|
if( m_status != InvalidStatus )
|
||||||
|
c->addAttribute( "status", statusString( m_status ) );
|
||||||
|
else
|
||||||
|
c->addAttribute( "status", statusString( Executing ) );
|
||||||
|
|
||||||
|
Tag* actions = new Tag( c, "actions" );
|
||||||
|
|
||||||
|
if( m_action != InvalidAction )
|
||||||
|
c->addAttribute( "execute", actionString( m_action ) );
|
||||||
|
else
|
||||||
|
c->addAttribute( "execute", actionString( Complete ) );
|
||||||
|
|
||||||
|
if( ( m_actions & Previous ) == Previous )
|
||||||
|
new Tag( actions, "prev" );
|
||||||
|
if( ( m_actions & Next ) == Next )
|
||||||
|
new Tag( actions, "next" );
|
||||||
|
if( ( m_actions & Complete ) == Complete )
|
||||||
|
new Tag( actions, "complete" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Single-stage command request/response or Multi-stage command request
|
||||||
|
|
||||||
|
if( m_action != InvalidAction )
|
||||||
|
c->addAttribute( "action", actionString( m_action ) );
|
||||||
|
if( m_status != InvalidStatus )
|
||||||
|
c->addAttribute( "status", statusString( m_status ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !m_sessionid.empty() )
|
||||||
|
c->addAttribute( "sessionid", m_sessionid );
|
||||||
|
|
||||||
|
if( m_form && *m_form )
|
||||||
|
c->addChild( m_form->tag() );
|
||||||
|
|
||||||
|
NoteList::const_iterator it = m_notes.begin();
|
||||||
|
for( ; it != m_notes.end(); ++it )
|
||||||
|
c->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
// ---- ~Adhoc::Command ----
|
||||||
|
|
||||||
|
// ---- Adhoc ----
|
||||||
|
Adhoc::Adhoc( ClientBase* parent )
|
||||||
|
: m_parent( parent )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->registerNodeHandler( this, EmptyString );
|
||||||
|
m_parent->registerIqHandler( this, ExtAdhocCommand );
|
||||||
|
m_parent->registerStanzaExtension( new Adhoc::Command() );
|
||||||
|
}
|
||||||
|
|
||||||
|
Adhoc::~Adhoc()
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->removeFeature( XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS );
|
||||||
|
m_parent->disco()->removeNodeHandler( this, EmptyString );
|
||||||
|
m_parent->removeIqHandler( this, ExtAdhocCommand );
|
||||||
|
m_parent->removeIDHandler( this );
|
||||||
|
m_parent->removeStanzaExtension( ExtAdhocCommand );
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList Adhoc::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ )
|
||||||
|
{
|
||||||
|
StringList features;
|
||||||
|
features.push_back( XMLNS_ADHOC_COMMANDS );
|
||||||
|
return features;
|
||||||
|
// return StringList( 1, XMLNS_ADHOC_COMMANDS );
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& /*to*/, const std::string& node )
|
||||||
|
{
|
||||||
|
Disco::ItemList l;
|
||||||
|
if( node.empty() )
|
||||||
|
{
|
||||||
|
l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) );
|
||||||
|
}
|
||||||
|
else if( node == XMLNS_ADHOC_COMMANDS )
|
||||||
|
{
|
||||||
|
StringMap::const_iterator it = m_items.begin();
|
||||||
|
for( ; it != m_items.end(); ++it )
|
||||||
|
{
|
||||||
|
AdhocCommandProviderMap::const_iterator itp = m_adhocCommandProviders.find( (*it).first );
|
||||||
|
if( itp != m_adhocCommandProviders.end()
|
||||||
|
&& (*itp).second
|
||||||
|
&& (*itp).second->handleAdhocAccessRequest( from, (*it).first ) )
|
||||||
|
{
|
||||||
|
l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& /*from*/, const std::string& node )
|
||||||
|
{
|
||||||
|
Disco::IdentityList l;
|
||||||
|
StringMap::const_iterator it = m_items.find( node );
|
||||||
|
l.push_back( new Disco::Identity( "automation",
|
||||||
|
node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node",
|
||||||
|
it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) );
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Adhoc::handleIq( const IQ& iq )
|
||||||
|
{
|
||||||
|
if( iq.subtype() != IQ::Set )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
|
||||||
|
if( !ac || ac->node().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() );
|
||||||
|
if( it != m_adhocCommandProviders.end() )
|
||||||
|
{
|
||||||
|
const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID();
|
||||||
|
m_activeSessions[sess] = iq.id();
|
||||||
|
(*it).second->handleAdhocCommand( iq.from(), *ac, sess );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleIqID( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
if( context != ExecuteAdhocCommand )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() );
|
||||||
|
if( it == m_adhocTrackMap.end() || (*it).second.context != context
|
||||||
|
|| (*it).second.remote != iq.from() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Error:
|
||||||
|
(*it).second.ah->handleAdhocError( iq.from(), iq.error() );
|
||||||
|
break;
|
||||||
|
case IQ::Result:
|
||||||
|
{
|
||||||
|
const Adhoc::Command* ac = iq.findExtension<Adhoc::Command>( ExtAdhocCommand );
|
||||||
|
if( ac )
|
||||||
|
(*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
|
||||||
|
const std::string& name )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->registerNodeHandler( this, command );
|
||||||
|
m_adhocCommandProviders[command] = acp;
|
||||||
|
m_items[command] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context )
|
||||||
|
{
|
||||||
|
if( context != CheckAdhocSupport )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end() && (*it).second.context != context
|
||||||
|
&& (*it).second.remote != from; ++it )
|
||||||
|
;
|
||||||
|
if( it == m_adhocTrackMap.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ) );
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context )
|
||||||
|
{
|
||||||
|
if( context != FetchAdhocCommands )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).second.context == context && (*it).second.remote == from )
|
||||||
|
{
|
||||||
|
StringMap commands;
|
||||||
|
const Disco::ItemList& l = items.items();
|
||||||
|
Disco::ItemList::const_iterator it2 = l.begin();
|
||||||
|
for( ; it2 != l.end(); ++it2 )
|
||||||
|
{
|
||||||
|
commands[(*it2)->node()] = (*it2)->name();
|
||||||
|
}
|
||||||
|
(*it).second.ah->handleAdhocCommands( from, commands );
|
||||||
|
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::handleDiscoError( const JID& from, const Error* error, int context )
|
||||||
|
{
|
||||||
|
AdhocTrackMap::iterator it = m_adhocTrackMap.begin();
|
||||||
|
for( ; it != m_adhocTrackMap.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).second.context == context && (*it).second.remote == from )
|
||||||
|
{
|
||||||
|
(*it).second.ah->handleAdhocError( from, error );
|
||||||
|
|
||||||
|
m_adhocTrackMap.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !ah || !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = CheckAdhocSupport;
|
||||||
|
track.ah = ah;
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::getCommands( const JID& remote, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !ah || !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = FetchAdhocCommands;
|
||||||
|
track.ah = ah;
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah )
|
||||||
|
{
|
||||||
|
if( !remote || !command || !m_parent || !ah )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string& id = m_parent->getID();
|
||||||
|
IQ iq( IQ::Set, remote, id );
|
||||||
|
iq.addExtension( command );
|
||||||
|
|
||||||
|
TrackStruct track;
|
||||||
|
track.remote = remote;
|
||||||
|
track.context = ExecuteAdhocCommand;
|
||||||
|
track.session = command->sessionID();
|
||||||
|
track.ah = ah;
|
||||||
|
m_adhocTrackMap[id] = track;
|
||||||
|
|
||||||
|
m_parent->send( iq, this, ExecuteAdhocCommand );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error )
|
||||||
|
{
|
||||||
|
if( !remote || !command || !m_parent )
|
||||||
|
return;
|
||||||
|
|
||||||
|
StringMap::iterator it = m_activeSessions.find( command->sessionID() );
|
||||||
|
if( it == m_activeSessions.end() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second );
|
||||||
|
re.addExtension( command );
|
||||||
|
if( error )
|
||||||
|
re.addExtension( error );
|
||||||
|
m_parent->send( re );
|
||||||
|
m_activeSessions.erase( it );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Adhoc::removeAdhocCommandProvider( const std::string& command )
|
||||||
|
{
|
||||||
|
if( !m_parent || !m_parent->disco() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_parent->disco()->removeNodeHandler( this, command );
|
||||||
|
m_adhocCommandProviders.erase( command );
|
||||||
|
m_items.erase( command );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOC_H__
|
||||||
|
#define ADHOC_H__
|
||||||
|
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "iqhandler.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClientBase;
|
||||||
|
class Stanza;
|
||||||
|
class AdhocHandler;
|
||||||
|
class AdhocCommandProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements a provider for XEP-0050 (Ad-hoc Commands).
|
||||||
|
*
|
||||||
|
* The current, not complete, implementation is probably best suited for fire-and-forget
|
||||||
|
* type of commands. Any additional feature, like multiple stages, etc., would have to be
|
||||||
|
* added separately.
|
||||||
|
*
|
||||||
|
* To offer commands to remote entities, use this class as follows:<br>
|
||||||
|
* Create a class that will handle command execution requests and derive it from
|
||||||
|
* AdhocCommandProvider. Instantiate an Adhoc object and register your
|
||||||
|
* AdhocCommandProvider-derived object with the Adhoc object using
|
||||||
|
* registerAdhocCommandProvider(). The additional parameters to that method are the internal
|
||||||
|
* name of the command as used in the code, and the public name of the command as it
|
||||||
|
* will be shown to an end user:
|
||||||
|
* @code
|
||||||
|
* MyClass::someFunc()
|
||||||
|
* {
|
||||||
|
* Adhoc* m_adhoc = new Adhoc( m_client );
|
||||||
|
*
|
||||||
|
* // this might be a bot monitoring a weather station, for example
|
||||||
|
* m_adhoc->registerAdhocCommandProvider( this, "getTemp", "Retrieve current temperature" );
|
||||||
|
* m_adhoc->registerAdhocCommandProvider( this, "getPressure", "Retrieve current air pressure" );
|
||||||
|
* [...]
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* In this example, MyClass is AdhocCommandProvider-derived so it is obviously the command handler, too.
|
||||||
|
*
|
||||||
|
* And that's about it you can do with the Adhoc class. Of course you can have a AdhocCommandProvider
|
||||||
|
* handle more than one command, just register it with the Adhoc object for every desired command,
|
||||||
|
* like shown above.
|
||||||
|
*
|
||||||
|
* What the Adhoc object does when you install a new command is tell the supplied Disco object
|
||||||
|
* to advertise these commands to clients using the 'Service Discovery' protocol to learn about
|
||||||
|
* this implementation's features. These clients can then call and execute the command. Of course you
|
||||||
|
* are free to implement access restrictions to not let anyone mess with your bot, for example.
|
||||||
|
* However, the commands offered using Service Discovery are publically visible in any case.
|
||||||
|
*
|
||||||
|
* To execute commands offered by a remote entity:<br>
|
||||||
|
* ...TBC...
|
||||||
|
*
|
||||||
|
* XEP version: 1.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Adhoc : public DiscoNodeHandler, public DiscoHandler, public IqHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of an Adhoc Command element (from Adhoc Commands, XEP-0050)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Command : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Adhoc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the action to undertake with the given command.
|
||||||
|
*/
|
||||||
|
enum Action
|
||||||
|
{
|
||||||
|
Execute = 1, /**< The command should be executed or continue to be executed.
|
||||||
|
* This is the default value. */
|
||||||
|
Cancel = 2, /**< The command should be canceled. */
|
||||||
|
Previous = 4, /**< The command should be digress to the previous stage of
|
||||||
|
* execution. */
|
||||||
|
Next = 8, /**< The command should progress to the next stage of
|
||||||
|
* execution. */
|
||||||
|
Complete = 16, /**< The command should be completed (if possible). */
|
||||||
|
InvalidAction = 32 /**< The action is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the current status of a command.
|
||||||
|
*/
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
Executing, /**< The command is being executed. */
|
||||||
|
Completed, /**< The command has completed. The command session has ended. */
|
||||||
|
Canceled, /**< The command has been canceled. The command session has ended. */
|
||||||
|
InvalidStatus /**< The status is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstraction of a command note.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Note
|
||||||
|
{
|
||||||
|
|
||||||
|
friend class Command;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Specifies the severity of a note.
|
||||||
|
*/
|
||||||
|
enum Severity
|
||||||
|
{
|
||||||
|
Info, /**< The note is informational only. This is not really an
|
||||||
|
* exceptional condition. */
|
||||||
|
Warning, /**< The note indicates a warning. Possibly due to illogical
|
||||||
|
* (yet valid) data. */
|
||||||
|
Error, /**< The note indicates an error. The text should indicate the
|
||||||
|
* reason for the error. */
|
||||||
|
InvalidSeverity /**< The note type is unknown or invalid. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience constructor.
|
||||||
|
* @param sev The note's severity.
|
||||||
|
* @param note The note's content.
|
||||||
|
*/
|
||||||
|
Note( Severity sev, const std::string& note )
|
||||||
|
: m_severity( sev ), m_note( note ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Note() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the note's severity.
|
||||||
|
* @return The note's severity.
|
||||||
|
*/
|
||||||
|
Severity severity() const { return m_severity; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the note's content.
|
||||||
|
* @return The note's content.
|
||||||
|
*/
|
||||||
|
const std::string& content() const { return m_note; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Tag representation of the Note.
|
||||||
|
* @return A Tag representation.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_COMMANDS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Constructs a new Note from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Note( const Tag* tag );
|
||||||
|
|
||||||
|
Severity m_severity; /**< The note's severity. */
|
||||||
|
std::string m_note; /**< The note's content. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of command notes.
|
||||||
|
*/
|
||||||
|
typedef std::list<const Note*> NoteList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to continue execution of a multi stage command
|
||||||
|
* (for which the session ID must be known).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The session ID of an already running adhoc command session.
|
||||||
|
* @param action The action to perform.
|
||||||
|
* @param form An optional DataForm to include in the request. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Action action,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to reply to an execute request.
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The (possibly newly created) session ID of the adhoc command session.
|
||||||
|
* @param status The execution status.
|
||||||
|
* @param form An optional DataForm to include in the reply. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to reply to a multi stage command that is not yet completed
|
||||||
|
* (for which the session ID must be known).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param sessionid The (possibly newly created) session ID of the adhoc command session.
|
||||||
|
* @param status The execution status.
|
||||||
|
* @param executeAction The action to execute.
|
||||||
|
* @param allowedActions Allowed reply actions.
|
||||||
|
* @param form An optional DataForm to include in the reply. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, const std::string& sessionid, Status status,
|
||||||
|
Action executeAction, int allowedActions = Complete,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object that can be used to perform the provided Action.
|
||||||
|
* This constructor is used best to execute the initial step of a command
|
||||||
|
* (single or multi stage).
|
||||||
|
* @param node The node (command) to perform the action on.
|
||||||
|
* @param action The action to perform.
|
||||||
|
* @param form An optional DataForm to include in the request. Will be deleted in Command's
|
||||||
|
* destructor.
|
||||||
|
*/
|
||||||
|
Command( const std::string& node, Action action,
|
||||||
|
DataForm* form = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Command object from the given Tag.
|
||||||
|
* @param tag A <command> tag in the adhoc commands' namespace.
|
||||||
|
*/
|
||||||
|
Command( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Command();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node identifier (the command).
|
||||||
|
* @return The node identifier.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's session ID, if any.
|
||||||
|
* @return The command's session ID.
|
||||||
|
*/
|
||||||
|
const std::string& sessionID() const { return m_sessionid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the execution status for a command. Only valid for execution
|
||||||
|
* results.
|
||||||
|
* @return The execution status for a command.
|
||||||
|
*/
|
||||||
|
Status status() const { return m_status; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's action.
|
||||||
|
* @return The command's action.
|
||||||
|
*/
|
||||||
|
Action action() const { return m_action; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ORed actions that are allowed to be executed on the
|
||||||
|
* current stage.
|
||||||
|
* @return An int containing the ORed actions.
|
||||||
|
*/
|
||||||
|
int actions() const { return m_actions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of notes associated with the command.
|
||||||
|
* @return The list of notes.
|
||||||
|
*/
|
||||||
|
const NoteList& notes() const { return m_notes; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to add a note to the command.
|
||||||
|
* @param note A pointer to a Note object. The Command will own
|
||||||
|
* the Note.
|
||||||
|
*/
|
||||||
|
void addNote( const Note* note ) { m_notes.push_back( note ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command's embedded DataForm.
|
||||||
|
* @return The command's embedded DataForm. May be 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Command( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
Command* c = new Command();
|
||||||
|
|
||||||
|
NoteList::const_iterator it = m_notes.begin();
|
||||||
|
for( ; it != m_notes.end(); ++it )
|
||||||
|
c->m_notes.push_back( new Note( *(*it) ) );
|
||||||
|
|
||||||
|
c->m_node = m_node;
|
||||||
|
c->m_sessionid = m_sessionid;
|
||||||
|
c->m_form = m_form ? static_cast<DataForm*>( m_form->clone() ) : 0;
|
||||||
|
c->m_action = m_action;
|
||||||
|
c->m_status = m_status;
|
||||||
|
c->m_actions = m_actions;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_COMMANDS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
NoteList m_notes;
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
std::string m_sessionid;
|
||||||
|
DataForm* m_form;
|
||||||
|
Action m_action;
|
||||||
|
Status m_status;
|
||||||
|
int m_actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* Creates a new Adhoc client that registers as IqHandler with a ClientBase.
|
||||||
|
* @param parent The ClientBase used for XMPP communication.
|
||||||
|
*/
|
||||||
|
Adhoc( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Adhoc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function queries the given remote entity for Adhoc Commands support.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void checkSupport( const JID& remote, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of commands from the remote entity. You should check whether the remote
|
||||||
|
* entity actually supports Adhoc Commands by means of checkSupport().
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void getCommands( const JID& remote, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes or continues the given command on the given remote entity.
|
||||||
|
* To construct the @c command object, it is recommended to use either
|
||||||
|
* Command( const std::string&, Action ) to begin execution of a command, or
|
||||||
|
* Command( const std::string&, const std::string&, Action ) to continue execution
|
||||||
|
* of a command.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param command The command to execute.
|
||||||
|
* @param ah The object handling the result of this request.
|
||||||
|
*/
|
||||||
|
void execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to respond to an execution request submitted by means
|
||||||
|
* of AdhocCommandProvider::handleAdhocCommand().
|
||||||
|
* It is recommended to use
|
||||||
|
* Command( const std::string&, const std::string&, Status, DataForm* )
|
||||||
|
* to construct the @c command object.
|
||||||
|
* Optionally, an Error object can be included. In that case the IQ sent is of type @c error.
|
||||||
|
* @param remote The requester's JID.
|
||||||
|
* @param command The response. The Adhoc object will own and delete the
|
||||||
|
* command object pointed to.
|
||||||
|
* @param error An optional Error obejct to include.
|
||||||
|
*/
|
||||||
|
void respond( const JID& remote, const Adhoc::Command* command, const Error* error = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using this function, you can register a AdhocCommandProvider -derived object as
|
||||||
|
* handler for a specific Ad-hoc Command as defined in XEP-0050.
|
||||||
|
* @param acp The obejct to register as handler for the specified command.
|
||||||
|
* @param command The node name of the command. Will be announced in disco#items.
|
||||||
|
* @param name The natural-language name of the command. Will be announced in disco#items.
|
||||||
|
*/
|
||||||
|
void registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command,
|
||||||
|
const std::string& name );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to unregister an adhoc command previously registered using
|
||||||
|
* registerAdhocCommandProvider().
|
||||||
|
* @param command The command to unregister.
|
||||||
|
*/
|
||||||
|
void removeAdhocCommandProvider( const std::string& command );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler
|
||||||
|
virtual bool handleIq( const IQ& iq );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler
|
||||||
|
virtual void handleIqID( const IQ& iq, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context );
|
||||||
|
|
||||||
|
// reimplemented from DiscoHandler
|
||||||
|
virtual void handleDiscoError( const JID& from, const Error* error, int context );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef ADHOC_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
typedef std::map<const std::string, AdhocCommandProvider*> AdhocCommandProviderMap;
|
||||||
|
AdhocCommandProviderMap m_adhocCommandProviders;
|
||||||
|
|
||||||
|
enum AdhocContext
|
||||||
|
{
|
||||||
|
CheckAdhocSupport,
|
||||||
|
FetchAdhocCommands,
|
||||||
|
ExecuteAdhocCommand
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TrackStruct
|
||||||
|
{
|
||||||
|
JID remote;
|
||||||
|
AdhocContext context;
|
||||||
|
std::string session;
|
||||||
|
AdhocHandler* ah;
|
||||||
|
};
|
||||||
|
typedef std::map<std::string, TrackStruct> AdhocTrackMap;
|
||||||
|
AdhocTrackMap m_adhocTrackMap;
|
||||||
|
|
||||||
|
ClientBase* m_parent;
|
||||||
|
|
||||||
|
StringMap m_items;
|
||||||
|
StringMap m_activeSessions;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOC_H__
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOCCOMMANDPROVIDER_H__
|
||||||
|
#define ADHOCCOMMANDPROVIDER_H__
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "adhoc.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface for an Ad-hoc Command Provider according to XEP-0050.
|
||||||
|
*
|
||||||
|
* Derived classes can be registered as Command Providers with the Adhoc object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API AdhocCommandProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AdhocCommandProvider() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when an Ad-hoc Command needs to be handled.
|
||||||
|
* The callee is responsible for the whole command execution, i.e. session
|
||||||
|
* handling etc.
|
||||||
|
* @param from The sender of the command request.
|
||||||
|
* @param command The name of the command to be executed.
|
||||||
|
* @param sessionID The session ID. Either newly generated or taken from the command.
|
||||||
|
* When responding, its value must be passed to Adhoc::Command's constructor.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocCommand( const JID& from, const Adhoc::Command& command,
|
||||||
|
const std::string& sessionID ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gets called for each registered command when a remote
|
||||||
|
* entity requests the list of available commands.
|
||||||
|
* @param from The requesting entity.
|
||||||
|
* @param command The command's name.
|
||||||
|
* @return @b True if the remote entity is allowed to see the command, @b false if not.
|
||||||
|
* @note The return value of this function does not influence
|
||||||
|
* the execution of a command. That is, you have to
|
||||||
|
* implement additional access control at the execution
|
||||||
|
* stage.
|
||||||
|
* @note This function should not block.
|
||||||
|
*/
|
||||||
|
virtual bool handleAdhocAccessRequest( const JID& from, const std::string& command )
|
||||||
|
{
|
||||||
|
(void)from;
|
||||||
|
(void)command;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOCCOMMANDPROVIDER_H__
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ADHOCHANDLER_H__
|
||||||
|
#define ADHOCHANDLER_H__
|
||||||
|
|
||||||
|
#include "adhoc.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface for an Ad-hoc Command users according to XEP-0050.
|
||||||
|
*
|
||||||
|
* Derived classes can be registered with the Adhoc object to receive notifications
|
||||||
|
* about Adhoc Commands remote entities support.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API AdhocHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AdhocHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::checkSupport().
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param support Whether the remote entity supports Adhoc Commands.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocSupport( const JID& remote, bool support ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::getCommands()
|
||||||
|
* and delivers a list of supported commands.
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param commands A map of supported commands and their human-readable name.
|
||||||
|
* The map may be empty.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocCommands( const JID& remote, const StringMap& commands ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a call to Adhoc::getCommands() or
|
||||||
|
* Adhoc::checkSupport() or Adhoc::execute() in case the respective request returned
|
||||||
|
* an error.
|
||||||
|
* @param remote The queried remote entity's JID.
|
||||||
|
* @param error The error condition. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocError( const JID& remote, const Error* error ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called in response to a remote command execution.
|
||||||
|
* @param remote The remote entity's JID.
|
||||||
|
* @param command The command being executed.
|
||||||
|
*/
|
||||||
|
virtual void handleAdhocExecutionResult( const JID& remote, const Adhoc::Command& command ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ADHOCHANDLER_H__
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "amp.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* conditionValues[] =
|
||||||
|
{
|
||||||
|
"deliver", "expire-at", "match-resource"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* actionValues[] =
|
||||||
|
{
|
||||||
|
"alert", "error", "drop", "notify"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* deliverValues[] =
|
||||||
|
{
|
||||||
|
"direct", "forward", "gateway", "none", "stored"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* matchResourceValues[] =
|
||||||
|
{
|
||||||
|
"any", "exact", "other"
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* statusValues[] =
|
||||||
|
{
|
||||||
|
"alert", "notify"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- AMP::Rule ----
|
||||||
|
AMP::Rule::Rule( DeliverType deliver, ActionType action )
|
||||||
|
: m_condition( ConditionDeliver ), m_deliver( deliver ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( const std::string& date, ActionType action )
|
||||||
|
: m_condition( ConditionExpireAt ), m_expireat( new std::string( date ) ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( MatchResourceType match, ActionType action )
|
||||||
|
: m_condition( ConditionMatchResource ), m_matchresource( match ), m_action( action )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::Rule( const std::string& condition, const std::string& action,
|
||||||
|
const std::string& value )
|
||||||
|
{
|
||||||
|
m_condition = (ConditionType)util::lookup( condition, conditionValues );
|
||||||
|
m_action = (ActionType)util::lookup( action, actionValues );
|
||||||
|
switch( m_condition )
|
||||||
|
{
|
||||||
|
case ConditionDeliver:
|
||||||
|
m_deliver = (DeliverType)util::lookup( value, deliverValues );
|
||||||
|
break;
|
||||||
|
case ConditionExpireAt:
|
||||||
|
m_expireat = new std::string( value );
|
||||||
|
break;
|
||||||
|
case ConditionMatchResource:
|
||||||
|
m_matchresource = (MatchResourceType)util::lookup( value, matchResourceValues );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case ConditionInvalid: // shouldn't happen
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::Rule::~Rule()
|
||||||
|
{
|
||||||
|
if( m_condition == ConditionExpireAt && m_expireat )
|
||||||
|
delete m_expireat;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* AMP::Rule::tag() const
|
||||||
|
{
|
||||||
|
if( m_condition == ConditionInvalid || m_action == ActionInvalid
|
||||||
|
|| ( m_condition == ConditionDeliver && m_deliver == DeliverInvalid )
|
||||||
|
|| ( m_condition == ConditionMatchResource && m_matchresource == MatchResourceInvalid )
|
||||||
|
|| ( m_condition == ConditionExpireAt && !m_expireat ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* rule = new Tag( "rule" );
|
||||||
|
rule->addAttribute( "condition", util::lookup( m_condition, conditionValues ) );
|
||||||
|
rule->addAttribute( "action", util::lookup( m_action, actionValues ) );
|
||||||
|
|
||||||
|
switch( m_condition )
|
||||||
|
{
|
||||||
|
case ConditionDeliver:
|
||||||
|
rule->addAttribute( "value", util::lookup( m_deliver, deliverValues ) );
|
||||||
|
break;
|
||||||
|
case ConditionExpireAt:
|
||||||
|
rule->addAttribute( "value", *m_expireat );
|
||||||
|
break;
|
||||||
|
case ConditionMatchResource:
|
||||||
|
rule->addAttribute( "value", util::lookup( m_matchresource, matchResourceValues ) );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
// ---- AMP::Rule ----
|
||||||
|
|
||||||
|
// ---- AMP ----
|
||||||
|
AMP::AMP( bool perhop )
|
||||||
|
: StanzaExtension( ExtAMP ), m_perhop( perhop ), m_status( StatusInvalid )
|
||||||
|
{
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::AMP( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtAMP ), m_perhop( false )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "amp" || tag->xmlns() != XMLNS_AMP )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ConstTagList& rules = tag->findTagList( "/amp/rule" );
|
||||||
|
ConstTagList::const_iterator it = rules.begin();
|
||||||
|
for( ; it != rules.end(); ++it )
|
||||||
|
{
|
||||||
|
m_rules.push_back( new Rule( (*it)->findAttribute( "condition" ),
|
||||||
|
(*it)->findAttribute( "action" ),
|
||||||
|
(*it)->findAttribute( "value" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_from = tag->findAttribute( "from" );
|
||||||
|
m_to = tag->findAttribute( "to" );
|
||||||
|
m_status = (Status)util::lookup( tag->findAttribute( "status" ), statusValues );
|
||||||
|
if( tag->hasAttribute( "per-hop", "true" ) || tag->hasAttribute( "per-hop", "1" ) )
|
||||||
|
m_perhop = true;
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AMP::~AMP()
|
||||||
|
{
|
||||||
|
util::clearList( m_rules );
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMP::addRule( const Rule* rule )
|
||||||
|
{
|
||||||
|
if( rule )
|
||||||
|
m_rules.push_back( rule );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& AMP::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/amp[@xmlns='" + XMLNS_AMP + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* AMP::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid || !m_rules.size() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* amp = new Tag( "amp" );
|
||||||
|
amp->setXmlns( XMLNS_AMP );
|
||||||
|
if( m_from )
|
||||||
|
amp->addAttribute( "from", m_from.full() );
|
||||||
|
if( m_to )
|
||||||
|
amp->addAttribute( "to", m_to.full() );
|
||||||
|
if( m_status != StatusInvalid )
|
||||||
|
amp->addAttribute( "status", util::lookup( m_status, statusValues ) );
|
||||||
|
if( m_perhop )
|
||||||
|
amp->addAttribute( "per-hop", "true" );
|
||||||
|
RuleList::const_iterator it = m_rules.begin();
|
||||||
|
for( ; it != m_rules.end(); ++it )
|
||||||
|
amp->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
return amp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef AMP_H__
|
||||||
|
#define AMP_H__
|
||||||
|
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "jid.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0079 (Advanced Message Processing)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* XEP Version: 1.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API AMP : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Possible types for a rule's condition.
|
||||||
|
*/
|
||||||
|
enum ConditionType
|
||||||
|
{
|
||||||
|
ConditionDeliver, /**< Ensures (non-)delivery of the message */
|
||||||
|
ConditionExpireAt, /**< Ensures delivery only before a certain time (UTC) */
|
||||||
|
ConditionMatchResource, /**< Ensures delivery only to a specific resource type */
|
||||||
|
ConditionInvalid /**< Invalid condition */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible actions to take when the corresponding condition is met.
|
||||||
|
*/
|
||||||
|
enum ActionType
|
||||||
|
{
|
||||||
|
|
||||||
|
ActionAlert, /**< Sends back a message stanza with an 'alert' status */
|
||||||
|
ActionError, /**< Sends back a message stanza with an error type */
|
||||||
|
ActionDrop, /**< Silently ignore the message */
|
||||||
|
ActionNotify, /**< Sends back a message stanza with a 'notify' status */
|
||||||
|
ActionInvalid /**< Invalid action */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible delivery rules.
|
||||||
|
*/
|
||||||
|
enum DeliverType
|
||||||
|
{
|
||||||
|
DeliverDirect, /**< The message would be immediately delivered to the intended
|
||||||
|
* recipient or routed to the next hop. */
|
||||||
|
DeliverForward, /**< The message would be forwarded to another XMPP address or
|
||||||
|
* account. */
|
||||||
|
DeliverGateway, /**< The message would be sent through a gateway to an address
|
||||||
|
* or account on a non-XMPP system. */
|
||||||
|
DeliverNone, /**< The message would not be delivered at all (e.g., because
|
||||||
|
* the intended recipient is offline and message storage is
|
||||||
|
* not enabled). */
|
||||||
|
DeliverStored, /**< The message would be stored offline for later delivery
|
||||||
|
* to the intended recipient. */
|
||||||
|
DeliverInvalid /**< Invalid deliver value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible resource matching rules.
|
||||||
|
*/
|
||||||
|
enum MatchResourceType
|
||||||
|
{
|
||||||
|
MatchResourceAny, /**< Destination resource matches any value, effectively
|
||||||
|
* ignoring the intended resource. */
|
||||||
|
MatchResourceExact, /**< Destination resource exactly matches the intended
|
||||||
|
* resource. */
|
||||||
|
MatchResourceOther, /**< Destination resource matches any value except for
|
||||||
|
* the intended resource. */
|
||||||
|
MatchResourceInvalid /**< Invalid match-resource value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available Stati.
|
||||||
|
*/
|
||||||
|
enum Status
|
||||||
|
{
|
||||||
|
StatusAlert, /**< The message is a reply to a @c Alert rule. */
|
||||||
|
StatusNotify, /**< The message is a reply to a @c Notify rule. */
|
||||||
|
StatusInvalid /**< Invalid status. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes an AMP rule.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Rule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'deliver'.
|
||||||
|
* @param deliver The delivery type.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( DeliverType deliver, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'expire-at'.
|
||||||
|
* @param date The expiry date/time in the format defined in XEP-0082.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( const std::string& date, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object with a condition of 'match-resource'.
|
||||||
|
* @param match The match type.
|
||||||
|
* @param action The rule's action.
|
||||||
|
*/
|
||||||
|
Rule( MatchResourceType match, ActionType action );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AMP rule object from the given strings.
|
||||||
|
* @param condition The rule's condition.
|
||||||
|
* @param action The rule's action.
|
||||||
|
* @param value The rule's value.
|
||||||
|
*/
|
||||||
|
Rule( const std::string& condition, const std::string& action,
|
||||||
|
const std::string& value );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Rule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Tag representation from the current rule.
|
||||||
|
* @return A Tag representation of the rule.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConditionType m_condition;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
DeliverType m_deliver;
|
||||||
|
MatchResourceType m_matchresource;
|
||||||
|
std::string* m_expireat;
|
||||||
|
};
|
||||||
|
ActionType m_action;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of AMP rules.
|
||||||
|
*/
|
||||||
|
typedef std::list<const Rule*> RuleList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object.
|
||||||
|
* @param perhop Indicates whether the ruleset should be applied to all hops,
|
||||||
|
* or at the edge servers only. Default: @c false (edge servers only)
|
||||||
|
*/
|
||||||
|
AMP( bool perhop = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The AMP Tag to parse.
|
||||||
|
*/
|
||||||
|
AMP( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given rule to the list of rules.
|
||||||
|
* @param rule The rule to add.
|
||||||
|
*/
|
||||||
|
void addRule( const Rule* rule );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current list of rules for inspection.
|
||||||
|
* @return The current list of rules.
|
||||||
|
*/
|
||||||
|
const RuleList& rules() const { return m_rules; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AMP();
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new AMP( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
AMP* a = new AMP();
|
||||||
|
a->m_perhop = m_perhop;
|
||||||
|
RuleList::const_iterator it = m_rules.begin();
|
||||||
|
for( ; it != m_rules.end(); ++it )
|
||||||
|
a->m_rules.push_back( new Rule( *(*it) ) );
|
||||||
|
a->m_status = m_status;
|
||||||
|
a->m_from = m_from;
|
||||||
|
a->m_to = m_to;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_perhop;
|
||||||
|
RuleList m_rules;
|
||||||
|
Status m_status;
|
||||||
|
JID m_from;
|
||||||
|
JID m_to;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AMP_H__
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "annotations.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Annotations::Annotations( ClientBase* parent )
|
||||||
|
: PrivateXML( parent ),
|
||||||
|
m_annotationsHandler( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Annotations::~Annotations()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::storeAnnotations( const AnnotationsList& aList )
|
||||||
|
{
|
||||||
|
Tag* s = new Tag( "storage", XMLNS, XMLNS_ANNOTATIONS );
|
||||||
|
|
||||||
|
AnnotationsList::const_iterator it = aList.begin();
|
||||||
|
for( ; it != aList.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* n = new Tag( s, "note", (*it).note );
|
||||||
|
n->addAttribute( "jid", (*it).jid );
|
||||||
|
n->addAttribute( "cdate", (*it).cdate );
|
||||||
|
n->addAttribute( "mdate", (*it).mdate );
|
||||||
|
}
|
||||||
|
|
||||||
|
storeXML( s, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::requestAnnotations()
|
||||||
|
{
|
||||||
|
requestXML( "storage", XMLNS_ANNOTATIONS, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::handlePrivateXML( const Tag* xml )
|
||||||
|
{
|
||||||
|
if( !xml )
|
||||||
|
return;
|
||||||
|
|
||||||
|
AnnotationsList aList;
|
||||||
|
const TagList& l = xml->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "note" )
|
||||||
|
{
|
||||||
|
const std::string& jid = (*it)->findAttribute( "jid" );
|
||||||
|
const std::string& note = (*it)->cdata();
|
||||||
|
|
||||||
|
if( !jid.empty() && !note.empty() )
|
||||||
|
{
|
||||||
|
const std::string& cdate = (*it)->findAttribute( "cdate" );
|
||||||
|
const std::string& mdate = (*it)->findAttribute( "mdate" );
|
||||||
|
AnnotationsListItem item;
|
||||||
|
item.jid = jid;
|
||||||
|
item.cdate = cdate;
|
||||||
|
item.mdate = mdate;
|
||||||
|
item.note = note;
|
||||||
|
aList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_annotationsHandler )
|
||||||
|
m_annotationsHandler->handleAnnotations( aList );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Annotations::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ANNOTATIONS_H__
|
||||||
|
#define ANNOTATIONS_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "annotationshandler.h"
|
||||||
|
#include "privatexml.h"
|
||||||
|
#include "privatexmlhandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0145 (Annotations).
|
||||||
|
*
|
||||||
|
* You can use this class to store arbitrary notes about a roster item on the server
|
||||||
|
* (and to retrieve them later on).
|
||||||
|
* To retrieve all stored annotations for the current user's roster you have to create
|
||||||
|
* a class which inherits from AnnotationsHandler. This handler receives retrieved notes.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* class MyClass : public AnnotationsHandler
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* // ...
|
||||||
|
* void myFuncRetrieve();
|
||||||
|
* void myFuncStore();
|
||||||
|
* void handleAnnotations( const AnnotationsList &aList );
|
||||||
|
*
|
||||||
|
* private:
|
||||||
|
* Annotations* m_notes;
|
||||||
|
* AnnotationsList m_list;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void MyClass::myFuncRetrieve()
|
||||||
|
* {
|
||||||
|
* [...]
|
||||||
|
* m_notes = new Annotations( m_client );
|
||||||
|
* m_notes->requestAnnotations();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void MyClass::handleAnnotations( const AnnotationsList &aList )
|
||||||
|
* {
|
||||||
|
* m_list = aList;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* To store an additional note you have to fetch the currently stored notes first,
|
||||||
|
* add your new note to the list of notes, and transfer them all together back to the
|
||||||
|
* server. This protocol does not support storage of 'deltas', that is, when saving
|
||||||
|
* notes all previously saved notes are overwritten.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void MyClass::myFuncStore()
|
||||||
|
* {
|
||||||
|
* annotationsListItem item;
|
||||||
|
* item.jid = "me@example.com";
|
||||||
|
* item.cdate = "2006-02-04T15:23:21Z";
|
||||||
|
* item.note = "some guy at example.com";
|
||||||
|
* m_list.push_back( item );
|
||||||
|
*
|
||||||
|
* item.jid = "abc@def.com";
|
||||||
|
* item.cdate = "2006-01-24T15:23:21Z";
|
||||||
|
* item.mdate = "2006-02-04T05:11:46Z";
|
||||||
|
* item.note = "some other guy";
|
||||||
|
* m_list.push_back( item );
|
||||||
|
*
|
||||||
|
* m_notes->storeAnnotations( m_list );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API Annotations : public PrivateXML, public PrivateXMLHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new Annotations object.
|
||||||
|
* @param parent The ClientBase to use for communication.
|
||||||
|
*/
|
||||||
|
Annotations( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Annotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to store notes (annotations to contacts in a roster) on the server.
|
||||||
|
* Make sure you store the whole set of annotations, not a 'delta'.
|
||||||
|
* @param aList A list of notes to store.
|
||||||
|
*/
|
||||||
|
void storeAnnotations( const AnnotationsList& aList );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to initiate retrieval of annotations. Use registerAnnotationsHandler()
|
||||||
|
* to register an object which will receive the lists of notes.
|
||||||
|
*/
|
||||||
|
void requestAnnotations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a AnnotationsHandler.
|
||||||
|
* @param ah The AnnotationsHandler which shall receive retrieved notes.
|
||||||
|
*/
|
||||||
|
void registerAnnotationsHandler( AnnotationsHandler* ah )
|
||||||
|
{ m_annotationsHandler = ah; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to un-register the AnnotationsHandler.
|
||||||
|
*/
|
||||||
|
void removeAnnotationsHandler()
|
||||||
|
{ m_annotationsHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXML( const Tag* xml );
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult );
|
||||||
|
|
||||||
|
private:
|
||||||
|
AnnotationsHandler* m_annotationsHandler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ANNOTATIONS_H__
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ANNOTATIONSHANDLER_H__
|
||||||
|
#define ANNOTATIONSHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single note item.
|
||||||
|
*/
|
||||||
|
struct AnnotationsListItem
|
||||||
|
{
|
||||||
|
std::string jid; /**< The JID of the roster item this note is about */
|
||||||
|
std::string cdate; /**< Creation date of this note. */
|
||||||
|
std::string mdate; /**< Date of last modification of this note. */
|
||||||
|
std::string note; /**< The note. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of note items.
|
||||||
|
*/
|
||||||
|
typedef std::list<AnnotationsListItem> AnnotationsList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface which can be reimplemented to receive notes with help of
|
||||||
|
* the Annotations object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API AnnotationsHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~AnnotationsHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when notes arrive from the server.
|
||||||
|
* @param aList A list of notes.
|
||||||
|
*/
|
||||||
|
virtual void handleAnnotations( const AnnotationsList &aList ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ANNOTATIONSHANDLER_H__
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "attention.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Attention::Attention()
|
||||||
|
: StanzaExtension( ExtAttention )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Attention::~Attention()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Attention::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/attention[@xmlns='" + XMLNS_ATTENTION + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Attention::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "attention" );
|
||||||
|
t->setXmlns( XMLNS_ATTENTION );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ATTENTION_H__
|
||||||
|
#define ATTENTION_H__
|
||||||
|
|
||||||
|
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0224 as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Attention : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
*/
|
||||||
|
Attention();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Attention();
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* /*tag*/ ) const
|
||||||
|
{
|
||||||
|
return new Attention();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Attention();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif// ATTENTION_H__
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Base64
|
||||||
|
{
|
||||||
|
|
||||||
|
static const std::string alphabet64( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" );
|
||||||
|
static const char pad = '=';
|
||||||
|
static const char np = (char)std::string::npos;
|
||||||
|
static char table64vals[] =
|
||||||
|
{
|
||||||
|
62, np, np, np, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, np, np, np, np, np,
|
||||||
|
np, np, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
18, 19, 20, 21, 22, 23, 24, 25, np, np, np, np, np, np, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
|
||||||
|
};
|
||||||
|
|
||||||
|
inline char table64( unsigned char c )
|
||||||
|
{
|
||||||
|
return ( c < 43 || c > 122 ) ? np : table64vals[c-43];
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string encode64( const std::string& input )
|
||||||
|
{
|
||||||
|
std::string encoded;
|
||||||
|
char c;
|
||||||
|
const std::string::size_type length = input.length();
|
||||||
|
|
||||||
|
encoded.reserve( length * 2 );
|
||||||
|
|
||||||
|
for( std::string::size_type i = 0; i < length; ++i )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( ( input[i] >> 2 ) & 0x3f );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
|
||||||
|
c = static_cast<char>( ( input[i] << 4 ) & 0x3f );
|
||||||
|
if( ++i < length )
|
||||||
|
c = static_cast<char>( c | static_cast<char>( ( input[i] >> 4 ) & 0x0f ) );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
|
||||||
|
if( i < length )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( ( input[i] << 2 ) & 0x3c );
|
||||||
|
if( ++i < length )
|
||||||
|
c = static_cast<char>( c | static_cast<char>( ( input[i] >> 6 ) & 0x03 ) );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
encoded += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( i < length )
|
||||||
|
{
|
||||||
|
c = static_cast<char>( input[i] & 0x3f );
|
||||||
|
encoded += alphabet64[c];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoded += pad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string decode64( const std::string& input )
|
||||||
|
{
|
||||||
|
char c, d;
|
||||||
|
const std::string::size_type length = input.length();
|
||||||
|
std::string decoded;
|
||||||
|
|
||||||
|
decoded.reserve( length );
|
||||||
|
|
||||||
|
for( std::string::size_type i = 0; i < length; ++i )
|
||||||
|
{
|
||||||
|
c = table64(input[i]);
|
||||||
|
++i;
|
||||||
|
d = table64(input[i]);
|
||||||
|
c = static_cast<char>( ( c << 2 ) | ( ( d >> 4 ) & 0x3 ) );
|
||||||
|
decoded += c;
|
||||||
|
if( ++i < length )
|
||||||
|
{
|
||||||
|
c = input[i];
|
||||||
|
if( pad == c )
|
||||||
|
break;
|
||||||
|
|
||||||
|
c = table64(input[i]);
|
||||||
|
d = static_cast<char>( ( ( d << 4 ) & 0xf0 ) | ( ( c >> 2 ) & 0xf ) );
|
||||||
|
decoded += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ++i < length )
|
||||||
|
{
|
||||||
|
d = input[i];
|
||||||
|
if( pad == d )
|
||||||
|
break;
|
||||||
|
|
||||||
|
d = table64(input[i]);
|
||||||
|
c = static_cast<char>( ( ( c << 6 ) & 0xc0 ) | d );
|
||||||
|
decoded += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BASE64_H__
|
||||||
|
#define BASE64_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of the Base64 data encoding (RFC 3548)
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
namespace Base64
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-encodes the input according to RFC 3548.
|
||||||
|
* @param input The data to encode.
|
||||||
|
* @return The encoded string.
|
||||||
|
*/
|
||||||
|
GLOOX_API const std::string encode64( const std::string& input );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64-decodes the input according to RFC 3548.
|
||||||
|
* @param input The encoded data.
|
||||||
|
* @return The decoded data.
|
||||||
|
*/
|
||||||
|
GLOOX_API const std::string decode64( const std::string& input );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BASE64_H__
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOKMARKHANDLER_H__
|
||||||
|
#define BOOKMARKHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single bookmarked URL item.
|
||||||
|
*/
|
||||||
|
struct BookmarkListItem
|
||||||
|
{
|
||||||
|
std::string name; /**< A human readable name of the bookmark. */
|
||||||
|
std::string url; /**< The URL of the bookmark. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This describes a single bookmarked conference item.
|
||||||
|
*/
|
||||||
|
struct ConferenceListItem
|
||||||
|
{
|
||||||
|
std::string name; /**< A human readable name of the conference room. */
|
||||||
|
std::string jid; /**< The address of the room. */
|
||||||
|
std::string nick; /**< The nick name to use in this room. */
|
||||||
|
std::string password; /**< The password to use for a protected room. */
|
||||||
|
bool autojoin; /**< The conference shall be joined automatically on login. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of URL items.
|
||||||
|
*/
|
||||||
|
typedef std::list<BookmarkListItem> BookmarkList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of conference items.
|
||||||
|
*/
|
||||||
|
typedef std::list<ConferenceListItem> ConferenceList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface which can be reimplemented to receive bookmarks with help of a
|
||||||
|
* BookmarkStorage object.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API BookmarkHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BookmarkHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when bookmarks arrive from the server.
|
||||||
|
* @param bList A list of URL bookmarks.
|
||||||
|
* @param cList A list of conference bookmarks.
|
||||||
|
*/
|
||||||
|
virtual void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOOKMARKHANDLER_H__
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "bookmarkstorage.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
BookmarkStorage::BookmarkStorage( ClientBase* parent )
|
||||||
|
: PrivateXML( parent ),
|
||||||
|
m_bookmarkHandler( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BookmarkStorage::~BookmarkStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::storeBookmarks( const BookmarkList& bList, const ConferenceList& cList )
|
||||||
|
{
|
||||||
|
Tag* s = new Tag( "storage" );
|
||||||
|
s->addAttribute( XMLNS, XMLNS_BOOKMARKS );
|
||||||
|
|
||||||
|
BookmarkList::const_iterator itb = bList.begin();
|
||||||
|
for( ; itb != bList.end(); ++itb )
|
||||||
|
{
|
||||||
|
Tag* i = new Tag( s, "url", "name", (*itb).name );
|
||||||
|
i->addAttribute( "url", (*itb).url );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConferenceList::const_iterator itc = cList.begin();
|
||||||
|
for( ; itc != cList.end(); ++itc )
|
||||||
|
{
|
||||||
|
Tag* i = new Tag( s, "conference", "name", (*itc).name );
|
||||||
|
i->addAttribute( "jid", (*itc).jid );
|
||||||
|
i->addAttribute( "autojoin", (*itc).autojoin ? "true" : "false" );
|
||||||
|
|
||||||
|
new Tag( i, "nick", (*itc).nick );
|
||||||
|
new Tag( i, "password", (*itc).password );
|
||||||
|
}
|
||||||
|
|
||||||
|
storeXML( s, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::requestBookmarks()
|
||||||
|
{
|
||||||
|
requestXML( "storage", XMLNS_BOOKMARKS, this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::handlePrivateXML( const Tag* xml )
|
||||||
|
{
|
||||||
|
if( !xml )
|
||||||
|
return;
|
||||||
|
|
||||||
|
BookmarkList bList;
|
||||||
|
ConferenceList cList;
|
||||||
|
const TagList& l = xml->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "url" )
|
||||||
|
{
|
||||||
|
const std::string& url = (*it)->findAttribute( "url" );
|
||||||
|
const std::string& name = (*it)->findAttribute( "name" );
|
||||||
|
|
||||||
|
if( !url.empty() && !name.empty() )
|
||||||
|
{
|
||||||
|
BookmarkListItem item;
|
||||||
|
item.url = url;
|
||||||
|
item.name = name;
|
||||||
|
bList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "conference" )
|
||||||
|
{
|
||||||
|
const std::string& jid = (*it)->findAttribute( "jid" );
|
||||||
|
const std::string& name = (*it)->findAttribute( "name" );
|
||||||
|
|
||||||
|
if( !jid.empty() && !name.empty() )
|
||||||
|
{
|
||||||
|
const std::string& join = (*it)->findAttribute( "autojoin" );
|
||||||
|
ConferenceListItem item;
|
||||||
|
item.jid = jid;
|
||||||
|
item.name = name;
|
||||||
|
const Tag* nick = (*it)->findChild( "nick" );
|
||||||
|
if( nick )
|
||||||
|
item.nick = nick->cdata();
|
||||||
|
const Tag* pwd = (*it)->findChild( "password" );
|
||||||
|
if( pwd )
|
||||||
|
item.password = pwd->cdata();
|
||||||
|
item.autojoin = ( join == "true" || join == "1" );
|
||||||
|
cList.push_back( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_bookmarkHandler )
|
||||||
|
m_bookmarkHandler->handleBookmarks( bList, cList );
|
||||||
|
}
|
||||||
|
|
||||||
|
void BookmarkStorage::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOOKMARKSTORAGE_H__
|
||||||
|
#define BOOKMARKSTORAGE_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include "bookmarkhandler.h"
|
||||||
|
#include "privatexml.h"
|
||||||
|
#include "privatexmlhandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0048 (Bookmark Storage).
|
||||||
|
*
|
||||||
|
* You can use this class to store bookmarks to multi-user chat rooms or ordinary URLs
|
||||||
|
* on the server (and to retrieve them later on).
|
||||||
|
* To retrieve all stored bookmarks for the current user you have to create a class which
|
||||||
|
* inherits from BookmarkHandler. This handler receives retrieved bookmarks.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* class MyClass : public BookmarkHandler
|
||||||
|
* {
|
||||||
|
* public:
|
||||||
|
* // ...
|
||||||
|
* void myFuncRetrieve();
|
||||||
|
* void myFuncStore();
|
||||||
|
* void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList );
|
||||||
|
*
|
||||||
|
* private:
|
||||||
|
* BookmarkStorage* m_bs;
|
||||||
|
* BookmarkList m_bList;
|
||||||
|
* ConferenceList m_cList;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* void MyClass::myFuncRetrieve()
|
||||||
|
* {
|
||||||
|
* m_bs = new BookmarkStorage( m_client );
|
||||||
|
* m_bs->requestBookmarks();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void MyClass::handleBookmarks( const BookmarkList &bList, const ConferenceList &cList )
|
||||||
|
* {
|
||||||
|
* m_bList = bList;
|
||||||
|
* m_cList = cList;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* To store additional bookmarks you have to fetch the currently stored ones first,
|
||||||
|
* add your new bookmark to the list, and transfer them all together back to the
|
||||||
|
* server. This protocol does not support storage of 'deltas', that is, when saving
|
||||||
|
* bookmarks all previously saved bookmarks are overwritten.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* void MyClass::myFuncStore()
|
||||||
|
* {
|
||||||
|
* BookmarkListItem bi;
|
||||||
|
* bi.url = "http://www.jabber.org";
|
||||||
|
* bi.name = "my favourite IM protocol";
|
||||||
|
* m_bList.push_back( bi );
|
||||||
|
*
|
||||||
|
* conferenceListItem ci
|
||||||
|
* ci.name = "jabber/xmpp development room";
|
||||||
|
* ci.jid = "jdev@conference.jabber.org";
|
||||||
|
* ci.nick = "myNick";
|
||||||
|
* ci.password = EmptyString;
|
||||||
|
* ci.autojoin = true;
|
||||||
|
* m_cList.push_back( ci );
|
||||||
|
*
|
||||||
|
* m_bs->storeBookmarks( m_bList, m_cList );
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API BookmarkStorage : public PrivateXML, public PrivateXMLHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new BookmarkStorage object.
|
||||||
|
* @param parent The ClientBase to use for communication.
|
||||||
|
*/
|
||||||
|
BookmarkStorage( ClientBase* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BookmarkStorage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to store a number of URL and conference bookmarks on the server.
|
||||||
|
* Make sure you store the whole set of bookmarks, not a 'delta'.
|
||||||
|
* @param bList A list of URLs to store.
|
||||||
|
* @param cList A list of conferences to store.
|
||||||
|
*/
|
||||||
|
void storeBookmarks( const BookmarkList& bList, const ConferenceList& cList );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to initiate retrieval of bookmarks. Use registerBookmarkHandler()
|
||||||
|
* to register an object which will receive the lists of bookmarks.
|
||||||
|
*/
|
||||||
|
void requestBookmarks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a BookmarkHandler.
|
||||||
|
* @param bmh The BookmarkHandler which shall receive retrieved bookmarks.
|
||||||
|
*/
|
||||||
|
void registerBookmarkHandler( BookmarkHandler* bmh )
|
||||||
|
{ m_bookmarkHandler = bmh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to un-register the BookmarkHandler.
|
||||||
|
*/
|
||||||
|
void removeBookmarkHandler()
|
||||||
|
{ m_bookmarkHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXML( const Tag* xml );
|
||||||
|
|
||||||
|
// reimplemented from PrivateXMLHandler
|
||||||
|
virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult );
|
||||||
|
|
||||||
|
private:
|
||||||
|
BookmarkHandler* m_bookmarkHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BOOKMARKSTORAGE_H__
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAM_H__
|
||||||
|
#define BYTESTREAM_H__
|
||||||
|
|
||||||
|
#include "jid.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class BytestreamDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a single bytestream.
|
||||||
|
*
|
||||||
|
* Used as a base class for InBand Bytestreams as well as SOCKS5 Bytestreams.
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Bytestream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Available stream types.
|
||||||
|
*/
|
||||||
|
enum StreamType
|
||||||
|
{
|
||||||
|
S5B, /**< SOCKS5 Bytestream */
|
||||||
|
IBB /**< In-Band Bytestream */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Bytestream.
|
||||||
|
* @param type The stream type.
|
||||||
|
* @param logInstance A Logsink to use for logging. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param initiator The initiator of the stream (usually the sender).
|
||||||
|
* @param target The target of the stream (usually the receiver).
|
||||||
|
* @param sid The stream's ID.
|
||||||
|
*/
|
||||||
|
Bytestream( StreamType type, LogSink& logInstance, const JID& initiator, const JID& target,
|
||||||
|
const std::string& sid )
|
||||||
|
: m_handler( 0 ), m_logInstance( logInstance ), m_initiator( initiator ), m_target( target ),
|
||||||
|
m_type( type ), m_sid( sid ), m_open( false )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Bytestream() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the bytestream is open, that is, accepted by both parties and ready
|
||||||
|
* to send/receive data.
|
||||||
|
* @return Whether or not the bytestream is open.
|
||||||
|
*/
|
||||||
|
bool isOpen() const { return m_open; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function starts the connection process.
|
||||||
|
* @return @b True if a connection to a remote entity could be established, @b false
|
||||||
|
* otherwise.
|
||||||
|
* @note If @b false is returned you should pass this Bytestream object
|
||||||
|
* to SIProfileFT::dispose() for deletion.
|
||||||
|
* @note Make sure you have a BytestreamDataHandler registered (using
|
||||||
|
* registerBytestreamDataHandler()) before calling this function.
|
||||||
|
*/
|
||||||
|
virtual bool connect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the bytestream.
|
||||||
|
*/
|
||||||
|
virtual void close() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to send a chunk of data over an open bytestream.
|
||||||
|
* If the stream is not open or has been closed again
|
||||||
|
* (by the remote entity or locally), nothing is sent and @b false is returned.
|
||||||
|
* This function does any base64 encoding for you, if necessary.
|
||||||
|
* @param data The block of data to send.
|
||||||
|
* @return @b True if the data has been sent (no guarantee of receipt), @b false
|
||||||
|
* in case of an error.
|
||||||
|
*/
|
||||||
|
virtual bool send( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function repeatedly to receive data. You should even do this
|
||||||
|
* if you use the bytestream to merely @b send data. May be a NOOP, depending on the actual
|
||||||
|
* stream type.
|
||||||
|
* @param timeout The timeout to use for select in microseconds. Default of -1 means blocking.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError recv( int timeout = -1 ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you retrieve the stream's ID.
|
||||||
|
* @return The stream's ID.
|
||||||
|
*/
|
||||||
|
const std::string& sid() const { return m_sid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the stream's type.
|
||||||
|
* @return The stream's type.
|
||||||
|
*/
|
||||||
|
StreamType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the target entity's JID. If this bytestream is remote-initiated, this is
|
||||||
|
* the local JID. If it is local-initiated, this is the remote entity's JID.
|
||||||
|
* @return The target's JID.
|
||||||
|
*/
|
||||||
|
const JID& target() const { return m_target; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the initiating entity's JID. If this bytestream is remote-initiated, this is
|
||||||
|
* the remote entity's JID. If it is local-initiated, this is the local JID.
|
||||||
|
* @return The initiator's JID.
|
||||||
|
*/
|
||||||
|
const JID& initiator() const { return m_initiator; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register an object that will receive any notifications from
|
||||||
|
* the Bytestream instance. Only one BytestreamDataHandler can be registered
|
||||||
|
* at any one time.
|
||||||
|
* @param bdh The BytestreamDataHandler-derived object to receive notifications.
|
||||||
|
*/
|
||||||
|
void registerBytestreamDataHandler( BytestreamDataHandler* bdh )
|
||||||
|
{ m_handler = bdh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the registered BytestreamDataHandler.
|
||||||
|
*/
|
||||||
|
void removeBytestreamDataHandler()
|
||||||
|
{ m_handler = 0; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming data and open/close events. */
|
||||||
|
BytestreamDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** A LogSink instance to use for logging. */
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
/** The initiator's JID. */
|
||||||
|
const JID m_initiator;
|
||||||
|
|
||||||
|
/** The target's JID. */
|
||||||
|
const JID m_target;
|
||||||
|
|
||||||
|
/** The stream type. */
|
||||||
|
StreamType m_type;
|
||||||
|
|
||||||
|
/** The stream ID. */
|
||||||
|
std::string m_sid;
|
||||||
|
|
||||||
|
/** Indicates whether or not the stream is open. */
|
||||||
|
bool m_open;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Bytestream& operator=( const Bytestream& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAM_H__
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAMDATAHANDLER_H__
|
||||||
|
#define BYTESTREAMDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Bytestream;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that allows implementors to receive data
|
||||||
|
* sent over a SOCKS5 Bytestream as defined in XEP-0066, or an In-Band Bytestream
|
||||||
|
* as defined in XEP-0047. You'll also need it for sending of data.
|
||||||
|
*
|
||||||
|
* An BytestreamDataHandler is registered with a Bytestream.
|
||||||
|
*
|
||||||
|
* See SIProfileFT for more information regarding file transfer.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API BytestreamDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BytestreamDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive data which is sent over the bytestream.
|
||||||
|
* The data received here is (probably) only a single chunk of the complete data (depending
|
||||||
|
* on the amount of data you want to send).
|
||||||
|
* @param bs The bytestream.
|
||||||
|
* @param data The actual stream payload.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamData( Bytestream* bs, const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies about an error occuring while using a bytestream.
|
||||||
|
* When this handler is called the stream has already been closed.
|
||||||
|
* @param bs The bytestream.
|
||||||
|
* @param iq The error stanza.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamError( Bytestream* bs, const IQ& iq ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler that the given bytestream has been acknowledged
|
||||||
|
* and is ready to send/receive data.
|
||||||
|
* @param bs The opened bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamOpen( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler that the given bytestream has been closed.
|
||||||
|
* @param bs The closed bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamClose( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAMDATAHANDLER_H__
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BYTESTREAMHANDLER_H__
|
||||||
|
#define BYTESTREAMHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "bytestream.h"
|
||||||
|
#include "iq.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that allows to receive new incoming Bytestream requests
|
||||||
|
* from remote entities.
|
||||||
|
*
|
||||||
|
* You should not need to use this interface directly.
|
||||||
|
*
|
||||||
|
* See SIProfileFT on how to implement file transfer in general.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API BytestreamHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~BytestreamHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of a new incoming bytestream request.
|
||||||
|
* You have to call either
|
||||||
|
* BytestreamManager::acceptBytestream() or
|
||||||
|
* BytestreamManager::rejectBytestream(), to accept or reject the bytestream
|
||||||
|
* request, respectively.
|
||||||
|
* @param sid The bytestream's id, to be passed to BytestreamManager::acceptBytestream()
|
||||||
|
* and BytestreamManager::rejectBytestream(), respectively.
|
||||||
|
* @param from The remote initiator of the bytestream request.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingBytestreamRequest( const std::string& sid, const JID& from ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of a new incoming bytestream. The bytestream is not yet ready to
|
||||||
|
* send data.
|
||||||
|
* To initialize the bytestream and to prepare it for data transfer, register a
|
||||||
|
* BytestreamDataHandler with it and call its connect() method.
|
||||||
|
* To not block your application while the data transfer lasts, you most
|
||||||
|
* likely want to put the bytestream into its own thread or process (before calling connect() on it).
|
||||||
|
* It is safe to do so without additional synchronization.
|
||||||
|
* When you are finished using the bytestream, use SIProfileFT::dispose() to get rid of it.
|
||||||
|
* @param bs The bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingBytestream( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the implementor of successful establishing of an outgoing bytestream request.
|
||||||
|
* The stream has been accepted by the remote entity and is ready to send data.
|
||||||
|
* The BytestreamHandler does @b not become the owner of the Bytestream object.
|
||||||
|
* Use SIProfileFT::dispose() to get rid of the bytestream object after it has been closed.
|
||||||
|
* @param bs The new bytestream.
|
||||||
|
*/
|
||||||
|
virtual void handleOutgoingBytestream( Bytestream* bs ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the handler of errors occuring when a bytestream was requested.
|
||||||
|
* For example, if the remote entity does not implement SOCKS5 bytestreams.
|
||||||
|
* @param iq The error stanza.
|
||||||
|
* @param sid The request's SID.
|
||||||
|
*/
|
||||||
|
virtual void handleBytestreamError( const IQ& iq, const std::string& sid ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BYTESTREAMHANDLER_H__
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "capabilities.h"
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "sha.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Capabilities::Capabilities( Disco* disco )
|
||||||
|
: StanzaExtension( ExtCaps ), m_disco( disco ), m_node( GLOOX_CAPS_NODE ),
|
||||||
|
m_hash( "sha-1" ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( m_disco )
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Capabilities::Capabilities( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtCaps ), m_disco( 0 ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "c" || !tag->hasAttribute( XMLNS, XMLNS_CAPS )
|
||||||
|
|| !tag->hasAttribute( "node" ) || !tag->hasAttribute( "ver" ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_ver = tag->findAttribute( "ver" );
|
||||||
|
m_hash = tag->findAttribute( "hash" );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Capabilities::~Capabilities()
|
||||||
|
{
|
||||||
|
if( m_disco )
|
||||||
|
m_disco->removeNodeHandlers( const_cast<Capabilities*>( this ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string Capabilities::ver() const
|
||||||
|
{
|
||||||
|
if( !m_disco )
|
||||||
|
return m_ver;
|
||||||
|
|
||||||
|
SHA sha;
|
||||||
|
sha.feed( generate( m_disco->identities(), m_disco->features( true ), m_disco->form() ) );
|
||||||
|
const std::string& hash = Base64::encode64( sha.binary() );
|
||||||
|
m_disco->removeNodeHandlers( const_cast<Capabilities*>( this ) );
|
||||||
|
m_disco->registerNodeHandler( const_cast<Capabilities*>( this ), m_node + '#' + hash );
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco::IdentityList& il, const StringList& features, const DataForm* form )
|
||||||
|
{
|
||||||
|
StringList sl;
|
||||||
|
Disco::IdentityList::const_iterator it = il.begin();
|
||||||
|
for( ; it != il.end(); ++it )
|
||||||
|
{
|
||||||
|
std::string id = (*it)->category();
|
||||||
|
id += '/';
|
||||||
|
id += (*it)->type();
|
||||||
|
id += '/';
|
||||||
|
// FIXME add xml:lang caps here. see XEP-0115 Section 5
|
||||||
|
id += '/';
|
||||||
|
id += (*it)->name();
|
||||||
|
sl.push_back( id );
|
||||||
|
}
|
||||||
|
sl.sort();
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
StringList::const_iterator it2 = sl.begin();
|
||||||
|
for( ; it2 != sl.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList f = features;
|
||||||
|
f.sort();
|
||||||
|
it2 = f.begin();
|
||||||
|
for( ; it2 != f.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
|
||||||
|
if( form )
|
||||||
|
{
|
||||||
|
DataForm::FieldList::const_iterator it3 = form->fields().begin();
|
||||||
|
typedef std::map<std::string, StringList> MapSSL;
|
||||||
|
|
||||||
|
MapSSL m;
|
||||||
|
for( ; it3 != form->fields().end(); ++it3 )
|
||||||
|
{
|
||||||
|
if( (*it3)->name() == "FORM_TYPE" )
|
||||||
|
{
|
||||||
|
s += (*it3)->value();
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m.insert( std::make_pair( (*it3)->name(), (*it3)->values() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
MapSSL::iterator it4 = m.begin();
|
||||||
|
for( ; it4 != m.end(); ++it4 )
|
||||||
|
{
|
||||||
|
s += it4->first;
|
||||||
|
s += '<';
|
||||||
|
it2 = it4->second.begin();
|
||||||
|
for( ; it2 != it4->second.end(); ++it2 )
|
||||||
|
{
|
||||||
|
s += (*it2);
|
||||||
|
s += '<';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco::Info* info )
|
||||||
|
{
|
||||||
|
return info ? generate( info->identities(), info->features(), info->form() ) : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Capabilities::generate( const Disco* disco )
|
||||||
|
{
|
||||||
|
return disco ? generate( disco->identities(), disco->features(), disco->form() ) : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Capabilities::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/presence/c[@xmlns='" + XMLNS_CAPS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Capabilities::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid || m_node.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( "c" );
|
||||||
|
t->setXmlns( XMLNS_CAPS );
|
||||||
|
t->addAttribute( "hash", m_hash );
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
t->addAttribute( "ver", ver() );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList Capabilities::handleDiscoNodeFeatures( const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
return m_disco->features();
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::IdentityList Capabilities::handleDiscoNodeIdentities( const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
const Disco::IdentityList& il = m_disco->identities();
|
||||||
|
Disco::IdentityList ret;
|
||||||
|
Disco::IdentityList::const_iterator it = il.begin();
|
||||||
|
for( ; it != il.end(); ++it )
|
||||||
|
{
|
||||||
|
ret.push_back( new Disco::Identity( *(*it) ) );
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::ItemList Capabilities::handleDiscoNodeItems( const JID&, const JID&, const std::string& )
|
||||||
|
{
|
||||||
|
return Disco::ItemList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CAPABILITIES_H__
|
||||||
|
#define CAPABILITIES_H__
|
||||||
|
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Disco;
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0115 (Entity Capabilities).
|
||||||
|
*
|
||||||
|
* XEP Version: 1.5-15
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Capabilities : public StanzaExtension, public DiscoNodeHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object and fills it according to the parameters.
|
||||||
|
* @param disco The current Client's Disco object.
|
||||||
|
*/
|
||||||
|
Capabilities( Disco* disco );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Capabilities( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Capabilities();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client's identifying node.
|
||||||
|
* @return The node.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the client's identifying node.
|
||||||
|
* @param node The node.
|
||||||
|
*/
|
||||||
|
void setNode( const std::string& node ) { m_node = node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client's identifying ver string.
|
||||||
|
* @return The ver string.
|
||||||
|
*/
|
||||||
|
const std::string ver() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Capabilities( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Capabilities( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node );
|
||||||
|
|
||||||
|
// reimplemented from DiscoNodeHandler
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
|
||||||
|
const std::string& node = EmptyString );
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Returns the hash function used for creating the caps info.
|
||||||
|
* @return The current hash function's name.
|
||||||
|
*/
|
||||||
|
const std::string& hash() const { return m_hash; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the hash function to use.
|
||||||
|
* @param hash The hash function.
|
||||||
|
* @todo Convert to using an enum and make public.
|
||||||
|
*/
|
||||||
|
void setHash( const std::string& hash ) { m_hash = hash; }
|
||||||
|
|
||||||
|
static std::string generate( const Disco::IdentityList& identities,
|
||||||
|
const StringList& features, const DataForm* form = 0 );
|
||||||
|
static std::string generate( const Disco::Info* info );
|
||||||
|
static std::string generate( const Disco* disco );
|
||||||
|
|
||||||
|
Disco* m_disco;
|
||||||
|
std::string m_node;
|
||||||
|
std::string m_hash;
|
||||||
|
std::string m_ver;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CAPABILITIES_H__
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chatstate.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/* chat state type values */
|
||||||
|
static const char* stateValues [] = {
|
||||||
|
"active",
|
||||||
|
"composing",
|
||||||
|
"paused",
|
||||||
|
"inactive",
|
||||||
|
"gone"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline ChatStateType chatStateType( const std::string& type )
|
||||||
|
{
|
||||||
|
return (ChatStateType)util::lookup2( type, stateValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatState::ChatState( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtChatState )
|
||||||
|
{
|
||||||
|
if( tag )
|
||||||
|
m_state = chatStateType( tag->name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& ChatState::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter =
|
||||||
|
"/message/active[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/composing[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/paused[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/inactive[@xmlns='" + XMLNS_CHAT_STATES + "']"
|
||||||
|
"|/message/gone[@xmlns='" + XMLNS_CHAT_STATES + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* ChatState::tag() const
|
||||||
|
{
|
||||||
|
if( m_state == ChatStateInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return new Tag( util::lookup2( m_state, stateValues ), XMLNS, XMLNS_CHAT_STATES );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATSTATE_H__
|
||||||
|
#define CHATSTATE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of Chat State Notifications (XEP-0085) as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatState : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag A Tag to parse.
|
||||||
|
*/
|
||||||
|
ChatState( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object of the given type.
|
||||||
|
* @param state The chat state.
|
||||||
|
*/
|
||||||
|
ChatState( ChatStateType state )
|
||||||
|
: StanzaExtension( ExtChatState ), m_state( state )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatState() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the object's state.
|
||||||
|
* @return The object's state.
|
||||||
|
*/
|
||||||
|
ChatStateType state() const { return m_state; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new ChatState( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new ChatState( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ChatStateType m_state;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATE_H__
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "chatstatefilter.h"
|
||||||
|
#include "chatstatehandler.h"
|
||||||
|
#include "messageeventhandler.h"
|
||||||
|
#include "messagesession.h"
|
||||||
|
#include "message.h"
|
||||||
|
#include "chatstate.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ChatStateFilter::ChatStateFilter( MessageSession* parent )
|
||||||
|
: MessageFilter( parent ), m_chatStateHandler( 0 ), m_lastSent( ChatStateGone ),
|
||||||
|
m_enableChatStates( true )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatStateFilter::~ChatStateFilter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::filter( Message& msg )
|
||||||
|
{
|
||||||
|
if( m_enableChatStates && m_chatStateHandler )
|
||||||
|
{
|
||||||
|
const ChatState* state = msg.findExtension<ChatState>( ExtChatState );
|
||||||
|
|
||||||
|
m_enableChatStates = state && state->state() != ChatStateInvalid;
|
||||||
|
if( m_enableChatStates && msg.body().empty() )
|
||||||
|
m_chatStateHandler->handleChatState( msg.from(), state->state() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::setChatState( ChatStateType state )
|
||||||
|
{
|
||||||
|
if( !m_enableChatStates || state == m_lastSent || state == ChatStateInvalid )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Message m( Message::Chat, m_parent->target() );
|
||||||
|
m.addExtension( new ChatState( state ) );
|
||||||
|
|
||||||
|
m_lastSent = state;
|
||||||
|
|
||||||
|
send( m );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatStateFilter::decorate( Message& msg )
|
||||||
|
{
|
||||||
|
if( m_enableChatStates )
|
||||||
|
msg.addExtension( new ChatState( ChatStateActive ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CHATSTATEFILTER_H__
|
||||||
|
#define CHATSTATEFILTER_H__
|
||||||
|
|
||||||
|
#include "messagefilter.h"
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
class ChatStateHandler;
|
||||||
|
class MessageSession;
|
||||||
|
class Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class adds Chat State Notifications (XEP-0085) support to a MessageSession.
|
||||||
|
*
|
||||||
|
* This implementation of Chat States is fully transparent to the user of the class.
|
||||||
|
* If the remote entity does not request chat states, ChatStateFilter will not send
|
||||||
|
* any, even if the user requests it. (This is required by the protocol specification.)
|
||||||
|
* You MUST annouce this capability by use of Disco (associated namespace is XMLNS_CHAT_STATES).
|
||||||
|
* (This is also required by the protocol specification.)
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatStateFilter : public MessageFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contstructs a new Chat State filter for a MessageSession.
|
||||||
|
* @param parent The MessageSession to decorate.
|
||||||
|
*/
|
||||||
|
ChatStateFilter( MessageSession* parent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatStateFilter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set a chat state as defined in XEP-0085.
|
||||||
|
* @note The Spec states that Chat States shall not be sent to an entity
|
||||||
|
* which did not request them. Reasonable effort is taken in this function to
|
||||||
|
* avoid spurious state sending. You should be safe to call this even if Message
|
||||||
|
* Events were not requested by the remote entity. However,
|
||||||
|
* calling setChatState( CHAT_STATE_COMPOSING ) for every keystroke still is
|
||||||
|
* discouraged. ;)
|
||||||
|
* @param state The state to set.
|
||||||
|
*/
|
||||||
|
void setChatState( ChatStateType state );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ChatStateHandler registered here will receive Chat States according
|
||||||
|
* to XEP-0085.
|
||||||
|
* @param csh The ChatStateHandler to register.
|
||||||
|
*/
|
||||||
|
void registerChatStateHandler( ChatStateHandler* csh )
|
||||||
|
{ m_chatStateHandler = csh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function clears the internal pointer to the ChatStateHandler.
|
||||||
|
* Chat States will not be delivered anymore after calling this function until another
|
||||||
|
* ChatStateHandler is registered.
|
||||||
|
*/
|
||||||
|
void removeChatStateHandler()
|
||||||
|
{ m_chatStateHandler = 0; }
|
||||||
|
|
||||||
|
// reimplemented from MessageFilter
|
||||||
|
virtual void decorate( Message& msg );
|
||||||
|
|
||||||
|
// reimplemented from MessageFilter
|
||||||
|
virtual void filter( Message& msg );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming chat state changes. */
|
||||||
|
ChatStateHandler* m_chatStateHandler;
|
||||||
|
|
||||||
|
/** Holds the state sent last. */
|
||||||
|
ChatStateType m_lastSent;
|
||||||
|
|
||||||
|
/** Indicates whether or not chat states are currently enabled. */
|
||||||
|
bool m_enableChatStates;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATEFILTER_H__
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CHATSTATEHANDLER_H__
|
||||||
|
#define CHATSTATEHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class JID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that enables an object to be notified about
|
||||||
|
* a remote entity's Chat States (XEP-0085).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
class GLOOX_API ChatStateHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ChatStateHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the ChatStateHandler that a different chat state has been set by the remote
|
||||||
|
* contact.
|
||||||
|
* @param from The originator of the Event.
|
||||||
|
* @param state The chat state set by the remote entity.
|
||||||
|
*/
|
||||||
|
virtual void handleChatState( const JID& from, ChatStateType state ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHATSTATEHANDLER_H__
|
|
@ -0,0 +1,603 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "client.h"
|
||||||
|
#include "capabilities.h"
|
||||||
|
#include "rostermanager.h"
|
||||||
|
#include "disco.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "nonsaslauth.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "stanzaextensionfactory.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "tlsbase.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#if !defined( _WIN32 ) && !defined( _WIN32_WCE )
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---- Client::ResourceBind ----
|
||||||
|
Client::ResourceBind::ResourceBind( const std::string& resource, bool bind )
|
||||||
|
: StanzaExtension( ExtResourceBind ), m_jid( JID() ), m_bind( bind )
|
||||||
|
{
|
||||||
|
prep::resourceprep( resource, m_resource );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::ResourceBind::ResourceBind( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtResourceBind ), m_resource( EmptyString ), m_bind( true )
|
||||||
|
{
|
||||||
|
if( !tag )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( tag->name() == "unbind" )
|
||||||
|
m_bind = false;
|
||||||
|
else if( tag->name() == "bind" )
|
||||||
|
m_bind = true;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( tag->hasChild( "jid" ) )
|
||||||
|
m_jid.setJID( tag->findChild( "jid" )->cdata() );
|
||||||
|
else if( tag->hasChild( "resource" ) )
|
||||||
|
m_resource = tag->findChild( "resource" )->cdata();
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::ResourceBind::~ResourceBind()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Client::ResourceBind::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/bind[@xmlns='" + XMLNS_STREAM_BIND + "']"
|
||||||
|
"|/iq/unbind[@xmlns='" + XMLNS_STREAM_BIND + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Client::ResourceBind::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( m_bind ? "bind" : "unbind" );
|
||||||
|
t->setXmlns( XMLNS_STREAM_BIND );
|
||||||
|
|
||||||
|
if( m_bind && m_resource.empty() && m_jid )
|
||||||
|
new Tag( t, "jid", m_jid.full() );
|
||||||
|
else
|
||||||
|
new Tag( t, "resource", m_resource );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Client::ResourceBind ----
|
||||||
|
|
||||||
|
// ---- Client::SessionCreation ----
|
||||||
|
Tag* Client::SessionCreation::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "session" );
|
||||||
|
t->setXmlns( XMLNS_STREAM_SESSION );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- Client::SessionCreation ----
|
||||||
|
|
||||||
|
// ---- Client ----
|
||||||
|
Client::Client( const std::string& server )
|
||||||
|
: ClientBase( XMLNS_CLIENT, server ),
|
||||||
|
m_rosterManager( 0 ), m_auth( 0 ),
|
||||||
|
m_presence( Presence::Available, JID() ), m_resourceBound( false ),
|
||||||
|
m_forceNonSasl( false ), m_manageRoster( true ),
|
||||||
|
m_streamFeatures( 0 )
|
||||||
|
{
|
||||||
|
m_jid.setServer( server );
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::Client( const JID& jid, const std::string& password, int port )
|
||||||
|
: ClientBase( XMLNS_CLIENT, password, EmptyString, port ),
|
||||||
|
m_rosterManager( 0 ), m_auth( 0 ),
|
||||||
|
m_presence( Presence::Available, JID() ), m_resourceBound( false ),
|
||||||
|
m_forceNonSasl( false ), m_manageRoster( true ),
|
||||||
|
m_streamFeatures( 0 )
|
||||||
|
{
|
||||||
|
m_jid = jid;
|
||||||
|
m_server = m_jid.serverRaw();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::~Client()
|
||||||
|
{
|
||||||
|
delete m_rosterManager;
|
||||||
|
delete m_auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::init()
|
||||||
|
{
|
||||||
|
m_rosterManager = new RosterManager( this );
|
||||||
|
m_disco->setIdentity( "client", "bot" );
|
||||||
|
registerStanzaExtension( new ResourceBind( 0 ) );
|
||||||
|
registerStanzaExtension( new Capabilities() );
|
||||||
|
m_presenceExtensions.push_back( new Capabilities( m_disco ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setUsername( const std::string &username )
|
||||||
|
{
|
||||||
|
m_jid.setUsername( username );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::handleNormalNode( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() == "features" && tag->xmlns() == XMLNS_STREAM )
|
||||||
|
{
|
||||||
|
m_streamFeatures = getStreamFeatures( tag );
|
||||||
|
|
||||||
|
if( m_tls == TLSRequired && !m_encryptionActive
|
||||||
|
&& ( !m_encryption || !( m_streamFeatures & StreamFeatureStartTls ) ) )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "Client is configured to require"
|
||||||
|
" TLS but either the server didn't offer TLS or"
|
||||||
|
" TLS support is not compiled in." );
|
||||||
|
disconnect( ConnTlsNotAvailable );
|
||||||
|
}
|
||||||
|
else if( m_tls > TLSDisabled && m_encryption && !m_encryptionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureStartTls ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventEncryption );
|
||||||
|
startTls();
|
||||||
|
}
|
||||||
|
else if( m_compress && m_compression && !m_compressionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureCompressZlib ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventCompression );
|
||||||
|
logInstance().warn( LogAreaClassClient, "The server offers compression, but negotiating Compression at this stage is not recommended. See XEP-0170 for details. We'll continue anyway." );
|
||||||
|
negotiateCompression( StreamFeatureCompressZlib );
|
||||||
|
}
|
||||||
|
else if( m_sasl )
|
||||||
|
{
|
||||||
|
if( m_authed )
|
||||||
|
{
|
||||||
|
if( m_streamFeatures & StreamFeatureBind )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventResourceBinding );
|
||||||
|
bindResource( resource() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( !username().empty() && !password().empty() )
|
||||||
|
{
|
||||||
|
if( !login() )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "The server doesn't support"
|
||||||
|
" any auth mechanisms we know about" );
|
||||||
|
disconnect( ConnNoSupportedAuth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( !m_clientCerts.empty() && !m_clientKey.empty()
|
||||||
|
&& m_streamFeatures & SaslMechExternal && m_availableSaslMechs & SaslMechExternal )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechExternal );
|
||||||
|
}
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
else if( m_streamFeatures & SaslMechGssapi && m_availableSaslMechs & SaslMechGssapi )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechGssapi );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & SaslMechNTLM && m_availableSaslMechs & SaslMechNTLM )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechNTLM );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if( m_streamFeatures & SaslMechAnonymous
|
||||||
|
&& m_availableSaslMechs & SaslMechAnonymous )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechAnonymous );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
connected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_compress && m_compression && !m_compressionActive
|
||||||
|
&& ( m_streamFeatures & StreamFeatureCompressZlib ) )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventCompression );
|
||||||
|
negotiateCompression( StreamFeatureCompressZlib );
|
||||||
|
}
|
||||||
|
// else if( ( m_streamFeatures & StreamFeatureCompressDclz )
|
||||||
|
// && m_connection->initCompression( StreamFeatureCompressDclz ) )
|
||||||
|
// {
|
||||||
|
// negotiateCompression( StreamFeatureCompressDclz );
|
||||||
|
// }
|
||||||
|
else if( m_streamFeatures & StreamFeatureIqAuth )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
nonSaslLogin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "fallback: the server doesn't "
|
||||||
|
"support any auth mechanisms we know about" );
|
||||||
|
disconnect( ConnNoSupportedAuth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string& name = tag->name(),
|
||||||
|
xmlns = tag->findAttribute( XMLNS );
|
||||||
|
if( name == "proceed" && xmlns == XMLNS_STREAM_TLS )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "starting TLS handshake..." );
|
||||||
|
|
||||||
|
if( m_encryption )
|
||||||
|
{
|
||||||
|
m_encryptionActive = true;
|
||||||
|
m_encryption->handshake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( name == "failure" )
|
||||||
|
{
|
||||||
|
if( xmlns == XMLNS_STREAM_TLS )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "TLS handshake failed (server-side)!" );
|
||||||
|
disconnect( ConnTlsFailed );
|
||||||
|
}
|
||||||
|
else if( xmlns == XMLNS_COMPRESSION )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "Stream compression init failed!" );
|
||||||
|
disconnect( ConnCompressionFailed );
|
||||||
|
}
|
||||||
|
else if( xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().err( LogAreaClassClient, "SASL authentication failed!" );
|
||||||
|
processSASLError( tag );
|
||||||
|
disconnect( ConnAuthenticationFailed );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( name == "compressed" && xmlns == XMLNS_COMPRESSION )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "Stream compression initialized" );
|
||||||
|
m_compressionActive = true;
|
||||||
|
header();
|
||||||
|
}
|
||||||
|
else if( name == "challenge" && xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "Processing SASL challenge" );
|
||||||
|
processSASLChallenge( tag->cdata() );
|
||||||
|
}
|
||||||
|
else if( name == "success" && xmlns == XMLNS_STREAM_SASL )
|
||||||
|
{
|
||||||
|
logInstance().dbg( LogAreaClassClient, "SASL authentication successful" );
|
||||||
|
processSASLSuccess();
|
||||||
|
setAuthed( true );
|
||||||
|
header();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getStreamFeatures( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "features" || tag->xmlns() != XMLNS_STREAM )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int features = 0;
|
||||||
|
|
||||||
|
if( tag->hasChild( "starttls", XMLNS, XMLNS_STREAM_TLS ) )
|
||||||
|
features |= StreamFeatureStartTls;
|
||||||
|
|
||||||
|
if( tag->hasChild( "mechanisms", XMLNS, XMLNS_STREAM_SASL ) )
|
||||||
|
features |= getSaslMechs( tag->findChild( "mechanisms" ) );
|
||||||
|
|
||||||
|
if( tag->hasChild( "bind", XMLNS, XMLNS_STREAM_BIND ) )
|
||||||
|
features |= StreamFeatureBind;
|
||||||
|
|
||||||
|
if( tag->hasChild( "unbind", XMLNS, XMLNS_STREAM_BIND ) )
|
||||||
|
features |= StreamFeatureUnbind;
|
||||||
|
|
||||||
|
if( tag->hasChild( "session", XMLNS, XMLNS_STREAM_SESSION ) )
|
||||||
|
features |= StreamFeatureSession;
|
||||||
|
|
||||||
|
if( tag->hasChild( "auth", XMLNS, XMLNS_STREAM_IQAUTH ) )
|
||||||
|
features |= StreamFeatureIqAuth;
|
||||||
|
|
||||||
|
if( tag->hasChild( "register", XMLNS, XMLNS_STREAM_IQREGISTER ) )
|
||||||
|
features |= StreamFeatureIqRegister;
|
||||||
|
|
||||||
|
if( tag->hasChild( "compression", XMLNS, XMLNS_STREAM_COMPRESS ) )
|
||||||
|
features |= getCompressionMethods( tag->findChild( "compression" ) );
|
||||||
|
|
||||||
|
if( features == 0 )
|
||||||
|
features = StreamFeatureIqAuth;
|
||||||
|
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getSaslMechs( Tag* tag )
|
||||||
|
{
|
||||||
|
int mechs = SaslMechNone;
|
||||||
|
|
||||||
|
const std::string mech = "mechanism";
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "DIGEST-MD5" ) )
|
||||||
|
mechs |= SaslMechDigestMd5;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "PLAIN" ) )
|
||||||
|
mechs |= SaslMechPlain;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "ANONYMOUS" ) )
|
||||||
|
mechs |= SaslMechAnonymous;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "EXTERNAL" ) )
|
||||||
|
mechs |= SaslMechExternal;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "GSSAPI" ) )
|
||||||
|
mechs |= SaslMechGssapi;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( mech, "NTLM" ) )
|
||||||
|
mechs |= SaslMechNTLM;
|
||||||
|
|
||||||
|
return mechs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Client::getCompressionMethods( Tag* tag )
|
||||||
|
{
|
||||||
|
int meths = 0;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( "method", "zlib" ) )
|
||||||
|
meths |= StreamFeatureCompressZlib;
|
||||||
|
|
||||||
|
if( tag->hasChildWithCData( "method", "lzw" ) )
|
||||||
|
meths |= StreamFeatureCompressDclz;
|
||||||
|
|
||||||
|
return meths;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::login()
|
||||||
|
{
|
||||||
|
bool retval = true;
|
||||||
|
|
||||||
|
if( m_streamFeatures & SaslMechDigestMd5 && m_availableSaslMechs & SaslMechDigestMd5
|
||||||
|
&& !m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechDigestMd5 );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & SaslMechPlain && m_availableSaslMechs & SaslMechPlain
|
||||||
|
&& !m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
startSASL( SaslMechPlain );
|
||||||
|
}
|
||||||
|
else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
nonSaslLogin();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
retval = false;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::handleIqIDForward( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case CtxResourceUnbind:
|
||||||
|
// we don't store known resources anyway
|
||||||
|
break;
|
||||||
|
case CtxResourceBind:
|
||||||
|
processResourceBind( iq );
|
||||||
|
break;
|
||||||
|
case CtxSessionEstablishment:
|
||||||
|
processCreateSession( iq );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::bindOperation( const std::string& resource, bool bind )
|
||||||
|
{
|
||||||
|
if( !( m_streamFeatures & StreamFeatureUnbind ) && m_resourceBound )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IQ iq( IQ::Set, JID(), getID() );
|
||||||
|
iq.addExtension( new ResourceBind( resource, bind ) );
|
||||||
|
|
||||||
|
send( iq, this, bind ? CtxResourceBind : CtxResourceUnbind );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::selectResource( const std::string& resource )
|
||||||
|
{
|
||||||
|
if( !( m_streamFeatures & StreamFeatureUnbind ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_selectedResource = resource;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::processResourceBind( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
{
|
||||||
|
const ResourceBind* rb = iq.findExtension<ResourceBind>( ExtResourceBind );
|
||||||
|
if( !rb || !rb->jid() )
|
||||||
|
{
|
||||||
|
notifyOnResourceBindError( 0 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_jid = rb->jid();
|
||||||
|
m_resourceBound = true;
|
||||||
|
m_selectedResource = m_jid.resource();
|
||||||
|
notifyOnResourceBind( m_jid.resource() );
|
||||||
|
|
||||||
|
if( m_streamFeatures & StreamFeatureSession )
|
||||||
|
createSession();
|
||||||
|
else
|
||||||
|
connected();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IQ::Error:
|
||||||
|
{
|
||||||
|
notifyOnResourceBindError( iq.error() );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::createSession()
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventSessionCreation );
|
||||||
|
IQ iq( IQ::Set, JID(), getID() );
|
||||||
|
iq.addExtension( new SessionCreation() );
|
||||||
|
send( iq, this, CtxSessionEstablishment );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::processCreateSession( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
connected();
|
||||||
|
break;
|
||||||
|
case IQ::Error:
|
||||||
|
notifyOnSessionCreateError( iq.error() );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::negotiateCompression( StreamFeature method )
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "compress", XMLNS, XMLNS_COMPRESSION );
|
||||||
|
|
||||||
|
if( method == StreamFeatureCompressZlib )
|
||||||
|
new Tag( t, "method", "zlib" );
|
||||||
|
|
||||||
|
if( method == StreamFeatureCompressDclz )
|
||||||
|
new Tag( t, "method", "lzw" );
|
||||||
|
|
||||||
|
send( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setPresence( Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status )
|
||||||
|
{
|
||||||
|
m_presence.setPresence( pres );
|
||||||
|
m_presence.setPriority( priority );
|
||||||
|
m_presence.addStatus( status );
|
||||||
|
sendPresence( m_presence );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setPresence( const JID& to, Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status )
|
||||||
|
{
|
||||||
|
Presence p( pres, to, status, priority );
|
||||||
|
sendPresence( p );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::sendPresence( Presence& pres )
|
||||||
|
{
|
||||||
|
if( state() < StateConnected )
|
||||||
|
return;
|
||||||
|
|
||||||
|
send( pres );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disableRoster()
|
||||||
|
{
|
||||||
|
m_manageRoster = false;
|
||||||
|
delete m_rosterManager;
|
||||||
|
m_rosterManager = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::nonSaslLogin()
|
||||||
|
{
|
||||||
|
if( !m_auth )
|
||||||
|
m_auth = new NonSaslAuth( this );
|
||||||
|
m_auth->doAuth( m_sid );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::connected()
|
||||||
|
{
|
||||||
|
if( m_authed )
|
||||||
|
{
|
||||||
|
if( m_manageRoster )
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventRoster );
|
||||||
|
m_rosterManager->fill();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rosterFilled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::rosterFilled()
|
||||||
|
{
|
||||||
|
sendPresence( m_presence );
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disconnect()
|
||||||
|
{
|
||||||
|
disconnect( ConnUserDisconnected );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::disconnect( ConnectionError reason )
|
||||||
|
{
|
||||||
|
m_resourceBound = false;
|
||||||
|
m_authed = false;
|
||||||
|
m_streamFeatures = 0;
|
||||||
|
ClientBase::disconnect( reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::cleanup()
|
||||||
|
{
|
||||||
|
m_authed = false;
|
||||||
|
m_resourceBound = false;
|
||||||
|
m_streamFeatures = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,441 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CLIENT_H__
|
||||||
|
#define CLIENT_H__
|
||||||
|
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "presence.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Capabilities;
|
||||||
|
class RosterManager;
|
||||||
|
class NonSaslAuth;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements a basic Jabber Client.
|
||||||
|
*
|
||||||
|
* It supports @ref sasl_auth as well as TLS (Encryption), which can be
|
||||||
|
* switched on/off separately. They are used automatically if the server supports them.
|
||||||
|
*
|
||||||
|
* To use, create a new Client instance and feed it connection credentials, either in the Constructor or
|
||||||
|
* afterwards using the setters. You should then register packet handlers implementing the corresponding
|
||||||
|
* Interfaces (ConnectionListener, PresenceHandler, MessageHandler, IqHandler, SubscriptionHandler),
|
||||||
|
* and call @ref connect() to establish the connection to the server.<br>
|
||||||
|
*
|
||||||
|
* @note While the MessageHandler interface is still available (and will be in future versions)
|
||||||
|
* it is now recommended to use the new @link gloox::MessageSession MessageSession @endlink for any
|
||||||
|
* serious messaging.
|
||||||
|
*
|
||||||
|
* Simple usage example:
|
||||||
|
* @code
|
||||||
|
* using namespace gloox;
|
||||||
|
*
|
||||||
|
* void TestProg::doIt()
|
||||||
|
* {
|
||||||
|
* Client* j = new Client( "user@server/resource", "password" );
|
||||||
|
* j->registerPresenceHandler( this );
|
||||||
|
* j->disco()->setVersion( "TestProg", "1.0" );
|
||||||
|
* j->disco()->setIdentity( "client", "bot" );
|
||||||
|
* j->connect();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* virtual void TestProg::presenceHandler( Presence* presence )
|
||||||
|
* {
|
||||||
|
* // handle incoming presence packets here
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* However, you can skip the presence handling stuff if you make use of the RosterManager.
|
||||||
|
*
|
||||||
|
* By default, the library handles a few (incoming) IQ namespaces on the application's behalf. These
|
||||||
|
* include:
|
||||||
|
* @li jabber:iq:roster: by default the server-side roster is fetched and handled. Use
|
||||||
|
* @ref rosterManager() and @ref RosterManager to interact with the Roster.
|
||||||
|
* @li XEP-0092 (Software Version): If no version is specified, a name of "based on gloox" with
|
||||||
|
* gloox's current version is announced.
|
||||||
|
* @li XEP-0030 (Service Discovery): All supported/available services are announced. No items are
|
||||||
|
* returned.
|
||||||
|
* @note As of gloox 0.9, by default a priority of 0 is sent along with the initial presence.
|
||||||
|
* @note As of gloox 0.9, initial presence is automatically sent. Presence: available, Priority: 0.
|
||||||
|
* To disable sending of initial Presence use setPresence() with a value of Unavailable
|
||||||
|
* prior to connecting.
|
||||||
|
*
|
||||||
|
* @section sasl_auth SASL Authentication
|
||||||
|
*
|
||||||
|
* Besides the simple, IQ-based authentication (XEP-0078), gloox supports several SASL (Simple
|
||||||
|
* Authentication and Security Layer, RFC 2222) authentication mechanisms.
|
||||||
|
* @li DIGEST-MD5: This mechanism is preferred over all other mechanisms if username and password are
|
||||||
|
* provided to the Client instance. It is secure even without TLS encryption.
|
||||||
|
* @li PLAIN: This mechanism is used if DIGEST-MD5 is not available. It is @b not secure without
|
||||||
|
* encryption.
|
||||||
|
* @li ANONYMOUS This mechanism is used if neither username nor password are set. The server generates
|
||||||
|
* random, temporary username and resource and may restrict available services.
|
||||||
|
* @li EXTERNAL This mechanism is currently only available if client certificate and private key
|
||||||
|
* are provided. The server tries to figure out who the client is by external means -- for instance,
|
||||||
|
* using the provided certificate or even the IP address. (The restriction to certificate/key
|
||||||
|
* availability is likely to be lifted in the future.)
|
||||||
|
*
|
||||||
|
* Of course, all these mechanisms are not tried unless the server offers them.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Client : public ClientBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
friend class NonSaslAuth;
|
||||||
|
friend class Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Client which can be used for account registration only.
|
||||||
|
* SASL and TLS are on by default. The port will be determined by looking up SRV records.
|
||||||
|
* Alternatively, you can set the port explicitly by calling @ref setPort().
|
||||||
|
* @param server The server to connect to.
|
||||||
|
*/
|
||||||
|
Client( const std::string& server );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Client.
|
||||||
|
* SASL and TLS are on by default. This should be the default constructor for most use cases.
|
||||||
|
* The server address will be taken from the JID. The actual host will be resolved using SRV
|
||||||
|
* records. The domain part of the JID is used as a fallback in case no SRV record is found, or
|
||||||
|
* you can set the server address separately by calling @ref setServer().
|
||||||
|
* @param jid A full Jabber ID used for connecting to the server.
|
||||||
|
* @param password The password used for authentication.
|
||||||
|
* @param port The port to connect to. The default of -1 means to look up the port via DNS SRV.
|
||||||
|
*/
|
||||||
|
Client( const JID& jid, const std::string& password, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Client();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to bind an additional resource or to @b re-try to bind a
|
||||||
|
* resource in case previous binding failed and you were notified by means of
|
||||||
|
* ConnectionListener::onResourceBindError(). Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. bindResource() is a NOOP if it doesn't.
|
||||||
|
* @note ConnectionListener::onResourceBound() and ConnectionListener::onResourceBindError()
|
||||||
|
* will be called in case of success and failure, respectively.
|
||||||
|
* @param resource The resource identifier to bind. May be empty. In that case
|
||||||
|
* the server will assign a unique resource identifier.
|
||||||
|
* @return Returns @b true if binding of multiple resources is supported, @b false
|
||||||
|
* otherwise. A return value of @b true does not indicate that the resource was
|
||||||
|
* successfully bound.
|
||||||
|
* @note It is not necessary to call this function to bind the initial, main, resource.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
bool bindResource( const std::string& resource )
|
||||||
|
{ return bindOperation( resource, true ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to select a resource identifier that has been bound
|
||||||
|
* previously by means of bindResource(). It is not necessary to call this function
|
||||||
|
* if only one resource is bound. Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. selectResource() is a NOOP if it doesn't.
|
||||||
|
* @param resource A resource string that has been bound previously.
|
||||||
|
* @note If the resource string has not been bound previously, future sending of
|
||||||
|
* stanzas will fail.
|
||||||
|
*/
|
||||||
|
bool selectResource( const std::string& resource );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to find out whether the server supports binding of multiple
|
||||||
|
* resources.
|
||||||
|
* @return @b True if binding of multiple resources is supported by the server,
|
||||||
|
* @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool hasResourceBind() const { return ((m_streamFeatures & StreamFeatureUnbind) == StreamFeatureUnbind); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to unbind a resource identifier that has been bound
|
||||||
|
* previously by means of bindResource(). Use hasResourceBind() to find out if the
|
||||||
|
* server supports binding of multiple resources. unbindResource() is a NOOP if it doesn't.
|
||||||
|
* @param resource A resource string that has been bound previously.
|
||||||
|
* @note Servers are encouraged to terminate the connection should the only bound
|
||||||
|
* resource be unbound.
|
||||||
|
*/
|
||||||
|
bool unbindResource( const std::string& resource )
|
||||||
|
{ return bindOperation( resource, false ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current prepped main resource.
|
||||||
|
* @return The resource used to connect.
|
||||||
|
*/
|
||||||
|
const std::string& resource() const { return m_jid.resource(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current priority.
|
||||||
|
* @return The priority of the current resource.
|
||||||
|
*/
|
||||||
|
int priority() const { return m_presence.priority(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the username to use to connect to the XMPP server.
|
||||||
|
* @param username The username to authenticate with.
|
||||||
|
*/
|
||||||
|
void setUsername( const std::string &username );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the main resource to use to connect to the XMPP server.
|
||||||
|
* @param resource The resource to use to log into the server.
|
||||||
|
*/
|
||||||
|
void setResource( const std::string &resource ) { m_jid.setResource( resource ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends directed presence to the given JID. This is a NOOP if there's no active connection.
|
||||||
|
* To broadcast presence use setPresence( Presence::PresenceType, int, const std::string& ).
|
||||||
|
* @param to The JID to send directed Presence to.
|
||||||
|
* @param pres The presence to send.
|
||||||
|
* @param priority The priority to include. Legal values: -128 <= priority <= 127
|
||||||
|
* @param status The optional status message to include.
|
||||||
|
* @note This function does not include any presence extensions (as added by
|
||||||
|
* means of addPresenceExtension()) to the stanza.
|
||||||
|
*/
|
||||||
|
void setPresence( const JID& to, Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the entity's presence, that is, to broadcast presence to all
|
||||||
|
* subscribed entities. To send directed presence, use
|
||||||
|
* setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
|
||||||
|
* If used prior to establishing a connection, the set values will be sent with
|
||||||
|
* the initial presence stanza.
|
||||||
|
* If used while a connection already is established, a presence stanza will be
|
||||||
|
* sent out immediately.
|
||||||
|
* @param pres The Presence value to set.
|
||||||
|
* @param priority An optional priority value. Legal values: -128 <= priority <= 127
|
||||||
|
* @param status An optional message describing the presence state.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
void setPresence( Presence::PresenceType pres, int priority,
|
||||||
|
const std::string& status = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to broadcast the entity's presence to all
|
||||||
|
* subscribed entities. This is a NOOP if there's no active connection.
|
||||||
|
* To send directed presence, use
|
||||||
|
* setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
|
||||||
|
* If used while a connection already is established a repective presence stanza will be
|
||||||
|
* sent out immediately. Use presence() to modify the Presence object.
|
||||||
|
* @note When login is finished, initial presence will be sent automatically.
|
||||||
|
* So you do not need to call this function after login.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
void setPresence() { sendPresence( m_presence ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current presence.
|
||||||
|
* @return The current presence.
|
||||||
|
*/
|
||||||
|
Presence& presence() { return m_presence; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a temporary hack to enforce Non-SASL login. You should not need to use it.
|
||||||
|
* @param force Whether to force non-SASL auth. Default @b true.
|
||||||
|
* @deprecated Please update the server to properly support SASL instead.
|
||||||
|
*/
|
||||||
|
GLOOX_DEPRECATED void setForceNonSasl( bool force = true ) { m_forceNonSasl = force; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables the automatic roster management.
|
||||||
|
* You have to keep track of incoming presence yourself if
|
||||||
|
* you want to have a roster.
|
||||||
|
*/
|
||||||
|
void disableRoster();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gives access to the @c RosterManager object.
|
||||||
|
* @return A pointer to the RosterManager.
|
||||||
|
*/
|
||||||
|
RosterManager* rosterManager() { return m_rosterManager; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from the server.
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a login attempt (currently SASL External not supported).
|
||||||
|
* This is useful after registering a new account. Simply use setUsername() and setPassword(),
|
||||||
|
* and call login().
|
||||||
|
* @return @b True if a login attempt could be started, @b false otherwise. A return
|
||||||
|
* value of @b true does not indicate that login was successful.
|
||||||
|
*/
|
||||||
|
bool login();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Initiates non-SASL login.
|
||||||
|
*/
|
||||||
|
void nonSaslLogin();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a resource binding StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ResourceBind : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object with the given resource string.
|
||||||
|
* @param resource The resource to set.
|
||||||
|
* @param bind Indicates whether this is an bind or unbind request.
|
||||||
|
* Defaults to @b true (bind).
|
||||||
|
*/
|
||||||
|
ResourceBind( const std::string& resource, bool bind = true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
ResourceBind( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~ResourceBind();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the requested resource.
|
||||||
|
* @return The requested resource.
|
||||||
|
*/
|
||||||
|
const std::string& resource() const { return m_resource; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the assigned JID.
|
||||||
|
* @return The assigned JID.
|
||||||
|
*/
|
||||||
|
const JID& jid() const { return m_jid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to find out whether the extension contains a
|
||||||
|
* bind or unbind request.
|
||||||
|
* @return @b True if the extension contains an unbind request, @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool unbind() const { return !m_bind; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new ResourceBind( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new ResourceBind( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_resource;
|
||||||
|
JID m_jid;
|
||||||
|
bool m_bind;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a session creating StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class SessionCreation : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object.
|
||||||
|
*/
|
||||||
|
SessionCreation() : StanzaExtension( ExtSessionCreation ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~SessionCreation() {}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const { return EmptyString; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{ (void)tag; return 0; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{ return 0; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void handleStartNode() {}
|
||||||
|
virtual bool handleNormalNode( Tag* tag );
|
||||||
|
virtual void disconnect( ConnectionError reason );
|
||||||
|
virtual void handleIqIDForward( const IQ& iq, int context );
|
||||||
|
|
||||||
|
int getStreamFeatures( Tag* tag );
|
||||||
|
int getSaslMechs( Tag* tag );
|
||||||
|
int getCompressionMethods( Tag* tag );
|
||||||
|
void processResourceBind( const IQ& iq );
|
||||||
|
void processCreateSession( const IQ& iq );
|
||||||
|
void sendPresence( Presence& pres );
|
||||||
|
void createSession();
|
||||||
|
void negotiateCompression( StreamFeature method );
|
||||||
|
void connected();
|
||||||
|
virtual void rosterFilled();
|
||||||
|
virtual void cleanup();
|
||||||
|
bool bindOperation( const std::string& resource, bool bind );
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
enum TrackContext
|
||||||
|
{
|
||||||
|
CtxResourceBind = 1000, // must be higher than the last element in ClientBase's TrackContext
|
||||||
|
CtxResourceUnbind,
|
||||||
|
CtxSessionEstablishment
|
||||||
|
};
|
||||||
|
|
||||||
|
RosterManager* m_rosterManager;
|
||||||
|
NonSaslAuth* m_auth;
|
||||||
|
|
||||||
|
Presence m_presence;
|
||||||
|
|
||||||
|
bool m_resourceBound;
|
||||||
|
bool m_forceNonSasl;
|
||||||
|
bool m_manageRoster;
|
||||||
|
|
||||||
|
int m_streamFeatures;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLIENT_H__
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "component.h"
|
||||||
|
|
||||||
|
#include "disco.h"
|
||||||
|
#include "stanza.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
Component::Component( const std::string& ns, const std::string& server,
|
||||||
|
const std::string& component, const std::string& password, int port )
|
||||||
|
: ClientBase( ns, password, server, port )
|
||||||
|
{
|
||||||
|
m_jid.setServer( component );
|
||||||
|
m_disco->setIdentity( "component", "generic" );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Component::handleStartNode()
|
||||||
|
{
|
||||||
|
if( m_sid.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
notifyStreamEvent( StreamEventAuthentication );
|
||||||
|
|
||||||
|
SHA sha;
|
||||||
|
sha.feed( m_sid + m_password );
|
||||||
|
sha.finalize();
|
||||||
|
|
||||||
|
Tag* h = new Tag( "handshake", sha.hex() );
|
||||||
|
send( h );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Component::handleNormalNode( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "handshake" )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_authed = true;
|
||||||
|
notifyStreamEvent( StreamEventFinished );
|
||||||
|
notifyOnConnect();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPONENT_H__
|
||||||
|
#define COMPONENT_H__
|
||||||
|
|
||||||
|
#include "clientbase.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a basic jabber Component.
|
||||||
|
*
|
||||||
|
* It's using XEP-0114 (Jabber Component Protocol) to authenticate with a server.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API Component : public ClientBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new Component.
|
||||||
|
* @param ns The namespace that qualifies the stream. Either @b jabber:component:accept or
|
||||||
|
* @b jabber:component:connect. See XEP-0114 for details.
|
||||||
|
* @param server The server to connect to.
|
||||||
|
* @param component The component's hostname. FQDN.
|
||||||
|
* @param password The component's password.
|
||||||
|
* @param port The port to connect to. The default of 5347 is the default port of the router
|
||||||
|
* in jabberd2.
|
||||||
|
*/
|
||||||
|
Component( const std::string& ns, const std::string& server,
|
||||||
|
const std::string& component, const std::string& password, int port = 5347 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Component() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from the server.
|
||||||
|
*/
|
||||||
|
void disconnect() { ClientBase::disconnect( ConnUserDisconnected ); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual void handleStartNode();
|
||||||
|
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual bool handleNormalNode( Tag* tag );
|
||||||
|
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual bool checkStreamVersion( const std::string& /*version*/ ) { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// reimplemented from ClientBase
|
||||||
|
virtual void rosterFilled() {}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPONENT_H__
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONBASE_H__
|
||||||
|
#define COMPRESSIONBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "compressiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class for stream compression implementations.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contructor.
|
||||||
|
* @param cdh A CompressionDataHandler-derived object that will be notified
|
||||||
|
* about finished de/compression.
|
||||||
|
*/
|
||||||
|
CompressionBase( CompressionDataHandler* cdh ) : m_handler( cdh ), m_valid( false ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionBase() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function initializes the compression module.
|
||||||
|
* it is mandatory to be called.
|
||||||
|
* @return @b True if the module was initialized successfully, false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool init() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses the given chunk of data.
|
||||||
|
* @param data The original (uncompressed) data.
|
||||||
|
*/
|
||||||
|
virtual void compress( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses the given chunk of data.
|
||||||
|
* @param data The compressed data.
|
||||||
|
*/
|
||||||
|
virtual void decompress( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs internal cleanup.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void cleanup() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for compressed/uncompressed data. */
|
||||||
|
CompressionDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** Whether the compression module can be used. */
|
||||||
|
bool m_valid;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONBASE_H__
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONDATAHANDLER_H__
|
||||||
|
#define COMPRESSIONDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class used to receive de/compressed data from a
|
||||||
|
* CompressionBase-derived object.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when compression is finished.
|
||||||
|
* @param data The compressed data.
|
||||||
|
*/
|
||||||
|
virtual void handleCompressedData( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when decompression is finished.
|
||||||
|
* @param data The decompressed data.
|
||||||
|
*/
|
||||||
|
virtual void handleDecompressedData( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONDATAHANDLER_H__
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compressiondefault.h"
|
||||||
|
|
||||||
|
#include "compressiondatahandler.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if defined( HAVE_ZLIB )
|
||||||
|
# define HAVE_COMPRESSION
|
||||||
|
# include "compressionzlib.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #if defined( HAVE_LZW )
|
||||||
|
// # define HAVE_COMPRESSION
|
||||||
|
// # include "compressionlzw.h"
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
CompressionDefault::CompressionDefault( CompressionDataHandler* cdh, Method method )
|
||||||
|
: CompressionBase( cdh ), m_impl( 0 )
|
||||||
|
{
|
||||||
|
switch( method )
|
||||||
|
{
|
||||||
|
case MethodZlib:
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
m_impl = new CompressionZlib( cdh );
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case MethodLZW:
|
||||||
|
#ifdef HAVE_LZW
|
||||||
|
m_impl = new CompressionLZW( cdh );
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionDefault::~CompressionDefault()
|
||||||
|
{
|
||||||
|
delete m_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompressionDefault::init()
|
||||||
|
{
|
||||||
|
return m_impl ? m_impl->init() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CompressionDefault::types()
|
||||||
|
{
|
||||||
|
int types = 0;
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
types |= MethodZlib;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LZW
|
||||||
|
types |= MethodLZW;
|
||||||
|
#endif
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::compress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->compress( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::decompress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->decompress( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionDefault::cleanup()
|
||||||
|
{
|
||||||
|
if( m_impl )
|
||||||
|
m_impl->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONDEFAULT_H__
|
||||||
|
#define COMPRESSIONDEFAULT_H__
|
||||||
|
|
||||||
|
#include "compressionbase.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class CompressionDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstraction of the various Compression implementations.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionDefault : public CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported ctypes.
|
||||||
|
*/
|
||||||
|
enum Method
|
||||||
|
{
|
||||||
|
MethodZlib = 1, /**< Zlib compression. */
|
||||||
|
MethodLZW = 2 /**< LZW compression. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new compression wrapper.
|
||||||
|
* @param cdh The CompressionDataHandler to handle de/compressed data.
|
||||||
|
* @param method The desired compression method.
|
||||||
|
*/
|
||||||
|
CompressionDefault( CompressionDataHandler* cdh, Method method = MethodZlib );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionDefault();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an int holding the available compression types, ORed.
|
||||||
|
* @return An int holding the available compression types, ORed.
|
||||||
|
*/
|
||||||
|
static int types();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual bool init();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void compress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void decompress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CompressionBase* m_impl;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // COMPRESSIONDEFAULT_H__
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "compressionzlib.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
CompressionZlib::CompressionZlib( CompressionDataHandler* cdh )
|
||||||
|
: CompressionBase( cdh )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompressionZlib::init()
|
||||||
|
{
|
||||||
|
int ret = Z_OK;
|
||||||
|
m_zinflate.zalloc = Z_NULL;
|
||||||
|
m_zinflate.zfree = Z_NULL;
|
||||||
|
m_zinflate.opaque = Z_NULL;
|
||||||
|
m_zinflate.avail_in = 0;
|
||||||
|
m_zinflate.next_in = Z_NULL;
|
||||||
|
ret = inflateInit( &m_zinflate );
|
||||||
|
if( ret != Z_OK )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_zdeflate.zalloc = Z_NULL;
|
||||||
|
m_zdeflate.zfree = Z_NULL;
|
||||||
|
m_zdeflate.opaque = Z_NULL;
|
||||||
|
m_zinflate.avail_in = 0;
|
||||||
|
m_zinflate.next_in = Z_NULL;
|
||||||
|
ret = deflateInit( &m_zdeflate, Z_BEST_COMPRESSION/*Z_DEFAULT_COMPRESSION*/ );
|
||||||
|
if( ret != Z_OK )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_valid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompressionZlib::~CompressionZlib()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::compress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
init();
|
||||||
|
|
||||||
|
if( !m_valid || !m_handler || data.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
long unsigned int CHUNK = data.length() + ( data.length() / 100 ) + 13;
|
||||||
|
Bytef* out = new Bytef[CHUNK];
|
||||||
|
char* in = const_cast<char*>( data.c_str() );
|
||||||
|
|
||||||
|
m_compressMutex.lock();
|
||||||
|
|
||||||
|
m_zdeflate.avail_in = static_cast<uInt>( data.length() );
|
||||||
|
m_zdeflate.next_in = (Bytef*)in;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
std::string result;
|
||||||
|
do {
|
||||||
|
m_zdeflate.avail_out = static_cast<uInt>( CHUNK );
|
||||||
|
m_zdeflate.next_out = (Bytef*)out;
|
||||||
|
|
||||||
|
ret = deflate( &m_zdeflate, Z_SYNC_FLUSH );
|
||||||
|
result.append( (char*)out, CHUNK - m_zdeflate.avail_out );
|
||||||
|
} while( m_zdeflate.avail_out == 0 );
|
||||||
|
|
||||||
|
m_compressMutex.unlock();
|
||||||
|
|
||||||
|
delete[] out;
|
||||||
|
|
||||||
|
m_handler->handleCompressedData( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::decompress( const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
init();
|
||||||
|
|
||||||
|
if( !m_valid || !m_handler || data.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
int CHUNK = 50;
|
||||||
|
char* out = new char[CHUNK];
|
||||||
|
char* in = const_cast<char*>( data.c_str() );
|
||||||
|
|
||||||
|
m_zinflate.avail_in = static_cast<uInt>( data.length() );
|
||||||
|
m_zinflate.next_in = (Bytef*)in;
|
||||||
|
|
||||||
|
int ret = Z_OK;
|
||||||
|
std::string result;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
m_zinflate.avail_out = CHUNK;
|
||||||
|
m_zinflate.next_out = (Bytef*)out;
|
||||||
|
|
||||||
|
ret = inflate( &m_zinflate, Z_SYNC_FLUSH );
|
||||||
|
result.append( out, CHUNK - m_zinflate.avail_out );
|
||||||
|
} while( m_zinflate.avail_out == 0 );
|
||||||
|
|
||||||
|
delete[] out;
|
||||||
|
|
||||||
|
m_handler->handleDecompressedData( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompressionZlib::cleanup()
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return;
|
||||||
|
|
||||||
|
inflateEnd( &m_zinflate );
|
||||||
|
deflateEnd( &m_zdeflate );
|
||||||
|
|
||||||
|
m_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_ZLIB
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMPRESSIONZLIB_H__
|
||||||
|
#define COMPRESSIONZLIB_H__
|
||||||
|
|
||||||
|
#include "compressionbase.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* An implementation of CompressionBase using zlib.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API CompressionZlib : public CompressionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Contructor.
|
||||||
|
* @param cdh The CompressionDataHandler to receive de/compressed data.
|
||||||
|
*/
|
||||||
|
CompressionZlib( CompressionDataHandler* cdh );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~CompressionZlib();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual bool init();
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void compress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void decompress( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from CompressionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
z_stream m_zinflate;
|
||||||
|
z_stream m_zdeflate;
|
||||||
|
|
||||||
|
util::Mutex m_compressMutex;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_ZLIB
|
||||||
|
|
||||||
|
#endif // COMPRESSIONZLIB_H__
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_H__
|
||||||
|
#define CONFIG_H__
|
||||||
|
|
||||||
|
#if ( defined _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include "../config.h.win"
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include "../config.h.win"
|
||||||
|
#elif defined( __SYMBIAN32__ )
|
||||||
|
# include "../config.h.symbian"
|
||||||
|
#else
|
||||||
|
# include "config.h.unix" // run ./configure to create config.h.unix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CONFIG_H__
|
|
@ -0,0 +1,123 @@
|
||||||
|
/* config.h.unix. Generated from config.h.unix.in by configure. */
|
||||||
|
/* config.h.unix.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/nameser.h> header file. */
|
||||||
|
#define HAVE_ARPA_NAMESER_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#define HAVE_DLFCN_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `dn_skipname' function. */
|
||||||
|
/* #undef HAVE_DN_SKIPNAME */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#define HAVE_ERRNO_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getaddrinfo' function. */
|
||||||
|
/* #undef HAVE_GETADDRINFO */
|
||||||
|
|
||||||
|
/* Define to 1 if you want TLS support (GnuTLS). Undefine HAVE_OPENSSL. */
|
||||||
|
/* #undef HAVE_GNUTLS */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `bind' library (-lbind). */
|
||||||
|
/* #undef HAVE_LIBBIND */
|
||||||
|
|
||||||
|
/* Define to 1 if you want IDN support. */
|
||||||
|
/* #undef HAVE_LIBIDN */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `network' library (-lnetwork). */
|
||||||
|
#define HAVE_LIBNETWORK 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `resolv' library (-lresolv). */
|
||||||
|
/* #undef HAVE_LIBRESOLV */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' library (-lsocket). */
|
||||||
|
/* #undef HAVE_LIBSOCKET */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#define HAVE_MEMORY_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you want TLS support (OpenSSL). Undefine HAVE_GNUTLS. */
|
||||||
|
#define HAVE_OPENSSL 1
|
||||||
|
|
||||||
|
/* Define if you have POSIX threads libraries and header files. */
|
||||||
|
#define HAVE_PTHREAD 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `res_query' function. */
|
||||||
|
/* #undef HAVE_RES_QUERY */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `res_querydomain' function. */
|
||||||
|
/* #undef HAVE_RES_QUERYDOMAIN */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `setsockopt' function. */
|
||||||
|
#define HAVE_SETSOCKOPT 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#define HAVE_STDINT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#define HAVE_STDLIB_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#define HAVE_STRINGS_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#define HAVE_STRING_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#define HAVE_SYS_TYPES_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#define HAVE_UNISTD_H 1
|
||||||
|
|
||||||
|
/* Define to 1 if you want Stream Compression support. */
|
||||||
|
#define HAVE_ZLIB 1
|
||||||
|
|
||||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||||
|
*/
|
||||||
|
#define LT_OBJDIR ".libs/"
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#define PACKAGE "gloox"
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#define PACKAGE_BUGREPORT "js@camaya.net"
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#define PACKAGE_NAME "gloox"
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#define PACKAGE_STRING "gloox 1.0"
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#define PACKAGE_TARNAME "gloox"
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#define PACKAGE_URL ""
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#define PACKAGE_VERSION "1.0"
|
||||||
|
|
||||||
|
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||||
|
your system. */
|
||||||
|
/* #undef PTHREAD_CREATE_JOINABLE */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#define STDC_HEADERS 1
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
/* #undef const */
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
/* #undef inline */
|
||||||
|
#endif
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONBASE_H__
|
||||||
|
#define CONNECTIONBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class for a connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param cdh An object derived from @ref ConnectionDataHandler that will receive
|
||||||
|
* received data.
|
||||||
|
*/
|
||||||
|
ConnectionBase( ConnectionDataHandler* cdh )
|
||||||
|
: m_handler( cdh ), m_state( StateDisconnected ), m_port( -1 )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionBase() { cleanup(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to initiate the connection.
|
||||||
|
* @return Returns the connection state.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError connect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this periodically to receive data from the socket.
|
||||||
|
* @param timeout The timeout to use for select in microseconds. Default of -1 means blocking.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError recv( int timeout = -1 ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to send a string of data over the wire. The function returns only after
|
||||||
|
* all data has been sent.
|
||||||
|
* @param data The data to send.
|
||||||
|
* @return @b True if the data has been sent (no guarantee of receipt), @b false
|
||||||
|
* in case of an error.
|
||||||
|
*/
|
||||||
|
virtual bool send( const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to put the connection into 'receive mode', i.e. this function returns only
|
||||||
|
* when the connection is terminated.
|
||||||
|
* @return Returns a value indicating the disconnection reason.
|
||||||
|
*/
|
||||||
|
virtual ConnectionError receive() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects an established connection. NOOP if no active connection exists.
|
||||||
|
*/
|
||||||
|
virtual void disconnect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called after a disconnect to clean up internal state. It is also called by
|
||||||
|
* ConnectionBase's destructor.
|
||||||
|
*/
|
||||||
|
virtual void cleanup() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current connection state.
|
||||||
|
* @return The state of the connection.
|
||||||
|
*/
|
||||||
|
ConnectionState state() const { return m_state; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a new ConnectionDataHandler. There can be only one
|
||||||
|
* ConnectionDataHandler at any one time.
|
||||||
|
* @param cdh The new ConnectionDataHandler.
|
||||||
|
*/
|
||||||
|
void registerConnectionDataHandler( ConnectionDataHandler* cdh ) { m_handler = cdh; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server to connect to.
|
||||||
|
* @param server The server to connect to. Either IP or fully qualified domain name.
|
||||||
|
* @param port The port to connect to.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string &server, int port = -1 ) { m_server = server; m_port = port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently set server/IP.
|
||||||
|
* @return The server host/IP.
|
||||||
|
*/
|
||||||
|
const std::string& server() const { return m_server; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently set port.
|
||||||
|
* @return The server port.
|
||||||
|
*/
|
||||||
|
int port() const { return m_port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local port.
|
||||||
|
* @return The local port.
|
||||||
|
*/
|
||||||
|
virtual int localPort() const { return -1; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locally bound IP address.
|
||||||
|
* @return The locally bound IP address.
|
||||||
|
*/
|
||||||
|
virtual const std::string localInterface() const { return EmptyString; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current connection statistics.
|
||||||
|
* @param totalIn The total number of bytes received.
|
||||||
|
* @param totalOut The total number of bytes sent.
|
||||||
|
*/
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns a new instance of the current ConnectionBase-derived object.
|
||||||
|
* The idea is to be able to 'clone' ConnectionBase-derived objects without knowing of
|
||||||
|
* what type they are exactly.
|
||||||
|
* @return A new Connection* instance.
|
||||||
|
*/
|
||||||
|
virtual ConnectionBase* newInstance() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** A handler for incoming data and connect/disconnect events. */
|
||||||
|
ConnectionDataHandler* m_handler;
|
||||||
|
|
||||||
|
/** Holds the current connection state. */
|
||||||
|
ConnectionState m_state;
|
||||||
|
|
||||||
|
/** Holds the server's name/address. */
|
||||||
|
std::string m_server;
|
||||||
|
|
||||||
|
/** Holds the port to connect to. */
|
||||||
|
int m_port;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONBASE_H__
|
|
@ -0,0 +1,641 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionbosh.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& boshHost, const std::string& xmppServer,
|
||||||
|
int xmppPort )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
|
||||||
|
m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
|
||||||
|
m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
|
||||||
|
m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
|
||||||
|
m_connMode( ModePipelining )
|
||||||
|
{
|
||||||
|
initInstance( connection, xmppServer, xmppPort );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
|
||||||
|
m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
|
||||||
|
m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
|
||||||
|
m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
|
||||||
|
m_connMode( ModePipelining )
|
||||||
|
{
|
||||||
|
initInstance( connection, xmppServer, xmppPort );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer,
|
||||||
|
const int xmppPort )
|
||||||
|
{
|
||||||
|
// FIXME: check return value
|
||||||
|
prep::idna( xmppServer, m_server );
|
||||||
|
m_port = xmppPort;
|
||||||
|
if( m_port != -1 )
|
||||||
|
{
|
||||||
|
m_boshedHost = m_boshHost + ":" + util::int2string( m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop this connection into our pool of available connections
|
||||||
|
if( connection )
|
||||||
|
{
|
||||||
|
connection->registerConnectionDataHandler( this );
|
||||||
|
m_connectionPool.push_back( connection );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBOSH::~ConnectionBOSH()
|
||||||
|
{
|
||||||
|
util::clearList( m_activeConnections );
|
||||||
|
util::clearList( m_connectionPool );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* pBaseConn = 0;
|
||||||
|
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
pBaseConn = m_connectionPool.front()->newInstance();
|
||||||
|
}
|
||||||
|
else if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
pBaseConn = m_activeConnections.front()->newInstance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance,
|
||||||
|
m_boshHost, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::connect()
|
||||||
|
{
|
||||||
|
if( m_state >= StateConnecting )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
if( !m_handler )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh initiating connection to server: " +
|
||||||
|
( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" )
|
||||||
|
: ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" )
|
||||||
|
: std::string( "PersistentHTTP" ) ) ) );
|
||||||
|
getConnection();
|
||||||
|
return ConnNoError; // FIXME?
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::disconnect()
|
||||||
|
{
|
||||||
|
if( ( m_connMode == ModePipelining && m_activeConnections.empty() )
|
||||||
|
|| ( m_connectionPool.empty() && m_activeConnections.empty() ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_state != StateDisconnected )
|
||||||
|
{
|
||||||
|
++m_rid;
|
||||||
|
|
||||||
|
std::string requestBody = "<body rid='" + util::int2string( m_rid ) + "' ";
|
||||||
|
requestBody += "sid='" + m_sid + "' ";
|
||||||
|
requestBody += "type='terminal' ";
|
||||||
|
requestBody += "xml:lang='en' ";
|
||||||
|
requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
|
||||||
|
if( m_sendBuffer.empty() ) // Make sure that any data in the send buffer gets sent
|
||||||
|
requestBody += "/>";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBody += ">" + m_sendBuffer + "</body>";
|
||||||
|
m_sendBuffer = EmptyString;
|
||||||
|
}
|
||||||
|
sendRequest( requestBody );
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "bosh disconnection request sent" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_logInstance.err( LogAreaClassConnectionBOSH,
|
||||||
|
"disconnecting from server in a non-graceful fashion" );
|
||||||
|
}
|
||||||
|
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::disconnect );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::disconnect );
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnUserDisconnected );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_state == StateDisconnected )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
m_connectionPool.front()->recv( 0 );
|
||||||
|
if( !m_activeConnections.empty() )
|
||||||
|
m_activeConnections.front()->recv( timeout );
|
||||||
|
|
||||||
|
// If there are no open requests then the spec allows us to send an empty request...
|
||||||
|
// (Some CMs do not obey this, it seems)
|
||||||
|
if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"Sending empty request (or there is data in the send buffer)" );
|
||||||
|
sendXML();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNoError; // FIXME?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionBOSH::send( const std::string& data )
|
||||||
|
{
|
||||||
|
|
||||||
|
if( m_state == StateDisconnected )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( data.substr( 0, 2 ) == "<?" )
|
||||||
|
{
|
||||||
|
// if( m_initialStreamSent )
|
||||||
|
{
|
||||||
|
m_streamRestart = true;
|
||||||
|
sendXML();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// m_initialStreamSent = true;
|
||||||
|
// m_logInstance.dbg( LogAreaClassConnectionBOSH, "initial <stream:stream> dropped" );
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else if( data == "</stream:stream>" )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_sendBuffer += data;
|
||||||
|
sendXML();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends XML. Wraps data in a <body/> tag, and then passes to sendRequest(). */
|
||||||
|
bool ConnectionBOSH::sendXML()
|
||||||
|
{
|
||||||
|
if( m_state != StateConnected )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Data sent before connection established (will be buffered)" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_sendBuffer.empty() )
|
||||||
|
{
|
||||||
|
time_t now = time( 0 );
|
||||||
|
unsigned int delta = (int)(now - m_lastRequestTime);
|
||||||
|
if( delta < m_minTimePerRequest && m_openRequests > 0 )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Too little time between requests: " + util::int2string( delta ) + " seconds" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Send buffer is empty, sending empty request" );
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_rid;
|
||||||
|
|
||||||
|
std::string requestBody = "<body rid='" + util::int2string( m_rid ) + "' ";
|
||||||
|
requestBody += "sid='" + m_sid + "' ";
|
||||||
|
requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
|
||||||
|
|
||||||
|
if( m_streamRestart )
|
||||||
|
{
|
||||||
|
requestBody += " xmpp:restart='true' to='" + m_server + "' xml:lang='en' xmlns:xmpp='"
|
||||||
|
+ XMLNS_XMPP_BOSH + "' />";
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
requestBody += ">" + m_sendBuffer + "</body>";
|
||||||
|
}
|
||||||
|
// Send a request. Force if we are not sending an empty request, or if there are no connections open
|
||||||
|
if( sendRequest( requestBody ) )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Successfully sent m_sendBuffer" );
|
||||||
|
m_sendBuffer = EmptyString;
|
||||||
|
m_streamRestart = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
--m_rid; // I think... (may need to rethink when acks are implemented)
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Unable to send. Connection not complete, or too many open requests,"
|
||||||
|
" so added to buffer.\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */
|
||||||
|
bool ConnectionBOSH::sendRequest( const std::string& xml )
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = getConnection();
|
||||||
|
if( !conn )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string request = "POST " + m_path;
|
||||||
|
if( m_connMode == ModeLegacyHTTP )
|
||||||
|
{
|
||||||
|
request += " HTTP/1.0\r\n";
|
||||||
|
request += "Connection: close\r\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
request += " HTTP/1.1\r\n";
|
||||||
|
|
||||||
|
request += "Host: " + m_boshedHost + "\r\n";
|
||||||
|
request += "Content-Type: text/xml; charset=utf-8\r\n";
|
||||||
|
request += "Content-Length: " + util::int2string( xml.length() ) + "\r\n";
|
||||||
|
request += "User-Agent: gloox/" + GLOOX_VERSION + "\r\n\r\n";
|
||||||
|
request += xml;
|
||||||
|
|
||||||
|
|
||||||
|
if( conn->send( request ) )
|
||||||
|
{
|
||||||
|
m_lastRequestTime = time( 0 );
|
||||||
|
++m_openRequests;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// else // FIXME What to do in this case?
|
||||||
|
// printf( "Error while trying to send on socket (state: %d)\n", conn->state() );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ci_equal( char ch1, char ch2 )
|
||||||
|
{
|
||||||
|
return std::toupper( (unsigned char)ch1 ) == std::toupper( (unsigned char)ch2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::size_type ci_find( const std::string& str1, const std::string& str2 )
|
||||||
|
{
|
||||||
|
std::string::const_iterator pos = std::search( str1.begin(), str1.end(),
|
||||||
|
str2.begin(), str2.end(), ci_equal );
|
||||||
|
if( pos == str1.end() )
|
||||||
|
return std::string::npos;
|
||||||
|
else
|
||||||
|
return std::distance( str1.begin(), pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ConnectionBOSH::getHTTPField( const std::string& field )
|
||||||
|
{
|
||||||
|
std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " );
|
||||||
|
|
||||||
|
if( fp == std::string::npos )
|
||||||
|
return EmptyString;
|
||||||
|
|
||||||
|
fp += field.length() + 4;
|
||||||
|
|
||||||
|
const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp );
|
||||||
|
if( fp2 == std::string::npos )
|
||||||
|
return EmptyString;
|
||||||
|
|
||||||
|
return m_bufferHeader.substr( fp, fp2 - fp );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionBOSH::receive()
|
||||||
|
{
|
||||||
|
ConnectionError err = ConnNoError;
|
||||||
|
while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError )
|
||||||
|
;
|
||||||
|
return err == ConnNoError ? ConnNotConnected : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::cleanup );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::cleanup );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut );
|
||||||
|
util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
m_buffer += data;
|
||||||
|
std::string::size_type headerLength = 0;
|
||||||
|
while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos )
|
||||||
|
{
|
||||||
|
m_bufferHeader = m_buffer.substr( 0, headerLength+2 );
|
||||||
|
|
||||||
|
const std::string& statusCode = m_bufferHeader.substr( 9, 3 );
|
||||||
|
if( statusCode != "200" )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Received error via legacy HTTP status code: " + statusCode
|
||||||
|
+ ". Disconnecting." );
|
||||||
|
m_state = StateDisconnected; // As per XEP, consider connection broken
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() );
|
||||||
|
if( !m_bufferContentLength )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close"
|
||||||
|
|| m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" );
|
||||||
|
m_connMode = ModeLegacyHTTP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) )
|
||||||
|
{
|
||||||
|
putConnection();
|
||||||
|
--m_openRequests;
|
||||||
|
std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength );
|
||||||
|
m_parser.feed( xml );
|
||||||
|
m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength );
|
||||||
|
m_bufferContentLength = 0;
|
||||||
|
m_bufferHeader = EmptyString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH, "buffer length mismatch" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_rid = rand() % 100000 + 1728679472;
|
||||||
|
|
||||||
|
Tag requestBody( "body" );
|
||||||
|
requestBody.setXmlns( XMLNS_HTTPBIND );
|
||||||
|
requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" );
|
||||||
|
|
||||||
|
requestBody.addAttribute( "content", "text/xml; charset=utf-8" );
|
||||||
|
requestBody.addAttribute( "hold", (long)m_hold );
|
||||||
|
requestBody.addAttribute( "rid", (long)m_rid );
|
||||||
|
requestBody.addAttribute( "ver", "1.6" );
|
||||||
|
requestBody.addAttribute( "wait", (long)m_wait );
|
||||||
|
requestBody.addAttribute( "ack", 0 );
|
||||||
|
requestBody.addAttribute( "secure", "false" );
|
||||||
|
requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" );
|
||||||
|
requestBody.addAttribute( "xml:lang", "en" );
|
||||||
|
requestBody.addAttribute( "xmpp:version", "1.0" );
|
||||||
|
requestBody.addAttribute( "to", m_server );
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending bosh connection request" );
|
||||||
|
sendRequest( requestBody.xml() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
if( m_handler && m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose
|
||||||
|
{
|
||||||
|
case ModePipelining:
|
||||||
|
m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"connection closed - falling back to HTTP/1.0 connection method" );
|
||||||
|
break;
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
// FIXME do we need to do anything here?
|
||||||
|
// printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::handleTag( Tag* tag )
|
||||||
|
{
|
||||||
|
if( !m_handler || tag->name() != "body" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_streamRestart )
|
||||||
|
{
|
||||||
|
m_streamRestart = false;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending spoofed <stream:stream>" );
|
||||||
|
m_handler->handleReceivedData( this, "<?xml version='1.0' ?>"
|
||||||
|
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams'"
|
||||||
|
" xmlns='" + XMLNS_CLIENT + "' version='" + XMPP_STREAM_VERSION_MAJOR
|
||||||
|
+ "." + XMPP_STREAM_VERSION_MINOR + "' from='" + m_server + "' id ='"
|
||||||
|
+ m_sid + "' xml:lang='en'>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "sid" ) )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_sid = tag->findAttribute( "sid" );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "requests" ) )
|
||||||
|
{
|
||||||
|
const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() );
|
||||||
|
if( serverRequests < m_maxOpenRequests )
|
||||||
|
{
|
||||||
|
m_maxOpenRequests = serverRequests;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'requests' now set to " + tag->findAttribute( "requests" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "hold" ) )
|
||||||
|
{
|
||||||
|
const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() );
|
||||||
|
if( maxHold < m_hold )
|
||||||
|
{
|
||||||
|
m_hold = maxHold;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'hold' now set to " + tag->findAttribute( "hold" ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "wait" ) )
|
||||||
|
{
|
||||||
|
const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() );
|
||||||
|
if( maxWait < m_wait )
|
||||||
|
{
|
||||||
|
m_wait = maxWait;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'wait' now set to " + tag->findAttribute( "wait" )
|
||||||
|
+ " seconds" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( tag->hasAttribute( "polling" ) )
|
||||||
|
{
|
||||||
|
const int minTime = atoi( tag->findAttribute( "polling" ).c_str() );
|
||||||
|
m_minTimePerRequest = minTime;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh parameter 'polling' now set to " + tag->findAttribute( "polling" )
|
||||||
|
+ " seconds" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_state < StateConnected )
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
|
||||||
|
m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" // FIXME move to send() so that
|
||||||
|
// it is more clearly a response
|
||||||
|
// to the initial stream opener?
|
||||||
|
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
|
||||||
|
"xmlns='" + XMLNS_CLIENT
|
||||||
|
+ "' version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR
|
||||||
|
+ "' from='" + m_server + "' id ='" + m_sid + "' xml:lang='en'>" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tag->findAttribute( "type" ) == "terminate" )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH,
|
||||||
|
"bosh connection closed by server: " + tag->findAttribute( "condition" ) );
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_handler->handleDisconnect( this, ConnStreamClosed );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagList& stanzas = tag->children();
|
||||||
|
TagList::const_iterator it = stanzas.begin();
|
||||||
|
for( ; it != stanzas.end(); ++it )
|
||||||
|
m_handler->handleReceivedData( this, (*it)->xml() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::getConnection()
|
||||||
|
{
|
||||||
|
if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Too many requests already open. Cannot send." );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* conn = 0;
|
||||||
|
switch( m_connMode )
|
||||||
|
{
|
||||||
|
case ModePipelining:
|
||||||
|
if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." );
|
||||||
|
return m_activeConnections.front();
|
||||||
|
}
|
||||||
|
else if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"Pipelining selected, but no connection open. Opening one." );
|
||||||
|
return activateConnection();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"No available connections to pipeline on." );
|
||||||
|
break;
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
{
|
||||||
|
if( !m_connectionPool.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, "
|
||||||
|
"using connection from pool." );
|
||||||
|
return activateConnection();
|
||||||
|
}
|
||||||
|
else if( !m_activeConnections.empty() )
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." );
|
||||||
|
conn = m_activeConnections.front()->newInstance();
|
||||||
|
conn->registerConnectionDataHandler( this );
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
conn->connect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_logInstance.warn( LogAreaClassConnectionBOSH,
|
||||||
|
"No available connections to send on." );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionBOSH::activateConnection()
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connectionPool.front();
|
||||||
|
m_connectionPool.pop_front();
|
||||||
|
if( conn->state() == StateConnected )
|
||||||
|
{
|
||||||
|
m_activeConnections.push_back( conn );
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." );
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
conn->connect();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionBOSH::putConnection()
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_activeConnections.front();
|
||||||
|
|
||||||
|
switch( m_connMode )
|
||||||
|
{
|
||||||
|
case ModeLegacyHTTP:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" );
|
||||||
|
conn->disconnect();
|
||||||
|
conn->cleanup(); // This is necessary
|
||||||
|
m_activeConnections.pop_front();
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
break;
|
||||||
|
case ModePersistentHTTP:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" );
|
||||||
|
m_activeConnections.pop_front();
|
||||||
|
m_connectionPool.push_back( conn );
|
||||||
|
break;
|
||||||
|
case ModePipelining:
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" );
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONBOSH_H__
|
||||||
|
#define CONNECTIONBOSH_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "taghandler.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a BOSH (HTTP binding) connection.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client *c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionBOSH( c,
|
||||||
|
* new ConnectionTCPClient( c->logInstance(), httpServer, httpPort ),
|
||||||
|
* c->logInstance(), boshHost, xmpphost, xmppPort ) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the BOSH connection manager's host/port to the transport connection
|
||||||
|
* (ConnectionTCPClient in this case), and the XMPP server's host and port to the BOSH connection.
|
||||||
|
* You must also pass to BOSH the address of the BOSH server you are dealing with, this is used
|
||||||
|
* in the HTTP Host header.
|
||||||
|
*
|
||||||
|
* In the case of using ConnectionBOSH through a HTTP proxy, supply httpServer and httpPort as
|
||||||
|
* those of the proxy. In all cases, boshHost should be set to the hostname (not IP address) of
|
||||||
|
* the server running the BOSH connection manager.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionBOSH doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like chained SOCKS5/HTTP proxies, or ConnectionTLS
|
||||||
|
* for HTTPS).
|
||||||
|
*
|
||||||
|
* @note To avoid problems, you should disable TLS in gloox by calling
|
||||||
|
* ClientBase::setTls( TLSDisabled ).
|
||||||
|
*
|
||||||
|
* Sample configurations for different servers can be found in the bosh_example.cpp file included
|
||||||
|
* with gloox in the @b src/examples/ directory.
|
||||||
|
*
|
||||||
|
* @author Matthew Wild <mwild1@gmail.com>
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionBOSH : public ConnectionBase, ConnectionDataHandler, TagHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionBOSH object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the BOSH connection manager's (or a HTTP proxy's) host and port, @b not to the XMPP host.
|
||||||
|
* ConnectionBOSH will own the transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param boshHost The hostname of the BOSH connection manager
|
||||||
|
* @param xmppServer A server to connect to. This is the XMPP server's address, @b not the
|
||||||
|
* connection manager's.
|
||||||
|
* @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy.
|
||||||
|
*/
|
||||||
|
ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort = 5222 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionBOSH object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the connection manager's (or proxy's) host and port, @b not to the XMPP host. ConnectionBOSH
|
||||||
|
* will own the transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param boshHost The hostname of the BOSH connection manager (not any intermediate proxy)
|
||||||
|
* @param xmppServer A server to connect to. This is the XMPP server's address, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
* @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection
|
||||||
|
* manager's.
|
||||||
|
*/
|
||||||
|
ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance, const std::string& boshHost,
|
||||||
|
const std::string& xmppServer, int xmppPort = 5222 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionBOSH();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supported connection modes. Usually auto-detected.
|
||||||
|
*/
|
||||||
|
enum ConnMode
|
||||||
|
{
|
||||||
|
ModeLegacyHTTP, /**< HTTP 1.0 connections, closed after receiving a response */
|
||||||
|
ModePersistentHTTP, /**< HTTP 1.1 connections, re-used after receiving a response */
|
||||||
|
ModePipelining /**< HTTP Pipelining (implies HTTP 1.1) a single connection is used */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XMPP server to proxy to.
|
||||||
|
* @param xmppHost The XMPP server hostname (IP address).
|
||||||
|
* @param xmppPort The XMPP server port.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& xmppHost, unsigned short xmppPort = 5222 )
|
||||||
|
{ m_server = xmppHost; m_port = xmppPort; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the path on the connection manager to request
|
||||||
|
* @param path The path, the default is "/http-bind/", which is the default for
|
||||||
|
* many connection managers.
|
||||||
|
*/
|
||||||
|
void setPath( const std::string& path ) { m_path = path; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the connection mode
|
||||||
|
* @param mode The connection mode, @sa ConnMode
|
||||||
|
* @note In the case that a mode is selected that the connection manager
|
||||||
|
* or proxy does not support, gloox will fall back to using HTTP/1.0 connections,
|
||||||
|
* which should work with any server.
|
||||||
|
*/
|
||||||
|
void setMode( ConnMode mode ) { m_connMode = mode; }
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int& totalIn, long int& totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
// reimplemented from TagHandler
|
||||||
|
virtual void handleTag( Tag* tag );
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionBOSH& operator=( const ConnectionBOSH& );
|
||||||
|
void initInstance( ConnectionBase* connection, const std::string& xmppServer, const int xmppPort );
|
||||||
|
bool sendRequest( const std::string& xml );
|
||||||
|
bool sendXML();
|
||||||
|
const std::string getHTTPField( const std::string& field );
|
||||||
|
ConnectionBase* getConnection();
|
||||||
|
ConnectionBase* activateConnection();
|
||||||
|
void putConnection();
|
||||||
|
|
||||||
|
//ConnectionBase *m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
Parser m_parser; // Used for parsing XML section of responses
|
||||||
|
std::string m_boshHost; // The hostname of the BOSH connection manager
|
||||||
|
std::string m_boshedHost; // The hostname of the BOSH connection manager + : + port
|
||||||
|
std::string m_path; // The path part of the URL that we need to request
|
||||||
|
|
||||||
|
// BOSH parameters
|
||||||
|
unsigned long m_rid;
|
||||||
|
std::string m_sid;
|
||||||
|
|
||||||
|
bool m_initialStreamSent;
|
||||||
|
int m_openRequests;
|
||||||
|
int m_maxOpenRequests;
|
||||||
|
int m_wait;
|
||||||
|
int m_hold;
|
||||||
|
|
||||||
|
bool m_streamRestart; // Set to true if we are waiting for an acknowledgement of a stream restart
|
||||||
|
|
||||||
|
time_t m_lastRequestTime;
|
||||||
|
unsigned long m_minTimePerRequest;
|
||||||
|
|
||||||
|
std::string m_buffer; // Buffer of received data
|
||||||
|
std::string m_bufferHeader; // HTTP header of data currently in buffer // FIXME doens't need to be member
|
||||||
|
std::string::size_type m_bufferContentLength; // Length of the data in the current response
|
||||||
|
|
||||||
|
std::string m_sendBuffer; // Data waiting to be sent
|
||||||
|
|
||||||
|
typedef std::list<ConnectionBase*> ConnectionList;
|
||||||
|
ConnectionList m_activeConnections;
|
||||||
|
ConnectionList m_connectionPool;
|
||||||
|
ConnMode m_connMode;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONBOSH_H__
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONDATAHANDLER_H__
|
||||||
|
#define CONNECTIONDATAHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class to receive events from a ConnectionBase-derived object.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionDataHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called for received from the underlying transport.
|
||||||
|
* @param connection The connection that received the data.
|
||||||
|
* @param data The data received.
|
||||||
|
*/
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when e.g. the raw TCP connection was established.
|
||||||
|
* @param connection The connection.
|
||||||
|
*/
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This connection is called when e.g. the raw TCP connection was closed.
|
||||||
|
* @param connection The connection.
|
||||||
|
* @param reason The reason for the disconnect.
|
||||||
|
*/
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONDATAHANDLER_H__
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONHANDLER_H__
|
||||||
|
#define CONNECTIONHANDLER_H__
|
||||||
|
|
||||||
|
#include "connectionbase.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an abstract base class to receive incoming connection attempts. Do not
|
||||||
|
* confuse this with ConnectionListener, which is used with XMPP streams and has a
|
||||||
|
* completely different meaning.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to receive an incoming connection.
|
||||||
|
* @param server The server that the connection was made to.
|
||||||
|
* @param connection The incoming connection.
|
||||||
|
*/
|
||||||
|
virtual void handleIncomingConnection( ConnectionBase* server, ConnectionBase* connection ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONHANDLER_H__
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionhttpproxy.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( 0 ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_http11( false )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionDataHandler* cdh,
|
||||||
|
ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( cdh ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionHTTPProxy::~ConnectionHTTPProxy()
|
||||||
|
{
|
||||||
|
delete m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionHTTPProxy::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
|
||||||
|
return new ConnectionHTTPProxy( m_handler, conn, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::connect()
|
||||||
|
{
|
||||||
|
if( m_connection && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnecting;
|
||||||
|
return m_connection->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::disconnect()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::recv( int timeout )
|
||||||
|
{
|
||||||
|
return m_connection ? m_connection->recv( timeout ) : ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionHTTPProxy::receive()
|
||||||
|
{
|
||||||
|
return m_connection ? m_connection->receive() : ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionHTTPProxy::send( const std::string& data )
|
||||||
|
{
|
||||||
|
return m_connection && m_connection->send( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
else
|
||||||
|
totalIn = totalOut = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
if( !m_handler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_state == StateConnecting )
|
||||||
|
{
|
||||||
|
m_proxyHandshakeBuffer += data;
|
||||||
|
if( ( !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.0 200" )
|
||||||
|
|| !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.1 200" ) )
|
||||||
|
&& !m_proxyHandshakeBuffer.compare( m_proxyHandshakeBuffer.length() - 4, 4, "\r\n\r\n" ) )
|
||||||
|
{
|
||||||
|
m_proxyHandshakeBuffer = EmptyString;
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy,
|
||||||
|
"http proxy connection established" );
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else if( !m_proxyHandshakeBuffer.compare( 9, 3, "407" ) )
|
||||||
|
{
|
||||||
|
m_handler->handleDisconnect( this, ConnProxyAuthRequired );
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
else if( !m_proxyHandshakeBuffer.compare( 9, 3, "403" )
|
||||||
|
|| !m_proxyHandshakeBuffer.compare( 9, 3, "404" ) )
|
||||||
|
{
|
||||||
|
m_handler->handleDisconnect( this, ConnProxyAuthFailed );
|
||||||
|
m_connection->disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_state == StateConnected )
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
{
|
||||||
|
std::string server = m_server;
|
||||||
|
int port = m_port;
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( !servers.empty() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string message = "Requesting http proxy connection to " + server + ":"
|
||||||
|
+ util::int2string( port );
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, message );
|
||||||
|
|
||||||
|
std::string os = "CONNECT " + server + ":" + util::int2string( port ) + " HTTP/1."
|
||||||
|
+ util::int2string( m_http11 ? 1 : 0 ) + "\r\n"
|
||||||
|
"Host: " + server + "\r\n"
|
||||||
|
"Content-Length: 0\r\n"
|
||||||
|
"Proxy-Connection: Keep-Alive\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"User-Agent: gloox/" + GLOOX_VERSION + "\r\n";
|
||||||
|
|
||||||
|
if( !m_proxyUser.empty() && !m_proxyPwd.empty() )
|
||||||
|
{
|
||||||
|
os += "Proxy-Authorization: Basic " + Base64::encode64( m_proxyUser + ":" + m_proxyPwd )
|
||||||
|
+ "\r\n";
|
||||||
|
}
|
||||||
|
os += "\r\n";
|
||||||
|
|
||||||
|
if( !m_connection->send( os ) )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionHTTPProxy::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, "HTTP Proxy connection closed" );
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONHTTPPROXY_H__
|
||||||
|
#define CONNECTIONHTTPPROXY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple HTTP Proxying connection.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client* c = new Client( ... );
|
||||||
|
* ConnectionTCPClient* conn0 = new ConnectionTCPClient( c->logInstance(),
|
||||||
|
* proxyHost, proxyPort );
|
||||||
|
* ConnectionHTTPProxy* conn1 = new ConnectionHTTPProxy( c, conn0, c->logInstance(),
|
||||||
|
* xmppHost, xmppPort );
|
||||||
|
* c->setConnectionImpl( conn1 );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case),
|
||||||
|
* and the XMPP host/port to the proxy connection.
|
||||||
|
*
|
||||||
|
* ConnectionHTTPProxy uses the CONNECT method to pass through the proxy. If your proxy does not
|
||||||
|
* allow this kind of connections, or if it kills connections after some time, you may want to use
|
||||||
|
* ConnectionBOSH instead or in addition.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionHTTPProxy doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like IPv6 or chained SOCKS5/HTTP proxies).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionHTTPProxy : public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionHTTPProxy object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The port to connect to. This is the XMPP server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy.
|
||||||
|
*/
|
||||||
|
ConnectionHTTPProxy( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionHTTPProxy object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The port to connect to. This is the XMPP server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionHTTPProxy( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionHTTPProxy();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the XMPP server to proxy to.
|
||||||
|
* @param host The XMPP server hostname (IP address).
|
||||||
|
* @param port The XMPP server port. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& host, int port = -1 )
|
||||||
|
{ m_server = host; m_port = port; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets proxy authorization credentials.
|
||||||
|
* @param user The user name to use for proxy authorization.
|
||||||
|
* @param password The password to use for proxy authorization.
|
||||||
|
*/
|
||||||
|
void setProxyAuth( const std::string& user, const std::string& password )
|
||||||
|
{ m_proxyUser = user; m_proxyPwd = password; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying transport connection. A possibly existing connection will be deleted.
|
||||||
|
* @param connection The ConnectionBase to replace the current connection, if any.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches usage of HTTP/1.1 on or off.
|
||||||
|
* @param http11 Set this to @b true to connect through a HTTP/1.1-only proxy, or @b false
|
||||||
|
* to use HTTP/1.0. Defaults to HTTP/1.0 which should work with 99.9% of proxies.
|
||||||
|
*/
|
||||||
|
void setHTTP11( bool http11 ) { m_http11 = http11; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionHTTPProxy &operator=( const ConnectionHTTPProxy& );
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
std::string m_proxyUser;
|
||||||
|
std::string m_proxyPwd;
|
||||||
|
std::string m_proxyHandshakeBuffer;
|
||||||
|
|
||||||
|
bool m_http11;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONHTTPPROXY_H__
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONLISTENER_H__
|
||||||
|
#define CONNECTIONLISTENER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derived classes can be registered as ConnectionListeners with the Client.
|
||||||
|
*
|
||||||
|
* This interface is mandatory to implement if a connection is to be made TLS-encrypted.
|
||||||
|
* In onTLSConnect(), the server's certificate information needs to be checked, and @b true
|
||||||
|
* returned if the certificate is to be accepted.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionListener() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function notifies about successful connections. It will be called either after all
|
||||||
|
* authentication is finished if username/password were supplied, or after a connection has
|
||||||
|
* been established if no credentials were supplied. Depending on the setting of AutoPresence,
|
||||||
|
* a presence stanza is sent or not.
|
||||||
|
*/
|
||||||
|
virtual void onConnect() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function notifies about disconnection and its reason.
|
||||||
|
* If @b e indicates a stream error, you can use @ref ClientBase::streamError() to find out
|
||||||
|
* what exactly went wrong, and @ref ClientBase::streamErrorText() to retrieve any explaining text
|
||||||
|
* sent along with the error.
|
||||||
|
* If @b e indicates an authentication error, you can use @ref ClientBase::authError()
|
||||||
|
* to get a finer grained reason.
|
||||||
|
* @param e The reason for the disconnection.
|
||||||
|
*/
|
||||||
|
virtual void onDisconnect( ConnectionError e ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will be called when a resource has been bound to the stream. It
|
||||||
|
* will be called for any bound resource, including the main one.
|
||||||
|
* @note The bound resource may be different from the one requested. The server
|
||||||
|
* has the authority to change/overwrite the requested resource.
|
||||||
|
* @param resource The resource string.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void onResourceBind( const std::string& resource ) { (void)resource; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called (by a Client object) if an error occurs while trying to bind a resource.
|
||||||
|
* @param error A pointer to an Error object that contains more
|
||||||
|
* information. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void onResourceBindError( const Error* error ) { (void) (error); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called (by a Client object) if an error occurs while trying to establish
|
||||||
|
* a session.
|
||||||
|
* @param error A pointer to an Error object that contains more
|
||||||
|
* information. May be 0.
|
||||||
|
*/
|
||||||
|
virtual void onSessionCreateError( const Error* error ) { (void) (error); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when the connection was TLS/SSL secured.
|
||||||
|
* @param info Comprehensive info on the certificate.
|
||||||
|
* @return @b True if cert credentials are accepted, @b false otherwise. If @b false is returned
|
||||||
|
* the connection is terminated.
|
||||||
|
*/
|
||||||
|
virtual bool onTLSConnect( const CertInfo& info ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called for certain stream events. Notifications are purely informational
|
||||||
|
* and implementation is optional. Not all StreamEvents will necessarily be emitted for
|
||||||
|
* a given connection.
|
||||||
|
* @param event A stream event.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
virtual void onStreamEvent( StreamEvent event ) { (void) (event); }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONLISTENER_H__
|
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectionsocks5proxy.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "base64.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server,
|
||||||
|
int port, bool ip )
|
||||||
|
: ConnectionBase( 0 ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh,
|
||||||
|
ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server,
|
||||||
|
int port, bool ip )
|
||||||
|
: ConnectionBase( cdh ), m_connection( connection ),
|
||||||
|
m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy::~ConnectionSOCKS5Proxy()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionSOCKS5Proxy::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0;
|
||||||
|
return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
delete m_connection;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::connect()
|
||||||
|
{
|
||||||
|
// FIXME CHECKME
|
||||||
|
if( m_connection && m_connection->state() == StateConnected && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_s5state = S5StateConnected;
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_connection && m_handler )
|
||||||
|
{
|
||||||
|
m_state = StateConnecting;
|
||||||
|
m_s5state = S5StateConnecting;
|
||||||
|
return m_connection->connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::disconnect()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->recv( timeout );
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionSOCKS5Proxy::receive()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->receive();
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionSOCKS5Proxy::send( const std::string& data )
|
||||||
|
{
|
||||||
|
// if( m_s5state != S5StateConnected )
|
||||||
|
// {
|
||||||
|
// printf( "p data sent: " );
|
||||||
|
// const char* x = data.c_str();
|
||||||
|
// for( unsigned int i = 0; i < data.length(); ++i )
|
||||||
|
// printf( "%02X ", (const char)x[i] );
|
||||||
|
// printf( "\n" );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->send( data );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::cleanup()
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_s5state = S5StateDisconnected;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalIn = 0;
|
||||||
|
totalOut = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleReceivedData( const ConnectionBase* /*connection*/,
|
||||||
|
const std::string& data )
|
||||||
|
{
|
||||||
|
// if( m_s5state != S5StateConnected )
|
||||||
|
// {
|
||||||
|
// printf( "data recv: " );
|
||||||
|
// const char* x = data.c_str();
|
||||||
|
// for( unsigned int i = 0; i < data.length(); ++i )
|
||||||
|
// printf( "%02X ", (const char)x[i] );
|
||||||
|
// printf( "\n" );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if( !m_connection || !m_handler )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ConnectionError connError = ConnNoError;
|
||||||
|
|
||||||
|
switch( m_s5state )
|
||||||
|
{
|
||||||
|
case S5StateConnecting:
|
||||||
|
if( data.length() != 2 || data[0] != 0x05 )
|
||||||
|
connError = ConnIoError;
|
||||||
|
|
||||||
|
if( data[1] == 0x00 ) // no auth
|
||||||
|
{
|
||||||
|
negotiate();
|
||||||
|
}
|
||||||
|
else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth
|
||||||
|
{
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
|
||||||
|
"authenticating to socks5 proxy as user " + m_proxyUser );
|
||||||
|
m_s5state = S5StateAuthenticating;
|
||||||
|
char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()];
|
||||||
|
size_t pos = 0;
|
||||||
|
d[pos++] = 0x01;
|
||||||
|
d[pos++] = (char)m_proxyUser.length();
|
||||||
|
strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() );
|
||||||
|
pos += m_proxyUser.length();
|
||||||
|
d[pos++] = (char)m_proxyPwd.length();
|
||||||
|
strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() );
|
||||||
|
pos += m_proxyPwd.length();
|
||||||
|
|
||||||
|
if( !send( std::string( d, pos ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
delete[] d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() )
|
||||||
|
connError = ConnProxyNoSupportedAuth;
|
||||||
|
else
|
||||||
|
connError = ConnProxyAuthRequired;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case S5StateNegotiating:
|
||||||
|
if( data.length() >= 6 && data[0] == 0x05 )
|
||||||
|
{
|
||||||
|
if( data[1] == 0x00 )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_s5state = S5StateConnected;
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else // connection refused
|
||||||
|
connError = ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
connError = ConnIoError;
|
||||||
|
break;
|
||||||
|
case S5StateAuthenticating:
|
||||||
|
if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 )
|
||||||
|
negotiate();
|
||||||
|
else
|
||||||
|
connError = ConnProxyAuthFailed;
|
||||||
|
break;
|
||||||
|
case S5StateConnected:
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( connError != ConnNoError )
|
||||||
|
{
|
||||||
|
m_connection->disconnect();
|
||||||
|
m_handler->handleDisconnect( this, connError );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::negotiate()
|
||||||
|
{
|
||||||
|
m_s5state = S5StateNegotiating;
|
||||||
|
char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1];
|
||||||
|
size_t pos = 0;
|
||||||
|
d[pos++] = 0x05; // SOCKS version 5
|
||||||
|
d[pos++] = 0x01; // command CONNECT
|
||||||
|
d[pos++] = 0x00; // reserved
|
||||||
|
int port = m_port;
|
||||||
|
std::string server = m_server;
|
||||||
|
if( m_ip ) // IP address
|
||||||
|
{
|
||||||
|
d[pos++] = 0x01; // IPv4 address
|
||||||
|
std::string s;
|
||||||
|
const size_t j = server.length();
|
||||||
|
size_t l = 0;
|
||||||
|
for( size_t k = 0; k < j && l < 4; ++k )
|
||||||
|
{
|
||||||
|
if( server[k] != '.' )
|
||||||
|
s += server[k];
|
||||||
|
|
||||||
|
if( server[k] == '.' || k == j-1 )
|
||||||
|
{
|
||||||
|
d[pos++] = static_cast<char>( atoi( s.c_str() ) & 0xFF );
|
||||||
|
s = EmptyString;
|
||||||
|
++l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // hostname
|
||||||
|
{
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( servers.size() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d[pos++] = 0x03; // hostname
|
||||||
|
d[pos++] = (char)m_server.length();
|
||||||
|
strncpy( d + pos, m_server.c_str(), m_server.length() );
|
||||||
|
pos += m_server.length();
|
||||||
|
}
|
||||||
|
int nport = htons( port );
|
||||||
|
d[pos++] = static_cast<char>( nport );
|
||||||
|
d[pos++] = static_cast<char>( nport >> 8 );
|
||||||
|
|
||||||
|
std::string message = "Requesting socks5 proxy connection to " + server + ":"
|
||||||
|
+ util::int2string( port );
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message );
|
||||||
|
|
||||||
|
if( !send( std::string( d, pos ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
delete[] d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
{
|
||||||
|
std::string server = m_server;
|
||||||
|
int port = m_port;
|
||||||
|
if( port == -1 )
|
||||||
|
{
|
||||||
|
const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance );
|
||||||
|
if( !servers.empty() )
|
||||||
|
{
|
||||||
|
const std::pair< std::string, int >& host = *servers.begin();
|
||||||
|
server = host.first;
|
||||||
|
port = host.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy,
|
||||||
|
"Attempting to negotiate socks5 proxy connection" );
|
||||||
|
|
||||||
|
const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty();
|
||||||
|
const char d[4] = {
|
||||||
|
0x05, // SOCKS version 5
|
||||||
|
static_cast<char>( auth ? 0x02 // two methods
|
||||||
|
: 0x01 ), // one method
|
||||||
|
0x00, // method: no auth
|
||||||
|
0x02 // method: username/password auth
|
||||||
|
};
|
||||||
|
|
||||||
|
if( !send( std::string( d, auth ? 4 : 3 ) ) )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionSOCKS5Proxy::handleDisconnect( const ConnectionBase* /*connection*/,
|
||||||
|
ConnectionError reason )
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" );
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONSOCKS5PROXY_H__
|
||||||
|
#define CONNECTIONSOCKS5PROXY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple SOCKS5 Proxying connection (RFC 1928 + RFC 1929).
|
||||||
|
*
|
||||||
|
* To use with a SOCKS5 proxy:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Client* c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionSOCKS5Proxy( c,
|
||||||
|
* new ConnectionTCPClient( c->logInstance(), proxyHost, proxyPort ),
|
||||||
|
* c->logInstance(), xmppHost, xmppPort ) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case),
|
||||||
|
* and the XMPP host/port to the proxy connection.
|
||||||
|
*
|
||||||
|
* The reason why ConnectionSOCKS5Proxy doesn't manage its own ConnectionTCPClient is that it allows it
|
||||||
|
* to be used with other transports (like IPv6 or chained HTTP/SOCKS5 proxies).
|
||||||
|
*
|
||||||
|
* @note This class is also used by the SOCKS5 bytestreams implementation (with slightly different
|
||||||
|
* semantics).
|
||||||
|
*
|
||||||
|
* @note Simple @b plain-text username/password authentication is supported. GSSAPI authentication
|
||||||
|
* is not supported.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionSOCKS5Proxy : public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionSOCKS5Proxy object.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c server is an IP address (true) or a host name (false).
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionSOCKS5Proxy( ConnectionBase* connection, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1, bool ip = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionSOCKS5Proxy object.
|
||||||
|
* @param cdh A ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param connection A transport connection. It should be configured to connect to
|
||||||
|
* the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the
|
||||||
|
* transport connection and delete it in its destructor.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to. This is the XMPP server's address, @b not the proxy.
|
||||||
|
* @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's.
|
||||||
|
* The default of -1 means that SRV records will be used to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c server is an IP address (true) or a host name (false).
|
||||||
|
*/
|
||||||
|
ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh, ConnectionBase* connection,
|
||||||
|
const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1, bool ip = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionSOCKS5Proxy();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the server to proxy to.
|
||||||
|
* @param host The server hostname (IP address).
|
||||||
|
* @param port The server port. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
* @param ip Indicates whether @c host is an IP address (true) or a host name (false).
|
||||||
|
*/
|
||||||
|
void setServer( const std::string& host, int port = -1, bool ip = false )
|
||||||
|
{ m_server = host; m_port = port; m_ip = ip; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets proxy authorization credentials.
|
||||||
|
* @param user The user name to use for proxy authorization.
|
||||||
|
* @param password The password to use for proxy authorization.
|
||||||
|
*/
|
||||||
|
void setProxyAuth( const std::string& user, const std::string& password )
|
||||||
|
{ m_proxyUser = user; m_proxyPwd = password; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the underlying transport connection. A possibly existing connection will be deleted.
|
||||||
|
* @param connection The ConnectionBase to replace the current connection, if any.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Socks5State
|
||||||
|
{
|
||||||
|
S5StateDisconnected,
|
||||||
|
S5StateConnecting,
|
||||||
|
S5StateNegotiating,
|
||||||
|
S5StateAuthenticating,
|
||||||
|
S5StateConnected
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionSOCKS5Proxy &operator=( const ConnectionSOCKS5Proxy& );
|
||||||
|
void negotiate();
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
|
||||||
|
Socks5State m_s5state;
|
||||||
|
|
||||||
|
std::string m_proxyUser;
|
||||||
|
std::string m_proxyPwd;
|
||||||
|
std::string m_proxyHandshakeBuffer;
|
||||||
|
bool m_ip;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONSOCKS5PROXY_H__
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "prep.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <string.h>
|
||||||
|
#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
typedef int socklen_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPBase::ConnectionTCPBase( const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
|
||||||
|
m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
|
||||||
|
{
|
||||||
|
init( server, port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPBase::ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
|
||||||
|
m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
|
||||||
|
{
|
||||||
|
init( server, port );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::init( const std::string& server, int port )
|
||||||
|
{
|
||||||
|
// FIXME check return value?
|
||||||
|
prep::idna( server, m_server );
|
||||||
|
m_port = port;
|
||||||
|
m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPBase::~ConnectionTCPBase()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
free( m_buf );
|
||||||
|
m_buf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::disconnect()
|
||||||
|
{
|
||||||
|
util::MutexGuard rm( m_recvMutex );
|
||||||
|
m_cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTCPBase::dataAvailable( int timeout )
|
||||||
|
{
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return true; // let recv() catch the closed fd
|
||||||
|
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
FD_ZERO( &fds );
|
||||||
|
// the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
|
||||||
|
// however, the reason for the warning can't be fixed in gloox.
|
||||||
|
FD_SET( m_socket, &fds );
|
||||||
|
|
||||||
|
tv.tv_sec = timeout / 1000000;
|
||||||
|
tv.tv_usec = timeout % 1000000;
|
||||||
|
|
||||||
|
return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
|
||||||
|
&& FD_ISSET( m_socket, &fds ) != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPBase::receive()
|
||||||
|
{
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
ConnectionError err = ConnNoError;
|
||||||
|
while( !m_cancel && ( err = recv( 10 ) ) == ConnNoError )
|
||||||
|
;
|
||||||
|
return err == ConnNoError ? ConnNotConnected : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTCPBase::send( const std::string& data )
|
||||||
|
{
|
||||||
|
m_sendMutex.lock();
|
||||||
|
|
||||||
|
if( data.empty() || ( m_socket < 0 ) )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sent = 0;
|
||||||
|
for( size_t num = 0, len = data.length(); sent != -1 && num < len; num += sent )
|
||||||
|
{
|
||||||
|
sent = static_cast<int>( ::send( m_socket, (data.c_str()+num), (int)(len - num), 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_totalBytesOut += (int)data.length();
|
||||||
|
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
|
||||||
|
if( sent == -1 && m_handler )
|
||||||
|
m_handler->handleDisconnect( this, ConnIoError );
|
||||||
|
|
||||||
|
return sent != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::getStatistics( long int &totalIn, long int &totalOut )
|
||||||
|
{
|
||||||
|
totalIn = m_totalBytesIn;
|
||||||
|
totalOut = m_totalBytesOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTCPBase::cleanup()
|
||||||
|
{
|
||||||
|
if( !m_sendMutex.trylock() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( !m_recvMutex.trylock() )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_socket >= 0 )
|
||||||
|
{
|
||||||
|
DNS::closeSocket( m_socket, m_logInstance );
|
||||||
|
m_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_cancel = true;
|
||||||
|
m_totalBytesIn = 0;
|
||||||
|
m_totalBytesOut = 0;
|
||||||
|
|
||||||
|
m_recvMutex.unlock(),
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConnectionTCPBase::localPort() const
|
||||||
|
{
|
||||||
|
struct sockaddr local;
|
||||||
|
socklen_t len = (socklen_t)sizeof( local );
|
||||||
|
if( getsockname ( m_socket, &local, &len ) < 0 )
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return ntohs( ((struct sockaddr_in *)&local)->sin_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string ConnectionTCPBase::localInterface() const
|
||||||
|
{
|
||||||
|
struct sockaddr_in local;
|
||||||
|
socklen_t len = (socklen_t)sizeof( local );
|
||||||
|
if( getsockname ( m_socket, (reinterpret_cast<struct sockaddr*>( &local )), &len ) < 0 )
|
||||||
|
return EmptyString;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// char addr[INET_ADDRSTRLEN];
|
||||||
|
// return inet_ntop( AF_INET, &(local.sin_addr), addr, sizeof( addr ) ); //FIXME is this portable?
|
||||||
|
return inet_ntoa( local.sin_addr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPBASE_H__
|
||||||
|
#define CONNECTIONTCPBASE_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
class Mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a base class for a simple TCP connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPBase : public ConnectionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPBase object.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that XMPP SRV records
|
||||||
|
* will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionTCPBase( const LogSink& logInstance, const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPBase object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPBase();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int &totalIn, long int &totalOut );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives access to the raw socket of this connection. Use it wisely. You can
|
||||||
|
* select()/poll() it and use ConnectionTCPBase::recv( -1 ) to fetch the data.
|
||||||
|
* @return The socket of the active connection, or -1 if no connection is established.
|
||||||
|
*/
|
||||||
|
int socket() const { return m_socket; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function allows to set an existing socket with an established
|
||||||
|
* connection to use in this connection. You will still need to call connect() in order to
|
||||||
|
* negotiate the XMPP stream. You should not set a new socket after having called connect().
|
||||||
|
* @param socket The existing socket.
|
||||||
|
*/
|
||||||
|
void setSocket( int socket ) { m_cancel = false; m_state = StateConnected; m_socket = socket; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local port.
|
||||||
|
* @return The local port.
|
||||||
|
*/
|
||||||
|
virtual int localPort() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locally bound IP address.
|
||||||
|
* @return The locally bound IP address.
|
||||||
|
*/
|
||||||
|
virtual const std::string localInterface() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ConnectionTCPBase& operator=( const ConnectionTCPBase& );
|
||||||
|
void init( const std::string& server, int port );
|
||||||
|
bool dataAvailable( int timeout = -1 );
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
const LogSink& m_logInstance;
|
||||||
|
util::Mutex m_sendMutex;
|
||||||
|
util::Mutex m_recvMutex;
|
||||||
|
|
||||||
|
char* m_buf;
|
||||||
|
int m_socket;
|
||||||
|
long int m_totalBytesIn;
|
||||||
|
long int m_totalBytesOut;
|
||||||
|
const int m_bufsize;
|
||||||
|
bool m_cancel;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPBASE_H__
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpclient.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPClient::ConnectionTCPClient( const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionTCPBase( logInstance, server, port )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPClient::ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port )
|
||||||
|
: ConnectionTCPBase( cdh, logInstance, server, port )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ConnectionTCPClient::~ConnectionTCPClient()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTCPClient::newInstance() const
|
||||||
|
{
|
||||||
|
return new ConnectionTCPClient( m_handler, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPClient::connect()
|
||||||
|
{
|
||||||
|
m_sendMutex.lock();
|
||||||
|
// FIXME CHECKME
|
||||||
|
if( !m_handler )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_socket >= 0 && m_state > StateDisconnected )
|
||||||
|
{
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
{
|
||||||
|
if( m_port == -1 )
|
||||||
|
m_socket = DNS::connect( m_server, m_logInstance );
|
||||||
|
else
|
||||||
|
m_socket = DNS::connect( m_server, m_port, m_logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sendMutex.unlock();
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
{
|
||||||
|
switch( m_socket )
|
||||||
|
{
|
||||||
|
case -ConnConnectionRefused:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
m_server + ": connection refused" );
|
||||||
|
break;
|
||||||
|
case -ConnDnsError:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
m_server + ": host not found" );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
m_logInstance.err( LogAreaClassConnectionTCPClient,
|
||||||
|
"Unknown error condition" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_handler->handleDisconnect( this, (ConnectionError)-m_socket );
|
||||||
|
return (ConnectionError)-m_socket;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cancel = false;
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPClient::recv( int timeout )
|
||||||
|
{
|
||||||
|
m_recvMutex.lock();
|
||||||
|
|
||||||
|
if( m_cancel || m_socket < 0 )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !dataAvailable( timeout ) )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = static_cast<int>( ::recv( m_socket, m_buf, m_bufsize, 0 ) );
|
||||||
|
if( size > 0 )
|
||||||
|
m_totalBytesIn += size;
|
||||||
|
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
|
||||||
|
if( size <= 0 )
|
||||||
|
{
|
||||||
|
ConnectionError error = ( size ? ConnIoError : ConnStreamClosed );
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, error );
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buf[size] = '\0';
|
||||||
|
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleReceivedData( this, std::string( m_buf, size ) );
|
||||||
|
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPCLIENT_H__
|
||||||
|
#define CONNECTIONTCPCLIENT_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple TCP connection.
|
||||||
|
*
|
||||||
|
* You should only need to use this class directly if you need access to some special feature, like
|
||||||
|
* the raw socket(), or if you need HTTP proxy support (see @ref gloox::ConnectionHTTPProxy for more
|
||||||
|
* information).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPClient : public ConnectionTCPBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPClient object.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that XMPP SRV records
|
||||||
|
* will be used to find out about the actual host:port.
|
||||||
|
* @note To properly use this object, you have to set a ConnectionDataHandler using
|
||||||
|
* registerConnectionDataHandler(). This is not necessary if this object is
|
||||||
|
* part of a 'connection chain', e.g. with ConnectionHTTPProxy.
|
||||||
|
*/
|
||||||
|
ConnectionTCPClient( const LogSink& logInstance, const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPClient object.
|
||||||
|
* @param cdh An ConnectionDataHandler-derived object that will handle incoming data.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param server A server to connect to.
|
||||||
|
* @param port The port to connect to. The default of -1 means that SRV records will be used
|
||||||
|
* to find out about the actual host:port.
|
||||||
|
*/
|
||||||
|
ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance,
|
||||||
|
const std::string& server, int port = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPClient();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTCPClient &operator=( const ConnectionTCPClient & );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPCLIENT_H__
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "connectiontcpserver.h"
|
||||||
|
#include "connectiontcpclient.h"
|
||||||
|
#include "connectionhandler.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "mutexguard.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <winsock.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
# include <resolv.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/un.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <errno.h>
|
||||||
|
# include <string.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifndef _WIN32_WCE
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTCPServer::ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance,
|
||||||
|
const std::string& ip, int port )
|
||||||
|
: ConnectionTCPBase( 0, logInstance, ip, port ),
|
||||||
|
m_connectionHandler( ch )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTCPServer::~ConnectionTCPServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTCPServer::newInstance() const
|
||||||
|
{
|
||||||
|
return new ConnectionTCPServer( m_connectionHandler, m_logInstance, m_server, m_port );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPServer::connect()
|
||||||
|
{
|
||||||
|
util::MutexGuard mg( &m_sendMutex );
|
||||||
|
|
||||||
|
if( m_socket >= 0 || m_state > StateDisconnected )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
m_socket = DNS::getSocket( m_logInstance );
|
||||||
|
|
||||||
|
if( m_socket < 0 )
|
||||||
|
return ConnIoError;
|
||||||
|
|
||||||
|
struct sockaddr_in local;
|
||||||
|
local.sin_family = AF_INET;
|
||||||
|
local.sin_port = static_cast<unsigned short int>( htons( m_port ) );
|
||||||
|
local.sin_addr.s_addr = m_server.empty() ? INADDR_ANY : inet_addr( m_server.c_str() );
|
||||||
|
memset( local.sin_zero, '\0', 8 );
|
||||||
|
|
||||||
|
if( bind( m_socket, (struct sockaddr*)&local, sizeof( struct sockaddr ) ) < 0 )
|
||||||
|
{
|
||||||
|
std::string message = "bind() to " + ( m_server.empty() ? std::string( "*" ) : m_server )
|
||||||
|
+ " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionTCPServer, message );
|
||||||
|
|
||||||
|
return ConnIoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( listen( m_socket, 10 ) < 0 )
|
||||||
|
{
|
||||||
|
std::string message = "listen on " + ( m_server.empty() ? std::string( "*" ) : m_server )
|
||||||
|
+ " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
m_logInstance.dbg( LogAreaClassConnectionTCPServer, message );
|
||||||
|
|
||||||
|
return ConnIoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cancel = false;
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTCPServer::recv( int timeout )
|
||||||
|
{
|
||||||
|
m_recvMutex.lock();
|
||||||
|
|
||||||
|
if( m_cancel || m_socket < 0 || !m_connectionHandler )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !dataAvailable( timeout ) )
|
||||||
|
{
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in they;
|
||||||
|
int sin_size = sizeof( struct sockaddr_in );
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
int newfd = static_cast<int>( accept( static_cast<SOCKET>( m_socket ), (struct sockaddr*)&they, &sin_size ) );
|
||||||
|
#else
|
||||||
|
int newfd = accept( m_socket, (struct sockaddr*)&they, (socklen_t*)&sin_size );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_recvMutex.unlock();
|
||||||
|
|
||||||
|
ConnectionTCPClient* conn = new ConnectionTCPClient( m_logInstance, inet_ntoa( they.sin_addr ),
|
||||||
|
ntohs( they.sin_port ) );
|
||||||
|
conn->setSocket( newfd );
|
||||||
|
m_connectionHandler->handleIncomingConnection( this, conn );
|
||||||
|
|
||||||
|
return ConnNoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTCPSERVER_H__
|
||||||
|
#define CONNECTIONTCPSERVER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "connectiontcpbase.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a simple listening TCP connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTCPServer : public ConnectionTCPBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTCPServer object.
|
||||||
|
* @param ch An ConnectionHandler-derived object that will handle incoming connections.
|
||||||
|
* @param logInstance The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
* @param ip The local IP address to listen on. This must @b not be a hostname.
|
||||||
|
* Leave this empty to listen on all local interfaces.
|
||||||
|
* @param port The port to listen on.
|
||||||
|
*/
|
||||||
|
ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance,
|
||||||
|
const std::string& ip, int port );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTCPServer();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function actually starts @c listening on the port given in the
|
||||||
|
* constructor.
|
||||||
|
*/
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTCPServer &operator=( const ConnectionTCPServer & );
|
||||||
|
|
||||||
|
ConnectionHandler* m_connectionHandler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTCPSERVER_H__
|
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "connectiontls.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTLS::ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionBase( cdh ),
|
||||||
|
m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ),
|
||||||
|
m_log( log )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLS::ConnectionTLS( ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionBase( 0 ),
|
||||||
|
m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ), m_log( log )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLS::~ConnectionTLS()
|
||||||
|
{
|
||||||
|
delete m_connection;
|
||||||
|
delete m_tls;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::setConnectionImpl( ConnectionBase* connection )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( 0 );
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->registerConnectionDataHandler( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::connect()
|
||||||
|
{
|
||||||
|
if( !m_connection )
|
||||||
|
return ConnNotConnected;
|
||||||
|
|
||||||
|
if( m_state == StateConnected )
|
||||||
|
return ConnNoError;
|
||||||
|
|
||||||
|
if( !m_tls )
|
||||||
|
m_tls = getTLSBase( this, m_connection->server() );
|
||||||
|
|
||||||
|
if( !m_tls )
|
||||||
|
return ConnTlsNotAvailable;
|
||||||
|
|
||||||
|
if( !m_tls->init( m_clientKey, m_clientCerts, m_cacerts ) )
|
||||||
|
return ConnTlsFailed;
|
||||||
|
|
||||||
|
// m_tls->setCACerts( m_cacerts );
|
||||||
|
// m_tls->setClientCert( m_clientKey, m_clientCerts );
|
||||||
|
|
||||||
|
m_state = StateConnecting;
|
||||||
|
|
||||||
|
if( m_connection->state() != StateConnected )
|
||||||
|
return m_connection->connect();
|
||||||
|
|
||||||
|
if( m_tls->handshake() )
|
||||||
|
return ConnNoError;
|
||||||
|
else
|
||||||
|
return ConnTlsFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::recv( int timeout )
|
||||||
|
{
|
||||||
|
if( m_connection->state() == StateConnected )
|
||||||
|
{
|
||||||
|
return m_connection->recv( timeout );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.log( LogLevelWarning, LogAreaClassConnectionTLS,
|
||||||
|
"Attempt to receive data on a connection that is not connected (or is connecting)" );
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConnectionTLS::send( const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_state != StateConnected )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_tls->encrypt( data );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionError ConnectionTLS::receive()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
return m_connection->receive();
|
||||||
|
else
|
||||||
|
return ConnNotConnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::disconnect()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->disconnect();
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::cleanup()
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->cleanup();
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->cleanup();
|
||||||
|
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::getStatistics( long int& totalIn, long int& totalOut )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->getStatistics( totalIn, totalOut );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTLS::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* newConn = 0;
|
||||||
|
if( m_connection )
|
||||||
|
newConn = m_connection->newInstance();
|
||||||
|
return new ConnectionTLS( m_handler, newConn, m_log );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->decrypt( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleConnect( const ConnectionBase* /*connection*/ )
|
||||||
|
{
|
||||||
|
if( m_tls )
|
||||||
|
m_tls->handshake();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
|
||||||
|
{
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleDisconnect( this, reason );
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleEncryptedData( const TLSBase* /*tls*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_connection )
|
||||||
|
m_connection->send( data );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleDecryptedData( const TLSBase* /*tls*/, const std::string& data )
|
||||||
|
{
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleReceivedData( this, data );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "Data received and decrypted but no handler" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionTLS::handleHandshakeResult( const TLSBase* tls, bool success, CertInfo& certinfo )
|
||||||
|
{
|
||||||
|
if( success )
|
||||||
|
{
|
||||||
|
m_state = StateConnected;
|
||||||
|
m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "TLS handshake succeeded" );
|
||||||
|
if( m_tlsHandler )
|
||||||
|
m_tlsHandler->handleHandshakeResult( tls, success, certinfo );
|
||||||
|
if( m_handler )
|
||||||
|
m_handler->handleConnect( this );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = StateDisconnected;
|
||||||
|
m_log.log( LogLevelWarning, LogAreaClassConnectionTLS, "TLS handshake failed" );
|
||||||
|
if( m_tlsHandler )
|
||||||
|
m_tlsHandler->handleHandshakeResult( tls, success, certinfo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTLS_H__
|
||||||
|
#define CONNECTIONTLS_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
#include "connectiondatahandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of a TLS/SSL connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this function directly. However,
|
||||||
|
* you can use it to connect to the legacy Jabber SSL port,
|
||||||
|
* 5223.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* @code
|
||||||
|
* Client *c = new Client( ... );
|
||||||
|
* c->setConnectionImpl( new ConnectionTLS( c,
|
||||||
|
* new ConnectionTCP( c->logInstance(), server, 5223 ),
|
||||||
|
* c->logInstance()) );
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Due to the need for handshaking data to be sent/received before the connection is fully
|
||||||
|
* established, be sure not to use the connection until ConnectionDataHandler::handleConnect()
|
||||||
|
* of the specified ConnectionDataHandler is called.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @author Matthew Wild <mwild1@gmail.com>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
class GLOOX_API ConnectionTLS : public TLSHandler, public ConnectionBase, public ConnectionDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLS object.
|
||||||
|
* @param cdh The ConnectionDataHandler that will be notified of events from this connection
|
||||||
|
* @param conn A transport connection. It should be configured to connect to
|
||||||
|
* the server and port you wish to make the encrypted connection to.
|
||||||
|
* ConnectionTLS will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLS object.
|
||||||
|
* @param conn A transport connection. It should be configured to connect to
|
||||||
|
* the server and port you wish to make the encrypted connection to.
|
||||||
|
* ConnectionTLS will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLS( ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTLS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set a number of trusted root CA certificates which shall be
|
||||||
|
* used to verify a servers certificate.
|
||||||
|
* @param cacerts A list of absolute paths to CA root certificate files in PEM format.
|
||||||
|
* @note This function is a wrapper for TLSBase::setCACerts().
|
||||||
|
*/
|
||||||
|
void setCACerts( const StringList& cacerts )
|
||||||
|
{
|
||||||
|
m_cacerts = cacerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to retrieve certificate and connection info of a encrypted connection.
|
||||||
|
* @return Certificate information.
|
||||||
|
* @note This funcztion is a wrapper around TLSBase::fetchTLSInfo().
|
||||||
|
*/
|
||||||
|
const CertInfo& fetchTLSInfo() const { return m_certInfo; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the user's certificate and private key. The certificate will
|
||||||
|
* be presented to the server upon request and can be used for SASL EXTERNAL authentication.
|
||||||
|
* The user's certificate file should be a bundle of more than one certificate in PEM format.
|
||||||
|
* The first one in the file should be the user's certificate, each cert following that one
|
||||||
|
* should have signed the previous one.
|
||||||
|
* @note These certificates are not necessarily the same as those used to verify the server's
|
||||||
|
* certificate.
|
||||||
|
* @param clientKey The absolute path to the user's private key in PEM format.
|
||||||
|
* @param clientCerts A path to a certificate bundle in PEM format.
|
||||||
|
* @note This function is a wrapper around TLSBase::setClientCert().
|
||||||
|
*/
|
||||||
|
void setClientCert( const std::string& clientKey, const std::string& clientCerts )
|
||||||
|
{
|
||||||
|
m_clientKey = clientKey;
|
||||||
|
m_clientCerts = clientCerts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the transport connection.
|
||||||
|
* @param connection The transport connection to use.
|
||||||
|
*/
|
||||||
|
void setConnectionImpl( ConnectionBase* connection );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an TLSHandler derived object. Only the handleHandshakeResult()
|
||||||
|
* function will be used after a handshake took place.
|
||||||
|
* You can review certificate info there.
|
||||||
|
* @param th The TLSHandler to register.
|
||||||
|
* @note If no handler is set, ConnectionTLS will accept
|
||||||
|
* any certificate and continue with the connection.
|
||||||
|
*/
|
||||||
|
void registerTLSHandler( TLSHandler* th ) { m_tlsHandler = th; }
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError connect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError recv( int timeout = -1 );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual bool send( const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual ConnectionError receive();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void disconnect();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void cleanup();
|
||||||
|
|
||||||
|
// reimplemented from ConnectionBase
|
||||||
|
virtual void getStatistics( long int& totalIn, long int& totalOut );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleConnect( const ConnectionBase* connection );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionDataHandler
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleEncryptedData( const TLSBase*, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleDecryptedData( const TLSBase*, const std::string& data );
|
||||||
|
|
||||||
|
// reimplemented from TLSHandler
|
||||||
|
virtual void handleHandshakeResult( const TLSBase* base, bool success, CertInfo& certinfo );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Returns a TLS object (client). Reimplement to change the
|
||||||
|
* type of the object.
|
||||||
|
* @return A TLS object.
|
||||||
|
*/
|
||||||
|
virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server )
|
||||||
|
{
|
||||||
|
return new TLSDefault( th, server, TLSDefault::VerifyingClient );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* m_connection;
|
||||||
|
TLSBase* m_tls;
|
||||||
|
TLSHandler* m_tlsHandler;
|
||||||
|
CertInfo m_certInfo;
|
||||||
|
const LogSink& m_log;
|
||||||
|
StringList m_cacerts;
|
||||||
|
std::string m_clientCerts;
|
||||||
|
std::string m_clientKey;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTLS& operator=( const ConnectionTLS& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTLS_H__
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "connectiontlsserver.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
ConnectionTLSServer::ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn,
|
||||||
|
const LogSink& log )
|
||||||
|
: ConnectionTLS( cdh, conn, log )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLSServer::ConnectionTLSServer( ConnectionBase* conn, const LogSink& log )
|
||||||
|
: ConnectionTLS( conn, log )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionTLSServer::~ConnectionTLSServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TLSBase* ConnectionTLSServer::getTLSBase( TLSHandler* th, const std::string server )
|
||||||
|
{
|
||||||
|
return new TLSDefault( th, server, TLSDefault::VerifyingServer );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionBase* ConnectionTLSServer::newInstance() const
|
||||||
|
{
|
||||||
|
ConnectionBase* newConn = 0;
|
||||||
|
if( m_connection )
|
||||||
|
newConn = m_connection->newInstance();
|
||||||
|
return new ConnectionTLSServer( m_handler, newConn, m_log );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
* This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
*
|
||||||
|
* This software is distributed under a license. The full license
|
||||||
|
* agreement can be found in the file LICENSE in this distribution.
|
||||||
|
* This software may not be copied, modified, sold or distributed
|
||||||
|
* other than expressed in the named license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTIONTLSSERVER_H__
|
||||||
|
#define CONNECTIONTLSSERVER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
#include "connectionbase.h"
|
||||||
|
#include "connectiontls.h"
|
||||||
|
#include "tlsdefault.h"
|
||||||
|
#include "tlshandler.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ConnectionDataHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of the server-side of a TLS/SSL connection.
|
||||||
|
*
|
||||||
|
* You should not need to use this class directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API ConnectionTLSServer : public ConnectionTLS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLSServer object.
|
||||||
|
* @param cdh The ConnectionDataHandler that will be notified of events from this connection
|
||||||
|
* @param conn A transport connection. It should be an established connection from
|
||||||
|
* a client that is about to perform a TLS handshake.
|
||||||
|
* ConnectionTLSServer will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ConnectionTLSServer object.
|
||||||
|
* @param conn A transport connection. It should be an established connection from
|
||||||
|
* a client that is about to perform a TLS handshake.
|
||||||
|
* ConnectionTLSServer will own the transport connection and delete it in its destructor.
|
||||||
|
* @param log The log target. Obtain it from ClientBase::logInstance().
|
||||||
|
*/
|
||||||
|
ConnectionTLSServer( ConnectionBase* conn, const LogSink& log );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~ConnectionTLSServer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TLS server.
|
||||||
|
* @return A TLS server.
|
||||||
|
*/
|
||||||
|
virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server );
|
||||||
|
|
||||||
|
// reimplemented from ConnectionTLS
|
||||||
|
virtual ConnectionBase* newInstance() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionTLSServer& operator=( const ConnectionTLSServer& );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONNECTIONTLSSERVER_H__
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "dataformfield.h"
|
||||||
|
#include "dataformitem.h"
|
||||||
|
#include "dataformreported.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataForm::DataForm( FormType type, const StringList& instructions, const std::string& title )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( type ), m_instructions( instructions ), m_title( title ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( FormType type, const std::string& title )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( type ), m_title( title ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDataForm ),
|
||||||
|
m_type( TypeInvalid ), m_reported( 0 )
|
||||||
|
{
|
||||||
|
parse( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::DataForm( const DataForm& form )
|
||||||
|
: StanzaExtension( ExtDataForm ), DataFormFieldContainer( form ),
|
||||||
|
m_type( form.m_type ), m_instructions( form.m_instructions ),
|
||||||
|
m_title( form.m_title ), m_reported( form.m_reported ? new DataFormReported( form.m_reported->tag() ) : 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataForm::~DataForm()
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
delete m_reported;
|
||||||
|
m_reported = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* dfTypeValues[] =
|
||||||
|
{
|
||||||
|
"form", "submit", "cancel", "result"
|
||||||
|
};
|
||||||
|
|
||||||
|
bool DataForm::parse( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->xmlns() != XMLNS_X_DATA || tag->name() != "x" )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::string& type = tag->findAttribute( TYPE );
|
||||||
|
if( type.empty() )
|
||||||
|
m_type = TypeForm;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_type = (FormType)util::lookup( type, dfTypeValues );
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "title" )
|
||||||
|
m_title = (*it)->cdata();
|
||||||
|
else if( (*it)->name() == "instructions" )
|
||||||
|
m_instructions.push_back( (*it)->cdata() );
|
||||||
|
else if( (*it)->name() == "field" )
|
||||||
|
m_fields.push_back( new DataFormField( (*it) ) );
|
||||||
|
else if( (*it)->name() == "reported" )
|
||||||
|
{
|
||||||
|
if( m_reported == NULL )
|
||||||
|
m_reported = new DataFormReported( (*it) );
|
||||||
|
// else - Invalid data form - only one "reported" is allowed
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "item" )
|
||||||
|
m_items.push_back( new DataFormItem( (*it) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DataForm::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/message/x[@xmlns='" + XMLNS_X_DATA + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataForm::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* x = new Tag( "x" );
|
||||||
|
x->setXmlns( XMLNS_X_DATA );
|
||||||
|
x->addAttribute( TYPE, util::lookup( m_type, dfTypeValues ) );
|
||||||
|
if( !m_title.empty() )
|
||||||
|
new Tag( x, "title", m_title );
|
||||||
|
|
||||||
|
StringList::const_iterator it_i = m_instructions.begin();
|
||||||
|
for( ; it_i != m_instructions.end(); ++it_i )
|
||||||
|
new Tag( x, "instructions", (*it_i) );
|
||||||
|
|
||||||
|
FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
x->addChild( (*it)->tag() );
|
||||||
|
|
||||||
|
if( m_reported != NULL )
|
||||||
|
{
|
||||||
|
x->addChild( m_reported->tag() );
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemList::const_iterator iti = m_items.begin();
|
||||||
|
for( ; iti != m_items.end(); ++iti )
|
||||||
|
x->addChild( (*iti)->tag() );
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORM_H__
|
||||||
|
#define DATAFORM_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
class DataFormItem;
|
||||||
|
class DataFormReported;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the possible Form Types.
|
||||||
|
*/
|
||||||
|
enum FormType
|
||||||
|
{
|
||||||
|
TypeForm, /**< The forms-processing entity is asking the forms-submitting
|
||||||
|
* entity to complete a form. */
|
||||||
|
TypeSubmit, /**< The forms-submitting entity is submitting data to the
|
||||||
|
* forms-processing entity. */
|
||||||
|
TypeCancel, /**< The forms-submitting entity has cancelled submission of data
|
||||||
|
* to the forms-processing entity. */
|
||||||
|
TypeResult, /**< The forms-processing entity is returning data (e.g., search
|
||||||
|
* results) to the forms-submitting entity, or the data is a
|
||||||
|
* generic data set. */
|
||||||
|
TypeInvalid /**< The form is invalid. Only possible if the form was created
|
||||||
|
* from an Tag which doesn't correctly describe a Data Form. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataForm : public StanzaExtension, public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* A list of DataFormItems.
|
||||||
|
*/
|
||||||
|
typedef std::list<DataFormItem*> ItemList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty form.
|
||||||
|
* @param type The form type.
|
||||||
|
* @param instructions Natural-language instructions for filling out the form. Should not contain
|
||||||
|
* newlines (\\n, \\r).
|
||||||
|
* @param title The natural-language title of the form. Should not contain newlines (\\n, \\r).
|
||||||
|
*/
|
||||||
|
DataForm( FormType type, const StringList& instructions, const std::string& title = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new, empty form without any instructions or title set. Probably best suited for
|
||||||
|
* result forms.
|
||||||
|
* @param type The form type.
|
||||||
|
* @param title The natural-language title of the form. Should not contain newlines (\\n, \\r).
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
DataForm( FormType type, const std::string& title = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm from an existing Tag/XML representation.
|
||||||
|
* @param tag The existing form to parse.
|
||||||
|
*/
|
||||||
|
DataForm( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DataForm, copying the given one.
|
||||||
|
* @param form The form to copy.
|
||||||
|
*/
|
||||||
|
DataForm( const DataForm& form );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataForm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the title of the form.
|
||||||
|
* @return The title of the form.
|
||||||
|
*/
|
||||||
|
const std::string& title() const { return m_title; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the title of the form.
|
||||||
|
* @param title The new title of the form.
|
||||||
|
* @note The title should not contain newlines (\\n, \\r).
|
||||||
|
*/
|
||||||
|
void setTitle( const std::string& title ) { m_title = title; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the natural-language instructions for the form.
|
||||||
|
* @return The fill-in instructions for the form.
|
||||||
|
*/
|
||||||
|
const StringList& instructions() const { return m_instructions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set natural-language instructions for the form.
|
||||||
|
* @param instructions The instructions for the form.
|
||||||
|
* @note The instructions should not contain newlines (\\n, \\r). Instead, every line should be an
|
||||||
|
* element of the StringMap. This allows for platform dependent newline handling on the target
|
||||||
|
* platform.
|
||||||
|
*/
|
||||||
|
void setInstructions( const StringList& instructions ) { m_instructions = instructions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the reported field list in a DataForm.
|
||||||
|
* @return The reported section, containing 0..n fields.
|
||||||
|
*/
|
||||||
|
const DataFormReported* reported() const { return m_reported; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of items in a DataForm.
|
||||||
|
* @return A list of items.
|
||||||
|
*/
|
||||||
|
const ItemList& items() const { return m_items; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the form's type.
|
||||||
|
* @return The form's type.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
FormType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the form's type.
|
||||||
|
* @param type The form's new type.
|
||||||
|
*/
|
||||||
|
void setType( FormType type ) { m_type = type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given Tag and creates an appropriate DataForm representation.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
* @return @b True on success, @b false otherwise.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
bool parse( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to @b true if the DataForm is valid, @b false otherwise.
|
||||||
|
*/
|
||||||
|
operator bool() const { return m_type != TypeInvalid; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new DataForm( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new DataForm( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FormType m_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringList m_instructions;
|
||||||
|
|
||||||
|
std::string m_title;
|
||||||
|
DataFormReported* m_reported;
|
||||||
|
ItemList m_items;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORM_H__
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dataformfield.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
static const char* fieldTypeValues[] =
|
||||||
|
{
|
||||||
|
"boolean", "fixed", "hidden", "jid-multi", "jid-single",
|
||||||
|
"list-multi", "list-single", "text-multi", "text-private", "text-single", ""
|
||||||
|
};
|
||||||
|
|
||||||
|
DataFormField::DataFormField( FieldType type )
|
||||||
|
: m_type( type ), m_required( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::DataFormField( const std::string& name, const std::string& value,
|
||||||
|
const std::string& label, FieldType type )
|
||||||
|
: m_type( type ), m_name( name ), m_label( label ), m_required( false )
|
||||||
|
{
|
||||||
|
m_values.push_back( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::DataFormField( const Tag* tag )
|
||||||
|
: m_type( TypeInvalid ), m_required( false )
|
||||||
|
{
|
||||||
|
if( !tag )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string& type = tag->findAttribute( TYPE );
|
||||||
|
if( type.empty() )
|
||||||
|
{
|
||||||
|
if( !tag->name().empty() )
|
||||||
|
m_type = TypeNone;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_type = (FieldType)util::lookup( type, fieldTypeValues );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "var" ) )
|
||||||
|
m_name = tag->findAttribute( "var" );
|
||||||
|
|
||||||
|
if( tag->hasAttribute( "label" ) )
|
||||||
|
m_label = tag->findAttribute( "label" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->name() == "desc" )
|
||||||
|
m_desc = (*it)->cdata();
|
||||||
|
else if( (*it)->name() == "required" )
|
||||||
|
m_required = true;
|
||||||
|
else if( (*it)->name() == "value" )
|
||||||
|
{
|
||||||
|
if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti )
|
||||||
|
addValue( (*it)->cdata() );
|
||||||
|
else
|
||||||
|
setValue( (*it)->cdata() );
|
||||||
|
}
|
||||||
|
else if( (*it)->name() == "option" )
|
||||||
|
{
|
||||||
|
Tag* v = (*it)->findChild( "value" );
|
||||||
|
if( v )
|
||||||
|
m_options.insert( std::make_pair( (*it)->findAttribute( "label" ), v->cdata() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField::~DataFormField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormField::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == TypeInvalid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* field = new Tag( "field" );
|
||||||
|
field->addAttribute( TYPE, util::lookup( m_type, fieldTypeValues ) );
|
||||||
|
field->addAttribute( "var", m_name );
|
||||||
|
field->addAttribute( "label", m_label );
|
||||||
|
if( m_required )
|
||||||
|
new Tag( field, "required" );
|
||||||
|
|
||||||
|
if( !m_desc.empty() )
|
||||||
|
new Tag( field, "desc", m_desc );
|
||||||
|
|
||||||
|
if( m_type == TypeListSingle || m_type == TypeListMulti )
|
||||||
|
{
|
||||||
|
StringMultiMap::const_iterator it = m_options.begin();
|
||||||
|
for( ; it != m_options.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* option = new Tag( field, "option", "label", (*it).first );
|
||||||
|
new Tag( option, "value", (*it).second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( m_type == TypeBoolean )
|
||||||
|
{
|
||||||
|
if( m_values.size() == 0 || m_values.front() == "false" || m_values.front() == "0" )
|
||||||
|
new Tag( field, "value", "0" );
|
||||||
|
else
|
||||||
|
new Tag( field, "value", "1" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti )
|
||||||
|
{
|
||||||
|
StringList::const_iterator it = m_values.begin();
|
||||||
|
for( ; it != m_values.end() ; ++it )
|
||||||
|
new Tag( field, "value", (*it) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_values.size() && !( m_type == TypeTextMulti || m_type == TypeListMulti
|
||||||
|
|| m_type == TypeBoolean || m_type == TypeJidMulti ) )
|
||||||
|
new Tag( field, "value", m_values.front() );
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMFIELD_H__
|
||||||
|
#define DATAFORMFIELD_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a single field in a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormField
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Describes the possible types of a Data Form Field.
|
||||||
|
*/
|
||||||
|
enum FieldType
|
||||||
|
{
|
||||||
|
TypeBoolean, /**< The field enables an entity to gather or provide an either-or
|
||||||
|
* choice between two options. The default value is "false". */
|
||||||
|
TypeFixed, /**< The field is intended for data description (e.g.,
|
||||||
|
* human-readable text such as "section" headers) rather than data
|
||||||
|
* gathering or provision. The <value/> child SHOULD NOT contain
|
||||||
|
* newlines (the \\n and \\r characters); instead an application SHOULD
|
||||||
|
* generate multiple fixed fields, each with one <value/> child. */
|
||||||
|
TypeHidden, /**< The field is not shown to the entity providing information, but
|
||||||
|
* instead is returned with the form. */
|
||||||
|
TypeJidMulti, /**< The field enables an entity to gather or provide multiple Jabber
|
||||||
|
* IDs.*/
|
||||||
|
TypeJidSingle, /**< The field enables an entity to gather or provide a single Jabber
|
||||||
|
* ID.*/
|
||||||
|
TypeListMulti, /**< The field enables an entity to gather or provide one or more options
|
||||||
|
* from among many. */
|
||||||
|
TypeListSingle, /**< The field enables an entity to gather or provide one option from
|
||||||
|
* among many. */
|
||||||
|
TypeTextMulti, /**< The field enables an entity to gather or provide multiple lines of
|
||||||
|
* text. */
|
||||||
|
TypeTextPrivate, /**< The field enables an entity to gather or provide a single line or
|
||||||
|
* word of text, which shall be obscured in an interface
|
||||||
|
* (e.g., *****). */
|
||||||
|
TypeTextSingle, /**< The field enables an entity to gather or provide a single line or
|
||||||
|
* word of text, which may be shown in an interface. This field type is
|
||||||
|
* the default and MUST be assumed if an entity receives a field type it
|
||||||
|
* does not understand.*/
|
||||||
|
TypeNone, /**< The field is child of either a <reported> or <item>
|
||||||
|
* element or has no type attribute. */
|
||||||
|
TypeInvalid /**< The field is invalid. Only possible if the field was created from
|
||||||
|
* a Tag not correctly describing a Data Form Field. */
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm field.
|
||||||
|
* @param type The type of the field. Default: text-single.
|
||||||
|
*/
|
||||||
|
DataFormField( FieldType type = TypeTextSingle );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new DataForm field and fills it with the given values.
|
||||||
|
* @param name The field's name (the value of the 'var' attribute).
|
||||||
|
* @param value The field's value.
|
||||||
|
* @param label The field's label.
|
||||||
|
* @param type The field's type.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
DataFormField( const std::string& name, const std::string& value = EmptyString,
|
||||||
|
const std::string& label = EmptyString, FieldType type = TypeTextSingle );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Data Form Field from an existing tag that describes a field.
|
||||||
|
* @param tag The tag to parse.
|
||||||
|
*/
|
||||||
|
DataFormField( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormField();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the optional values of a field.
|
||||||
|
* @return The options of a field.
|
||||||
|
*/
|
||||||
|
const StringMultiMap& options() const { return m_options; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to create a Tag representation of the form field. This is usually called by
|
||||||
|
* DataForm.
|
||||||
|
* @return A Tag hierarchically describing the form field, or NULL if the field is invalid (i.e.
|
||||||
|
* created from a Tag not correctly describing a Data Form Field).
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the name of the field (the content of the 'var' attribute).
|
||||||
|
* @return The name of the field.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name (the content of the 'var' attribute) of the field. The name identifies the
|
||||||
|
* field uniquely in the form.
|
||||||
|
* @param name The new name of the field.
|
||||||
|
* @note Fields of type other than 'fixed' MUST have a name, if it is 'fixed', it MAY.
|
||||||
|
*/
|
||||||
|
void setName( const std::string& name ) { m_name = name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the optional values of the field. The key of the map
|
||||||
|
* will be used as the label of the option, while the value will be used as ... the
|
||||||
|
* value. ;)
|
||||||
|
* @param options The optional values of a list* or *multi type of field.
|
||||||
|
*/
|
||||||
|
void setOptions( const StringMultiMap& options ) { m_options = options; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single option to the list of options.
|
||||||
|
* @param label The label of the option.
|
||||||
|
* @param value The value of the option.
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
void addOption( const std::string& label, const std::string& value )
|
||||||
|
{ m_options.insert( std::make_pair( label, value ) ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to determine whether or not this field is required.
|
||||||
|
* @return Whether or not this field is required.
|
||||||
|
*/
|
||||||
|
bool required() const { return m_required; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this field to set this field to be required.
|
||||||
|
* @param required Whether or not this field is required.
|
||||||
|
*/
|
||||||
|
void setRequired( bool required ) { m_required = required; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the describing label of this field.
|
||||||
|
* @return The describing label of this field.
|
||||||
|
*/
|
||||||
|
const std::string& label() const { return m_label; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the describing label of this field.
|
||||||
|
* @param label The describing label of this field.
|
||||||
|
*/
|
||||||
|
void setLabel( const std::string& label ) { m_label = label; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the description of this field.
|
||||||
|
* @return The description of this field
|
||||||
|
*/
|
||||||
|
const std::string& description() const { return m_desc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the description of this field.
|
||||||
|
* @param desc The description of this field.
|
||||||
|
*/
|
||||||
|
void setDescription( const std::string& desc ) { m_desc = desc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the value of this field.
|
||||||
|
* @return The value of this field.
|
||||||
|
*/
|
||||||
|
const std::string& value() const { return ( m_values.size() > 0 ) ? m_values.front() : EmptyString; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the value of this field.
|
||||||
|
* @param value The new value of this field.
|
||||||
|
*/
|
||||||
|
void setValue( const std::string& value ) { m_values.clear(); addValue( value ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the values of this field, if its of type 'text-multi'.
|
||||||
|
* @return The value of this field.
|
||||||
|
*/
|
||||||
|
const StringList& values() const { return m_values; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set multiple values of this field, if it is of type 'text-multi'. If its not,
|
||||||
|
* use @ref setValue() instead.
|
||||||
|
* @param values The new values of this field.
|
||||||
|
*/
|
||||||
|
void setValues( const StringList& values ) { m_values = values; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single value to the list of values.
|
||||||
|
* @param value The value to add.
|
||||||
|
*/
|
||||||
|
void addValue( const std::string& value ) { m_values.push_back( value ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the type of this field.
|
||||||
|
* @return The type of this field.
|
||||||
|
*/
|
||||||
|
FieldType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts to @b true if the FormBase is valid, @b false otherwise.
|
||||||
|
*/
|
||||||
|
operator bool() const { return m_type != TypeInvalid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FieldType m_type;
|
||||||
|
|
||||||
|
StringMultiMap m_options;
|
||||||
|
StringList m_values;
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_desc;
|
||||||
|
std::string m_label;
|
||||||
|
|
||||||
|
bool m_required;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMFIELD_H__
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormFieldContainer::DataFormFieldContainer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormFieldContainer::DataFormFieldContainer( const DataFormFieldContainer& dffc )
|
||||||
|
{
|
||||||
|
FieldList::const_iterator it = dffc.m_fields.begin();
|
||||||
|
for( ; it != dffc.m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
m_fields.push_back( new DataFormField( *(*it) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormFieldContainer::~DataFormFieldContainer()
|
||||||
|
{
|
||||||
|
util::clearList( m_fields );
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormField* DataFormFieldContainer::field( const std::string& field ) const
|
||||||
|
{
|
||||||
|
FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end() && (*it)->name() != field; ++it )
|
||||||
|
;
|
||||||
|
return it != m_fields.end() ? (*it) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMFIELDCONTAINER_H__
|
||||||
|
#define DATAFORMFIELDCONTAINER_H__
|
||||||
|
|
||||||
|
#include "dataformfield.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class DataFormField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstract base class for a XEP-0004 Data Form.
|
||||||
|
*
|
||||||
|
* You shouldn't need to use this class directly. Use DataForm instead.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new FieldContainer.
|
||||||
|
*/
|
||||||
|
DataFormFieldContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new FieldContainer, copying all fields from the given FieldContainer.
|
||||||
|
* @param dffc The FieldContainer to copy.
|
||||||
|
*/
|
||||||
|
DataFormFieldContainer( const DataFormFieldContainer& dffc );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormFieldContainer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of XEP-0004 Data Form Fields.
|
||||||
|
*/
|
||||||
|
typedef std::list<DataFormField*> FieldList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to check whether this form contains a field with the given name.
|
||||||
|
* @param field The name of the field (the content of the 'var' attribute).
|
||||||
|
* @return Whether or not the form contains the named field.
|
||||||
|
*/
|
||||||
|
bool hasField( const std::string& field ) const
|
||||||
|
{ return DataFormFieldContainer::field( field ) != 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to fetch a pointer to a field of the form. If no such field exists,
|
||||||
|
* 0 is returned.
|
||||||
|
* @param field The name of the field (the content of the 'var' attribute).
|
||||||
|
* @return A copy of the field with the given name if it exists, 0 otherwise.
|
||||||
|
*/
|
||||||
|
DataFormField* field( const std::string& field ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the list of fields of a form.
|
||||||
|
* @return The list of fields the form contains.
|
||||||
|
*/
|
||||||
|
FieldList& fields() { return m_fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to retrieve the const list of fields of a form.
|
||||||
|
* @return The const list of fields the form contains.
|
||||||
|
*/
|
||||||
|
const FieldList& fields() const { return m_fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to set the fields the form contains.
|
||||||
|
* @param fields The list of fields.
|
||||||
|
* @note Any previously set fields will be deleted. Always set all fields, not a delta.
|
||||||
|
*/
|
||||||
|
virtual void setFields( FieldList& fields ) { m_fields = fields; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to add a single field to the list of existing fields.
|
||||||
|
* @param field The field to add.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
virtual void addField( DataFormField* field ) { m_fields.push_back( field ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a single new Field and returns a pointer to that field.
|
||||||
|
* @param type The field's type.
|
||||||
|
* @param name The field's name (the value of the 'var' attribute).
|
||||||
|
* @param value The field's value.
|
||||||
|
* @param label The field's label.
|
||||||
|
* @since 0.9.4
|
||||||
|
*/
|
||||||
|
DataFormField* addField( DataFormField::FieldType type, const std::string& name,
|
||||||
|
const std::string& value = EmptyString,
|
||||||
|
const std::string& label = EmptyString )
|
||||||
|
{
|
||||||
|
DataFormField* field = new DataFormField( name, value, label, type );
|
||||||
|
m_fields.push_back( field );
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FieldList m_fields;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMFIELDCONTAINER_H__
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformitem.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormItem::DataFormItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormItem::DataFormItem( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "item" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const TagList &l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
DataFormField* f = new DataFormField( (*it) );
|
||||||
|
m_fields.push_back( f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormItem::~DataFormItem()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormItem::tag() const
|
||||||
|
{
|
||||||
|
Tag* i = new Tag ( "item" );
|
||||||
|
DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
i->addChild( (*it)->tag() );
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMITEM_H__
|
||||||
|
#define DATAFORMITEM_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of an <item> element in a XEP-0004 Data Form of type result.
|
||||||
|
*
|
||||||
|
* There are some constraints regarding usage of this element you should be aware of. Check XEP-0004
|
||||||
|
* section 3.4. This class does not enforce correct usage at this point.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormItem : public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an empty 'item' element you can add fields to.
|
||||||
|
*/
|
||||||
|
DataFormItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a 'item' element and fills it with the 'field' elements contained in the given Tag.
|
||||||
|
* The Tag's root element must be a 'item' element. Its child element should be 'field' elements.
|
||||||
|
* @param tag The tag to read the 'field' elements from.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
DataFormItem( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of the current object.
|
||||||
|
* @return A Tag representation of the current object.
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMITEM_H__
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "dataformreported.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DataFormReported::DataFormReported()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormReported::DataFormReported( Tag* tag )
|
||||||
|
{
|
||||||
|
if( tag->name() != "reported" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const TagList &l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
DataFormField* f = new DataFormField( (*it) );
|
||||||
|
m_fields.push_back( f );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFormReported::~DataFormReported()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DataFormReported::tag() const
|
||||||
|
{
|
||||||
|
Tag* r = new Tag ( "reported" );
|
||||||
|
DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin();
|
||||||
|
for( ; it != m_fields.end(); ++it )
|
||||||
|
{
|
||||||
|
r->addChild( (*it)->tag() );
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DATAFORMREPORTED_H__
|
||||||
|
#define DATAFORMREPORTED_H__
|
||||||
|
|
||||||
|
#include "dataformfieldcontainer.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a <reported> element in a XEP-0004 Data Form of type result.
|
||||||
|
*
|
||||||
|
* There are some constraints regarding usage of this element you should be aware of. Check XEP-0004
|
||||||
|
* section 3.4. This class does not enforce correct usage at this point.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.7
|
||||||
|
*/
|
||||||
|
class GLOOX_API DataFormReported : public DataFormFieldContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an empty 'reported' element you can add fields to.
|
||||||
|
*/
|
||||||
|
DataFormReported();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a 'reported' element and fills it with the 'field' elements contained in the given Tag.
|
||||||
|
* The Tag's root element must be a 'reported' element. Its child element should be 'field' elements.
|
||||||
|
* @param tag The tag to read the 'field' elements from.
|
||||||
|
* @since 0.8.5
|
||||||
|
*/
|
||||||
|
DataFormReported( Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DataFormReported();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of the current object.
|
||||||
|
* @return A Tag representation of the current object.
|
||||||
|
*/
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DATAFORMREPORTED_H__
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "delayeddelivery.h"
|
||||||
|
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
DelayedDelivery::DelayedDelivery( const JID& from, const std::string stamp, const std::string& reason )
|
||||||
|
: StanzaExtension( ExtDelay ), m_from( from ), m_stamp( stamp ), m_reason( reason ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !m_stamp.empty() )
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DelayedDelivery::DelayedDelivery( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDelay ), m_valid( false )
|
||||||
|
{
|
||||||
|
if( !tag || !tag->hasAttribute( "stamp" ) )
|
||||||
|
return;
|
||||||
|
if( !( tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_DELAY ) ) )
|
||||||
|
if( !( tag->name() == "delay" && tag->hasAttribute( XMLNS, XMLNS_DELAY ) ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_reason = tag->cdata();
|
||||||
|
m_stamp = tag->findAttribute( "stamp" );
|
||||||
|
m_from = tag->findAttribute( "from" );
|
||||||
|
m_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayedDelivery::~DelayedDelivery()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& DelayedDelivery::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter =
|
||||||
|
"/presence/delay[@xmlns='" + XMLNS_DELAY + "']"
|
||||||
|
"|/message/delay[@xmlns='" + XMLNS_DELAY + "']"
|
||||||
|
"|/presence/x[@xmlns='" + XMLNS_X_DELAY + "']"
|
||||||
|
"|/message/x[@xmlns='" + XMLNS_X_DELAY + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* DelayedDelivery::tag() const
|
||||||
|
{
|
||||||
|
if( !m_valid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* t = new Tag( "delay" );
|
||||||
|
t->addAttribute( XMLNS, XMLNS_DELAY );
|
||||||
|
if( m_from )
|
||||||
|
t->addAttribute( "from", m_from.full() );
|
||||||
|
if( !m_stamp.empty() )
|
||||||
|
t->addAttribute( "stamp", m_stamp );
|
||||||
|
if( !m_reason.empty() )
|
||||||
|
t->setCData( m_reason );
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DELAYEDDELIVERY_H__
|
||||||
|
#define DELAYEDDELIVERY_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "jid.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is an implementation of XEP-0203 (Delayed Delivery).
|
||||||
|
*
|
||||||
|
* The class also implements the deprecated XEP-0091 (Delayed Delivery) in a read-only fashion.
|
||||||
|
* It understands both XEP formats for input, but any output will conform to XEP-0203.
|
||||||
|
*
|
||||||
|
* XEP Version: 0.1
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
class GLOOX_API DelayedDelivery : public StanzaExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a new object and fills it according to the parameters.
|
||||||
|
* @param from The JID of the original sender or the entity that delayed the sending.
|
||||||
|
* @param stamp The datetime stamp of the original send.
|
||||||
|
* @param reason An optional natural language reason for the delay.
|
||||||
|
*/
|
||||||
|
DelayedDelivery( const JID& from, const std::string stamp,
|
||||||
|
const std::string& reason = "" );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
DelayedDelivery( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DelayedDelivery();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the datetime when the stanza was originally sent.
|
||||||
|
* The format MUST adhere to the dateTime format specified in XEP-0082 and MUST
|
||||||
|
* be expressed in UTC.
|
||||||
|
* @return The original datetime.
|
||||||
|
*/
|
||||||
|
const std::string& stamp() const { return m_stamp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the original datetime.
|
||||||
|
* @param stamp The original datetime.
|
||||||
|
*/
|
||||||
|
void setStamp( const std::string& stamp ) { m_stamp = stamp; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JID of the original sender of the stanza or the entity that
|
||||||
|
* delayed the sending.
|
||||||
|
* The format MUST adhere to the dateTime format specified in XEP-0082 and MUST
|
||||||
|
* be expressed in UTC.
|
||||||
|
* @return The JID.
|
||||||
|
*/
|
||||||
|
const JID& from() const { return m_from; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the JID of the origianl sender or the entity that delayed the sending.
|
||||||
|
* @param from The JID.
|
||||||
|
*/
|
||||||
|
void setFrom( const JID& from ) { m_from = from; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a natural language reason for the delay.
|
||||||
|
* @return A natural language reason for the delay.
|
||||||
|
*/
|
||||||
|
const std::string& reason() const { return m_reason; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the reason for the delay.
|
||||||
|
* @param reason The reason for the delay.
|
||||||
|
*/
|
||||||
|
void setReason( const std::string& reason ) { m_reason = reason; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new DelayedDelivery( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new DelayedDelivery( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
JID m_from;
|
||||||
|
std::string m_stamp;
|
||||||
|
std::string m_reason;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DELAYEDDELIVERY_H__
|
|
@ -0,0 +1,535 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "disco.h"
|
||||||
|
#include "discohandler.h"
|
||||||
|
#include "dataform.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "clientbase.h"
|
||||||
|
#include "disconodehandler.h"
|
||||||
|
#include "softwareversion.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
// ---- Disco::Identity ----
|
||||||
|
Disco::Identity::Identity( const std::string& category,
|
||||||
|
const std::string& type,
|
||||||
|
const std::string& name )
|
||||||
|
: m_category( category ), m_type( type ), m_name( name )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::Identity( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "identity" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_category = tag->findAttribute( "category" );
|
||||||
|
m_type = tag->findAttribute( "type" );
|
||||||
|
m_name = tag->findAttribute( "name" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::Identity( const Identity& id )
|
||||||
|
: m_category( id.m_category ), m_type( id.m_type ), m_name( id.m_name )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Identity::~Identity()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Identity::tag() const
|
||||||
|
{
|
||||||
|
if( m_category.empty() || m_type.empty() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* i = new Tag( "identity" );
|
||||||
|
i->addAttribute( "category", m_category );
|
||||||
|
i->addAttribute( "type", m_type );
|
||||||
|
|
||||||
|
if( !m_name.empty() )
|
||||||
|
i->addAttribute( "name", m_name );
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Identity ----
|
||||||
|
|
||||||
|
// ---- Disco::Info ----
|
||||||
|
Disco::Info::Info( const std::string& node, bool defaultFeatures )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_node( node ), m_form( 0 )
|
||||||
|
{
|
||||||
|
if( defaultFeatures )
|
||||||
|
{
|
||||||
|
m_features.push_back( XMLNS_DISCO_INFO );
|
||||||
|
m_features.push_back( XMLNS_DISCO_ITEMS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::Info( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_form( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_INFO )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
const std::string& name = (*it)->name();
|
||||||
|
if( name == "identity" )
|
||||||
|
m_identities.push_back( new Identity( (*it) ) );
|
||||||
|
else if( name == "feature" && (*it)->hasAttribute( "var" ) )
|
||||||
|
m_features.push_back( (*it)->findAttribute( "var" ) );
|
||||||
|
else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA )
|
||||||
|
m_form = new DataForm( (*it) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::Info( const Info& info )
|
||||||
|
: StanzaExtension( ExtDiscoInfo ), m_node( info.m_node ), m_features( info.m_features ),
|
||||||
|
m_identities( info.m_identities ), m_form( info.m_form ? new DataForm( *(info.m_form) ) : 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Info::~Info()
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
util::clearList( m_identities );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::Info::setForm( DataForm* form )
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disco::Info::hasFeature( const std::string& feature ) const
|
||||||
|
{
|
||||||
|
StringList::const_iterator it = m_features.begin();
|
||||||
|
for( ; it != m_features.end() && (*it) != feature; ++it )
|
||||||
|
;
|
||||||
|
return it != m_features.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Disco::Info::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_INFO + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Info::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_INFO );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
|
||||||
|
IdentityList::const_iterator it_i = m_identities.begin();
|
||||||
|
for( ; it_i != m_identities.end(); ++it_i )
|
||||||
|
t->addChild( (*it_i)->tag() );
|
||||||
|
|
||||||
|
StringList::const_iterator it_f = m_features.begin();
|
||||||
|
for( ; it_f != m_features.end(); ++it_f )
|
||||||
|
new Tag( t, "feature", "var", (*it_f) );
|
||||||
|
|
||||||
|
if( m_form )
|
||||||
|
t->addChild( m_form->tag() );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Info ----
|
||||||
|
|
||||||
|
// ---- Disco::Item ----
|
||||||
|
Disco::Item::Item( const Tag* tag )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "item" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_jid = tag->findAttribute( "jid" );
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
m_name = tag->findAttribute( "name" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Item::tag() const
|
||||||
|
{
|
||||||
|
if( !m_jid )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* i = new Tag( "item" );
|
||||||
|
i->addAttribute( "jid", m_jid.full() );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
i->addAttribute( "node", m_node );
|
||||||
|
if( !m_name.empty() )
|
||||||
|
i->addAttribute( "name", m_name );
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Item ----
|
||||||
|
|
||||||
|
// ---- Disco::Items ----
|
||||||
|
Disco::Items::Items( const std::string& node )
|
||||||
|
: StanzaExtension( ExtDiscoItems ), m_node( node )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Items::Items( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtDiscoItems )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_ITEMS )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_node = tag->findAttribute( "node" );
|
||||||
|
|
||||||
|
const TagList& l = tag->children();
|
||||||
|
TagList::const_iterator it = l.begin();
|
||||||
|
for( ; it != l.end(); ++it )
|
||||||
|
{
|
||||||
|
const std::string& name = (*it)->name();
|
||||||
|
if( name == "item" )
|
||||||
|
m_items.push_back( new Item( (*it) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::Items::~Items()
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::Items::setItems( const ItemList& items )
|
||||||
|
{
|
||||||
|
util::clearList( m_items );
|
||||||
|
m_items = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const std::string& Disco::Items::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_ITEMS + "']";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tag* Disco::Items::tag() const
|
||||||
|
{
|
||||||
|
Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_ITEMS );
|
||||||
|
|
||||||
|
if( !m_node.empty() )
|
||||||
|
t->addAttribute( "node", m_node );
|
||||||
|
|
||||||
|
ItemList::const_iterator it_i = m_items.begin();
|
||||||
|
for( ; it_i != m_items.end(); ++it_i )
|
||||||
|
t->addChild( (*it_i)->tag() );
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// ---- ~Disco::Items ----
|
||||||
|
|
||||||
|
// ---- Disco ----
|
||||||
|
Disco::Disco( ClientBase* parent )
|
||||||
|
: m_parent( parent ), m_form( 0 )
|
||||||
|
{
|
||||||
|
addFeature( XMLNS_VERSION );
|
||||||
|
// addFeature( XMLNS_DISCO_INFO ); //handled by Disco::Info now
|
||||||
|
// addFeature( XMLNS_DISCO_ITEMS ); //handled by Disco::Info now
|
||||||
|
if( m_parent )
|
||||||
|
{
|
||||||
|
m_parent->registerIqHandler( this, ExtDiscoInfo );
|
||||||
|
m_parent->registerIqHandler( this, ExtDiscoItems );
|
||||||
|
m_parent->registerIqHandler( this, ExtVersion );
|
||||||
|
m_parent->registerStanzaExtension( new Disco::Info() );
|
||||||
|
m_parent->registerStanzaExtension( new Disco::Items() );
|
||||||
|
m_parent->registerStanzaExtension( new SoftwareVersion() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Disco::~Disco()
|
||||||
|
{
|
||||||
|
util::clearList( m_identities );
|
||||||
|
delete m_form;
|
||||||
|
|
||||||
|
if( m_parent )
|
||||||
|
{
|
||||||
|
m_parent->removeIqHandler( this, ExtDiscoInfo );
|
||||||
|
m_parent->removeIqHandler( this, ExtDiscoItems );
|
||||||
|
m_parent->removeIqHandler( this, ExtVersion );
|
||||||
|
m_parent->removeStanzaExtension( ExtDiscoInfo );
|
||||||
|
m_parent->removeStanzaExtension( ExtDiscoItems );
|
||||||
|
m_parent->removeStanzaExtension( ExtVersion );
|
||||||
|
m_parent->removeIDHandler( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setForm( DataForm* form )
|
||||||
|
{
|
||||||
|
delete m_form;
|
||||||
|
m_form = form;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disco::handleIq( const IQ& iq )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Get:
|
||||||
|
{
|
||||||
|
IQ re( IQ::Result, iq.from(), iq.id() );
|
||||||
|
re.setFrom( iq.to() );
|
||||||
|
|
||||||
|
const SoftwareVersion* sv = iq.findExtension<SoftwareVersion>( ExtVersion );
|
||||||
|
if( sv )
|
||||||
|
{
|
||||||
|
re.addExtension( new SoftwareVersion( m_versionName, m_versionVersion, m_versionOs ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Info *info = iq.findExtension<Info>( ExtDiscoInfo );
|
||||||
|
if( info )
|
||||||
|
{
|
||||||
|
Info *i = new Info( EmptyString, true );
|
||||||
|
if( !info->node().empty() )
|
||||||
|
{
|
||||||
|
i->setNode( info->node() );
|
||||||
|
IdentityList identities;
|
||||||
|
StringList features;
|
||||||
|
DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( info->node() );
|
||||||
|
if( it == m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
delete i;
|
||||||
|
IQ re( IQ::Error, iq.from(), iq.id() );
|
||||||
|
re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
|
||||||
|
for( ; in != (*it).second.end(); ++in )
|
||||||
|
{
|
||||||
|
IdentityList il = (*in)->handleDiscoNodeIdentities( iq.from(), info->node() );
|
||||||
|
il.sort(); // needed on win32
|
||||||
|
identities.merge( il );
|
||||||
|
StringList fl = (*in)->handleDiscoNodeFeatures( iq.from(), info->node() );
|
||||||
|
fl.sort(); // needed on win32
|
||||||
|
features.merge( fl );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i->setIdentities( identities );
|
||||||
|
i->setFeatures( features );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IdentityList il;
|
||||||
|
IdentityList::const_iterator it = m_identities.begin();
|
||||||
|
for( ; it != m_identities.end(); ++it )
|
||||||
|
{
|
||||||
|
il.push_back( new Identity( *(*it) ) );
|
||||||
|
}
|
||||||
|
i->setIdentities( il );
|
||||||
|
i->setFeatures( m_features );
|
||||||
|
if( m_form )
|
||||||
|
i->setForm( new DataForm( *m_form ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
re.addExtension( i );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Items *items = iq.findExtension<Items>( ExtDiscoItems );
|
||||||
|
if( items )
|
||||||
|
{
|
||||||
|
Items *i = new Items( items->node() );
|
||||||
|
if( !items->node().empty() )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( items->node() );
|
||||||
|
if( it == m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
delete i;
|
||||||
|
IQ re( IQ::Error, iq.from(), iq.id() );
|
||||||
|
re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ItemList itemlist;
|
||||||
|
DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
|
||||||
|
for( ; in != (*it).second.end(); ++in )
|
||||||
|
{
|
||||||
|
ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() );
|
||||||
|
il.sort(); // needed on win32
|
||||||
|
itemlist.merge( il );
|
||||||
|
}
|
||||||
|
i->setItems( itemlist );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
re.addExtension( i );
|
||||||
|
m_parent->send( re );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IQ::Set:
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
DiscoHandlerList::const_iterator it = m_discoHandlers.begin();
|
||||||
|
for( ; it != m_discoHandlers.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it)->handleDiscoSet( iq ) )
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::handleIqID( const IQ& iq, int context )
|
||||||
|
{
|
||||||
|
DiscoHandlerMap::iterator it = m_track.find( iq.id() );
|
||||||
|
if( it != m_track.end() && (*it).second.dh )
|
||||||
|
{
|
||||||
|
switch( iq.subtype() )
|
||||||
|
{
|
||||||
|
case IQ::Result:
|
||||||
|
switch( context )
|
||||||
|
{
|
||||||
|
case GetDiscoInfo:
|
||||||
|
{
|
||||||
|
const Info* di = iq.findExtension<Info>( ExtDiscoInfo );
|
||||||
|
if( di )
|
||||||
|
(*it).second.dh->handleDiscoInfo( iq.from(), *di, (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GetDiscoItems:
|
||||||
|
{
|
||||||
|
const Items* di = iq.findExtension<Items>( ExtDiscoItems );
|
||||||
|
if( di )
|
||||||
|
(*it).second.dh->handleDiscoItems( iq.from(), *di, (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IQ::Error:
|
||||||
|
{
|
||||||
|
(*it).second.dh->handleDiscoError( iq.from(), iq.error(), (*it).second.context );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_track.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::getDisco( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
IdType idType, const std::string& tid )
|
||||||
|
{
|
||||||
|
const std::string& id = tid.empty() ? m_parent->getID() : tid;
|
||||||
|
|
||||||
|
IQ iq( IQ::Get, to, id );
|
||||||
|
if( idType == GetDiscoInfo )
|
||||||
|
iq.addExtension( new Info( node ) );
|
||||||
|
else
|
||||||
|
iq.addExtension( new Items( node ) );
|
||||||
|
|
||||||
|
DiscoHandlerContext ct;
|
||||||
|
ct.dh = dh;
|
||||||
|
ct.context = context;
|
||||||
|
m_track[id] = ct;
|
||||||
|
m_parent->send( iq, this, idType );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setVersion( const std::string& name, const std::string& version, const std::string& os )
|
||||||
|
{
|
||||||
|
m_versionName = name;
|
||||||
|
m_versionVersion = version;
|
||||||
|
m_versionOs = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::setIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name )
|
||||||
|
{
|
||||||
|
util::clearList( m_identities );
|
||||||
|
addIdentity( category, type, name );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeDiscoHandler( DiscoHandler* dh )
|
||||||
|
{
|
||||||
|
m_discoHandlers.remove( dh );
|
||||||
|
DiscoHandlerMap::iterator t;
|
||||||
|
DiscoHandlerMap::iterator it = m_track.begin();
|
||||||
|
while( it != m_track.end() )
|
||||||
|
{
|
||||||
|
t = it;
|
||||||
|
++it;
|
||||||
|
if( dh == (*t).second.dh )
|
||||||
|
{
|
||||||
|
m_track.erase( t );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::registerNodeHandler( DiscoNodeHandler* nh, const std::string& node )
|
||||||
|
{
|
||||||
|
m_nodeHandlers[node].push_back( nh );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeNodeHandler( DiscoNodeHandler* nh, const std::string& node )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::iterator it = m_nodeHandlers.find( node );
|
||||||
|
if( it != m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
(*it).second.remove( nh );
|
||||||
|
if( (*it).second.empty() )
|
||||||
|
m_nodeHandlers.erase( it );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disco::removeNodeHandlers( DiscoNodeHandler* nh )
|
||||||
|
{
|
||||||
|
DiscoNodeHandlerMap::iterator it = m_nodeHandlers.begin();
|
||||||
|
DiscoNodeHandlerMap::iterator it2;
|
||||||
|
while( it != m_nodeHandlers.end() )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
removeNodeHandler( nh, (*it2).first );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StringList Disco::features( bool defaultFeatures ) const
|
||||||
|
{
|
||||||
|
StringList f = m_features;
|
||||||
|
if( defaultFeatures )
|
||||||
|
{
|
||||||
|
f.push_back( XMLNS_DISCO_INFO );
|
||||||
|
f.push_back( XMLNS_DISCO_ITEMS );
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,636 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCO_H__
|
||||||
|
#define DISCO_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
|
||||||
|
#include "iqhandler.h"
|
||||||
|
#include "jid.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClientBase;
|
||||||
|
class DataForm;
|
||||||
|
class DiscoHandler;
|
||||||
|
class DiscoNodeHandler;
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class implements XEP-0030 (Service Discovery) and XEP-0092 (Software Version).
|
||||||
|
*
|
||||||
|
* ClientBase will automatically instantiate a Disco object. It can be used to
|
||||||
|
* announce special features of your client, or its version, or...
|
||||||
|
*
|
||||||
|
* XEP version: 2.2
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API Disco : public IqHandler
|
||||||
|
{
|
||||||
|
friend class ClientBase;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Identity; // declared below class Info
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of pointers to Identity objects. Used with Disco::Info.
|
||||||
|
*/
|
||||||
|
typedef std::list<Identity*> IdentityList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco Info element (from Service Discovery, XEP-0030)
|
||||||
|
* as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Info : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Returns the queried node identifier, if any.
|
||||||
|
* @return The node identifier. May be empty.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's supported features.
|
||||||
|
* @return A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
const StringList& features() const { return m_features; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to check if the entity the Info came from supports agiven feature.
|
||||||
|
* @param feature The feature to check for.
|
||||||
|
* @return @b True if the entity announces support for the feature, @b false otherwise.
|
||||||
|
*/
|
||||||
|
bool hasFeature( const std::string& feature ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's identities.
|
||||||
|
* @return A list of pointers to Identity objects.
|
||||||
|
*/
|
||||||
|
const IdentityList& identities() const { return m_identities; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an optionally included data form. This is used by e.g. MUC (XEP-0045).
|
||||||
|
* @return An optional data form included in the disco#info. May be 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an optional DataForm, e.g. for XEP-0232. Only one form can be added
|
||||||
|
* at this point.
|
||||||
|
* @param form An optional DataForm to include in the Info reply.
|
||||||
|
* The form will be owned by and deleted on destruction of the Info object.
|
||||||
|
* @note If called more than once the previously set form will be deleted.
|
||||||
|
*/
|
||||||
|
void setForm( DataForm* form );
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Info( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Info( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_INFO_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Creates a empty Info object, suitable for making disco#info requests.
|
||||||
|
* @param node The node identifier to query (optional).
|
||||||
|
* @param defaultFeatures Indicates whether or not the default features should be
|
||||||
|
* included in the Info. Should be @b false for requests, @b true for replies.
|
||||||
|
* Defaults to @b false.
|
||||||
|
*/
|
||||||
|
Info( const std::string& node = EmptyString, bool defaultFeatures = false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Info object from the given Tag.
|
||||||
|
* @param tag A <query> tag in the disco#info namespace, (possibly) containing
|
||||||
|
* a disco#info reply.
|
||||||
|
*/
|
||||||
|
Info( const Tag* tag );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
* @param info An Info object to copy.
|
||||||
|
*/
|
||||||
|
Info( const Info& info );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Info();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current info node.
|
||||||
|
* @param node The info node.
|
||||||
|
*/
|
||||||
|
void setNode( const std::string& node ) { m_node = node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's features.
|
||||||
|
* @param features A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
void setFeatures( const StringList& features )
|
||||||
|
{
|
||||||
|
StringList fl( features );
|
||||||
|
fl.sort(); // needed on win32
|
||||||
|
m_features.merge( fl );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's identities.
|
||||||
|
* @param identities A list of pointers to the entity's identities.
|
||||||
|
* @note The Identity objects pointed to will be owned by the Info object. The
|
||||||
|
* list should neither be used again nor should the Identity objects be deleted.
|
||||||
|
*/
|
||||||
|
void setIdentities( const IdentityList& identities ) { m_identities = identities; }
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
StringList m_features;
|
||||||
|
IdentityList m_identities;
|
||||||
|
DataForm* m_form;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco identity (Service Discovery, XEP-0030).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Identity
|
||||||
|
{
|
||||||
|
friend class Info;
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a Disco Identity from a category, type and name.
|
||||||
|
* See http://www.xmpp.org/registrar/disco-categories.html for more info.
|
||||||
|
* @param category The identity's category.
|
||||||
|
* @param type The identity's type.
|
||||||
|
* @param name The identity's name.
|
||||||
|
*/
|
||||||
|
Identity( const std::string& category,
|
||||||
|
const std::string& type,
|
||||||
|
const std::string& name );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy Contructor.
|
||||||
|
* @param id An Identity to create a new Identity object from.
|
||||||
|
*/
|
||||||
|
Identity( const Identity& id );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Identity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's category.
|
||||||
|
* @return The identity's category.
|
||||||
|
*/
|
||||||
|
const std::string& category() const { return m_category; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's type.
|
||||||
|
* @return The identity's type.
|
||||||
|
*/
|
||||||
|
const std::string& type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identity's name.
|
||||||
|
* @return The identity's name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of this identity.
|
||||||
|
* @return A Tag, or 0.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Creates a Disco Identity from the given Tag.
|
||||||
|
* @param tag A Tag representation of a disco identity.
|
||||||
|
*/
|
||||||
|
Identity( const Tag* tag );
|
||||||
|
|
||||||
|
std::string m_category; /**< The identity's category. */
|
||||||
|
std::string m_type; /**< The identity's type. */
|
||||||
|
std::string m_name; /**< The identity's name. */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Item; // declared below class Items
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of pointers to Item objects. Used with Disco::Items.
|
||||||
|
*/
|
||||||
|
typedef std::list<Item*> ItemList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco query element (from Service Discovery, XEP-0030)
|
||||||
|
* in the disco#items namespace, implemented as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Items : public StanzaExtension
|
||||||
|
{
|
||||||
|
friend class Disco;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// This needs to be public so one can proactively send a list of adhoc commands
|
||||||
|
// see XEP-0050
|
||||||
|
/**
|
||||||
|
* Creates an empty Items object, suitable for making disco#items requests.
|
||||||
|
* @param node The node identifier to query (optional).
|
||||||
|
*/
|
||||||
|
Items( const std::string& node = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Items();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to set the entity's/node's items.
|
||||||
|
* @param items A list of pointers to the entity's/node's items.
|
||||||
|
* @note The Item objects pointed to will be owned by the Items object. The
|
||||||
|
* list should neither be used again nor should the Item objects be deleted.
|
||||||
|
*/
|
||||||
|
void setItems( const ItemList& items );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the queried node identifier, if any.
|
||||||
|
* @return The node identifier. May be empty.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's/node's items.
|
||||||
|
* @return A list of pointers to Item objects.
|
||||||
|
*/
|
||||||
|
const ItemList& items() const { return m_items; }
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Items( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Items( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_ITEMS_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* Creates an Items object from the given Tag.
|
||||||
|
* @param tag A <query> tag in the disco#items namespace, (possibly) containing
|
||||||
|
* a disco#items reply.
|
||||||
|
*/
|
||||||
|
Items( const Tag* tag );
|
||||||
|
|
||||||
|
std::string m_node;
|
||||||
|
ItemList m_items;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An abstraction of a Disco item (Service Discovery, XEP-0030).
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Item
|
||||||
|
{
|
||||||
|
friend class Items;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs a Disco Item from a JID, node and name.
|
||||||
|
* @param jid The item's JID.
|
||||||
|
* @param node The item's type.
|
||||||
|
* @param name The item's name.
|
||||||
|
*/
|
||||||
|
Item( const JID& jid,
|
||||||
|
const std::string& node,
|
||||||
|
const std::string& name )
|
||||||
|
: m_jid( jid ), m_node( node ), m_name( name ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
~Item() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's JID.
|
||||||
|
* @return The item's JID.
|
||||||
|
*/
|
||||||
|
const JID& jid() const { return m_jid; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's node.
|
||||||
|
* @return The item's node.
|
||||||
|
*/
|
||||||
|
const std::string& node() const { return m_node; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item's name.
|
||||||
|
* @return The item's name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_name; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Tag representation of this item.
|
||||||
|
* @return A Tag, or 0.
|
||||||
|
*/
|
||||||
|
Tag* tag() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Creates a Disco Item from the given Tag.
|
||||||
|
* @param tag A Tag representation of a Disco item.
|
||||||
|
*/
|
||||||
|
Item( const Tag* tag );
|
||||||
|
|
||||||
|
JID m_jid; /**< The item's jid. */
|
||||||
|
std::string m_node; /**< The item's type. */
|
||||||
|
std::string m_name; /**< The item's name. */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a feature to the list of supported Jabber features.
|
||||||
|
* The list will be posted as an answer to IQ queries in the
|
||||||
|
* "http://jabber.org/protocol/disco#info" namespace.
|
||||||
|
* These IQ packets will also be forwarded to the
|
||||||
|
* application's IqHandler, if it listens to the @c disco\#info namespace.
|
||||||
|
* By default, disco(very) queries are handled by the library.
|
||||||
|
* By default, all supported, not disabled features are announced.
|
||||||
|
* @param feature A feature (namespace) the host app supports.
|
||||||
|
* @note Use this function for non-queryable features. For nodes that shall
|
||||||
|
* answer to @c disco\#info queries, use registerNodeHandler().
|
||||||
|
*/
|
||||||
|
void addFeature( const std::string& feature )
|
||||||
|
{ m_features.push_back( feature ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given feature from the list of advertised client features.
|
||||||
|
* @param feature The feature to remove.
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
void removeFeature( const std::string& feature )
|
||||||
|
{ m_features.remove( feature ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets you retrieve the features this Disco instance supports.
|
||||||
|
* @param defaultFeatures Include default features. Defaults to @b false.
|
||||||
|
* @return A list of supported features/namespaces.
|
||||||
|
*/
|
||||||
|
const StringList features( bool defaultFeatures = false ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the given JID for general infomation according to
|
||||||
|
* XEP-0030 (Service Discovery).
|
||||||
|
* To receive the results inherit from DiscoHandler and register with the Disco object.
|
||||||
|
* @param to The destination-JID of the query.
|
||||||
|
* @param node An optional node to query. Not inserted if empty.
|
||||||
|
* @param dh The DiscoHandler to notify about results.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @param tid An optional id that is going to be used as the IQ request's id. Only
|
||||||
|
* necessary if you need to know the request's id.
|
||||||
|
*/
|
||||||
|
void getDiscoInfo( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
const std::string& tid = EmptyString )
|
||||||
|
{ getDisco( to, node, dh, context, GetDiscoInfo, tid ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the given JID for its items according to
|
||||||
|
* XEP-0030 (Service Discovery).
|
||||||
|
* To receive the results inherit from DiscoHandler and register with the Disco object.
|
||||||
|
* @param to The destination-JID of the query.
|
||||||
|
* @param node An optional node to query. Not inserted if empty.
|
||||||
|
* @param dh The DiscoHandler to notify about results.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @param tid An optional id that is going to be used as the IQ request's id. Only
|
||||||
|
* necessary if you need to know the request's id.
|
||||||
|
*/
|
||||||
|
void getDiscoItems( const JID& to, const std::string& node, DiscoHandler* dh, int context,
|
||||||
|
const std::string& tid = EmptyString )
|
||||||
|
{ getDisco( to, node, dh, context, GetDiscoItems, tid ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the version of the host application using this library.
|
||||||
|
* The library takes care of jabber:iq:version requests. These
|
||||||
|
* IQ packets will not be forwarded to the IqHandlers.
|
||||||
|
* @param name The name to be returned to inquireing clients.
|
||||||
|
* @param version The version to be returned to inquireing clients.
|
||||||
|
* @param os The operating system to announce. Default: don't include.
|
||||||
|
*/
|
||||||
|
void setVersion( const std::string& name, const std::string& version,
|
||||||
|
const std::string& os = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised name.
|
||||||
|
* @return The application's advertised name.
|
||||||
|
*/
|
||||||
|
const std::string& name() const { return m_versionName; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised version.
|
||||||
|
* @return The application's advertised version.
|
||||||
|
*/
|
||||||
|
const std::string& version() const { return m_versionVersion; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application's advertised operating system.
|
||||||
|
* @return The application's advertised operating system.
|
||||||
|
*/
|
||||||
|
const std::string& os() const { return m_versionOs; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the identity of this entity.
|
||||||
|
* The library uses this information to answer disco#info requests
|
||||||
|
* with a correct identity.
|
||||||
|
* XEP-0030 requires an entity to have at least one identity. See XEP-0030
|
||||||
|
* for more information on categories and types.
|
||||||
|
* @param category The entity category of this client. Default: client.
|
||||||
|
* @param type The type of this entity. Default: bot.
|
||||||
|
* @param name The name of the entity. Default: empty.
|
||||||
|
* @note An entity can have more than one identity. You cann add more identities
|
||||||
|
* using addIdentity(). A call to setIdentity() will clear the list of identities
|
||||||
|
* and, after that, add the new identity given by the arguments to setIdentity().
|
||||||
|
*/
|
||||||
|
void setIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name = EmptyString );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds another identity to the list of identities.
|
||||||
|
* @param category The entity category of this client. Default: client.
|
||||||
|
* @param type The type of this entity. Default: bot.
|
||||||
|
* @param name The name of the entity. Default: empty.
|
||||||
|
*/
|
||||||
|
void addIdentity( const std::string& category, const std::string& type,
|
||||||
|
const std::string& name = EmptyString )
|
||||||
|
{ m_identities.push_back( new Identity( category, type, name ) ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entity's identities.
|
||||||
|
* @return The entity's identities.
|
||||||
|
*/
|
||||||
|
const IdentityList& identities() const { return m_identities; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an optional DataForm to Disco:Info replies, e.g. for XEP-0232.
|
||||||
|
* Only one form can be added at this point.
|
||||||
|
* @param form An optional DataForm to include in the Info reply.
|
||||||
|
* The form will be owned by and deleted on destruction of the Disco object.
|
||||||
|
* @note If called more than once the previously set form will be deleted.
|
||||||
|
*/
|
||||||
|
void setForm( DataForm* form );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the DataForm set by setForm(). Used by Capabilities.
|
||||||
|
* @return The DataForm, or 0.
|
||||||
|
*/
|
||||||
|
const DataForm* form() const { return m_form; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register an @ref DiscoHandler with the Disco
|
||||||
|
* object. This is only necessary if you want to receive Disco-set requests. Else
|
||||||
|
* a one-time registration happens when calling getDiscoInfo() and getDiscoItems(), respectively.
|
||||||
|
* @param dh The DiscoHandler-derived object to register.
|
||||||
|
*/
|
||||||
|
void registerDiscoHandler( DiscoHandler* dh )
|
||||||
|
{ m_discoHandlers.push_back( dh ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the given DiscoHandler.
|
||||||
|
* @param dh The DiscoHandler to unregister.
|
||||||
|
*/
|
||||||
|
void removeDiscoHandler( DiscoHandler* dh );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to register a @ref DiscoNodeHandler with the Disco
|
||||||
|
* object. The DiscoNodeHandler will receive disco#items queries which are
|
||||||
|
* directed to the corresponding node registered for the handler.
|
||||||
|
* @param nh The NodeHandler-derived object to register.
|
||||||
|
* @param node The node name to associate with this handler. Use an empty string to
|
||||||
|
* register for the root node.
|
||||||
|
*/
|
||||||
|
void registerNodeHandler( DiscoNodeHandler* nh, const std::string& node );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the node handler for the given node.
|
||||||
|
* @param nh The NodeHandler to unregister.
|
||||||
|
* @param node The node for which the handler shall be removed. Use an empty string to
|
||||||
|
* remove the root node's handler.
|
||||||
|
*/
|
||||||
|
void removeNodeHandler( DiscoNodeHandler* nh, const std::string& node );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all registered nodes of the given node handler.
|
||||||
|
* @param nh The NodeHandler to unregister.
|
||||||
|
*/
|
||||||
|
void removeNodeHandlers( DiscoNodeHandler* nh );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual bool handleIq( const IQ& iq );
|
||||||
|
|
||||||
|
// reimplemented from IqHandler.
|
||||||
|
virtual void handleIqID( const IQ& iq, int context );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef DISCO_TEST
|
||||||
|
public:
|
||||||
|
#endif
|
||||||
|
Disco( ClientBase* parent );
|
||||||
|
virtual ~Disco();
|
||||||
|
|
||||||
|
enum IdType
|
||||||
|
{
|
||||||
|
GetDiscoInfo,
|
||||||
|
GetDiscoItems
|
||||||
|
};
|
||||||
|
|
||||||
|
void getDisco( const JID& to, const std::string& node, DiscoHandler* dh,
|
||||||
|
int context, IdType idType, const std::string& tid );
|
||||||
|
|
||||||
|
struct DiscoHandlerContext
|
||||||
|
{
|
||||||
|
DiscoHandler* dh;
|
||||||
|
int context;
|
||||||
|
};
|
||||||
|
|
||||||
|
ClientBase* m_parent;
|
||||||
|
|
||||||
|
typedef std::list<DiscoHandler*> DiscoHandlerList;
|
||||||
|
typedef std::list<DiscoNodeHandler*> DiscoNodeHandlerList;
|
||||||
|
typedef std::map<std::string, DiscoNodeHandlerList> DiscoNodeHandlerMap;
|
||||||
|
typedef std::map<std::string, DiscoHandlerContext> DiscoHandlerMap;
|
||||||
|
|
||||||
|
DiscoHandlerList m_discoHandlers;
|
||||||
|
DiscoNodeHandlerMap m_nodeHandlers;
|
||||||
|
DiscoHandlerMap m_track;
|
||||||
|
IdentityList m_identities;
|
||||||
|
StringList m_features;
|
||||||
|
StringMap m_queryIDs;
|
||||||
|
DataForm* m_form;
|
||||||
|
|
||||||
|
std::string m_versionName;
|
||||||
|
std::string m_versionVersion;
|
||||||
|
std::string m_versionOs;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCO_H__
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCOHANDLER_H__
|
||||||
|
#define DISCOHANDLER_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "disco.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class IQ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A virtual interface that enables objects to receive Service Discovery (XEP-0030) events.
|
||||||
|
*
|
||||||
|
* A class implementing this interface can receive the results of sent disco queries.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API DiscoHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DiscoHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function if you want to be notified about the result
|
||||||
|
* of an disco#info query.
|
||||||
|
* @param from The sender of the disco#info result.
|
||||||
|
* @param info The Info.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function if you want to be notified about the result
|
||||||
|
* of a disco#items query.
|
||||||
|
* @param from The sender of the disco#items result.
|
||||||
|
* @param items The Items.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive disco error notifications.
|
||||||
|
* @param from The sender of the error result.
|
||||||
|
* @param error The Error. May be 0.
|
||||||
|
* @param context A context identifier.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
virtual void handleDiscoError( const JID& from, const Error* error, int context ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reimplement this function to receive notifications about incoming IQ
|
||||||
|
* stanzas of type 'set' in the disco namespace.
|
||||||
|
* @param iq The full IQ.
|
||||||
|
* @return Returns @b true if the stanza was handled and answered, @b false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool handleDiscoSet( const IQ& iq ) { (void)iq; return false; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCOHANDLER_H__
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DISCONODEHANDLER_H__
|
||||||
|
#define DISCONODEHANDLER_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "disco.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derived classes can be registered as NodeHandlers for certain nodes with the Disco object.
|
||||||
|
*
|
||||||
|
* Incoming disco#info and disco#items queries are delegated to their respective handlers.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
*/
|
||||||
|
class GLOOX_API DiscoNodeHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~DiscoNodeHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In addition to @c handleDiscoNodeIdentities, this function is used to gather
|
||||||
|
* more information on a specific node. It is called when a disco#info query
|
||||||
|
* arrives with a node attribute that matches the one registered for this handler.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of features supported by this node.
|
||||||
|
*/
|
||||||
|
virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In addition to @c handleDiscoNodeFeatures, this function is used to gather
|
||||||
|
* more information on a specific node. It is called when a disco#info query
|
||||||
|
* arrives with a node attribute that matches the one registered for this handler.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of identities for the given node. The caller will own the identities.
|
||||||
|
*/
|
||||||
|
virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from,
|
||||||
|
const std::string& node ) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to gather more information on a specific node.
|
||||||
|
* It is called when a disco#items query arrives with a node attribute that
|
||||||
|
* matches the one registered for this handler. If node is empty, items for the
|
||||||
|
* root node (no node) shall be returned.
|
||||||
|
* @param from The sender of the request.
|
||||||
|
* @param to The receiving JID (useful for transports).
|
||||||
|
* @param node The node this handler is supposed to handle.
|
||||||
|
* @return A list of items supported by this node.
|
||||||
|
*/
|
||||||
|
virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to,
|
||||||
|
const std::string& node = EmptyString ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DISCONODEHANDLER_H__
|
|
@ -0,0 +1,488 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32_WCE
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
# include <resolv.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/un.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
# include <winsock.h>
|
||||||
|
#elif defined( _WIN32_WCE )
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_WINDNS_H
|
||||||
|
# include <windns.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SRV_COST (RRFIXEDSZ+0)
|
||||||
|
#define SRV_WEIGHT (RRFIXEDSZ+2)
|
||||||
|
#define SRV_PORT (RRFIXEDSZ+4)
|
||||||
|
#define SRV_SERVER (RRFIXEDSZ+6)
|
||||||
|
#define SRV_FIXEDSZ (RRFIXEDSZ+6)
|
||||||
|
|
||||||
|
#ifndef T_SRV
|
||||||
|
# define T_SRV 33
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// mingw
|
||||||
|
#ifndef DNS_TYPE_SRV
|
||||||
|
# define DNS_TYPE_SRV 33
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_CMPRSFLGS
|
||||||
|
# define NS_CMPRSFLGS 0xc0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef C_IN
|
||||||
|
# define C_IN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INVALID_SOCKET
|
||||||
|
# define INVALID_SOCKET -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define XMPP_PORT 5222
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY )
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
buffer srvbuf;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
const std::string dname = "_" + service + "._" + proto;
|
||||||
|
|
||||||
|
if( !domain.empty() )
|
||||||
|
srvbuf.len = res_querydomain( dname.c_str(), const_cast<char*>( domain.c_str() ),
|
||||||
|
C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
|
||||||
|
else
|
||||||
|
srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ );
|
||||||
|
|
||||||
|
if( srvbuf.len < 0 )
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
|
||||||
|
HEADER* hdr = (HEADER*)srvbuf.buf;
|
||||||
|
unsigned char* here = srvbuf.buf + NS_HFIXEDSZ;
|
||||||
|
|
||||||
|
if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( hdr->rcode >= 1 && hdr->rcode <= 5 )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( ntohs( hdr->ancount ) == 0 )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
if( ntohs( hdr->ancount ) > NS_PACKETSZ )
|
||||||
|
error = true;
|
||||||
|
|
||||||
|
int cnt;
|
||||||
|
for( cnt = ntohs( hdr->qdcount ); cnt > 0; --cnt )
|
||||||
|
{
|
||||||
|
int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
here += strlen + NS_QFIXEDSZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* srv[NS_PACKETSZ];
|
||||||
|
int srvnum = 0;
|
||||||
|
for( cnt = ntohs( hdr->ancount ); cnt > 0; --cnt )
|
||||||
|
{
|
||||||
|
int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
here += strlen;
|
||||||
|
srv[srvnum++] = here;
|
||||||
|
here += SRV_FIXEDSZ;
|
||||||
|
here += dn_skipname( here, srvbuf.buf + srvbuf.len );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( error )
|
||||||
|
{
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
// (q)sort here
|
||||||
|
|
||||||
|
HostMap servers;
|
||||||
|
for( cnt = 0; cnt < srvnum; ++cnt )
|
||||||
|
{
|
||||||
|
char srvname[NS_MAXDNAME];
|
||||||
|
srvname[0] = '\0';
|
||||||
|
|
||||||
|
if( dn_expand( srvbuf.buf, srvbuf.buf + NS_PACKETSZ,
|
||||||
|
srv[cnt] + SRV_SERVER, srvname, NS_MAXDNAME ) < 0
|
||||||
|
|| !(*srvname) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsigned char* c = srv[cnt] + SRV_PORT;
|
||||||
|
servers.insert( std::make_pair( (char*)srvname, ntohs( c[1] << 8 | c[0] ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !servers.size() )
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined( _WIN32 ) && defined( HAVE_WINDNS_H )
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
const std::string dname = "_" + service + "._" + proto + "." + domain;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
DNS::HostMap servers;
|
||||||
|
DNS_RECORD* pRecord = NULL;
|
||||||
|
DNS_STATUS status = DnsQuery_UTF8( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL );
|
||||||
|
if( status == ERROR_SUCCESS )
|
||||||
|
{
|
||||||
|
DNS_RECORD* pRec = pRecord;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if( pRec->wType == DNS_TYPE_SRV )
|
||||||
|
{
|
||||||
|
servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort;
|
||||||
|
}
|
||||||
|
pRec = pRec->pNext;
|
||||||
|
}
|
||||||
|
while( pRec != NULL );
|
||||||
|
DnsRecordListFree( pRecord, DnsFreeRecordList );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance.warn( LogAreaClassDns, "DnsQuery_UTF8() failed: " + util::int2string( status ) );
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( error || !servers.size() )
|
||||||
|
{
|
||||||
|
servers = defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
DNS::HostMap DNS::resolve( const std::string& /*service*/, const std::string& /*proto*/,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
logInstance.warn( LogAreaClassDns, "Notice: gloox does not support SRV "
|
||||||
|
"records on this platform. Using A records instead." );
|
||||||
|
return defaultHostMap( domain, logInstance );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
HostMap server;
|
||||||
|
|
||||||
|
logInstance.warn( LogAreaClassDns, "Notice: no SRV record found for "
|
||||||
|
+ domain + ", using default port." );
|
||||||
|
|
||||||
|
if( !domain.empty() )
|
||||||
|
server[domain] = XMPP_PORT;
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain );
|
||||||
|
struct addrinfo hints;
|
||||||
|
if( proto == "tcp" )
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
else if( proto == "udp" )
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto );
|
||||||
|
}
|
||||||
|
memset( &hints, '\0', sizeof( hints ) );
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res );
|
||||||
|
if( e )
|
||||||
|
logInstance.err( LogAreaClassDns, "getaddrinfo() failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
struct addrinfo* results = 0;
|
||||||
|
|
||||||
|
resolve( &results, host, logInstance );
|
||||||
|
if( !results )
|
||||||
|
{
|
||||||
|
logInstance.err( LogAreaClassDns, "host not found: " + host );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct addrinfo* runp = results;
|
||||||
|
while( runp )
|
||||||
|
{
|
||||||
|
int fd = DNS::connect( runp, logInstance );
|
||||||
|
if( fd >= 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
runp = runp->ai_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo( results );
|
||||||
|
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( struct addrinfo* res, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
if( !res )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol, logInstance );
|
||||||
|
if( fd < 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 )
|
||||||
|
{
|
||||||
|
char ip[NI_MAXHOST];
|
||||||
|
char port[NI_MAXSERV];
|
||||||
|
|
||||||
|
if( getnameinfo( res->ai_addr, sizeof( sockaddr ),
|
||||||
|
ip, sizeof( ip ),
|
||||||
|
port, sizeof( port ),
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV ) )
|
||||||
|
{
|
||||||
|
//FIXME do we need to handle this? How? Can it actually happen at all?
|
||||||
|
// printf( "could not get numeric hostname");
|
||||||
|
}
|
||||||
|
|
||||||
|
if( res->ai_canonname )
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + std::string( res->ai_canonname )
|
||||||
|
+ " (" + ip + "), port " + port );
|
||||||
|
else
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + ip + ":" + port );
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message = "connect() failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
closeSocket( fd, logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
HostMap hosts = resolve( host, logInstance );
|
||||||
|
if( hosts.size() == 0 )
|
||||||
|
return -ConnDnsError;
|
||||||
|
|
||||||
|
HostMap::const_iterator it = hosts.begin();
|
||||||
|
for( ; it != hosts.end(); ++it )
|
||||||
|
{
|
||||||
|
int fd = DNS::connect( (*it).first, (*it).second, logInstance );
|
||||||
|
if( fd >= 0 )
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int DNS::getSocket( const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
WSADATA wsaData;
|
||||||
|
if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "WSAStartup() failed. WSAGetLastError: "
|
||||||
|
+ util::int2string( ::WSAGetLastError() ) );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int protocol = IPPROTO_TCP;
|
||||||
|
struct protoent* prot;
|
||||||
|
if( ( prot = getprotobyname( "tcp" ) ) != 0 )
|
||||||
|
{
|
||||||
|
protocol = prot->p_proto;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string message = "getprotobyname( \"tcp\" ) failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() )
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
+ ". Falling back to IPPROTO_TCP: " + util::int2string( IPPROTO_TCP );
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
// Do not return an error. We'll fall back to IPPROTO_TCP.
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSocket( PF_INET, SOCK_STREAM, protocol, logInstance );
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::getSocket( int af, int socktype, int proto, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
SOCKET fd;
|
||||||
|
#else
|
||||||
|
int fd;
|
||||||
|
#endif
|
||||||
|
if( ( fd = socket( af, socktype, proto ) ) == INVALID_SOCKET )
|
||||||
|
{
|
||||||
|
std::string message = "getSocket( "
|
||||||
|
+ util::int2string( af ) + ", "
|
||||||
|
+ util::int2string( socktype ) + ", "
|
||||||
|
+ util::int2string( proto )
|
||||||
|
+ " ) failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SETSOCKOPT
|
||||||
|
int timeout = 5000;
|
||||||
|
setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) );
|
||||||
|
setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return (int)fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DNS::connect( const std::string& host, int port, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
int fd = getSocket( logInstance );
|
||||||
|
if( fd < 0 )
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
struct hostent* h;
|
||||||
|
if( ( h = gethostbyname( host.c_str() ) ) == 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "gethostbyname() failed for " + host + "." );
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in target;
|
||||||
|
target.sin_family = AF_INET;
|
||||||
|
target.sin_port = htons( static_cast<unsigned short int>( port ) );
|
||||||
|
|
||||||
|
if( h->h_length != sizeof( struct in_addr ) )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "gethostbyname() returned unexpected structure." );
|
||||||
|
cleanup( logInstance );
|
||||||
|
return -ConnDnsError;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connecting to " + host
|
||||||
|
+ " (" + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
|
||||||
|
|
||||||
|
memset( target.sin_zero, '\0', 8 );
|
||||||
|
if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "Connected to " + host + " ("
|
||||||
|
+ inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" );
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string message = "Connection to " + host + " ("
|
||||||
|
+ inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ") failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
|
||||||
|
closeSocket( fd, logInstance );
|
||||||
|
return -ConnConnectionRefused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNS::closeSocket( int fd, const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
int result = closesocket( fd );
|
||||||
|
#else
|
||||||
|
int result = close( fd );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if( result != 0 )
|
||||||
|
{
|
||||||
|
std::string message = "closeSocket() failed. "
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
"WSAGetLastError: " + util::int2string( ::WSAGetLastError() );
|
||||||
|
#else
|
||||||
|
"errno: " + util::int2string( errno );
|
||||||
|
#endif
|
||||||
|
logInstance.dbg( LogAreaClassDns, message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DNS::cleanup( const LogSink& logInstance )
|
||||||
|
{
|
||||||
|
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
|
||||||
|
if( WSACleanup() != 0 )
|
||||||
|
{
|
||||||
|
logInstance.dbg( LogAreaClassDns, "WSACleanup() failed. WSAGetLastError: "
|
||||||
|
+ util::int2string( ::WSAGetLastError() ) );
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)logInstance;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2005-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DNS_H__
|
||||||
|
#define DNS_H__
|
||||||
|
|
||||||
|
#include "macros.h"
|
||||||
|
#include "logsink.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
# include <windows.h>
|
||||||
|
# include <windns.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ARPA_NAMESER_H
|
||||||
|
# include <arpa/nameser.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <arpa/nameser_compat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_MAXDNAME
|
||||||
|
# define NS_MAXDNAME 1025
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_PACKETSZ
|
||||||
|
# define NS_PACKETSZ 512
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class holds a number of static functions used for DNS related stuff.
|
||||||
|
*
|
||||||
|
* You should not need to use these functions directly.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 0.3
|
||||||
|
*/
|
||||||
|
class GLOOX_API DNS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of strings (used for server addresses) and ints (used for port numbers).
|
||||||
|
*/
|
||||||
|
typedef std::map<std::string, int> HostMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function resolves a service/protocol/domain tuple.
|
||||||
|
* @param service The SRV service type.
|
||||||
|
* @param proto The SRV protocol.
|
||||||
|
* @param domain The domain to search for SRV records.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV
|
||||||
|
* records where found.
|
||||||
|
*/
|
||||||
|
static HostMap resolve( const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience funtion which uses @ref resolve() to resolve SRV records
|
||||||
|
* for a given domain, using a service of @b xmpp-client and a proto of @b tcp.
|
||||||
|
* @param domain The domain to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV
|
||||||
|
* records where found.
|
||||||
|
*/
|
||||||
|
static HostMap resolve( const std::string& domain, const LogSink& logInstance )
|
||||||
|
{ return resolve( "xmpp-client", "tcp", domain, logInstance ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience function which uses @ref resolve() to get a list of hosts
|
||||||
|
* and connects to one of them.
|
||||||
|
* @param host The host to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( const std::string& host, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience function which connects to the given host and port. No SRV
|
||||||
|
* records are resolved. Use this function for special setups.
|
||||||
|
* @param host The host/IP address to connect to.
|
||||||
|
* @param port A custom port to connect to.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( const std::string& host, int port, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience function that prepares and returnes a simple, unconnected TCP socket.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A TCP socket.
|
||||||
|
*/
|
||||||
|
static int getSocket( const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the given socket.
|
||||||
|
* @param fd The socket to close.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void closeSocket( int fd, const LogSink& logInstance );
|
||||||
|
|
||||||
|
private:
|
||||||
|
#ifdef HAVE_GETADDRINFO
|
||||||
|
/**
|
||||||
|
* Resolves the given service for the given domain and protocol, using the IPv6-ready
|
||||||
|
* getaddrinfo(). The result is put into the first parameter.
|
||||||
|
* @param res A pointer to a pointer holding the query results.
|
||||||
|
* @param service A service string to query for, e.g. xmpp-client.
|
||||||
|
* @param proto A protocol name.
|
||||||
|
* @param domain The domain to query for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void resolve( struct addrinfo** res, const std::string& service, const std::string& proto,
|
||||||
|
const std::string& domain, const LogSink& logInstance );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience funtion which uses @ref resolve() to resolve SRV records
|
||||||
|
* for a given domain, using a service of @b xmpp-client and a proto of @b tcp.
|
||||||
|
* @param res A pointer to a pointer holding the query results.
|
||||||
|
* @param domain The domain to resolve SRV records for.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
*/
|
||||||
|
static void resolve( struct addrinfo** res, const std::string& domain, const LogSink& logInstance )
|
||||||
|
{ resolve( res, "xmpp-client", "tcp", domain, logInstance ); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to connect to the host/address contained in the addrinfo structure.
|
||||||
|
* @param res The connection parameters.
|
||||||
|
* @param logInstance A LogSink to use for logging.
|
||||||
|
* @return A file descriptor for the established connection.
|
||||||
|
*/
|
||||||
|
static int connect( struct addrinfo* res, const LogSink& logInstance );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function prepares and returns a socket with the given parameters.
|
||||||
|
* @param af The address family. E.g. PF_INET.
|
||||||
|
* @param socktype The socket type. E.g. SOCK_STREAM.
|
||||||
|
* @param proto The protocol number. E.g. 6 (TCP).
|
||||||
|
*/
|
||||||
|
static int getSocket( int af, int socktype, int proto, const LogSink& logInstance );
|
||||||
|
|
||||||
|
static HostMap defaultHostMap( const std::string& domain, const LogSink& logInstance );
|
||||||
|
static void cleanup( const LogSink& logInstance );
|
||||||
|
|
||||||
|
struct buffer
|
||||||
|
{
|
||||||
|
unsigned char buf[NS_PACKETSZ];
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // DNS_H__
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Error type values */
|
||||||
|
static const char* errValues [] = {
|
||||||
|
"auth",
|
||||||
|
"cancel",
|
||||||
|
"continue",
|
||||||
|
"modify",
|
||||||
|
"wait"
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Stanza error values */
|
||||||
|
static const char* stanzaErrValues [] = {
|
||||||
|
"bad-request",
|
||||||
|
"conflict",
|
||||||
|
"feature-not-implemented",
|
||||||
|
"forbidden",
|
||||||
|
"gone",
|
||||||
|
"internal-server-error",
|
||||||
|
"item-not-found",
|
||||||
|
"jid-malformed",
|
||||||
|
"not-acceptable",
|
||||||
|
"not-allowed",
|
||||||
|
"not-authorized",
|
||||||
|
"not-modified",
|
||||||
|
"payment-required",
|
||||||
|
"recipient-unavailable",
|
||||||
|
"redirect",
|
||||||
|
"registration-required",
|
||||||
|
"remote-server-not-found",
|
||||||
|
"remote-server-timeout",
|
||||||
|
"resource-constraint",
|
||||||
|
"service-unavailable",
|
||||||
|
"subscription-required",
|
||||||
|
"undefined-condition",
|
||||||
|
"unexpected-request",
|
||||||
|
"unknown-sender"
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline StanzaErrorType stanzaErrorType( const std::string& type )
|
||||||
|
{
|
||||||
|
return (StanzaErrorType)util::lookup( type, errValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline StanzaError stanzaError( const std::string& type )
|
||||||
|
{
|
||||||
|
return (StanzaError)util::lookup( type, stanzaErrValues );
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error( const Tag* tag )
|
||||||
|
: StanzaExtension( ExtError ),
|
||||||
|
m_error( StanzaErrorUndefined ), m_appError( 0 )
|
||||||
|
{
|
||||||
|
if( !tag || tag->name() != "error" )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_type = stanzaErrorType( tag->findAttribute( TYPE ) );
|
||||||
|
|
||||||
|
TagList::const_iterator it = tag->children().begin();
|
||||||
|
for( ; it != tag->children().end(); ++it )
|
||||||
|
{
|
||||||
|
StanzaError srt = gloox::stanzaError( (*it)->name() );
|
||||||
|
if( srt != StanzaErrorUndefined )
|
||||||
|
m_error = srt;
|
||||||
|
else if( (*it)->name() == "text" )
|
||||||
|
m_text[(*it)->findAttribute("xml:lang")] = (*it)->cdata();
|
||||||
|
else
|
||||||
|
m_appError = (*it)->clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Error( const Error& error )
|
||||||
|
: StanzaExtension( ExtError ), m_type( error.m_type ),
|
||||||
|
m_error( error.m_error ), m_appError( error.m_appError ? m_appError->clone() : 0 )
|
||||||
|
{}
|
||||||
|
|
||||||
|
Error::~Error()
|
||||||
|
{
|
||||||
|
delete m_appError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Error::filterString() const
|
||||||
|
{
|
||||||
|
static const std::string filter = "/iq/error"
|
||||||
|
"|/message/error"
|
||||||
|
"|/presence/error"
|
||||||
|
"|/subscription/error";
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Tag* Error::tag() const
|
||||||
|
{
|
||||||
|
if( m_type == StanzaErrorTypeUndefined || m_error == StanzaErrorUndefined )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Tag* error = new Tag( "error", TYPE, util::lookup( m_type, errValues ) );
|
||||||
|
new Tag( error, util::lookup( m_error, stanzaErrValues ), XMLNS, XMLNS_XMPP_STANZAS );
|
||||||
|
|
||||||
|
StringMap::const_iterator it = m_text.begin();
|
||||||
|
for( ; it != m_text.end(); ++it )
|
||||||
|
{
|
||||||
|
Tag* txt = new Tag( error, "text" );
|
||||||
|
txt->setXmlns( XMLNS_XMPP_STANZAS );
|
||||||
|
txt->addAttribute( "xml:lang", (*it).first );
|
||||||
|
txt->setCData( (*it).second );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_appError )
|
||||||
|
error->addChild( m_appError->clone() );
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Error::text( const std::string& lang ) const
|
||||||
|
{
|
||||||
|
StringMap::const_iterator it = m_text.find( lang );
|
||||||
|
return it != m_text.end() ? (*it).second : EmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2007-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ERROR_H__
|
||||||
|
#define ERROR_H__
|
||||||
|
|
||||||
|
#include "gloox.h"
|
||||||
|
#include "stanzaextension.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A stanza error abstraction implemented as a StanzaExtension.
|
||||||
|
*
|
||||||
|
* @author Vincent Thomasset
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class GLOOX_API Error : public StanzaExtension
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Error()
|
||||||
|
// : StanzaExtension( ExtError ), m_type( StanzaErrorTypeUndefined ),
|
||||||
|
// m_error( StanzaErrorUndefined ), m_appError( 0 )
|
||||||
|
// {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Error object from the given Tag.
|
||||||
|
* @param tag The Tag to parse.
|
||||||
|
*/
|
||||||
|
Error( const Tag* tag = 0 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Error object.
|
||||||
|
* @param type The error type.
|
||||||
|
* @param error The actual stanza error.
|
||||||
|
* @param appError An optional application-specific error.
|
||||||
|
*/
|
||||||
|
Error( StanzaErrorType type, StanzaError error, Tag* appError = 0 )
|
||||||
|
: StanzaExtension( ExtError ), m_type( type ),
|
||||||
|
m_error( error ), m_appError( appError )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Error();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error type.
|
||||||
|
* @return The error type.
|
||||||
|
*/
|
||||||
|
StanzaErrorType type() const { return m_type; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the stanza error.
|
||||||
|
* @return The actual error.
|
||||||
|
*/
|
||||||
|
StanzaError error() const { return m_error; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function can be used to retrieve the application-specific error
|
||||||
|
* condition of a stanza error.
|
||||||
|
* @return The application-specific error element of a stanza error.
|
||||||
|
* 0 if no respective element was found or no error occured.
|
||||||
|
*/
|
||||||
|
const Tag* appError() const { return m_appError; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text of a error stanza for the given language if available.
|
||||||
|
* If the requested language is not available, the default text (without
|
||||||
|
* a xml:lang attribute) will be returned.
|
||||||
|
* @param lang The language identifier for the desired language. It must
|
||||||
|
* conform to section 2.12 of the XML specification and RFC 3066. If
|
||||||
|
* empty, the default text will be returned, if any.
|
||||||
|
* @return The text of an error stanza.
|
||||||
|
*/
|
||||||
|
const std::string& text( const std::string& lang = EmptyString ) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text of a error stanza for the given language.
|
||||||
|
* @param text The error text to set.
|
||||||
|
* @param lang The language identifier for the desired language. It must
|
||||||
|
* conform to section 2.12 of the XML specification and RFC 3066. If
|
||||||
|
* empty, the default text will be set.
|
||||||
|
*/
|
||||||
|
void setText( const std::string& text, const std::string& lang = EmptyString )
|
||||||
|
{
|
||||||
|
m_text[lang] = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual const std::string& filterString() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* newInstance( const Tag* tag ) const
|
||||||
|
{
|
||||||
|
return new Error( tag );
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual Tag* tag() const;
|
||||||
|
|
||||||
|
// reimplemented from StanzaExtension
|
||||||
|
virtual StanzaExtension* clone() const
|
||||||
|
{
|
||||||
|
return new Error( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Error( const Error& error );
|
||||||
|
|
||||||
|
void setValues( const Tag* tag );
|
||||||
|
|
||||||
|
StanzaErrorType m_type;
|
||||||
|
StanzaError m_error;
|
||||||
|
Tag* m_appError;
|
||||||
|
StringMap m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ERROR_H__ */
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENT_H__
|
||||||
|
#define EVENT_H__
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Stanza;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A base class for events.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Event types.
|
||||||
|
*/
|
||||||
|
enum EventType
|
||||||
|
{
|
||||||
|
PingPing, /**< Incoming Ping (XEP-0199). */
|
||||||
|
PingPong, /**< Incoming Pong (XEP-0199). */
|
||||||
|
PingError /**< Incoming Error Pong (XEP-0199). */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Event of the given type.
|
||||||
|
* @param type The Event type.
|
||||||
|
*/
|
||||||
|
Event( EventType type ) : m_eventType( type ), m_stanza( 0 ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Event of the given type, referencing the given Stanza.
|
||||||
|
* @param type The Event type.
|
||||||
|
* @param stanza A Stanza to point at. No copy of the Stanza is taken, just its address.
|
||||||
|
*/
|
||||||
|
Event( EventType type, const Stanza& stanza ) : m_eventType( type ), m_stanza( &stanza ) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~Event() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Event's type.
|
||||||
|
* @return The Event's type.
|
||||||
|
*/
|
||||||
|
EventType eventType() const { return m_eventType; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a pointer to a Stanza-derived object.
|
||||||
|
* @return A pointer to a Stanza that caused the event. May be 0.
|
||||||
|
* @note You should @b not delete the Stanza object.
|
||||||
|
*/
|
||||||
|
const Stanza* stanza() const { return m_stanza; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EventType m_eventType;
|
||||||
|
const Stanza* m_stanza;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENT_H__
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "eventdispatcher.h"
|
||||||
|
#include "eventhandler.h"
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
EventDispatcher::EventDispatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
EventDispatcher::~EventDispatcher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::dispatch( const Event& event, const std::string& context, bool remove )
|
||||||
|
{
|
||||||
|
typedef ContextHandlerMap::iterator Ei;
|
||||||
|
std::pair<Ei, Ei> g = m_contextHandlers.equal_range( context );
|
||||||
|
Ei it = g.first;
|
||||||
|
Ei it2;
|
||||||
|
while( it != g.second )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
(*it2).second->handleEvent( event );
|
||||||
|
if( remove )
|
||||||
|
m_contextHandlers.erase( it2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::dispatch( const Event& event )
|
||||||
|
{
|
||||||
|
TypeHandlerMap::iterator it = m_typeHandlers.begin();
|
||||||
|
for( ; it != m_typeHandlers.end(); ++it )
|
||||||
|
{
|
||||||
|
if( (*it).first == event.eventType() )
|
||||||
|
(*it).second->handleEvent( event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::registerEventHandler( EventHandler* eh, const std::string& context )
|
||||||
|
{
|
||||||
|
if( !eh || context.empty() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_contextHandlers.insert( std::make_pair( context, eh ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventDispatcher::removeEventHandler( EventHandler* eh )
|
||||||
|
{
|
||||||
|
ContextHandlerMap::iterator it = m_contextHandlers.begin();
|
||||||
|
ContextHandlerMap::iterator it2;
|
||||||
|
while( it != m_contextHandlers.end() )
|
||||||
|
{
|
||||||
|
it2 = it++;
|
||||||
|
if( (*it2).second == eh )
|
||||||
|
m_contextHandlers.erase( it2 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENTDISPATCHER_H__
|
||||||
|
#define EVENTDISPATCHER_H__
|
||||||
|
|
||||||
|
#include "event.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class EventHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An Event dispatcher.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class EventDispatcher
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates a new EventDispatcher object. You should not need to use this class directly.
|
||||||
|
*/
|
||||||
|
EventDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~EventDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for handlers for the given Event, and removes the handlers if requested.
|
||||||
|
* @param event The Event to dispatch.
|
||||||
|
* @param context An identifier that limits the EventHandlers that will get notified to
|
||||||
|
* those that are specifically interested in this context.
|
||||||
|
* @param remove Whether or not to remove the context from the list of known contexts. Useful for
|
||||||
|
* IQ IDs.
|
||||||
|
*/
|
||||||
|
void dispatch( const Event& event, const std::string& context, bool remove );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for handlers for the given Event, identified by its type.
|
||||||
|
* @param event The event to dispatch.
|
||||||
|
*/
|
||||||
|
void dispatch( const Event& event );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given EventHandler to be notified about Events with the given context.
|
||||||
|
* The context will usually be an IQ ID.
|
||||||
|
* @param eh The EventHandler to register.
|
||||||
|
* @param context The context to register the EventHandler for.
|
||||||
|
*/
|
||||||
|
void registerEventHandler( EventHandler* eh, const std::string& context );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given EventHandler.
|
||||||
|
* @param eh The EventHandler to remove.
|
||||||
|
*/
|
||||||
|
void removeEventHandler( EventHandler* eh );
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::multimap<const std::string, EventHandler*> ContextHandlerMap;
|
||||||
|
typedef std::multimap<Event::EventType, EventHandler*> TypeHandlerMap;
|
||||||
|
|
||||||
|
ContextHandlerMap m_contextHandlers;
|
||||||
|
TypeHandlerMap m_typeHandlers;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENTDISPATCHER_H__
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2008-2009 by Jakob Schroeter <js@camaya.net>
|
||||||
|
This file is part of the gloox library. http://camaya.net/gloox
|
||||||
|
|
||||||
|
This software is distributed under a license. The full license
|
||||||
|
agreement can be found in the file LICENSE in this distribution.
|
||||||
|
This software may not be copied, modified, sold or distributed
|
||||||
|
other than expressed in the named license agreement.
|
||||||
|
|
||||||
|
This software is distributed without any warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENTHANDLER_H__
|
||||||
|
#define EVENTHANDLER_H__
|
||||||
|
|
||||||
|
namespace gloox
|
||||||
|
{
|
||||||
|
|
||||||
|
class Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An base class for event handlers.
|
||||||
|
*
|
||||||
|
* @author Jakob Schroeter <js@camaya.net>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class EventHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Virtual Destructor.
|
||||||
|
*/
|
||||||
|
virtual ~EventHandler() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function gets called for Events this handler was registered for.
|
||||||
|
* @param event The Event.
|
||||||
|
*/
|
||||||
|
virtual void handleEvent( const Event& event ) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // EVENTHANDLER_H__
|
Some files were not shown because too many files have changed in this diff Show More
Ŝarĝante…
Reference in New Issue