Import Jabber GPL'ed protocol (beware, it crashes but it's too late to find out why).

This commit is contained in:
plfiorini 2010-05-28 01:04:31 +00:00
commit 0706699184
268 changed files with 49140 additions and 0 deletions

7
Jamfile Normal file
View File

@ -0,0 +1,7 @@
SubDir TOP ;
# Include all the components
SubInclude TOP libs ;
SubInclude TOP protocols ;
UninstallTarget $(CAYA_DIRECTORY) ;

71
Jamrules Normal file
View File

@ -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 ] ;

113
build/jam/BuildSettings Normal file
View File

@ -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 ;

106
build/jam/CheckRules Normal file
View File

@ -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) ;
}

146
build/jam/ConfigRules Normal file
View File

@ -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
;

35
build/jam/DistroRules Normal file
View File

@ -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) ;
}

31
build/jam/FileRules Normal file
View File

@ -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)"
}

52
build/jam/HelperRules Normal file
View File

@ -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) ;
}

27
build/jam/InstallRules Normal file
View File

@ -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 : $(<) ;
}

380
build/jam/MainBuildRules Normal file
View File

@ -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)
}

View File

@ -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 ;

247
build/jam/PackageRules Normal file
View File

@ -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)" ;
}

View File

@ -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 ;

145
configure vendored Executable file
View File

@ -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."

4
libs/Jamfile Normal file
View File

@ -0,0 +1,4 @@
SubDir TOP libs ;
# Include all the components.
SubInclude TOP libs libgloox ;

106
libs/libgloox/Jamfile Normal file
View File

@ -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
;

472
libs/libgloox/adhoc.cpp Normal file
View File

@ -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 );
}
}

487
libs/libgloox/adhoc.h Normal file
View File

@ -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 &lt;command&gt; 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__

View File

@ -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__

View File

@ -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__

189
libs/libgloox/amp.cpp Normal file
View File

@ -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;
}
}

243
libs/libgloox/amp.h Normal file
View File

@ -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__

View File

@ -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*/ )
{
}
}

147
libs/libgloox/annotations.h Normal file
View File

@ -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__

View File

@ -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__

View File

@ -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;
}
}

70
libs/libgloox/attention.h Normal file
View File

@ -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__

126
libs/libgloox/base64.cpp Normal file
View File

@ -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;
}
}
}

51
libs/libgloox/base64.h Normal file
View File

@ -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__

View File

@ -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__

View File

@ -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*/ )
{
}
}

View File

@ -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__

180
libs/libgloox/bytestream.h Normal file
View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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();
}
}

View File

@ -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__

View File

@ -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 );
}
}

87
libs/libgloox/chatstate.h Normal file
View File

@ -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__

View File

@ -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 ) );
}
}

View File

@ -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__

View File

@ -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__

603
libs/libgloox/client.cpp Normal file
View File

@ -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;
}
}

441
libs/libgloox/client.h Normal file
View File

@ -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__

1516
libs/libgloox/clientbase.cpp Normal file

File diff suppressed because it is too large Load Diff

1030
libs/libgloox/clientbase.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;
}
}

77
libs/libgloox/component.h Normal file
View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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();
}
}

View File

@ -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__

View File

@ -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

View File

@ -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__

27
libs/libgloox/config.h Normal file
View File

@ -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__

123
libs/libgloox/config.h.unix Normal file
View File

@ -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

View File

@ -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__

View File

@ -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;
}
}
}

View File

@ -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__

View File

@ -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__

View File

@ -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__

View File

@ -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 );
}
}

View File

@ -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__

View File

@ -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__

View File

@ -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 );
}
}

View File

@ -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__

View File

@ -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 );
}
}
}

View File

@ -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__

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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 );
}
}
}

View File

@ -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__

View File

@ -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 );
}
}

View File

@ -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__

137
libs/libgloox/dataform.cpp Normal file
View File

@ -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;
}
}

197
libs/libgloox/dataform.h Normal file
View File

@ -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__

View File

@ -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;
}
}

View File

@ -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 &lt;value/&gt; child SHOULD NOT contain
* newlines (the \\n and \\r characters); instead an application SHOULD
* generate multiple fixed fields, each with one &lt;value/&gt; 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 &lt;reported&gt; or &lt;item&gt;
* 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__

View File

@ -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;
}
}

View File

@ -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__

View File

@ -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;
}
}

View File

@ -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 &lt;item&gt; 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__

View File

@ -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;
}
}

View File

@ -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 &lt;reported&gt; 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__

View File

@ -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;
}
}

View File

@ -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__

535
libs/libgloox/disco.cpp Normal file
View File

@ -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;
}
}

636
libs/libgloox/disco.h Normal file
View File

@ -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 &lt;query&gt; 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 &lt;query&gt; 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__

View File

@ -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__

View File

@ -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__

488
libs/libgloox/dns.cpp Normal file
View File

@ -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
}
}

179
libs/libgloox/dns.h Normal file
View File

@ -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__

138
libs/libgloox/error.cpp Normal file
View File

@ -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;
}
}

139
libs/libgloox/error.h Normal file
View File

@ -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__ */

81
libs/libgloox/event.h Normal file
View File

@ -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__

View File

@ -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 );
}
}
}

View File

@ -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__

View File

@ -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