Changeset - 20d8dd690e21
[Not reviewed]
1 72 0
Vitaly Takmazov - 10 years ago 2015-10-15 14:41:16
vitalyster@gmail.com
fix tests (TODO filetransfer)
73 files changed with 18140 insertions and 18267 deletions:
src/user.cpp
508
508
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
project(libtransport)
 
 
message(STATUS "Variables to override default places where to find libraries:")
 
message(STATUS "|- cppunit : -DCPPUNIT_INCLUDE_DIR,  -DCPPUNIT_LIBRARY")
 
message(STATUS "|- swiften : -DSWIFTEN_INCLUDE_DIR,  -DSWIFTEN_LIBRARY")
 
message(STATUS "  |- zlib  :                         -DZLIB_LIBRARY")
 
message(STATUS "  |- expat :                         -DEXPAT_LIBRARY")
 
message(STATUS "  |-libidn :                         -DLIBIDN_LIBRARY")
 
message(STATUS "  |-libxml :                         -DLIBXML_LIBRARY")
 
message(STATUS "|- boost   : -DBOOST_INCLUDEDIR,     -DBOOST_LIBRARYDIR")
 
message(STATUS "|- protobuf: -DPROTOBUF_INCLUDE_DIR, -DPROTOBUF_LIBRARY")
 
message(STATUS "           : -DPROTOBUF_PROTOC_EXECUTABLE")
 
message(STATUS "|- log4cxx : -DLOG4CXX_INCLUDE_DIR,  -DLOG4CXX_LIBRARY")
 
message(STATUS "|- purple  : -DPURPLE_INCLUDE_DIR,   -DPURPLE_LIBRARY")
 
message(STATUS "           : -DPURPLE_NOT_RUNTIME - enables compilation with libpurple.lib")
 
 
option(ENABLE_SQLITE3 "Build with SQLite3 support" ON)
 
option(ENABLE_MYSQL "Build with MySQL support" ON)
 
option(ENABLE_PQXX "Build with Postgres supoort" ON)
 
 
option(ENABLE_FROTZ "Build Frotz plugin" ON)
 
option(ENABLE_IRC "Build IRC plugin" ON)
 
option(ENABLE_PURPLE "Build Libpurple plugin" ON)
 
option(ENABLE_SMSTOOLS3 "Build SMSTools3 plugin" ON)
 
option(ENABLE_SKYPE "Build Skype plugin" ON)
 
option(ENABLE_SWIFTEN "Build Swiften plugin" ON)
 
option(ENABLE_TWITTER "Build Twitter plugin" ON)
 
option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON)
 
 
option(ENABLE_DOCS "Build Docs" ON)
 
# option(ENABLE_LOG "Build with logging using Log4cxx" ON)
 
option(ENABLE_TESTS "Build Tests using CppUnit" OFF)
 
 
MACRO(LIST_CONTAINS var value)
 
	SET(${var})
 
	FOREACH (value2 ${ARGN})
 
		IF (${value} STREQUAL ${value2})
 
		SET(${var} TRUE)
 
		ENDIF (${value} STREQUAL ${value2})
 
	ENDFOREACH (value2)
 
ENDMACRO(LIST_CONTAINS)
 
 
if(NOT LIB_INSTALL_DIR)
 
	set(LIB_INSTALL_DIR "lib")
 
endif()
 
 
set(CMAKE_MODULE_PATH "cmake_modules")
 
 
###### Prerequisites ######
 
 
 
# FIND SWIFTEN
 
 
set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Swiften)
 
 
if(NOT SWIFTEN_FOUND)
 
	if (ZLIB_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${ZLIB_LIBRARY})
 
	endif()
 
	if (EXPAT_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${EXPAT_LIBRARY})
 
	endif()
 
	if (LIBIDN_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBIDN_LIBRARY})
 
	endif()
 
	if (LIBXML_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBXML_LIBRARY})
 
	endif()
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Dnsapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Crypt32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Secur32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Iphlpapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Winscard")
 
	message(STATUS "Using swiften: ${SWIFTEN_INCLUDE_DIR} ${SWIFTEN_LIBRARY}")
 
endif()
 
 
# FIND BOOST
 
if (WIN32)
 
	set(Boost_USE_STATIC_LIBS      ON)
 
	set(Boost_USE_MULTITHREADED      ON)
 
	set(Boost_USE_STATIC_RUNTIME    OFF)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
else(WIN32)
 
	LIST_CONTAINS(contains -lboost_program_options ${SWIFTEN_LIBRARY})
 
	if(contains)
 
		message(STATUS "Using non-multithreaded boost")
 
		set(Boost_USE_MULTITHREADED 0)
 
	endif(contains)
 
	set(Boost_FIND_QUIETLY ON)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals)
 
	if (NOT Boost_FOUND)
 
		set(Boost_FIND_QUIETLY OFF)
 
		find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
	endif()
 
endif(WIN32)
 
 
message( STATUS "Found Boost: ${Boost_VERSION}, ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}")
 
 
if (${Boost_VERSION} GREATER 104999)
 
	message( STATUS "Using BOOST_FILESYSTEM_VERSION=3")
 
	add_definitions(-DBOOST_FILESYSTEM_VERSION=3)
 
endif()
 
 
# FIND POPT
 
if (NOT WIN32)
 
	set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(popt REQUIRED)
 
endif()
 
 
###### Database ######
 
 
# FIND SQLITE3
 
if (ENABLE_SQLITE3)
 
	if (MSVC)
 
		set(SQLITE3_FOUND 1)
 
		ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps)
 
	else()
 
		if (WIN32)
 
			ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3)
 
		else()
 
			set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
			find_package(sqlite3)
 
		endif()
 
	endif()
 
endif()
 
 
# FIND MYSQL
 
if(ENABLE_MYSQL)
 
	set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(mysql)
 
endif()
 
 
# FIND PQXX
 
if(ENABLE_PQXX)
 
	set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(pqxx)
 
endif()
 
 
###### Plugins ######
 
 
# FIND LIBPURPLE
 
if(ENABLE_PURPLE)
 
	set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(purple)
 
 
	if (WIN32)
 
		if (PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=0)
 
		else(PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=1)
 
		endif(PURPLE_NOT_RUNTIME)
 
	else()
 
		add_definitions(-DPURPLE_RUNTIME=0)
 
	endif()
 
 
	# FIND LIBEVENT
 
	set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(event)
 
endif()
 
 
# FIND GLIB
 
if(ENABLE_SKYPE OR ENABLE_PURPLE)
 
#	if (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
 
#		set(GLIB2_FOUND TRUE)
 
#	else()
 
		set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
		find_package(glib)
 
#	endif()
 
endif()
 
 
# FIND LIBXML2
 
# set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# find_package(libxml2)
 
 
# FIND PROTOBUF
 
set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Protobuf REQUIRED)
 
 
if (NOT PROTOBUF_FOUND AND PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY)
 
	set(PROTOBUF_FOUND 1)
 
	set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR})
 
	if (PROTOBUF_PROTOC_EXECUTABLE)
 
	else()
 
		set(PROTOBUF_PROTOC_EXECUTABLE protoc)
 
	endif()
 
	message(STATUS "Using protobuf: ${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_LIBRARY}")
 
endif()
 
 
if (WIN32)
 
	add_definitions(-DSWIFTEN_STATIC=1)
 
	ADD_DEFINITIONS(-D_UNICODE)
 
	ADD_DEFINITIONS(-DUNICODE)
 
endif()
 
 
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(openssl)
 
endif()
 
 
if(ENABLE_IRC)
 
	set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(Communi)
 
 
	INCLUDE(FindQt4)
 
	FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork)
 
	# ADD_DEFINITIONS(${SWIFTEN_CFLAGS})
 
	ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS)
 
	# ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2)
 
endif()
 
 
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(event)
 
 
if (NOT WIN32 AND ENABLE_SKYPE)
 
set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(dbus)
 
endif()
 
 
# if(ENABLE_YAHOO2)
 
# 	set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# 	find_package(yahoo2)
 
# endif()
 
 
####### Miscallanous ######
 
 
if(ENABLE_DOCS)
 
	find_package(Doxygen)
 
endif()
 
 
# if(ENABLE_LOG)
 
	if(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARY)
 
		set(LOG4CXX_LIBRARIES ${LOG4CXX_LIBRARY})
 
		set(LOG4CXX_FOUND 1)
 
		message(STATUS "Using log4cxx: ${CPPUNIT_INCLUDE_DIR} ${LOG4CXX_INCLUDE_DIR}")
 
	else()
 
		set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
		find_package(log4cxx)
 
	endif()
 
# endif()
 
 
# FIND CPPUNIT
 
if(ENABLE_TESTS)
 
	set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(cppunit)
 
 
	if(NOT CPPUNIT_FOUND AND CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY)
 
		set(CCPUNIT_LIBRARIES ${CPPUNIT_LIBRARY})
 
		set(CPPUNIT_FOUND 1)
 
		message(STATUS "Using cppunit: ${CPPUNIT_INCLUDE_DIR} ${CPPUNIT_LIBRARIES}")
 
	endif()
 
endif()
 
 
if (APPLE)
 
        FIND_LIBRARY(IOKIT_FRAMEWORK IOKit)
 
        FIND_LIBRARY(SECURITY_FRAMEWORK Security)
 
        FIND_LIBRARY(APPKIT_FRAMEWORK AppKit)
 
        FIND_LIBRARY(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration)
 
        FIND_LIBRARY(SECURITYINTERFACE_FRAMEWORK SecurityInterface)
 
        MARK_AS_ADVANCED(IOKIT_FRAMEWORK APPKIT_FRAMEWORK SYSTEMCONFIGURATION_FRAMEWORK SECURITY_FRAMEWORK SECURITYINTERFACE_FRAMEWORK)
 
        SET (APPLE_FRAMEWORKS ${IOKIT_FRAMEWORK} ${APPKIT_FRAMEWORK} ${SYSTEMCONFIGURATION_FRAMEWORK} ${SECURITY_FRAMEWORK} ${SECURITYINTERFACE_FRAMEWORK})
 
endif()
 
 
message("  Supported features")
 
message("-----------------------")
 
 
if (SPECTRUM_VERSION)
 
	ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
else (SPECTRUM_VERSION)
 
	if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		if (NOT GIT_EXECUTABLE)
 
		set (GIT_EXECUTABLE git)
 
		endif()
 
		execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD
 
						OUTPUT_VARIABLE GIT_REVISION
 
						OUTPUT_STRIP_TRAILING_WHITESPACE
 
		)
 
		set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION})
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		set(SPECTRUM_VERSION 2.0.0-alpha)
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
endif (SPECTRUM_VERSION)
 
 
message("Version           : " ${SPECTRUM_VERSION})
 
 
if (SQLITE3_FOUND)
 
	ADD_DEFINITIONS(-DWITH_SQLITE)
 
	include_directories(${SQLITE3_INCLUDE_DIR})
 
	message("SQLite3           : yes")
 
else (SQLITE3_FOUND)
 
	if (WIN32)
 
		ADD_DEFINITIONS(-DWITH_SQLITE)
 
		include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
 
		message("SQLite3           : bundled")
 
	else()
 
		set(SQLITE3_LIBRARIES "")
 
		if(ENABLE_SQLITE3)
 
			message("SQLite3           : no (install sqlite3)")
 
		else(ENABLE_SQLITE3)
 
			message("SQLite3           : no (user disabled)")
 
		endif()
 
	endif()
 
endif (SQLITE3_FOUND)
 
 
if (MYSQL_FOUND)
 
	ADD_DEFINITIONS(-DWITH_MYSQL)
 
	include_directories(${MYSQL_INCLUDE_DIR})
 
	message("MySQL             : yes")
 
else (MYSQL_FOUND)
 
	set(MYSQL_LIBRARIES "")
 
	if(ENABLE_MYSQL)
 
		message("MySQL             : no (install mysql-devel)")
 
	else(ENABLE_MYSQL)
 
		message("MySQL             : no (user disabled)")
 
	endif()
 
endif (MYSQL_FOUND)
 
 
if (PQXX_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PQXX)
 
	include_directories(${PQXX_INCLUDE_DIR})
 
	message("PostgreSQL        : yes")
 
else (PQXX_FOUND)
 
	set(PQXX_LIBRARY "")
 
	set(PQ_LIBRARY "")
 
	if(ENABLE_PQXX)
 
		message("PostgreSQL        : no (install libpqxx-devel)")
 
	else(ENABLE_PQXX)
 
		message("PostgreSQL        : no (user disabled)")
 
	endif()
 
endif (PQXX_FOUND)
 
 
if (PROTOBUF_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PROTOBUF)
 
	include_directories(${PROTOBUF_INCLUDE_DIRS})
 
	message("Network plugins   : yes")
 
 
	if(PURPLE_FOUND)
 
		message("Libpurple plugin  : yes")
 
		include_directories(${PURPLE_INCLUDE_DIR})
 
		include_directories(${GLIB2_INCLUDE_DIR})
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (install libpurple)")
 
		else(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (user disabled)")
 
		endif()
 
	endif()
 
 
	if (HAVE_EVENT)
 
		ADD_DEFINITIONS(-DWITH_LIBEVENT)
 
		include_directories(${EVENT_INCLUDE_DIRS})
 
		message("  libev eventloop : yes")
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("  libev eventloop : no (install libev-devel)")
 
		endif()
 
	endif()
 
 
	if(IRC_FOUND)
 
		ADD_DEFINITIONS(-DCOMMUNI_SHARED)
 
		message("IRC plugin        : yes")
 
		include_directories(${QT_QTNETWORK_INCLUDE_DIR})
 
		include_directories(${IRC_INCLUDE_DIR})
 
		include(${QT_USE_FILE})
 
	else()
 
		if(ENABLE_IRC)
 
			message("IRC plugin        : no (install libCommuni and libprotobuf-dev)")
 
		else(ENABLE_IRC)
 
			message("IRC plugin        : no (user disabled)")
 
		endif()
 
	endif()
 
	if(ENABLE_TWITTER)
 
		message("Twitter plugin    : yes")
 
	else(ENABLE_TWITTER)
 
		message("Twitter plugin    : no (user disabled)")
 
	endif()
 
	if (NOT WIN32)
 
		if(ENABLE_FROTZ)
 
			message("Frotz plugin      : yes")
 
		else()
 
			message("Frotz plugin      : no (user disabled)")
 
		endif()
 
		if(ENABLE_SMSTOOLS3)
 
			message("SMSTools3 plugin  : yes")
 
		else()
 
			message("SMSTools3 plugin  : no (user disabled)")
 
		endif()
 
		if(${LIBDBUSGLIB_FOUND})
 
			message("Skype plugin      : yes")
 
			include_directories(${LIBDBUSGLIB_INCLUDE_DIRS})
 
		else()
 
			if(ENABLE_SKYPE)
 
				message("Skype plugin      : no (install dbus-glib-devel)")
 
			else(ENABLE_SKYPE)
 
				message("Skype plugin      : no (user disabled)")
 
			endif()
 
		endif()
 
	else()
 
		message("Frotz plugin      : no (does not run on Win32)")
 
		message("SMSTools3 plugin  : no (does not run on Win32)")
 
		message("Skype plugin      : no (does not run on Win32)")
 
	endif()
 
 
#  	if(YAHOO2_FOUND)
 
# 		message("Libyahoo2 plugin  : yes")
 
#  		include_directories(${YAHOO2_INCLUDE_DIR})
 
# 	else()
 
		if(ENABLE_YAHOO2)
 
			set(YAHOO2_FOUND 1)
 
			message("Libyahoo2 plugin  : yes")
 
		else(ENABLE_YAHOO2)
 
			message("Libyahoo2 plugin  : no (user disabled)")
 
		endif()
 
#  	endif()
 
 
	if(ENABLE_SWIFTEN)
 
		message("Swiften plugin    : yes")
 
	else()
 
		message("Swiften plugin    : no (user disabled)")
 
	endif()
 
else()
 
	message("Network plugins   : no (install libprotobuf-dev)")
 
	message("Libpurple plugin  : no (install libpurple and libprotobuf-dev)")
 
	message("IRC plugin        : no (install libircclient-qt and libprotobuf-dev)")
 
	message("Frotz plugin      : no (install libprotobuf-dev)")
 
	message("SMSTools3 plugin  : no (install libprotobuf-dev)")
 
	message("Swiften plugin    : no (install libprotobuf-dev)")
 
    message("Twitter plugin    : no (install libprotobuf-dev)")
 
endif()
 
 
if (LOG4CXX_FOUND)
 
	message("Log4cxx           : yes")
 
	include_directories(${LOG4CXX_INCLUDE_DIR})
 
	ADD_DEFINITIONS(-DWITH_LOG4CXX)
 
else()
 
	set(LOG4CXX_LIBRARIES "")
 
	if (WIN32)
 
		message("Log4cxx           : no (install log4cxx-devel)")
 
	else()
 
		message(FATAL_ERROR "Log4cxx           : no (install log4cxx-devel)")
 
	endif()
 
endif()
 
 
if (WIN32)
 
	ADD_DEFINITIONS(-DLOG4CXX_STATIC)
 
	ADD_DEFINITIONS(-D_WIN32_WINNT=0x501)
 
	ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
 
	ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H)
 
	ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB)
 
endif()
 
 
if(CMAKE_BUILD_TYPE MATCHES Debug)
 
	if (CMAKE_COMPILER_IS_GNUCXX)
 
		ADD_DEFINITIONS(-O0)
 
		ADD_DEFINITIONS(-ggdb)
 
	endif()
 
	ADD_DEFINITIONS(-DDEBUG)
 
	message("Debug             : yes")
 
else(CMAKE_BUILD_TYPE MATCHES Debug)
 
	message("Debug             : no (run \"cmake . -DCMAKE_BUILD_TYPE=Debug\")")
 
endif(CMAKE_BUILD_TYPE MATCHES Debug)
 
 
 
SET(TRANSPORT_VERSION 2.0)
 
SET(PROJECT_VERSION 2.0)
 
include_directories(include)
 
 
 
include_directories(${EVENT_INCLUDE_DIRS})
 
include_directories(${SWIFTEN_INCLUDE_DIR})
 
include_directories(${Boost_INCLUDE_DIRS})
 
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
include_directories(${OPENSSL_INCLUDE_DIR})
 
endif()
 
 
ADD_SUBDIRECTORY(src)
 
ADD_SUBDIRECTORY(plugin)
 
ADD_SUBDIRECTORY(include)
 
ADD_SUBDIRECTORY(spectrum)
 
ADD_SUBDIRECTORY(backends)
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(spectrum_manager)
 
#	ADD_SUBDIRECTORY(spectrum2_send_message)
 
endif()
 
 
if (CPPUNIT_FOUND)
 
	message("tests             : yes")
 
	include_directories(${CPPUNIT_INCLUDE_DIR})
 
else()
 
	if(ENABLE_TESTS)
 
		message("tests             : no (install CPPUnit)")
 
	else(ENABLE_TESTS)
 
		message("tests             : no (user disabled)")
 
	endif()
 
endif()
 
 
if(DOXYGEN_FOUND)
 
	message("Docs              : yes")
 
	ADD_SUBDIRECTORY(docs)
 
else(DOXYGEN_FOUND)
 
	if(ENABLE_DOCS)
 
		message("Docs              : no (install doxygen)")
 
	else(ENABLE_DOCS)
 
		message("Docs              : no (user disabled)")
 
	endif()
 
endif(DOXYGEN_FOUND)
 
 
message("----------------------")
 
 
if(NOT SQLITE3_FOUND AND NOT MYSQL_FOUND AND NOT PQXX_FOUND)
 
	if(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Could not find any database - Please install at least one of sqlite3-devel, mysql-devel or pqxx-devel if you want to use transport mode.")
 
	else(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Please enable at least one of SQLITE3, MYSQL, PQXX databases to use transport mode.")
 
	endif()
 
endif()
 
 
cmake_minimum_required(VERSION 2.6)
 
project(libtransport)
 

	
 
message(STATUS "Variables to override default places where to find libraries:")
 
message(STATUS "|- cppunit : -DCPPUNIT_INCLUDE_DIR,  -DCPPUNIT_LIBRARY")
 
message(STATUS "|- swiften : -DSWIFTEN_INCLUDE_DIR,  -DSWIFTEN_LIBRARY")
 
message(STATUS "  |- zlib  :                         -DZLIB_LIBRARY")
 
message(STATUS "  |- expat :                         -DEXPAT_LIBRARY")
 
message(STATUS "  |-libidn :                         -DLIBIDN_LIBRARY")
 
message(STATUS "  |-libxml :                         -DLIBXML_LIBRARY")
 
message(STATUS "|- boost   : -DBOOST_INCLUDEDIR,     -DBOOST_LIBRARYDIR")
 
message(STATUS "|- protobuf: -DPROTOBUF_INCLUDE_DIR, -DPROTOBUF_LIBRARY")
 
message(STATUS "           : -DPROTOBUF_PROTOC_EXECUTABLE")
 
message(STATUS "|- log4cxx : -DLOG4CXX_INCLUDE_DIR,  -DLOG4CXX_LIBRARY")
 
message(STATUS "|- purple  : -DPURPLE_INCLUDE_DIR,   -DPURPLE_LIBRARY")
 
message(STATUS "           : -DPURPLE_NOT_RUNTIME - enables compilation with libpurple.lib")
 

	
 
option(ENABLE_SQLITE3 "Build with SQLite3 support" ON)
 
option(ENABLE_MYSQL "Build with MySQL support" ON)
 
option(ENABLE_PQXX "Build with Postgres supoort" ON)
 

	
 
option(ENABLE_FROTZ "Build Frotz plugin" ON)
 
option(ENABLE_IRC "Build IRC plugin" ON)
 
option(ENABLE_PURPLE "Build Libpurple plugin" ON)
 
option(ENABLE_SMSTOOLS3 "Build SMSTools3 plugin" ON)
 
option(ENABLE_SKYPE "Build Skype plugin" ON)
 
option(ENABLE_SWIFTEN "Build Swiften plugin" ON)
 
option(ENABLE_TWITTER "Build Twitter plugin" ON)
 
option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON)
 

	
 
option(ENABLE_DOCS "Build Docs" ON)
 
# option(ENABLE_LOG "Build with logging using Log4cxx" ON)
 
option(ENABLE_TESTS "Build Tests using CppUnit" OFF)
 

	
 
MACRO(LIST_CONTAINS var value)
 
	SET(${var})
 
	FOREACH (value2 ${ARGN})
 
		IF (${value} STREQUAL ${value2})
 
		SET(${var} TRUE)
 
		ENDIF (${value} STREQUAL ${value2})
 
	ENDFOREACH (value2)
 
ENDMACRO(LIST_CONTAINS)
 

	
 
if(NOT LIB_INSTALL_DIR)
 
	set(LIB_INSTALL_DIR "lib")
 
endif()
 

	
 
set(CMAKE_MODULE_PATH "cmake_modules")
 

	
 
###### Prerequisites ######
 

	
 

	
 
# FIND SWIFTEN
 

	
 
set(Swiften_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Swiften)
 

	
 
if(NOT SWIFTEN_FOUND)
 
	if (ZLIB_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${ZLIB_LIBRARY})
 
	endif()
 
	if (EXPAT_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${EXPAT_LIBRARY})
 
	endif()
 
	if (LIBIDN_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBIDN_LIBRARY})
 
	endif()
 
	if (LIBXML_LIBRARY)
 
		set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} ${LIBXML_LIBRARY})
 
	endif()
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Dnsapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Crypt32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Secur32")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Iphlpapi")
 
	set(SWIFTEN_LIBRARY ${SWIFTEN_LIBRARY} "Winscard")
 
	message(STATUS "Using swiften: ${SWIFTEN_INCLUDE_DIR} ${SWIFTEN_LIBRARY}")
 
endif()
 

	
 
# FIND BOOST
 
if (WIN32)
 
	set(Boost_USE_STATIC_LIBS      ON)
 
	set(Boost_USE_MULTITHREADED      ON)
 
	set(Boost_USE_STATIC_RUNTIME    OFF)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
else(WIN32)
 
	LIST_CONTAINS(contains -lboost_program_options ${SWIFTEN_LIBRARY})
 
	if(contains)
 
		message(STATUS "Using non-multithreaded boost")
 
		set(Boost_USE_MULTITHREADED 0)
 
	endif(contains)
 
	set(Boost_FIND_QUIETLY ON)
 
	find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals)
 
	if (NOT Boost_FOUND)
 
		set(Boost_FIND_QUIETLY OFF)
 
		find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED)
 
	endif()
 
endif(WIN32)
 

	
 
message( STATUS "Found Boost: ${Boost_VERSION}, ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}")
 

	
 
if (${Boost_VERSION} GREATER 104999)
 
	message( STATUS "Using BOOST_FILESYSTEM_VERSION=3")
 
	add_definitions(-DBOOST_FILESYSTEM_VERSION=3)
 
endif()
 

	
 
# FIND POPT
 
if (NOT WIN32)
 
	set(popt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(popt REQUIRED)
 
endif()
 

	
 
###### Database ######
 

	
 
# FIND SQLITE3
 
if (ENABLE_SQLITE3)
 
	if (MSVC)
 
		set(SQLITE3_FOUND 1)
 
		ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps)
 
	else()
 
		if (WIN32)
 
			ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3)
 
		else()
 
			set(sqlite3_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
			find_package(sqlite3)
 
		endif()
 
	endif()
 
endif()
 

	
 
# FIND MYSQL
 
if(ENABLE_MYSQL)
 
	set(mysql_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(mysql)
 
endif()
 

	
 
# FIND PQXX
 
if(ENABLE_PQXX)
 
	set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(pqxx)
 
endif()
 

	
 
###### Plugins ######
 

	
 
# FIND LIBPURPLE
 
if(ENABLE_PURPLE)
 
	set(purple_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(purple)
 

	
 
	if (WIN32)
 
		if (PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=0)
 
		else(PURPLE_NOT_RUNTIME)
 
			add_definitions(-DPURPLE_RUNTIME=1)
 
		endif(PURPLE_NOT_RUNTIME)
 
	else()
 
		add_definitions(-DPURPLE_RUNTIME=0)
 
	endif()
 

	
 
	# FIND LIBEVENT
 
	set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(event)
 
endif()
 

	
 
# FIND GLIB
 
if(ENABLE_SKYPE OR ENABLE_PURPLE)
 
#	if (GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
 
#		set(GLIB2_FOUND TRUE)
 
#	else()
 
		set(glib_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
		find_package(glib)
 
#	endif()
 
endif()
 

	
 
# FIND LIBXML2
 
# set(libxml2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# find_package(libxml2)
 

	
 
# FIND PROTOBUF
 
set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Protobuf REQUIRED)
 

	
 
if (NOT PROTOBUF_FOUND AND PROTOBUF_INCLUDE_DIR AND PROTOBUF_LIBRARY)
 
	set(PROTOBUF_FOUND 1)
 
	set(PROTOBUF_INCLUDE_DIRS ${PROTOBUF_INCLUDE_DIR})
 
	if (PROTOBUF_PROTOC_EXECUTABLE)
 
	else()
 
		set(PROTOBUF_PROTOC_EXECUTABLE protoc)
 
	endif()
 
	message(STATUS "Using protobuf: ${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_LIBRARY}")
 
endif()
 

	
 
if (WIN32)
 
	add_definitions(-DSWIFTEN_STATIC=1)
 
	ADD_DEFINITIONS(-D_UNICODE)
 
	ADD_DEFINITIONS(-DUNICODE)
 
endif()
 

	
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
set(openssl_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(openssl)
 
endif()
 

	
 
if(ENABLE_IRC)
 
	set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(Communi)
 

	
 
	INCLUDE(FindQt4)
 
	FIND_PACKAGE(Qt4 COMPONENTS QtCore QtNetwork)
 
	# ADD_DEFINITIONS(${SWIFTEN_CFLAGS})
 
	ADD_DEFINITIONS(-DSUPPORT_LEGACY_CAPS)
 
	# ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2)
 
endif()
 

	
 
set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(event)
 

	
 
if (NOT WIN32 AND ENABLE_SKYPE)
 
set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(dbus)
 
endif()
 

	
 
# if(ENABLE_YAHOO2)
 
# 	set(yahoo2_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
# 	find_package(yahoo2)
 
# endif()
 

	
 
####### Miscallanous ######
 

	
 
if(ENABLE_DOCS)
 
	find_package(Doxygen)
 
endif()
 

	
 
# if(ENABLE_LOG)
 
	if(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARY)
 
		set(LOG4CXX_LIBRARIES ${LOG4CXX_LIBRARY})
 
		set(LOG4CXX_FOUND 1)
 
		message(STATUS "Using log4cxx: ${CPPUNIT_INCLUDE_DIR} ${LOG4CXX_INCLUDE_DIR}")
 
	else()
 
		set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
		find_package(log4cxx)
 
	endif()
 
# endif()
 

	
 
# FIND CPPUNIT
 
if(ENABLE_TESTS)
 
	set(cppunit_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
	find_package(cppunit)
 

	
 
	if(NOT CPPUNIT_FOUND AND CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARY)
 
		set(CCPUNIT_LIBRARIES ${CPPUNIT_LIBRARY})
 
		set(CPPUNIT_FOUND 1)
 
		message(STATUS "Using cppunit: ${CPPUNIT_INCLUDE_DIR} ${CPPUNIT_LIBRARIES}")
 
	endif()
 
endif()
 

	
 
message("  Supported features")
 
message("-----------------------")
 

	
 
if (SPECTRUM_VERSION)
 
	ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
else (SPECTRUM_VERSION)
 
	if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		if (NOT GIT_EXECUTABLE)
 
		set (GIT_EXECUTABLE git)
 
		endif()
 
		execute_process(COMMAND ${GIT_EXECUTABLE} "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD
 
						OUTPUT_VARIABLE GIT_REVISION
 
						OUTPUT_STRIP_TRAILING_WHITESPACE
 
		)
 
		set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION})
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
		set(SPECTRUM_VERSION 2.0.0-alpha)
 
		ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}")
 
	endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
 
endif (SPECTRUM_VERSION)
 

	
 
message("Version           : " ${SPECTRUM_VERSION})
 

	
 
if (SQLITE3_FOUND)
 
	ADD_DEFINITIONS(-DWITH_SQLITE)
 
	include_directories(${SQLITE3_INCLUDE_DIR})
 
	message("SQLite3           : yes")
 
else (SQLITE3_FOUND)
 
	if (WIN32)
 
		ADD_DEFINITIONS(-DWITH_SQLITE)
 
		include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
 
		message("SQLite3           : bundled")
 
	else()
 
		set(SQLITE3_LIBRARIES "")
 
		if(ENABLE_SQLITE3)
 
			message("SQLite3           : no (install sqlite3)")
 
		else(ENABLE_SQLITE3)
 
			message("SQLite3           : no (user disabled)")
 
		endif()
 
	endif()
 
endif (SQLITE3_FOUND)
 

	
 
if (MYSQL_FOUND)
 
	ADD_DEFINITIONS(-DWITH_MYSQL)
 
	include_directories(${MYSQL_INCLUDE_DIR})
 
	message("MySQL             : yes")
 
else (MYSQL_FOUND)
 
	set(MYSQL_LIBRARIES "")
 
	if(ENABLE_MYSQL)
 
		message("MySQL             : no (install mysql-devel)")
 
	else(ENABLE_MYSQL)
 
		message("MySQL             : no (user disabled)")
 
	endif()
 
endif (MYSQL_FOUND)
 

	
 
if (PQXX_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PQXX)
 
	include_directories(${PQXX_INCLUDE_DIR})
 
	message("PostgreSQL        : yes")
 
else (PQXX_FOUND)
 
	set(PQXX_LIBRARY "")
 
	set(PQ_LIBRARY "")
 
	if(ENABLE_PQXX)
 
		message("PostgreSQL        : no (install libpqxx-devel)")
 
	else(ENABLE_PQXX)
 
		message("PostgreSQL        : no (user disabled)")
 
	endif()
 
endif (PQXX_FOUND)
 

	
 
if (PROTOBUF_FOUND)
 
	ADD_DEFINITIONS(-DWITH_PROTOBUF)
 
	include_directories(${PROTOBUF_INCLUDE_DIRS})
 
	message("Network plugins   : yes")
 

	
 
	if(PURPLE_FOUND)
 
		message("Libpurple plugin  : yes")
 
		include_directories(${PURPLE_INCLUDE_DIR})
 
		include_directories(${GLIB2_INCLUDE_DIR})
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (install libpurple)")
 
		else(ENABLE_PURPLE)
 
			message("Libpurple plugin  : no (user disabled)")
 
		endif()
 
	endif()
 

	
 
	if (HAVE_EVENT)
 
		ADD_DEFINITIONS(-DWITH_LIBEVENT)
 
		include_directories(${EVENT_INCLUDE_DIRS})
 
		message("  libev eventloop : yes")
 
	else()
 
		if(ENABLE_PURPLE)
 
			message("  libev eventloop : no (install libev-devel)")
 
		endif()
 
	endif()
 

	
 
	if(IRC_FOUND)
 
		ADD_DEFINITIONS(-DCOMMUNI_SHARED)
 
		message("IRC plugin        : yes")
 
		include_directories(${QT_QTNETWORK_INCLUDE_DIR})
 
		include_directories(${IRC_INCLUDE_DIR})
 
		include(${QT_USE_FILE})
 
	else()
 
		if(ENABLE_IRC)
 
			message("IRC plugin        : no (install libCommuni and libprotobuf-dev)")
 
		else(ENABLE_IRC)
 
			message("IRC plugin        : no (user disabled)")
 
		endif()
 
	endif()
 
	if(ENABLE_TWITTER)
 
		message("Twitter plugin    : yes")
 
	else(ENABLE_TWITTER)
 
		message("Twitter plugin    : no (user disabled)")
 
	endif()
 
	if (NOT WIN32)
 
		if(ENABLE_FROTZ)
 
			message("Frotz plugin      : yes")
 
		else()
 
			message("Frotz plugin      : no (user disabled)")
 
		endif()
 
		if(ENABLE_SMSTOOLS3)
 
			message("SMSTools3 plugin  : yes")
 
		else()
 
			message("SMSTools3 plugin  : no (user disabled)")
 
		endif()
 
		if(${LIBDBUSGLIB_FOUND})
 
			message("Skype plugin      : yes")
 
			include_directories(${LIBDBUSGLIB_INCLUDE_DIRS})
 
		else()
 
			if(ENABLE_SKYPE)
 
				message("Skype plugin      : no (install dbus-glib-devel)")
 
			else(ENABLE_SKYPE)
 
				message("Skype plugin      : no (user disabled)")
 
			endif()
 
		endif()
 
	else()
 
		message("Frotz plugin      : no (does not run on Win32)")
 
		message("SMSTools3 plugin  : no (does not run on Win32)")
 
		message("Skype plugin      : no (does not run on Win32)")
 
	endif()
 

	
 
#  	if(YAHOO2_FOUND)
 
# 		message("Libyahoo2 plugin  : yes")
 
#  		include_directories(${YAHOO2_INCLUDE_DIR})
 
# 	else()
 
		if(ENABLE_YAHOO2)
 
			set(YAHOO2_FOUND 1)
 
			message("Libyahoo2 plugin  : yes")
 
		else(ENABLE_YAHOO2)
 
			message("Libyahoo2 plugin  : no (user disabled)")
 
		endif()
 
#  	endif()
 

	
 
	if(ENABLE_SWIFTEN)
 
		message("Swiften plugin    : yes")
 
	else()
 
		message("Swiften plugin    : no (user disabled)")
 
	endif()
 
else()
 
	message("Network plugins   : no (install libprotobuf-dev)")
 
	message("Libpurple plugin  : no (install libpurple and libprotobuf-dev)")
 
	message("IRC plugin        : no (install libircclient-qt and libprotobuf-dev)")
 
	message("Frotz plugin      : no (install libprotobuf-dev)")
 
	message("SMSTools3 plugin  : no (install libprotobuf-dev)")
 
	message("Swiften plugin    : no (install libprotobuf-dev)")
 
    message("Twitter plugin    : no (install libprotobuf-dev)")
 
endif()
 

	
 
if (LOG4CXX_FOUND)
 
	message("Log4cxx           : yes")
 
	include_directories(${LOG4CXX_INCLUDE_DIR})
 
	ADD_DEFINITIONS(-DWITH_LOG4CXX)
 
else()
 
	set(LOG4CXX_LIBRARIES "")
 
	if (WIN32)
 
		message("Log4cxx           : no (install log4cxx-devel)")
 
	else()
 
		message(FATAL_ERROR "Log4cxx           : no (install log4cxx-devel)")
 
	endif()
 
endif()
 

	
 
if (WIN32)
 
	ADD_DEFINITIONS(-DLOG4CXX_STATIC)
 
	ADD_DEFINITIONS(-D_WIN32_WINNT=0x501)
 
	ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
 
	ADD_DEFINITIONS(-DBOOST_USE_WINDOWS_H)
 
	ADD_DEFINITIONS(-DBOOST_THREAD_USE_LIB)
 
endif()
 

	
 
if(CMAKE_BUILD_TYPE MATCHES Debug)
 
	if (CMAKE_COMPILER_IS_GNUCXX)
 
		ADD_DEFINITIONS(-O0)
 
		ADD_DEFINITIONS(-ggdb)
 
	endif()
 
	ADD_DEFINITIONS(-DDEBUG)
 
	message("Debug             : yes")
 
else(CMAKE_BUILD_TYPE MATCHES Debug)
 
	message("Debug             : no (run \"cmake . -DCMAKE_BUILD_TYPE=Debug\")")
 
endif(CMAKE_BUILD_TYPE MATCHES Debug)
 

	
 

	
 
SET(TRANSPORT_VERSION 2.0)
 
SET(PROJECT_VERSION 2.0)
 
include_directories(include)
 

	
 

	
 
include_directories(${EVENT_INCLUDE_DIRS})
 
include_directories(${SWIFTEN_INCLUDE_DIR})
 
include_directories(${Boost_INCLUDE_DIRS})
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
include_directories(${OPENSSL_INCLUDE_DIR})
 
endif()
 

	
 
ADD_SUBDIRECTORY(src)
 
ADD_SUBDIRECTORY(plugin)
 
ADD_SUBDIRECTORY(include)
 
ADD_SUBDIRECTORY(spectrum)
 
ADD_SUBDIRECTORY(backends)
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(spectrum_manager)
 
#	ADD_SUBDIRECTORY(spectrum2_send_message)
 
endif()
 

	
 
if (CPPUNIT_FOUND)
 
	message("tests             : yes")
 
	include_directories(${CPPUNIT_INCLUDE_DIR})
 
else()
 
	if(ENABLE_TESTS)
 
		message("tests             : no (install CPPUnit)")
 
	else(ENABLE_TESTS)
 
		message("tests             : no (user disabled)")
 
	endif()
 
endif()
 

	
 
if(DOXYGEN_FOUND)
 
	message("Docs              : yes")
 
	ADD_SUBDIRECTORY(docs)
 
else(DOXYGEN_FOUND)
 
	if(ENABLE_DOCS)
 
		message("Docs              : no (install doxygen)")
 
	else(ENABLE_DOCS)
 
		message("Docs              : no (user disabled)")
 
	endif()
 
endif(DOXYGEN_FOUND)
 

	
 
message("----------------------")
 

	
 
if(NOT SQLITE3_FOUND AND NOT MYSQL_FOUND AND NOT PQXX_FOUND)
 
	if(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Could not find any database - Please install at least one of sqlite3-devel, mysql-devel or pqxx-devel if you want to use transport mode.")
 
	else(ENABLE_SQLITE3 OR ENABLE_MYSQL OR ENABLE_PQXX)
 
		message("Please enable at least one of SQLITE3, MYSQL, PQXX databases to use transport mode.")
 
	endif()
 
endif()
 

	
README.win32
Show inline comments
 
Prerequisites
 
=============
 
 
1. Microsoft Visual C++ 2010 Express or higher edition (http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express)
 
2. Git for Windows (http://code.google.com/p/msysgit/downloads/list)
 
3. CMake 2.8 or newer (http://www.cmake.org/cmake/resources/software.html)
 
4. Python 2.x for Swiften build scripts (scons) (http://www.python.org)
 
 
Libraries
 
=========
 
3. Swiften library (http://swift.im/git/swift)
 
4. Boost 1.48 or newer (http://sourceforge.net/projects/boost/files/boost/1.49.0/)
 
5. Google ProtoBuf library (http://code.google.com/p/protobuf/downloads/list)
 
 
 
Environment
 
===========
 
 
To create spectrum build environment do:
 
 
0. Create directory where we'll install all dependencies, e.g. C:\env-msvc-x64.
 
Create C:\env-msvc-x64\bin and add it to %PATH%.
 
Assuming you have git, python and cmake in %PATH%, 
 
launch "Visual Studio 2010 command prompt" or
 
"Visual Studio 2010(x64) command prompt", depends on your target (Windows x86 or Windows x86_64).
 
1. unpack and build boost libraries:
 
 
	bootstrap.bat
 
	b2.exe --without-mpi --without-python
 
	b2.exe --without-mpi --without-python install --prefix=C:\env-msvc-x64 release
 
 
2. clone swift repository and build it. Don't forget to point it to our env directory:
 
 
	git clone git://swift.im/swift
 
	cd swift
 
	echo boost_includedir="c:/env-msvc-x64/include/boost-1_49" > config.py
 
	echo boost_libdir="c:/env-msvc-x64/lib" >> config.py 
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64 force_configure=1
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64 C:\env-msvc-x64
 
 
3. unpack and compile protobuf as described in its documentation. 
 
 
Run extract_includes.bat in vsprojects/ directory and move resulting vsprojects/include/google/ directory to our C:\env-msvc-x64\include
 
 
Move protoc.exe to C:\env-msvc-x64\bin\ and libprotobuf.lib to C:\env-msvc-x64\lib
 
 
4. Install gtkmm
 
 
Download installer from https://live.gnome.org/gtkmm/MSWindows and install gtkmm into C:\env-msvc-x64\
 
 
5. Install libpurple headers
 
 
Download http://www.pidgin.im/download/source/ , extract it and copy libpurple directory in C:\env-msvc-x64\include
 
 
6. You're ready! :) Clone libtransport into C:\env-msvc-x64\libtransport (You *must* clone it into this directory, because libtransport will try to find the dependencies in ../lib and ../include)
 
 
Compile it as:
 
 
	set CMAKE_INCLUDE_PATH=C:\env-msvc-x64\include
 
	cmake . -G "NMake Makefiles" -DBOOST_INCLUDEDIR=../include/boost-1_49 -DBOOST_LIBRARYDIR=../lib -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=C:\env-msvc-x64 -DGIT_EXECUTABLE="c:\Program Files (x86)\git\bin\git.exe"
 
	nmake
 
Prerequisites
 
=============
 

	
 
1. Microsoft Visual C++ 2010 Express or higher edition (http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express)
 
2. Git for Windows (http://code.google.com/p/msysgit/downloads/list)
 
3. CMake 2.8 or newer (http://www.cmake.org/cmake/resources/software.html)
 
4. Python 2.x for Swiften build scripts (scons) (http://www.python.org)
 

	
 
Libraries
 
=========
 
3. Swiften library (http://swift.im/git/swift)
 
4. Boost 1.48 or newer (http://sourceforge.net/projects/boost/files/boost/1.49.0/)
 
5. Google ProtoBuf library (http://code.google.com/p/protobuf/downloads/list)
 

	
 

	
 
Environment
 
===========
 

	
 
To create spectrum build environment do:
 

	
 
0. Create directory where we'll install all dependencies, e.g. C:\env-msvc-x64.
 
Create C:\env-msvc-x64\bin and add it to %PATH%.
 
Assuming you have git, python and cmake in %PATH%, 
 
launch "Visual Studio 2010 command prompt" or
 
"Visual Studio 2010(x64) command prompt", depends on your target (Windows x86 or Windows x86_64).
 
1. unpack and build boost libraries:
 

	
 
	bootstrap.bat
 
	b2.exe --without-mpi --without-python
 
	b2.exe --without-mpi --without-python install --prefix=C:\env-msvc-x64 release
 

	
 
2. clone swift repository and build it. Don't forget to point it to our env directory:
 

	
 
	git clone git://swift.im/swift
 
	cd swift
 
	echo boost_includedir="c:/env-msvc-x64/include/boost-1_49" > config.py
 
	echo boost_libdir="c:/env-msvc-x64/lib" >> config.py 
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64 force_configure=1
 
	scons.bat debug=no SWIFTEN_INSTALLDIR=C:\env-msvc-x64 C:\env-msvc-x64
 

	
 
3. unpack and compile protobuf as described in its documentation. 
 

	
 
Run extract_includes.bat in vsprojects/ directory and move resulting vsprojects/include/google/ directory to our C:\env-msvc-x64\include
 

	
 
Move protoc.exe to C:\env-msvc-x64\bin\ and libprotobuf.lib to C:\env-msvc-x64\lib
 

	
 
4. Install gtkmm
 

	
 
Download installer from https://live.gnome.org/gtkmm/MSWindows and install gtkmm into C:\env-msvc-x64\
 

	
 
5. Install libpurple headers
 

	
 
Download http://www.pidgin.im/download/source/ , extract it and copy libpurple directory in C:\env-msvc-x64\include
 

	
 
6. You're ready! :) Clone libtransport into C:\env-msvc-x64\libtransport (You *must* clone it into this directory, because libtransport will try to find the dependencies in ../lib and ../include)
 

	
 
Compile it as:
 

	
 
	set CMAKE_INCLUDE_PATH=C:\env-msvc-x64\include
 
	cmake . -G "NMake Makefiles" -DBOOST_INCLUDEDIR=../include/boost-1_49 -DBOOST_LIBRARYDIR=../lib -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=C:\env-msvc-x64 -DGIT_EXECUTABLE="c:\Program Files (x86)\git\bin\git.exe"
 
	nmake
backends/frotz/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
ADD_SUBDIRECTORY(dfrotz)
 
 
FILE(GLOB SRC *.c *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_frotz_backend ${SRC})
 
 
target_link_libraries(spectrum2_frotz_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
 
INSTALL(TARGETS spectrum2_frotz_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
ADD_SUBDIRECTORY(dfrotz)
 

	
 
FILE(GLOB SRC *.c *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_frotz_backend ${SRC})
 

	
 
target_link_libraries(spectrum2_frotz_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 

	
 
INSTALL(TARGETS spectrum2_frotz_backend RUNTIME DESTINATION bin)
 

	
backends/frotz/dfrotz/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC common/*.c dumb/*.c)
 
 
ADD_EXECUTABLE(dfrotz ${SRC})
 
 
# target_link_libraries(dfrotz)
 
 
INSTALL(TARGETS dfrotz RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC common/*.c dumb/*.c)
 

	
 
ADD_EXECUTABLE(dfrotz ${SRC})
 

	
 
# target_link_libraries(dfrotz)
 

	
 
INSTALL(TARGETS dfrotz RUNTIME DESTINATION bin)
 

	
backends/libcommuni/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 
FILE(GLOB HEADERS *.h)
 
QT4_WRAP_CPP(SRC ${HEADERS} OPTIONS -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED)
 
ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC})
 
 
if (NOT WIN32)
 
	target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread)
 
else ()
 
	target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport)
 
endif()
 
INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 
FILE(GLOB HEADERS *.h)
 
QT4_WRAP_CPP(SRC ${HEADERS} OPTIONS -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED)
 
ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC})
 

	
 
if (NOT WIN32)
 
	target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread)
 
else ()
 
	target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport)
 
endif()
 
INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin)
 

	
backends/libpurple/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_libpurple_backend ${SRC})
 
 
if(CMAKE_COMPILER_IS_GNUCXX)
 
	if (NOT WIN32)
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin pthread)
 
	else()
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin)
 
	endif()
 
else()
 
target_link_libraries(spectrum2_libpurple_backend sqlite3 ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${LIBXML2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin ${PROTOBUF_LIBRARY})
 
endif()
 
 
INSTALL(TARGETS spectrum2_libpurple_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_libpurple_backend ${SRC})
 

	
 
if(CMAKE_COMPILER_IS_GNUCXX)
 
	if (NOT WIN32)
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin pthread)
 
	else()
 
	target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin)
 
	endif()
 
else()
 
target_link_libraries(spectrum2_libpurple_backend sqlite3 ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${LIBXML2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin ${PROTOBUF_LIBRARY})
 
endif()
 

	
 
INSTALL(TARGETS spectrum2_libpurple_backend RUNTIME DESTINATION bin)
 

	
backends/libyahoo2/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
FILE(GLOB_RECURSE SRC *.c *.cpp)
 
 
ADD_DEFINITIONS(-DHAVE_STDINT_H=1)
 
 
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/yahoo)
 
 
ADD_EXECUTABLE(spectrum2_libyahoo2_backend ${SRC})
 
 
target_link_libraries(spectrum2_libyahoo2_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
 
INSTALL(TARGETS spectrum2_libyahoo2_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
FILE(GLOB_RECURSE SRC *.c *.cpp)
 

	
 
ADD_DEFINITIONS(-DHAVE_STDINT_H=1)
 

	
 
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/yahoo)
 

	
 
ADD_EXECUTABLE(spectrum2_libyahoo2_backend ${SRC})
 

	
 
target_link_libraries(spectrum2_libyahoo2_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 

	
 
INSTALL(TARGETS spectrum2_libyahoo2_backend RUNTIME DESTINATION bin)
 

	
backends/skype/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_skype_backend ${SRC})
 
 
target_link_libraries(spectrum2_skype_backend ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport pthread ${LIBDBUSGLIB_LIBRARIES})
 
 
INSTALL(TARGETS spectrum2_skype_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_skype_backend ${SRC})
 

	
 
target_link_libraries(spectrum2_skype_backend ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport pthread ${LIBDBUSGLIB_LIBRARIES})
 

	
 
INSTALL(TARGETS spectrum2_skype_backend RUNTIME DESTINATION bin)
 

	
backends/smstools3/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
FILE(GLOB SRC *.c *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_smstools3_backend ${SRC})
 
 
target_link_libraries(spectrum2_smstools3_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
 
INSTALL(TARGETS spectrum2_smstools3_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
FILE(GLOB SRC *.c *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_smstools3_backend ${SRC})
 

	
 
target_link_libraries(spectrum2_smstools3_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 

	
 
INSTALL(TARGETS spectrum2_smstools3_backend RUNTIME DESTINATION bin)
 

	
backends/swiften/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
FILE(GLOB SRC *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_swiften_backend ${SRC})
 
 
IF (NOT WIN32)
 
target_link_libraries(spectrum2_swiften_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_swiften_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
 
INSTALL(TARGETS spectrum2_swiften_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
FILE(GLOB SRC *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_swiften_backend ${SRC})
 

	
 
IF (NOT WIN32)
 
target_link_libraries(spectrum2_swiften_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_swiften_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 

	
 
INSTALL(TARGETS spectrum2_swiften_backend RUNTIME DESTINATION bin)
 

	
backends/swiften/main.cpp
Show inline comments
 
// Transport includes
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 
 
#include "boost/date_time/posix_time/posix_time.hpp"
 
 
// Swiften
 
#include "Swiften/Swiften.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
#ifndef WIN32
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
// malloc_trim
 
#include "malloc.h"
 
#endif
 
#endif
 
 
// Boost
 
#include <boost/algorithm/string.hpp>
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 
 
DEFINE_LOGGER(logger, "Swiften");
 
DEFINE_LOGGER(logger_xml, "backend.xml");
 
 
// eventloop
 
Swift::SimpleEventLoop *loop_;
 
 
// Plugins
 
class SwiftenPlugin;
 
NetworkPlugin *np = NULL;
 
Swift::XMPPSerializer *serializer;
 
 
class ForwardIQHandler : public Swift::IQHandler {
 
	public:
 
		std::map <std::string, std::string> m_id2resource;
 
 
		ForwardIQHandler(NetworkPlugin *np, const std::string &user) {
 
			m_np = np;
 
			m_user = user;
 
		}
 
 
		bool handleIQ(boost::shared_ptr<Swift::IQ> iq) {
 
			if (iq->getPayload<Swift::RosterPayload>() != NULL) {
 
				return false;
 
			}
 
			if (iq->getType() == Swift::IQ::Get) {
 
				m_id2resource[iq->getID()] = iq->getFrom().getResource();
 
			}
 
 
			iq->setTo(m_user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(iq));
 
			m_np->sendRawXML(xml);
 
			return true;
 
		}
 
 
	private:
 
		NetworkPlugin *m_np;
 
		std::string m_user;
 
		
 
};
 
 
class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 
		bool m_firstPing;
 
		
 
		Swift::FullPayloadSerializerCollection collection;
 
		Swift::XMPPParser *m_xmppParser;
 
		Swift::FullPayloadParserFactoryCollection m_collection2;
 
 
		SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
 
			this->config = config;
 
			m_firstPing = true;
 
			m_factories = new Swift::BoostNetworkFactories(loop);
 
			m_conn = m_factories->getConnectionFactory()->createConnection();
 
			m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1));
 
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 
#if HAVE_SWIFTEN_3
 
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType, false);
 
#else
 
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType);
 
#endif
 
			m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory());
 
			m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 
 
			LOG4CXX_INFO(logger, "Starting the plugin.");
 
		}
 
 
		// NetworkPlugin uses this method to send the data to networkplugin server
 
		void sendData(const std::string &string) {
 
			m_conn->write(Swift::createSafeByteArray(string));
 
		}
 
 
		// This method has to call handleDataRead with all received data from network plugin server
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
 
			if (m_firstPing) {
 
				m_firstPing = false;
 
				NetworkPlugin::PluginConfig cfg;
 
				cfg.setRawXML(true);
 
				sendConfig(cfg);
 
			}
 
			std::string d(data->begin(), data->end());
 
			handleDataRead(d);
 
		}
 
 
		void handleStreamStart(const Swift::ProtocolHeader&) {}
 
#if HAVE_SWIFTEN_3
 
		void handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
 
#else
 
		void handleElement(boost::shared_ptr<Swift::Element> element) {
 
#endif
 
			boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
 
			if (!stanza) {
 
				return;
 
			}
 
 
			std::string user = stanza->getFrom().toBare();
 
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (!client)
 
				return;
 
 
			stanza->setFrom(client->getJID());
 
 
			boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
 
			if (message) {
 
				client->sendMessage(message);
 
				return;
 
			}
 
 
			boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
 
			if (presence) {
 
				client->sendPresence(presence);
 
				return;
 
			}
 
 
			boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
 
			if (iq) {
 
				if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) {
 
					std::string resource = m_handlers[user]->m_id2resource[stanza->getID()];
 
					if (resource.empty()) {
 
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain()));					
 
					} else {
 
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), resource));					
 
					}
 
					
 
					m_handlers[user]->m_id2resource.erase(stanza->getID());
 
				}
 
				client->getIQRouter()->sendIQ(iq);
 
				return;
 
			}
 
		}
 
 
		void handleStreamEnd() {}
 
 
		void handleRawXML(const std::string &xml) {
 
			m_xmppParser->parse(xml);
 
		}
 
 
		void handleSwiftDisconnected(const std::string &user, const boost::optional<Swift::ClientError> &error) {
 
			std::string message = "";
 
			bool reconnect = false;
 
			if (error) {
 
				switch(error->getType()) {
 
					case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break;
 
					case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break;
 
					case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break;
 
					case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break;
 
					case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break;
 
					case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break;
 
					case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break;
 
					case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break;
 
					case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break;
 
					case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break;
 
					case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break;
 
					case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break;
 
					case Swift::ClientError::SessionStartError: message = ("Error starting session"); break;
 
					case Swift::ClientError::StreamError: message = ("Stream error"); break;
 
					case Swift::ClientError::TLSError: message = ("Encryption error"); break;
 
					case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break;
 
					case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break;
 
 
					case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break;
 
					case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break;
 
					case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break;
 
					case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break;
 
					case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break;
 
					case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break;
 
					case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break;
 
					case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break;
 
					case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break;
 
					case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break;
 
					case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break;
 
				}
 
			}
 
			LOG4CXX_INFO(logger, user << ": Disconnected " << message);
 
			handleDisconnected(user, reconnect ? 0 : 3, message);
 
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				m_users.erase(user);
 
				m_handlers.erase(user);
 
			}
 
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
			// force returning of memory chunks allocated by libxml2 to kernel
 
			malloc_trim(0);
 
#endif
 
#endif
 
#endif
 
		}
 
 
		void handleSwiftConnected(const std::string &user) {
 
			LOG4CXX_INFO(logger, user << ": Connected to XMPP server.");
 
			handleConnected(user);
 
			m_users[user]->requestRoster();
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(m_users[user]->getJID());
 
			m_users[user]->sendPresence(response);
 
		}
 
 
		void handleSwiftRosterReceived(const std::string &user) {
 
			Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle();
 
			BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) {
 
				Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID());
 
				pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE;
 
				handleBuddyChanged(user, item.getJID().toBare().toString(),
 
								   item.getName(), item.getGroups(), status);
 
			}
 
		}
 
 
		void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
 
// 			boost::shared_ptr<Swift::Client> client = m_users[user];
 
// 			if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
 
// 				return;
 
// 			}
 
// 
 
// 			if (presence->getPayload<Swift::MUCUserPayload>() != NULL || presence->getPayload<Swift::MUCPayload>() != NULL) {
 
// 				return;
 
// 			}
 
// 
 
// 			LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
 
// 
 
// 			std::string message = presence->getStatus();
 
// 			std::string photo = "";
 
// 
 
// 			boost::shared_ptr<Swift::VCardUpdate> update = presence->getPayload<Swift::VCardUpdate>();
 
// 			if (update) {
 
// 				photo = update->getPhotoHash();
 
// 			}
 
// 
 
// 			boost::optional<Swift::XMPPRosterItem> item = m_users[user]->getRoster()->getItem(presence->getFrom());
 
// 			if (item) {
 
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo);
 
// 			}
 
// 			else {
 
// 				std::vector<std::string> groups;
 
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo);
 
// 			}
 
			presence->setTo(user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(presence));
 
			sendRawXML(xml);
 
		}
 
 
		void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) {
 
			message->setTo(user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(message));
 
			sendRawXML(xml);
 
		}
 
 
		void handleSwiftenDataRead(const Swift::SafeByteArray &data) {
 
			std::string d = safeByteArrayToString(data);
 
			if (!boost::starts_with(d, "<auth")) {
 
				LOG4CXX_INFO(logger_xml, "XML IN " << d);
 
			}
 
		}
 
 
		void handleSwiftenDataWritten(const Swift::SafeByteArray &data) {
 
			LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
 
		}
 
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			LOG4CXX_INFO(logger, user << ": connecting as " << legacyName);
 
			boost::shared_ptr<Swift::Client> client = boost::make_shared<Swift::Client>(Swift::JID(legacyName + "/Spectrum"), password, m_factories);
 
			m_users[user] = client;
 
			client->setAlwaysTrustCertificates();
 
			client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
			client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
			client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
			client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
			client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
			client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1));
 
			client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1));
 
			client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3));
 
			client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2));
 
			Swift::ClientOptions opt;
 
			opt.allowPLAINWithoutTLS = true;
 
			client->connect(opt);
 
 
			boost::shared_ptr<ForwardIQHandler> handler = boost::make_shared<ForwardIQHandler>(this, user);
 
			client->getIQRouter()->addHandler(handler);
 
			m_handlers[user] = handler;
 
		}
 
 
		void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) {
 
			handleSwiftPresenceChanged(user, presence);
 
		}
 
 
		void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) {
 
			Swift::Presence::ref presence = Swift::Presence::create();
 
			presence->setTo(user);
 
			presence->setFrom(jid);
 
			presence->setType(Swift::Presence::Unsubscribe);
 
			handleSwiftPresenceChanged(user, presence);
 
		}
 
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
// 				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
				client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
				client->disconnect();
 
			}
 
		}
 
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") {
 
		}
 
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
		}
 
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
 
				if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) {
 
					Swift::RosterItemPayload item;
 
					item.setName(alias);
 
					item.setJID(buddyName);
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
					client->getSubscriptionManager()->requestSubscription(buddyName);
 
				}
 
				else {
 
					Swift::JID contact(buddyName);
 
					Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
				}
 
 
			}
 
		}
 
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
 
				boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
				roster->addItem(item);
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 				request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
				request->send();
 
			}
 
		}
 
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
 
 
		}
 
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
 
 
		}
 
 
	private:
 
		Config *config;
 
		std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
 
		std::map<std::string, boost::shared_ptr<ForwardIQHandler> > m_handlers;
 
};
 
 
#ifndef WIN32
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 
#endif
 
 
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 
 
#ifndef WIN32
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 
#endif
 
 
	std::string error;
 
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
 
	if (cfg == NULL) {
 
		std::cerr << error;
 
		return 1;
 
	}
 
 
	Logging::initBackendLogging(cfg);
 
 
	Swift::SimpleEventLoop eventLoop;
 
	loop_ = &eventLoop;
 
	np = new SwiftenPlugin(cfg, &eventLoop, host, port);
 
	loop_->run();
 
 
	return 0;
 
}
 
// Transport includes
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 

	
 
#include "boost/date_time/posix_time/posix_time.hpp"
 

	
 
// Swiften
 
#include "Swiften/Swiften.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
#ifndef WIN32
 
// for signal handler
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 

	
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
// malloc_trim
 
#include "malloc.h"
 
#endif
 
#endif
 

	
 
// Boost
 
#include <boost/algorithm/string.hpp>
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
DEFINE_LOGGER(logger, "Swiften");
 
DEFINE_LOGGER(logger_xml, "backend.xml");
 

	
 
// eventloop
 
Swift::SimpleEventLoop *loop_;
 

	
 
// Plugins
 
class SwiftenPlugin;
 
NetworkPlugin *np = NULL;
 
Swift::XMPPSerializer *serializer;
 

	
 
class ForwardIQHandler : public Swift::IQHandler {
 
	public:
 
		std::map <std::string, std::string> m_id2resource;
 

	
 
		ForwardIQHandler(NetworkPlugin *np, const std::string &user) {
 
			m_np = np;
 
			m_user = user;
 
		}
 

	
 
		bool handleIQ(boost::shared_ptr<Swift::IQ> iq) {
 
			if (iq->getPayload<Swift::RosterPayload>() != NULL) {
 
				return false;
 
			}
 
			if (iq->getType() == Swift::IQ::Get) {
 
				m_id2resource[iq->getID()] = iq->getFrom().getResource();
 
			}
 

	
 
			iq->setTo(m_user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(iq));
 
			m_np->sendRawXML(xml);
 
			return true;
 
		}
 

	
 
	private:
 
		NetworkPlugin *m_np;
 
		std::string m_user;
 
		
 
};
 

	
 
class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 
		bool m_firstPing;
 
		
 
		Swift::FullPayloadSerializerCollection collection;
 
		Swift::XMPPParser *m_xmppParser;
 
		Swift::FullPayloadParserFactoryCollection m_collection2;
 

	
 
		SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
 
			this->config = config;
 
			m_firstPing = true;
 
			m_factories = new Swift::BoostNetworkFactories(loop);
 
			m_conn = m_factories->getConnectionFactory()->createConnection();
 
			m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1));
 
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 
#if HAVE_SWIFTEN_3
 
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType, false);
 
#else
 
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType);
 
#endif
 
			m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory());
 
			m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 

	
 
			LOG4CXX_INFO(logger, "Starting the plugin.");
 
		}
 

	
 
		// NetworkPlugin uses this method to send the data to networkplugin server
 
		void sendData(const std::string &string) {
 
			m_conn->write(Swift::createSafeByteArray(string));
 
		}
 

	
 
		// This method has to call handleDataRead with all received data from network plugin server
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
 
			if (m_firstPing) {
 
				m_firstPing = false;
 
				NetworkPlugin::PluginConfig cfg;
 
				cfg.setRawXML(true);
 
				sendConfig(cfg);
 
			}
 
			std::string d(data->begin(), data->end());
 
			handleDataRead(d);
 
		}
 

	
 
		void handleStreamStart(const Swift::ProtocolHeader&) {}
 
#if HAVE_SWIFTEN_3
 
		void handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
 
#else
 
		void handleElement(boost::shared_ptr<Swift::Element> element) {
 
#endif
 
			boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
 
			if (!stanza) {
 
				return;
 
			}
 

	
 
			std::string user = stanza->getFrom().toBare();
 

	
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (!client)
 
				return;
 

	
 
			stanza->setFrom(client->getJID());
 

	
 
			boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
 
			if (message) {
 
				client->sendMessage(message);
 
				return;
 
			}
 

	
 
			boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
 
			if (presence) {
 
				client->sendPresence(presence);
 
				return;
 
			}
 

	
 
			boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
 
			if (iq) {
 
				if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) {
 
					std::string resource = m_handlers[user]->m_id2resource[stanza->getID()];
 
					if (resource.empty()) {
 
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain()));					
 
					} else {
 
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), resource));					
 
					}
 
					
 
					m_handlers[user]->m_id2resource.erase(stanza->getID());
 
				}
 
				client->getIQRouter()->sendIQ(iq);
 
				return;
 
			}
 
		}
 

	
 
		void handleStreamEnd() {}
 

	
 
		void handleRawXML(const std::string &xml) {
 
			m_xmppParser->parse(xml);
 
		}
 

	
 
		void handleSwiftDisconnected(const std::string &user, const boost::optional<Swift::ClientError> &error) {
 
			std::string message = "";
 
			bool reconnect = false;
 
			if (error) {
 
				switch(error->getType()) {
 
					case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break;
 
					case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break;
 
					case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break;
 
					case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break;
 
					case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break;
 
					case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break;
 
					case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break;
 
					case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break;
 
					case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break;
 
					case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break;
 
					case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break;
 
					case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break;
 
					case Swift::ClientError::SessionStartError: message = ("Error starting session"); break;
 
					case Swift::ClientError::StreamError: message = ("Stream error"); break;
 
					case Swift::ClientError::TLSError: message = ("Encryption error"); break;
 
					case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break;
 
					case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break;
 

	
 
					case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break;
 
					case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break;
 
					case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break;
 
					case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break;
 
					case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break;
 
					case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break;
 
					case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break;
 
					case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break;
 
					case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break;
 
					case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break;
 
					case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break;
 
				}
 
			}
 
			LOG4CXX_INFO(logger, user << ": Disconnected " << message);
 
			handleDisconnected(user, reconnect ? 0 : 3, message);
 

	
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				m_users.erase(user);
 
				m_handlers.erase(user);
 
			}
 

	
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
			// force returning of memory chunks allocated by libxml2 to kernel
 
			malloc_trim(0);
 
#endif
 
#endif
 
#endif
 
		}
 

	
 
		void handleSwiftConnected(const std::string &user) {
 
			LOG4CXX_INFO(logger, user << ": Connected to XMPP server.");
 
			handleConnected(user);
 
			m_users[user]->requestRoster();
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(m_users[user]->getJID());
 
			m_users[user]->sendPresence(response);
 
		}
 

	
 
		void handleSwiftRosterReceived(const std::string &user) {
 
			Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle();
 
			BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) {
 
				Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID());
 
				pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE;
 
				handleBuddyChanged(user, item.getJID().toBare().toString(),
 
								   item.getName(), item.getGroups(), status);
 
			}
 
		}
 

	
 
		void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
 
// 			boost::shared_ptr<Swift::Client> client = m_users[user];
 
// 			if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
 
// 				return;
 
// 			}
 
// 
 
// 			if (presence->getPayload<Swift::MUCUserPayload>() != NULL || presence->getPayload<Swift::MUCPayload>() != NULL) {
 
// 				return;
 
// 			}
 
// 
 
// 			LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
 
// 
 
// 			std::string message = presence->getStatus();
 
// 			std::string photo = "";
 
// 
 
// 			boost::shared_ptr<Swift::VCardUpdate> update = presence->getPayload<Swift::VCardUpdate>();
 
// 			if (update) {
 
// 				photo = update->getPhotoHash();
 
// 			}
 
// 
 
// 			boost::optional<Swift::XMPPRosterItem> item = m_users[user]->getRoster()->getItem(presence->getFrom());
 
// 			if (item) {
 
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo);
 
// 			}
 
// 			else {
 
// 				std::vector<std::string> groups;
 
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo);
 
// 			}
 
			presence->setTo(user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(presence));
 
			sendRawXML(xml);
 
		}
 

	
 
		void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) {
 
			message->setTo(user);
 
			std::string xml = safeByteArrayToString(serializer->serializeElement(message));
 
			sendRawXML(xml);
 
		}
 

	
 
		void handleSwiftenDataRead(const Swift::SafeByteArray &data) {
 
			std::string d = safeByteArrayToString(data);
 
			if (!boost::starts_with(d, "<auth")) {
 
				LOG4CXX_INFO(logger_xml, "XML IN " << d);
 
			}
 
		}
 

	
 
		void handleSwiftenDataWritten(const Swift::SafeByteArray &data) {
 
			LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			LOG4CXX_INFO(logger, user << ": connecting as " << legacyName);
 
			boost::shared_ptr<Swift::Client> client = boost::make_shared<Swift::Client>(Swift::JID(legacyName + "/Spectrum"), password, m_factories);
 
			m_users[user] = client;
 
			client->setAlwaysTrustCertificates();
 
			client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
			client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
			client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
			client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
			client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
			client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1));
 
			client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1));
 
			client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3));
 
			client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2));
 
			Swift::ClientOptions opt;
 
			opt.allowPLAINWithoutTLS = true;
 
			client->connect(opt);
 

	
 
			boost::shared_ptr<ForwardIQHandler> handler = boost::make_shared<ForwardIQHandler>(this, user);
 
			client->getIQRouter()->addHandler(handler);
 
			m_handlers[user] = handler;
 
		}
 

	
 
		void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) {
 
			handleSwiftPresenceChanged(user, presence);
 
		}
 

	
 
		void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) {
 
			Swift::Presence::ref presence = Swift::Presence::create();
 
			presence->setTo(user);
 
			presence->setFrom(jid);
 
			presence->setType(Swift::Presence::Unsubscribe);
 
			handleSwiftPresenceChanged(user, presence);
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
 
// 				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
 
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
 
				client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
 
				client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
 
				client->disconnect();
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") {
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
 
				if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) {
 
					Swift::RosterItemPayload item;
 
					item.setName(alias);
 
					item.setJID(buddyName);
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
					client->getSubscriptionManager()->requestSubscription(buddyName);
 
				}
 
				else {
 
					Swift::JID contact(buddyName);
 
					Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
 
					item.setGroups(groups);
 
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
					roster->addItem(item);
 
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
					request->send();
 
				}
 

	
 
			}
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
			boost::shared_ptr<Swift::Client> client = m_users[user];
 
			if (client) {
 
				Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
 
				boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
 
				roster->addItem(item);
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
 
// 				request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
 
				request->send();
 
			}
 
		}
 

	
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
 

	
 
		}
 

	
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
 

	
 
		}
 

	
 
	private:
 
		Config *config;
 
		std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
 
		std::map<std::string, boost::shared_ptr<ForwardIQHandler> > m_handlers;
 
};
 

	
 
#ifndef WIN32
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 

	
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 

	
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 
#endif
 

	
 

	
 
int main (int argc, char* argv[]) {
 
	std::string host;
 
	int port;
 

	
 
#ifndef WIN32
 
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
		std::cout << "SIGCHLD handler can't be set\n";
 
		return -1;
 
	}
 
#endif
 

	
 
	std::string error;
 
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
 
	if (cfg == NULL) {
 
		std::cerr << error;
 
		return 1;
 
	}
 

	
 
	Logging::initBackendLogging(cfg);
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	loop_ = &eventLoop;
 
	np = new SwiftenPlugin(cfg, &eventLoop, host, port);
 
	loop_->run();
 

	
 
	return 0;
 
}
backends/template/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
FILE(GLOB SRC *.c *.cpp)
 
 
ADD_EXECUTABLE(spectrum2_template_backend ${SRC})
 
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
if (NOT WIN32)
 
target_link_libraries(spectrum2_template_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
 
#INSTALL(TARGETS spectrum2_template_backend RUNTIME DESTINATION bin)
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
FILE(GLOB SRC *.c *.cpp)
 

	
 
ADD_EXECUTABLE(spectrum2_template_backend ${SRC})
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
if (NOT WIN32)
 
target_link_libraries(spectrum2_template_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${PROTOBUF_LIBRARY} ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 

	
 
#INSTALL(TARGETS spectrum2_template_backend RUNTIME DESTINATION bin)
 

	
backends/twitter/TwitterPlugin.cpp
Show inline comments
 
#include "TwitterPlugin.h"
 
#include "Requests/StatusUpdateRequest.h"
 
#include "Requests/DirectMessageRequest.h"
 
#include "Requests/TimelineRequest.h"
 
#include "Requests/FetchFriends.h"
 
#include "Requests/HelpMessageRequest.h"
 
#include "Requests/PINExchangeProcess.h"
 
#include "Requests/OAuthFlow.h"
 
#include "Requests/CreateFriendRequest.h"
 
#include "Requests/DestroyFriendRequest.h"
 
#include "Requests/RetweetRequest.h"
 
#include "Requests/ProfileImageRequest.h"
 
#include "Swiften/StringCodecs/Hexify.h"
 
 
DEFINE_LOGGER(logger, "Twitter Backend");
 
 
TwitterPlugin *np = NULL;
 
Swift::SimpleEventLoop *loop_; // Event Loop
 
 
const std::string OLD_APP_KEY = "PCWAdQpyyR12ezp2fVwEhw";
 
const std::string OLD_APP_SECRET = "EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg";
 
 
#define abs(x) ((x)<0?-(x):(x))
 
#define SHA(x) (Swift::Hexify::hexify(Swift::SHA1::getHash(Swift::createByteArray((x)))))
 
 
//Compares two +ve intergers 'a' and 'b' represented as strings 
 
static int cmp(std::string a, std::string b)
 
{
 
	int diff = abs((int)a.size() - (int)b.size());
 
	if(a.size() < b.size()) a = std::string(diff,'0') + a;
 
	else b = std::string(diff,'0') + b;
 
	
 
	if(a == b) return 0;
 
	if(a < b) return -1;
 
	return 1;
 
}
 
 
 
TwitterPlugin::TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port) : NetworkPlugin() 
 
{
 
	this->config = config;
 
	this->storagebackend = storagebackend;
 
	this->m_firstPing = true;
 
 
	if (CONFIG_HAS_KEY(config, "twitter.consumer_key") == false) {
 
		consumerKey = "5mFePMiJi0KpeURONkelg";
 
	}
 
	else {
 
		consumerKey = CONFIG_STRING(config, "twitter.consumer_key");
 
	}
 
	if (CONFIG_HAS_KEY(config, "twitter.consumer_secret") == false) {
 
		consumerSecret = "YFZCDJwRhbkccXEnaYr1waCQejTJcOY8F7l5Wim3FA";
 
	}
 
	else {
 
		consumerSecret = CONFIG_STRING(config, "twitter.consumer_secret");
 
	}
 
 
	if (consumerSecret.empty() || consumerKey.empty()) {
 
		LOG4CXX_ERROR(logger, "Consumer key and Consumer secret can't be empty.");
 
		exit(1);
 
	}
 
 
	adminLegacyName = "twitter.com"; 
 
	adminChatRoom = "#twitter"; 
 
	adminNickName = "twitter"; 
 
	adminAlias = "twitter";
 
 
	OAUTH_KEY = "twitter_oauth_token";
 
	OAUTH_SECRET = "twitter_oauth_secret";
 
	MODE = "mode";
 
 
	m_factories = new Swift::BoostNetworkFactories(loop);
 
	m_conn = m_factories->getConnectionFactory()->createConnection();
 
	m_conn->onDataRead.connect(boost::bind(&TwitterPlugin::_handleDataRead, this, _1));
 
	m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 
 
	tp = new ThreadPool(loop_, 10);
 
		
 
	tweet_timer = m_factories->getTimerFactory()->createTimer(90000);
 
	message_timer = m_factories->getTimerFactory()->createTimer(90000);
 
 
	tweet_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForTweets, this));
 
	message_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForDirectMessages, this));
 
 
	tweet_timer->start();
 
	message_timer->start();
 
	
 
	LOG4CXX_INFO(logger, "Starting the plugin.");
 
}
 
 
TwitterPlugin::~TwitterPlugin() 
 
{
 
	delete storagebackend;
 
	std::set<std::string>::iterator it;
 
	for(it = onlineUsers.begin() ; it != onlineUsers.end() ; it++) delete userdb[*it].sessions;
 
	delete tp;
 
}
 
 
// Send data to NetworkPlugin server
 
void TwitterPlugin::sendData(const std::string &string) 
 
{
 
	m_conn->write(Swift::createSafeByteArray(string));
 
}
 
 
// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class)
 
void TwitterPlugin::_handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) 
 
{
 
	if (m_firstPing) {
 
		m_firstPing = false;
 
		// Users can join the network without registering if we allow
 
		// one user to connect multiple IRC networks.
 
		NetworkPlugin::PluginConfig cfg;
 
		cfg.setNeedPassword(false);
 
		sendConfig(cfg);
 
	}
 
 
	std::string d(data->begin(), data->end());
 
	handleDataRead(d);
 
}
 
 
// User trying to login into his twitter account
 
void TwitterPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) 
 
{
 
	if(userdb.count(user) && (userdb[user].connectionState == NEW || 
 
										userdb[user].connectionState == CONNECTED || 
 
										userdb[user].connectionState == WAITING_FOR_PIN)) {
 
		LOG4CXX_INFO(logger, std::string("A session corresponding to ") + user + std::string(" is already active"))
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, std::string("Received login request for ") + user)	
 
	initUserSession(user, legacyName, password);
 
	handleConnected(user);
 
	
 
	LOG4CXX_INFO(logger, "SPECTRUM 1 USER? - " << (userdb[user].spectrum1User? "true" : "false")) 
 
	
 
	LOG4CXX_INFO(logger, user << ": Adding Buddy " << adminLegacyName << " " << adminAlias)
 
	handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
 
	userdb[user].nickName = "";
 
	
 
	LOG4CXX_INFO(logger, "Querying database for usersettings of " << user)
 
	std::string key, secret;
 
	getUserOAuthKeyAndSecret(user, key, secret);
 
 
	if(key == "" || secret == "") {			
 
		LOG4CXX_INFO(logger, "Intiating OAuth Flow for user " << user)
 
		setTwitterMode(user, 0);
 
		tp->runAsThread(new OAuthFlow(np, userdb[user].sessions, user, userdb[user].sessions->getTwitterUsername()));
 
	} else {
 
		LOG4CXX_INFO(logger, user << " is already registerd. Using the stored oauth key and secret")
 
		LOG4CXX_INFO(logger, key << " " << secret)	
 
		pinExchangeComplete(user, key, secret);
 
	}
 
}
 
 
// User logging out
 
void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) 
 
{
 
	if (userdb.count(user)) {
 
		delete userdb[user].sessions;
 
		userdb[user].sessions = NULL;
 
		userdb[user].connectionState = DISCONNECTED;
 
	}
 
 
	if(onlineUsers.count(user)) {
 
		onlineUsers.erase(user);
 
	}
 
}
 
 
// User joining a Chatroom
 
void TwitterPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password)
 
{
 
	if(room == adminChatRoom) {	
 
		LOG4CXX_INFO(logger, "Received Join Twitter room request for " << user)
 
 
		setTwitterMode(user, 2);
 
		handleParticipantChanged(user, adminNickName, room, 0, pbnetwork::STATUS_ONLINE);
 
		userdb[user].nickName = nickname;
 
		handleMessage(user, adminChatRoom, "Connected to Twitter room! Populating your followers list", adminNickName);
 
		tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
 
										 boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 
	} else {
 
		setTwitterMode(user, 0);
 
		LOG4CXX_ERROR(logger, "Couldn't connect to chatroom - " << room <<"! Try twitter-chatroom as the chatroom to access Twitter account")
 
		handleMessage(user, adminLegacyName, "Couldn't connect to chatroom! Try twitter-chatroom as the chatroom to access Twitter account");
 
	}	
 
}
 
 
// User leaving a Chatroom
 
void TwitterPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room)
 
{
 
	if(room == adminChatRoom && onlineUsers.count(user)) {
 
		LOG4CXX_INFO(logger, "Leaving chatroom! Switching back to default mode 0")
 
		setTwitterMode(user, 0);
 
		handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
 
	}
 
}
 
 
// Messages to be sent to Twitter 
 
void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &/*id*/) 
 
{
 
 
	LOG4CXX_INFO(logger, "Received " << user << " --> " << legacyName << " - " << message)
 
	
 
	if(legacyName == adminLegacyName || legacyName == adminChatRoom)  {
 
		std::string cmd = "", data = "";
 
	 
 
		/** Parsing the message - Assuming message format to be <cmd>[ ]*<data>**/	
 
		int i;
 
		for(i=0 ; i<message.size() && message[i] != ' '; i++) cmd += message[i];
 
		while(i<message.size() && message[i] == ' ') i++;
 
		data = message.substr(i);
 
		/***********************************************************************/
 
		
 
		if(cmd == "#pin") 
 
			tp->runAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data));
 
		else if(cmd == "#help") 
 
			tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2)));
 
		else if(cmd[0] == '@') {
 
			std::string username = cmd.substr(1); 
 
			tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data,
 
												     boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		}
 
		else if(cmd == "#status") 
 
			tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, data,
 
														boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
 
		else if(cmd == "#timeline") 
 
			tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, data, "",
 
														boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
 
		else if(cmd == "#friends") 
 
			tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
 
													   boost::bind(&TwitterPlugin::displayFriendlist, this, _1, _2, _3, _4)));
 
		else if(cmd == "#follow") 
 
			tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
 
													   boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
 
		else if(cmd == "#unfollow") 
 
			tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
 
													   boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
 
		else if(cmd == "#retweet") 
 
			tp->runAsThread(new RetweetRequest(userdb[user].sessions, user, data,
 
													   boost::bind(&TwitterPlugin::RetweetResponse, this, _1, _2)));
 
		else if(cmd == "#mode") {
 
			int m = 0;
 
			m = atoi(data.c_str());
 
			mode prevm = userdb[user].twitterMode;
 
 
			if((mode)m == userdb[user].twitterMode) return; //If same as current mode return
 
			if(m < 0 || m > 2) { // Invalid modes
 
				handleMessage(user, adminLegacyName, std::string("Error! Unknown mode ") + data + ". Allowed values 0,1,2." );
 
				return;
 
			}
 
 
			setTwitterMode(user, m);
 
			if((userdb[user].twitterMode == SINGLECONTACT || userdb[user].twitterMode == CHATROOM) && prevm == MULTIPLECONTACT) clearRoster(user);
 
			else if(userdb[user].twitterMode == MULTIPLECONTACT) 
 
				tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
								std::string("Changed mode to ") + data, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
 
			LOG4CXX_INFO(logger, user << ": Changed mode to " << data  << " <" << (userdb[user].twitterMode == CHATROOM ? adminNickName : "") << ">" )
 
		}
 
 
		else if(userdb[user].twitterMode == CHATROOM) {
 
			std::string buddy = message.substr(0, message.find(":"));
 
			if(userdb[user].buddies.count(buddy) == 0) {
 
				tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, message,
 
														boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
 
			} else {
 
				data = message.substr(message.find(":")+1); //Can parse better??:P
 
				tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, data,
 
												 		 boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
			}
 
		}
 
		else handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
				                 "Unknown command! Type #help for a list of available commands.", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} 
 
 
	else {	
 
		std::string buddy = legacyName;
 
		if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1);
 
		if(legacyName != "twitter") {
 
			tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message,
 
												 boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		}
 
	}
 
}
 
 
void TwitterPlugin::handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) 
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 
 
	LOG4CXX_INFO(logger, user << " - Adding Twitter contact " << buddyName)
 
	tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, buddyName, 
 
											boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
 
}
 
 
void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) 
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, user << " - Removing Twitter contact " << buddyName)
 
	tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, buddyName, 
 
											 boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
 
}
 
 
void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id)
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << userdb[user].buddiesInfo[legacyName].getProfileImgURL())
 
 
	if(getTwitterMode(user) != SINGLECONTACT && userdb[user].buddies.count(legacyName) 
 
		&& userdb[user].buddiesInfo[legacyName].getProfileImgURL().length()) {
 
		if(userdb[user].buddiesImgs.count(legacyName) == 0) {
 
			tp->runAsThread(new ProfileImageRequest(config, user, legacyName, userdb[user].buddiesInfo[legacyName].getProfileImgURL(), id,
 
													boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5)));
 
		}
 
		handleVCard(user, id, legacyName, legacyName, "", userdb[user].buddiesImgs[legacyName]);
 
	}
 
}
 
 
void TwitterPlugin::pollForTweets()
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 
	std::set<std::string>::iterator it = onlineUsers.begin();
 
	while(it != onlineUsers.end()) {
 
		std::string user = *it;
 
		tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user),
 
											boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
 
		it++;
 
	}
 
	tweet_timer->start();
 
}
 
 
void TwitterPlugin::pollForDirectMessages()
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 
	std::set<std::string>::iterator it = onlineUsers.begin();
 
	while(it != onlineUsers.end()) {
 
		std::string user = *it;
 
		tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, "", getMostRecentDMIDUnsafe(user),
 
											boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		it++;
 
	}
 
	message_timer->start();
 
}
 
 
 
bool TwitterPlugin::getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 
 
	key="", secret=""; int type;
 
	storagebackend->getUserSetting((long)info.id, OAUTH_KEY, type, key);
 
	storagebackend->getUserSetting((long)info.id, OAUTH_SECRET, type, secret);
 
	return true;
 
}
 
 
bool TwitterPlugin::checkSpectrum1User(const std::string user) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 
 
	std::string first_synchronization_done = "";
 
	int type;
 
	storagebackend->getUserSetting((long)info.id, "first_synchronization_done", type, first_synchronization_done);
 
 
	LOG4CXX_INFO(logger, "first_synchronization_done: " << first_synchronization_done)
 
 
	if(first_synchronization_done.length()) return true;
 
	return false;
 
}
 
 
int TwitterPlugin::getTwitterMode(const std::string user) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return -1;
 
	}
 
 
	int type; int m;
 
	std::string s_m;
 
	storagebackend->getUserSetting((long)info.id, MODE, type, s_m);
 
	if(s_m == "") {
 
		s_m  = "0";
 
		storagebackend->updateUserSetting((long)info.id, MODE, s_m);
 
	}
 
	m = atoi(s_m.c_str());
 
	return m;
 
}
 
 
bool TwitterPlugin::setTwitterMode(const std::string user, int m) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 
 
	if(m < 0 || m > 2) {
 
		LOG4CXX_ERROR(logger, "Unknown mode " << m <<". Using default mode 0")
 
		m = 0;
 
	}
 
 
	userdb[user].twitterMode = (mode)m;
 
 
	//int type;
 
	std::string s_m = std::string(1,m+'0');
 
	LOG4CXX_ERROR(logger, "Storing mode " << m <<" for user " << user)
 
	storagebackend->updateUserSetting((long)info.id, MODE, s_m);
 
	return true;
 
}
 
 
bool TwitterPlugin::storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret) 
 
{
 
 
	boost::mutex::scoped_lock lock(dblock);
 
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 
 
	storagebackend->updateUserSetting((long)info.id, OAUTH_KEY, OAuthKey);	
 
	storagebackend->updateUserSetting((long)info.id, OAUTH_SECRET, OAuthSecret);
 
	return true;
 
}
 
 
void TwitterPlugin::initUserSession(const std::string user, const std::string legacyName, const std::string password)
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 
 
	std::string username = legacyName;
 
	std::string passwd = password;
 
	LOG4CXX_INFO(logger, username + "  " + passwd)
 
 
	userdb[user].sessions = new twitCurl();	
 
	if(CONFIG_HAS_KEY(config,"proxy.server")) {			
 
		std::string ip = CONFIG_STRING(config,"proxy.server");
 
 
		std::ostringstream out; 
 
		out << CONFIG_INT(config,"proxy.port");
 
		std::string port = out.str();
 
 
		std::string puser = CONFIG_STRING(config,"proxy.user");
 
		std::string ppasswd = CONFIG_STRING(config,"proxy.password");
 
 
		LOG4CXX_INFO(logger, ip << " " << port << " " << puser << " " << ppasswd)
 
		
 
		if(ip != "localhost" && port != "0") {
 
			userdb[user].sessions->setProxyServerIp(ip);
 
			userdb[user].sessions->setProxyServerPort(port);
 
			userdb[user].sessions->setProxyUserName(puser);
 
			userdb[user].sessions->setProxyPassword(ppasswd);
 
		}
 
	}
 
 
	//Check if the user is spectrum1 user
 
	userdb[user].spectrum1User = checkSpectrum1User(user);
 
 
	userdb[user].connectionState = NEW;
 
	userdb[user].legacyName = username;	
 
	userdb[user].sessions->setTwitterUsername(username);
 
	userdb[user].sessions->setTwitterPassword(passwd);
 
 
	if(!userdb[user].spectrum1User) {
 
		userdb[user].sessions->getOAuth().setConsumerKey(consumerKey);
 
		userdb[user].sessions->getOAuth().setConsumerSecret(consumerSecret);
 
	} else {
 
		userdb[user].sessions->getOAuth().setConsumerKey(OLD_APP_KEY);
 
		userdb[user].sessions->getOAuth().setConsumerSecret(OLD_APP_SECRET);
 
	}
 
}
 
 
void TwitterPlugin::OAuthFlowComplete(const std::string user, twitCurl *obj) 
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
 
	delete userdb[user].sessions;
 
	userdb[user].sessions = obj->clone();	
 
	userdb[user].connectionState = WAITING_FOR_PIN;
 
}	
 
 
void TwitterPlugin::pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret) 
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
		
 
	userdb[user].sessions->getOAuth().setOAuthTokenKey( OAuthAccessTokenKey );
 
	userdb[user].sessions->getOAuth().setOAuthTokenSecret( OAuthAccessTokenSecret );
 
	userdb[user].connectionState = CONNECTED;
 
	userdb[user].twitterMode = (mode)getTwitterMode(user);
 
	
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
		tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 
	}
 
 
	onlineUsers.insert(user);
 
	userdb[user].mostRecentTweetID = "";
 
	userdb[user].mostRecentDirectMessageID = "";
 
}	
 
 
void TwitterPlugin::updateLastTweetID(const std::string user, const std::string ID)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	userdb[user].mostRecentTweetID = ID;
 
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return;
 
	}
 
 
	storagebackend->updateUserSetting((long)info.id, "twitter_last_tweet", ID);
 
}
 
 
std::string TwitterPlugin::getMostRecentTweetIDUnsafe(const std::string user)
 
{	
 
	std::string ID = "";
 
	if(onlineUsers.count(user)) {
 
		ID = userdb[user].mostRecentTweetID;
 
		if (ID.empty()) {
 
			int type;
 
			UserInfo info;
 
			if(storagebackend->getUser(user, info) == false) {
 
				LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
			}
 
			else {
 
				storagebackend->getUserSetting(info.id, "twitter_last_tweet", type, ID);
 
			}
 
		}
 
	}
 
	return ID;
 
}
 
 
std::string TwitterPlugin::getMostRecentTweetID(const std::string user)
 
{	
 
	boost::mutex::scoped_lock lock(userlock);
 
	return getMostRecentTweetIDUnsafe(user);
 
}
 
 
void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	userdb[user].mostRecentDirectMessageID = ID;
 
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return;
 
	}
 
 
	storagebackend->updateUserSetting((long)info.id, "twitter_last_dm", ID);
 
}
 
 
std::string TwitterPlugin::getMostRecentDMIDUnsafe(const std::string user) {
 
	std::string ID = "";
 
	if(onlineUsers.count(user)) {
 
		ID = userdb[user].mostRecentDirectMessageID;
 
		if (ID.empty()) {
 
			int type;
 
			UserInfo info;
 
			if(storagebackend->getUser(user, info) == false) {
 
				LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
			}
 
			else {
 
				storagebackend->getUserSetting(info.id, "twitter_last_dm", type, ID);
 
			}
 
		}
 
	}
 
	return ID;
 
}
 
 
std::string TwitterPlugin::getMostRecentDMID(const std::string user)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	return getMostRecentDMIDUnsafe(user);
 
}
 
 
/************************************** Twitter response functions **********************************/
 
void TwitterPlugin::statusUpdateResponse(std::string &user, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							"Status Update successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 
}
 
 
void TwitterPlugin::helpMessageResponse(std::string &user, std::string &msg)
 
{
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						msg, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
}
 
 
void TwitterPlugin::clearRoster(const std::string user)
 
{
 
	if(userdb[user].buddies.size() == 0) return;
 
	std::set<std::string>::iterator it = userdb[user].buddies.begin();
 
	while(it != userdb[user].buddies.end()) {
 
		handleBuddyRemoved(user, *it);
 
		it++;
 
	}
 
	userdb[user].buddies.clear();
 
}
 
 
void TwitterPlugin::populateRoster(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg) 
 
{
 
	if(errMsg.getMessage().length() == 0) 
 
	{
 
		for(int i=0 ; i<friends.size() ; i++) {
 
			userdb[user].buddies.insert(friends[i].getScreenName());
 
			userdb[user].buddiesInfo[friends[i].getScreenName()] = friends[i];
 
			userdb[user].buddiesImgs[friends[i].getScreenName()] = friendAvatars[i];
 
			
 
			if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
				std::string lastTweet = friends[i].getLastStatus().getTweet();
 
				//LOG4CXX_INFO(logger, user << " - " << SHA(friendAvatars[i]))
 
				handleBuddyChanged(user, friends[i].getScreenName(), friends[i].getUserName(), std::vector<std::string>(), 
 
#if HAVE_SWIFTEN_3
 
					pbnetwork::STATUS_ONLINE, lastTweet, Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(friendAvatars[i]))));
 
#else
 
								   pbnetwork::STATUS_ONLINE, lastTweet, SHA(friendAvatars[i]));
 
#endif
 
			}
 
			else if(userdb[user].twitterMode == CHATROOM)
 
				handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
			
 
			/*handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   	friends[i].getScreenName() + " - " + friends[i].getLastStatus().getTweet(), 
 
								userdb[user].twitterMode == CHATROOM ? adminNickName : "");*/
 
		}
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   std::string("Error populating roster - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 
 
	if(userdb[user].twitterMode == CHATROOM) handleParticipantChanged(user, userdb[user].nickName, adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
}
 
 
void TwitterPlugin::displayFriendlist(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length() == 0) 
 
	{
 
		std::string userlist = "\n***************USER LIST****************\n";
 
		for(int i=0 ; i < friends.size() ; i++) {
 
			userlist += " - " + friends[i].getUserName() + " (" + friends[i].getScreenName() + ")\n";
 
		}	
 
		userlist += "***************************************\n";
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							userlist, userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, 
 
							   errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	}
 
 
 
}
 
 
void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::vector<Status> &tweets , Error &errMsg)
 
{
 
	if(errMsg.getMessage().length() == 0) {
 
		std::map<std::string, int> lastTweet;
 
		std::map<std::string, int>::iterator it;
 
 
		for(int i = tweets.size() - 1 ; i >= 0 ; i--) {
 
			if(userdb[user].twitterMode != CHATROOM) {
 
				std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")\n";
 
				handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime(), true);
 
 
				std::string scrname = tweets[i].getUserData().getScreenName();
 
				if(lastTweet.count(scrname) == 0 || cmp(tweets[lastTweet[scrname]].getID(), tweets[i].getID()) <= 0) lastTweet[scrname] = i;
 
 
			} else {
 
				handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
									tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime(), true);
 
			}
 
		}
 
		
 
		if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
			//Set as status user's last tweet
 
			for(it=lastTweet.begin() ; it!=lastTweet.end() ; it++) {
 
				int t =  it->second;
 
				handleBuddyChanged(user, tweets[t].getUserData().getScreenName(), tweets[t].getUserData().getUserName(), 
 
								   std::vector<std::string>(), pbnetwork::STATUS_ONLINE, tweets[t].getTweet());
 
			}
 
		}
 
 
		if((userRequested == "" || userRequested == user) && tweets.size()) {
 
			std::string tweetID = getMostRecentTweetID(user);
 
			if(tweetID != tweets[0].getID()) updateLastTweetID(user, tweets[0].getID());
 
		}
 
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	}
 
}
 
 
void TwitterPlugin::directMessageResponse(std::string &user, std::string &username, std::vector<DirectMessage> &messages, Error &errMsg)
 
{
 
	if(errMsg.getCode() == "93") //Permission Denied
 
		return;
 
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
 
		if(username != "")
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						  std::string("Error while sending direct message! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		else
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						  std::string("Error while fetching direct messages! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	}
 
 
	if(username != "") {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						   "Message delivered!", userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
		return;
 
	}
 
	
 
	if(!messages.size()) return;
 
	
 
	if(userdb[user].twitterMode == SINGLECONTACT) {
 
 
		std::string msglist = "";
 
		std::string msgID = getMostRecentDMID(user);
 
		std::string maxID = msgID;
 
		
 
		for(int i=0 ; i < messages.size() ; i++) {
 
			if(cmp(msgID, messages[i].getID()) == -1) {
 
				msglist += " - " + messages[i].getSenderData().getScreenName() + ": " + messages[i].getMessage() + "\n";
 
				if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
 
			}
 
		}	
 
 
		if(msglist.length()) handleMessage(user, adminLegacyName, msglist, "");	
 
		updateLastDMID(user, maxID);
 
 
	} else {
 
		
 
		std::string msgID = getMostRecentDMID(user);
 
		std::string maxID = msgID;
 
 
		for(int i=0 ; i < messages.size() ; i++) {
 
			if(cmp(msgID, messages[i].getID()) == -1) {
 
				if(userdb[user].twitterMode == MULTIPLECONTACT)
 
					handleMessage(user, messages[i].getSenderData().getScreenName(), messages[i].getMessage(), "");
 
				else
 
					handleMessage(user, adminChatRoom, messages[i].getMessage() + " - <Direct Message>", messages[i].getSenderData().getScreenName());
 
				if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
 
			}
 
		}	
 
		
 
		if(maxID == getMostRecentDMID(user)) LOG4CXX_INFO(logger, "No new direct messages for " << user)
 
		updateLastDMID(user, maxID);
 
	}
 
}
 
 
void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	}
 
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						std::string("You are now following ") + frnd.getScreenName(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	
 
	userdb[user].buddies.insert(frnd.getScreenName());
 
	userdb[user].buddiesInfo[frnd.getScreenName()] = frnd;
 
	userdb[user].buddiesImgs[frnd.getScreenName()] = img;
 
	
 
	LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL())
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
#if HAVE_SWIFTEN_3
 
		handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(img))));
 
#else
 
		handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", SHA(img));
 
#endif
 
	} else if(userdb[user].twitterMode == CHATROOM) {
 
		handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
	}
 
}
 
 
void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, 
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	} 
 
	
 
	LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL())
 
	userdb[user].buddies.erase(frnd.getScreenName());
 
	
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						std::string("You are not following ") + frnd.getScreenName() + " anymore", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	
 
	if (userdb[user].twitterMode == CHATROOM) {
 
		handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE);
 
	}
 
	
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
		handleBuddyRemoved(user, frnd.getScreenName());
 
	} 
 
}
 
 
 
void TwitterPlugin::RetweetResponse(std::string &user, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							"Retweet successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 
}
 
 
void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy)
 
		handleVCard(user, reqID, buddy, buddy, "", img);
 
	}
 
}
 
#include "TwitterPlugin.h"
 
#include "Requests/StatusUpdateRequest.h"
 
#include "Requests/DirectMessageRequest.h"
 
#include "Requests/TimelineRequest.h"
 
#include "Requests/FetchFriends.h"
 
#include "Requests/HelpMessageRequest.h"
 
#include "Requests/PINExchangeProcess.h"
 
#include "Requests/OAuthFlow.h"
 
#include "Requests/CreateFriendRequest.h"
 
#include "Requests/DestroyFriendRequest.h"
 
#include "Requests/RetweetRequest.h"
 
#include "Requests/ProfileImageRequest.h"
 
#include "Swiften/StringCodecs/Hexify.h"
 

	
 
DEFINE_LOGGER(logger, "Twitter Backend");
 

	
 
TwitterPlugin *np = NULL;
 
Swift::SimpleEventLoop *loop_; // Event Loop
 

	
 
const std::string OLD_APP_KEY = "PCWAdQpyyR12ezp2fVwEhw";
 
const std::string OLD_APP_SECRET = "EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg";
 

	
 
#define abs(x) ((x)<0?-(x):(x))
 
#define SHA(x) (Swift::Hexify::hexify(Swift::SHA1::getHash(Swift::createByteArray((x)))))
 

	
 
//Compares two +ve intergers 'a' and 'b' represented as strings 
 
static int cmp(std::string a, std::string b)
 
{
 
	int diff = abs((int)a.size() - (int)b.size());
 
	if(a.size() < b.size()) a = std::string(diff,'0') + a;
 
	else b = std::string(diff,'0') + b;
 
	
 
	if(a == b) return 0;
 
	if(a < b) return -1;
 
	return 1;
 
}
 

	
 

	
 
TwitterPlugin::TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port) : NetworkPlugin() 
 
{
 
	this->config = config;
 
	this->storagebackend = storagebackend;
 
	this->m_firstPing = true;
 

	
 
	if (CONFIG_HAS_KEY(config, "twitter.consumer_key") == false) {
 
		consumerKey = "5mFePMiJi0KpeURONkelg";
 
	}
 
	else {
 
		consumerKey = CONFIG_STRING(config, "twitter.consumer_key");
 
	}
 
	if (CONFIG_HAS_KEY(config, "twitter.consumer_secret") == false) {
 
		consumerSecret = "YFZCDJwRhbkccXEnaYr1waCQejTJcOY8F7l5Wim3FA";
 
	}
 
	else {
 
		consumerSecret = CONFIG_STRING(config, "twitter.consumer_secret");
 
	}
 

	
 
	if (consumerSecret.empty() || consumerKey.empty()) {
 
		LOG4CXX_ERROR(logger, "Consumer key and Consumer secret can't be empty.");
 
		exit(1);
 
	}
 

	
 
	adminLegacyName = "twitter.com"; 
 
	adminChatRoom = "#twitter"; 
 
	adminNickName = "twitter"; 
 
	adminAlias = "twitter";
 

	
 
	OAUTH_KEY = "twitter_oauth_token";
 
	OAUTH_SECRET = "twitter_oauth_secret";
 
	MODE = "mode";
 

	
 
	m_factories = new Swift::BoostNetworkFactories(loop);
 
	m_conn = m_factories->getConnectionFactory()->createConnection();
 
	m_conn->onDataRead.connect(boost::bind(&TwitterPlugin::_handleDataRead, this, _1));
 
	m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
 

	
 
	tp = new ThreadPool(loop_, 10);
 
		
 
	tweet_timer = m_factories->getTimerFactory()->createTimer(90000);
 
	message_timer = m_factories->getTimerFactory()->createTimer(90000);
 

	
 
	tweet_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForTweets, this));
 
	message_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForDirectMessages, this));
 

	
 
	tweet_timer->start();
 
	message_timer->start();
 
	
 
	LOG4CXX_INFO(logger, "Starting the plugin.");
 
}
 

	
 
TwitterPlugin::~TwitterPlugin() 
 
{
 
	delete storagebackend;
 
	std::set<std::string>::iterator it;
 
	for(it = onlineUsers.begin() ; it != onlineUsers.end() ; it++) delete userdb[*it].sessions;
 
	delete tp;
 
}
 

	
 
// Send data to NetworkPlugin server
 
void TwitterPlugin::sendData(const std::string &string) 
 
{
 
	m_conn->write(Swift::createSafeByteArray(string));
 
}
 

	
 
// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class)
 
void TwitterPlugin::_handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) 
 
{
 
	if (m_firstPing) {
 
		m_firstPing = false;
 
		// Users can join the network without registering if we allow
 
		// one user to connect multiple IRC networks.
 
		NetworkPlugin::PluginConfig cfg;
 
		cfg.setNeedPassword(false);
 
		sendConfig(cfg);
 
	}
 

	
 
	std::string d(data->begin(), data->end());
 
	handleDataRead(d);
 
}
 

	
 
// User trying to login into his twitter account
 
void TwitterPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) 
 
{
 
	if(userdb.count(user) && (userdb[user].connectionState == NEW || 
 
										userdb[user].connectionState == CONNECTED || 
 
										userdb[user].connectionState == WAITING_FOR_PIN)) {
 
		LOG4CXX_INFO(logger, std::string("A session corresponding to ") + user + std::string(" is already active"))
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, std::string("Received login request for ") + user)	
 
	initUserSession(user, legacyName, password);
 
	handleConnected(user);
 
	
 
	LOG4CXX_INFO(logger, "SPECTRUM 1 USER? - " << (userdb[user].spectrum1User? "true" : "false")) 
 
	
 
	LOG4CXX_INFO(logger, user << ": Adding Buddy " << adminLegacyName << " " << adminAlias)
 
	handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
 
	userdb[user].nickName = "";
 
	
 
	LOG4CXX_INFO(logger, "Querying database for usersettings of " << user)
 
	std::string key, secret;
 
	getUserOAuthKeyAndSecret(user, key, secret);
 

	
 
	if(key == "" || secret == "") {			
 
		LOG4CXX_INFO(logger, "Intiating OAuth Flow for user " << user)
 
		setTwitterMode(user, 0);
 
		tp->runAsThread(new OAuthFlow(np, userdb[user].sessions, user, userdb[user].sessions->getTwitterUsername()));
 
	} else {
 
		LOG4CXX_INFO(logger, user << " is already registerd. Using the stored oauth key and secret")
 
		LOG4CXX_INFO(logger, key << " " << secret)	
 
		pinExchangeComplete(user, key, secret);
 
	}
 
}
 

	
 
// User logging out
 
void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) 
 
{
 
	if (userdb.count(user)) {
 
		delete userdb[user].sessions;
 
		userdb[user].sessions = NULL;
 
		userdb[user].connectionState = DISCONNECTED;
 
	}
 

	
 
	if(onlineUsers.count(user)) {
 
		onlineUsers.erase(user);
 
	}
 
}
 

	
 
// User joining a Chatroom
 
void TwitterPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password)
 
{
 
	if(room == adminChatRoom) {	
 
		LOG4CXX_INFO(logger, "Received Join Twitter room request for " << user)
 

	
 
		setTwitterMode(user, 2);
 
		handleParticipantChanged(user, adminNickName, room, 0, pbnetwork::STATUS_ONLINE);
 
		userdb[user].nickName = nickname;
 
		handleMessage(user, adminChatRoom, "Connected to Twitter room! Populating your followers list", adminNickName);
 
		tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
 
										 boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 
	} else {
 
		setTwitterMode(user, 0);
 
		LOG4CXX_ERROR(logger, "Couldn't connect to chatroom - " << room <<"! Try twitter-chatroom as the chatroom to access Twitter account")
 
		handleMessage(user, adminLegacyName, "Couldn't connect to chatroom! Try twitter-chatroom as the chatroom to access Twitter account");
 
	}	
 
}
 

	
 
// User leaving a Chatroom
 
void TwitterPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room)
 
{
 
	if(room == adminChatRoom && onlineUsers.count(user)) {
 
		LOG4CXX_INFO(logger, "Leaving chatroom! Switching back to default mode 0")
 
		setTwitterMode(user, 0);
 
		handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
 
	}
 
}
 

	
 
// Messages to be sent to Twitter 
 
void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &/*id*/) 
 
{
 

	
 
	LOG4CXX_INFO(logger, "Received " << user << " --> " << legacyName << " - " << message)
 
	
 
	if(legacyName == adminLegacyName || legacyName == adminChatRoom)  {
 
		std::string cmd = "", data = "";
 
	 
 
		/** Parsing the message - Assuming message format to be <cmd>[ ]*<data>**/	
 
		int i;
 
		for(i=0 ; i<message.size() && message[i] != ' '; i++) cmd += message[i];
 
		while(i<message.size() && message[i] == ' ') i++;
 
		data = message.substr(i);
 
		/***********************************************************************/
 
		
 
		if(cmd == "#pin") 
 
			tp->runAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data));
 
		else if(cmd == "#help") 
 
			tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2)));
 
		else if(cmd[0] == '@') {
 
			std::string username = cmd.substr(1); 
 
			tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data,
 
												     boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		}
 
		else if(cmd == "#status") 
 
			tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, data,
 
														boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
 
		else if(cmd == "#timeline") 
 
			tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, data, "",
 
														boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
 
		else if(cmd == "#friends") 
 
			tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
 
													   boost::bind(&TwitterPlugin::displayFriendlist, this, _1, _2, _3, _4)));
 
		else if(cmd == "#follow") 
 
			tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
 
													   boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
 
		else if(cmd == "#unfollow") 
 
			tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
 
													   boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
 
		else if(cmd == "#retweet") 
 
			tp->runAsThread(new RetweetRequest(userdb[user].sessions, user, data,
 
													   boost::bind(&TwitterPlugin::RetweetResponse, this, _1, _2)));
 
		else if(cmd == "#mode") {
 
			int m = 0;
 
			m = atoi(data.c_str());
 
			mode prevm = userdb[user].twitterMode;
 

	
 
			if((mode)m == userdb[user].twitterMode) return; //If same as current mode return
 
			if(m < 0 || m > 2) { // Invalid modes
 
				handleMessage(user, adminLegacyName, std::string("Error! Unknown mode ") + data + ". Allowed values 0,1,2." );
 
				return;
 
			}
 

	
 
			setTwitterMode(user, m);
 
			if((userdb[user].twitterMode == SINGLECONTACT || userdb[user].twitterMode == CHATROOM) && prevm == MULTIPLECONTACT) clearRoster(user);
 
			else if(userdb[user].twitterMode == MULTIPLECONTACT) 
 
				tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 

	
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
								std::string("Changed mode to ") + data, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 

	
 
			LOG4CXX_INFO(logger, user << ": Changed mode to " << data  << " <" << (userdb[user].twitterMode == CHATROOM ? adminNickName : "") << ">" )
 
		}
 

	
 
		else if(userdb[user].twitterMode == CHATROOM) {
 
			std::string buddy = message.substr(0, message.find(":"));
 
			if(userdb[user].buddies.count(buddy) == 0) {
 
				tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, message,
 
														boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
 
			} else {
 
				data = message.substr(message.find(":")+1); //Can parse better??:P
 
				tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, data,
 
												 		 boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
			}
 
		}
 
		else handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
				                 "Unknown command! Type #help for a list of available commands.", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} 
 

	
 
	else {	
 
		std::string buddy = legacyName;
 
		if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1);
 
		if(legacyName != "twitter") {
 
			tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message,
 
												 boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		}
 
	}
 
}
 

	
 
void TwitterPlugin::handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) 
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, user << " - Adding Twitter contact " << buddyName)
 
	tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, buddyName, 
 
											boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
 
}
 

	
 
void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) 
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, user << " - Removing Twitter contact " << buddyName)
 
	tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, buddyName, 
 
											 boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
 
}
 

	
 
void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id)
 
{
 
	if(userdb[user].connectionState != CONNECTED) {
 
		LOG4CXX_ERROR(logger, user << " is not connected to twitter!")
 
		return;
 
	}
 
	
 
	LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << userdb[user].buddiesInfo[legacyName].getProfileImgURL())
 

	
 
	if(getTwitterMode(user) != SINGLECONTACT && userdb[user].buddies.count(legacyName) 
 
		&& userdb[user].buddiesInfo[legacyName].getProfileImgURL().length()) {
 
		if(userdb[user].buddiesImgs.count(legacyName) == 0) {
 
			tp->runAsThread(new ProfileImageRequest(config, user, legacyName, userdb[user].buddiesInfo[legacyName].getProfileImgURL(), id,
 
													boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5)));
 
		}
 
		handleVCard(user, id, legacyName, legacyName, "", userdb[user].buddiesImgs[legacyName]);
 
	}
 
}
 

	
 
void TwitterPlugin::pollForTweets()
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 
	std::set<std::string>::iterator it = onlineUsers.begin();
 
	while(it != onlineUsers.end()) {
 
		std::string user = *it;
 
		tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user),
 
											boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
 
		it++;
 
	}
 
	tweet_timer->start();
 
}
 

	
 
void TwitterPlugin::pollForDirectMessages()
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 
	std::set<std::string>::iterator it = onlineUsers.begin();
 
	while(it != onlineUsers.end()) {
 
		std::string user = *it;
 
		tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, "", getMostRecentDMIDUnsafe(user),
 
											boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
 
		it++;
 
	}
 
	message_timer->start();
 
}
 

	
 

	
 
bool TwitterPlugin::getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 

	
 
	key="", secret=""; int type;
 
	storagebackend->getUserSetting((long)info.id, OAUTH_KEY, type, key);
 
	storagebackend->getUserSetting((long)info.id, OAUTH_SECRET, type, secret);
 
	return true;
 
}
 

	
 
bool TwitterPlugin::checkSpectrum1User(const std::string user) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 

	
 
	std::string first_synchronization_done = "";
 
	int type;
 
	storagebackend->getUserSetting((long)info.id, "first_synchronization_done", type, first_synchronization_done);
 

	
 
	LOG4CXX_INFO(logger, "first_synchronization_done: " << first_synchronization_done)
 

	
 
	if(first_synchronization_done.length()) return true;
 
	return false;
 
}
 

	
 
int TwitterPlugin::getTwitterMode(const std::string user) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return -1;
 
	}
 

	
 
	int type; int m;
 
	std::string s_m;
 
	storagebackend->getUserSetting((long)info.id, MODE, type, s_m);
 
	if(s_m == "") {
 
		s_m  = "0";
 
		storagebackend->updateUserSetting((long)info.id, MODE, s_m);
 
	}
 
	m = atoi(s_m.c_str());
 
	return m;
 
}
 

	
 
bool TwitterPlugin::setTwitterMode(const std::string user, int m) 
 
{
 
	boost::mutex::scoped_lock lock(dblock);
 
	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 

	
 
	if(m < 0 || m > 2) {
 
		LOG4CXX_ERROR(logger, "Unknown mode " << m <<". Using default mode 0")
 
		m = 0;
 
	}
 

	
 
	userdb[user].twitterMode = (mode)m;
 

	
 
	//int type;
 
	std::string s_m = std::string(1,m+'0');
 
	LOG4CXX_ERROR(logger, "Storing mode " << m <<" for user " << user)
 
	storagebackend->updateUserSetting((long)info.id, MODE, s_m);
 
	return true;
 
}
 

	
 
bool TwitterPlugin::storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret) 
 
{
 

	
 
	boost::mutex::scoped_lock lock(dblock);
 

	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return false;
 
	}
 

	
 
	storagebackend->updateUserSetting((long)info.id, OAUTH_KEY, OAuthKey);	
 
	storagebackend->updateUserSetting((long)info.id, OAUTH_SECRET, OAuthSecret);
 
	return true;
 
}
 

	
 
void TwitterPlugin::initUserSession(const std::string user, const std::string legacyName, const std::string password)
 
{
 
	boost::mutex::scoped_lock lock(userlock);
 

	
 
	std::string username = legacyName;
 
	std::string passwd = password;
 
	LOG4CXX_INFO(logger, username + "  " + passwd)
 

	
 
	userdb[user].sessions = new twitCurl();	
 
	if(CONFIG_HAS_KEY(config,"proxy.server")) {			
 
		std::string ip = CONFIG_STRING(config,"proxy.server");
 

	
 
		std::ostringstream out; 
 
		out << CONFIG_INT(config,"proxy.port");
 
		std::string port = out.str();
 

	
 
		std::string puser = CONFIG_STRING(config,"proxy.user");
 
		std::string ppasswd = CONFIG_STRING(config,"proxy.password");
 

	
 
		LOG4CXX_INFO(logger, ip << " " << port << " " << puser << " " << ppasswd)
 
		
 
		if(ip != "localhost" && port != "0") {
 
			userdb[user].sessions->setProxyServerIp(ip);
 
			userdb[user].sessions->setProxyServerPort(port);
 
			userdb[user].sessions->setProxyUserName(puser);
 
			userdb[user].sessions->setProxyPassword(ppasswd);
 
		}
 
	}
 

	
 
	//Check if the user is spectrum1 user
 
	userdb[user].spectrum1User = checkSpectrum1User(user);
 

	
 
	userdb[user].connectionState = NEW;
 
	userdb[user].legacyName = username;	
 
	userdb[user].sessions->setTwitterUsername(username);
 
	userdb[user].sessions->setTwitterPassword(passwd);
 

	
 
	if(!userdb[user].spectrum1User) {
 
		userdb[user].sessions->getOAuth().setConsumerKey(consumerKey);
 
		userdb[user].sessions->getOAuth().setConsumerSecret(consumerSecret);
 
	} else {
 
		userdb[user].sessions->getOAuth().setConsumerKey(OLD_APP_KEY);
 
		userdb[user].sessions->getOAuth().setConsumerSecret(OLD_APP_SECRET);
 
	}
 
}
 

	
 
void TwitterPlugin::OAuthFlowComplete(const std::string user, twitCurl *obj) 
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 

	
 
	delete userdb[user].sessions;
 
	userdb[user].sessions = obj->clone();	
 
	userdb[user].connectionState = WAITING_FOR_PIN;
 
}	
 

	
 
void TwitterPlugin::pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret) 
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
		
 
	userdb[user].sessions->getOAuth().setOAuthTokenKey( OAuthAccessTokenKey );
 
	userdb[user].sessions->getOAuth().setOAuthTokenSecret( OAuthAccessTokenSecret );
 
	userdb[user].connectionState = CONNECTED;
 
	userdb[user].twitterMode = (mode)getTwitterMode(user);
 
	
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
		tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
 
	}
 

	
 
	onlineUsers.insert(user);
 
	userdb[user].mostRecentTweetID = "";
 
	userdb[user].mostRecentDirectMessageID = "";
 
}	
 

	
 
void TwitterPlugin::updateLastTweetID(const std::string user, const std::string ID)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	userdb[user].mostRecentTweetID = ID;
 

	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return;
 
	}
 

	
 
	storagebackend->updateUserSetting((long)info.id, "twitter_last_tweet", ID);
 
}
 

	
 
std::string TwitterPlugin::getMostRecentTweetIDUnsafe(const std::string user)
 
{	
 
	std::string ID = "";
 
	if(onlineUsers.count(user)) {
 
		ID = userdb[user].mostRecentTweetID;
 
		if (ID.empty()) {
 
			int type;
 
			UserInfo info;
 
			if(storagebackend->getUser(user, info) == false) {
 
				LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
			}
 
			else {
 
				storagebackend->getUserSetting(info.id, "twitter_last_tweet", type, ID);
 
			}
 
		}
 
	}
 
	return ID;
 
}
 

	
 
std::string TwitterPlugin::getMostRecentTweetID(const std::string user)
 
{	
 
	boost::mutex::scoped_lock lock(userlock);
 
	return getMostRecentTweetIDUnsafe(user);
 
}
 

	
 
void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	userdb[user].mostRecentDirectMessageID = ID;
 

	
 
	UserInfo info;
 
	if(storagebackend->getUser(user, info) == false) {
 
		LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
		return;
 
	}
 

	
 
	storagebackend->updateUserSetting((long)info.id, "twitter_last_dm", ID);
 
}
 

	
 
std::string TwitterPlugin::getMostRecentDMIDUnsafe(const std::string user) {
 
	std::string ID = "";
 
	if(onlineUsers.count(user)) {
 
		ID = userdb[user].mostRecentDirectMessageID;
 
		if (ID.empty()) {
 
			int type;
 
			UserInfo info;
 
			if(storagebackend->getUser(user, info) == false) {
 
				LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!")
 
			}
 
			else {
 
				storagebackend->getUserSetting(info.id, "twitter_last_dm", type, ID);
 
			}
 
		}
 
	}
 
	return ID;
 
}
 

	
 
std::string TwitterPlugin::getMostRecentDMID(const std::string user)
 
{
 
	boost::mutex::scoped_lock lock(userlock);	
 
	return getMostRecentDMIDUnsafe(user);
 
}
 

	
 
/************************************** Twitter response functions **********************************/
 
void TwitterPlugin::statusUpdateResponse(std::string &user, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							"Status Update successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 
}
 

	
 
void TwitterPlugin::helpMessageResponse(std::string &user, std::string &msg)
 
{
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						msg, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
}
 

	
 
void TwitterPlugin::clearRoster(const std::string user)
 
{
 
	if(userdb[user].buddies.size() == 0) return;
 
	std::set<std::string>::iterator it = userdb[user].buddies.begin();
 
	while(it != userdb[user].buddies.end()) {
 
		handleBuddyRemoved(user, *it);
 
		it++;
 
	}
 
	userdb[user].buddies.clear();
 
}
 

	
 
void TwitterPlugin::populateRoster(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg) 
 
{
 
	if(errMsg.getMessage().length() == 0) 
 
	{
 
		for(int i=0 ; i<friends.size() ; i++) {
 
			userdb[user].buddies.insert(friends[i].getScreenName());
 
			userdb[user].buddiesInfo[friends[i].getScreenName()] = friends[i];
 
			userdb[user].buddiesImgs[friends[i].getScreenName()] = friendAvatars[i];
 
			
 
			if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
				std::string lastTweet = friends[i].getLastStatus().getTweet();
 
				//LOG4CXX_INFO(logger, user << " - " << SHA(friendAvatars[i]))
 
				handleBuddyChanged(user, friends[i].getScreenName(), friends[i].getUserName(), std::vector<std::string>(), 
 
#if HAVE_SWIFTEN_3
 
					pbnetwork::STATUS_ONLINE, lastTweet, Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(friendAvatars[i]))));
 
#else
 
								   pbnetwork::STATUS_ONLINE, lastTweet, SHA(friendAvatars[i]));
 
#endif
 
			}
 
			else if(userdb[user].twitterMode == CHATROOM)
 
				handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
			
 
			/*handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   	friends[i].getScreenName() + " - " + friends[i].getLastStatus().getTweet(), 
 
								userdb[user].twitterMode == CHATROOM ? adminNickName : "");*/
 
		}
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   std::string("Error populating roster - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 

	
 
	if(userdb[user].twitterMode == CHATROOM) handleParticipantChanged(user, userdb[user].nickName, adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
}
 

	
 
void TwitterPlugin::displayFriendlist(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length() == 0) 
 
	{
 
		std::string userlist = "\n***************USER LIST****************\n";
 
		for(int i=0 ; i < friends.size() ; i++) {
 
			userlist += " - " + friends[i].getUserName() + " (" + friends[i].getScreenName() + ")\n";
 
		}	
 
		userlist += "***************************************\n";
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							userlist, userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, 
 
							   errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	}
 
 
 
}
 

	
 
void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::vector<Status> &tweets , Error &errMsg)
 
{
 
	if(errMsg.getMessage().length() == 0) {
 
		std::map<std::string, int> lastTweet;
 
		std::map<std::string, int>::iterator it;
 

	
 
		for(int i = tweets.size() - 1 ; i >= 0 ; i--) {
 
			if(userdb[user].twitterMode != CHATROOM) {
 
				std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")\n";
 
				handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime(), true);
 

	
 
				std::string scrname = tweets[i].getUserData().getScreenName();
 
				if(lastTweet.count(scrname) == 0 || cmp(tweets[lastTweet[scrname]].getID(), tweets[i].getID()) <= 0) lastTweet[scrname] = i;
 

	
 
			} else {
 
				handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
									tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime(), true);
 
			}
 
		}
 
		
 
		if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
			//Set as status user's last tweet
 
			for(it=lastTweet.begin() ; it!=lastTweet.end() ; it++) {
 
				int t =  it->second;
 
				handleBuddyChanged(user, tweets[t].getUserData().getScreenName(), tweets[t].getUserData().getUserName(), 
 
								   std::vector<std::string>(), pbnetwork::STATUS_ONLINE, tweets[t].getTweet());
 
			}
 
		}
 

	
 
		if((userRequested == "" || userRequested == user) && tweets.size()) {
 
			std::string tweetID = getMostRecentTweetID(user);
 
			if(tweetID != tweets[0].getID()) updateLastTweetID(user, tweets[0].getID());
 
		}
 

	
 
	} else {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							   errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
	}
 
}
 

	
 
void TwitterPlugin::directMessageResponse(std::string &user, std::string &username, std::vector<DirectMessage> &messages, Error &errMsg)
 
{
 
	if(errMsg.getCode() == "93") //Permission Denied
 
		return;
 

	
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 

	
 
		if(username != "")
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						  std::string("Error while sending direct message! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		else
 
			handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						  std::string("Error while fetching direct messages! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	}
 

	
 
	if(username != "") {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						   "Message delivered!", userdb[user].twitterMode == CHATROOM ? adminNickName : "");	
 
		return;
 
	}
 
	
 
	if(!messages.size()) return;
 
	
 
	if(userdb[user].twitterMode == SINGLECONTACT) {
 

	
 
		std::string msglist = "";
 
		std::string msgID = getMostRecentDMID(user);
 
		std::string maxID = msgID;
 
		
 
		for(int i=0 ; i < messages.size() ; i++) {
 
			if(cmp(msgID, messages[i].getID()) == -1) {
 
				msglist += " - " + messages[i].getSenderData().getScreenName() + ": " + messages[i].getMessage() + "\n";
 
				if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
 
			}
 
		}	
 

	
 
		if(msglist.length()) handleMessage(user, adminLegacyName, msglist, "");	
 
		updateLastDMID(user, maxID);
 

	
 
	} else {
 
		
 
		std::string msgID = getMostRecentDMID(user);
 
		std::string maxID = msgID;
 

	
 
		for(int i=0 ; i < messages.size() ; i++) {
 
			if(cmp(msgID, messages[i].getID()) == -1) {
 
				if(userdb[user].twitterMode == MULTIPLECONTACT)
 
					handleMessage(user, messages[i].getSenderData().getScreenName(), messages[i].getMessage(), "");
 
				else
 
					handleMessage(user, adminChatRoom, messages[i].getMessage() + " - <Direct Message>", messages[i].getSenderData().getScreenName());
 
				if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
 
			}
 
		}	
 
		
 
		if(maxID == getMostRecentDMID(user)) LOG4CXX_INFO(logger, "No new direct messages for " << user)
 
		updateLastDMID(user, maxID);
 
	}
 
}
 

	
 
void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	}
 

	
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						std::string("You are now following ") + frnd.getScreenName(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	
 
	userdb[user].buddies.insert(frnd.getScreenName());
 
	userdb[user].buddiesInfo[frnd.getScreenName()] = frnd;
 
	userdb[user].buddiesImgs[frnd.getScreenName()] = img;
 
	
 
	LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL())
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
#if HAVE_SWIFTEN_3
 
		handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(img))));
 
#else
 
		handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", SHA(img));
 
#endif
 
	} else if(userdb[user].twitterMode == CHATROOM) {
 
		handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
 
	}
 
}
 

	
 
void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName, 
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
		return;
 
	} 
 
	
 
	LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL())
 
	userdb[user].buddies.erase(frnd.getScreenName());
 
	
 
	handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
						std::string("You are not following ") + frnd.getScreenName() + " anymore", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	
 
	if (userdb[user].twitterMode == CHATROOM) {
 
		handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE);
 
	}
 
	
 
	if(userdb[user].twitterMode == MULTIPLECONTACT) {
 
		handleBuddyRemoved(user, frnd.getScreenName());
 
	} 
 
}
 

	
 

	
 
void TwitterPlugin::RetweetResponse(std::string &user, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							"Retweet successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	}
 
}
 

	
 
void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg)
 
{
 
	if(errMsg.getMessage().length()) {
 
		if (errMsg.isCurlError()) {
 
			handleDisconnected(user, 3, errMsg.getMessage());
 
			return;
 
		}
 
		handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
 
							errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
 
	} else {
 
		LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy)
 
		handleVCard(user, reqID, buddy, buddy, "", img);
 
	}
 
}
backends/twitter/TwitterPlugin.h
Show inline comments
 
#ifndef TWITTER_PLUGIN
 
#define TWITTER_PLUGIN
 
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 
#include "transport/sqlite3backend.h"
 
#include "transport/mysqlbackend.h"
 
#include "transport/pqxxbackend.h"
 
#include "transport/storagebackend.h"
 
#include "transport/threadpool.h"
 
 
#include "Swiften/Swiften.h"
 
#ifndef _WIN32
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 
#include <boost/algorithm/string.hpp>
 
#include <boost/signal.hpp>
 
#include <boost/thread.hpp>
 
#include <boost/thread/mutex.hpp>
 
 
#include "twitcurl.h"
 
#include "TwitterResponseParser.h"
 
 
#include <iostream>
 
#include <sstream>
 
#include <map>
 
#include <vector>
 
#include <queue>
 
#include <set>
 
#include <cstdio>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Crypto/CryptoProvider.h>
 
#include <Swiften/Crypto/PlatformCryptoProvider.h>
 
#else
 
#include "Swiften/StringCodecs/SHA1.h"
 
#endif
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 
 
#define STR(x) (std::string("(") + x.from + ", " + x.to + ", " + x.message + ")")
 
 
class TwitterPlugin;
 
extern TwitterPlugin *np;
 
extern Swift::SimpleEventLoop *loop_; // Event Loop
 
 
 
class TwitterPlugin : public NetworkPlugin {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 
#if HAVE_SWIFTEN_3
 
		boost::shared_ptr<Swift::CryptoProvider> cryptoProvider;
 
#endif
 
		Swift::Timer::ref tweet_timer;
 
		Swift::Timer::ref message_timer;
 
		StorageBackend *storagebackend;
 
 
		TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port);
 
		~TwitterPlugin();
 
 
		// Send data to NetworkPlugin server
 
		void sendData(const std::string &string);
 
 
		// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class)
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data);
 
	
 
		// User trying to login into his twitter account
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password);
 
		
 
		// User logging out
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName);
 
		
 
		void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/);
 
 
		void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/);
 
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = "");
 
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups);
 
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups);
 
		
 
		void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/);
 
		
 
		void pollForTweets();
 
 
		void pollForDirectMessages();
 
		
 
		bool getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret);
 
		
 
		bool checkSpectrum1User(const std::string user);
 
		
 
		bool storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret);
 
		
 
		void initUserSession(const std::string user, const std::string legacyName, const std::string password);
 
		
 
		void OAuthFlowComplete(const std::string user, twitCurl *obj);
 
		
 
		void pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret);
 
		
 
		void updateLastTweetID(const std::string user, const std::string ID);
 
 
		std::string getMostRecentTweetID(const std::string user);
 
 
		void updateLastDMID(const std::string user, const std::string ID);
 
		
 
		std::string getMostRecentDMID(const std::string user);
 
 
		void clearRoster(const std::string user);
 
 
		int getTwitterMode(const std::string user);
 
 
		bool setTwitterMode(const std::string user, int m);
 
 
		/****************** Twitter response handlers **************************************/
 
		void statusUpdateResponse(std::string &user, Error &errMsg);
 
		
 
		void helpMessageResponse(std::string &user, std::string &msg);
 
		
 
		void populateRoster(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg);
 
		
 
		void displayFriendlist(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg);
 
		
 
		void displayTweets(std::string &user, std::string &userRequested, std::vector<Status> &tweets , Error &errMsg);
 
		
 
		void directMessageResponse(std::string &user, std::string &username, std::vector<DirectMessage> &messages, Error &errMsg);
 
		
 
		void createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg);
 
		
 
		void deleteFriendResponse(std::string &user, User &frnd, Error &errMsg);
 
		
 
		void RetweetResponse(std::string &user, Error &errMsg);
 
		
 
		void profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg);
 
		/***********************************************************************************/
 
 
	private:
 
		std::string getMostRecentTweetIDUnsafe(const std::string user);
 
		std::string getMostRecentDMIDUnsafe(const std::string user);
 
 
		enum status {NEW, WAITING_FOR_PIN, CONNECTED, DISCONNECTED};
 
		enum mode {SINGLECONTACT, MULTIPLECONTACT, CHATROOM};
 
 
		Config *config;
 
		std::string adminLegacyName;
 
		std::string adminChatRoom;
 
		std::string adminNickName;
 
		std::string adminAlias;
 
 
		std::string consumerKey;
 
		std::string consumerSecret;
 
		std::string OAUTH_KEY;
 
		std::string OAUTH_SECRET;
 
		std::string MODE;
 
 
		boost::mutex dblock, userlock;
 
 
		ThreadPool *tp;
 
		std::set<std::string> onlineUsers;
 
		struct UserData
 
		{
 
			std::string legacyName;
 
			bool spectrum1User; //Legacy support
 
			User userTwitterObj;
 
			std::string userImg;
 
			twitCurl* sessions;		
 
			status connectionState;
 
			std::string mostRecentTweetID;
 
			std::string mostRecentDirectMessageID;
 
			std::string nickName;
 
			std::set<std::string> buddies;
 
			std::map<std::string, User> buddiesInfo;
 
			std::map<std::string, std::string> buddiesImgs;
 
			mode twitterMode;
 
 
			UserData() { sessions = NULL; }
 
		};
 
		std::map<std::string, UserData> userdb;
 
		bool m_firstPing;
 
};
 
#endif
 
#ifndef TWITTER_PLUGIN
 
#define TWITTER_PLUGIN
 

	
 
#include "transport/config.h"
 
#include "transport/networkplugin.h"
 
#include "transport/logging.h"
 
#include "transport/sqlite3backend.h"
 
#include "transport/mysqlbackend.h"
 
#include "transport/pqxxbackend.h"
 
#include "transport/storagebackend.h"
 
#include "transport/threadpool.h"
 

	
 
#include "Swiften/Swiften.h"
 
#ifndef _WIN32
 
#include "unistd.h"
 
#include "signal.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#endif
 
#include <boost/algorithm/string.hpp>
 
#include <boost/signal.hpp>
 
#include <boost/thread.hpp>
 
#include <boost/thread/mutex.hpp>
 

	
 
#include "twitcurl.h"
 
#include "TwitterResponseParser.h"
 

	
 
#include <iostream>
 
#include <sstream>
 
#include <map>
 
#include <vector>
 
#include <queue>
 
#include <set>
 
#include <cstdio>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Crypto/CryptoProvider.h>
 
#include <Swiften/Crypto/PlatformCryptoProvider.h>
 
#else
 
#include "Swiften/StringCodecs/SHA1.h"
 
#endif
 
using namespace boost::filesystem;
 
using namespace boost::program_options;
 
using namespace Transport;
 

	
 
#define STR(x) (std::string("(") + x.from + ", " + x.to + ", " + x.message + ")")
 

	
 
class TwitterPlugin;
 
extern TwitterPlugin *np;
 
extern Swift::SimpleEventLoop *loop_; // Event Loop
 

	
 

	
 
class TwitterPlugin : public NetworkPlugin {
 
	public:
 
		Swift::BoostNetworkFactories *m_factories;
 
		Swift::BoostIOServiceThread m_boostIOServiceThread;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 
#if HAVE_SWIFTEN_3
 
		boost::shared_ptr<Swift::CryptoProvider> cryptoProvider;
 
#endif
 
		Swift::Timer::ref tweet_timer;
 
		Swift::Timer::ref message_timer;
 
		StorageBackend *storagebackend;
 

	
 
		TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port);
 
		~TwitterPlugin();
 

	
 
		// Send data to NetworkPlugin server
 
		void sendData(const std::string &string);
 

	
 
		// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class)
 
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data);
 
	
 
		// User trying to login into his twitter account
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password);
 
		
 
		// User logging out
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName);
 
		
 
		void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/);
 

	
 
		void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/);
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = "");
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups);
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups);
 
		
 
		void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/);
 
		
 
		void pollForTweets();
 

	
 
		void pollForDirectMessages();
 
		
 
		bool getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret);
 
		
 
		bool checkSpectrum1User(const std::string user);
 
		
 
		bool storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret);
 
		
 
		void initUserSession(const std::string user, const std::string legacyName, const std::string password);
 
		
 
		void OAuthFlowComplete(const std::string user, twitCurl *obj);
 
		
 
		void pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret);
 
		
 
		void updateLastTweetID(const std::string user, const std::string ID);
 

	
 
		std::string getMostRecentTweetID(const std::string user);
 

	
 
		void updateLastDMID(const std::string user, const std::string ID);
 
		
 
		std::string getMostRecentDMID(const std::string user);
 

	
 
		void clearRoster(const std::string user);
 

	
 
		int getTwitterMode(const std::string user);
 

	
 
		bool setTwitterMode(const std::string user, int m);
 

	
 
		/****************** Twitter response handlers **************************************/
 
		void statusUpdateResponse(std::string &user, Error &errMsg);
 
		
 
		void helpMessageResponse(std::string &user, std::string &msg);
 
		
 
		void populateRoster(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg);
 
		
 
		void displayFriendlist(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg);
 
		
 
		void displayTweets(std::string &user, std::string &userRequested, std::vector<Status> &tweets , Error &errMsg);
 
		
 
		void directMessageResponse(std::string &user, std::string &username, std::vector<DirectMessage> &messages, Error &errMsg);
 
		
 
		void createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg);
 
		
 
		void deleteFriendResponse(std::string &user, User &frnd, Error &errMsg);
 
		
 
		void RetweetResponse(std::string &user, Error &errMsg);
 
		
 
		void profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg);
 
		/***********************************************************************************/
 

	
 
	private:
 
		std::string getMostRecentTweetIDUnsafe(const std::string user);
 
		std::string getMostRecentDMIDUnsafe(const std::string user);
 

	
 
		enum status {NEW, WAITING_FOR_PIN, CONNECTED, DISCONNECTED};
 
		enum mode {SINGLECONTACT, MULTIPLECONTACT, CHATROOM};
 

	
 
		Config *config;
 
		std::string adminLegacyName;
 
		std::string adminChatRoom;
 
		std::string adminNickName;
 
		std::string adminAlias;
 

	
 
		std::string consumerKey;
 
		std::string consumerSecret;
 
		std::string OAUTH_KEY;
 
		std::string OAUTH_SECRET;
 
		std::string MODE;
 

	
 
		boost::mutex dblock, userlock;
 

	
 
		ThreadPool *tp;
 
		std::set<std::string> onlineUsers;
 
		struct UserData
 
		{
 
			std::string legacyName;
 
			bool spectrum1User; //Legacy support
 
			User userTwitterObj;
 
			std::string userImg;
 
			twitCurl* sessions;		
 
			status connectionState;
 
			std::string mostRecentTweetID;
 
			std::string mostRecentDirectMessageID;
 
			std::string nickName;
 
			std::set<std::string> buddies;
 
			std::map<std::string, User> buddiesInfo;
 
			std::map<std::string, std::string> buddiesImgs;
 
			mode twitterMode;
 

	
 
			UserData() { sessions = NULL; }
 
		};
 
		std::map<std::string, UserData> userdb;
 
		bool m_firstPing;
 
};
 
#endif
backends/twitter/libtwitcurl/HMAC_SHA1.cpp
Show inline comments
 
//******************************************************************************
 
//* HMAC_SHA1.cpp : Implementation of HMAC SHA1 algorithm
 
//*                 Comfort to RFC 2104
 
//*
 
//******************************************************************************
 
#include "HMAC_SHA1.h"
 
#include <iostream>
 
#include <memory>
 
 
 
void CHMAC_SHA1::HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest)
 
{
 
	memset(SHA1_Key, 0, SHA1_BLOCK_SIZE);
 
 
	/* repeated 64 times for values in ipad and opad */
 
	memset(m_ipad, 0x36, sizeof(m_ipad));
 
	memset(m_opad, 0x5c, sizeof(m_opad));
 
 
	/* STEP 1 */
 
	if (key_len > SHA1_BLOCK_SIZE)
 
	{
 
		CSHA1::Reset();
 
		CSHA1::Update((UINT_8 *)key, key_len);
 
		CSHA1::Final();
 
 
		CSHA1::GetHash((UINT_8 *)SHA1_Key);
 
	}
 
	else
 
		memcpy(SHA1_Key, key, key_len);
 
 
	/* STEP 2 */
 
	for (size_t i=0; i<sizeof(m_ipad); i++)
 
	{
 
		m_ipad[i] ^= SHA1_Key[i];		
 
	}
 
 
	/* STEP 3 */
 
	memcpy(AppendBuf1, m_ipad, sizeof(m_ipad));
 
	memcpy(AppendBuf1 + sizeof(m_ipad), text, text_len);
 
 
	/* STEP 4 */
 
	CSHA1::Reset();
 
	CSHA1::Update((UINT_8 *)AppendBuf1, sizeof(m_ipad) + text_len);
 
	CSHA1::Final();
 
 
	CSHA1::GetHash((UINT_8 *)szReport);
 
 
	/* STEP 5 */
 
	for (size_t j=0; j<sizeof(m_opad); j++)
 
	{
 
		m_opad[j] ^= SHA1_Key[j];
 
	}
 
 
	/* STEP 6 */
 
	memcpy(AppendBuf2, m_opad, sizeof(m_opad));
 
	memcpy(AppendBuf2 + sizeof(m_opad), szReport, SHA1_DIGEST_LENGTH);
 
 
	/*STEP 7 */
 
	CSHA1::Reset();
 
	CSHA1::Update((UINT_8 *)AppendBuf2, sizeof(m_opad) + SHA1_DIGEST_LENGTH);
 
	CSHA1::Final();
 
 
	CSHA1::GetHash((UINT_8 *)digest);
 
}
 
//******************************************************************************
 
//* HMAC_SHA1.cpp : Implementation of HMAC SHA1 algorithm
 
//*                 Comfort to RFC 2104
 
//*
 
//******************************************************************************
 
#include "HMAC_SHA1.h"
 
#include <iostream>
 
#include <memory>
 

	
 

	
 
void CHMAC_SHA1::HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest)
 
{
 
	memset(SHA1_Key, 0, SHA1_BLOCK_SIZE);
 

	
 
	/* repeated 64 times for values in ipad and opad */
 
	memset(m_ipad, 0x36, sizeof(m_ipad));
 
	memset(m_opad, 0x5c, sizeof(m_opad));
 

	
 
	/* STEP 1 */
 
	if (key_len > SHA1_BLOCK_SIZE)
 
	{
 
		CSHA1::Reset();
 
		CSHA1::Update((UINT_8 *)key, key_len);
 
		CSHA1::Final();
 

	
 
		CSHA1::GetHash((UINT_8 *)SHA1_Key);
 
	}
 
	else
 
		memcpy(SHA1_Key, key, key_len);
 

	
 
	/* STEP 2 */
 
	for (size_t i=0; i<sizeof(m_ipad); i++)
 
	{
 
		m_ipad[i] ^= SHA1_Key[i];		
 
	}
 

	
 
	/* STEP 3 */
 
	memcpy(AppendBuf1, m_ipad, sizeof(m_ipad));
 
	memcpy(AppendBuf1 + sizeof(m_ipad), text, text_len);
 

	
 
	/* STEP 4 */
 
	CSHA1::Reset();
 
	CSHA1::Update((UINT_8 *)AppendBuf1, sizeof(m_ipad) + text_len);
 
	CSHA1::Final();
 

	
 
	CSHA1::GetHash((UINT_8 *)szReport);
 

	
 
	/* STEP 5 */
 
	for (size_t j=0; j<sizeof(m_opad); j++)
 
	{
 
		m_opad[j] ^= SHA1_Key[j];
 
	}
 

	
 
	/* STEP 6 */
 
	memcpy(AppendBuf2, m_opad, sizeof(m_opad));
 
	memcpy(AppendBuf2 + sizeof(m_opad), szReport, SHA1_DIGEST_LENGTH);
 

	
 
	/*STEP 7 */
 
	CSHA1::Reset();
 
	CSHA1::Update((UINT_8 *)AppendBuf2, sizeof(m_opad) + SHA1_DIGEST_LENGTH);
 
	CSHA1::Final();
 

	
 
	CSHA1::GetHash((UINT_8 *)digest);
 
}
backends/twitter/libtwitcurl/HMAC_SHA1.h
Show inline comments
 
/*
 
	100% free public domain implementation of the HMAC-SHA1 algorithm
 
	by Chien-Chung, Chung (Jim Chung) <jimchung1221@gmail.com>
 
*/
 
 
 
#ifndef __HMAC_SHA1_H__
 
#define __HMAC_SHA1_H__
 
 
#include "SHA1.h"
 
 
typedef unsigned char BYTE ;
 
 
class CHMAC_SHA1 : public CSHA1
 
{
 
    private:
 
		BYTE m_ipad[64];
 
        BYTE m_opad[64];
 
 
		char * szReport ;
 
		char * SHA1_Key ;
 
		char * AppendBuf1 ;
 
		char * AppendBuf2 ;
 
 
 
	public:
 
		
 
		enum {
 
			SHA1_DIGEST_LENGTH	= 20,
 
			SHA1_BLOCK_SIZE		= 64,
 
			HMAC_BUF_LEN		= 4096
 
		} ;
 
 
		CHMAC_SHA1()
 
			:szReport(new char[HMAC_BUF_LEN]),
 
             SHA1_Key(new char[HMAC_BUF_LEN]),
 
             AppendBuf1(new char[HMAC_BUF_LEN]),
 
             AppendBuf2(new char[HMAC_BUF_LEN])
 
		{}
 
 
        ~CHMAC_SHA1()
 
        {
 
            delete[] szReport ;
 
            delete[] AppendBuf1 ;
 
            delete[] AppendBuf2 ;
 
            delete[] SHA1_Key ;
 
        }
 
 
        void HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest);
 
};
 
 
 
#endif /* __HMAC_SHA1_H__ */
 
/*
 
	100% free public domain implementation of the HMAC-SHA1 algorithm
 
	by Chien-Chung, Chung (Jim Chung) <jimchung1221@gmail.com>
 
*/
 

	
 

	
 
#ifndef __HMAC_SHA1_H__
 
#define __HMAC_SHA1_H__
 

	
 
#include "SHA1.h"
 

	
 
typedef unsigned char BYTE ;
 

	
 
class CHMAC_SHA1 : public CSHA1
 
{
 
    private:
 
		BYTE m_ipad[64];
 
        BYTE m_opad[64];
 

	
 
		char * szReport ;
 
		char * SHA1_Key ;
 
		char * AppendBuf1 ;
 
		char * AppendBuf2 ;
 

	
 

	
 
	public:
 
		
 
		enum {
 
			SHA1_DIGEST_LENGTH	= 20,
 
			SHA1_BLOCK_SIZE		= 64,
 
			HMAC_BUF_LEN		= 4096
 
		} ;
 

	
 
		CHMAC_SHA1()
 
			:szReport(new char[HMAC_BUF_LEN]),
 
             SHA1_Key(new char[HMAC_BUF_LEN]),
 
             AppendBuf1(new char[HMAC_BUF_LEN]),
 
             AppendBuf2(new char[HMAC_BUF_LEN])
 
		{}
 

	
 
        ~CHMAC_SHA1()
 
        {
 
            delete[] szReport ;
 
            delete[] AppendBuf1 ;
 
            delete[] AppendBuf2 ;
 
            delete[] SHA1_Key ;
 
        }
 

	
 
        void HMAC_SHA1(BYTE *text, int text_len, BYTE *key, int key_len, BYTE *digest);
 
};
 

	
 

	
 
#endif /* __HMAC_SHA1_H__ */
backends/twitter/libtwitcurl/SHA1.cpp
Show inline comments
 
/*
 
	100% free public domain implementation of the SHA-1 algorithm
 
	by Dominik Reichl <dominik.reichl@t-online.de>
 
	Web: http://www.dominik-reichl.de/
 
 
	Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches)
 
	- You can set the endianness in your files, no need to modify the
 
	  header file of the CSHA1 class any more
 
	- Aligned data support
 
	- Made support/compilation of the utility functions (ReportHash
 
	  and HashFile) optional (useful, if bytes count, for example in
 
	  embedded environments)
 
 
	Version 1.5 - 2005-01-01
 
	- 64-bit compiler compatibility added
 
	- Made variable wiping optional (define SHA1_WIPE_VARIABLES)
 
	- Removed unnecessary variable initializations
 
	- ROL32 improvement for the Microsoft compiler (using _rotl)
 
 
	======== Test Vectors (from FIPS PUB 180-1) ========
 
 
	SHA1("abc") =
 
		A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 
 
	SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") =
 
		84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 
 
	SHA1(A million repetitions of "a") =
 
		34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 
*/
 
 
#include "SHA1.h"
 
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
#define SHA1_MAX_FILE_BUFFER 8000
 
#endif
 
 
// Rotate x bits to the left
 
#ifndef ROL32
 
#ifdef _MSC_VER
 
#define ROL32(_val32, _nBits) _rotl(_val32, _nBits)
 
#else
 
#define ROL32(_val32, _nBits) (((_val32)<<(_nBits))|((_val32)>>(32-(_nBits))))
 
#endif
 
#endif
 
 
#ifdef SHA1_LITTLE_ENDIAN
 
#define SHABLK0(i) (m_block->l[i] = \
 
	(ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF))
 
#else
 
#define SHABLK0(i) (m_block->l[i])
 
#endif
 
 
#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ m_block->l[(i+8)&15] \
 
	^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1))
 
 
// SHA-1 rounds
 
#define _R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); }
 
#define _R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); }
 
#define _R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); }
 
#define _R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); }
 
#define _R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); }
 
 
CSHA1::CSHA1()
 
{
 
	m_block = (SHA1_WORKSPACE_BLOCK *)m_workspace;
 
 
	Reset();
 
}
 
 
CSHA1::~CSHA1()
 
{
 
	Reset();
 
}
 
 
void CSHA1::Reset()
 
{
 
	// SHA1 initialization constants
 
	m_state[0] = 0x67452301;
 
	m_state[1] = 0xEFCDAB89;
 
	m_state[2] = 0x98BADCFE;
 
	m_state[3] = 0x10325476;
 
	m_state[4] = 0xC3D2E1F0;
 
 
	m_count[0] = 0;
 
	m_count[1] = 0;
 
}
 
 
void CSHA1::Transform(UINT_32 *state, UINT_8 *buffer)
 
{
 
	// Copy state[] to working vars
 
	UINT_32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
 
 
	memcpy(m_block, buffer, 64);
 
 
	// 4 rounds of 20 operations each. Loop unrolled.
 
	_R0(a,b,c,d,e, 0); _R0(e,a,b,c,d, 1); _R0(d,e,a,b,c, 2); _R0(c,d,e,a,b, 3);
 
	_R0(b,c,d,e,a, 4); _R0(a,b,c,d,e, 5); _R0(e,a,b,c,d, 6); _R0(d,e,a,b,c, 7);
 
	_R0(c,d,e,a,b, 8); _R0(b,c,d,e,a, 9); _R0(a,b,c,d,e,10); _R0(e,a,b,c,d,11);
 
	_R0(d,e,a,b,c,12); _R0(c,d,e,a,b,13); _R0(b,c,d,e,a,14); _R0(a,b,c,d,e,15);
 
	_R1(e,a,b,c,d,16); _R1(d,e,a,b,c,17); _R1(c,d,e,a,b,18); _R1(b,c,d,e,a,19);
 
	_R2(a,b,c,d,e,20); _R2(e,a,b,c,d,21); _R2(d,e,a,b,c,22); _R2(c,d,e,a,b,23);
 
	_R2(b,c,d,e,a,24); _R2(a,b,c,d,e,25); _R2(e,a,b,c,d,26); _R2(d,e,a,b,c,27);
 
	_R2(c,d,e,a,b,28); _R2(b,c,d,e,a,29); _R2(a,b,c,d,e,30); _R2(e,a,b,c,d,31);
 
	_R2(d,e,a,b,c,32); _R2(c,d,e,a,b,33); _R2(b,c,d,e,a,34); _R2(a,b,c,d,e,35);
 
	_R2(e,a,b,c,d,36); _R2(d,e,a,b,c,37); _R2(c,d,e,a,b,38); _R2(b,c,d,e,a,39);
 
	_R3(a,b,c,d,e,40); _R3(e,a,b,c,d,41); _R3(d,e,a,b,c,42); _R3(c,d,e,a,b,43);
 
	_R3(b,c,d,e,a,44); _R3(a,b,c,d,e,45); _R3(e,a,b,c,d,46); _R3(d,e,a,b,c,47);
 
	_R3(c,d,e,a,b,48); _R3(b,c,d,e,a,49); _R3(a,b,c,d,e,50); _R3(e,a,b,c,d,51);
 
	_R3(d,e,a,b,c,52); _R3(c,d,e,a,b,53); _R3(b,c,d,e,a,54); _R3(a,b,c,d,e,55);
 
	_R3(e,a,b,c,d,56); _R3(d,e,a,b,c,57); _R3(c,d,e,a,b,58); _R3(b,c,d,e,a,59);
 
	_R4(a,b,c,d,e,60); _R4(e,a,b,c,d,61); _R4(d,e,a,b,c,62); _R4(c,d,e,a,b,63);
 
	_R4(b,c,d,e,a,64); _R4(a,b,c,d,e,65); _R4(e,a,b,c,d,66); _R4(d,e,a,b,c,67);
 
	_R4(c,d,e,a,b,68); _R4(b,c,d,e,a,69); _R4(a,b,c,d,e,70); _R4(e,a,b,c,d,71);
 
	_R4(d,e,a,b,c,72); _R4(c,d,e,a,b,73); _R4(b,c,d,e,a,74); _R4(a,b,c,d,e,75);
 
	_R4(e,a,b,c,d,76); _R4(d,e,a,b,c,77); _R4(c,d,e,a,b,78); _R4(b,c,d,e,a,79);
 
 
	// Add the working vars back into state
 
	state[0] += a;
 
	state[1] += b;
 
	state[2] += c;
 
	state[3] += d;
 
	state[4] += e;
 
 
	// Wipe variables
 
#ifdef SHA1_WIPE_VARIABLES
 
	a = b = c = d = e = 0;
 
#endif
 
}
 
 
// Use this function to hash in binary data and strings
 
void CSHA1::Update(UINT_8 *data, UINT_32 len)
 
{
 
	UINT_32 i, j;
 
 
	j = (m_count[0] >> 3) & 63;
 
 
	if((m_count[0] += len << 3) < (len << 3)) m_count[1]++;
 
 
	m_count[1] += (len >> 29);
 
 
	if((j + len) > 63)
 
	{
 
		i = 64 - j;
 
		memcpy(&m_buffer[j], data, i);
 
		Transform(m_state, m_buffer);
 
 
		for(; i + 63 < len; i += 64) Transform(m_state, &data[i]);
 
 
		j = 0;
 
	}
 
	else i = 0;
 
 
	memcpy(&m_buffer[j], &data[i], len - i);
 
}
 
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
// Hash in file contents
 
bool CSHA1::HashFile(char *szFileName)
 
{
 
	unsigned long ulFileSize, ulRest, ulBlocks;
 
	unsigned long i;
 
	UINT_8 uData[SHA1_MAX_FILE_BUFFER];
 
	FILE *fIn;
 
 
	if(szFileName == NULL) return false;
 
 
	fIn = fopen(szFileName, "rb");
 
	if(fIn == NULL) return false;
 
 
	fseek(fIn, 0, SEEK_END);
 
	ulFileSize = (unsigned long)ftell(fIn);
 
	fseek(fIn, 0, SEEK_SET);
 
 
	if(ulFileSize != 0)
 
	{
 
		ulBlocks = ulFileSize / SHA1_MAX_FILE_BUFFER;
 
		ulRest = ulFileSize % SHA1_MAX_FILE_BUFFER;
 
	}
 
	else
 
	{
 
		ulBlocks = 0;
 
		ulRest = 0;
 
	}
 
 
	for(i = 0; i < ulBlocks; i++)
 
	{
 
		fread(uData, 1, SHA1_MAX_FILE_BUFFER, fIn);
 
		Update((UINT_8 *)uData, SHA1_MAX_FILE_BUFFER);
 
	}
 
 
	if(ulRest != 0)
 
	{
 
		fread(uData, 1, ulRest, fIn);
 
		Update((UINT_8 *)uData, ulRest);
 
	}
 
 
	fclose(fIn); fIn = NULL;
 
	return true;
 
}
 
#endif
 
 
void CSHA1::Final()
 
{
 
	UINT_32 i;
 
	UINT_8 finalcount[8];
 
 
	for(i = 0; i < 8; i++)
 
		finalcount[i] = (UINT_8)((m_count[((i >= 4) ? 0 : 1)]
 
			>> ((3 - (i & 3)) * 8) ) & 255); // Endian independent
 
 
	Update((UINT_8 *)"\200", 1);
 
 
	while ((m_count[0] & 504) != 448)
 
		Update((UINT_8 *)"\0", 1);
 
 
	Update(finalcount, 8); // Cause a SHA1Transform()
 
 
	for(i = 0; i < 20; i++)
 
	{
 
		m_digest[i] = (UINT_8)((m_state[i >> 2] >> ((3 - (i & 3)) * 8) ) & 255);
 
	}
 
 
	// Wipe variables for security reasons
 
#ifdef SHA1_WIPE_VARIABLES
 
	i = 0;
 
	memset(m_buffer, 0, 64);
 
	memset(m_state, 0, 20);
 
	memset(m_count, 0, 8);
 
	memset(finalcount, 0, 8);
 
	Transform(m_state, m_buffer);
 
#endif
 
}
 
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
// Get the final hash as a pre-formatted string
 
void CSHA1::ReportHash(char *szReport, unsigned char uReportType)
 
{
 
	unsigned char i;
 
	char szTemp[16];
 
 
	if(szReport == NULL) return;
 
 
	if(uReportType == REPORT_HEX)
 
	{
 
		sprintf(szTemp, "%02X", m_digest[0]);
 
		strcat(szReport, szTemp);
 
 
		for(i = 1; i < 20; i++)
 
		{
 
			sprintf(szTemp, " %02X", m_digest[i]);
 
			strcat(szReport, szTemp);
 
		}
 
	}
 
	else if(uReportType == REPORT_DIGIT)
 
	{
 
		sprintf(szTemp, "%u", m_digest[0]);
 
		strcat(szReport, szTemp);
 
 
		for(i = 1; i < 20; i++)
 
		{
 
			sprintf(szTemp, " %u", m_digest[i]);
 
			strcat(szReport, szTemp);
 
		}
 
	}
 
	else strcpy(szReport, "Error: Unknown report type!");
 
}
 
#endif
 
 
// Get the raw message digest
 
void CSHA1::GetHash(UINT_8 *puDest)
 
{
 
	memcpy(puDest, m_digest, 20);
 
}
 
/*
 
	100% free public domain implementation of the SHA-1 algorithm
 
	by Dominik Reichl <dominik.reichl@t-online.de>
 
	Web: http://www.dominik-reichl.de/
 

	
 
	Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches)
 
	- You can set the endianness in your files, no need to modify the
 
	  header file of the CSHA1 class any more
 
	- Aligned data support
 
	- Made support/compilation of the utility functions (ReportHash
 
	  and HashFile) optional (useful, if bytes count, for example in
 
	  embedded environments)
 

	
 
	Version 1.5 - 2005-01-01
 
	- 64-bit compiler compatibility added
 
	- Made variable wiping optional (define SHA1_WIPE_VARIABLES)
 
	- Removed unnecessary variable initializations
 
	- ROL32 improvement for the Microsoft compiler (using _rotl)
 

	
 
	======== Test Vectors (from FIPS PUB 180-1) ========
 

	
 
	SHA1("abc") =
 
		A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 

	
 
	SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") =
 
		84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 

	
 
	SHA1(A million repetitions of "a") =
 
		34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 
*/
 

	
 
#include "SHA1.h"
 

	
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
#define SHA1_MAX_FILE_BUFFER 8000
 
#endif
 

	
 
// Rotate x bits to the left
 
#ifndef ROL32
 
#ifdef _MSC_VER
 
#define ROL32(_val32, _nBits) _rotl(_val32, _nBits)
 
#else
 
#define ROL32(_val32, _nBits) (((_val32)<<(_nBits))|((_val32)>>(32-(_nBits))))
 
#endif
 
#endif
 

	
 
#ifdef SHA1_LITTLE_ENDIAN
 
#define SHABLK0(i) (m_block->l[i] = \
 
	(ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF))
 
#else
 
#define SHABLK0(i) (m_block->l[i])
 
#endif
 

	
 
#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ m_block->l[(i+8)&15] \
 
	^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1))
 

	
 
// SHA-1 rounds
 
#define _R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); }
 
#define _R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); }
 
#define _R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); }
 
#define _R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); }
 
#define _R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); }
 

	
 
CSHA1::CSHA1()
 
{
 
	m_block = (SHA1_WORKSPACE_BLOCK *)m_workspace;
 

	
 
	Reset();
 
}
 

	
 
CSHA1::~CSHA1()
 
{
 
	Reset();
 
}
 

	
 
void CSHA1::Reset()
 
{
 
	// SHA1 initialization constants
 
	m_state[0] = 0x67452301;
 
	m_state[1] = 0xEFCDAB89;
 
	m_state[2] = 0x98BADCFE;
 
	m_state[3] = 0x10325476;
 
	m_state[4] = 0xC3D2E1F0;
 

	
 
	m_count[0] = 0;
 
	m_count[1] = 0;
 
}
 

	
 
void CSHA1::Transform(UINT_32 *state, UINT_8 *buffer)
 
{
 
	// Copy state[] to working vars
 
	UINT_32 a = state[0], b = state[1], c = state[2], d = state[3], e = state[4];
 

	
 
	memcpy(m_block, buffer, 64);
 

	
 
	// 4 rounds of 20 operations each. Loop unrolled.
 
	_R0(a,b,c,d,e, 0); _R0(e,a,b,c,d, 1); _R0(d,e,a,b,c, 2); _R0(c,d,e,a,b, 3);
 
	_R0(b,c,d,e,a, 4); _R0(a,b,c,d,e, 5); _R0(e,a,b,c,d, 6); _R0(d,e,a,b,c, 7);
 
	_R0(c,d,e,a,b, 8); _R0(b,c,d,e,a, 9); _R0(a,b,c,d,e,10); _R0(e,a,b,c,d,11);
 
	_R0(d,e,a,b,c,12); _R0(c,d,e,a,b,13); _R0(b,c,d,e,a,14); _R0(a,b,c,d,e,15);
 
	_R1(e,a,b,c,d,16); _R1(d,e,a,b,c,17); _R1(c,d,e,a,b,18); _R1(b,c,d,e,a,19);
 
	_R2(a,b,c,d,e,20); _R2(e,a,b,c,d,21); _R2(d,e,a,b,c,22); _R2(c,d,e,a,b,23);
 
	_R2(b,c,d,e,a,24); _R2(a,b,c,d,e,25); _R2(e,a,b,c,d,26); _R2(d,e,a,b,c,27);
 
	_R2(c,d,e,a,b,28); _R2(b,c,d,e,a,29); _R2(a,b,c,d,e,30); _R2(e,a,b,c,d,31);
 
	_R2(d,e,a,b,c,32); _R2(c,d,e,a,b,33); _R2(b,c,d,e,a,34); _R2(a,b,c,d,e,35);
 
	_R2(e,a,b,c,d,36); _R2(d,e,a,b,c,37); _R2(c,d,e,a,b,38); _R2(b,c,d,e,a,39);
 
	_R3(a,b,c,d,e,40); _R3(e,a,b,c,d,41); _R3(d,e,a,b,c,42); _R3(c,d,e,a,b,43);
 
	_R3(b,c,d,e,a,44); _R3(a,b,c,d,e,45); _R3(e,a,b,c,d,46); _R3(d,e,a,b,c,47);
 
	_R3(c,d,e,a,b,48); _R3(b,c,d,e,a,49); _R3(a,b,c,d,e,50); _R3(e,a,b,c,d,51);
 
	_R3(d,e,a,b,c,52); _R3(c,d,e,a,b,53); _R3(b,c,d,e,a,54); _R3(a,b,c,d,e,55);
 
	_R3(e,a,b,c,d,56); _R3(d,e,a,b,c,57); _R3(c,d,e,a,b,58); _R3(b,c,d,e,a,59);
 
	_R4(a,b,c,d,e,60); _R4(e,a,b,c,d,61); _R4(d,e,a,b,c,62); _R4(c,d,e,a,b,63);
 
	_R4(b,c,d,e,a,64); _R4(a,b,c,d,e,65); _R4(e,a,b,c,d,66); _R4(d,e,a,b,c,67);
 
	_R4(c,d,e,a,b,68); _R4(b,c,d,e,a,69); _R4(a,b,c,d,e,70); _R4(e,a,b,c,d,71);
 
	_R4(d,e,a,b,c,72); _R4(c,d,e,a,b,73); _R4(b,c,d,e,a,74); _R4(a,b,c,d,e,75);
 
	_R4(e,a,b,c,d,76); _R4(d,e,a,b,c,77); _R4(c,d,e,a,b,78); _R4(b,c,d,e,a,79);
 

	
 
	// Add the working vars back into state
 
	state[0] += a;
 
	state[1] += b;
 
	state[2] += c;
 
	state[3] += d;
 
	state[4] += e;
 

	
 
	// Wipe variables
 
#ifdef SHA1_WIPE_VARIABLES
 
	a = b = c = d = e = 0;
 
#endif
 
}
 

	
 
// Use this function to hash in binary data and strings
 
void CSHA1::Update(UINT_8 *data, UINT_32 len)
 
{
 
	UINT_32 i, j;
 

	
 
	j = (m_count[0] >> 3) & 63;
 

	
 
	if((m_count[0] += len << 3) < (len << 3)) m_count[1]++;
 

	
 
	m_count[1] += (len >> 29);
 

	
 
	if((j + len) > 63)
 
	{
 
		i = 64 - j;
 
		memcpy(&m_buffer[j], data, i);
 
		Transform(m_state, m_buffer);
 

	
 
		for(; i + 63 < len; i += 64) Transform(m_state, &data[i]);
 

	
 
		j = 0;
 
	}
 
	else i = 0;
 

	
 
	memcpy(&m_buffer[j], &data[i], len - i);
 
}
 

	
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
// Hash in file contents
 
bool CSHA1::HashFile(char *szFileName)
 
{
 
	unsigned long ulFileSize, ulRest, ulBlocks;
 
	unsigned long i;
 
	UINT_8 uData[SHA1_MAX_FILE_BUFFER];
 
	FILE *fIn;
 

	
 
	if(szFileName == NULL) return false;
 

	
 
	fIn = fopen(szFileName, "rb");
 
	if(fIn == NULL) return false;
 

	
 
	fseek(fIn, 0, SEEK_END);
 
	ulFileSize = (unsigned long)ftell(fIn);
 
	fseek(fIn, 0, SEEK_SET);
 

	
 
	if(ulFileSize != 0)
 
	{
 
		ulBlocks = ulFileSize / SHA1_MAX_FILE_BUFFER;
 
		ulRest = ulFileSize % SHA1_MAX_FILE_BUFFER;
 
	}
 
	else
 
	{
 
		ulBlocks = 0;
 
		ulRest = 0;
 
	}
 

	
 
	for(i = 0; i < ulBlocks; i++)
 
	{
 
		fread(uData, 1, SHA1_MAX_FILE_BUFFER, fIn);
 
		Update((UINT_8 *)uData, SHA1_MAX_FILE_BUFFER);
 
	}
 

	
 
	if(ulRest != 0)
 
	{
 
		fread(uData, 1, ulRest, fIn);
 
		Update((UINT_8 *)uData, ulRest);
 
	}
 

	
 
	fclose(fIn); fIn = NULL;
 
	return true;
 
}
 
#endif
 

	
 
void CSHA1::Final()
 
{
 
	UINT_32 i;
 
	UINT_8 finalcount[8];
 

	
 
	for(i = 0; i < 8; i++)
 
		finalcount[i] = (UINT_8)((m_count[((i >= 4) ? 0 : 1)]
 
			>> ((3 - (i & 3)) * 8) ) & 255); // Endian independent
 

	
 
	Update((UINT_8 *)"\200", 1);
 

	
 
	while ((m_count[0] & 504) != 448)
 
		Update((UINT_8 *)"\0", 1);
 

	
 
	Update(finalcount, 8); // Cause a SHA1Transform()
 

	
 
	for(i = 0; i < 20; i++)
 
	{
 
		m_digest[i] = (UINT_8)((m_state[i >> 2] >> ((3 - (i & 3)) * 8) ) & 255);
 
	}
 

	
 
	// Wipe variables for security reasons
 
#ifdef SHA1_WIPE_VARIABLES
 
	i = 0;
 
	memset(m_buffer, 0, 64);
 
	memset(m_state, 0, 20);
 
	memset(m_count, 0, 8);
 
	memset(finalcount, 0, 8);
 
	Transform(m_state, m_buffer);
 
#endif
 
}
 

	
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
// Get the final hash as a pre-formatted string
 
void CSHA1::ReportHash(char *szReport, unsigned char uReportType)
 
{
 
	unsigned char i;
 
	char szTemp[16];
 

	
 
	if(szReport == NULL) return;
 

	
 
	if(uReportType == REPORT_HEX)
 
	{
 
		sprintf(szTemp, "%02X", m_digest[0]);
 
		strcat(szReport, szTemp);
 

	
 
		for(i = 1; i < 20; i++)
 
		{
 
			sprintf(szTemp, " %02X", m_digest[i]);
 
			strcat(szReport, szTemp);
 
		}
 
	}
 
	else if(uReportType == REPORT_DIGIT)
 
	{
 
		sprintf(szTemp, "%u", m_digest[0]);
 
		strcat(szReport, szTemp);
 

	
 
		for(i = 1; i < 20; i++)
 
		{
 
			sprintf(szTemp, " %u", m_digest[i]);
 
			strcat(szReport, szTemp);
 
		}
 
	}
 
	else strcpy(szReport, "Error: Unknown report type!");
 
}
 
#endif
 

	
 
// Get the raw message digest
 
void CSHA1::GetHash(UINT_8 *puDest)
 
{
 
	memcpy(puDest, m_digest, 20);
 
}
backends/twitter/libtwitcurl/SHA1.h
Show inline comments
 
/*
 
	100% free public domain implementation of the SHA-1 algorithm
 
	by Dominik Reichl <dominik.reichl@t-online.de>
 
	Web: http://www.dominik-reichl.de/
 
 
	Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches)
 
	- You can set the endianness in your files, no need to modify the
 
	  header file of the CSHA1 class any more
 
	- Aligned data support
 
	- Made support/compilation of the utility functions (ReportHash
 
	  and HashFile) optional (useful, if bytes count, for example in
 
	  embedded environments)
 
 
	Version 1.5 - 2005-01-01
 
	- 64-bit compiler compatibility added
 
	- Made variable wiping optional (define SHA1_WIPE_VARIABLES)
 
	- Removed unnecessary variable initializations
 
	- ROL32 improvement for the Microsoft compiler (using _rotl)
 
 
	======== Test Vectors (from FIPS PUB 180-1) ========
 
 
	SHA1("abc") =
 
		A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 
 
	SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") =
 
		84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 
 
	SHA1(A million repetitions of "a") =
 
		34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 
*/
 
 
#ifndef ___SHA1_HDR___
 
#define ___SHA1_HDR___
 
 
#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS)
 
#define SHA1_UTILITY_FUNCTIONS
 
#endif
 
 
#include <memory.h> // Needed for memset and memcpy
 
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
#include <stdio.h>  // Needed for file access and sprintf
 
#include <string.h> // Needed for strcat and strcpy
 
#endif
 
 
#ifdef _MSC_VER
 
#include <stdlib.h>
 
#endif
 
 
// You can define the endian mode in your files, without modifying the SHA1
 
// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN
 
// in your files, before including the SHA1.h header file. If you don't
 
// define anything, the class defaults to little endian.
 
 
#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN)
 
#define SHA1_LITTLE_ENDIAN
 
#endif
 
 
// Same here. If you want variable wiping, #define SHA1_WIPE_VARIABLES, if
 
// not, #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it
 
// defaults to wiping.
 
 
#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES)
 
#define SHA1_WIPE_VARIABLES
 
#endif
 
 
/////////////////////////////////////////////////////////////////////////////
 
// Define 8- and 32-bit variables
 
 
#ifndef UINT_32
 
 
#ifdef _MSC_VER
 
 
#define UINT_8  unsigned __int8
 
#define UINT_32 unsigned __int32
 
 
#else
 
 
#define UINT_8 unsigned char
 
 
#if (ULONG_MAX == 0xFFFFFFFF)
 
#define UINT_32 unsigned long
 
#else
 
#define UINT_32 unsigned int
 
#endif
 
 
#endif
 
#endif
 
 
/////////////////////////////////////////////////////////////////////////////
 
// Declare SHA1 workspace
 
 
typedef union
 
{
 
	UINT_8  c[64];
 
	UINT_32 l[16];
 
} SHA1_WORKSPACE_BLOCK;
 
 
class CSHA1
 
{
 
public:
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	// Two different formats for ReportHash(...)
 
	enum
 
	{
 
		REPORT_HEX = 0,
 
		REPORT_DIGIT = 1
 
	};
 
#endif
 
 
	// Constructor and Destructor
 
	CSHA1();
 
	~CSHA1();
 
 
	UINT_32 m_state[5];
 
	UINT_32 m_count[2];
 
	UINT_32 __reserved1[1];
 
	UINT_8  m_buffer[64];
 
	UINT_8  m_digest[20];
 
	UINT_32 __reserved2[3];
 
 
	void Reset();
 
 
	// Update the hash value
 
	void Update(UINT_8 *data, UINT_32 len);
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	bool HashFile(char *szFileName);
 
#endif
 
 
	// Finalize hash and report
 
	void Final();
 
 
	// Report functions: as pre-formatted and raw data
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	void ReportHash(char *szReport, unsigned char uReportType = REPORT_HEX);
 
#endif
 
	void GetHash(UINT_8 *puDest);
 
 
private:
 
	// Private SHA-1 transformation
 
	void Transform(UINT_32 *state, UINT_8 *buffer);
 
 
	// Member variables
 
	UINT_8 m_workspace[64];
 
	SHA1_WORKSPACE_BLOCK *m_block; // SHA1 pointer to the byte array above
 
};
 
 
#endif
 
/*
 
	100% free public domain implementation of the SHA-1 algorithm
 
	by Dominik Reichl <dominik.reichl@t-online.de>
 
	Web: http://www.dominik-reichl.de/
 

	
 
	Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches)
 
	- You can set the endianness in your files, no need to modify the
 
	  header file of the CSHA1 class any more
 
	- Aligned data support
 
	- Made support/compilation of the utility functions (ReportHash
 
	  and HashFile) optional (useful, if bytes count, for example in
 
	  embedded environments)
 

	
 
	Version 1.5 - 2005-01-01
 
	- 64-bit compiler compatibility added
 
	- Made variable wiping optional (define SHA1_WIPE_VARIABLES)
 
	- Removed unnecessary variable initializations
 
	- ROL32 improvement for the Microsoft compiler (using _rotl)
 

	
 
	======== Test Vectors (from FIPS PUB 180-1) ========
 

	
 
	SHA1("abc") =
 
		A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
 

	
 
	SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") =
 
		84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
 

	
 
	SHA1(A million repetitions of "a") =
 
		34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
 
*/
 

	
 
#ifndef ___SHA1_HDR___
 
#define ___SHA1_HDR___
 

	
 
#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS)
 
#define SHA1_UTILITY_FUNCTIONS
 
#endif
 

	
 
#include <memory.h> // Needed for memset and memcpy
 

	
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
#include <stdio.h>  // Needed for file access and sprintf
 
#include <string.h> // Needed for strcat and strcpy
 
#endif
 

	
 
#ifdef _MSC_VER
 
#include <stdlib.h>
 
#endif
 

	
 
// You can define the endian mode in your files, without modifying the SHA1
 
// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN
 
// in your files, before including the SHA1.h header file. If you don't
 
// define anything, the class defaults to little endian.
 

	
 
#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN)
 
#define SHA1_LITTLE_ENDIAN
 
#endif
 

	
 
// Same here. If you want variable wiping, #define SHA1_WIPE_VARIABLES, if
 
// not, #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it
 
// defaults to wiping.
 

	
 
#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES)
 
#define SHA1_WIPE_VARIABLES
 
#endif
 

	
 
/////////////////////////////////////////////////////////////////////////////
 
// Define 8- and 32-bit variables
 

	
 
#ifndef UINT_32
 

	
 
#ifdef _MSC_VER
 

	
 
#define UINT_8  unsigned __int8
 
#define UINT_32 unsigned __int32
 

	
 
#else
 

	
 
#define UINT_8 unsigned char
 

	
 
#if (ULONG_MAX == 0xFFFFFFFF)
 
#define UINT_32 unsigned long
 
#else
 
#define UINT_32 unsigned int
 
#endif
 

	
 
#endif
 
#endif
 

	
 
/////////////////////////////////////////////////////////////////////////////
 
// Declare SHA1 workspace
 

	
 
typedef union
 
{
 
	UINT_8  c[64];
 
	UINT_32 l[16];
 
} SHA1_WORKSPACE_BLOCK;
 

	
 
class CSHA1
 
{
 
public:
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	// Two different formats for ReportHash(...)
 
	enum
 
	{
 
		REPORT_HEX = 0,
 
		REPORT_DIGIT = 1
 
	};
 
#endif
 

	
 
	// Constructor and Destructor
 
	CSHA1();
 
	~CSHA1();
 

	
 
	UINT_32 m_state[5];
 
	UINT_32 m_count[2];
 
	UINT_32 __reserved1[1];
 
	UINT_8  m_buffer[64];
 
	UINT_8  m_digest[20];
 
	UINT_32 __reserved2[3];
 

	
 
	void Reset();
 

	
 
	// Update the hash value
 
	void Update(UINT_8 *data, UINT_32 len);
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	bool HashFile(char *szFileName);
 
#endif
 

	
 
	// Finalize hash and report
 
	void Final();
 

	
 
	// Report functions: as pre-formatted and raw data
 
#ifdef SHA1_UTILITY_FUNCTIONS
 
	void ReportHash(char *szReport, unsigned char uReportType = REPORT_HEX);
 
#endif
 
	void GetHash(UINT_8 *puDest);
 

	
 
private:
 
	// Private SHA-1 transformation
 
	void Transform(UINT_32 *state, UINT_8 *buffer);
 

	
 
	// Member variables
 
	UINT_8 m_workspace[64];
 
	SHA1_WORKSPACE_BLOCK *m_block; // SHA1 pointer to the byte array above
 
};
 

	
 
#endif
backends/twitter/libtwitcurl/base64.cpp
Show inline comments
 
/* 
 
   base64.cpp and base64.h
 
 
   Copyright (C) 2004-2008 René Nyffenegger
 
 
   This source code is provided 'as-is', without any express or implied
 
   warranty. In no event will the author be held liable for any damages
 
   arising from the use of this software.
 
 
   Permission is granted to anyone to use this software for any purpose,
 
   including commercial applications, and to alter it and redistribute it
 
   freely, subject to the following restrictions:
 
 
   1. The origin of this source code must not be misrepresented; you must not
 
      claim that you wrote the original source code. If you use this source code
 
      in a product, an acknowledgment in the product documentation would be
 
      appreciated but is not required.
 
 
   2. Altered source versions must be plainly marked as such, and must not be
 
      misrepresented as being the original source code.
 
 
   3. This notice may not be removed or altered from any source distribution.
 
 
   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
 
 
*/
 
 
#include "base64.h"
 
#include <iostream>
 
 
static const std::string base64_chars = 
 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
             "abcdefghijklmnopqrstuvwxyz"
 
             "0123456789+/";
 
 
 
static inline bool is_base64(unsigned char c) {
 
  return (isalnum(c) || (c == '+') || (c == '/'));
 
}
 
 
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
 
  std::string ret;
 
  int i = 0;
 
  int j = 0;
 
  unsigned char char_array_3[3];
 
  unsigned char char_array_4[4];
 
 
  while (in_len--) {
 
    char_array_3[i++] = *(bytes_to_encode++);
 
    if (i == 3) {
 
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
 
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
 
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
 
      char_array_4[3] = char_array_3[2] & 0x3f;
 
 
      for(i = 0; (i <4) ; i++)
 
        ret += base64_chars[char_array_4[i]];
 
      i = 0;
 
    }
 
  }
 
 
  if (i)
 
  {
 
    for(j = i; j < 3; j++)
 
      char_array_3[j] = '\0';
 
 
    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
 
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
 
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
 
    char_array_4[3] = char_array_3[2] & 0x3f;
 
 
    for (j = 0; (j < i + 1); j++)
 
      ret += base64_chars[char_array_4[j]];
 
 
    while((i++ < 3))
 
      ret += '=';
 
 
  }
 
 
  return ret;
 
 
}
 
 
std::string base64_decode(std::string const& encoded_string) {
 
  int in_len = encoded_string.size();
 
  int i = 0;
 
  int j = 0;
 
  int in_ = 0;
 
  unsigned char char_array_4[4], char_array_3[3];
 
  std::string ret;
 
 
  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
 
    char_array_4[i++] = encoded_string[in_]; in_++;
 
    if (i ==4) {
 
      for (i = 0; i <4; i++)
 
        char_array_4[i] = base64_chars.find(char_array_4[i]);
 
 
      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
 
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
 
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 
 
      for (i = 0; (i < 3); i++)
 
        ret += char_array_3[i];
 
      i = 0;
 
    }
 
  }
 
 
  if (i) {
 
    for (j = i; j <4; j++)
 
      char_array_4[j] = 0;
 
 
    for (j = 0; j <4; j++)
 
      char_array_4[j] = base64_chars.find(char_array_4[j]);
 
 
    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
 
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
 
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 
 
    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
 
  }
 
 
  return ret;
 
/* 
 
   base64.cpp and base64.h
 

	
 
   Copyright (C) 2004-2008 René Nyffenegger
 

	
 
   This source code is provided 'as-is', without any express or implied
 
   warranty. In no event will the author be held liable for any damages
 
   arising from the use of this software.
 

	
 
   Permission is granted to anyone to use this software for any purpose,
 
   including commercial applications, and to alter it and redistribute it
 
   freely, subject to the following restrictions:
 

	
 
   1. The origin of this source code must not be misrepresented; you must not
 
      claim that you wrote the original source code. If you use this source code
 
      in a product, an acknowledgment in the product documentation would be
 
      appreciated but is not required.
 

	
 
   2. Altered source versions must be plainly marked as such, and must not be
 
      misrepresented as being the original source code.
 

	
 
   3. This notice may not be removed or altered from any source distribution.
 

	
 
   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
 

	
 
*/
 

	
 
#include "base64.h"
 
#include <iostream>
 

	
 
static const std::string base64_chars = 
 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
             "abcdefghijklmnopqrstuvwxyz"
 
             "0123456789+/";
 

	
 

	
 
static inline bool is_base64(unsigned char c) {
 
  return (isalnum(c) || (c == '+') || (c == '/'));
 
}
 

	
 
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
 
  std::string ret;
 
  int i = 0;
 
  int j = 0;
 
  unsigned char char_array_3[3];
 
  unsigned char char_array_4[4];
 

	
 
  while (in_len--) {
 
    char_array_3[i++] = *(bytes_to_encode++);
 
    if (i == 3) {
 
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
 
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
 
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
 
      char_array_4[3] = char_array_3[2] & 0x3f;
 

	
 
      for(i = 0; (i <4) ; i++)
 
        ret += base64_chars[char_array_4[i]];
 
      i = 0;
 
    }
 
  }
 

	
 
  if (i)
 
  {
 
    for(j = i; j < 3; j++)
 
      char_array_3[j] = '\0';
 

	
 
    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
 
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
 
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
 
    char_array_4[3] = char_array_3[2] & 0x3f;
 

	
 
    for (j = 0; (j < i + 1); j++)
 
      ret += base64_chars[char_array_4[j]];
 

	
 
    while((i++ < 3))
 
      ret += '=';
 

	
 
  }
 

	
 
  return ret;
 

	
 
}
 

	
 
std::string base64_decode(std::string const& encoded_string) {
 
  int in_len = encoded_string.size();
 
  int i = 0;
 
  int j = 0;
 
  int in_ = 0;
 
  unsigned char char_array_4[4], char_array_3[3];
 
  std::string ret;
 

	
 
  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
 
    char_array_4[i++] = encoded_string[in_]; in_++;
 
    if (i ==4) {
 
      for (i = 0; i <4; i++)
 
        char_array_4[i] = base64_chars.find(char_array_4[i]);
 

	
 
      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
 
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
 
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 

	
 
      for (i = 0; (i < 3); i++)
 
        ret += char_array_3[i];
 
      i = 0;
 
    }
 
  }
 

	
 
  if (i) {
 
    for (j = i; j <4; j++)
 
      char_array_4[j] = 0;
 

	
 
    for (j = 0; j <4; j++)
 
      char_array_4[j] = base64_chars.find(char_array_4[j]);
 

	
 
    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
 
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
 
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
 

	
 
    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
 
  }
 

	
 
  return ret;
 
}
 
\ No newline at end of file
backends/twitter/libtwitcurl/base64.h
Show inline comments
 
#include <string>
 
 
std::string base64_encode(unsigned char const* , unsigned int len);
 
#include <string>
 

	
 
std::string base64_encode(unsigned char const* , unsigned int len);
 
std::string base64_decode(std::string const& s);
 
\ No newline at end of file
backends/twitter/libtwitcurl/oauthlib.cpp
Show inline comments
 
#include "twitcurlurls.h"
 
#include "oauthlib.h"
 
#include "HMAC_SHA1.h"
 
#include "base64.h"
 
#include "urlencode.h"
 
 
/*++
 
* @method: oAuth::oAuth
 
*
 
* @description: constructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
oAuth::oAuth()
 
{
 
}
 
 
/*++
 
* @method: oAuth::~oAuth
 
*
 
* @description: destructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
oAuth::~oAuth()
 
{
 
}
 
 
/*++
 
* @method: oAuth::clone
 
*
 
* @description: creates a clone of oAuth object
 
*
 
* @input: none
 
*
 
* @output: cloned oAuth object
 
*
 
*--*/
 
oAuth oAuth::clone()
 
{
 
    oAuth cloneObj;
 
    cloneObj.m_consumerKey = m_consumerKey;
 
    cloneObj.m_consumerSecret = m_consumerSecret;
 
    cloneObj.m_oAuthTokenKey = m_oAuthTokenKey;
 
    cloneObj.m_oAuthTokenSecret = m_oAuthTokenSecret;
 
    cloneObj.m_oAuthPin = m_oAuthPin;
 
    cloneObj.m_nonce = m_nonce;
 
    cloneObj.m_timeStamp = m_timeStamp;
 
    cloneObj.m_oAuthScreenName =  m_oAuthScreenName;
 
    return cloneObj;
 
}
 
 
 
/*++
 
* @method: oAuth::getConsumerKey
 
*
 
* @description: this method gives consumer key that is being used currently
 
*
 
* @input: none
 
*
 
* @output: consumer key
 
*
 
*--*/
 
void oAuth::getConsumerKey( std::string& consumerKey )
 
{
 
    consumerKey = m_consumerKey;
 
}
 
 
/*++
 
* @method: oAuth::setConsumerKey
 
*
 
* @description: this method saves consumer key that should be used
 
*
 
* @input: consumer key
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setConsumerKey( const std::string& consumerKey )
 
{
 
    m_consumerKey.assign( consumerKey );
 
}
 
 
/*++
 
* @method: oAuth::getConsumerSecret
 
*
 
* @description: this method gives consumer secret that is being used currently
 
*
 
* @input: none
 
*
 
* @output: consumer secret
 
*
 
*--*/
 
void oAuth::getConsumerSecret( std::string& consumerSecret )
 
{
 
    consumerSecret = m_consumerSecret;
 
}
 
 
/*++
 
* @method: oAuth::setConsumerSecret
 
*
 
* @description: this method saves consumer secret that should be used
 
*
 
* @input: consumer secret
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setConsumerSecret( const std::string& consumerSecret )
 
{
 
    m_consumerSecret = consumerSecret;
 
}
 
 
/*++
 
* @method: oAuth::getOAuthTokenKey
 
*
 
* @description: this method gives OAuth token (also called access token) that is being used currently
 
*
 
* @input: none
 
*
 
* @output: OAuth token
 
*
 
*--*/
 
void oAuth::getOAuthTokenKey( std::string& oAuthTokenKey )
 
{
 
    oAuthTokenKey = m_oAuthTokenKey;
 
}
 
 
/*++
 
* @method: oAuth::setOAuthTokenKey
 
*
 
* @description: this method saves OAuth token that should be used
 
*
 
* @input: OAuth token
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthTokenKey( const std::string& oAuthTokenKey )
 
{
 
    m_oAuthTokenKey = oAuthTokenKey;
 
}
 
 
/*++
 
* @method: oAuth::getOAuthTokenSecret
 
*
 
* @description: this method gives OAuth token secret that is being used currently
 
*
 
* @input: none
 
*
 
* @output: OAuth token secret
 
*
 
*--*/
 
void oAuth::getOAuthTokenSecret( std::string& oAuthTokenSecret )
 
{
 
    oAuthTokenSecret = m_oAuthTokenSecret;
 
}
 
 
/*++
 
* @method: oAuth::setOAuthTokenSecret
 
*
 
* @description: this method saves OAuth token that should be used
 
*
 
* @input: OAuth token secret
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthTokenSecret( const std::string& oAuthTokenSecret )
 
{
 
    m_oAuthTokenSecret = oAuthTokenSecret;
 
}
 
 
/*++
 
* @method: oAuth::getOAuthScreenName
 
*
 
* @description: this method gives authorized user's screenname
 
*
 
* @input: none
 
*
 
* @output: screen name
 
*
 
*--*/
 
void oAuth::getOAuthScreenName( std::string& oAuthScreenName )
 
{
 
    oAuthScreenName = m_oAuthScreenName;
 
}
 
 
/*++
 
* @method: oAuth::setOAuthScreenName
 
*
 
* @description: this method sets authorized user's screenname
 
*
 
* @input: screen name
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthScreenName( const std::string& oAuthScreenName )
 
{
 
    m_oAuthScreenName = oAuthScreenName;
 
}
 
 
/*++
 
* @method: oAuth::getOAuthPin
 
*
 
* @description: this method gives OAuth verifier PIN
 
*
 
* @input: none
 
*
 
* @output: OAuth verifier PIN
 
*
 
*--*/
 
void oAuth::getOAuthPin( std::string& oAuthPin )
 
{
 
    oAuthPin = m_oAuthPin;
 
}
 
 
/*++
 
* @method: oAuth::setOAuthPin
 
*
 
* @description: this method sets OAuth verifier PIN
 
*
 
* @input: OAuth verifier PIN
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthPin( const std::string& oAuthPin )
 
{
 
    m_oAuthPin = oAuthPin;
 
}
 
 
/*++
 
* @method: oAuth::generateNonceTimeStamp
 
*
 
* @description: this method generates nonce and timestamp for OAuth header
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void oAuth::generateNonceTimeStamp()
 
{
 
    char szTime[oAuthLibDefaults::OAUTHLIB_BUFFSIZE];
 
    char szRand[oAuthLibDefaults::OAUTHLIB_BUFFSIZE];
 
    memset( szTime, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE );
 
    memset( szRand, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE );
 
    srand( (unsigned int)time( NULL ) );
 
    sprintf( szRand, "%x", rand()%1000 );
 
    sprintf( szTime, "%ld", time( NULL ) );
 
 
    m_nonce.assign( szTime );
 
    m_nonce.append( szRand );
 
    m_timeStamp.assign( szTime );
 
}
 
 
/*++
 
* @method: oAuth::buildOAuthRawDataKeyValPairs
 
*
 
* @description: this method prepares key-value pairs from the data part of the URL
 
*               or from the URL post fields data, as required by OAuth header
 
*               and signature generation.
 
*
 
* @input: rawData - Raw data either from the URL itself or from post fields.
 
*                   Should already be url encoded.
 
*         urlencodeData - If true, string will be urlencoded before converting
 
*                         to key value pairs.
 
*
 
* @output: rawDataKeyValuePairs - Map in which key-value pairs are populated
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void oAuth::buildOAuthRawDataKeyValPairs( const std::string& rawData,
 
                                          bool urlencodeData,
 
                                          oAuthKeyValuePairs& rawDataKeyValuePairs )
 
{
 
    /* Raw data if it's present. Data should already be urlencoded once */
 
    if( rawData.empty() )
 
    {
 
        return;
 
    }
 
 
    size_t nSep = std::string::npos;
 
    size_t nPos = std::string::npos;
 
    std::string dataKeyVal;
 
    std::string dataKey;
 
    std::string dataVal;
 
 
    /* This raw data part can contain many key value pairs: key1=value1&key2=value2&key3=value3 */
 
    std::string dataPart = rawData;
 
    while( std::string::npos != ( nSep = dataPart.find_first_of("&") ) )
 
    {
 
        /* Extract first key=value pair */
 
        dataKeyVal = dataPart.substr( 0, nSep );
 
 
        /* Split them */
 
        nPos = dataKeyVal.find_first_of( "=" );
 
        if( std::string::npos != nPos )
 
        {
 
            dataKey = dataKeyVal.substr( 0, nPos );
 
            dataVal = dataKeyVal.substr( nPos + 1 );
 
 
            /* Put this key=value pair in map */
 
            rawDataKeyValuePairs[dataKey] = urlencodeData ? urlencode( dataVal ) : dataVal;
 
        }
 
        dataPart = dataPart.substr( nSep + 1 );
 
    }
 
 
    /* For the last key=value */
 
    dataKeyVal = dataPart.substr( 0, nSep );
 
 
    /* Split them */
 
    nPos = dataKeyVal.find_first_of( "=" );
 
    if( std::string::npos != nPos )
 
    {
 
        dataKey = dataKeyVal.substr( 0, nPos );
 
        dataVal = dataKeyVal.substr( nPos + 1 );
 
 
        /* Put this key=value pair in map */
 
        rawDataKeyValuePairs[dataKey] = urlencodeData ? urlencode( dataVal ) : dataVal;
 
    }
 
}
 
 
/*++
 
* @method: oAuth::buildOAuthTokenKeyValuePairs
 
*
 
* @description: this method prepares key-value pairs required for OAuth header
 
*               and signature generation.
 
*
 
* @input: includeOAuthVerifierPin - flag to indicate whether oauth_verifer key-value
 
*                                   pair needs to be included. oauth_verifer is only
 
*                                   used during exchanging request token with access token.
 
*         oauthSignature - base64 and url encoded OAuth signature.
 
*         generateTimestamp - If true, then generate new timestamp for nonce.
 
*
 
* @output: keyValueMap - map in which key-value pairs are populated
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin,
 
                                          const std::string& oauthSignature,
 
                                          oAuthKeyValuePairs& keyValueMap,
 
                                          const bool generateTimestamp )
 
{
 
    /* Generate nonce and timestamp if required */
 
    if( generateTimestamp )
 
    {
 
        generateNonceTimeStamp();
 
    }
 
 
    /* Consumer key and its value */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_CONSUMERKEY_KEY] = m_consumerKey;
 
 
    /* Nonce key and its value */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_NONCE_KEY] = m_nonce;
 
 
    /* Signature if supplied */
 
    if( oauthSignature.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_SIGNATURE_KEY] = oauthSignature;
 
    }
 
 
    /* Signature method, only HMAC-SHA1 as of now */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_SIGNATUREMETHOD_KEY] = std::string( "HMAC-SHA1" );
 
 
    /* Timestamp */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_TIMESTAMP_KEY] = m_timeStamp;
 
 
    /* Token */
 
    if( m_oAuthTokenKey.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_TOKEN_KEY] = m_oAuthTokenKey;
 
    }
 
 
    /* Verifier */
 
    if( includeOAuthVerifierPin && m_oAuthPin.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_VERIFIER_KEY] = m_oAuthPin;
 
    }
 
 
    /* Version */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_VERSION_KEY] = std::string( "1.0" );
 
 
    return !keyValueMap.empty();
 
}
 
 
/*++
 
* @method: oAuth::getSignature
 
*
 
* @description: this method calculates HMAC-SHA1 signature of OAuth header
 
*
 
* @input: eType - HTTP request type
 
*         rawUrl - raw url of the HTTP request
 
*         rawKeyValuePairs - key-value pairs containing OAuth headers and HTTP data
 
*
 
* @output: oAuthSignature - base64 and url encoded signature
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::getSignature( const eOAuthHttpRequestType eType,
 
                          const std::string& rawUrl,
 
                          const oAuthKeyValuePairs& rawKeyValuePairs,
 
                          std::string& oAuthSignature )
 
{
 
    std::string rawParams;
 
    std::string paramsSeperator;
 
    std::string sigBase;
 
 
    /* Initially empty signature */
 
    oAuthSignature = "";
 
 
    /* Build a string using key-value pairs */
 
    paramsSeperator = "&";
 
    getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator );
 
 
    /* Start constructing base signature string. Refer http://dev.twitter.com/auth#intro */
 
    switch( eType )
 
    {
 
    case eOAuthHttpGet:
 
        {
 
            sigBase.assign( "GET&" );
 
        }
 
        break;
 
 
    case eOAuthHttpPost:
 
        {
 
            sigBase.assign( "POST&" );
 
        }
 
        break;
 
 
    case eOAuthHttpDelete:
 
        {
 
            sigBase.assign( "DELETE&" );
 
        }
 
        break;
 
 
    default:
 
        {
 
            return false;
 
        }
 
        break;
 
    }
 
    sigBase.append( urlencode( rawUrl ) );
 
    sigBase.append( "&" );
 
    sigBase.append( urlencode( rawParams ) );
 
 
    /* Now, hash the signature base string using HMAC_SHA1 class */
 
    CHMAC_SHA1 objHMACSHA1;
 
    std::string secretSigningKey;
 
    unsigned char strDigest[oAuthLibDefaults::OAUTHLIB_BUFFSIZE_LARGE];
 
 
    memset( strDigest, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE_LARGE );
 
 
    /* Signing key is composed of consumer_secret&token_secret */
 
    secretSigningKey.assign( m_consumerSecret );
 
    secretSigningKey.append( "&" );
 
    if( m_oAuthTokenSecret.length() )
 
    {
 
        secretSigningKey.append( m_oAuthTokenSecret );
 
    }
 
  
 
    objHMACSHA1.HMAC_SHA1( (unsigned char*)sigBase.c_str(),
 
                           sigBase.length(),
 
                           (unsigned char*)secretSigningKey.c_str(),
 
                           secretSigningKey.length(),
 
                           strDigest ); 
 
 
    /* Do a base64 encode of signature */
 
    std::string base64Str = base64_encode( strDigest, 20 /* SHA 1 digest is 160 bits */ );
 
 
    /* Do an url encode */
 
    oAuthSignature = urlencode( base64Str );
 
 
    return !oAuthSignature.empty();
 
}
 
 
/*++
 
* @method: oAuth::getOAuthHeader
 
*
 
* @description: this method builds OAuth header that should be used in HTTP requests to twitter
 
*
 
* @input: eType - HTTP request type
 
*         rawUrl - raw url of the HTTP request
 
*         rawData - HTTP data (post fields)
 
*         includeOAuthVerifierPin - flag to indicate whether or not oauth_verifier needs to included
 
*                                   in OAuth header
 
*
 
* @output: oAuthHttpHeader - OAuth header
 
*
 
*--*/
 
bool oAuth::getOAuthHeader( const eOAuthHttpRequestType eType,
 
                            const std::string& rawUrl,
 
                            const std::string& rawData,
 
                            std::string& oAuthHttpHeader,
 
                            const bool includeOAuthVerifierPin )
 
{
 
    oAuthKeyValuePairs rawKeyValuePairs;
 
    std::string rawParams;
 
    std::string oauthSignature;
 
    std::string paramsSeperator;
 
    std::string pureUrl( rawUrl );
 
 
    /* Clear header string initially */
 
    oAuthHttpHeader = "";
 
    rawKeyValuePairs.clear();
 
 
    /* If URL itself contains ?key=value, then extract and put them in map */
 
    size_t nPos = rawUrl.find_first_of( "?" );
 
    if( std::string::npos != nPos )
 
    {
 
        /* Get only URL */
 
        pureUrl = rawUrl.substr( 0, nPos );
 
 
        /* Get only key=value data part */
 
        std::string dataPart = rawUrl.substr( nPos + 1 );
 
 
        /* Split the data in URL as key=value pairs */
 
        buildOAuthRawDataKeyValPairs( dataPart, true, rawKeyValuePairs );
 
    }
 
 
    /* Split the raw data if it's present, as key=value pairs. Data should already be urlencoded once */
 
    buildOAuthRawDataKeyValPairs( rawData, false, rawKeyValuePairs );
 
 
    /* Build key-value pairs needed for OAuth request token, without signature */
 
    buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, std::string( "" ), rawKeyValuePairs, true );
 
 
    /* Get url encoded base64 signature using request type, url and parameters */
 
    getSignature( eType, pureUrl, rawKeyValuePairs, oauthSignature );
 
 
    /* Clear map so that the parameters themselves are not sent along with the OAuth values */
 
    rawKeyValuePairs.clear();
 
 
    /* Now, again build key-value pairs with signature this time */
 
    buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, oauthSignature, rawKeyValuePairs, false );
 
 
    /* Get OAuth header in string format */
 
    paramsSeperator = ",";
 
    getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator );
 
 
    /* Build authorization header */
 
    oAuthHttpHeader.assign( oAuthLibDefaults::OAUTHLIB_AUTHHEADER_STRING );
 
    oAuthHttpHeader.append( rawParams );
 
 
    return !oAuthHttpHeader.empty();
 
}
 
 
/*++
 
* @method: oAuth::getStringFromOAuthKeyValuePairs
 
*
 
* @description: this method builds a sorted string from key-value pairs
 
*
 
* @input: rawParamMap - key-value pairs map
 
*         paramsSeperator - sepearator, either & or ,
 
*
 
* @output: rawParams - sorted string of OAuth parameters
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::getStringFromOAuthKeyValuePairs( const oAuthKeyValuePairs& rawParamMap,
 
                                             std::string& rawParams,
 
                                             const std::string& paramsSeperator )
 
{
 
    rawParams = "";
 
    if( rawParamMap.empty() )
 
    {
 
        return false;
 
    }
 
 
    oAuthKeyValueList keyValueList;
 
    std::string dummyStr;
 
 
    /* Push key-value pairs to a list of strings */
 
    keyValueList.clear();
 
    oAuthKeyValuePairs::const_iterator itMap = rawParamMap.begin();
 
    for( ; itMap != rawParamMap.end(); itMap++ )
 
    {
 
        dummyStr.assign( itMap->first );
 
        dummyStr.append( "=" );
 
        if( paramsSeperator == "," )
 
        {
 
            dummyStr.append( "\"" );
 
        }
 
        dummyStr.append( itMap->second );
 
        if( paramsSeperator == "," )
 
        {
 
            dummyStr.append( "\"" );
 
        }
 
        keyValueList.push_back( dummyStr );
 
    }
 
 
    /* Sort key-value pairs based on key name */
 
    keyValueList.sort();
 
 
    /* Now, form a string */
 
    dummyStr = "";
 
    oAuthKeyValueList::iterator itKeyValue = keyValueList.begin();
 
    for( ; itKeyValue != keyValueList.end(); itKeyValue++ )
 
    {
 
        if( dummyStr.length() )
 
        {
 
            dummyStr.append( paramsSeperator );
 
         }
 
         dummyStr.append( itKeyValue->c_str() );
 
    }
 
    rawParams = dummyStr;
 
    return !rawParams.empty();
 
}
 
 
/*++
 
* @method: oAuth::extractOAuthTokenKeySecret
 
*
 
* @description: this method extracts oauth token key and secret from
 
*               twitter's HTTP response
 
*
 
* @input: requestTokenResponse - response from twitter
 
*
 
* @output: none
 
*
 
*--*/
 
bool oAuth::extractOAuthTokenKeySecret( const std::string& requestTokenResponse )
 
{
 
    if( requestTokenResponse.empty() )
 
    {
 
        return false;
 
    }
 
 
    size_t nPos = std::string::npos;
 
    std::string strDummy;
 
 
    /* Get oauth_token key */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_TOKEN_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_TOKEN_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        nPos = strDummy.find( "&" );
 
        if( std::string::npos != nPos )
 
        {
 
            m_oAuthTokenKey = strDummy.substr( 0, nPos );
 
        }
 
    }
 
 
    /* Get oauth_token_secret */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_TOKENSECRET_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_TOKENSECRET_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        nPos = strDummy.find( "&" );
 
        if( std::string::npos != nPos )
 
        {
 
            m_oAuthTokenSecret = strDummy.substr( 0, nPos );
 
        }
 
    }
 
 
    /* Get screen_name */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_SCREENNAME_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_SCREENNAME_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        m_oAuthScreenName = strDummy;
 
    }
 
 
    return true;
 
}
 
 
#include "twitcurlurls.h"
 
#include "oauthlib.h"
 
#include "HMAC_SHA1.h"
 
#include "base64.h"
 
#include "urlencode.h"
 

	
 
/*++
 
* @method: oAuth::oAuth
 
*
 
* @description: constructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
oAuth::oAuth()
 
{
 
}
 

	
 
/*++
 
* @method: oAuth::~oAuth
 
*
 
* @description: destructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
oAuth::~oAuth()
 
{
 
}
 

	
 
/*++
 
* @method: oAuth::clone
 
*
 
* @description: creates a clone of oAuth object
 
*
 
* @input: none
 
*
 
* @output: cloned oAuth object
 
*
 
*--*/
 
oAuth oAuth::clone()
 
{
 
    oAuth cloneObj;
 
    cloneObj.m_consumerKey = m_consumerKey;
 
    cloneObj.m_consumerSecret = m_consumerSecret;
 
    cloneObj.m_oAuthTokenKey = m_oAuthTokenKey;
 
    cloneObj.m_oAuthTokenSecret = m_oAuthTokenSecret;
 
    cloneObj.m_oAuthPin = m_oAuthPin;
 
    cloneObj.m_nonce = m_nonce;
 
    cloneObj.m_timeStamp = m_timeStamp;
 
    cloneObj.m_oAuthScreenName =  m_oAuthScreenName;
 
    return cloneObj;
 
}
 

	
 

	
 
/*++
 
* @method: oAuth::getConsumerKey
 
*
 
* @description: this method gives consumer key that is being used currently
 
*
 
* @input: none
 
*
 
* @output: consumer key
 
*
 
*--*/
 
void oAuth::getConsumerKey( std::string& consumerKey )
 
{
 
    consumerKey = m_consumerKey;
 
}
 

	
 
/*++
 
* @method: oAuth::setConsumerKey
 
*
 
* @description: this method saves consumer key that should be used
 
*
 
* @input: consumer key
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setConsumerKey( const std::string& consumerKey )
 
{
 
    m_consumerKey.assign( consumerKey );
 
}
 

	
 
/*++
 
* @method: oAuth::getConsumerSecret
 
*
 
* @description: this method gives consumer secret that is being used currently
 
*
 
* @input: none
 
*
 
* @output: consumer secret
 
*
 
*--*/
 
void oAuth::getConsumerSecret( std::string& consumerSecret )
 
{
 
    consumerSecret = m_consumerSecret;
 
}
 

	
 
/*++
 
* @method: oAuth::setConsumerSecret
 
*
 
* @description: this method saves consumer secret that should be used
 
*
 
* @input: consumer secret
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setConsumerSecret( const std::string& consumerSecret )
 
{
 
    m_consumerSecret = consumerSecret;
 
}
 

	
 
/*++
 
* @method: oAuth::getOAuthTokenKey
 
*
 
* @description: this method gives OAuth token (also called access token) that is being used currently
 
*
 
* @input: none
 
*
 
* @output: OAuth token
 
*
 
*--*/
 
void oAuth::getOAuthTokenKey( std::string& oAuthTokenKey )
 
{
 
    oAuthTokenKey = m_oAuthTokenKey;
 
}
 

	
 
/*++
 
* @method: oAuth::setOAuthTokenKey
 
*
 
* @description: this method saves OAuth token that should be used
 
*
 
* @input: OAuth token
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthTokenKey( const std::string& oAuthTokenKey )
 
{
 
    m_oAuthTokenKey = oAuthTokenKey;
 
}
 

	
 
/*++
 
* @method: oAuth::getOAuthTokenSecret
 
*
 
* @description: this method gives OAuth token secret that is being used currently
 
*
 
* @input: none
 
*
 
* @output: OAuth token secret
 
*
 
*--*/
 
void oAuth::getOAuthTokenSecret( std::string& oAuthTokenSecret )
 
{
 
    oAuthTokenSecret = m_oAuthTokenSecret;
 
}
 

	
 
/*++
 
* @method: oAuth::setOAuthTokenSecret
 
*
 
* @description: this method saves OAuth token that should be used
 
*
 
* @input: OAuth token secret
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthTokenSecret( const std::string& oAuthTokenSecret )
 
{
 
    m_oAuthTokenSecret = oAuthTokenSecret;
 
}
 

	
 
/*++
 
* @method: oAuth::getOAuthScreenName
 
*
 
* @description: this method gives authorized user's screenname
 
*
 
* @input: none
 
*
 
* @output: screen name
 
*
 
*--*/
 
void oAuth::getOAuthScreenName( std::string& oAuthScreenName )
 
{
 
    oAuthScreenName = m_oAuthScreenName;
 
}
 

	
 
/*++
 
* @method: oAuth::setOAuthScreenName
 
*
 
* @description: this method sets authorized user's screenname
 
*
 
* @input: screen name
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthScreenName( const std::string& oAuthScreenName )
 
{
 
    m_oAuthScreenName = oAuthScreenName;
 
}
 

	
 
/*++
 
* @method: oAuth::getOAuthPin
 
*
 
* @description: this method gives OAuth verifier PIN
 
*
 
* @input: none
 
*
 
* @output: OAuth verifier PIN
 
*
 
*--*/
 
void oAuth::getOAuthPin( std::string& oAuthPin )
 
{
 
    oAuthPin = m_oAuthPin;
 
}
 

	
 
/*++
 
* @method: oAuth::setOAuthPin
 
*
 
* @description: this method sets OAuth verifier PIN
 
*
 
* @input: OAuth verifier PIN
 
*
 
* @output: none
 
*
 
*--*/
 
void oAuth::setOAuthPin( const std::string& oAuthPin )
 
{
 
    m_oAuthPin = oAuthPin;
 
}
 

	
 
/*++
 
* @method: oAuth::generateNonceTimeStamp
 
*
 
* @description: this method generates nonce and timestamp for OAuth header
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void oAuth::generateNonceTimeStamp()
 
{
 
    char szTime[oAuthLibDefaults::OAUTHLIB_BUFFSIZE];
 
    char szRand[oAuthLibDefaults::OAUTHLIB_BUFFSIZE];
 
    memset( szTime, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE );
 
    memset( szRand, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE );
 
    srand( (unsigned int)time( NULL ) );
 
    sprintf( szRand, "%x", rand()%1000 );
 
    sprintf( szTime, "%ld", time( NULL ) );
 

	
 
    m_nonce.assign( szTime );
 
    m_nonce.append( szRand );
 
    m_timeStamp.assign( szTime );
 
}
 

	
 
/*++
 
* @method: oAuth::buildOAuthRawDataKeyValPairs
 
*
 
* @description: this method prepares key-value pairs from the data part of the URL
 
*               or from the URL post fields data, as required by OAuth header
 
*               and signature generation.
 
*
 
* @input: rawData - Raw data either from the URL itself or from post fields.
 
*                   Should already be url encoded.
 
*         urlencodeData - If true, string will be urlencoded before converting
 
*                         to key value pairs.
 
*
 
* @output: rawDataKeyValuePairs - Map in which key-value pairs are populated
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void oAuth::buildOAuthRawDataKeyValPairs( const std::string& rawData,
 
                                          bool urlencodeData,
 
                                          oAuthKeyValuePairs& rawDataKeyValuePairs )
 
{
 
    /* Raw data if it's present. Data should already be urlencoded once */
 
    if( rawData.empty() )
 
    {
 
        return;
 
    }
 

	
 
    size_t nSep = std::string::npos;
 
    size_t nPos = std::string::npos;
 
    std::string dataKeyVal;
 
    std::string dataKey;
 
    std::string dataVal;
 

	
 
    /* This raw data part can contain many key value pairs: key1=value1&key2=value2&key3=value3 */
 
    std::string dataPart = rawData;
 
    while( std::string::npos != ( nSep = dataPart.find_first_of("&") ) )
 
    {
 
        /* Extract first key=value pair */
 
        dataKeyVal = dataPart.substr( 0, nSep );
 

	
 
        /* Split them */
 
        nPos = dataKeyVal.find_first_of( "=" );
 
        if( std::string::npos != nPos )
 
        {
 
            dataKey = dataKeyVal.substr( 0, nPos );
 
            dataVal = dataKeyVal.substr( nPos + 1 );
 

	
 
            /* Put this key=value pair in map */
 
            rawDataKeyValuePairs[dataKey] = urlencodeData ? urlencode( dataVal ) : dataVal;
 
        }
 
        dataPart = dataPart.substr( nSep + 1 );
 
    }
 

	
 
    /* For the last key=value */
 
    dataKeyVal = dataPart.substr( 0, nSep );
 

	
 
    /* Split them */
 
    nPos = dataKeyVal.find_first_of( "=" );
 
    if( std::string::npos != nPos )
 
    {
 
        dataKey = dataKeyVal.substr( 0, nPos );
 
        dataVal = dataKeyVal.substr( nPos + 1 );
 

	
 
        /* Put this key=value pair in map */
 
        rawDataKeyValuePairs[dataKey] = urlencodeData ? urlencode( dataVal ) : dataVal;
 
    }
 
}
 

	
 
/*++
 
* @method: oAuth::buildOAuthTokenKeyValuePairs
 
*
 
* @description: this method prepares key-value pairs required for OAuth header
 
*               and signature generation.
 
*
 
* @input: includeOAuthVerifierPin - flag to indicate whether oauth_verifer key-value
 
*                                   pair needs to be included. oauth_verifer is only
 
*                                   used during exchanging request token with access token.
 
*         oauthSignature - base64 and url encoded OAuth signature.
 
*         generateTimestamp - If true, then generate new timestamp for nonce.
 
*
 
* @output: keyValueMap - map in which key-value pairs are populated
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin,
 
                                          const std::string& oauthSignature,
 
                                          oAuthKeyValuePairs& keyValueMap,
 
                                          const bool generateTimestamp )
 
{
 
    /* Generate nonce and timestamp if required */
 
    if( generateTimestamp )
 
    {
 
        generateNonceTimeStamp();
 
    }
 

	
 
    /* Consumer key and its value */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_CONSUMERKEY_KEY] = m_consumerKey;
 

	
 
    /* Nonce key and its value */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_NONCE_KEY] = m_nonce;
 

	
 
    /* Signature if supplied */
 
    if( oauthSignature.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_SIGNATURE_KEY] = oauthSignature;
 
    }
 

	
 
    /* Signature method, only HMAC-SHA1 as of now */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_SIGNATUREMETHOD_KEY] = std::string( "HMAC-SHA1" );
 

	
 
    /* Timestamp */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_TIMESTAMP_KEY] = m_timeStamp;
 

	
 
    /* Token */
 
    if( m_oAuthTokenKey.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_TOKEN_KEY] = m_oAuthTokenKey;
 
    }
 

	
 
    /* Verifier */
 
    if( includeOAuthVerifierPin && m_oAuthPin.length() )
 
    {
 
        keyValueMap[oAuthLibDefaults::OAUTHLIB_VERIFIER_KEY] = m_oAuthPin;
 
    }
 

	
 
    /* Version */
 
    keyValueMap[oAuthLibDefaults::OAUTHLIB_VERSION_KEY] = std::string( "1.0" );
 

	
 
    return !keyValueMap.empty();
 
}
 

	
 
/*++
 
* @method: oAuth::getSignature
 
*
 
* @description: this method calculates HMAC-SHA1 signature of OAuth header
 
*
 
* @input: eType - HTTP request type
 
*         rawUrl - raw url of the HTTP request
 
*         rawKeyValuePairs - key-value pairs containing OAuth headers and HTTP data
 
*
 
* @output: oAuthSignature - base64 and url encoded signature
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::getSignature( const eOAuthHttpRequestType eType,
 
                          const std::string& rawUrl,
 
                          const oAuthKeyValuePairs& rawKeyValuePairs,
 
                          std::string& oAuthSignature )
 
{
 
    std::string rawParams;
 
    std::string paramsSeperator;
 
    std::string sigBase;
 

	
 
    /* Initially empty signature */
 
    oAuthSignature = "";
 

	
 
    /* Build a string using key-value pairs */
 
    paramsSeperator = "&";
 
    getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator );
 

	
 
    /* Start constructing base signature string. Refer http://dev.twitter.com/auth#intro */
 
    switch( eType )
 
    {
 
    case eOAuthHttpGet:
 
        {
 
            sigBase.assign( "GET&" );
 
        }
 
        break;
 

	
 
    case eOAuthHttpPost:
 
        {
 
            sigBase.assign( "POST&" );
 
        }
 
        break;
 

	
 
    case eOAuthHttpDelete:
 
        {
 
            sigBase.assign( "DELETE&" );
 
        }
 
        break;
 

	
 
    default:
 
        {
 
            return false;
 
        }
 
        break;
 
    }
 
    sigBase.append( urlencode( rawUrl ) );
 
    sigBase.append( "&" );
 
    sigBase.append( urlencode( rawParams ) );
 

	
 
    /* Now, hash the signature base string using HMAC_SHA1 class */
 
    CHMAC_SHA1 objHMACSHA1;
 
    std::string secretSigningKey;
 
    unsigned char strDigest[oAuthLibDefaults::OAUTHLIB_BUFFSIZE_LARGE];
 

	
 
    memset( strDigest, 0, oAuthLibDefaults::OAUTHLIB_BUFFSIZE_LARGE );
 

	
 
    /* Signing key is composed of consumer_secret&token_secret */
 
    secretSigningKey.assign( m_consumerSecret );
 
    secretSigningKey.append( "&" );
 
    if( m_oAuthTokenSecret.length() )
 
    {
 
        secretSigningKey.append( m_oAuthTokenSecret );
 
    }
 
  
 
    objHMACSHA1.HMAC_SHA1( (unsigned char*)sigBase.c_str(),
 
                           sigBase.length(),
 
                           (unsigned char*)secretSigningKey.c_str(),
 
                           secretSigningKey.length(),
 
                           strDigest ); 
 

	
 
    /* Do a base64 encode of signature */
 
    std::string base64Str = base64_encode( strDigest, 20 /* SHA 1 digest is 160 bits */ );
 

	
 
    /* Do an url encode */
 
    oAuthSignature = urlencode( base64Str );
 

	
 
    return !oAuthSignature.empty();
 
}
 

	
 
/*++
 
* @method: oAuth::getOAuthHeader
 
*
 
* @description: this method builds OAuth header that should be used in HTTP requests to twitter
 
*
 
* @input: eType - HTTP request type
 
*         rawUrl - raw url of the HTTP request
 
*         rawData - HTTP data (post fields)
 
*         includeOAuthVerifierPin - flag to indicate whether or not oauth_verifier needs to included
 
*                                   in OAuth header
 
*
 
* @output: oAuthHttpHeader - OAuth header
 
*
 
*--*/
 
bool oAuth::getOAuthHeader( const eOAuthHttpRequestType eType,
 
                            const std::string& rawUrl,
 
                            const std::string& rawData,
 
                            std::string& oAuthHttpHeader,
 
                            const bool includeOAuthVerifierPin )
 
{
 
    oAuthKeyValuePairs rawKeyValuePairs;
 
    std::string rawParams;
 
    std::string oauthSignature;
 
    std::string paramsSeperator;
 
    std::string pureUrl( rawUrl );
 

	
 
    /* Clear header string initially */
 
    oAuthHttpHeader = "";
 
    rawKeyValuePairs.clear();
 

	
 
    /* If URL itself contains ?key=value, then extract and put them in map */
 
    size_t nPos = rawUrl.find_first_of( "?" );
 
    if( std::string::npos != nPos )
 
    {
 
        /* Get only URL */
 
        pureUrl = rawUrl.substr( 0, nPos );
 

	
 
        /* Get only key=value data part */
 
        std::string dataPart = rawUrl.substr( nPos + 1 );
 

	
 
        /* Split the data in URL as key=value pairs */
 
        buildOAuthRawDataKeyValPairs( dataPart, true, rawKeyValuePairs );
 
    }
 

	
 
    /* Split the raw data if it's present, as key=value pairs. Data should already be urlencoded once */
 
    buildOAuthRawDataKeyValPairs( rawData, false, rawKeyValuePairs );
 

	
 
    /* Build key-value pairs needed for OAuth request token, without signature */
 
    buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, std::string( "" ), rawKeyValuePairs, true );
 

	
 
    /* Get url encoded base64 signature using request type, url and parameters */
 
    getSignature( eType, pureUrl, rawKeyValuePairs, oauthSignature );
 

	
 
    /* Clear map so that the parameters themselves are not sent along with the OAuth values */
 
    rawKeyValuePairs.clear();
 

	
 
    /* Now, again build key-value pairs with signature this time */
 
    buildOAuthTokenKeyValuePairs( includeOAuthVerifierPin, oauthSignature, rawKeyValuePairs, false );
 

	
 
    /* Get OAuth header in string format */
 
    paramsSeperator = ",";
 
    getStringFromOAuthKeyValuePairs( rawKeyValuePairs, rawParams, paramsSeperator );
 

	
 
    /* Build authorization header */
 
    oAuthHttpHeader.assign( oAuthLibDefaults::OAUTHLIB_AUTHHEADER_STRING );
 
    oAuthHttpHeader.append( rawParams );
 

	
 
    return !oAuthHttpHeader.empty();
 
}
 

	
 
/*++
 
* @method: oAuth::getStringFromOAuthKeyValuePairs
 
*
 
* @description: this method builds a sorted string from key-value pairs
 
*
 
* @input: rawParamMap - key-value pairs map
 
*         paramsSeperator - sepearator, either & or ,
 
*
 
* @output: rawParams - sorted string of OAuth parameters
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool oAuth::getStringFromOAuthKeyValuePairs( const oAuthKeyValuePairs& rawParamMap,
 
                                             std::string& rawParams,
 
                                             const std::string& paramsSeperator )
 
{
 
    rawParams = "";
 
    if( rawParamMap.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    oAuthKeyValueList keyValueList;
 
    std::string dummyStr;
 

	
 
    /* Push key-value pairs to a list of strings */
 
    keyValueList.clear();
 
    oAuthKeyValuePairs::const_iterator itMap = rawParamMap.begin();
 
    for( ; itMap != rawParamMap.end(); itMap++ )
 
    {
 
        dummyStr.assign( itMap->first );
 
        dummyStr.append( "=" );
 
        if( paramsSeperator == "," )
 
        {
 
            dummyStr.append( "\"" );
 
        }
 
        dummyStr.append( itMap->second );
 
        if( paramsSeperator == "," )
 
        {
 
            dummyStr.append( "\"" );
 
        }
 
        keyValueList.push_back( dummyStr );
 
    }
 

	
 
    /* Sort key-value pairs based on key name */
 
    keyValueList.sort();
 

	
 
    /* Now, form a string */
 
    dummyStr = "";
 
    oAuthKeyValueList::iterator itKeyValue = keyValueList.begin();
 
    for( ; itKeyValue != keyValueList.end(); itKeyValue++ )
 
    {
 
        if( dummyStr.length() )
 
        {
 
            dummyStr.append( paramsSeperator );
 
         }
 
         dummyStr.append( itKeyValue->c_str() );
 
    }
 
    rawParams = dummyStr;
 
    return !rawParams.empty();
 
}
 

	
 
/*++
 
* @method: oAuth::extractOAuthTokenKeySecret
 
*
 
* @description: this method extracts oauth token key and secret from
 
*               twitter's HTTP response
 
*
 
* @input: requestTokenResponse - response from twitter
 
*
 
* @output: none
 
*
 
*--*/
 
bool oAuth::extractOAuthTokenKeySecret( const std::string& requestTokenResponse )
 
{
 
    if( requestTokenResponse.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    size_t nPos = std::string::npos;
 
    std::string strDummy;
 

	
 
    /* Get oauth_token key */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_TOKEN_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_TOKEN_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        nPos = strDummy.find( "&" );
 
        if( std::string::npos != nPos )
 
        {
 
            m_oAuthTokenKey = strDummy.substr( 0, nPos );
 
        }
 
    }
 

	
 
    /* Get oauth_token_secret */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_TOKENSECRET_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_TOKENSECRET_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        nPos = strDummy.find( "&" );
 
        if( std::string::npos != nPos )
 
        {
 
            m_oAuthTokenSecret = strDummy.substr( 0, nPos );
 
        }
 
    }
 

	
 
    /* Get screen_name */
 
    nPos = requestTokenResponse.find( oAuthLibDefaults::OAUTHLIB_SCREENNAME_KEY );
 
    if( std::string::npos != nPos )
 
    {
 
        nPos = nPos + oAuthLibDefaults::OAUTHLIB_SCREENNAME_KEY.length() + strlen( "=" );
 
        strDummy = requestTokenResponse.substr( nPos );
 
        m_oAuthScreenName = strDummy;
 
    }
 

	
 
    return true;
 
}
 

	
backends/twitter/libtwitcurl/oauthlib.h
Show inline comments
 
#ifndef __OAUTHLIB_H__
 
#define __OAUTHLIB_H__
 
 
#include "time.h"
 
#include <cstdlib>
 
#include <sstream>
 
#include <iostream>
 
#include <fstream>
 
#include <string>
 
#include <list>
 
#include <map>
 
 
typedef enum _eOAuthHttpRequestType
 
{
 
    eOAuthHttpInvalid = 0,
 
    eOAuthHttpGet,
 
    eOAuthHttpPost,
 
    eOAuthHttpDelete
 
} eOAuthHttpRequestType;
 
 
typedef std::list<std::string> oAuthKeyValueList;
 
typedef std::map<std::string, std::string> oAuthKeyValuePairs;
 
 
class oAuth
 
{
 
public:
 
    oAuth();
 
    ~oAuth();
 
 
    /* OAuth public methods used by twitCurl */
 
    void getConsumerKey( std::string& consumerKey /* out */ );
 
    void setConsumerKey( const std::string& consumerKey /* in */ );
 
 
    void getConsumerSecret( std::string& consumerSecret /* out */ );
 
    void setConsumerSecret( const std::string& consumerSecret /* in */ );
 
 
    void getOAuthTokenKey( std::string& oAuthTokenKey /* out */ );
 
    void setOAuthTokenKey( const std::string& oAuthTokenKey /* in */ );
 
 
    void getOAuthTokenSecret( std::string& oAuthTokenSecret /* out */ );
 
    void setOAuthTokenSecret( const std::string& oAuthTokenSecret /* in */ );
 
 
    void getOAuthScreenName( std::string& oAuthScreenName /* out */ );
 
    void setOAuthScreenName( const std::string& oAuthScreenName /* in */ );
 
 
    void getOAuthPin( std::string& oAuthPin /* out */ );
 
    void setOAuthPin( const std::string& oAuthPin /* in */ );
 
 
    bool getOAuthHeader( const eOAuthHttpRequestType eType, /* in */
 
                         const std::string& rawUrl, /* in */
 
                         const std::string& rawData, /* in */
 
                         std::string& oAuthHttpHeader, /* out */
 
                         const bool includeOAuthVerifierPin = false /* in */ );
 
 
    bool extractOAuthTokenKeySecret( const std::string& requestTokenResponse /* in */ );
 
 
    oAuth clone();
 
 
private:
 
 
    /* OAuth data */
 
    std::string m_consumerKey;
 
    std::string m_consumerSecret;
 
    std::string m_oAuthTokenKey;
 
    std::string m_oAuthTokenSecret;
 
    std::string m_oAuthPin;
 
    std::string m_nonce;
 
    std::string m_timeStamp;
 
    std::string m_oAuthScreenName;
 
 
    /* OAuth twitter related utility methods */
 
    void buildOAuthRawDataKeyValPairs( const std::string& rawData, /* in */
 
                                       bool urlencodeData, /* in */
 
                                       oAuthKeyValuePairs& rawDataKeyValuePairs /* out */ );
 
 
    bool buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin, /* in */
 
                                       const std::string& oauthSignature, /* in */
 
                                       oAuthKeyValuePairs& keyValueMap /* out */,
 
                                       const bool generateTimestamp /* in */ );
 
 
    bool getStringFromOAuthKeyValuePairs( const oAuthKeyValuePairs& rawParamMap, /* in */
 
                                          std::string& rawParams, /* out */
 
                                          const std::string& paramsSeperator /* in */ );
 
 
    bool getSignature( const eOAuthHttpRequestType eType, /* in */
 
                       const std::string& rawUrl, /* in */
 
                       const oAuthKeyValuePairs& rawKeyValuePairs, /* in */
 
                       std::string& oAuthSignature /* out */ );
 
 
    void generateNonceTimeStamp();
 
};
 
 
#endif // __OAUTHLIB_H__
 
#ifndef __OAUTHLIB_H__
 
#define __OAUTHLIB_H__
 

	
 
#include "time.h"
 
#include <cstdlib>
 
#include <sstream>
 
#include <iostream>
 
#include <fstream>
 
#include <string>
 
#include <list>
 
#include <map>
 

	
 
typedef enum _eOAuthHttpRequestType
 
{
 
    eOAuthHttpInvalid = 0,
 
    eOAuthHttpGet,
 
    eOAuthHttpPost,
 
    eOAuthHttpDelete
 
} eOAuthHttpRequestType;
 

	
 
typedef std::list<std::string> oAuthKeyValueList;
 
typedef std::map<std::string, std::string> oAuthKeyValuePairs;
 

	
 
class oAuth
 
{
 
public:
 
    oAuth();
 
    ~oAuth();
 

	
 
    /* OAuth public methods used by twitCurl */
 
    void getConsumerKey( std::string& consumerKey /* out */ );
 
    void setConsumerKey( const std::string& consumerKey /* in */ );
 

	
 
    void getConsumerSecret( std::string& consumerSecret /* out */ );
 
    void setConsumerSecret( const std::string& consumerSecret /* in */ );
 

	
 
    void getOAuthTokenKey( std::string& oAuthTokenKey /* out */ );
 
    void setOAuthTokenKey( const std::string& oAuthTokenKey /* in */ );
 

	
 
    void getOAuthTokenSecret( std::string& oAuthTokenSecret /* out */ );
 
    void setOAuthTokenSecret( const std::string& oAuthTokenSecret /* in */ );
 

	
 
    void getOAuthScreenName( std::string& oAuthScreenName /* out */ );
 
    void setOAuthScreenName( const std::string& oAuthScreenName /* in */ );
 

	
 
    void getOAuthPin( std::string& oAuthPin /* out */ );
 
    void setOAuthPin( const std::string& oAuthPin /* in */ );
 

	
 
    bool getOAuthHeader( const eOAuthHttpRequestType eType, /* in */
 
                         const std::string& rawUrl, /* in */
 
                         const std::string& rawData, /* in */
 
                         std::string& oAuthHttpHeader, /* out */
 
                         const bool includeOAuthVerifierPin = false /* in */ );
 

	
 
    bool extractOAuthTokenKeySecret( const std::string& requestTokenResponse /* in */ );
 

	
 
    oAuth clone();
 

	
 
private:
 

	
 
    /* OAuth data */
 
    std::string m_consumerKey;
 
    std::string m_consumerSecret;
 
    std::string m_oAuthTokenKey;
 
    std::string m_oAuthTokenSecret;
 
    std::string m_oAuthPin;
 
    std::string m_nonce;
 
    std::string m_timeStamp;
 
    std::string m_oAuthScreenName;
 

	
 
    /* OAuth twitter related utility methods */
 
    void buildOAuthRawDataKeyValPairs( const std::string& rawData, /* in */
 
                                       bool urlencodeData, /* in */
 
                                       oAuthKeyValuePairs& rawDataKeyValuePairs /* out */ );
 

	
 
    bool buildOAuthTokenKeyValuePairs( const bool includeOAuthVerifierPin, /* in */
 
                                       const std::string& oauthSignature, /* in */
 
                                       oAuthKeyValuePairs& keyValueMap /* out */,
 
                                       const bool generateTimestamp /* in */ );
 

	
 
    bool getStringFromOAuthKeyValuePairs( const oAuthKeyValuePairs& rawParamMap, /* in */
 
                                          std::string& rawParams, /* out */
 
                                          const std::string& paramsSeperator /* in */ );
 

	
 
    bool getSignature( const eOAuthHttpRequestType eType, /* in */
 
                       const std::string& rawUrl, /* in */
 
                       const oAuthKeyValuePairs& rawKeyValuePairs, /* in */
 
                       std::string& oAuthSignature /* out */ );
 

	
 
    void generateNonceTimeStamp();
 
};
 

	
 
#endif // __OAUTHLIB_H__
backends/twitter/libtwitcurl/twitcurl.cpp
Show inline comments
 
#define NOMINMAX
 
#include <algorithm>
 
#include <memory.h>
 
#include "twitcurlurls.h"
 
#include "twitcurl.h"
 
#include "urlencode.h"
 
 
/*++
 
* @method: twitCurl::twitCurl
 
*
 
* @description: constructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
twitCurl::twitCurl():
 
m_curlHandle( NULL ),
 
m_curlProxyParamsSet( false ),
 
m_curlLoginParamsSet( false ),
 
m_curlCallbackParamsSet( false ),
 
m_eApiFormatType( twitCurlTypes::eTwitCurlApiFormatJson ),
 
m_eProtocolType( twitCurlTypes::eTwitCurlProtocolHttps )
 
{
 
    /* Alloc memory for cURL error responses */
 
    m_errorBuffer = (char*)malloc( twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE );
 
 
    /* Clear callback buffers */
 
    clearCurlCallbackBuffers();
 
 
    /* Initialize cURL */
 
    m_curlHandle = curl_easy_init();
 
    if( NULL == m_curlHandle )
 
    {
 
        std::string dummyStr;
 
        getLastCurlError( dummyStr );
 
    }
 
    curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0);
 
}
 
 
/*++
 
* @method: twitCurl::~twitCurl
 
*
 
* @description: destructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
twitCurl::~twitCurl()
 
{
 
    /* Cleanup cURL */
 
    if( m_curlHandle )
 
    {
 
        curl_easy_cleanup( m_curlHandle );
 
        m_curlHandle = NULL;
 
    }
 
    if( m_errorBuffer )
 
    {
 
        free( m_errorBuffer );
 
        m_errorBuffer = NULL;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::clone
 
*
 
* @description: creates a clone of twitcurl object
 
*
 
* @input: none
 
*
 
* @output: cloned object
 
*
 
*--*/
 
twitCurl* twitCurl::clone()
 
{
 
    twitCurl *cloneObj = new twitCurl();
 
 
    /* cURL proxy data */
 
    cloneObj->setProxyServerIp(m_proxyServerIp);
 
    cloneObj->setProxyServerPort(m_proxyServerPort);
 
    cloneObj->setProxyUserName(m_proxyUserName);
 
    cloneObj->setProxyPassword(m_proxyPassword);
 
 
    /* Twitter data */
 
    cloneObj->setTwitterUsername(m_twitterUsername);
 
    cloneObj->setTwitterPassword(m_twitterPassword);
 
 
    /* OAuth data */
 
    cloneObj->m_oAuth = m_oAuth.clone();
 
 
    return cloneObj;
 
}
 
 
/*++
 
* @method: twitCurl::isCurlInit
 
*
 
* @description: method to check if cURL is initialized properly
 
*
 
* @input: none
 
*
 
* @output: true if cURL is intialized, otherwise false
 
*
 
*--*/
 
bool twitCurl::isCurlInit()
 
{
 
    return ( NULL != m_curlHandle ) ? true : false;
 
}
 
 
/*++
 
* @method: twitCurl::getTwitterUsername
 
*
 
* @description: method to get stored Twitter username
 
*
 
* @input: none
 
*
 
* @output: twitter username
 
*
 
*--*/
 
std::string& twitCurl::getTwitterUsername()
 
{
 
    return m_twitterUsername;
 
}
 
 
/*++
 
* @method: twitCurl::getTwitterPassword
 
*
 
* @description: method to get stored Twitter password
 
*
 
* @input: none
 
*
 
* @output: twitter password
 
*
 
*--*/
 
std::string& twitCurl::getTwitterPassword()
 
{
 
    return m_twitterPassword;
 
}
 
 
/*++
 
* @method: twitCurl::setTwitterUsername
 
*
 
* @description: method to set username
 
*
 
* @input: userName
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setTwitterUsername( std::string& userName )
 
{
 
    if( userName.length() )
 
    {
 
        m_twitterUsername = userName;
 
        m_curlLoginParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::setTwitterPassword
 
*
 
* @description: method to set password
 
*
 
* @input: passWord
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setTwitterPassword( std::string& passWord )
 
{
 
    if( passWord.length() )
 
    {
 
        m_twitterPassword = passWord;
 
        m_curlLoginParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::getProxyServerIp
 
*
 
* @description: method to get proxy server IP address
 
*
 
* @input: none
 
*
 
* @output: proxy server IP address
 
*
 
*--*/
 
std::string& twitCurl::getProxyServerIp()
 
{
 
    return m_proxyServerIp;
 
}
 
 
/*++
 
* @method: twitCurl::getProxyServerPort
 
*
 
* @description: method to get proxy server port
 
*
 
* @input: none
 
*
 
* @output: proxy server port
 
*
 
*--*/
 
std::string& twitCurl::getProxyServerPort()
 
{
 
    return m_proxyServerPort;
 
}
 
 
/*++
 
* @method: twitCurl::getProxyUserName
 
*
 
* @description: method to get proxy user name
 
*
 
* @input: none
 
*
 
* @output: proxy server user name
 
*
 
*--*/
 
std::string& twitCurl::getProxyUserName()
 
{
 
    return m_proxyUserName;
 
}
 
 
/*++
 
* @method: twitCurl::getProxyPassword
 
*
 
* @description: method to get proxy server password
 
*
 
* @input: none
 
*
 
* @output: proxy server password
 
*
 
*--*/
 
std::string& twitCurl::getProxyPassword()
 
{
 
    return m_proxyPassword;
 
}
 
 
/*++
 
* @method: twitCurl::setProxyServerIp
 
*
 
* @description: method to set proxy server IP address
 
*
 
* @input: proxyServerIp
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyServerIp( std::string& proxyServerIp )
 
{
 
    if( proxyServerIp.length() )
 
    {
 
        m_proxyServerIp = proxyServerIp;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::setProxyServerPort
 
*
 
* @description: method to set proxy server port
 
*
 
* @input: proxyServerPort
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyServerPort( std::string& proxyServerPort )
 
{
 
    if( proxyServerPort.length() )
 
    {
 
        m_proxyServerPort = proxyServerPort;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::setProxyUserName
 
*
 
* @description: method to set proxy server username
 
*
 
* @input: proxyUserName
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyUserName( std::string& proxyUserName )
 
{
 
    if( proxyUserName.length() )
 
    {
 
        m_proxyUserName = proxyUserName;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::setProxyPassword
 
*
 
* @description: method to set proxy server password
 
*
 
* @input: proxyPassword
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyPassword( std::string& proxyPassword )
 
{
 
    if( proxyPassword.length() )
 
    {
 
        m_proxyPassword = proxyPassword;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::search
 
*
 
* @description: method to return tweets that match a specified query.
 
*
 
* @input: searchQuery - search query in string format
 
*         resultCount - optional search result count
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
* @note: Only ATOM and JSON format supported.
 
*
 
*--*/
 
bool twitCurl::search( std::string& searchQuery, std::string resultCount )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SEARCH_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] +
 
                           twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SEARCHQUERYSTRING +
 
                           searchQuery;
 
 
    /* Add number of results count if provided */
 
    if( resultCount.size() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_COUNT + urlencode( resultCount );
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::statusUpdate
 
*
 
* @description: method to update new status message in twitter profile
 
*
 
* @input: newStatus - status message text
 
*         inReplyToStatusId - optional status id to we're replying to
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusUpdate( std::string& newStatus, std::string inReplyToStatusId )
 
{
 
    if( newStatus.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare new status message */
 
    std::string newStatusMsg = twitCurlDefaults::TWITCURL_STATUSSTRING + urlencode( newStatus );
 
 
    /* Append status id to which we're replying to */
 
    if( inReplyToStatusId.size() )
 
    {
 
        newStatusMsg += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                        twitCurlDefaults::TWITCURL_INREPLYTOSTATUSID +
 
                        urlencode( inReplyToStatusId );
 
    }
 
 
    /* Perform POST */
 
    return  performPost( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                         twitterDefaults::TWITCURL_STATUSUPDATE_URL +
 
                         twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                         newStatusMsg );
 
}
 
 
/*++
 
* @method: twitCurl::statusShowById
 
*
 
* @description: method to get a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusShowById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_STATUSSHOW_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::statusDestroyById
 
*
 
* @description: method to delete a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusDestroyById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_STATUDESTROY_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::retweetById
 
*
 
* @description: method to RETWEET a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if RETWEET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::retweetById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_RETWEET_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 
 
    /* Perform Retweet */
 
    return performPost( buildUrl, dummyData );
 
}
 
 
/*++
 
* @method: twitCurl::timelineHomeGet
 
*
 
* @description: method to get home timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineHomeGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_HOME_TIMELINE_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::timelinePublicGet
 
*
 
* @description: method to get public timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelinePublicGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_PUBLIC_TIMELINE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::featuredUsersGet
 
*
 
* @description: method to get featured users
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::featuredUsersGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FEATURED_USERS_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::timelineFriendsGet
 
*
 
* @description: method to get friends timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineFriendsGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FRIENDS_TIMELINE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::mentionsGet
 
*
 
* @description: method to get mentions
 
*
 
* @input: sinceId - String specifying since id parameter
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::mentionsGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_MENTIONS_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::timelineUserGet
 
*
 
* @description: method to get mentions
 
*
 
* @input: trimUser - Trim user name if true
 
*         tweetCount - Number of tweets to get. Max 200.
 
*         userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineUserGet( bool trimUser, bool includeRetweets, unsigned int tweetCount,
 
                                std::string userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_USERTIMELINE_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    if( userInfo.empty() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
    }
 
 
    if( tweetCount )
 
    {
 
        if( tweetCount > twitCurlDefaults::MAX_TIMELINE_TWEET_COUNT )
 
        {
 
            tweetCount = twitCurlDefaults::MAX_TIMELINE_TWEET_COUNT;
 
        }
 
        std::stringstream tmpStrm;
 
        tmpStrm << twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_COUNT << tweetCount;
 
        buildUrl += tmpStrm.str();
 
        tmpStrm.str().clear();
 
    }
 
 
    if( includeRetweets )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_INCRETWEETS;
 
    }
 
 
    if( trimUser )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_TRIMUSER;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::userLookup
 
*
 
* @description: method to get a number of user's profiles
 
*
 
* @input: userInfo - vector of screen names or user ids
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::userLookup( std::vector<std::string> &userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 
 
    std::string userIds = "";
 
    std::string sep = "";
 
    for( unsigned int i = 0 ; i < std::min((size_t)100, userInfo.size()); i++, sep = "," )
 
    {
 
        userIds += sep + userInfo[i];
 
    }
 
 
    userIds = ( isUserId ? twitCurlDefaults::TWITCURL_USERID : twitCurlDefaults::TWITCURL_SCREENNAME ) +
 
              urlencode( userIds );
 
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] + 
 
                           twitterDefaults::TWITCURL_LOOKUPUSERS_URL + 
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform POST */
 
    return performPost( buildUrl, userIds);
 
}
 
 
/*++
 
* @method: twitCurl::userGet
 
*
 
* @description: method to get a user's profile
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::userGet( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Set URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWUSERS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::friendsGet
 
*
 
* @description: method to get a user's friends
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendsGet( std::string userInfo, bool isUserId )
 
{
 
    /* Set URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWFRIENDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::followersGet
 
*
 
* @description: method to get a user's followers
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::followersGet( std::string userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWFOLLOWERS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::directMessageGet
 
*
 
* @description: method to get direct messages
 
*
 
* @input: since id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_DIRECTMESSAGES_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::directMessageSend
 
*
 
* @description: method to send direct message to a user
 
*
 
* @input: userInfo - screen name or user id of a user to whom message needs to be sent,
 
*         dMsg - message
 
*         isUserId - true if userInfo contains target user's id
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageSend( std::string& userInfo, std::string& dMsg, bool isUserId )
 
{
 
    if( userInfo.empty() || dMsg.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare new direct message */
 
    std::string newDm = twitCurlDefaults::TWITCURL_TEXTSTRING + urlencode( dMsg );
 
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_DIRECTMESSAGENEW_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Perform POST */
 
    return performPost( buildUrl, newDm );
 
}
 
 
/*++
 
* @method: twitCurl::directMessageGetSent
 
*
 
* @description: method to get sent direct messages
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageGetSent()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_DIRECTMESSAGESSENT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::directMessageDestroyById
 
*
 
* @description: method to delete direct messages by its id
 
*
 
* @input: dMsgId - id of direct message in string format
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageDestroyById( std::string& dMsgId )
 
{
 
    if( dMsgId.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_DIRECTMESSAGEDESTROY_URL + dMsgId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::friendshipCreate
 
*
 
* @description: method to add a twitter user as friend (follow a user)
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipCreate( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSHIPSCREATE_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 
 
    /* Perform POST */
 
    return performPost( buildUrl, dummyData );
 
}
 
 
/*++
 
* @method: twitCurl::friendshipDestroy
 
*
 
* @description: method to delete a twitter user from friend list (unfollow a user)
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipDestroy( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSHIPSDESTROY_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::friendshipShow
 
*
 
* @description: method to show all friends
 
*
 
* @input: userInfo - user id or screen name of a user of whom friends need to be shown
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipShow( std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FRIENDSHIPSSHOW_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( userInfo.length() )
 
    {
 
        /* Append username to the URL */
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
        if( isUserId )
 
        {
 
            buildUrl += twitCurlDefaults::TWITCURL_TARGETUSERID;
 
        }
 
        else
 
        {
 
            buildUrl += twitCurlDefaults::TWITCURL_TARGETSCREENNAME;
 
        }
 
        buildUrl += userInfo;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::friendsIdsGet
 
*
 
* @description: method to show IDs of all friends of a twitter user
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendsIdsGet( std::string& nextCursor, std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSIDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    if( buildUrl.length() && nextCursor.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_NEXT_CURSOR +
 
                    nextCursor;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::followersIdsGet
 
*
 
* @description: method to show IDs of all followers of a twitter user
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::followersIdsGet( std::string& nextCursor, std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FOLLOWERSIDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 
 
    if( buildUrl.length() && nextCursor.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_NEXT_CURSOR +
 
                    nextCursor;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::accountRateLimitGet
 
*
 
* @description: method to get API rate limit of current user
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::accountRateLimitGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_ACCOUNTRATELIMIT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::accountVerifyCredGet
 
*
 
* @description: method to get information on user identified by given credentials
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::accountVerifyCredGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_ACCOUNTVERIFYCRED_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::favoriteGet
 
*
 
* @description: method to get favorite users' statuses
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FAVORITESGET_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::favoriteCreate
 
*
 
* @description: method to favorite a status message
 
*
 
* @input: statusId - id in string format of the status to be favorited
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteCreate( std::string& statusId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FAVORITECREATE_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 
 
    /* Perform POST */
 
    return performPost( buildUrl, dummyData );
 
}
 
 
/*++
 
* @method: twitCurl::favoriteDestroy
 
*
 
* @description: method to delete a favorited the status
 
*
 
* @input: statusId - id in string format of the favorite status to be deleted
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteDestroy( std::string& statusId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FAVORITEDESTROY_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::blockCreate
 
*
 
* @description: method to block a user
 
*
 
* @input: userInfo - user id or screen name who needs to be blocked
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockCreate( std::string& userInfo )
 
{
 
        /* Prepare URL */
 
        std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                               twitterDefaults::TWITCURL_BLOCKSCREATE_URL + userInfo +
 
                               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
        /* Send some dummy data in POST */
 
        std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                                urlencode( std::string( "dummy" ) );
 
 
        /* Perform POST */
 
        return performPost( buildUrl, dummyData );
 
}
 
 
/*++
 
* @method: twitCurl::blockDestroy
 
*
 
* @description: method to unblock a user
 
*
 
* @input: userInfo - user id or screen name who need to unblocked
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockDestroy( std::string& userInfo )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_BLOCKSDESTROY_URL + userInfo +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::blockListGet
 
*
 
* @description: method to get list of users blocked by authenticated user
 
*
 
* @input: includeEntities - indicates whether or not to include 'entities' node
 
*         skipStatus - indicates whether or not to include status for returned users
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockListGet( std::string& nextCursor, bool includeEntities, bool skipStatus )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl, urlParams;
 
 
    buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
               twitterDefaults::TWITCURL_BLOCKSLIST_URL +
 
               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( includeEntities )
 
    {
 
        urlParams += twitCurlDefaults::TWITCURL_INCLUDE_ENTITIES + std::string("true");
 
    }
 
    if( skipStatus )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_SKIP_STATUS + std::string("true");
 
    }
 
    if( nextCursor.length() )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_NEXT_CURSOR + nextCursor;
 
    }
 
    if( urlParams.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + urlParams;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::blockIdsGet
 
*
 
* @description: method to get list of IDs blocked by authenticated user
 
*
 
* @input: stringifyIds - indicates whether or not returned ids should
 
*                        be in string format
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockIdsGet( std::string& nextCursor, bool stringifyIds )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl, urlParams;
 
 
    buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
               twitterDefaults::TWITCURL_BLOCKSIDS_URL +
 
               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( stringifyIds )
 
    {
 
        urlParams += twitCurlDefaults::TWITCURL_STRINGIFY_IDS + std::string("true");
 
    }
 
    if( nextCursor.length() )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_NEXT_CURSOR + nextCursor;
 
    }
 
    if( urlParams.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + urlParams;
 
    }
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::savedSearchGet
 
*
 
* @description: gets authenticated user's saved search queries.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchGet( )
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_SAVEDSEARCHGET_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::savedSearchShow
 
*
 
* @description: method to retrieve the data for a saved search owned by the authenticating user
 
*               specified by the given id.
 
*
 
* @input: searchId - id in string format of the search to be displayed
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchShow( std::string& searchId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHSHOW_URL + searchId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 
 
/*++
 
* @method: twitCurl::savedSearchCreate
 
*
 
* @description: creates a saved search for the authenticated user
 
*
 
* @input: query - the query of the search the user would like to save
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchCreate( std::string& query )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHCREATE_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Send some dummy data in POST */
 
    std::string queryStr = twitCurlDefaults::TWITCURL_QUERYSTRING + urlencode( query );
 
 
    /* Perform POST */
 
    return performPost( buildUrl, queryStr );
 
}
 
 
 
/*++
 
* @method: twitCurl::savedSearchDestroy
 
*
 
* @description: method to destroy a saved search for the authenticated user. The search specified
 
*               by id must be owned by the authenticating user.
 
*
 
* @input: searchId - search id of item to be deleted
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchDestroy( std::string& searchId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHDESTROY_URL + searchId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 
 
 
/*++
 
* @method: twitCurl::trendsGet()
 
*
 
* @description: gets trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDS_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
 
/*++
 
* @method: twitCurl::trendsDailyGet()
 
*
 
* @description: gets daily trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsDailyGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSDAILY_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::trendsWeeklyGet()
 
*
 
* @description: gets weekly trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsWeeklyGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSWEEKLY_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::trendsCurrentGet()
 
*
 
* @description: gets current trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsCurrentGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSCURRENT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::trendsAvailableGet()
 
*
 
* @description: gets available trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsAvailableGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSAVAILABLE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 
 
/*++
 
* @method: twitCurl::getLastWebResponse
 
*
 
* @description: method to get http response for the most recent request sent.
 
*               twitcurl users need to call this method and parse the XML
 
*               data returned by twitter to see what has happened.
 
*
 
* @input: outWebResp - string in which twitter's response is supplied back to caller
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::getLastWebResponse( std::string& outWebResp )
 
{
 
    outWebResp = "";
 
    if( m_callbackData.length() )
 
    {
 
        outWebResp = m_callbackData;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::getLastCurlError
 
*
 
* @description: method to get cURL error response for most recent http request.
 
*               twitcurl users can call this method if any of the APIs return
 
*               false.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::getLastCurlError( std::string& outErrResp )
 
{
 
    m_errorBuffer[twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE-1] = twitCurlDefaults::TWITCURL_EOS;
 
    outErrResp.assign( m_errorBuffer );
 
}
 
 
/*++
 
* @method: twitCurl::curlCallback
 
*
 
* @description: static method to get http response back from cURL.
 
*               this is an internal method, users of twitcurl need not
 
*               use this.
 
*
 
* @input: as per cURL convention.
 
*
 
* @output: size of data stored in our buffer
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
int twitCurl::curlCallback( char* data, size_t size, size_t nmemb, twitCurl* pTwitCurlObj )
 
{
 
    if( pTwitCurlObj && data )
 
    {
 
        /* Save http response in twitcurl object's buffer */
 
        return pTwitCurlObj->saveLastWebResponse( data, ( size*nmemb ) );
 
    }
 
    return 0;
 
}
 
 
/*++
 
* @method: twitCurl::saveLastWebResponse
 
*
 
* @description: method to save http responses. this is an internal method
 
*               and twitcurl users need not use this.
 
*
 
* @input: data - character buffer from cURL,
 
*         size - size of character buffer
 
*
 
* @output: size of data stored in our buffer
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
int twitCurl::saveLastWebResponse(  char*& data, size_t size )
 
{
 
    if( data && size )
 
    {
 
        /* Append data in our internal buffer */
 
        m_callbackData.append( data, size );
 
        return (int)size;
 
    }
 
    return 0;
 
}
 
 
/*++
 
* @method: twitCurl::clearCurlCallbackBuffers
 
*
 
* @description: method to clear callback buffers used by cURL. this is an
 
*               internal method and twitcurl users need not use this.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::clearCurlCallbackBuffers()
 
{
 
    m_callbackData = "";
 
    memset( m_errorBuffer, 0, twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE );
 
}
 
 
/*++
 
* @method: twitCurl::prepareCurlProxy
 
*
 
* @description: method to set proxy details into cURL. this is an internal method.
 
*               twitcurl users should not use this method, instead use setProxyXxx
 
*               methods to set proxy server information.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlProxy()
 
{
 
    if( m_curlProxyParamsSet )
 
    {
 
        return;
 
    }
 
 
    /* Reset existing proxy details in cURL */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, NULL );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, NULL );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, (long)CURLAUTH_ANY );
 
 
    /* Set proxy details in cURL */
 
    std::string proxyIpPort("");
 
    if( getProxyServerIp().size() )
 
    {
 
        utilMakeCurlParams( proxyIpPort, getProxyServerIp(), getProxyServerPort() );
 
    }
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, proxyIpPort.c_str() );
 
 
    /* Prepare username and password for proxy server */
 
    if( m_proxyUserName.length() && m_proxyPassword.length() )
 
    {
 
        std::string proxyUserPass;
 
        utilMakeCurlParams( proxyUserPass, getProxyUserName(), getProxyPassword() );
 
        curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, proxyUserPass.c_str() );
 
    }
 
 
    /* Set the flag to true indicating that proxy info is set in cURL */
 
    m_curlProxyParamsSet = true;
 
}
 
 
/*++
 
* @method: twitCurl::prepareCurlCallback
 
*
 
* @description: method to set callback details into cURL. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlCallback()
 
{
 
    if( m_curlCallbackParamsSet )
 
    {
 
        return;
 
    }
 
 
    /* Set buffer to get error */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, m_errorBuffer );
 
 
    /* Set callback function to get response */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, curlCallback );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, this );
 
 
    /* Set the flag to true indicating that callback info is set in cURL */
 
    m_curlCallbackParamsSet = true;
 
}
 
 
/*++
 
* @method: twitCurl::prepareCurlUserPass
 
*
 
* @description: method to set twitter credentials into cURL. this is an internal method.
 
*               twitcurl users should not use this method, instead use setTwitterXxx
 
*               methods to set twitter username and password.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlUserPass()
 
{
 
    if( m_curlLoginParamsSet )
 
    {
 
        return;
 
    }
 
 
    /* Reset existing username and password stored in cURL */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, "" );
 
 
    if( getTwitterUsername().size() )
 
    {
 
        /* Prepare username:password */
 
        std::string userNamePassword;
 
        utilMakeCurlParams( userNamePassword, getTwitterUsername(), getTwitterPassword() );
 
 
        /* Set username and password */
 
        curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userNamePassword.c_str() );
 
    }
 
 
    /* Set the flag to true indicating that twitter credentials are set in cURL */
 
    m_curlLoginParamsSet = true;
 
}
 
 
/*++
 
* @method: twitCurl::prepareStandardParams
 
*
 
* @description: method to set standard params into cURL. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareStandardParams()
 
{
 
    /* Restore any custom request we may have */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, NULL );
 
 
    /* All supported encodings */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_ENCODING, "" );
 
 
    /* Clear callback and error buffers */
 
    clearCurlCallbackBuffers();
 
 
    /* Prepare proxy */
 
    prepareCurlProxy();
 
 
    /* Prepare cURL callback data and error buffer */
 
    prepareCurlCallback();
 
 
    /* Prepare username and password for twitter */
 
    prepareCurlUserPass();
 
}
 
 
/*++
 
* @method: twitCurl::performGet
 
*
 
* @description: method to send http GET request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: getUrl - url
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performGet( const std::string& getUrl )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    std::string dataStrDummy;
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpGet, getUrl, dataStrDummy, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, getUrl.c_str() );
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 
 
/*++
 
* @method: twitCurl::performGetInternal
 
*
 
* @description: method to send http GET request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: const std::string& getUrl, const std::string& oAuthHttpHeader
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performGetInternal( const std::string& getUrl,
 
                                   const std::string& oAuthHttpHeader )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    struct curl_slist* pOAuthHeaderList = NULL;
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, getUrl.c_str() );
 
 
    /* Set header */
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 
 
/*++
 
* @method: twitCurl::performDelete
 
*
 
* @description: method to send http DELETE request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: deleteUrl - url
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performDelete( const std::string& deleteUrl )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    std::string dataStrDummy;
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpDelete, deleteUrl, dataStrDummy, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE" );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, deleteUrl.c_str() );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStrDummy.c_str() );
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 
 
/*++
 
* @method: twitCurl::performPost
 
*
 
* @description: method to send http POST request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: postUrl - url,
 
*         dataStr - url encoded data to be posted
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*           data value in dataStr must already be url encoded.
 
*           ex: dataStr = "key=urlencode(value)"
 
*
 
*--*/
 
bool twitCurl::performPost( const std::string& postUrl, std::string dataStr )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpPost, postUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Set http request, url and data */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, postUrl.c_str() );
 
    if( dataStr.length() )
 
    {
 
        curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStr.c_str() );
 
    }
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 
 
/*++
 
* @method: utilMakeCurlParams
 
*
 
* @description: utility function to build parameter strings in the format
 
*               required by cURL ("param1:param2"). twitcurl users should
 
*               not use this function.
 
*
 
* @input: inParam1 - first parameter,
 
*         inParam2 - second parameter
 
*
 
* @output: outStr - built parameter
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void utilMakeCurlParams( std::string& outStr, std::string& inParam1, std::string& inParam2 )
 
{
 
    outStr = inParam1;
 
    outStr += twitCurlDefaults::TWITCURL_COLON + inParam2;
 
}
 
 
/*++
 
* @method: utilMakeUrlForUser
 
*
 
* @description: utility function to build url compatible to twitter. twitcurl
 
*               users should not use this function.
 
*
 
* @input: baseUrl - base twitter url,
 
*         userInfo - user name,
 
*         isUserId - indicates if userInfo contains a user id or scree name
 
*
 
* @output: outUrl - built url
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void utilMakeUrlForUser( std::string& outUrl, const std::string& baseUrl, std::string& userInfo, bool isUserId )
 
{
 
    /* Copy base URL */
 
    outUrl = baseUrl;
 
 
    if( userInfo.length() )
 
    {
 
        /* Append username to the URL */
 
        outUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
        if( isUserId )
 
        {
 
            outUrl += twitCurlDefaults::TWITCURL_USERID;
 
        }
 
        else
 
        {
 
            outUrl += twitCurlDefaults::TWITCURL_SCREENNAME;
 
        }
 
        outUrl += userInfo;
 
    }
 
}
 
 
/*++
 
* @method: twitCurl::getOAuth
 
*
 
* @description: method to get a reference to oAuth object.
 
*
 
* @input: none
 
*
 
* @output: reference to oAuth object
 
*
 
*--*/
 
oAuth& twitCurl::getOAuth()
 
{
 
    return m_oAuth;
 
}
 
 
/*++
 
* @method: twitCurl::oAuthRequestToken
 
*
 
* @description: method to get a request token key and secret. this token
 
*               will be used to get authorize user and get PIN from twitter
 
*
 
* @input: authorizeUrl is an output parameter. this method will set the url
 
*         in this string. user should visit this link and get PIN from that page.
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthRequestToken( std::string& authorizeUrl /* out */ )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    /* Get OAuth header for request token */
 
    std::string oAuthHeader;
 
    authorizeUrl = "";
 
    if( m_oAuth.getOAuthHeader( eOAuthHttpGet,
 
                                twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_REQUEST_TOKEN_URL,
 
                                std::string( "" ),
 
                                oAuthHeader ) )
 
    {
 
        if( performGetInternal( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_REQUEST_TOKEN_URL,
 
                                oAuthHeader ) )
 
        {
 
            /* Tell OAuth object to save access token and secret from web response */
 
            std::string twitterResp;
 
            getLastWebResponse( twitterResp );
 
            m_oAuth.extractOAuthTokenKeySecret( twitterResp );
 
 
            /* Get access token and secret from OAuth object */
 
            std::string oAuthTokenKey;
 
            m_oAuth.getOAuthTokenKey( oAuthTokenKey );
 
 
            /* Build authorize url so that user can visit in browser and get PIN */
 
            authorizeUrl.assign(twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_AUTHORIZE_URL );
 
            authorizeUrl.append( oAuthTokenKey.c_str() );
 
 
            return true;
 
        }
 
    }
 
    return false;
 
}
 
 
/*++
 
* @method: twitCurl::oAuthAccessToken
 
*
 
* @description: method to exchange request token with access token
 
*
 
* @input: none
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthAccessToken()
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
    /* Get OAuth header for access token */
 
    std::string oAuthHeader;
 
    if( m_oAuth.getOAuthHeader( eOAuthHttpGet,
 
                                twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_ACCESS_TOKEN_URL,
 
                                std::string( "" ),
 
                                oAuthHeader, true ) )
 
    {
 
        if( performGetInternal( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_ACCESS_TOKEN_URL,
 
                                oAuthHeader ) )
 
        {
 
            /* Tell OAuth object to save access token and secret from web response */
 
            std::string twitterResp;
 
            getLastWebResponse( twitterResp );
 
            m_oAuth.extractOAuthTokenKeySecret( twitterResp );
 
 
            return true;
 
        }
 
    }
 
    return false;
 
}
 
 
/*++
 
* ADDED BY ANTIROOT
 
*
 
* @method: twitCurl::oAuthHandlePIN
 
*
 
* @description: method to handle user's PIN code from the authentiation URLs
 
*
 
* @input: none
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthHandlePIN( const std::string& authorizeUrl /* in */ )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
 
    std::string dataStr;
 
    std::string oAuthHttpHeader;
 
    std::string authenticityTokenVal;
 
    std::string oauthTokenVal;
 
    std::string pinCodeVal;
 
    unsigned long httpStatusCode = 0;
 
    size_t nPosStart, nPosEnd;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpGet, authorizeUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, authorizeUrl.c_str() );
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_getinfo( m_curlHandle, CURLINFO_HTTP_CODE, &httpStatusCode );
 
            curl_slist_free_all( pOAuthHeaderList );
 
 
            // Now, let's find the authenticity token and oauth token
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            authenticityTokenVal = m_callbackData.substr( nPosStart, nPosEnd );
 
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_TOKEN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_TOKEN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            oauthTokenVal = m_callbackData.substr( nPosStart, nPosEnd );
 
        }
 
    }
 
    else if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
        return false;
 
    }
 
 
    // Second phase for the authorization
 
    pOAuthHeaderList = NULL;
 
    oAuthHttpHeader.clear();
 
 
    /* Prepare standard params */
 
    prepareStandardParams();
 
 
    /*
 
    Now, we need to make a data string for POST operation
 
    which includes oauth token, authenticity token, username, password.
 
    */
 
    dataStr = oAuthLibDefaults::OAUTHLIB_TOKEN_KEY + "=" + oauthTokenVal + "&" +                      \
 
              oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_KEY + "=" + authenticityTokenVal + "&" +  \
 
              oAuthLibDefaults::OAUTHLIB_SESSIONUSERNAME_KEY + "=" + getTwitterUsername() + "&" +     \
 
              oAuthLibDefaults::OAUTHLIB_SESSIONPASSWORD_KEY + "=" + getTwitterPassword();
 
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpPost, authorizeUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, authorizeUrl.c_str() );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStr.c_str() );
 
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_getinfo( m_curlHandle, CURLINFO_HTTP_CODE, &httpStatusCode );
 
            curl_slist_free_all( pOAuthHeaderList );
 
 
            // Now, let's find the PIN CODE
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_PIN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_PIN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_PIN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            pinCodeVal = m_callbackData.substr( nPosStart, nPosEnd );
 
            getOAuth().setOAuthPin( pinCodeVal );
 
            return true;
 
        }
 
    }
 
    else if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 
 
#define NOMINMAX
 
#include <algorithm>
 
#include <memory.h>
 
#include "twitcurlurls.h"
 
#include "twitcurl.h"
 
#include "urlencode.h"
 

	
 
/*++
 
* @method: twitCurl::twitCurl
 
*
 
* @description: constructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
twitCurl::twitCurl():
 
m_curlHandle( NULL ),
 
m_curlProxyParamsSet( false ),
 
m_curlLoginParamsSet( false ),
 
m_curlCallbackParamsSet( false ),
 
m_eApiFormatType( twitCurlTypes::eTwitCurlApiFormatJson ),
 
m_eProtocolType( twitCurlTypes::eTwitCurlProtocolHttps )
 
{
 
    /* Alloc memory for cURL error responses */
 
    m_errorBuffer = (char*)malloc( twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE );
 

	
 
    /* Clear callback buffers */
 
    clearCurlCallbackBuffers();
 

	
 
    /* Initialize cURL */
 
    m_curlHandle = curl_easy_init();
 
    if( NULL == m_curlHandle )
 
    {
 
        std::string dummyStr;
 
        getLastCurlError( dummyStr );
 
    }
 
    curl_easy_setopt(m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0);
 
}
 

	
 
/*++
 
* @method: twitCurl::~twitCurl
 
*
 
* @description: destructor
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
twitCurl::~twitCurl()
 
{
 
    /* Cleanup cURL */
 
    if( m_curlHandle )
 
    {
 
        curl_easy_cleanup( m_curlHandle );
 
        m_curlHandle = NULL;
 
    }
 
    if( m_errorBuffer )
 
    {
 
        free( m_errorBuffer );
 
        m_errorBuffer = NULL;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::clone
 
*
 
* @description: creates a clone of twitcurl object
 
*
 
* @input: none
 
*
 
* @output: cloned object
 
*
 
*--*/
 
twitCurl* twitCurl::clone()
 
{
 
    twitCurl *cloneObj = new twitCurl();
 

	
 
    /* cURL proxy data */
 
    cloneObj->setProxyServerIp(m_proxyServerIp);
 
    cloneObj->setProxyServerPort(m_proxyServerPort);
 
    cloneObj->setProxyUserName(m_proxyUserName);
 
    cloneObj->setProxyPassword(m_proxyPassword);
 

	
 
    /* Twitter data */
 
    cloneObj->setTwitterUsername(m_twitterUsername);
 
    cloneObj->setTwitterPassword(m_twitterPassword);
 

	
 
    /* OAuth data */
 
    cloneObj->m_oAuth = m_oAuth.clone();
 

	
 
    return cloneObj;
 
}
 

	
 
/*++
 
* @method: twitCurl::isCurlInit
 
*
 
* @description: method to check if cURL is initialized properly
 
*
 
* @input: none
 
*
 
* @output: true if cURL is intialized, otherwise false
 
*
 
*--*/
 
bool twitCurl::isCurlInit()
 
{
 
    return ( NULL != m_curlHandle ) ? true : false;
 
}
 

	
 
/*++
 
* @method: twitCurl::getTwitterUsername
 
*
 
* @description: method to get stored Twitter username
 
*
 
* @input: none
 
*
 
* @output: twitter username
 
*
 
*--*/
 
std::string& twitCurl::getTwitterUsername()
 
{
 
    return m_twitterUsername;
 
}
 

	
 
/*++
 
* @method: twitCurl::getTwitterPassword
 
*
 
* @description: method to get stored Twitter password
 
*
 
* @input: none
 
*
 
* @output: twitter password
 
*
 
*--*/
 
std::string& twitCurl::getTwitterPassword()
 
{
 
    return m_twitterPassword;
 
}
 

	
 
/*++
 
* @method: twitCurl::setTwitterUsername
 
*
 
* @description: method to set username
 
*
 
* @input: userName
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setTwitterUsername( std::string& userName )
 
{
 
    if( userName.length() )
 
    {
 
        m_twitterUsername = userName;
 
        m_curlLoginParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::setTwitterPassword
 
*
 
* @description: method to set password
 
*
 
* @input: passWord
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setTwitterPassword( std::string& passWord )
 
{
 
    if( passWord.length() )
 
    {
 
        m_twitterPassword = passWord;
 
        m_curlLoginParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::getProxyServerIp
 
*
 
* @description: method to get proxy server IP address
 
*
 
* @input: none
 
*
 
* @output: proxy server IP address
 
*
 
*--*/
 
std::string& twitCurl::getProxyServerIp()
 
{
 
    return m_proxyServerIp;
 
}
 

	
 
/*++
 
* @method: twitCurl::getProxyServerPort
 
*
 
* @description: method to get proxy server port
 
*
 
* @input: none
 
*
 
* @output: proxy server port
 
*
 
*--*/
 
std::string& twitCurl::getProxyServerPort()
 
{
 
    return m_proxyServerPort;
 
}
 

	
 
/*++
 
* @method: twitCurl::getProxyUserName
 
*
 
* @description: method to get proxy user name
 
*
 
* @input: none
 
*
 
* @output: proxy server user name
 
*
 
*--*/
 
std::string& twitCurl::getProxyUserName()
 
{
 
    return m_proxyUserName;
 
}
 

	
 
/*++
 
* @method: twitCurl::getProxyPassword
 
*
 
* @description: method to get proxy server password
 
*
 
* @input: none
 
*
 
* @output: proxy server password
 
*
 
*--*/
 
std::string& twitCurl::getProxyPassword()
 
{
 
    return m_proxyPassword;
 
}
 

	
 
/*++
 
* @method: twitCurl::setProxyServerIp
 
*
 
* @description: method to set proxy server IP address
 
*
 
* @input: proxyServerIp
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyServerIp( std::string& proxyServerIp )
 
{
 
    if( proxyServerIp.length() )
 
    {
 
        m_proxyServerIp = proxyServerIp;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::setProxyServerPort
 
*
 
* @description: method to set proxy server port
 
*
 
* @input: proxyServerPort
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyServerPort( std::string& proxyServerPort )
 
{
 
    if( proxyServerPort.length() )
 
    {
 
        m_proxyServerPort = proxyServerPort;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::setProxyUserName
 
*
 
* @description: method to set proxy server username
 
*
 
* @input: proxyUserName
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyUserName( std::string& proxyUserName )
 
{
 
    if( proxyUserName.length() )
 
    {
 
        m_proxyUserName = proxyUserName;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::setProxyPassword
 
*
 
* @description: method to set proxy server password
 
*
 
* @input: proxyPassword
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::setProxyPassword( std::string& proxyPassword )
 
{
 
    if( proxyPassword.length() )
 
    {
 
        m_proxyPassword = proxyPassword;
 
        /*
 
         * Reset the flag so that next cURL http request
 
         * would set proxy details again into cURL.
 
         */
 
        m_curlProxyParamsSet = false;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::search
 
*
 
* @description: method to return tweets that match a specified query.
 
*
 
* @input: searchQuery - search query in string format
 
*         resultCount - optional search result count
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
* @note: Only ATOM and JSON format supported.
 
*
 
*--*/
 
bool twitCurl::search( std::string& searchQuery, std::string resultCount )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SEARCH_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] +
 
                           twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SEARCHQUERYSTRING +
 
                           searchQuery;
 

	
 
    /* Add number of results count if provided */
 
    if( resultCount.size() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_COUNT + urlencode( resultCount );
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::statusUpdate
 
*
 
* @description: method to update new status message in twitter profile
 
*
 
* @input: newStatus - status message text
 
*         inReplyToStatusId - optional status id to we're replying to
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusUpdate( std::string& newStatus, std::string inReplyToStatusId )
 
{
 
    if( newStatus.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare new status message */
 
    std::string newStatusMsg = twitCurlDefaults::TWITCURL_STATUSSTRING + urlencode( newStatus );
 

	
 
    /* Append status id to which we're replying to */
 
    if( inReplyToStatusId.size() )
 
    {
 
        newStatusMsg += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                        twitCurlDefaults::TWITCURL_INREPLYTOSTATUSID +
 
                        urlencode( inReplyToStatusId );
 
    }
 

	
 
    /* Perform POST */
 
    return  performPost( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                         twitterDefaults::TWITCURL_STATUSUPDATE_URL +
 
                         twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                         newStatusMsg );
 
}
 

	
 
/*++
 
* @method: twitCurl::statusShowById
 
*
 
* @description: method to get a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusShowById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_STATUSSHOW_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::statusDestroyById
 
*
 
* @description: method to delete a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::statusDestroyById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_STATUDESTROY_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::retweetById
 
*
 
* @description: method to RETWEET a status message by its id
 
*
 
* @input: statusId - a number in std::string format
 
*
 
* @output: true if RETWEET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::retweetById( std::string& statusId )
 
{
 
    if( statusId.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_RETWEET_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 

	
 
    /* Perform Retweet */
 
    return performPost( buildUrl, dummyData );
 
}
 

	
 
/*++
 
* @method: twitCurl::timelineHomeGet
 
*
 
* @description: method to get home timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineHomeGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_HOME_TIMELINE_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::timelinePublicGet
 
*
 
* @description: method to get public timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelinePublicGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_PUBLIC_TIMELINE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::featuredUsersGet
 
*
 
* @description: method to get featured users
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::featuredUsersGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FEATURED_USERS_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::timelineFriendsGet
 
*
 
* @description: method to get friends timeline
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineFriendsGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FRIENDS_TIMELINE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::mentionsGet
 
*
 
* @description: method to get mentions
 
*
 
* @input: sinceId - String specifying since id parameter
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::mentionsGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_MENTIONS_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::timelineUserGet
 
*
 
* @description: method to get mentions
 
*
 
* @input: trimUser - Trim user name if true
 
*         tweetCount - Number of tweets to get. Max 200.
 
*         userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::timelineUserGet( bool trimUser, bool includeRetweets, unsigned int tweetCount,
 
                                std::string userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 

	
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_USERTIMELINE_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    if( userInfo.empty() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
    }
 

	
 
    if( tweetCount )
 
    {
 
        if( tweetCount > twitCurlDefaults::MAX_TIMELINE_TWEET_COUNT )
 
        {
 
            tweetCount = twitCurlDefaults::MAX_TIMELINE_TWEET_COUNT;
 
        }
 
        std::stringstream tmpStrm;
 
        tmpStrm << twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_COUNT << tweetCount;
 
        buildUrl += tmpStrm.str();
 
        tmpStrm.str().clear();
 
    }
 

	
 
    if( includeRetweets )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_INCRETWEETS;
 
    }
 

	
 
    if( trimUser )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP + twitCurlDefaults::TWITCURL_TRIMUSER;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::userLookup
 
*
 
* @description: method to get a number of user's profiles
 
*
 
* @input: userInfo - vector of screen names or user ids
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::userLookup( std::vector<std::string> &userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    std::string userIds = "";
 
    std::string sep = "";
 
    for( unsigned int i = 0 ; i < std::min((size_t)100, userInfo.size()); i++, sep = "," )
 
    {
 
        userIds += sep + userInfo[i];
 
    }
 

	
 
    userIds = ( isUserId ? twitCurlDefaults::TWITCURL_USERID : twitCurlDefaults::TWITCURL_SCREENNAME ) +
 
              urlencode( userIds );
 

	
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] + 
 
                           twitterDefaults::TWITCURL_LOOKUPUSERS_URL + 
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform POST */
 
    return performPost( buildUrl, userIds);
 
}
 

	
 
/*++
 
* @method: twitCurl::userGet
 
*
 
* @description: method to get a user's profile
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::userGet( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Set URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWUSERS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::friendsGet
 
*
 
* @description: method to get a user's friends
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendsGet( std::string userInfo, bool isUserId )
 
{
 
    /* Set URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWFRIENDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::followersGet
 
*
 
* @description: method to get a user's followers
 
*
 
* @input: userInfo - screen name or user id in string format,
 
*         isUserId - true if userInfo contains an id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::followersGet( std::string userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_SHOWFOLLOWERS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::directMessageGet
 
*
 
* @description: method to get direct messages
 
*
 
* @input: since id
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageGet( std::string sinceId )
 
{
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_DIRECTMESSAGES_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    if( sinceId.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + twitCurlDefaults::TWITCURL_SINCEID + sinceId;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::directMessageSend
 
*
 
* @description: method to send direct message to a user
 
*
 
* @input: userInfo - screen name or user id of a user to whom message needs to be sent,
 
*         dMsg - message
 
*         isUserId - true if userInfo contains target user's id
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageSend( std::string& userInfo, std::string& dMsg, bool isUserId )
 
{
 
    if( userInfo.empty() || dMsg.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare new direct message */
 
    std::string newDm = twitCurlDefaults::TWITCURL_TEXTSTRING + urlencode( dMsg );
 

	
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_DIRECTMESSAGENEW_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Perform POST */
 
    return performPost( buildUrl, newDm );
 
}
 

	
 
/*++
 
* @method: twitCurl::directMessageGetSent
 
*
 
* @description: method to get sent direct messages
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageGetSent()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_DIRECTMESSAGESSENT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::directMessageDestroyById
 
*
 
* @description: method to delete direct messages by its id
 
*
 
* @input: dMsgId - id of direct message in string format
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::directMessageDestroyById( std::string& dMsgId )
 
{
 
    if( dMsgId.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_DIRECTMESSAGEDESTROY_URL + dMsgId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::friendshipCreate
 
*
 
* @description: method to add a twitter user as friend (follow a user)
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipCreate( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSHIPSCREATE_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 

	
 
    /* Perform POST */
 
    return performPost( buildUrl, dummyData );
 
}
 

	
 
/*++
 
* @method: twitCurl::friendshipDestroy
 
*
 
* @description: method to delete a twitter user from friend list (unfollow a user)
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipDestroy( std::string& userInfo, bool isUserId )
 
{
 
    if( userInfo.empty() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSHIPSDESTROY_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::friendshipShow
 
*
 
* @description: method to show all friends
 
*
 
* @input: userInfo - user id or screen name of a user of whom friends need to be shown
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendshipShow( std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FRIENDSHIPSSHOW_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( userInfo.length() )
 
    {
 
        /* Append username to the URL */
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
        if( isUserId )
 
        {
 
            buildUrl += twitCurlDefaults::TWITCURL_TARGETUSERID;
 
        }
 
        else
 
        {
 
            buildUrl += twitCurlDefaults::TWITCURL_TARGETSCREENNAME;
 
        }
 
        buildUrl += userInfo;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::friendsIdsGet
 
*
 
* @description: method to show IDs of all friends of a twitter user
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::friendsIdsGet( std::string& nextCursor, std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FRIENDSIDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    if( buildUrl.length() && nextCursor.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_NEXT_CURSOR +
 
                    nextCursor;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::followersIdsGet
 
*
 
* @description: method to show IDs of all followers of a twitter user
 
*
 
* @input: userInfo - user id or screen name of a user
 
*         isUserId - true if userInfo contains a user id instead of screen name
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::followersIdsGet( std::string& nextCursor, std::string& userInfo, bool isUserId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl;
 
    utilMakeUrlForUser( buildUrl, twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                        twitterDefaults::TWITCURL_FOLLOWERSIDS_URL +
 
                        twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType],
 
                        userInfo, isUserId );
 

	
 
    if( buildUrl.length() && nextCursor.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_AMP +
 
                    twitCurlDefaults::TWITCURL_NEXT_CURSOR +
 
                    nextCursor;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::accountRateLimitGet
 
*
 
* @description: method to get API rate limit of current user
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::accountRateLimitGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_ACCOUNTRATELIMIT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::accountVerifyCredGet
 
*
 
* @description: method to get information on user identified by given credentials
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::accountVerifyCredGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_ACCOUNTVERIFYCRED_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::favoriteGet
 
*
 
* @description: method to get favorite users' statuses
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_FAVORITESGET_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::favoriteCreate
 
*
 
* @description: method to favorite a status message
 
*
 
* @input: statusId - id in string format of the status to be favorited
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteCreate( std::string& statusId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FAVORITECREATE_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Send some dummy data in POST */
 
    std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                            urlencode( std::string( "dummy" ) );
 

	
 
    /* Perform POST */
 
    return performPost( buildUrl, dummyData );
 
}
 

	
 
/*++
 
* @method: twitCurl::favoriteDestroy
 
*
 
* @description: method to delete a favorited the status
 
*
 
* @input: statusId - id in string format of the favorite status to be deleted
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::favoriteDestroy( std::string& statusId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_FAVORITEDESTROY_URL + statusId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::blockCreate
 
*
 
* @description: method to block a user
 
*
 
* @input: userInfo - user id or screen name who needs to be blocked
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockCreate( std::string& userInfo )
 
{
 
        /* Prepare URL */
 
        std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                               twitterDefaults::TWITCURL_BLOCKSCREATE_URL + userInfo +
 
                               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
        /* Send some dummy data in POST */
 
        std::string dummyData = twitCurlDefaults::TWITCURL_TEXTSTRING +
 
                                urlencode( std::string( "dummy" ) );
 

	
 
        /* Perform POST */
 
        return performPost( buildUrl, dummyData );
 
}
 

	
 
/*++
 
* @method: twitCurl::blockDestroy
 
*
 
* @description: method to unblock a user
 
*
 
* @input: userInfo - user id or screen name who need to unblocked
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockDestroy( std::string& userInfo )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_BLOCKSDESTROY_URL + userInfo +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::blockListGet
 
*
 
* @description: method to get list of users blocked by authenticated user
 
*
 
* @input: includeEntities - indicates whether or not to include 'entities' node
 
*         skipStatus - indicates whether or not to include status for returned users
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockListGet( std::string& nextCursor, bool includeEntities, bool skipStatus )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl, urlParams;
 

	
 
    buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
               twitterDefaults::TWITCURL_BLOCKSLIST_URL +
 
               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( includeEntities )
 
    {
 
        urlParams += twitCurlDefaults::TWITCURL_INCLUDE_ENTITIES + std::string("true");
 
    }
 
    if( skipStatus )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_SKIP_STATUS + std::string("true");
 
    }
 
    if( nextCursor.length() )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_NEXT_CURSOR + nextCursor;
 
    }
 
    if( urlParams.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + urlParams;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::blockIdsGet
 
*
 
* @description: method to get list of IDs blocked by authenticated user
 
*
 
* @input: stringifyIds - indicates whether or not returned ids should
 
*                        be in string format
 
*         nextCursor - next cursor string returned from a previous call
 
*                      to this API, otherwise an empty string
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::blockIdsGet( std::string& nextCursor, bool stringifyIds )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl, urlParams;
 

	
 
    buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
               twitterDefaults::TWITCURL_BLOCKSIDS_URL +
 
               twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 
    if( stringifyIds )
 
    {
 
        urlParams += twitCurlDefaults::TWITCURL_STRINGIFY_IDS + std::string("true");
 
    }
 
    if( nextCursor.length() )
 
    {
 
        if( urlParams.length() )
 
        {
 
            urlParams += twitCurlDefaults::TWITCURL_URL_SEP_AMP;
 
        }
 
        urlParams += twitCurlDefaults::TWITCURL_NEXT_CURSOR + nextCursor;
 
    }
 
    if( urlParams.length() )
 
    {
 
        buildUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES + urlParams;
 
    }
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::savedSearchGet
 
*
 
* @description: gets authenticated user's saved search queries.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchGet( )
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_SAVEDSEARCHGET_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::savedSearchShow
 
*
 
* @description: method to retrieve the data for a saved search owned by the authenticating user
 
*               specified by the given id.
 
*
 
* @input: searchId - id in string format of the search to be displayed
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchShow( std::string& searchId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHSHOW_URL + searchId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform GET */
 
    return performGet( buildUrl );
 
}
 

	
 
/*++
 
* @method: twitCurl::savedSearchCreate
 
*
 
* @description: creates a saved search for the authenticated user
 
*
 
* @input: query - the query of the search the user would like to save
 
*
 
* @output: true if POST is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchCreate( std::string& query )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHCREATE_URL +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Send some dummy data in POST */
 
    std::string queryStr = twitCurlDefaults::TWITCURL_QUERYSTRING + urlencode( query );
 

	
 
    /* Perform POST */
 
    return performPost( buildUrl, queryStr );
 
}
 

	
 

	
 
/*++
 
* @method: twitCurl::savedSearchDestroy
 
*
 
* @description: method to destroy a saved search for the authenticated user. The search specified
 
*               by id must be owned by the authenticating user.
 
*
 
* @input: searchId - search id of item to be deleted
 
*
 
* @output: true if DELETE is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::savedSearchDestroy( std::string& searchId )
 
{
 
    /* Prepare URL */
 
    std::string buildUrl = twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                           twitterDefaults::TWITCURL_SAVEDSEARCHDESTROY_URL + searchId +
 
                           twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType];
 

	
 
    /* Perform DELETE */
 
    return performDelete( buildUrl );
 
}
 

	
 

	
 
/*++
 
* @method: twitCurl::trendsGet()
 
*
 
* @description: gets trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDS_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 

	
 
/*++
 
* @method: twitCurl::trendsDailyGet()
 
*
 
* @description: gets daily trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsDailyGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSDAILY_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::trendsWeeklyGet()
 
*
 
* @description: gets weekly trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsWeeklyGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSWEEKLY_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::trendsCurrentGet()
 
*
 
* @description: gets current trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsCurrentGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSCURRENT_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::trendsAvailableGet()
 
*
 
* @description: gets available trends.
 
*
 
* @input: none
 
*
 
* @output: true if GET is success, otherwise false. This does not check http
 
*          response by twitter. Use getLastWebResponse() for that.
 
*
 
*--*/
 
bool twitCurl::trendsAvailableGet()
 
{
 
    /* Perform GET */
 
    return performGet( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                       twitterDefaults::TWITCURL_TRENDSAVAILABLE_URL +
 
                       twitCurlDefaults::TWITCURL_EXTENSIONFORMATS[m_eApiFormatType] );
 
}
 

	
 
/*++
 
* @method: twitCurl::getLastWebResponse
 
*
 
* @description: method to get http response for the most recent request sent.
 
*               twitcurl users need to call this method and parse the XML
 
*               data returned by twitter to see what has happened.
 
*
 
* @input: outWebResp - string in which twitter's response is supplied back to caller
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::getLastWebResponse( std::string& outWebResp )
 
{
 
    outWebResp = "";
 
    if( m_callbackData.length() )
 
    {
 
        outWebResp = m_callbackData;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::getLastCurlError
 
*
 
* @description: method to get cURL error response for most recent http request.
 
*               twitcurl users can call this method if any of the APIs return
 
*               false.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
*--*/
 
void twitCurl::getLastCurlError( std::string& outErrResp )
 
{
 
    m_errorBuffer[twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE-1] = twitCurlDefaults::TWITCURL_EOS;
 
    outErrResp.assign( m_errorBuffer );
 
}
 

	
 
/*++
 
* @method: twitCurl::curlCallback
 
*
 
* @description: static method to get http response back from cURL.
 
*               this is an internal method, users of twitcurl need not
 
*               use this.
 
*
 
* @input: as per cURL convention.
 
*
 
* @output: size of data stored in our buffer
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
int twitCurl::curlCallback( char* data, size_t size, size_t nmemb, twitCurl* pTwitCurlObj )
 
{
 
    if( pTwitCurlObj && data )
 
    {
 
        /* Save http response in twitcurl object's buffer */
 
        return pTwitCurlObj->saveLastWebResponse( data, ( size*nmemb ) );
 
    }
 
    return 0;
 
}
 

	
 
/*++
 
* @method: twitCurl::saveLastWebResponse
 
*
 
* @description: method to save http responses. this is an internal method
 
*               and twitcurl users need not use this.
 
*
 
* @input: data - character buffer from cURL,
 
*         size - size of character buffer
 
*
 
* @output: size of data stored in our buffer
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
int twitCurl::saveLastWebResponse(  char*& data, size_t size )
 
{
 
    if( data && size )
 
    {
 
        /* Append data in our internal buffer */
 
        m_callbackData.append( data, size );
 
        return (int)size;
 
    }
 
    return 0;
 
}
 

	
 
/*++
 
* @method: twitCurl::clearCurlCallbackBuffers
 
*
 
* @description: method to clear callback buffers used by cURL. this is an
 
*               internal method and twitcurl users need not use this.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::clearCurlCallbackBuffers()
 
{
 
    m_callbackData = "";
 
    memset( m_errorBuffer, 0, twitCurlDefaults::TWITCURL_DEFAULT_BUFFSIZE );
 
}
 

	
 
/*++
 
* @method: twitCurl::prepareCurlProxy
 
*
 
* @description: method to set proxy details into cURL. this is an internal method.
 
*               twitcurl users should not use this method, instead use setProxyXxx
 
*               methods to set proxy server information.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlProxy()
 
{
 
    if( m_curlProxyParamsSet )
 
    {
 
        return;
 
    }
 

	
 
    /* Reset existing proxy details in cURL */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, NULL );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, NULL );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXYAUTH, (long)CURLAUTH_ANY );
 

	
 
    /* Set proxy details in cURL */
 
    std::string proxyIpPort("");
 
    if( getProxyServerIp().size() )
 
    {
 
        utilMakeCurlParams( proxyIpPort, getProxyServerIp(), getProxyServerPort() );
 
    }
 
    curl_easy_setopt( m_curlHandle, CURLOPT_PROXY, proxyIpPort.c_str() );
 

	
 
    /* Prepare username and password for proxy server */
 
    if( m_proxyUserName.length() && m_proxyPassword.length() )
 
    {
 
        std::string proxyUserPass;
 
        utilMakeCurlParams( proxyUserPass, getProxyUserName(), getProxyPassword() );
 
        curl_easy_setopt( m_curlHandle, CURLOPT_PROXYUSERPWD, proxyUserPass.c_str() );
 
    }
 

	
 
    /* Set the flag to true indicating that proxy info is set in cURL */
 
    m_curlProxyParamsSet = true;
 
}
 

	
 
/*++
 
* @method: twitCurl::prepareCurlCallback
 
*
 
* @description: method to set callback details into cURL. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlCallback()
 
{
 
    if( m_curlCallbackParamsSet )
 
    {
 
        return;
 
    }
 

	
 
    /* Set buffer to get error */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_ERRORBUFFER, m_errorBuffer );
 

	
 
    /* Set callback function to get response */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_WRITEFUNCTION, curlCallback );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_WRITEDATA, this );
 

	
 
    /* Set the flag to true indicating that callback info is set in cURL */
 
    m_curlCallbackParamsSet = true;
 
}
 

	
 
/*++
 
* @method: twitCurl::prepareCurlUserPass
 
*
 
* @description: method to set twitter credentials into cURL. this is an internal method.
 
*               twitcurl users should not use this method, instead use setTwitterXxx
 
*               methods to set twitter username and password.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareCurlUserPass()
 
{
 
    if( m_curlLoginParamsSet )
 
    {
 
        return;
 
    }
 

	
 
    /* Reset existing username and password stored in cURL */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, "" );
 

	
 
    if( getTwitterUsername().size() )
 
    {
 
        /* Prepare username:password */
 
        std::string userNamePassword;
 
        utilMakeCurlParams( userNamePassword, getTwitterUsername(), getTwitterPassword() );
 

	
 
        /* Set username and password */
 
        curl_easy_setopt( m_curlHandle, CURLOPT_USERPWD, userNamePassword.c_str() );
 
    }
 

	
 
    /* Set the flag to true indicating that twitter credentials are set in cURL */
 
    m_curlLoginParamsSet = true;
 
}
 

	
 
/*++
 
* @method: twitCurl::prepareStandardParams
 
*
 
* @description: method to set standard params into cURL. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: none
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void twitCurl::prepareStandardParams()
 
{
 
    /* Restore any custom request we may have */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, NULL );
 

	
 
    /* All supported encodings */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_ENCODING, "" );
 

	
 
    /* Clear callback and error buffers */
 
    clearCurlCallbackBuffers();
 

	
 
    /* Prepare proxy */
 
    prepareCurlProxy();
 

	
 
    /* Prepare cURL callback data and error buffer */
 
    prepareCurlCallback();
 

	
 
    /* Prepare username and password for twitter */
 
    prepareCurlUserPass();
 
}
 

	
 
/*++
 
* @method: twitCurl::performGet
 
*
 
* @description: method to send http GET request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: getUrl - url
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performGet( const std::string& getUrl )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    std::string dataStrDummy;
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpGet, getUrl, dataStrDummy, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, getUrl.c_str() );
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* @method: twitCurl::performGetInternal
 
*
 
* @description: method to send http GET request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: const std::string& getUrl, const std::string& oAuthHttpHeader
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performGetInternal( const std::string& getUrl,
 
                                   const std::string& oAuthHttpHeader )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    struct curl_slist* pOAuthHeaderList = NULL;
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, getUrl.c_str() );
 

	
 
    /* Set header */
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* @method: twitCurl::performDelete
 
*
 
* @description: method to send http DELETE request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: deleteUrl - url
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
bool twitCurl::performDelete( const std::string& deleteUrl )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    std::string dataStrDummy;
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpDelete, deleteUrl, dataStrDummy, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_CUSTOMREQUEST, "DELETE" );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, deleteUrl.c_str() );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStrDummy.c_str() );
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* @method: twitCurl::performPost
 
*
 
* @description: method to send http POST request. this is an internal method.
 
*               twitcurl users should not use this method.
 
*
 
* @input: postUrl - url,
 
*         dataStr - url encoded data to be posted
 
*
 
* @output: none
 
*
 
* @remarks: internal method
 
*           data value in dataStr must already be url encoded.
 
*           ex: dataStr = "key=urlencode(value)"
 
*
 
*--*/
 
bool twitCurl::performPost( const std::string& postUrl, std::string dataStr )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    std::string oAuthHttpHeader;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpPost, postUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Set http request, url and data */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, postUrl.c_str() );
 
    if( dataStr.length() )
 
    {
 
        curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStr.c_str() );
 
    }
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_slist_free_all( pOAuthHeaderList );
 
        }
 
        return true;
 
    }
 
    if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* @method: utilMakeCurlParams
 
*
 
* @description: utility function to build parameter strings in the format
 
*               required by cURL ("param1:param2"). twitcurl users should
 
*               not use this function.
 
*
 
* @input: inParam1 - first parameter,
 
*         inParam2 - second parameter
 
*
 
* @output: outStr - built parameter
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void utilMakeCurlParams( std::string& outStr, std::string& inParam1, std::string& inParam2 )
 
{
 
    outStr = inParam1;
 
    outStr += twitCurlDefaults::TWITCURL_COLON + inParam2;
 
}
 

	
 
/*++
 
* @method: utilMakeUrlForUser
 
*
 
* @description: utility function to build url compatible to twitter. twitcurl
 
*               users should not use this function.
 
*
 
* @input: baseUrl - base twitter url,
 
*         userInfo - user name,
 
*         isUserId - indicates if userInfo contains a user id or scree name
 
*
 
* @output: outUrl - built url
 
*
 
* @remarks: internal method
 
*
 
*--*/
 
void utilMakeUrlForUser( std::string& outUrl, const std::string& baseUrl, std::string& userInfo, bool isUserId )
 
{
 
    /* Copy base URL */
 
    outUrl = baseUrl;
 

	
 
    if( userInfo.length() )
 
    {
 
        /* Append username to the URL */
 
        outUrl += twitCurlDefaults::TWITCURL_URL_SEP_QUES;
 
        if( isUserId )
 
        {
 
            outUrl += twitCurlDefaults::TWITCURL_USERID;
 
        }
 
        else
 
        {
 
            outUrl += twitCurlDefaults::TWITCURL_SCREENNAME;
 
        }
 
        outUrl += userInfo;
 
    }
 
}
 

	
 
/*++
 
* @method: twitCurl::getOAuth
 
*
 
* @description: method to get a reference to oAuth object.
 
*
 
* @input: none
 
*
 
* @output: reference to oAuth object
 
*
 
*--*/
 
oAuth& twitCurl::getOAuth()
 
{
 
    return m_oAuth;
 
}
 

	
 
/*++
 
* @method: twitCurl::oAuthRequestToken
 
*
 
* @description: method to get a request token key and secret. this token
 
*               will be used to get authorize user and get PIN from twitter
 
*
 
* @input: authorizeUrl is an output parameter. this method will set the url
 
*         in this string. user should visit this link and get PIN from that page.
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthRequestToken( std::string& authorizeUrl /* out */ )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    /* Get OAuth header for request token */
 
    std::string oAuthHeader;
 
    authorizeUrl = "";
 
    if( m_oAuth.getOAuthHeader( eOAuthHttpGet,
 
                                twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_REQUEST_TOKEN_URL,
 
                                std::string( "" ),
 
                                oAuthHeader ) )
 
    {
 
        if( performGetInternal( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_REQUEST_TOKEN_URL,
 
                                oAuthHeader ) )
 
        {
 
            /* Tell OAuth object to save access token and secret from web response */
 
            std::string twitterResp;
 
            getLastWebResponse( twitterResp );
 
            m_oAuth.extractOAuthTokenKeySecret( twitterResp );
 

	
 
            /* Get access token and secret from OAuth object */
 
            std::string oAuthTokenKey;
 
            m_oAuth.getOAuthTokenKey( oAuthTokenKey );
 

	
 
            /* Build authorize url so that user can visit in browser and get PIN */
 
            authorizeUrl.assign(twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_AUTHORIZE_URL );
 
            authorizeUrl.append( oAuthTokenKey.c_str() );
 

	
 
            return true;
 
        }
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* @method: twitCurl::oAuthAccessToken
 
*
 
* @description: method to exchange request token with access token
 
*
 
* @input: none
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthAccessToken()
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 
    /* Get OAuth header for access token */
 
    std::string oAuthHeader;
 
    if( m_oAuth.getOAuthHeader( eOAuthHttpGet,
 
                                twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_ACCESS_TOKEN_URL,
 
                                std::string( "" ),
 
                                oAuthHeader, true ) )
 
    {
 
        if( performGetInternal( twitCurlDefaults::TWITCURL_PROTOCOLS[m_eProtocolType] +
 
                                oAuthTwitterApiUrls::OAUTHLIB_TWITTER_ACCESS_TOKEN_URL,
 
                                oAuthHeader ) )
 
        {
 
            /* Tell OAuth object to save access token and secret from web response */
 
            std::string twitterResp;
 
            getLastWebResponse( twitterResp );
 
            m_oAuth.extractOAuthTokenKeySecret( twitterResp );
 

	
 
            return true;
 
        }
 
    }
 
    return false;
 
}
 

	
 
/*++
 
* ADDED BY ANTIROOT
 
*
 
* @method: twitCurl::oAuthHandlePIN
 
*
 
* @description: method to handle user's PIN code from the authentiation URLs
 
*
 
* @input: none
 
*
 
* @output: true if everything went sucessfully, otherwise false
 
*
 
*--*/
 
bool twitCurl::oAuthHandlePIN( const std::string& authorizeUrl /* in */ )
 
{
 
    /* Return if cURL is not initialized */
 
    if( !isCurlInit() )
 
    {
 
        return false;
 
    }
 

	
 
    std::string dataStr;
 
    std::string oAuthHttpHeader;
 
    std::string authenticityTokenVal;
 
    std::string oauthTokenVal;
 
    std::string pinCodeVal;
 
    unsigned long httpStatusCode = 0;
 
    size_t nPosStart, nPosEnd;
 
    struct curl_slist* pOAuthHeaderList = NULL;
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpGet, authorizeUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_HTTPGET, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, authorizeUrl.c_str() );
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_getinfo( m_curlHandle, CURLINFO_HTTP_CODE, &httpStatusCode );
 
            curl_slist_free_all( pOAuthHeaderList );
 

	
 
            // Now, let's find the authenticity token and oauth token
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            authenticityTokenVal = m_callbackData.substr( nPosStart, nPosEnd );
 

	
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_TOKEN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_TOKEN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            oauthTokenVal = m_callbackData.substr( nPosStart, nPosEnd );
 
        }
 
    }
 
    else if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
        return false;
 
    }
 

	
 
    // Second phase for the authorization
 
    pOAuthHeaderList = NULL;
 
    oAuthHttpHeader.clear();
 

	
 
    /* Prepare standard params */
 
    prepareStandardParams();
 

	
 
    /*
 
    Now, we need to make a data string for POST operation
 
    which includes oauth token, authenticity token, username, password.
 
    */
 
    dataStr = oAuthLibDefaults::OAUTHLIB_TOKEN_KEY + "=" + oauthTokenVal + "&" +                      \
 
              oAuthLibDefaults::OAUTHLIB_AUTHENTICITY_TOKEN_KEY + "=" + authenticityTokenVal + "&" +  \
 
              oAuthLibDefaults::OAUTHLIB_SESSIONUSERNAME_KEY + "=" + getTwitterUsername() + "&" +     \
 
              oAuthLibDefaults::OAUTHLIB_SESSIONPASSWORD_KEY + "=" + getTwitterPassword();
 

	
 
    /* Set OAuth header */
 
    m_oAuth.getOAuthHeader( eOAuthHttpPost, authorizeUrl, dataStr, oAuthHttpHeader );
 
    if( oAuthHttpHeader.length() )
 
    {
 
        pOAuthHeaderList = curl_slist_append( pOAuthHeaderList, oAuthHttpHeader.c_str() );
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_setopt( m_curlHandle, CURLOPT_HTTPHEADER, pOAuthHeaderList );
 
        }
 
    }
 

	
 
    /* Set http request and url */
 
    curl_easy_setopt( m_curlHandle, CURLOPT_POST, 1 );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_URL, authorizeUrl.c_str() );
 
    curl_easy_setopt( m_curlHandle, CURLOPT_COPYPOSTFIELDS, dataStr.c_str() );
 

	
 
    /* Send http request */
 
    if( CURLE_OK == curl_easy_perform( m_curlHandle ) )
 
    {
 
        if( pOAuthHeaderList )
 
        {
 
            curl_easy_getinfo( m_curlHandle, CURLINFO_HTTP_CODE, &httpStatusCode );
 
            curl_slist_free_all( pOAuthHeaderList );
 

	
 
            // Now, let's find the PIN CODE
 
            nPosStart = m_callbackData.find( oAuthLibDefaults::OAUTHLIB_PIN_TWITTER_RESP_KEY );
 
            if( std::string::npos == nPosStart )
 
            {
 
                return false;
 
            }
 
            nPosStart += oAuthLibDefaults::OAUTHLIB_PIN_TWITTER_RESP_KEY.length();
 
            nPosEnd = m_callbackData.substr( nPosStart ).find( oAuthLibDefaults::OAUTHLIB_PIN_END_TAG_TWITTER_RESP );
 
            if( std::string::npos == nPosEnd )
 
            {
 
                return false;
 
            }
 
            pinCodeVal = m_callbackData.substr( nPosStart, nPosEnd );
 
            getOAuth().setOAuthPin( pinCodeVal );
 
            return true;
 
        }
 
    }
 
    else if( pOAuthHeaderList )
 
    {
 
        curl_slist_free_all( pOAuthHeaderList );
 
    }
 
    return false;
 
}
 

	
backends/twitter/libtwitcurl/twitcurl.dsp
Show inline comments
 
# Microsoft Developer Studio Project File - Name="twitcurl" - Package Owner=<4>
 
# Microsoft Developer Studio Generated Build File, Format Version 6.00
 
# ** DO NOT EDIT **
 
 
# TARGTYPE "Win32 (x86) Static Library" 0x0104
 
 
CFG=twitcurl - Win32 Debug
 
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
 
!MESSAGE use the Export Makefile command and run
 
!MESSAGE 
 
!MESSAGE NMAKE /f "twitcurl.mak".
 
!MESSAGE 
 
!MESSAGE You can specify a configuration when running NMAKE
 
!MESSAGE by defining the macro CFG on the command line. For example:
 
!MESSAGE 
 
!MESSAGE NMAKE /f "twitcurl.mak" CFG="twitcurl - Win32 Debug"
 
!MESSAGE 
 
!MESSAGE Possible choices for configuration are:
 
!MESSAGE 
 
!MESSAGE "twitcurl - Win32 Release" (based on "Win32 (x86) Static Library")
 
!MESSAGE "twitcurl - Win32 Debug" (based on "Win32 (x86) Static Library")
 
!MESSAGE 
 
 
# Begin Project
 
# PROP AllowPerConfigDependencies 0
 
# PROP Scc_ProjName ""
 
# PROP Scc_LocalPath ""
 
CPP=cl.exe
 
RSC=rc.exe
 
 
!IF  "$(CFG)" == "twitcurl - Win32 Release"
 
 
# PROP BASE Use_MFC 0
 
# PROP BASE Use_Debug_Libraries 0
 
# PROP BASE Output_Dir "Release"
 
# PROP BASE Intermediate_Dir "Release"
 
# PROP BASE Target_Dir ""
 
# PROP Use_MFC 0
 
# PROP Use_Debug_Libraries 0
 
# PROP Output_Dir "Release"
 
# PROP Intermediate_Dir "Release"
 
# PROP Target_Dir ""
 
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
 
# ADD CPP /nologo /W3 /GX /O2 /I "./curl" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
 
# ADD BASE RSC /l 0x409 /d "NDEBUG"
 
# ADD RSC /l 0x409 /d "NDEBUG"
 
BSC32=bscmake.exe
 
# ADD BASE BSC32 /nologo
 
# ADD BSC32 /nologo
 
LIB32=link.exe -lib
 
# ADD BASE LIB32 /nologo
 
# ADD LIB32 /nologo
 
 
!ELSEIF  "$(CFG)" == "twitcurl - Win32 Debug"
 
 
# PROP BASE Use_MFC 0
 
# PROP BASE Use_Debug_Libraries 1
 
# PROP BASE Output_Dir "Debug"
 
# PROP BASE Intermediate_Dir "Debug"
 
# PROP BASE Target_Dir ""
 
# PROP Use_MFC 0
 
# PROP Use_Debug_Libraries 1
 
# PROP Output_Dir "Debug"
 
# PROP Intermediate_Dir "Debug"
 
# PROP Target_Dir ""
 
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
 
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "./curl" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
 
# ADD BASE RSC /l 0x409 /d "_DEBUG"
 
# ADD RSC /l 0x409 /d "_DEBUG"
 
BSC32=bscmake.exe
 
# ADD BASE BSC32 /nologo
 
# ADD BSC32 /nologo
 
LIB32=link.exe -lib
 
# ADD BASE LIB32 /nologo
 
# ADD LIB32 /nologo
 
 
!ENDIF 
 
 
# Begin Target
 
 
# Name "twitcurl - Win32 Release"
 
# Name "twitcurl - Win32 Debug"
 
# Begin Group "Source Files"
 
 
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 
# Begin Source File
 
 
SOURCE=.\base64.cpp
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\HMAC_SHA1.cpp
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\oauthlib.cpp
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\SHA1.cpp
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\twitcurl.cpp
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\urlencode.cpp
 
# End Source File
 
# End Group
 
# Begin Group "Header Files"
 
 
# PROP Default_Filter "h;hpp;hxx;hm;inl"
 
# Begin Source File
 
 
SOURCE=.\base64.h
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\HMAC_SHA1.h
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\oauthlib.h
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\SHA1.h
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\twitcurl.h
 
# End Source File
 
# Begin Source File
 
 
SOURCE=.\urlencode.h
 
# End Source File
 
# End Group
 
# End Target
 
# End Project
 
# Microsoft Developer Studio Project File - Name="twitcurl" - Package Owner=<4>
 
# Microsoft Developer Studio Generated Build File, Format Version 6.00
 
# ** DO NOT EDIT **
 

	
 
# TARGTYPE "Win32 (x86) Static Library" 0x0104
 

	
 
CFG=twitcurl - Win32 Debug
 
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
 
!MESSAGE use the Export Makefile command and run
 
!MESSAGE 
 
!MESSAGE NMAKE /f "twitcurl.mak".
 
!MESSAGE 
 
!MESSAGE You can specify a configuration when running NMAKE
 
!MESSAGE by defining the macro CFG on the command line. For example:
 
!MESSAGE 
 
!MESSAGE NMAKE /f "twitcurl.mak" CFG="twitcurl - Win32 Debug"
 
!MESSAGE 
 
!MESSAGE Possible choices for configuration are:
 
!MESSAGE 
 
!MESSAGE "twitcurl - Win32 Release" (based on "Win32 (x86) Static Library")
 
!MESSAGE "twitcurl - Win32 Debug" (based on "Win32 (x86) Static Library")
 
!MESSAGE 
 

	
 
# Begin Project
 
# PROP AllowPerConfigDependencies 0
 
# PROP Scc_ProjName ""
 
# PROP Scc_LocalPath ""
 
CPP=cl.exe
 
RSC=rc.exe
 

	
 
!IF  "$(CFG)" == "twitcurl - Win32 Release"
 

	
 
# PROP BASE Use_MFC 0
 
# PROP BASE Use_Debug_Libraries 0
 
# PROP BASE Output_Dir "Release"
 
# PROP BASE Intermediate_Dir "Release"
 
# PROP BASE Target_Dir ""
 
# PROP Use_MFC 0
 
# PROP Use_Debug_Libraries 0
 
# PROP Output_Dir "Release"
 
# PROP Intermediate_Dir "Release"
 
# PROP Target_Dir ""
 
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
 
# ADD CPP /nologo /W3 /GX /O2 /I "./curl" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
 
# ADD BASE RSC /l 0x409 /d "NDEBUG"
 
# ADD RSC /l 0x409 /d "NDEBUG"
 
BSC32=bscmake.exe
 
# ADD BASE BSC32 /nologo
 
# ADD BSC32 /nologo
 
LIB32=link.exe -lib
 
# ADD BASE LIB32 /nologo
 
# ADD LIB32 /nologo
 

	
 
!ELSEIF  "$(CFG)" == "twitcurl - Win32 Debug"
 

	
 
# PROP BASE Use_MFC 0
 
# PROP BASE Use_Debug_Libraries 1
 
# PROP BASE Output_Dir "Debug"
 
# PROP BASE Intermediate_Dir "Debug"
 
# PROP BASE Target_Dir ""
 
# PROP Use_MFC 0
 
# PROP Use_Debug_Libraries 1
 
# PROP Output_Dir "Debug"
 
# PROP Intermediate_Dir "Debug"
 
# PROP Target_Dir ""
 
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
 
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "./curl" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
 
# ADD BASE RSC /l 0x409 /d "_DEBUG"
 
# ADD RSC /l 0x409 /d "_DEBUG"
 
BSC32=bscmake.exe
 
# ADD BASE BSC32 /nologo
 
# ADD BSC32 /nologo
 
LIB32=link.exe -lib
 
# ADD BASE LIB32 /nologo
 
# ADD LIB32 /nologo
 

	
 
!ENDIF 
 

	
 
# Begin Target
 

	
 
# Name "twitcurl - Win32 Release"
 
# Name "twitcurl - Win32 Debug"
 
# Begin Group "Source Files"
 

	
 
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 
# Begin Source File
 

	
 
SOURCE=.\base64.cpp
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\HMAC_SHA1.cpp
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\oauthlib.cpp
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\SHA1.cpp
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\twitcurl.cpp
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\urlencode.cpp
 
# End Source File
 
# End Group
 
# Begin Group "Header Files"
 

	
 
# PROP Default_Filter "h;hpp;hxx;hm;inl"
 
# Begin Source File
 

	
 
SOURCE=.\base64.h
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\HMAC_SHA1.h
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\oauthlib.h
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\SHA1.h
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\twitcurl.h
 
# End Source File
 
# Begin Source File
 

	
 
SOURCE=.\urlencode.h
 
# End Source File
 
# End Group
 
# End Target
 
# End Project
backends/twitter/libtwitcurl/twitcurl.dsw
Show inline comments
 
Microsoft Developer Studio Workspace File, Format Version 6.00
 
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
 
 
###############################################################################
 
 
Project: "twitcurl"=.\twitcurl.dsp - Package Owner=<4>
 
 
Package=<5>
 
{{{
 
}}}
 
 
Package=<4>
 
{{{
 
}}}
 
 
###############################################################################
 
 
Global:
 
 
Package=<5>
 
{{{
 
}}}
 
 
Package=<3>
 
{{{
 
}}}
 
 
###############################################################################
 
 
Microsoft Developer Studio Workspace File, Format Version 6.00
 
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
 

	
 
###############################################################################
 

	
 
Project: "twitcurl"=.\twitcurl.dsp - Package Owner=<4>
 

	
 
Package=<5>
 
{{{
 
}}}
 

	
 
Package=<4>
 
{{{
 
}}}
 

	
 
###############################################################################
 

	
 
Global:
 

	
 
Package=<5>
 
{{{
 
}}}
 

	
 
Package=<3>
 
{{{
 
}}}
 

	
 
###############################################################################
 

	
backends/twitter/libtwitcurl/twitcurl.h
Show inline comments
 
#ifndef _TWITCURL_H_
 
#define _TWITCURL_H_
 
 
#include <string>
 
#include <sstream>
 
#include <cstring>
 
#include <vector>
 
#include "oauthlib.h"
 
#include "curl/curl.h"
 
 
/* Few common types used by twitCurl */
 
namespace twitCurlTypes
 
{
 
    typedef enum _eTwitCurlApiFormatType
 
    {
 
        eTwitCurlApiFormatJson = 0,
 
        eTwitCurlApiFormatXml,
 
        eTwitCurlApiFormatMax
 
    } eTwitCurlApiFormatType;
 
 
    typedef enum _eTwitCurlProtocolType
 
    {
 
        eTwitCurlProtocolHttps = 0,
 
        eTwitCurlProtocolHttp,
 
        eTwitCurlProtocolMax
 
    } eTwitCurlProtocolType;
 
};
 
 
/* twitCurl class */
 
class twitCurl
 
{
 
public:
 
    twitCurl();
 
    ~twitCurl();
 
 
    /* Twitter OAuth authorization methods */
 
    oAuth& getOAuth();
 
    bool oAuthRequestToken( std::string& authorizeUrl /* out */ );
 
    bool oAuthAccessToken();
 
    bool oAuthHandlePIN( const std::string& authorizeUrl /* in */ );
 
 
    /* Twitter login APIs, set once and forget */
 
    std::string& getTwitterUsername();
 
    std::string& getTwitterPassword();
 
    void setTwitterUsername( std::string& userName /* in */ );
 
    void setTwitterPassword( std::string& passWord /* in */ );
 
 
    /* Twitter search APIs */
 
    bool search( std::string& searchQuery /* in */, std::string resultCount = "" /* in */ );
 
 
    /* Twitter status APIs */
 
    bool statusUpdate( std::string& newStatus /* in */, std::string inReplyToStatusId = "" /* in */ );
 
    bool statusShowById( std::string& statusId /* in */ );
 
    bool statusDestroyById( std::string& statusId /* in */ );
 
    bool retweetById( std::string& statusId /* in */ );
 
 
    /* Twitter timeline APIs */
 
    bool timelineHomeGet( std::string sinceId = ""  /* in */ );
 
    bool timelinePublicGet();
 
    bool timelineFriendsGet();
 
    bool timelineUserGet( bool trimUser /* in */, bool includeRetweets /* in */,
 
                          unsigned int tweetCount /* in */,
 
                          std::string userInfo = "" /* in */,
 
                          bool isUserId = false /* in */ );
 
    bool featuredUsersGet();
 
    bool mentionsGet( std::string sinceId = "" /* in */ );
 
 
    /* Twitter user APIs */
 
    bool userLookup( std::vector<std::string> &userInfo /* in */,  bool isUserId = false /* in */ );
 
    bool userGet( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendsGet( std::string userInfo = "" /* in */, bool isUserId = false /* in */ );
 
    bool followersGet( std::string userInfo = "" /* in */, bool isUserId = false /* in */ );
 
 
    /* Twitter direct message APIs */
 
    bool directMessageGet( std::string sinceId = "" /* in */ );
 
    bool directMessageSend( std::string& userInfo /* in */, std::string& dMsg /* in */, bool isUserId = false /* in */ );
 
    bool directMessageGetSent();
 
    bool directMessageDestroyById( std::string& dMsgId /* in */ );
 
 
    /* Twitter friendships APIs */
 
    bool friendshipCreate( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendshipDestroy( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendshipShow( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
 
    /* Twitter social graphs APIs */
 
    bool friendsIdsGet( std::string& nextCursor /* in */,
 
                        std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool followersIdsGet( std::string& nextCursor /* in */,
 
                          std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
 
    /* Twitter account APIs */
 
    bool accountRateLimitGet();
 
    bool accountVerifyCredGet();
 
 
    /* Twitter favorites APIs */
 
    bool favoriteGet();
 
    bool favoriteCreate( std::string& statusId /* in */ );
 
    bool favoriteDestroy( std::string& statusId /* in */ );
 
 
    /* Twitter block APIs */
 
    bool blockCreate( std::string& userInfo /* in */ );
 
    bool blockDestroy( std::string& userInfo /* in */ );
 
    bool blockListGet( std::string& nextCursor /* in */,
 
                        bool includeEntities /* in */, bool skipStatus /* in */ );
 
    bool blockIdsGet( std::string& nextCursor /* in */, bool stringifyIds /* in */ );
 
 
    /* Twitter search APIs */
 
    bool savedSearchGet();
 
    bool savedSearchCreate( std::string& query /* in */ );
 
    bool savedSearchShow( std::string& searchId /* in */ );
 
    bool savedSearchDestroy( std::string& searchId /* in */ );
 
 
    /* Twitter trends APIs (JSON) */
 
    bool trendsGet();
 
    bool trendsDailyGet();
 
    bool trendsWeeklyGet();
 
    bool trendsCurrentGet();
 
    bool trendsAvailableGet();
 
 
    /* cURL APIs */
 
    bool isCurlInit();
 
    void getLastWebResponse( std::string& outWebResp /* out */ );
 
    void getLastCurlError( std::string& outErrResp /* out */);
 
 
    /* Internal cURL related methods */
 
    int saveLastWebResponse( char*& data, size_t size );
 
 
    /* cURL proxy APIs */
 
    std::string& getProxyServerIp();
 
    std::string& getProxyServerPort();
 
    std::string& getProxyUserName();
 
    std::string& getProxyPassword();
 
    void setProxyServerIp( std::string& proxyServerIp /* in */ );
 
    void setProxyServerPort( std::string& proxyServerPort /* in */ );
 
    void setProxyUserName( std::string& proxyUserName /* in */ );
 
    void setProxyPassword( std::string& proxyPassword /* in */ );
 
    
 
    /* Clones this object */
 
    twitCurl* clone();
 
 
private:
 
    /* cURL data */
 
    CURL* m_curlHandle;
 
    char* m_errorBuffer;
 
    std::string m_callbackData;
 
 
    /* cURL flags */
 
    bool m_curlProxyParamsSet;
 
    bool m_curlLoginParamsSet;
 
    bool m_curlCallbackParamsSet;
 
 
    /* cURL proxy data */
 
    std::string m_proxyServerIp;
 
    std::string m_proxyServerPort;
 
    std::string m_proxyUserName;
 
    std::string m_proxyPassword;
 
 
    /* Twitter data */
 
    std::string m_twitterUsername;
 
    std::string m_twitterPassword;
 
 
    /* Twitter API type */
 
    twitCurlTypes::eTwitCurlApiFormatType m_eApiFormatType;
 
    twitCurlTypes::eTwitCurlProtocolType m_eProtocolType;
 
 
    /* OAuth data */
 
    oAuth m_oAuth;
 
 
    /* Private methods */
 
    void clearCurlCallbackBuffers();
 
    void prepareCurlProxy();
 
    void prepareCurlCallback();
 
    void prepareCurlUserPass();
 
    void prepareStandardParams();
 
    bool performGet( const std::string& getUrl );
 
    bool performGetInternal( const std::string& getUrl,
 
                             const std::string& oAuthHttpHeader );
 
    bool performDelete( const std::string& deleteUrl );
 
    bool performPost( const std::string& postUrl, std::string dataStr = "" );
 
 
    /* Internal cURL related methods */
 
    static int curlCallback( char* data, size_t size, size_t nmemb, twitCurl* pTwitCurlObj );
 
};
 
 
 
/* Private functions */
 
void utilMakeCurlParams( std::string& outStr, std::string& inParam1, std::string& inParam2 );
 
void utilMakeUrlForUser( std::string& outUrl, const std::string& baseUrl, std::string& userInfo, bool isUserId );
 
 
#endif // _TWITCURL_H_
 
#ifndef _TWITCURL_H_
 
#define _TWITCURL_H_
 

	
 
#include <string>
 
#include <sstream>
 
#include <cstring>
 
#include <vector>
 
#include "oauthlib.h"
 
#include "curl/curl.h"
 

	
 
/* Few common types used by twitCurl */
 
namespace twitCurlTypes
 
{
 
    typedef enum _eTwitCurlApiFormatType
 
    {
 
        eTwitCurlApiFormatJson = 0,
 
        eTwitCurlApiFormatXml,
 
        eTwitCurlApiFormatMax
 
    } eTwitCurlApiFormatType;
 

	
 
    typedef enum _eTwitCurlProtocolType
 
    {
 
        eTwitCurlProtocolHttps = 0,
 
        eTwitCurlProtocolHttp,
 
        eTwitCurlProtocolMax
 
    } eTwitCurlProtocolType;
 
};
 

	
 
/* twitCurl class */
 
class twitCurl
 
{
 
public:
 
    twitCurl();
 
    ~twitCurl();
 

	
 
    /* Twitter OAuth authorization methods */
 
    oAuth& getOAuth();
 
    bool oAuthRequestToken( std::string& authorizeUrl /* out */ );
 
    bool oAuthAccessToken();
 
    bool oAuthHandlePIN( const std::string& authorizeUrl /* in */ );
 

	
 
    /* Twitter login APIs, set once and forget */
 
    std::string& getTwitterUsername();
 
    std::string& getTwitterPassword();
 
    void setTwitterUsername( std::string& userName /* in */ );
 
    void setTwitterPassword( std::string& passWord /* in */ );
 

	
 
    /* Twitter search APIs */
 
    bool search( std::string& searchQuery /* in */, std::string resultCount = "" /* in */ );
 

	
 
    /* Twitter status APIs */
 
    bool statusUpdate( std::string& newStatus /* in */, std::string inReplyToStatusId = "" /* in */ );
 
    bool statusShowById( std::string& statusId /* in */ );
 
    bool statusDestroyById( std::string& statusId /* in */ );
 
    bool retweetById( std::string& statusId /* in */ );
 

	
 
    /* Twitter timeline APIs */
 
    bool timelineHomeGet( std::string sinceId = ""  /* in */ );
 
    bool timelinePublicGet();
 
    bool timelineFriendsGet();
 
    bool timelineUserGet( bool trimUser /* in */, bool includeRetweets /* in */,
 
                          unsigned int tweetCount /* in */,
 
                          std::string userInfo = "" /* in */,
 
                          bool isUserId = false /* in */ );
 
    bool featuredUsersGet();
 
    bool mentionsGet( std::string sinceId = "" /* in */ );
 

	
 
    /* Twitter user APIs */
 
    bool userLookup( std::vector<std::string> &userInfo /* in */,  bool isUserId = false /* in */ );
 
    bool userGet( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendsGet( std::string userInfo = "" /* in */, bool isUserId = false /* in */ );
 
    bool followersGet( std::string userInfo = "" /* in */, bool isUserId = false /* in */ );
 

	
 
    /* Twitter direct message APIs */
 
    bool directMessageGet( std::string sinceId = "" /* in */ );
 
    bool directMessageSend( std::string& userInfo /* in */, std::string& dMsg /* in */, bool isUserId = false /* in */ );
 
    bool directMessageGetSent();
 
    bool directMessageDestroyById( std::string& dMsgId /* in */ );
 

	
 
    /* Twitter friendships APIs */
 
    bool friendshipCreate( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendshipDestroy( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool friendshipShow( std::string& userInfo /* in */, bool isUserId = false /* in */ );
 

	
 
    /* Twitter social graphs APIs */
 
    bool friendsIdsGet( std::string& nextCursor /* in */,
 
                        std::string& userInfo /* in */, bool isUserId = false /* in */ );
 
    bool followersIdsGet( std::string& nextCursor /* in */,
 
                          std::string& userInfo /* in */, bool isUserId = false /* in */ );
 

	
 
    /* Twitter account APIs */
 
    bool accountRateLimitGet();
 
    bool accountVerifyCredGet();
 

	
 
    /* Twitter favorites APIs */
 
    bool favoriteGet();
 
    bool favoriteCreate( std::string& statusId /* in */ );
 
    bool favoriteDestroy( std::string& statusId /* in */ );
 

	
 
    /* Twitter block APIs */
 
    bool blockCreate( std::string& userInfo /* in */ );
 
    bool blockDestroy( std::string& userInfo /* in */ );
 
    bool blockListGet( std::string& nextCursor /* in */,
 
                        bool includeEntities /* in */, bool skipStatus /* in */ );
 
    bool blockIdsGet( std::string& nextCursor /* in */, bool stringifyIds /* in */ );
 

	
 
    /* Twitter search APIs */
 
    bool savedSearchGet();
 
    bool savedSearchCreate( std::string& query /* in */ );
 
    bool savedSearchShow( std::string& searchId /* in */ );
 
    bool savedSearchDestroy( std::string& searchId /* in */ );
 

	
 
    /* Twitter trends APIs (JSON) */
 
    bool trendsGet();
 
    bool trendsDailyGet();
 
    bool trendsWeeklyGet();
 
    bool trendsCurrentGet();
 
    bool trendsAvailableGet();
 

	
 
    /* cURL APIs */
 
    bool isCurlInit();
 
    void getLastWebResponse( std::string& outWebResp /* out */ );
 
    void getLastCurlError( std::string& outErrResp /* out */);
 

	
 
    /* Internal cURL related methods */
 
    int saveLastWebResponse( char*& data, size_t size );
 

	
 
    /* cURL proxy APIs */
 
    std::string& getProxyServerIp();
 
    std::string& getProxyServerPort();
 
    std::string& getProxyUserName();
 
    std::string& getProxyPassword();
 
    void setProxyServerIp( std::string& proxyServerIp /* in */ );
 
    void setProxyServerPort( std::string& proxyServerPort /* in */ );
 
    void setProxyUserName( std::string& proxyUserName /* in */ );
 
    void setProxyPassword( std::string& proxyPassword /* in */ );
 
    
 
    /* Clones this object */
 
    twitCurl* clone();
 

	
 
private:
 
    /* cURL data */
 
    CURL* m_curlHandle;
 
    char* m_errorBuffer;
 
    std::string m_callbackData;
 

	
 
    /* cURL flags */
 
    bool m_curlProxyParamsSet;
 
    bool m_curlLoginParamsSet;
 
    bool m_curlCallbackParamsSet;
 

	
 
    /* cURL proxy data */
 
    std::string m_proxyServerIp;
 
    std::string m_proxyServerPort;
 
    std::string m_proxyUserName;
 
    std::string m_proxyPassword;
 

	
 
    /* Twitter data */
 
    std::string m_twitterUsername;
 
    std::string m_twitterPassword;
 

	
 
    /* Twitter API type */
 
    twitCurlTypes::eTwitCurlApiFormatType m_eApiFormatType;
 
    twitCurlTypes::eTwitCurlProtocolType m_eProtocolType;
 

	
 
    /* OAuth data */
 
    oAuth m_oAuth;
 

	
 
    /* Private methods */
 
    void clearCurlCallbackBuffers();
 
    void prepareCurlProxy();
 
    void prepareCurlCallback();
 
    void prepareCurlUserPass();
 
    void prepareStandardParams();
 
    bool performGet( const std::string& getUrl );
 
    bool performGetInternal( const std::string& getUrl,
 
                             const std::string& oAuthHttpHeader );
 
    bool performDelete( const std::string& deleteUrl );
 
    bool performPost( const std::string& postUrl, std::string dataStr = "" );
 

	
 
    /* Internal cURL related methods */
 
    static int curlCallback( char* data, size_t size, size_t nmemb, twitCurl* pTwitCurlObj );
 
};
 

	
 

	
 
/* Private functions */
 
void utilMakeCurlParams( std::string& outStr, std::string& inParam1, std::string& inParam2 );
 
void utilMakeUrlForUser( std::string& outUrl, const std::string& baseUrl, std::string& userInfo, bool isUserId );
 

	
 
#endif // _TWITCURL_H_
backends/twitter/libtwitcurl/twitcurl.plg
Show inline comments
 
<html>
 
<body>
 
<pre>
 
<h1>Build Log</h1>
 
<h3>
 
--------------------Configuration: twitcurl - Win32 Release--------------------
 
</h3>
 
<h3>Command Lines</h3>
 
Creating temporary file "C:\DOCUME~1\Mahesh\LOCALS~1\Temp\RSP239.tmp" with contents
 
[
 
/nologo /ML /W3 /GX /O2 /I "./curl" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"Release/twitcurl.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c 
 
"C:\Projects\twitcurl\base64.cpp"
 
"C:\Projects\twitcurl\HMAC_SHA1.cpp"
 
"C:\Projects\twitcurl\oauthlib.cpp"
 
"C:\Projects\twitcurl\SHA1.cpp"
 
"C:\Projects\twitcurl\twitcurl.cpp"
 
"C:\Projects\twitcurl\urlencode.cpp"
 
]
 
Creating command line "cl.exe @C:\DOCUME~1\Mahesh\LOCALS~1\Temp\RSP239.tmp" 
 
Creating command line "link.exe -lib /nologo /out:"Release\twitcurl.lib"  .\Release\base64.obj .\Release\HMAC_SHA1.obj .\Release\oauthlib.obj .\Release\SHA1.obj .\Release\twitcurl.obj .\Release\urlencode.obj "
 
<h3>Output Window</h3>
 
Compiling...
 
base64.cpp
 
HMAC_SHA1.cpp
 
oauthlib.cpp
 
SHA1.cpp
 
twitcurl.cpp
 
urlencode.cpp
 
Creating library...
 
 
 
 
<h3>Results</h3>
 
twitcurl.lib - 0 error(s), 0 warning(s)
 
</pre>
 
</body>
 
</html>
 
<html>
 
<body>
 
<pre>
 
<h1>Build Log</h1>
 
<h3>
 
--------------------Configuration: twitcurl - Win32 Release--------------------
 
</h3>
 
<h3>Command Lines</h3>
 
Creating temporary file "C:\DOCUME~1\Mahesh\LOCALS~1\Temp\RSP239.tmp" with contents
 
[
 
/nologo /ML /W3 /GX /O2 /I "./curl" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fp"Release/twitcurl.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c 
 
"C:\Projects\twitcurl\base64.cpp"
 
"C:\Projects\twitcurl\HMAC_SHA1.cpp"
 
"C:\Projects\twitcurl\oauthlib.cpp"
 
"C:\Projects\twitcurl\SHA1.cpp"
 
"C:\Projects\twitcurl\twitcurl.cpp"
 
"C:\Projects\twitcurl\urlencode.cpp"
 
]
 
Creating command line "cl.exe @C:\DOCUME~1\Mahesh\LOCALS~1\Temp\RSP239.tmp" 
 
Creating command line "link.exe -lib /nologo /out:"Release\twitcurl.lib"  .\Release\base64.obj .\Release\HMAC_SHA1.obj .\Release\oauthlib.obj .\Release\SHA1.obj .\Release\twitcurl.obj .\Release\urlencode.obj "
 
<h3>Output Window</h3>
 
Compiling...
 
base64.cpp
 
HMAC_SHA1.cpp
 
oauthlib.cpp
 
SHA1.cpp
 
twitcurl.cpp
 
urlencode.cpp
 
Creating library...
 

	
 

	
 

	
 
<h3>Results</h3>
 
twitcurl.lib - 0 error(s), 0 warning(s)
 
</pre>
 
</body>
 
</html>
backends/twitter/libtwitcurl/twitcurl.sln
Show inline comments
 

 
Microsoft Visual Studio Solution File, Format Version 10.00
 
# Visual C++ Express 2008
 
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "twitcurl", "twitcurl.vcproj", "{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}"
 
EndProject
 
Global
 
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 
		Debug|Win32 = Debug|Win32
 
		Release|Win32 = Release|Win32
 
	EndGlobalSection
 
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Debug|Win32.ActiveCfg = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Debug|Win32.Build.0 = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Release|Win32.ActiveCfg = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Release|Win32.Build.0 = Release|Win32
 
	EndGlobalSection
 
	GlobalSection(SolutionProperties) = preSolution
 
		HideSolutionNode = FALSE
 
	EndGlobalSection
 
EndGlobal
 

	
 
Microsoft Visual Studio Solution File, Format Version 10.00
 
# Visual C++ Express 2008
 
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "twitcurl", "twitcurl.vcproj", "{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}"
 
EndProject
 
Global
 
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 
		Debug|Win32 = Debug|Win32
 
		Release|Win32 = Release|Win32
 
	EndGlobalSection
 
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Debug|Win32.ActiveCfg = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Debug|Win32.Build.0 = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Release|Win32.ActiveCfg = Release|Win32
 
		{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}.Release|Win32.Build.0 = Release|Win32
 
	EndGlobalSection
 
	GlobalSection(SolutionProperties) = preSolution
 
		HideSolutionNode = FALSE
 
	EndGlobalSection
 
EndGlobal
backends/twitter/libtwitcurl/twitcurl.vcproj
Show inline comments
 
<?xml version="1.0" encoding="Windows-1252"?>
 
<VisualStudioProject
 
	ProjectType="Visual C++"
 
	Version="9.00"
 
	Name="twitcurl"
 
	ProjectGUID="{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}"
 
	TargetFrameworkVersion="0"
 
	>
 
	<Platforms>
 
		<Platform
 
			Name="Win32"
 
		/>
 
	</Platforms>
 
	<ToolFiles>
 
	</ToolFiles>
 
	<Configurations>
 
		<Configuration
 
			Name="Debug|Win32"
 
			OutputDirectory=".\Debug"
 
			IntermediateDirectory=".\Debug"
 
			ConfigurationType="4"
 
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
 
			UseOfMFC="0"
 
			ATLMinimizesCRunTimeLibraryUsage="false"
 
			CharacterSet="2"
 
			>
 
			<Tool
 
				Name="VCPreBuildEventTool"
 
			/>
 
			<Tool
 
				Name="VCCustomBuildTool"
 
			/>
 
			<Tool
 
				Name="VCXMLDataGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCWebServiceProxyGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCMIDLTool"
 
			/>
 
			<Tool
 
				Name="VCCLCompilerTool"
 
				Optimization="0"
 
				AdditionalIncludeDirectories="./curl"
 
				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
 
				MinimalRebuild="true"
 
				BasicRuntimeChecks="3"
 
				RuntimeLibrary="1"
 
				PrecompiledHeaderFile=".\Debug/twitcurl.pch"
 
				AssemblerListingLocation=".\Debug/"
 
				ObjectFile=".\Debug/"
 
				ProgramDataBaseFileName=".\Debug/"
 
				WarningLevel="3"
 
				SuppressStartupBanner="true"
 
				DebugInformationFormat="4"
 
			/>
 
			<Tool
 
				Name="VCManagedResourceCompilerTool"
 
			/>
 
			<Tool
 
				Name="VCResourceCompilerTool"
 
				PreprocessorDefinitions="_DEBUG"
 
				Culture="1033"
 
			/>
 
			<Tool
 
				Name="VCPreLinkEventTool"
 
			/>
 
			<Tool
 
				Name="VCLibrarianTool"
 
				OutputFile=".\Debug\twitcurl.lib"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCALinkTool"
 
			/>
 
			<Tool
 
				Name="VCXDCMakeTool"
 
			/>
 
			<Tool
 
				Name="VCBscMakeTool"
 
				SuppressStartupBanner="true"
 
				OutputFile=".\Debug/twitcurl.bsc"
 
			/>
 
			<Tool
 
				Name="VCFxCopTool"
 
			/>
 
			<Tool
 
				Name="VCPostBuildEventTool"
 
			/>
 
		</Configuration>
 
		<Configuration
 
			Name="Release|Win32"
 
			OutputDirectory=".\Release"
 
			IntermediateDirectory=".\Release"
 
			ConfigurationType="4"
 
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
 
			UseOfMFC="0"
 
			ATLMinimizesCRunTimeLibraryUsage="false"
 
			CharacterSet="2"
 
			>
 
			<Tool
 
				Name="VCPreBuildEventTool"
 
			/>
 
			<Tool
 
				Name="VCCustomBuildTool"
 
			/>
 
			<Tool
 
				Name="VCXMLDataGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCWebServiceProxyGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCMIDLTool"
 
			/>
 
			<Tool
 
				Name="VCCLCompilerTool"
 
				Optimization="2"
 
				InlineFunctionExpansion="1"
 
				AdditionalIncludeDirectories="./include"
 
				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;CURL_STATICLIB"
 
				StringPooling="true"
 
				RuntimeLibrary="2"
 
				EnableFunctionLevelLinking="true"
 
				PrecompiledHeaderFile=".\Release/twitcurl.pch"
 
				AssemblerListingLocation=".\Release/"
 
				ObjectFile=".\Release/"
 
				ProgramDataBaseFileName=".\Release/"
 
				WarningLevel="3"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCManagedResourceCompilerTool"
 
			/>
 
			<Tool
 
				Name="VCResourceCompilerTool"
 
				PreprocessorDefinitions="NDEBUG"
 
				Culture="1033"
 
			/>
 
			<Tool
 
				Name="VCPreLinkEventTool"
 
			/>
 
			<Tool
 
				Name="VCLibrarianTool"
 
				LinkLibraryDependencies="true"
 
				AdditionalDependencies="libcurl.lib"
 
				OutputFile=".\Release\twitcurl.lib"
 
				AdditionalLibraryDirectories="./lib"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCALinkTool"
 
			/>
 
			<Tool
 
				Name="VCXDCMakeTool"
 
			/>
 
			<Tool
 
				Name="VCBscMakeTool"
 
				SuppressStartupBanner="true"
 
				OutputFile=".\Release/twitcurl.bsc"
 
			/>
 
			<Tool
 
				Name="VCFxCopTool"
 
			/>
 
			<Tool
 
				Name="VCPostBuildEventTool"
 
			/>
 
		</Configuration>
 
	</Configurations>
 
	<References>
 
	</References>
 
	<Files>
 
		<Filter
 
			Name="Source Files"
 
			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 
			>
 
			<File
 
				RelativePath="base64.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="HMAC_SHA1.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="oauthlib.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="SHA1.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="twitcurl.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="urlencode.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
		</Filter>
 
		<Filter
 
			Name="Header Files"
 
			Filter="h;hpp;hxx;hm;inl"
 
			>
 
			<File
 
				RelativePath="base64.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="HMAC_SHA1.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="oauthlib.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="SHA1.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="twitcurl.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\twitcurlurls.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="urlencode.h"
 
				>
 
			</File>
 
		</Filter>
 
	</Files>
 
	<Globals>
 
	</Globals>
 
</VisualStudioProject>
 
<?xml version="1.0" encoding="Windows-1252"?>
 
<VisualStudioProject
 
	ProjectType="Visual C++"
 
	Version="9.00"
 
	Name="twitcurl"
 
	ProjectGUID="{00175D8C-EA44-48AE-AC59-B3B7BE04E65C}"
 
	TargetFrameworkVersion="0"
 
	>
 
	<Platforms>
 
		<Platform
 
			Name="Win32"
 
		/>
 
	</Platforms>
 
	<ToolFiles>
 
	</ToolFiles>
 
	<Configurations>
 
		<Configuration
 
			Name="Debug|Win32"
 
			OutputDirectory=".\Debug"
 
			IntermediateDirectory=".\Debug"
 
			ConfigurationType="4"
 
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
 
			UseOfMFC="0"
 
			ATLMinimizesCRunTimeLibraryUsage="false"
 
			CharacterSet="2"
 
			>
 
			<Tool
 
				Name="VCPreBuildEventTool"
 
			/>
 
			<Tool
 
				Name="VCCustomBuildTool"
 
			/>
 
			<Tool
 
				Name="VCXMLDataGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCWebServiceProxyGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCMIDLTool"
 
			/>
 
			<Tool
 
				Name="VCCLCompilerTool"
 
				Optimization="0"
 
				AdditionalIncludeDirectories="./curl"
 
				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
 
				MinimalRebuild="true"
 
				BasicRuntimeChecks="3"
 
				RuntimeLibrary="1"
 
				PrecompiledHeaderFile=".\Debug/twitcurl.pch"
 
				AssemblerListingLocation=".\Debug/"
 
				ObjectFile=".\Debug/"
 
				ProgramDataBaseFileName=".\Debug/"
 
				WarningLevel="3"
 
				SuppressStartupBanner="true"
 
				DebugInformationFormat="4"
 
			/>
 
			<Tool
 
				Name="VCManagedResourceCompilerTool"
 
			/>
 
			<Tool
 
				Name="VCResourceCompilerTool"
 
				PreprocessorDefinitions="_DEBUG"
 
				Culture="1033"
 
			/>
 
			<Tool
 
				Name="VCPreLinkEventTool"
 
			/>
 
			<Tool
 
				Name="VCLibrarianTool"
 
				OutputFile=".\Debug\twitcurl.lib"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCALinkTool"
 
			/>
 
			<Tool
 
				Name="VCXDCMakeTool"
 
			/>
 
			<Tool
 
				Name="VCBscMakeTool"
 
				SuppressStartupBanner="true"
 
				OutputFile=".\Debug/twitcurl.bsc"
 
			/>
 
			<Tool
 
				Name="VCFxCopTool"
 
			/>
 
			<Tool
 
				Name="VCPostBuildEventTool"
 
			/>
 
		</Configuration>
 
		<Configuration
 
			Name="Release|Win32"
 
			OutputDirectory=".\Release"
 
			IntermediateDirectory=".\Release"
 
			ConfigurationType="4"
 
			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
 
			UseOfMFC="0"
 
			ATLMinimizesCRunTimeLibraryUsage="false"
 
			CharacterSet="2"
 
			>
 
			<Tool
 
				Name="VCPreBuildEventTool"
 
			/>
 
			<Tool
 
				Name="VCCustomBuildTool"
 
			/>
 
			<Tool
 
				Name="VCXMLDataGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCWebServiceProxyGeneratorTool"
 
			/>
 
			<Tool
 
				Name="VCMIDLTool"
 
			/>
 
			<Tool
 
				Name="VCCLCompilerTool"
 
				Optimization="2"
 
				InlineFunctionExpansion="1"
 
				AdditionalIncludeDirectories="./include"
 
				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;CURL_STATICLIB"
 
				StringPooling="true"
 
				RuntimeLibrary="2"
 
				EnableFunctionLevelLinking="true"
 
				PrecompiledHeaderFile=".\Release/twitcurl.pch"
 
				AssemblerListingLocation=".\Release/"
 
				ObjectFile=".\Release/"
 
				ProgramDataBaseFileName=".\Release/"
 
				WarningLevel="3"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCManagedResourceCompilerTool"
 
			/>
 
			<Tool
 
				Name="VCResourceCompilerTool"
 
				PreprocessorDefinitions="NDEBUG"
 
				Culture="1033"
 
			/>
 
			<Tool
 
				Name="VCPreLinkEventTool"
 
			/>
 
			<Tool
 
				Name="VCLibrarianTool"
 
				LinkLibraryDependencies="true"
 
				AdditionalDependencies="libcurl.lib"
 
				OutputFile=".\Release\twitcurl.lib"
 
				AdditionalLibraryDirectories="./lib"
 
				SuppressStartupBanner="true"
 
			/>
 
			<Tool
 
				Name="VCALinkTool"
 
			/>
 
			<Tool
 
				Name="VCXDCMakeTool"
 
			/>
 
			<Tool
 
				Name="VCBscMakeTool"
 
				SuppressStartupBanner="true"
 
				OutputFile=".\Release/twitcurl.bsc"
 
			/>
 
			<Tool
 
				Name="VCFxCopTool"
 
			/>
 
			<Tool
 
				Name="VCPostBuildEventTool"
 
			/>
 
		</Configuration>
 
	</Configurations>
 
	<References>
 
	</References>
 
	<Files>
 
		<Filter
 
			Name="Source Files"
 
			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
 
			>
 
			<File
 
				RelativePath="base64.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="HMAC_SHA1.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="oauthlib.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="SHA1.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="twitcurl.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
			<File
 
				RelativePath="urlencode.cpp"
 
				>
 
				<FileConfiguration
 
					Name="Debug|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
				<FileConfiguration
 
					Name="Release|Win32"
 
					>
 
					<Tool
 
						Name="VCCLCompilerTool"
 
						AdditionalIncludeDirectories=""
 
						PreprocessorDefinitions=""
 
					/>
 
				</FileConfiguration>
 
			</File>
 
		</Filter>
 
		<Filter
 
			Name="Header Files"
 
			Filter="h;hpp;hxx;hm;inl"
 
			>
 
			<File
 
				RelativePath="base64.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="HMAC_SHA1.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="oauthlib.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="SHA1.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="twitcurl.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath=".\twitcurlurls.h"
 
				>
 
			</File>
 
			<File
 
				RelativePath="urlencode.h"
 
				>
 
			</File>
 
		</Filter>
 
	</Files>
 
	<Globals>
 
	</Globals>
 
</VisualStudioProject>
backends/twitter/libtwitcurl/twitcurlurls.h
Show inline comments
 
#ifndef _TWITCURLURLS_H_
 
#define _TWITCURLURLS_H_
 
 
#include <string>
 
#include <cstring>
 
 
/* Default values used in twitcurl */
 
namespace twitCurlDefaults
 
{
 
    /* Constants */
 
    const int TWITCURL_DEFAULT_BUFFSIZE = 1024;
 
    const std::string TWITCURL_COLON = ":";
 
    const char TWITCURL_EOS = '\0';
 
    const unsigned int MAX_TIMELINE_TWEET_COUNT = 200;
 
 
    /* Miscellaneous data used to build twitter URLs*/
 
    const std::string TWITCURL_STATUSSTRING = "status=";
 
    const std::string TWITCURL_TEXTSTRING = "text=";
 
    const std::string TWITCURL_QUERYSTRING = "query=";
 
    const std::string TWITCURL_SEARCHQUERYSTRING = "q=";
 
    const std::string TWITCURL_SCREENNAME = "screen_name=";
 
    const std::string TWITCURL_USERID = "user_id=";
 
    const std::string TWITCURL_EXTENSIONFORMATS[2] = { ".json",
 
                                                       ".xml"
 
                                                     };
 
    const std::string TWITCURL_PROTOCOLS[2] =        { "https://",
 
                                                       "http://"
 
                                                     };
 
    const std::string TWITCURL_TARGETSCREENNAME = "target_screen_name=";
 
    const std::string TWITCURL_TARGETUSERID = "target_id=";
 
    const std::string TWITCURL_SINCEID = "since_id=";
 
    const std::string TWITCURL_TRIMUSER = "trim_user=true";
 
    const std::string TWITCURL_INCRETWEETS = "include_rts=true";
 
    const std::string TWITCURL_COUNT = "count=";
 
    const std::string TWITCURL_NEXT_CURSOR = "cursor=";
 
    const std::string TWITCURL_SKIP_STATUS = "skip_status=";
 
    const std::string TWITCURL_INCLUDE_ENTITIES = "include_entities=";
 
    const std::string TWITCURL_STRINGIFY_IDS = "stringify_ids=";
 
    const std::string TWITCURL_INREPLYTOSTATUSID = "in_reply_to_status_id=";
 
 
    /* URL separators */
 
    const std::string TWITCURL_URL_SEP_AMP = "&";
 
    const std::string TWITCURL_URL_SEP_QUES = "?";
 
};
 
 
/* Default twitter URLs */
 
namespace twitterDefaults
 
{
 
    /* Base URL */
 
    const std::string TWITCURL_BASE_URL = "api.twitter.com/1.1/";
 
 
    /* Search URLs */
 
    const std::string TWITCURL_SEARCH_URL = TWITCURL_BASE_URL + "search/tweets";
 
 
    /* Status URLs */
 
    const std::string TWITCURL_STATUSUPDATE_URL = TWITCURL_BASE_URL + "statuses/update";
 
    const std::string TWITCURL_STATUSSHOW_URL = TWITCURL_BASE_URL + "statuses/show/";
 
    const std::string TWITCURL_STATUDESTROY_URL = TWITCURL_BASE_URL + "statuses/destroy/";
 
    const std::string TWITCURL_RETWEET_URL = TWITCURL_BASE_URL + "statuses/retweet/";
 
 
    /* Timeline URLs */
 
    const std::string TWITCURL_HOME_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/home_timeline";
 
    const std::string TWITCURL_PUBLIC_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/public_timeline";
 
    const std::string TWITCURL_FEATURED_USERS_URL = TWITCURL_BASE_URL + "statuses/featured";
 
    const std::string TWITCURL_FRIENDS_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/friends_timeline";
 
    const std::string TWITCURL_MENTIONS_URL = TWITCURL_BASE_URL + "statuses/mentions";
 
    const std::string TWITCURL_USERTIMELINE_URL = TWITCURL_BASE_URL + "statuses/user_timeline";
 
 
    /* Users URLs */
 
    const std::string TWITCURL_LOOKUPUSERS_URL = TWITCURL_BASE_URL + "users/lookup";
 
    const std::string TWITCURL_SHOWUSERS_URL = TWITCURL_BASE_URL + "users/show";
 
    const std::string TWITCURL_SHOWFRIENDS_URL = TWITCURL_BASE_URL + "statuses/friends";
 
    const std::string TWITCURL_SHOWFOLLOWERS_URL = TWITCURL_BASE_URL + "statuses/followers";
 
 
    /* Direct messages URLs */
 
    const std::string TWITCURL_DIRECTMESSAGES_URL = TWITCURL_BASE_URL + "direct_messages";
 
    const std::string TWITCURL_DIRECTMESSAGENEW_URL = TWITCURL_BASE_URL + "direct_messages/new";
 
    const std::string TWITCURL_DIRECTMESSAGESSENT_URL = TWITCURL_BASE_URL + "direct_messages/sent";
 
    const std::string TWITCURL_DIRECTMESSAGEDESTROY_URL = TWITCURL_BASE_URL + "direct_messages/destroy/";
 
 
    /* Friendships URLs */
 
    const std::string TWITCURL_FRIENDSHIPSCREATE_URL = TWITCURL_BASE_URL + "friendships/create";
 
    const std::string TWITCURL_FRIENDSHIPSDESTROY_URL = TWITCURL_BASE_URL + "friendships/destroy";
 
    const std::string TWITCURL_FRIENDSHIPSSHOW_URL = TWITCURL_BASE_URL + "friendships/show";
 
 
    /* Social graphs URLs */
 
    const std::string TWITCURL_FRIENDSIDS_URL = TWITCURL_BASE_URL + "friends/ids";
 
    const std::string TWITCURL_FOLLOWERSIDS_URL = TWITCURL_BASE_URL + "followers/ids";
 
 
    /* Account URLs */
 
    const std::string TWITCURL_ACCOUNTRATELIMIT_URL = TWITCURL_BASE_URL + "account/rate_limit_status";
 
    const std::string TWITCURL_ACCOUNTVERIFYCRED_URL = TWITCURL_BASE_URL + "account/verify_credentials";
 
 
    /* Favorites URLs */
 
    const std::string TWITCURL_FAVORITESGET_URL = TWITCURL_BASE_URL + "favorites";
 
    const std::string TWITCURL_FAVORITECREATE_URL = TWITCURL_BASE_URL + "favorites/create/";
 
    const std::string TWITCURL_FAVORITEDESTROY_URL = TWITCURL_BASE_URL + "favorites/destroy/";
 
 
    /* Block URLs */
 
    const std::string TWITCURL_BLOCKSCREATE_URL = TWITCURL_BASE_URL + "blocks/create/";
 
    const std::string TWITCURL_BLOCKSDESTROY_URL = TWITCURL_BASE_URL + "blocks/destroy/";
 
    const std::string TWITCURL_BLOCKSLIST_URL = TWITCURL_BASE_URL + "blocks/list";
 
    const std::string TWITCURL_BLOCKSIDS_URL = TWITCURL_BASE_URL + "blocks/ids";
 
 
    /* Saved Search URLs */
 
    const std::string TWITCURL_SAVEDSEARCHGET_URL = TWITCURL_BASE_URL + "saved_searches";
 
    const std::string TWITCURL_SAVEDSEARCHSHOW_URL = TWITCURL_BASE_URL + "saved_searches/show/";
 
    const std::string TWITCURL_SAVEDSEARCHCREATE_URL = TWITCURL_BASE_URL + "saved_searches/create";
 
    const std::string TWITCURL_SAVEDSEARCHDESTROY_URL = TWITCURL_BASE_URL + "saved_searches/destroy/";
 
 
    /* Trends URLs */
 
    const std::string TWITCURL_TRENDS_URL = TWITCURL_BASE_URL + "trends";
 
    const std::string TWITCURL_TRENDSDAILY_URL = TWITCURL_BASE_URL + "trends/daily";
 
    const std::string TWITCURL_TRENDSCURRENT_URL = TWITCURL_BASE_URL + "trends/current";
 
    const std::string TWITCURL_TRENDSWEEKLY_URL = TWITCURL_BASE_URL + "trends/weekly";
 
    const std::string TWITCURL_TRENDSAVAILABLE_URL = TWITCURL_BASE_URL + "trends/available";
 
 
};
 
 
namespace oAuthLibDefaults
 
{
 
    /* Constants */
 
    const int OAUTHLIB_BUFFSIZE = 1024;
 
    const int OAUTHLIB_BUFFSIZE_LARGE = 1024;
 
    const std::string OAUTHLIB_CONSUMERKEY_KEY = "oauth_consumer_key";
 
    const std::string OAUTHLIB_CALLBACK_KEY = "oauth_callback";
 
    const std::string OAUTHLIB_VERSION_KEY = "oauth_version";
 
    const std::string OAUTHLIB_SIGNATUREMETHOD_KEY = "oauth_signature_method";
 
    const std::string OAUTHLIB_SIGNATURE_KEY = "oauth_signature";
 
    const std::string OAUTHLIB_TIMESTAMP_KEY = "oauth_timestamp";
 
    const std::string OAUTHLIB_NONCE_KEY = "oauth_nonce";
 
    const std::string OAUTHLIB_TOKEN_KEY = "oauth_token";
 
    const std::string OAUTHLIB_TOKENSECRET_KEY = "oauth_token_secret";
 
    const std::string OAUTHLIB_VERIFIER_KEY = "oauth_verifier";
 
    const std::string OAUTHLIB_SCREENNAME_KEY = "screen_name";
 
    const std::string OAUTHLIB_AUTHENTICITY_TOKEN_KEY = "authenticity_token";
 
    const std::string OAUTHLIB_SESSIONUSERNAME_KEY = "session[username_or_email]";
 
    const std::string OAUTHLIB_SESSIONPASSWORD_KEY = "session[password]";
 
    const std::string OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY = "authenticity_token\" type=\"hidden\" value=\"";
 
    const std::string OAUTHLIB_TOKEN_TWITTER_RESP_KEY = "oauth_token\" type=\"hidden\" value=\"";
 
    const std::string OAUTHLIB_PIN_TWITTER_RESP_KEY = "code-desc\"><code>";
 
    const std::string OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP = "\" />";
 
    const std::string OAUTHLIB_PIN_END_TAG_TWITTER_RESP = "</code>";
 
 
    const std::string OAUTHLIB_AUTHHEADER_STRING = "Authorization: OAuth ";
 
};
 
 
namespace oAuthTwitterApiUrls
 
{
 
    /* Twitter OAuth API URLs */
 
    const std::string OAUTHLIB_TWITTER_REQUEST_TOKEN_URL = "api.twitter.com/oauth/request_token";
 
    const std::string OAUTHLIB_TWITTER_AUTHORIZE_URL = "api.twitter.com/oauth/authorize?oauth_token=";
 
    const std::string OAUTHLIB_TWITTER_ACCESS_TOKEN_URL = "api.twitter.com/oauth/access_token";
 
};
 
 
#endif // _TWITCURLURLS_H_
 
#ifndef _TWITCURLURLS_H_
 
#define _TWITCURLURLS_H_
 

	
 
#include <string>
 
#include <cstring>
 

	
 
/* Default values used in twitcurl */
 
namespace twitCurlDefaults
 
{
 
    /* Constants */
 
    const int TWITCURL_DEFAULT_BUFFSIZE = 1024;
 
    const std::string TWITCURL_COLON = ":";
 
    const char TWITCURL_EOS = '\0';
 
    const unsigned int MAX_TIMELINE_TWEET_COUNT = 200;
 

	
 
    /* Miscellaneous data used to build twitter URLs*/
 
    const std::string TWITCURL_STATUSSTRING = "status=";
 
    const std::string TWITCURL_TEXTSTRING = "text=";
 
    const std::string TWITCURL_QUERYSTRING = "query=";
 
    const std::string TWITCURL_SEARCHQUERYSTRING = "q=";
 
    const std::string TWITCURL_SCREENNAME = "screen_name=";
 
    const std::string TWITCURL_USERID = "user_id=";
 
    const std::string TWITCURL_EXTENSIONFORMATS[2] = { ".json",
 
                                                       ".xml"
 
                                                     };
 
    const std::string TWITCURL_PROTOCOLS[2] =        { "https://",
 
                                                       "http://"
 
                                                     };
 
    const std::string TWITCURL_TARGETSCREENNAME = "target_screen_name=";
 
    const std::string TWITCURL_TARGETUSERID = "target_id=";
 
    const std::string TWITCURL_SINCEID = "since_id=";
 
    const std::string TWITCURL_TRIMUSER = "trim_user=true";
 
    const std::string TWITCURL_INCRETWEETS = "include_rts=true";
 
    const std::string TWITCURL_COUNT = "count=";
 
    const std::string TWITCURL_NEXT_CURSOR = "cursor=";
 
    const std::string TWITCURL_SKIP_STATUS = "skip_status=";
 
    const std::string TWITCURL_INCLUDE_ENTITIES = "include_entities=";
 
    const std::string TWITCURL_STRINGIFY_IDS = "stringify_ids=";
 
    const std::string TWITCURL_INREPLYTOSTATUSID = "in_reply_to_status_id=";
 

	
 
    /* URL separators */
 
    const std::string TWITCURL_URL_SEP_AMP = "&";
 
    const std::string TWITCURL_URL_SEP_QUES = "?";
 
};
 

	
 
/* Default twitter URLs */
 
namespace twitterDefaults
 
{
 
    /* Base URL */
 
    const std::string TWITCURL_BASE_URL = "api.twitter.com/1.1/";
 

	
 
    /* Search URLs */
 
    const std::string TWITCURL_SEARCH_URL = TWITCURL_BASE_URL + "search/tweets";
 

	
 
    /* Status URLs */
 
    const std::string TWITCURL_STATUSUPDATE_URL = TWITCURL_BASE_URL + "statuses/update";
 
    const std::string TWITCURL_STATUSSHOW_URL = TWITCURL_BASE_URL + "statuses/show/";
 
    const std::string TWITCURL_STATUDESTROY_URL = TWITCURL_BASE_URL + "statuses/destroy/";
 
    const std::string TWITCURL_RETWEET_URL = TWITCURL_BASE_URL + "statuses/retweet/";
 

	
 
    /* Timeline URLs */
 
    const std::string TWITCURL_HOME_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/home_timeline";
 
    const std::string TWITCURL_PUBLIC_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/public_timeline";
 
    const std::string TWITCURL_FEATURED_USERS_URL = TWITCURL_BASE_URL + "statuses/featured";
 
    const std::string TWITCURL_FRIENDS_TIMELINE_URL = TWITCURL_BASE_URL + "statuses/friends_timeline";
 
    const std::string TWITCURL_MENTIONS_URL = TWITCURL_BASE_URL + "statuses/mentions";
 
    const std::string TWITCURL_USERTIMELINE_URL = TWITCURL_BASE_URL + "statuses/user_timeline";
 

	
 
    /* Users URLs */
 
    const std::string TWITCURL_LOOKUPUSERS_URL = TWITCURL_BASE_URL + "users/lookup";
 
    const std::string TWITCURL_SHOWUSERS_URL = TWITCURL_BASE_URL + "users/show";
 
    const std::string TWITCURL_SHOWFRIENDS_URL = TWITCURL_BASE_URL + "statuses/friends";
 
    const std::string TWITCURL_SHOWFOLLOWERS_URL = TWITCURL_BASE_URL + "statuses/followers";
 

	
 
    /* Direct messages URLs */
 
    const std::string TWITCURL_DIRECTMESSAGES_URL = TWITCURL_BASE_URL + "direct_messages";
 
    const std::string TWITCURL_DIRECTMESSAGENEW_URL = TWITCURL_BASE_URL + "direct_messages/new";
 
    const std::string TWITCURL_DIRECTMESSAGESSENT_URL = TWITCURL_BASE_URL + "direct_messages/sent";
 
    const std::string TWITCURL_DIRECTMESSAGEDESTROY_URL = TWITCURL_BASE_URL + "direct_messages/destroy/";
 

	
 
    /* Friendships URLs */
 
    const std::string TWITCURL_FRIENDSHIPSCREATE_URL = TWITCURL_BASE_URL + "friendships/create";
 
    const std::string TWITCURL_FRIENDSHIPSDESTROY_URL = TWITCURL_BASE_URL + "friendships/destroy";
 
    const std::string TWITCURL_FRIENDSHIPSSHOW_URL = TWITCURL_BASE_URL + "friendships/show";
 

	
 
    /* Social graphs URLs */
 
    const std::string TWITCURL_FRIENDSIDS_URL = TWITCURL_BASE_URL + "friends/ids";
 
    const std::string TWITCURL_FOLLOWERSIDS_URL = TWITCURL_BASE_URL + "followers/ids";
 

	
 
    /* Account URLs */
 
    const std::string TWITCURL_ACCOUNTRATELIMIT_URL = TWITCURL_BASE_URL + "account/rate_limit_status";
 
    const std::string TWITCURL_ACCOUNTVERIFYCRED_URL = TWITCURL_BASE_URL + "account/verify_credentials";
 

	
 
    /* Favorites URLs */
 
    const std::string TWITCURL_FAVORITESGET_URL = TWITCURL_BASE_URL + "favorites";
 
    const std::string TWITCURL_FAVORITECREATE_URL = TWITCURL_BASE_URL + "favorites/create/";
 
    const std::string TWITCURL_FAVORITEDESTROY_URL = TWITCURL_BASE_URL + "favorites/destroy/";
 

	
 
    /* Block URLs */
 
    const std::string TWITCURL_BLOCKSCREATE_URL = TWITCURL_BASE_URL + "blocks/create/";
 
    const std::string TWITCURL_BLOCKSDESTROY_URL = TWITCURL_BASE_URL + "blocks/destroy/";
 
    const std::string TWITCURL_BLOCKSLIST_URL = TWITCURL_BASE_URL + "blocks/list";
 
    const std::string TWITCURL_BLOCKSIDS_URL = TWITCURL_BASE_URL + "blocks/ids";
 

	
 
    /* Saved Search URLs */
 
    const std::string TWITCURL_SAVEDSEARCHGET_URL = TWITCURL_BASE_URL + "saved_searches";
 
    const std::string TWITCURL_SAVEDSEARCHSHOW_URL = TWITCURL_BASE_URL + "saved_searches/show/";
 
    const std::string TWITCURL_SAVEDSEARCHCREATE_URL = TWITCURL_BASE_URL + "saved_searches/create";
 
    const std::string TWITCURL_SAVEDSEARCHDESTROY_URL = TWITCURL_BASE_URL + "saved_searches/destroy/";
 

	
 
    /* Trends URLs */
 
    const std::string TWITCURL_TRENDS_URL = TWITCURL_BASE_URL + "trends";
 
    const std::string TWITCURL_TRENDSDAILY_URL = TWITCURL_BASE_URL + "trends/daily";
 
    const std::string TWITCURL_TRENDSCURRENT_URL = TWITCURL_BASE_URL + "trends/current";
 
    const std::string TWITCURL_TRENDSWEEKLY_URL = TWITCURL_BASE_URL + "trends/weekly";
 
    const std::string TWITCURL_TRENDSAVAILABLE_URL = TWITCURL_BASE_URL + "trends/available";
 

	
 
};
 

	
 
namespace oAuthLibDefaults
 
{
 
    /* Constants */
 
    const int OAUTHLIB_BUFFSIZE = 1024;
 
    const int OAUTHLIB_BUFFSIZE_LARGE = 1024;
 
    const std::string OAUTHLIB_CONSUMERKEY_KEY = "oauth_consumer_key";
 
    const std::string OAUTHLIB_CALLBACK_KEY = "oauth_callback";
 
    const std::string OAUTHLIB_VERSION_KEY = "oauth_version";
 
    const std::string OAUTHLIB_SIGNATUREMETHOD_KEY = "oauth_signature_method";
 
    const std::string OAUTHLIB_SIGNATURE_KEY = "oauth_signature";
 
    const std::string OAUTHLIB_TIMESTAMP_KEY = "oauth_timestamp";
 
    const std::string OAUTHLIB_NONCE_KEY = "oauth_nonce";
 
    const std::string OAUTHLIB_TOKEN_KEY = "oauth_token";
 
    const std::string OAUTHLIB_TOKENSECRET_KEY = "oauth_token_secret";
 
    const std::string OAUTHLIB_VERIFIER_KEY = "oauth_verifier";
 
    const std::string OAUTHLIB_SCREENNAME_KEY = "screen_name";
 
    const std::string OAUTHLIB_AUTHENTICITY_TOKEN_KEY = "authenticity_token";
 
    const std::string OAUTHLIB_SESSIONUSERNAME_KEY = "session[username_or_email]";
 
    const std::string OAUTHLIB_SESSIONPASSWORD_KEY = "session[password]";
 
    const std::string OAUTHLIB_AUTHENTICITY_TOKEN_TWITTER_RESP_KEY = "authenticity_token\" type=\"hidden\" value=\"";
 
    const std::string OAUTHLIB_TOKEN_TWITTER_RESP_KEY = "oauth_token\" type=\"hidden\" value=\"";
 
    const std::string OAUTHLIB_PIN_TWITTER_RESP_KEY = "code-desc\"><code>";
 
    const std::string OAUTHLIB_TOKEN_END_TAG_TWITTER_RESP = "\" />";
 
    const std::string OAUTHLIB_PIN_END_TAG_TWITTER_RESP = "</code>";
 

	
 
    const std::string OAUTHLIB_AUTHHEADER_STRING = "Authorization: OAuth ";
 
};
 

	
 
namespace oAuthTwitterApiUrls
 
{
 
    /* Twitter OAuth API URLs */
 
    const std::string OAUTHLIB_TWITTER_REQUEST_TOKEN_URL = "api.twitter.com/oauth/request_token";
 
    const std::string OAUTHLIB_TWITTER_AUTHORIZE_URL = "api.twitter.com/oauth/authorize?oauth_token=";
 
    const std::string OAUTHLIB_TWITTER_ACCESS_TOKEN_URL = "api.twitter.com/oauth/access_token";
 
};
 

	
 
#endif // _TWITCURLURLS_H_
backends/twitter/libtwitcurl/urlencode.cpp
Show inline comments
 
#include "urlencode.h"
 
 
std::string char2hex( char dec )
 
{
 
	char dig1 = (dec&0xF0)>>4;
 
	char dig2 = (dec&0x0F);
 
	if ( 0<= dig1 && dig1<= 9) dig1+=48;    //0,48 in ascii
 
	if (10<= dig1 && dig1<=15) dig1+=65-10; //A,65 in ascii
 
	if ( 0<= dig2 && dig2<= 9) dig2+=48;
 
	if (10<= dig2 && dig2<=15) dig2+=65-10;
 
 
    std::string r;
 
	r.append( &dig1, 1);
 
	r.append( &dig2, 1);
 
	return r;
 
}
 
 
std::string urlencode( const std::string &c )
 
{
 
 
    std::string escaped;
 
	int max = c.length();
 
	for(int i=0; i<max; i++)
 
	{
 
		if ( (48 <= c[i] && c[i] <= 57) ||//0-9
 
			(65 <= c[i] && c[i] <= 90) ||//ABC...XYZ
 
			(97 <= c[i] && c[i] <= 122) || //abc...xyz
 
			(c[i]=='~' || c[i]=='-' || c[i]=='_' || c[i]=='.')
 
			)
 
		{
 
			escaped.append( &c[i], 1);
 
		}
 
		else
 
		{
 
			escaped.append("%");
 
			escaped.append( char2hex(c[i]) );//converts char 255 to string "FF"
 
		}
 
	}
 
	return escaped;
 
#include "urlencode.h"
 

	
 
std::string char2hex( char dec )
 
{
 
	char dig1 = (dec&0xF0)>>4;
 
	char dig2 = (dec&0x0F);
 
	if ( 0<= dig1 && dig1<= 9) dig1+=48;    //0,48 in ascii
 
	if (10<= dig1 && dig1<=15) dig1+=65-10; //A,65 in ascii
 
	if ( 0<= dig2 && dig2<= 9) dig2+=48;
 
	if (10<= dig2 && dig2<=15) dig2+=65-10;
 

	
 
    std::string r;
 
	r.append( &dig1, 1);
 
	r.append( &dig2, 1);
 
	return r;
 
}
 

	
 
std::string urlencode( const std::string &c )
 
{
 

	
 
    std::string escaped;
 
	int max = c.length();
 
	for(int i=0; i<max; i++)
 
	{
 
		if ( (48 <= c[i] && c[i] <= 57) ||//0-9
 
			(65 <= c[i] && c[i] <= 90) ||//ABC...XYZ
 
			(97 <= c[i] && c[i] <= 122) || //abc...xyz
 
			(c[i]=='~' || c[i]=='-' || c[i]=='_' || c[i]=='.')
 
			)
 
		{
 
			escaped.append( &c[i], 1);
 
		}
 
		else
 
		{
 
			escaped.append("%");
 
			escaped.append( char2hex(c[i]) );//converts char 255 to string "FF"
 
		}
 
	}
 
	return escaped;
 
}
 
\ No newline at end of file
backends/twitter/libtwitcurl/urlencode.h
Show inline comments
 
#ifndef __URLENCODE_H__
 
#define __URLENCODE_H__
 
 
#include <iostream>
 
#include <string>
 
 
std::string char2hex( char dec );
 
std::string urlencode( const std::string &c );
 
 
#ifndef __URLENCODE_H__
 
#define __URLENCODE_H__
 

	
 
#include <iostream>
 
#include <string>
 

	
 
std::string char2hex( char dec );
 
std::string urlencode( const std::string &c );
 

	
 
#endif // __URLENCODE_H__
 
\ No newline at end of file
cmake_modules/SwiftenConfig.cmake
Show inline comments
 
FIND_LIBRARY(SWIFTEN_LIBRARY NAMES Swiften Swiften3 HINTS ../lib)
 
FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften/Swiften.h" PATH_SUFFIXES libSwiften Swiften HINTS ../include)
 
 
if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
	find_program(SWIFTEN_CONFIG_EXECUTABLE NAMES swiften-config DOC "swiften-config executable" HINTS ../bin)
 
	set( SWIFTEN_CFLAGS "" )
 
	if (SWIFTEN_CONFIG_EXECUTABLE)
 
		execute_process(
 
			COMMAND ${SWIFTEN_CONFIG_EXECUTABLE} --libs
 
			OUTPUT_VARIABLE SWIFTEN_LIB)
 
		string(REGEX REPLACE "[\r\n]"                  " " SWIFTEN_LIB ${SWIFTEN_LIB})
 
		string(REGEX REPLACE " +$"                     ""  SWIFTEN_LIB ${SWIFTEN_LIB})
 
		string(REGEX REPLACE " " ";" SWIFTEN_LIB ${SWIFTEN_LIB})
 
		set(SWIFTEN_LIBRARY "")
 
		foreach(f ${SWIFTEN_LIB})
 
			STRING(SUBSTRING ${f} 0 2 f_out)
 
			STRING(COMPARE EQUAL ${f_out} "/L" IS_PATH)
 
			if(${IS_PATH})
 
				string(REGEX REPLACE "/LIBPATH:" ""  f_replaced "${f}")
 
				message("Added link directory: ${f_replaced}")
 
				link_directories(${f_replaced})
 
			else()
 
				list(APPEND SWIFTEN_LIBRARY ${f})
 
			endif()
 
		endforeach(f) 
 
		set( SWIFTEN_FOUND 1 )
 
	else()
 
		message( STATUS "Could NOT find swiften-config" )
 
	endif()
 
 
	if (SWIFTEN_FOUND)
 
		set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR} )
 
		message( STATUS "Found libSwiften: ${SWIFTEN_LIBRARY}, ${SWIFTEN_INCLUDE_DIR}")
 
	endif()
 
 
else( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
    message( STATUS "Could NOT find libSwiften" )
 
endif( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
FIND_LIBRARY(SWIFTEN_LIBRARY NAMES Swiften Swiften3 HINTS ../lib)
 
FIND_PATH(SWIFTEN_INCLUDE_DIR NAMES "Swiften/Swiften.h" PATH_SUFFIXES libSwiften Swiften HINTS ../include)
 

	
 
if( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
	find_program(SWIFTEN_CONFIG_EXECUTABLE NAMES swiften-config DOC "swiften-config executable" HINTS ../bin)
 
	set( SWIFTEN_CFLAGS "" )
 
	if (SWIFTEN_CONFIG_EXECUTABLE)
 
		execute_process(
 
			COMMAND ${SWIFTEN_CONFIG_EXECUTABLE} --libs
 
			OUTPUT_VARIABLE SWIFTEN_LIB)
 
		string(REGEX REPLACE "[\r\n]"                  " " SWIFTEN_LIB ${SWIFTEN_LIB})
 
		string(REGEX REPLACE " +$"                     ""  SWIFTEN_LIB ${SWIFTEN_LIB})
 
		set(SWIFTEN_LIBRARY "")
 
		if (APPLE)
 
			string(REGEX MATCHALL "-framework [A-Za-z]+" APPLE_FRAMEWORKS ${SWIFTEN_LIB})
 
			foreach(framework ${APPLE_FRAMEWORKS})
 
				list(APPEND SWIFTEN_LIBRARY ${framework} )
 
			endforeach(framework)
 
			string(REGEX REPLACE "-framework [A-Za-z]+" "" SWIFTEN_LIB ${SWIFTEN_LIB})
 
		endif(APPLE)
 
		string(REGEX REPLACE " " ";" SWIFTEN_LIB ${SWIFTEN_LIB})
 
		foreach(f ${SWIFTEN_LIB})
 
			STRING(SUBSTRING ${f} 0 2 f_out)
 
			STRING(COMPARE EQUAL ${f_out} "/L" IS_PATH)
 
			if(${IS_PATH})
 
				string(REGEX REPLACE "/LIBPATH:" ""  f_replaced "${f}")
 
				message("Added link directory: ${f_replaced}")
 
				link_directories(${f_replaced})
 
			else()
 
				list(APPEND SWIFTEN_LIBRARY ${f})
 
			endif()
 
		endforeach(f) 
 
		set( SWIFTEN_FOUND 1 )
 
	else()
 
		message( STATUS "Could NOT find swiften-config" )
 
	endif()
 

	
 
	if (SWIFTEN_FOUND)
 
		set( SWIFTEN_INCLUDE_DIR ${SWIFTEN_INCLUDE_DIR} )
 
		message( STATUS "Found libSwiften: ${SWIFTEN_LIBRARY}, ${SWIFTEN_INCLUDE_DIR}")
 
	endif()
 

	
 
else( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
 
    message( STATUS "Could NOT find libSwiften" )
 
endif( SWIFTEN_LIBRARY AND SWIFTEN_INCLUDE_DIR )
include/CMakeLists.txt
Show inline comments
 
ADD_SUBDIRECTORY(transport)
 
ADD_SUBDIRECTORY(transport)
include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp
Show inline comments
 
/*
 
 * Copyright (c) 2011 Tobias Markmann
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 
 
#include "CombinedOutgoingFileTransferManager.h"
 
 
#include <boost/smart_ptr/make_shared.hpp>
 
 
#include <Swiften/JID/JID.h>
 
#include "Swiften/Disco/EntityCapsProvider.h"
 
#include <Swiften/Jingle/JingleSessionManager.h>
 
#include <Swiften/Jingle/JingleSessionImpl.h>
 
#include <Swiften/Jingle/JingleContentID.h>
 
#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
 
#include <Swiften/FileTransfer/MyOutgoingSIFileTransfer.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/Base/IDGenerator.h>
 
#include <Swiften/Elements/Presence.h>
 
#include <Swiften/Base/foreach.h>
 
 
 
namespace Swift {
 
 
CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) {
 
	idGenerator = new IDGenerator();
 
}
 
 
CombinedOutgoingFileTransferManager::~CombinedOutgoingFileTransferManager() {
 
	delete idGenerator;
 
}
 
 
boost::shared_ptr<OutgoingFileTransfer> CombinedOutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
 
	// check if receipient support Jingle FT
 
	boost::optional<JID> fullJID = highestPriorityJIDSupportingJingle(receipient);
 
	if (!fullJID.is_initialized()) {
 
		fullJID = highestPriorityJIDSupportingSI(receipient);
 
	}
 
	else {
 
		JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(from, receipient, idGenerator->generateID(), iqRouter);
 
 
		//jsManager->getSession(receipient, idGenerator->generateID());
 
		assert(jingleSession);
 
		jsManager->registerOutgoingSession(from, jingleSession);
 
#if !HAVE_SWIFTEN_3
 
		boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT =  boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy));
 
		return jingleFT;
 
#endif
 
	}
 
 
	if (!fullJID.is_initialized()) {
 
		return boost::shared_ptr<OutgoingFileTransfer>();
 
	}
 
	
 
	// otherwise try SI
 
	boost::shared_ptr<MyOutgoingSIFileTransfer> jingleFT =  boost::shared_ptr<MyOutgoingSIFileTransfer>(new MyOutgoingSIFileTransfer(idGenerator->generateID(), from, fullJID.get(), fileInfo.getName(), fileInfo.getSize(), fileInfo.getDescription(), readBytestream, iqRouter, bytestreamServer, bytestreamRegistry));
 
	// else fail
 
	
 
	return jingleFT;
 
}
 
 
boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingJingle(const JID& bareJID) {
 
	JID fullReceipientJID;
 
	int priority = INT_MIN;
 
	
 
	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
 
	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
 
 
	//iterate over them
 
	foreach(Presence::ref pres, presences) {
 
		if (pres->getPriority() > priority) {
 
			// look up caps from the jid
 
			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
 
			if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
 
				info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
 
			
 
				priority = pres->getPriority();
 
				fullReceipientJID = pres->getFrom();
 
			}
 
		}
 
	}
 
	
 
	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
 
}
 
 
boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingSI(const JID& bareJID) {
 
	JID fullReceipientJID;
 
	int priority = INT_MIN;
 
	
 
	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
 
	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
 
 
	//iterate over them
 
	foreach(Presence::ref pres, presences) {
 
		if (pres->getPriority() > priority) {
 
			// look up caps from the jid
 
			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
 
			if (info && info->hasFeature("http://jabber.org/protocol/si/profile/file-transfer")) {
 
			
 
				priority = pres->getPriority();
 
				fullReceipientJID = pres->getFrom();
 
			}
 
		}
 
	}
 
	
 
	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
 
}
 
 
}
 
/*
 
 * Copyright (c) 2011 Tobias Markmann
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#include "CombinedOutgoingFileTransferManager.h"
 

	
 
#include <boost/smart_ptr/make_shared.hpp>
 

	
 
#include <Swiften/JID/JID.h>
 
#include "Swiften/Disco/EntityCapsProvider.h"
 
#include <Swiften/Jingle/JingleSessionManager.h>
 
#include <Swiften/Jingle/JingleSessionImpl.h>
 
#include <Swiften/Jingle/JingleContentID.h>
 
#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h>
 
#include <Swiften/FileTransfer/MyOutgoingSIFileTransfer.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/Base/IDGenerator.h>
 
#include <Swiften/Elements/Presence.h>
 
#include <Swiften/Base/foreach.h>
 

	
 

	
 
namespace Swift {
 

	
 
CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) {
 
	idGenerator = new IDGenerator();
 
}
 

	
 
CombinedOutgoingFileTransferManager::~CombinedOutgoingFileTransferManager() {
 
	delete idGenerator;
 
}
 

	
 
boost::shared_ptr<OutgoingFileTransfer> CombinedOutgoingFileTransferManager::createOutgoingFileTransfer(const JID& from, const JID& receipient, boost::shared_ptr<ReadBytestream> readBytestream, const StreamInitiationFileInfo& fileInfo) {
 
	// check if receipient support Jingle FT
 
	boost::optional<JID> fullJID = highestPriorityJIDSupportingJingle(receipient);
 
	if (!fullJID.is_initialized()) {
 
		fullJID = highestPriorityJIDSupportingSI(receipient);
 
	}
 
	else {
 
		JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(from, receipient, idGenerator->generateID(), iqRouter);
 

	
 
		//jsManager->getSession(receipient, idGenerator->generateID());
 
		assert(jingleSession);
 
		jsManager->registerOutgoingSession(from, jingleSession);
 
#if !HAVE_SWIFTEN_3
 
		boost::shared_ptr<OutgoingJingleFileTransfer> jingleFT =  boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(jingleSession, remoteFactory, localFactory, iqRouter, idGenerator, from, receipient, readBytestream, fileInfo, bytestreamRegistry, bytestreamProxy));
 
		return jingleFT;
 
#endif
 
	}
 

	
 
	if (!fullJID.is_initialized()) {
 
		return boost::shared_ptr<OutgoingFileTransfer>();
 
	}
 
	
 
	// otherwise try SI
 
	boost::shared_ptr<MyOutgoingSIFileTransfer> jingleFT =  boost::shared_ptr<MyOutgoingSIFileTransfer>(new MyOutgoingSIFileTransfer(idGenerator->generateID(), from, fullJID.get(), fileInfo.getName(), fileInfo.getSize(), fileInfo.getDescription(), readBytestream, iqRouter, bytestreamServer, bytestreamRegistry));
 
	// else fail
 
	
 
	return jingleFT;
 
}
 

	
 
boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingJingle(const JID& bareJID) {
 
	JID fullReceipientJID;
 
	int priority = INT_MIN;
 
	
 
	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
 
	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
 

	
 
	//iterate over them
 
	foreach(Presence::ref pres, presences) {
 
		if (pres->getPriority() > priority) {
 
			// look up caps from the jid
 
			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
 
			if (info && info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) &&
 
				info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) {
 
			
 
				priority = pres->getPriority();
 
				fullReceipientJID = pres->getFrom();
 
			}
 
		}
 
	}
 
	
 
	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
 
}
 

	
 
boost::optional<JID> CombinedOutgoingFileTransferManager::highestPriorityJIDSupportingSI(const JID& bareJID) {
 
	JID fullReceipientJID;
 
	int priority = INT_MIN;
 
	
 
	//getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Remko Tronçon @ 11:11
 
	std::vector<Presence::ref> presences = presenceOracle->getAllPresence(bareJID);
 

	
 
	//iterate over them
 
	foreach(Presence::ref pres, presences) {
 
		if (pres->getPriority() > priority) {
 
			// look up caps from the jid
 
			DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom());
 
			if (info && info->hasFeature("http://jabber.org/protocol/si/profile/file-transfer")) {
 
			
 
				priority = pres->getPriority();
 
				fullReceipientJID = pres->getFrom();
 
			}
 
		}
 
	}
 
	
 
	return fullReceipientJID.isValid() ? boost::optional<JID>(fullReceipientJID) : boost::optional<JID>();
 
}
 

	
 
}
include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h
Show inline comments
 
/*
 
 * Copyright (c) 2011 Tobias Markmann
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 
 
#pragma once
 
 
#include <boost/shared_ptr.hpp>
 
#include <boost/optional.hpp>
 
 
#include <Swiften/JID/JID.h>
 
 
#include "transport/presenceoracle.h"
 
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Swift {
 
 
class JingleSessionManager;
 
class IQRouter;
 
class EntityCapsProvider;
 
class RemoteJingleTransportCandidateSelectorFactory;
 
class LocalJingleTransportCandidateGeneratorFactory;
 
class OutgoingFileTransfer;
 
class JID;
 
class IDGenerator;
 
class ReadBytestream;
 
class StreamInitiationFileInfo;
 
class SOCKS5BytestreamRegistry;
 
class SOCKS5BytestreamProxy;
 
class SOCKS5BytestreamServer;
 
class PresenceOracle;
 
 
class CombinedOutgoingFileTransferManager {
 
public:
 
	CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server);
 
	~CombinedOutgoingFileTransferManager();
 
	
 
	boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
 
 
private:
 
	boost::optional<JID> highestPriorityJIDSupportingJingle(const JID& bareJID);
 
	boost::optional<JID> highestPriorityJIDSupportingSI(const JID& bareJID);
 
	JingleSessionManager* jsManager;
 
	IQRouter* iqRouter;
 
	EntityCapsProvider* capsProvider;
 
	RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
 
	LocalJingleTransportCandidateGeneratorFactory* localFactory;
 
	IDGenerator *idGenerator;
 
	SOCKS5BytestreamRegistry* bytestreamRegistry;
 
	SOCKS5BytestreamProxy* bytestreamProxy;
 
	Transport::PresenceOracle* presenceOracle;
 
	SOCKS5BytestreamServer *bytestreamServer;
 
};
 
 
}
 
/*
 
 * Copyright (c) 2011 Tobias Markmann
 
 * Licensed under the simplified BSD license.
 
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include <boost/shared_ptr.hpp>
 
#include <boost/optional.hpp>
 

	
 
#include <Swiften/JID/JID.h>
 

	
 
#include "transport/presenceoracle.h"
 
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Swift {
 

	
 
class JingleSessionManager;
 
class IQRouter;
 
class EntityCapsProvider;
 
class RemoteJingleTransportCandidateSelectorFactory;
 
class LocalJingleTransportCandidateGeneratorFactory;
 
class OutgoingFileTransfer;
 
class JID;
 
class IDGenerator;
 
class ReadBytestream;
 
class StreamInitiationFileInfo;
 
class SOCKS5BytestreamRegistry;
 
class SOCKS5BytestreamProxy;
 
class SOCKS5BytestreamServer;
 
class PresenceOracle;
 

	
 
class CombinedOutgoingFileTransferManager {
 
public:
 
	CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server);
 
	~CombinedOutgoingFileTransferManager();
 
	
 
	boost::shared_ptr<OutgoingFileTransfer> createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr<ReadBytestream>, const StreamInitiationFileInfo&);
 

	
 
private:
 
	boost::optional<JID> highestPriorityJIDSupportingJingle(const JID& bareJID);
 
	boost::optional<JID> highestPriorityJIDSupportingSI(const JID& bareJID);
 
	JingleSessionManager* jsManager;
 
	IQRouter* iqRouter;
 
	EntityCapsProvider* capsProvider;
 
	RemoteJingleTransportCandidateSelectorFactory* remoteFactory;
 
	LocalJingleTransportCandidateGeneratorFactory* localFactory;
 
	IDGenerator *idGenerator;
 
	SOCKS5BytestreamRegistry* bytestreamRegistry;
 
	SOCKS5BytestreamProxy* bytestreamProxy;
 
	Transport::PresenceOracle* presenceOracle;
 
	SOCKS5BytestreamServer *bytestreamServer;
 
};
 

	
 
}
include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.cpp
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#include <Swiften/FileTransfer/MyOutgoingSIFileTransfer.h>
 
 
#include <boost/bind.hpp>
 
 
#include <Swiften/FileTransfer/StreamInitiationRequest.h>
 
#include <Swiften/FileTransfer/BytestreamsRequest.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
#include <Swiften/FileTransfer/IBBSendSession.h>
 
 
namespace Swift {
 
 
MyOutgoingSIFileTransfer::MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer), registry(registry) {
 
}
 
 
void MyOutgoingSIFileTransfer::start() {
 
	StreamInitiation::ref streamInitiation(new StreamInitiation());
 
	streamInitiation->setID(id);
 
	streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size));
 
	streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
 
	streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
 
	StreamInitiationRequest::ref request = StreamInitiationRequest::create(from, to, streamInitiation, iqRouter);
 
	request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
 
	request->send();
 
}
 
 
void MyOutgoingSIFileTransfer::stop() {
 
}
 
 
void MyOutgoingSIFileTransfer::cancel() {
 
	// TODO
 
// 	session->sendTerminate(JinglePayload::Reason::Cancel);
 
 
	if (ibbSession) {
 
		ibbSession->stop();
 
	}
 
#if !HAVE_SWIFTEN_3
 
	SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to));
 
	if (serverSession) {
 
		serverSession->stop();
 
	}
 
 
	onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
 
#endif
 
}
 
 
void MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) {
 
	if (error) {
 
		finish(FileTransferError());
 
	}
 
	else {
 
		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
 
#if !HAVE_SWIFTEN_3
 
			registry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(id, from, to), bytestream);
 
			socksServer->addReadBytestream(id, from, to, bytestream);
 
#endif
 
			Bytestreams::ref bytestreams(new Bytestreams());
 
			bytestreams->setStreamID(id);
 
			HostAddressPort addressPort = socksServer->getAddressPort();
 
			bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort()));
 
			BytestreamsRequest::ref request = BytestreamsRequest::create(from, to, bytestreams, iqRouter);
 
			request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
 
			request->send();
 
		}
 
		else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
 
			ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, from, to, bytestream, iqRouter));
 
			ibbSession->onFinished.connect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
 
			ibbSession->start();
 
#if !HAVE_SWIFTEN_3
 
			onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
 
#endif
 
		}
 
	}
 
}
 
 
void MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) {
 
	if (error) {
 
		finish(FileTransferError());
 
		return;
 
	}
 
#if !HAVE_SWIFTEN_3
 
	SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to));
 
// 	serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
 
// 	serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
 
	serverSession->startTransfer();
 
	onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
 
#endif	
 
	//socksServer->onTransferFinished.connect();
 
}
 
 
void MyOutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
 
	if (ibbSession) {
 
		ibbSession->onFinished.disconnect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
 
		ibbSession.reset();
 
	}
 
#if !HAVE_SWIFTEN_3
 
	socksServer->removeReadBytestream(id, from, to);
 
	if(error) {
 
		onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
 
	}
 
	else {
 
		onStateChange(FileTransfer::State(FileTransfer::State::Finished));
 
	}
 
#endif
 
	onFinished(error);
 
}
 
 
void MyOutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
 
	finish(error);
 
}
 
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#include <Swiften/FileTransfer/MyOutgoingSIFileTransfer.h>
 

	
 
#include <boost/bind.hpp>
 

	
 
#include <Swiften/FileTransfer/StreamInitiationRequest.h>
 
#include <Swiften/FileTransfer/BytestreamsRequest.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
#include <Swiften/FileTransfer/IBBSendSession.h>
 

	
 
namespace Swift {
 

	
 
MyOutgoingSIFileTransfer::MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer), registry(registry) {
 
}
 

	
 
void MyOutgoingSIFileTransfer::start() {
 
	StreamInitiation::ref streamInitiation(new StreamInitiation());
 
	streamInitiation->setID(id);
 
	streamInitiation->setFileInfo(StreamInitiationFileInfo(name, description, size));
 
	streamInitiation->addProvidedMethod("http://jabber.org/protocol/bytestreams");
 
	streamInitiation->addProvidedMethod("http://jabber.org/protocol/ibb");
 
	StreamInitiationRequest::ref request = StreamInitiationRequest::create(from, to, streamInitiation, iqRouter);
 
	request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2));
 
	request->send();
 
}
 

	
 
void MyOutgoingSIFileTransfer::stop() {
 
}
 

	
 
void MyOutgoingSIFileTransfer::cancel() {
 
	// TODO
 
// 	session->sendTerminate(JinglePayload::Reason::Cancel);
 

	
 
	if (ibbSession) {
 
		ibbSession->stop();
 
	}
 
#if !HAVE_SWIFTEN_3
 
	SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to));
 
	if (serverSession) {
 
		serverSession->stop();
 
	}
 

	
 
	onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
 
#endif
 
}
 

	
 
void MyOutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) {
 
	if (error) {
 
		finish(FileTransferError());
 
	}
 
	else {
 
		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") {
 
#if !HAVE_SWIFTEN_3
 
			registry->addReadBytestream(SOCKS5BytestreamRegistry::getHostname(id, from, to), bytestream);
 
			socksServer->addReadBytestream(id, from, to, bytestream);
 
#endif
 
			Bytestreams::ref bytestreams(new Bytestreams());
 
			bytestreams->setStreamID(id);
 
			HostAddressPort addressPort = socksServer->getAddressPort();
 
			bytestreams->addStreamHost(Bytestreams::StreamHost(addressPort.getAddress().toString(), from, addressPort.getPort()));
 
			BytestreamsRequest::ref request = BytestreamsRequest::create(from, to, bytestreams, iqRouter);
 
			request->onResponse.connect(boost::bind(&MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2));
 
			request->send();
 
		}
 
		else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") {
 
			ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, from, to, bytestream, iqRouter));
 
			ibbSession->onFinished.connect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
 
			ibbSession->start();
 
#if !HAVE_SWIFTEN_3
 
			onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
 
#endif
 
		}
 
	}
 
}
 

	
 
void MyOutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) {
 
	if (error) {
 
		finish(FileTransferError());
 
		return;
 
	}
 
#if !HAVE_SWIFTEN_3
 
	SOCKS5BytestreamServerSession *serverSession = registry->getConnectedSession(SOCKS5BytestreamRegistry::getHostname(id, from, to));
 
// 	serverSession->onBytesSent.connect(boost::bind(boost::ref(onProcessedBytes), _1));
 
// 	serverSession->onFinished.connect(boost::bind(&OutgoingJingleFileTransfer::handleTransferFinished, this, _1));
 
	serverSession->startTransfer();
 
	onStateChange(FileTransfer::State(FileTransfer::State::Transferring));
 
#endif	
 
	//socksServer->onTransferFinished.connect();
 
}
 

	
 
void MyOutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) {
 
	if (ibbSession) {
 
		ibbSession->onFinished.disconnect(boost::bind(&MyOutgoingSIFileTransfer::handleIBBSessionFinished, this, _1));
 
		ibbSession.reset();
 
	}
 
#if !HAVE_SWIFTEN_3
 
	socksServer->removeReadBytestream(id, from, to);
 
	if(error) {
 
		onStateChange(FileTransfer::State(FileTransfer::State::Canceled));
 
	}
 
	else {
 
		onStateChange(FileTransfer::State(FileTransfer::State::Finished));
 
	}
 
#endif
 
	onFinished(error);
 
}
 

	
 
void MyOutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) {
 
	finish(error);
 
}
 

	
 
}
include/Swiften/FileTransfer/MyOutgoingSIFileTransfer.h
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#pragma once
 
 
#include <boost/shared_ptr.hpp>
 
 
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 
#include <Swiften/FileTransfer/ReadBytestream.h>
 
#include <Swiften/Base/boost_bsignals.h>
 
#include <Swiften/FileTransfer/FileTransferError.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/JID/JID.h>
 
#include <Swiften/Elements/StreamInitiation.h>
 
#include <Swiften/Elements/Bytestreams.h>
 
#include <Swiften/Elements/ErrorPayload.h>
 
#include <Swiften/FileTransfer/IBBSendSession.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Swift {
 
	class IQRouter;
 
	class SOCKS5BytestreamServer;
 
	class SOCKS5BytestreamRegistry;
 
 
	class MyOutgoingSIFileTransfer : public OutgoingFileTransfer {
 
		public:
 
			MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry);
 
 
			virtual void start();
 
			virtual void stop();
 
			virtual void cancel();
 
 
			boost::signal<void (const boost::optional<FileTransferError>&)> onFinished;
 
 
		private:
 
			void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref);
 
			void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref);
 
			void finish(boost::optional<FileTransferError> error);
 
			void handleIBBSessionFinished(boost::optional<FileTransferError> error);
 
 
		private:
 
			std::string id;
 
			JID from;
 
			JID to;
 
			std::string name;
 
			int size;
 
			std::string description;
 
			boost::shared_ptr<ReadBytestream> bytestream;
 
			IQRouter* iqRouter;
 
			SOCKS5BytestreamServer* socksServer;
 
			boost::shared_ptr<IBBSendSession> ibbSession;
 
			SOCKS5BytestreamRegistry *registry;
 
	};
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include <boost/shared_ptr.hpp>
 

	
 
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
 
#include <Swiften/FileTransfer/ReadBytestream.h>
 
#include <Swiften/Base/boost_bsignals.h>
 
#include <Swiften/FileTransfer/FileTransferError.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#include <Swiften/JID/JID.h>
 
#include <Swiften/Elements/StreamInitiation.h>
 
#include <Swiften/Elements/Bytestreams.h>
 
#include <Swiften/Elements/ErrorPayload.h>
 
#include <Swiften/FileTransfer/IBBSendSession.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Swift {
 
	class IQRouter;
 
	class SOCKS5BytestreamServer;
 
	class SOCKS5BytestreamRegistry;
 

	
 
	class MyOutgoingSIFileTransfer : public OutgoingFileTransfer {
 
		public:
 
			MyOutgoingSIFileTransfer(const std::string& id, const JID& from, const JID& to, const std::string& name, int size, const std::string& description, boost::shared_ptr<ReadBytestream> bytestream, IQRouter* iqRouter, SOCKS5BytestreamServer* socksServer, SOCKS5BytestreamRegistry* registry);
 

	
 
			virtual void start();
 
			virtual void stop();
 
			virtual void cancel();
 

	
 
			boost::signal<void (const boost::optional<FileTransferError>&)> onFinished;
 

	
 
		private:
 
			void handleStreamInitiationRequestResponse(StreamInitiation::ref, ErrorPayload::ref);
 
			void handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref);
 
			void finish(boost::optional<FileTransferError> error);
 
			void handleIBBSessionFinished(boost::optional<FileTransferError> error);
 

	
 
		private:
 
			std::string id;
 
			JID from;
 
			JID to;
 
			std::string name;
 
			int size;
 
			std::string description;
 
			boost::shared_ptr<ReadBytestream> bytestream;
 
			IQRouter* iqRouter;
 
			SOCKS5BytestreamServer* socksServer;
 
			boost::shared_ptr<IBBSendSession> ibbSession;
 
			SOCKS5BytestreamRegistry *registry;
 
	};
 
}
include/Swiften/Network/DummyConnectionServer.cpp
Show inline comments
 
@@ -25,7 +25,7 @@ void DummyConnectionServer::stop() {
 
	
 
}
 

	
 
void DummyConnectionServer::acceptConnection(boost::shared_ptr<Connection> connection) {
 
void DummyConnectionServer::acceptConnection(boost::shared_ptr<Swift::Connection> connection) {
 
		eventLoop->postEvent(
 
				boost::bind(boost::ref(onNewConnection), connection), 
 
				shared_from_this());
include/Swiften/Network/DummyConnectionServer.h
Show inline comments
 
@@ -30,7 +30,11 @@ namespace Swift {
 
				return ref(new DummyConnectionServer(eventLoop));
 
			}
 

	
 
			void acceptConnection(boost::shared_ptr<Connection> connection);
 
			void acceptConnection(boost::shared_ptr<Swift::Connection> connection);
 

	
 
			virtual boost::optional<Swift::ConnectionServer::Error> tryStart() {
 
				return boost::optional<Swift::ConnectionServer::Error>();
 
			}
 

	
 
			virtual void start();
 
			virtual void stop();
include/Swiften/Network/DummyNetworkFactories.cpp
Show inline comments
 
@@ -9,6 +9,12 @@
 
#include <Swiften/Network/DummyConnectionFactory.h>
 
#include <Swiften/Network/PlatformDomainNameResolver.h>
 
#include <Swiften/Network/DummyConnectionServerFactory.h>
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Crypto/CryptoProvider.h>
 
#include <Swiften/Crypto/PlatformCryptoProvider.h>
 
#include <Swiften/Network/NetworkEnvironment.h>
 
#include <Swiften/Network/PlatformNetworkEnvironment.h>
 
#endif
 

	
 
namespace Swift {
 

	
 
@@ -18,6 +24,8 @@ DummyNetworkFactories::DummyNetworkFactories(EventLoop* eventLoop) {
 
#if HAVE_SWIFTEN_3
 
	idnConverter = boost::shared_ptr<IDNConverter>(PlatformIDNConverter::create());
 
	domainNameResolver = new PlatformDomainNameResolver(idnConverter.get(), eventLoop);
 
	cryptoProvider = PlatformCryptoProvider::create();
 
	networkEnvironment = new PlatformNetworkEnvironment();
 
#else
 
	domainNameResolver = new PlatformDomainNameResolver(eventLoop);
 
#endif
include/Swiften/Network/DummyNetworkFactories.h
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#pragma once
 
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
#include <Swiften/Network/NetworkFactories.h>
 
#include <Swiften/Parser/PlatformXMLParserFactory.h>
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/IDN/IDNConverter.h>
 
#include <Swiften/IDN/PlatformIDNConverter.h>
 
#endif
 
 
namespace Swift {
 
	class EventLoop;
 
 
	class DummyNetworkFactories : public NetworkFactories {
 
		public:
 
			DummyNetworkFactories(EventLoop *eventLoop);
 
			~DummyNetworkFactories();
 
 
			virtual TimerFactory* getTimerFactory() const {
 
				return timerFactory;
 
			}
 
 
			virtual ConnectionFactory* getConnectionFactory() const {
 
				return connectionFactory;
 
			}
 
 
#if HAVE_SWIFTEN_3
 
			IDNConverter* getIDNConverter() const {
 
				return idnConverter.get();
 
			}
 
#endif
 
 
			DomainNameResolver* getDomainNameResolver() const {
 
				return domainNameResolver;
 
			}
 
 
			ConnectionServerFactory* getConnectionServerFactory() const {
 
				return connectionServerFactory;
 
			}
 
 
			virtual Swift::NATTraverser* getNATTraverser() const {
 
				return 0;
 
			}
 
 
			Swift::XMLParserFactory* getXMLParserFactory() const {
 
				return m_platformXMLParserFactory;
 
			}
 
 
			EventLoop *getEventLoop() const {
 
				return eventLoop;
 
			}
 
 
            Swift::TLSContextFactory* getTLSContextFactory() const {
 
                return 0;
 
            }
 
 
            Swift::ProxyProvider* getProxyProvider() const {
 
                return 0;
 
            }
 
 
		private:
 
			PlatformXMLParserFactory *m_platformXMLParserFactory;
 
			TimerFactory* timerFactory;
 
			ConnectionFactory* connectionFactory;
 
#if HAVE_SWIFTEN_3
 
			boost::shared_ptr<IDNConverter> idnConverter;
 
#endif
 
			DomainNameResolver* domainNameResolver;
 
			ConnectionServerFactory* connectionServerFactory;
 
			EventLoop *eventLoop;
 
	};
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
#include <Swiften/Network/NetworkFactories.h>
 
#include <Swiften/Parser/PlatformXMLParserFactory.h>
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/IDN/IDNConverter.h>
 
#include <Swiften/IDN/PlatformIDNConverter.h>
 
#endif
 

	
 
namespace Swift {
 
	class EventLoop;
 

	
 
	class DummyNetworkFactories : public NetworkFactories {
 
		public:
 
			DummyNetworkFactories(EventLoop *eventLoop);
 
			~DummyNetworkFactories();
 

	
 
			virtual TimerFactory* getTimerFactory() const {
 
				return timerFactory;
 
			}
 

	
 
			virtual ConnectionFactory* getConnectionFactory() const {
 
				return connectionFactory;
 
			}
 

	
 
#if HAVE_SWIFTEN_3
 
			IDNConverter* getIDNConverter() const {
 
				return idnConverter.get();
 
			}
 
			Swift::CryptoProvider* getCryptoProvider() const {
 
		        	return cryptoProvider;
 
			}
 
			Swift::NetworkEnvironment* getNetworkEnvironment() const {
 
				return networkEnvironment;
 
			}
 

	
 
#endif
 

	
 
			DomainNameResolver* getDomainNameResolver() const {
 
				return domainNameResolver;
 
			}
 

	
 
			ConnectionServerFactory* getConnectionServerFactory() const {
 
				return connectionServerFactory;
 
			}
 

	
 
			virtual Swift::NATTraverser* getNATTraverser() const {
 
				return 0;
 
			}
 

	
 
			Swift::XMLParserFactory* getXMLParserFactory() const {
 
				return m_platformXMLParserFactory;
 
			}
 

	
 
			EventLoop *getEventLoop() const {
 
				return eventLoop;
 
			}
 

	
 
            Swift::TLSContextFactory* getTLSContextFactory() const {
 
                return 0;
 
            }
 

	
 
            Swift::ProxyProvider* getProxyProvider() const {
 
                return 0;
 
            }
 
		private:
 
			PlatformXMLParserFactory *m_platformXMLParserFactory;
 
			TimerFactory* timerFactory;
 
			ConnectionFactory* connectionFactory;
 
#if HAVE_SWIFTEN_3
 
			boost::shared_ptr<IDNConverter> idnConverter;
 
			CryptoProvider* cryptoProvider;
 
			NetworkEnvironment* networkEnvironment;
 
#endif
 
			DomainNameResolver* domainNameResolver;
 
			ConnectionServerFactory* connectionServerFactory;
 
			EventLoop *eventLoop;
 
	};
 
}
include/Swiften/Server/ServerFromClientSession.cpp
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#include <Swiften/Server/ServerFromClientSession.h>
 
 
#include <boost/bind.hpp>
 
 
#include <Swiften/Elements/ProtocolHeader.h>
 
#include <Swiften/Elements/StreamError.h>
 
#include <Swiften/Elements/Message.h>
 
#include <Swiften/Server/UserRegistry.h>
 
#include <Swiften/Network/Connection.h>
 
#include <Swiften/StreamStack/XMPPLayer.h>
 
#include <Swiften/Elements/StreamFeatures.h>
 
#include <Swiften/Elements/ResourceBind.h>
 
#include <Swiften/Elements/StartSession.h>
 
#include <Swiften/Elements/IQ.h>
 
#include <Swiften/Elements/AuthSuccess.h>
 
#include <Swiften/Elements/AuthFailure.h>
 
#include <Swiften/Elements/AuthRequest.h>
 
#include <Swiften/SASL/PLAINMessage.h>
 
#include <Swiften/StreamStack/StreamStack.h>
 
#include <Swiften/StreamStack/TLSServerLayer.h>
 
#include <Swiften/Elements/StartTLSRequest.h>
 
#include <Swiften/Elements/TLSProceed.h>
 
#include <iostream>
 
#include <Swiften/TLS/CertificateWithKey.h>
 
 
namespace Swift {
 
 
ServerFromClientSession::ServerFromClientSession(
 
		const std::string& id,
 
		boost::shared_ptr<Connection> connection, 
 
		PayloadParserFactoryCollection* payloadParserFactories, 
 
		PayloadSerializerCollection* payloadSerializers,
 
		UserRegistry* userRegistry,
 
		XMLParserFactory* factory,
 
		Swift::JID remoteJID) : 
 
			Session(connection, payloadParserFactories, payloadSerializers, factory),
 
			id_(id),
 
			userRegistry_(userRegistry),
 
			authenticated_(false),
 
			initialized(false),
 
			allowSASLEXTERNAL(false),
 
			tlsLayer(0),
 
			tlsConnected(false) {
 
				setRemoteJID(remoteJID);
 
}
 
 
ServerFromClientSession::~ServerFromClientSession() {
 
	if (tlsLayer) {
 
		delete tlsLayer;
 
	}
 
}
 
 
void ServerFromClientSession::handlePasswordValid() {
 
	if (!isInitialized()) {
 
		getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
 
		authenticated_ = true;
 
		getXMPPLayer()->resetParser();
 
	}
 
}
 
 
void ServerFromClientSession::handlePasswordInvalid(const std::string &error) {
 
	if (!isInitialized()) {
 
		getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
 
		if (!error.empty()) {
 
			boost::shared_ptr<StreamError> msg(new StreamError(StreamError::UndefinedCondition, error));
 
			getXMPPLayer()->writeElement(msg);
 
		}
 
		
 
		finishSession(AuthenticationFailedError);
 
	}
 
}
 
 
void ServerFromClientSession::handleElement(boost::shared_ptr<ToplevelElement> element) {
 
	if (isInitialized()) {
 
		onElementReceived(element);
 
	}
 
	else {
 
		if (AuthRequest* authRequest = dynamic_cast<AuthRequest*>(element.get())) {
 
			if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) {
 
				if (authRequest->getMechanism() == "EXTERNAL") {
 
						getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
 
						authenticated_ = true;
 
						getXMPPLayer()->resetParser();
 
				}
 
				else {
 
					PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray(""));
 
					user_ = plainMessage.getAuthenticationID();
 
					userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), this, plainMessage.getPassword());
 
				}
 
			}
 
			else {
 
				getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
 
				finishSession(NoSupportedAuthMechanismsError);
 
			}
 
		}
 
		else if (dynamic_cast<StartTLSRequest*>(element.get()) != NULL) {
 
			getXMPPLayer()->writeElement(boost::shared_ptr<TLSProceed>(new TLSProceed));
 
			getStreamStack()->addLayer(tlsLayer);
 
			tlsLayer->connect();
 
			getXMPPLayer()->resetParser();
 
		}
 
		else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
 
			if (boost::shared_ptr<ResourceBind> resourceBind = iq->getPayload<ResourceBind>()) {
 
				std::string bucket = "abcdefghijklmnopqrstuvwxyz";
 
				std::string uuid;
 
				for (int i = 0; i < 10; i++) {
 
					uuid += bucket[rand() % bucket.size()];
 
				}
 
				setRemoteJID(JID(user_, getLocalJID().getDomain(), uuid));
 
				boost::shared_ptr<ResourceBind> resultResourceBind(new ResourceBind());
 
				resultResourceBind->setJID(getRemoteJID());
 
				getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind));
 
			}
 
			else if (iq->getPayload<StartSession>()) {
 
				getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID()));
 
				setInitialized();
 
			}
 
		}
 
	}
 
}
 
 
void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) {
 
	setLocalJID(JID("", incomingHeader.getTo()));
 
	ProtocolHeader header;
 
	header.setFrom(incomingHeader.getTo());
 
	header.setID(id_);
 
	getXMPPLayer()->writeHeader(header);
 
 
	boost::shared_ptr<StreamFeatures> features(new StreamFeatures());
 
 
	if (!authenticated_) {
 
		if (tlsLayer && !tlsConnected) {
 
			features->setHasStartTLS();
 
		}
 
		features->addAuthenticationMechanism("PLAIN");
 
		if (allowSASLEXTERNAL) {
 
			features->addAuthenticationMechanism("EXTERNAL");
 
		}
 
	}
 
	else {
 
		features->setHasResourceBind();
 
		features->setHasSession();
 
	}
 
	getXMPPLayer()->writeElement(features);
 
}
 
 
void ServerFromClientSession::setInitialized() {
 
	initialized = true;
 
	onSessionStarted();
 
}
 
 
void ServerFromClientSession::setAllowSASLEXTERNAL() {
 
	allowSASLEXTERNAL = true;
 
}
 
 
void ServerFromClientSession::handleSessionFinished(const boost::optional<SessionError>&) {
 
	userRegistry_->stopLogin(JID(user_, getLocalJID().getDomain()), this);
 
}
 
 
void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) {
 
	tlsLayer = new TLSServerLayer(tlsContextFactory);
 
	if (!tlsLayer->setServerCertificate(cert)) {
 
// 		std::cout << "error\n";
 
		// TODO:
 
// 		onClosed(boost::shared_ptr<Error>(new Error(Error::InvalidTLSCertificateError)));
 
	}
 
	else {
 
		tlsLayer->onError.connect(boost::bind(&ServerFromClientSession::handleTLSError, this));
 
		tlsLayer->onConnected.connect(boost::bind(&ServerFromClientSession::handleTLSConnected, this));
 
// 		getStreamStack()->addLayer(tlsLayer);
 
// 		tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this));
 
// 		tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, this));
 
// 		tlsLayer->connect();
 
	}
 
}
 
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#include <Swiften/Server/ServerFromClientSession.h>
 

	
 
#include <boost/bind.hpp>
 

	
 
#include <Swiften/Elements/ProtocolHeader.h>
 
#include <Swiften/Elements/StreamError.h>
 
#include <Swiften/Elements/Message.h>
 
#include <Swiften/Server/UserRegistry.h>
 
#include <Swiften/Network/Connection.h>
 
#include <Swiften/StreamStack/XMPPLayer.h>
 
#include <Swiften/Elements/StreamFeatures.h>
 
#include <Swiften/Elements/ResourceBind.h>
 
#include <Swiften/Elements/StartSession.h>
 
#include <Swiften/Elements/IQ.h>
 
#include <Swiften/Elements/AuthSuccess.h>
 
#include <Swiften/Elements/AuthFailure.h>
 
#include <Swiften/Elements/AuthRequest.h>
 
#include <Swiften/SASL/PLAINMessage.h>
 
#include <Swiften/StreamStack/StreamStack.h>
 
#include <Swiften/StreamStack/TLSServerLayer.h>
 
#include <Swiften/Elements/StartTLSRequest.h>
 
#include <Swiften/Elements/TLSProceed.h>
 
#include <iostream>
 
#include <Swiften/TLS/CertificateWithKey.h>
 

	
 
namespace Swift {
 

	
 
ServerFromClientSession::ServerFromClientSession(
 
		const std::string& id,
 
		boost::shared_ptr<Connection> connection, 
 
		PayloadParserFactoryCollection* payloadParserFactories, 
 
		PayloadSerializerCollection* payloadSerializers,
 
		UserRegistry* userRegistry,
 
		XMLParserFactory* factory,
 
		Swift::JID remoteJID) : 
 
			Session(connection, payloadParserFactories, payloadSerializers, factory),
 
			id_(id),
 
			userRegistry_(userRegistry),
 
			authenticated_(false),
 
			initialized(false),
 
			allowSASLEXTERNAL(false),
 
			tlsLayer(0),
 
			tlsConnected(false) {
 
				setRemoteJID(remoteJID);
 
}
 

	
 
ServerFromClientSession::~ServerFromClientSession() {
 
	if (tlsLayer) {
 
		delete tlsLayer;
 
	}
 
}
 

	
 
void ServerFromClientSession::handlePasswordValid() {
 
	if (!isInitialized()) {
 
		getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
 
		authenticated_ = true;
 
		getXMPPLayer()->resetParser();
 
	}
 
}
 

	
 
void ServerFromClientSession::handlePasswordInvalid(const std::string &error) {
 
	if (!isInitialized()) {
 
		getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
 
		if (!error.empty()) {
 
			boost::shared_ptr<StreamError> msg(new StreamError(StreamError::UndefinedCondition, error));
 
			getXMPPLayer()->writeElement(msg);
 
		}
 
		
 
		finishSession(AuthenticationFailedError);
 
	}
 
}
 

	
 
void ServerFromClientSession::handleElement(boost::shared_ptr<ToplevelElement> element) {
 
	if (isInitialized()) {
 
		onElementReceived(element);
 
	}
 
	else {
 
		if (AuthRequest* authRequest = dynamic_cast<AuthRequest*>(element.get())) {
 
			if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) {
 
				if (authRequest->getMechanism() == "EXTERNAL") {
 
						getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
 
						authenticated_ = true;
 
						getXMPPLayer()->resetParser();
 
				}
 
				else {
 
					PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray(""));
 
					user_ = plainMessage.getAuthenticationID();
 
					userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), this, plainMessage.getPassword());
 
				}
 
			}
 
			else {
 
				getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
 
				finishSession(NoSupportedAuthMechanismsError);
 
			}
 
		}
 
		else if (dynamic_cast<StartTLSRequest*>(element.get()) != NULL) {
 
			getXMPPLayer()->writeElement(boost::shared_ptr<TLSProceed>(new TLSProceed));
 
			getStreamStack()->addLayer(tlsLayer);
 
			tlsLayer->connect();
 
			getXMPPLayer()->resetParser();
 
		}
 
		else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
 
			if (boost::shared_ptr<ResourceBind> resourceBind = iq->getPayload<ResourceBind>()) {
 
				std::string bucket = "abcdefghijklmnopqrstuvwxyz";
 
				std::string uuid;
 
				for (int i = 0; i < 10; i++) {
 
					uuid += bucket[rand() % bucket.size()];
 
				}
 
				setRemoteJID(JID(user_, getLocalJID().getDomain(), uuid));
 
				boost::shared_ptr<ResourceBind> resultResourceBind(new ResourceBind());
 
				resultResourceBind->setJID(getRemoteJID());
 
				getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind));
 
			}
 
			else if (iq->getPayload<StartSession>()) {
 
				getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID()));
 
				setInitialized();
 
			}
 
		}
 
	}
 
}
 

	
 
void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) {
 
	setLocalJID(JID("", incomingHeader.getTo()));
 
	ProtocolHeader header;
 
	header.setFrom(incomingHeader.getTo());
 
	header.setID(id_);
 
	getXMPPLayer()->writeHeader(header);
 

	
 
	boost::shared_ptr<StreamFeatures> features(new StreamFeatures());
 

	
 
	if (!authenticated_) {
 
		if (tlsLayer && !tlsConnected) {
 
			features->setHasStartTLS();
 
		}
 
		features->addAuthenticationMechanism("PLAIN");
 
		if (allowSASLEXTERNAL) {
 
			features->addAuthenticationMechanism("EXTERNAL");
 
		}
 
	}
 
	else {
 
		features->setHasResourceBind();
 
		features->setHasSession();
 
	}
 
	getXMPPLayer()->writeElement(features);
 
}
 

	
 
void ServerFromClientSession::setInitialized() {
 
	initialized = true;
 
	onSessionStarted();
 
}
 

	
 
void ServerFromClientSession::setAllowSASLEXTERNAL() {
 
	allowSASLEXTERNAL = true;
 
}
 

	
 
void ServerFromClientSession::handleSessionFinished(const boost::optional<SessionError>&) {
 
	userRegistry_->stopLogin(JID(user_, getLocalJID().getDomain()), this);
 
}
 

	
 
void ServerFromClientSession::addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert) {
 
	tlsLayer = new TLSServerLayer(tlsContextFactory);
 
	if (!tlsLayer->setServerCertificate(cert)) {
 
// 		std::cout << "error\n";
 
		// TODO:
 
// 		onClosed(boost::shared_ptr<Error>(new Error(Error::InvalidTLSCertificateError)));
 
	}
 
	else {
 
		tlsLayer->onError.connect(boost::bind(&ServerFromClientSession::handleTLSError, this));
 
		tlsLayer->onConnected.connect(boost::bind(&ServerFromClientSession::handleTLSConnected, this));
 
// 		getStreamStack()->addLayer(tlsLayer);
 
// 		tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, this));
 
// 		tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, this));
 
// 		tlsLayer->connect();
 
	}
 
}
 

	
 
}
include/Swiften/Server/ServerFromClientSession.h
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#pragma once
 
 
#include <boost/shared_ptr.hpp>
 
#include <Swiften/Base/boost_bsignals.h>
 
#include <boost/enable_shared_from_this.hpp>
 
 
#include <string>
 
#include <Swiften/Session/Session.h>
 
#include <Swiften/JID/JID.h>
 
#include <Swiften/Network/Connection.h>
 
#include <Swiften/Base/ByteArray.h>
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Swift {
 
	class ProtocolHeader;
 
	class Element;
 
	class Stanza;
 
	class PayloadParserFactoryCollection;
 
	class PayloadSerializerCollection;
 
	class StreamStack;
 
	class UserRegistry;
 
	class XMPPLayer;
 
	class ConnectionLayer;
 
	class Connection;
 
	class TLSServerLayer;
 
	class TLSServerContextFactory;
 
	class PKCS12Certificate;
 
 
	class ServerFromClientSession : public Session {
 
		public:
 
			ServerFromClientSession(
 
					const std::string& id,
 
					boost::shared_ptr<Connection> connection, 
 
					PayloadParserFactoryCollection* payloadParserFactories, 
 
					PayloadSerializerCollection* payloadSerializers,
 
					UserRegistry* userRegistry,
 
					XMLParserFactory* factory,
 
					Swift::JID remoteJID = Swift::JID());
 
			~ServerFromClientSession();
 
 
			boost::signal<void ()> onSessionStarted;
 
			void setAllowSASLEXTERNAL();
 
			const std::string &getUser() {
 
				return user_;
 
			}
 
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert);
 
 
			Swift::JID getBareJID() {
 
				return Swift::JID(user_, getLocalJID().getDomain());
 
			}
 
 
			void handlePasswordValid();
 
			void handlePasswordInvalid(const std::string &error = "");
 
 
		private:
 
#if HAVE_SWIFTEN_3
 
			void handleElement(boost::shared_ptr<ToplevelElement>);
 
#else		
 
			void handleElement(boost::shared_ptr<Element>);
 
#endif
 
			void handleStreamStart(const ProtocolHeader& header);
 
			void handleSessionFinished(const boost::optional<SessionError>&);
 
 
			void setInitialized();
 
			bool isInitialized() const { 
 
				return initialized; 
 
			}
 
 
			void handleTLSError() { }
 
			void handleTLSConnected() { tlsConnected = true; }
 
 
		private:
 
			std::string id_;
 
			UserRegistry* userRegistry_;
 
			bool authenticated_;
 
			bool initialized;
 
			bool allowSASLEXTERNAL;
 
			std::string user_;
 
			TLSServerLayer* tlsLayer;
 
			bool tlsConnected;
 
	};
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include <boost/shared_ptr.hpp>
 
#include <Swiften/Base/boost_bsignals.h>
 
#include <boost/enable_shared_from_this.hpp>
 

	
 
#include <string>
 
#include <Swiften/Session/Session.h>
 
#include <Swiften/JID/JID.h>
 
#include <Swiften/Network/Connection.h>
 
#include <Swiften/Base/ByteArray.h>
 
#include <Swiften/TLS/CertificateWithKey.h>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Swift {
 
	class ProtocolHeader;
 
	class Element;
 
	class Stanza;
 
	class PayloadParserFactoryCollection;
 
	class PayloadSerializerCollection;
 
	class StreamStack;
 
	class UserRegistry;
 
	class XMPPLayer;
 
	class ConnectionLayer;
 
	class Connection;
 
	class TLSServerLayer;
 
	class TLSServerContextFactory;
 
	class PKCS12Certificate;
 

	
 
	class ServerFromClientSession : public Session {
 
		public:
 
			ServerFromClientSession(
 
					const std::string& id,
 
					boost::shared_ptr<Connection> connection, 
 
					PayloadParserFactoryCollection* payloadParserFactories, 
 
					PayloadSerializerCollection* payloadSerializers,
 
					UserRegistry* userRegistry,
 
					XMLParserFactory* factory,
 
					Swift::JID remoteJID = Swift::JID());
 
			~ServerFromClientSession();
 

	
 
			boost::signal<void ()> onSessionStarted;
 
			void setAllowSASLEXTERNAL();
 
			const std::string &getUser() {
 
				return user_;
 
			}
 

	
 
			void addTLSEncryption(TLSServerContextFactory* tlsContextFactory, CertificateWithKey::ref cert);
 

	
 
			Swift::JID getBareJID() {
 
				return Swift::JID(user_, getLocalJID().getDomain());
 
			}
 

	
 
			void handlePasswordValid();
 
			void handlePasswordInvalid(const std::string &error = "");
 

	
 
		private:
 
#if HAVE_SWIFTEN_3
 
			void handleElement(boost::shared_ptr<ToplevelElement>);
 
#else		
 
			void handleElement(boost::shared_ptr<Element>);
 
#endif
 
			void handleStreamStart(const ProtocolHeader& header);
 
			void handleSessionFinished(const boost::optional<SessionError>&);
 

	
 
			void setInitialized();
 
			bool isInitialized() const { 
 
				return initialized; 
 
			}
 

	
 
			void handleTLSError() { }
 
			void handleTLSConnected() { tlsConnected = true; }
 

	
 
		private:
 
			std::string id_;
 
			UserRegistry* userRegistry_;
 
			bool authenticated_;
 
			bool initialized;
 
			bool allowSASLEXTERNAL;
 
			std::string user_;
 
			TLSServerLayer* tlsLayer;
 
			bool tlsConnected;
 
	};
 
}
include/Swiften/Server/ServerStanzaChannel.cpp
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Base/Error.h"
 
#include <iostream>
 
 
#include <boost/bind.hpp>
 
 
namespace Swift {
 
 
namespace {
 
// 	struct PriorityLessThan {
 
// 		bool operator()(const ServerSession* s1, const ServerSession* s2) const {
 
// 			return s1->getPriority() < s2->getPriority();
 
// 		}
 
// 	};
 
 
	struct HasJID {
 
		HasJID(const JID& jid) : jid(jid) {}
 
		bool operator()(const boost::shared_ptr<ServerFromClientSession> session) const {
 
			return session->getRemoteJID().equals(jid, JID::WithResource);
 
		}
 
		JID jid;
 
	};
 
}
 
 
void ServerStanzaChannel::addSession(boost::shared_ptr<ServerFromClientSession> session) {
 
	sessions[session->getRemoteJID().toBare().toString()].push_back(session);
 
	session->onSessionFinished.connect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session));
 
	session->onElementReceived.connect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session));
 
	session->onDataRead.connect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session));
 
}
 
 
void ServerStanzaChannel::removeSession(boost::shared_ptr<ServerFromClientSession> session) {
 
	session->onSessionFinished.disconnect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session));
 
	session->onElementReceived.disconnect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session));
 
	session->onDataRead.disconnect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session));
 
	std::list<boost::shared_ptr<ServerFromClientSession> > &lst = sessions[session->getRemoteJID().toBare().toString()];
 
	lst.erase(std::remove(lst.begin(), lst.end(), session), lst.end());
 
}
 
 
void ServerStanzaChannel::sendIQ(boost::shared_ptr<IQ> iq) {
 
	send(iq);
 
}
 
 
void ServerStanzaChannel::sendMessage(boost::shared_ptr<Message> message) {
 
	send(message);
 
}
 
 
void ServerStanzaChannel::sendPresence(boost::shared_ptr<Presence> presence) {
 
	send(presence);
 
}
 
 
void ServerStanzaChannel::handleDataRead(const SafeByteArray &data, const boost::shared_ptr<ServerFromClientSession> &session) {
 
	if (safeByteArrayToString(data).find("</stream:stream>") != std::string::npos) {
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		presence->setFrom(session->getRemoteJID());
 
		presence->setType(Swift::Presence::Unavailable);
 
		onPresenceReceived(presence);
 
	}
 
}
 
#if HAVE_SWIFTEN_3
 
void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr<ToplevelElement> element, bool last) {
 
#else
 
void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr<Element> element, bool last) {
 
#endif
 
	std::vector<boost::shared_ptr<ServerFromClientSession> > candidateSessions;
 
	for (std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = sessions[to.toBare().toString()].begin(); i != sessions[to.toBare().toString()].end(); ++i) {
 
		candidateSessions.push_back(*i);
 
	}
 
 
	for (std::vector<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = candidateSessions.begin(); i != candidateSessions.end(); ++i) {
 
		removeSession(*i);
 
		if (element) {
 
			(*i)->sendElement(element);
 
		}
 
 
		if (last && (*i)->getRemoteJID().isValid()) {
 
			Swift::Presence::ref presence = Swift::Presence::create();
 
			presence->setFrom((*i)->getRemoteJID());
 
			presence->setType(Swift::Presence::Unavailable);
 
			onPresenceReceived(presence);
 
		}
 
 
		(*i)->finishSession();
 
// 		std::cout << "FINISH SESSION " << sessions[to.toBare().toString()].size() << "\n";
 
		if (last) {
 
			break;
 
		}
 
	}
 
}
 
 
std::string ServerStanzaChannel::getNewIQID() {
 
	return idGenerator.generateID();
 
}
 
 
void ServerStanzaChannel::send(boost::shared_ptr<Stanza> stanza) {
 
	JID to = stanza->getTo();
 
	assert(to.isValid());
 
 
	// For a full JID, first try to route to a session with the full JID
 
	if (!to.isBare()) {
 
		std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = std::find_if(sessions[stanza->getTo().toBare().toString()].begin(), sessions[stanza->getTo().toBare().toString()].end(), HasJID(to));
 
		if (i != sessions[stanza->getTo().toBare().toString()].end()) {
 
			(*i)->sendElement(stanza);
 
			return;
 
		}
 
	}
 
 
	// Look for candidate sessions
 
	to = to.toBare();
 
	std::vector<boost::shared_ptr<ServerFromClientSession> > candidateSessions;
 
	for (std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = sessions[stanza->getTo().toBare().toString()].begin(); i != sessions[stanza->getTo().toBare().toString()].end(); ++i) {
 
		if ((*i)->getRemoteJID().equals(to, JID::WithoutResource)) {
 
			candidateSessions.push_back(*i);
 
			(*i)->sendElement(stanza);
 
		}
 
	}
 
	if (candidateSessions.empty()) {
 
		return;
 
	}
 
 
	// Find the session with the highest priority
 
// 	std::vector<ServerSession*>::const_iterator i = std::max_element(sessions.begin(), sessions.end(), PriorityLessThan());
 
// 	(*i)->sendStanza(stanza);
 
	return;
 
}
 
 
void ServerStanzaChannel::handleSessionFinished(const boost::optional<Session::SessionError>&, const boost::shared_ptr<ServerFromClientSession>& session) {
 
	removeSession(session);
 
 
// 	if (!session->initiatedFinish()) {
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		presence->setFrom(session->getRemoteJID());
 
		presence->setType(Swift::Presence::Unavailable);
 
		onPresenceReceived(presence);
 
// 	}
 
}
 
 
void ServerStanzaChannel::handleElement(boost::shared_ptr<Element> element, const boost::shared_ptr<ServerFromClientSession>& session) {
 
	boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element);
 
	if (!stanza) {
 
		return;
 
	}
 
 
	stanza->setFrom(session->getRemoteJID());
 
 
	if (!stanza->getFrom().isValid())
 
		return;
 
	
 
 
	boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(stanza);
 
	if (message) {
 
		onMessageReceived(message);
 
		return;
 
	}
 
 
	boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(stanza);
 
	if (presence) {
 
		onPresenceReceived(presence);
 
		return;
 
	}
 
 
	boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(stanza);
 
	if (iq) {
 
		onIQReceived(iq);
 
		return;
 
	}
 
}
 
 
void ServerStanzaChannel::handleSessionInitialized() {
 
	onAvailableChanged(true);
 
}
 
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Base/Error.h"
 
#include <iostream>
 

	
 
#include <boost/bind.hpp>
 

	
 
namespace Swift {
 

	
 
namespace {
 
// 	struct PriorityLessThan {
 
// 		bool operator()(const ServerSession* s1, const ServerSession* s2) const {
 
// 			return s1->getPriority() < s2->getPriority();
 
// 		}
 
// 	};
 

	
 
	struct HasJID {
 
		HasJID(const JID& jid) : jid(jid) {}
 
		bool operator()(const boost::shared_ptr<ServerFromClientSession> session) const {
 
			return session->getRemoteJID().equals(jid, JID::WithResource);
 
		}
 
		JID jid;
 
	};
 
}
 

	
 
void ServerStanzaChannel::addSession(boost::shared_ptr<ServerFromClientSession> session) {
 
	sessions[session->getRemoteJID().toBare().toString()].push_back(session);
 
	session->onSessionFinished.connect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session));
 
	session->onElementReceived.connect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session));
 
	session->onDataRead.connect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session));
 
}
 

	
 
void ServerStanzaChannel::removeSession(boost::shared_ptr<ServerFromClientSession> session) {
 
	session->onSessionFinished.disconnect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session));
 
	session->onElementReceived.disconnect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session));
 
	session->onDataRead.disconnect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session));
 
	std::list<boost::shared_ptr<ServerFromClientSession> > &lst = sessions[session->getRemoteJID().toBare().toString()];
 
	lst.erase(std::remove(lst.begin(), lst.end(), session), lst.end());
 
}
 

	
 
void ServerStanzaChannel::sendIQ(boost::shared_ptr<IQ> iq) {
 
	send(iq);
 
}
 

	
 
void ServerStanzaChannel::sendMessage(boost::shared_ptr<Message> message) {
 
	send(message);
 
}
 

	
 
void ServerStanzaChannel::sendPresence(boost::shared_ptr<Presence> presence) {
 
	send(presence);
 
}
 

	
 
void ServerStanzaChannel::handleDataRead(const SafeByteArray &data, const boost::shared_ptr<ServerFromClientSession> &session) {
 
	if (safeByteArrayToString(data).find("</stream:stream>") != std::string::npos) {
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		presence->setFrom(session->getRemoteJID());
 
		presence->setType(Swift::Presence::Unavailable);
 
		onPresenceReceived(presence);
 
	}
 
}
 
#if HAVE_SWIFTEN_3
 
void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr<ToplevelElement> element, bool last) {
 
#else
 
void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr<Element> element, bool last) {
 
#endif
 
	std::vector<boost::shared_ptr<ServerFromClientSession> > candidateSessions;
 
	for (std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = sessions[to.toBare().toString()].begin(); i != sessions[to.toBare().toString()].end(); ++i) {
 
		candidateSessions.push_back(*i);
 
	}
 

	
 
	for (std::vector<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = candidateSessions.begin(); i != candidateSessions.end(); ++i) {
 
		removeSession(*i);
 
		if (element) {
 
			(*i)->sendElement(element);
 
		}
 

	
 
		if (last && (*i)->getRemoteJID().isValid()) {
 
			Swift::Presence::ref presence = Swift::Presence::create();
 
			presence->setFrom((*i)->getRemoteJID());
 
			presence->setType(Swift::Presence::Unavailable);
 
			onPresenceReceived(presence);
 
		}
 

	
 
		(*i)->finishSession();
 
// 		std::cout << "FINISH SESSION " << sessions[to.toBare().toString()].size() << "\n";
 
		if (last) {
 
			break;
 
		}
 
	}
 
}
 

	
 
std::string ServerStanzaChannel::getNewIQID() {
 
	return idGenerator.generateID();
 
}
 

	
 
void ServerStanzaChannel::send(boost::shared_ptr<Stanza> stanza) {
 
	JID to = stanza->getTo();
 
	assert(to.isValid());
 

	
 
	// For a full JID, first try to route to a session with the full JID
 
	if (!to.isBare()) {
 
		std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = std::find_if(sessions[stanza->getTo().toBare().toString()].begin(), sessions[stanza->getTo().toBare().toString()].end(), HasJID(to));
 
		if (i != sessions[stanza->getTo().toBare().toString()].end()) {
 
			(*i)->sendElement(stanza);
 
			return;
 
		}
 
	}
 

	
 
	// Look for candidate sessions
 
	to = to.toBare();
 
	std::vector<boost::shared_ptr<ServerFromClientSession> > candidateSessions;
 
	for (std::list<boost::shared_ptr<ServerFromClientSession> >::const_iterator i = sessions[stanza->getTo().toBare().toString()].begin(); i != sessions[stanza->getTo().toBare().toString()].end(); ++i) {
 
		if ((*i)->getRemoteJID().equals(to, JID::WithoutResource)) {
 
			candidateSessions.push_back(*i);
 
			(*i)->sendElement(stanza);
 
		}
 
	}
 
	if (candidateSessions.empty()) {
 
		return;
 
	}
 

	
 
	// Find the session with the highest priority
 
// 	std::vector<ServerSession*>::const_iterator i = std::max_element(sessions.begin(), sessions.end(), PriorityLessThan());
 
// 	(*i)->sendStanza(stanza);
 
	return;
 
}
 

	
 
void ServerStanzaChannel::handleSessionFinished(const boost::optional<Session::SessionError>&, const boost::shared_ptr<ServerFromClientSession>& session) {
 
	removeSession(session);
 

	
 
// 	if (!session->initiatedFinish()) {
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		presence->setFrom(session->getRemoteJID());
 
		presence->setType(Swift::Presence::Unavailable);
 
		onPresenceReceived(presence);
 
// 	}
 
}
 

	
 
void ServerStanzaChannel::handleElement(boost::shared_ptr<Element> element, const boost::shared_ptr<ServerFromClientSession>& session) {
 
	boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element);
 
	if (!stanza) {
 
		return;
 
	}
 

	
 
	stanza->setFrom(session->getRemoteJID());
 

	
 
	if (!stanza->getFrom().isValid())
 
		return;
 
	
 

	
 
	boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(stanza);
 
	if (message) {
 
		onMessageReceived(message);
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(stanza);
 
	if (presence) {
 
		onPresenceReceived(presence);
 
		return;
 
	}
 

	
 
	boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(stanza);
 
	if (iq) {
 
		onIQReceived(iq);
 
		return;
 
	}
 
}
 

	
 
void ServerStanzaChannel::handleSessionInitialized() {
 
	onAvailableChanged(true);
 
}
 

	
 
}
include/Swiften/Server/ServerStanzaChannel.h
Show inline comments
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 
 
#pragma once
 
 
#include <boost/shared_ptr.hpp>
 
 
#include "Swiften/Base/IDGenerator.h"
 
#include "Swiften/Server/ServerFromClientSession.h"
 
#include "Swiften/Client/StanzaChannel.h"
 
#include "Swiften/Elements/Message.h"
 
#include "Swiften/Elements/IQ.h"
 
#include "Swiften/Elements/Presence.h"
 
#include "Swiften/TLS/Certificate.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Swift {
 
	class Error;
 
	class ServerStanzaChannel : public StanzaChannel {
 
		public:
 
			void addSession(boost::shared_ptr<ServerFromClientSession> session);
 
			void removeSession(boost::shared_ptr<ServerFromClientSession> session);
 
 
			void sendIQ(boost::shared_ptr<IQ> iq);
 
			void sendMessage(boost::shared_ptr<Message> message);
 
			void sendPresence(boost::shared_ptr<Presence> presence);
 
#if HAVE_SWIFTEN_3
 
			void finishSession(const JID& to, boost::shared_ptr<ToplevelElement> element, bool last = false);
 
#else
 
			void finishSession(const JID& to, boost::shared_ptr<Element> element, bool last = false);
 
#endif
 
			bool getStreamManagementEnabled() const {
 
				return false;
 
			}
 
	
 
			bool isAvailable() const {
 
				return true;
 
			}
 
			
 
			std::vector<Certificate::ref> getPeerCertificateChain() const {
 
				return std::vector<Certificate::ref>();
 
			}
 
 
		private:
 
			std::string getNewIQID();
 
			void send(boost::shared_ptr<Stanza> stanza);
 
			void handleSessionFinished(const boost::optional<Session::SessionError>&, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleElement(boost::shared_ptr<Element> element, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleDataRead(const SafeByteArray &data, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleSessionInitialized();
 
 
		private:
 
			IDGenerator idGenerator;
 
			// [JID][resources][ServerFromClientSession]
 
			std::map<std::string, std::list<boost::shared_ptr<ServerFromClientSession> > > sessions;
 
	};
 
 
}
 
/*
 
 * Copyright (c) 2010 Remko Tronçon
 
 * Licensed under the GNU General Public License v3.
 
 * See Documentation/Licenses/GPLv3.txt for more information.
 
 */
 

	
 
#pragma once
 

	
 
#include <boost/shared_ptr.hpp>
 

	
 
#include "Swiften/Base/IDGenerator.h"
 
#include "Swiften/Server/ServerFromClientSession.h"
 
#include "Swiften/Client/StanzaChannel.h"
 
#include "Swiften/Elements/Message.h"
 
#include "Swiften/Elements/IQ.h"
 
#include "Swiften/Elements/Presence.h"
 
#include "Swiften/TLS/Certificate.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Swift {
 
	class Error;
 
	class ServerStanzaChannel : public StanzaChannel {
 
		public:
 
			void addSession(boost::shared_ptr<ServerFromClientSession> session);
 
			void removeSession(boost::shared_ptr<ServerFromClientSession> session);
 

	
 
			void sendIQ(boost::shared_ptr<IQ> iq);
 
			void sendMessage(boost::shared_ptr<Message> message);
 
			void sendPresence(boost::shared_ptr<Presence> presence);
 
#if HAVE_SWIFTEN_3
 
			void finishSession(const JID& to, boost::shared_ptr<ToplevelElement> element, bool last = false);
 
#else
 
			void finishSession(const JID& to, boost::shared_ptr<Element> element, bool last = false);
 
#endif
 
			bool getStreamManagementEnabled() const {
 
				return false;
 
			}
 
	
 
			bool isAvailable() const {
 
				return true;
 
			}
 
			
 
			std::vector<Certificate::ref> getPeerCertificateChain() const {
 
				return std::vector<Certificate::ref>();
 
			}
 

	
 
		private:
 
			std::string getNewIQID();
 
			void send(boost::shared_ptr<Stanza> stanza);
 
			void handleSessionFinished(const boost::optional<Session::SessionError>&, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleElement(boost::shared_ptr<Element> element, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleDataRead(const SafeByteArray &data, const boost::shared_ptr<ServerFromClientSession> &session);
 
			void handleSessionInitialized();
 

	
 
		private:
 
			IDGenerator idGenerator;
 
			// [JID][resources][ServerFromClientSession]
 
			std::map<std::string, std::list<boost::shared_ptr<ServerFromClientSession> > > sessions;
 
	};
 

	
 
}
include/transport/filetransfermanager.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#pragma once
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#include <Swiften/Elements/StreamInitiationFileInfo.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/ConnectivityManager.h>
 
#endif
 
#include <Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h>
 
#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
 
#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
 
#else
 
#include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
 
#endif
 
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
 
#endif
 
 
namespace Transport {
 
 
class UserManager;
 
class User;
 
class Component;
 
class Buddy;
 
 
class FileTransferManager {
 
	public:
 
		typedef struct Transfer {
 
			boost::shared_ptr<Swift::OutgoingFileTransfer> ft;
 
			Swift::JID from;
 
			Swift::JID to;
 
			boost::shared_ptr<Swift::ReadBytestream> readByteStream;
 
		} Transfer;
 
 
		FileTransferManager(Component *component, UserManager *userManager);
 
		virtual ~FileTransferManager();
 
		
 
		FileTransferManager::Transfer sendFile(User *user, Buddy *buddy, boost::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info);
 
 
	private:
 
		Component *m_component;
 
		UserManager *m_userManager;
 
		Swift::CombinedOutgoingFileTransferManager* m_outgoingFTManager;
 
		Swift::RemoteJingleTransportCandidateSelectorFactory* m_remoteCandidateSelectorFactory;
 
		Swift::LocalJingleTransportCandidateGeneratorFactory* m_localCandidateGeneratorFactory;
 
		Swift::JingleSessionManager *m_jingleSessionManager;
 
		Swift::SOCKS5BytestreamRegistry* m_bytestreamRegistry;
 
#if HAVE_SWIFTEN_3
 
		Swift::SOCKS5BytestreamServerManager* m_proxyServerManager;
 
		Swift::SOCKS5BytestreamProxiesManager *m_proxyManager;
 
#else
 
		Swift::SOCKS5BytestreamServer* m_bytestreamServer;
 
		Swift::SOCKS5BytestreamProxy* m_bytestreamProxy;
 
		Swift::SOCKS5BytestreamServer *bytestreamServer;
 
		Swift::ConnectivityManager* m_connectivityManager;
 
#endif
 
};
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#include <Swiften/Elements/StreamInitiationFileInfo.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/ConnectivityManager.h>
 
#endif
 
#include <Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h>
 
#include <Swiften/FileTransfer/IncomingFileTransferManager.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/DefaultLocalJingleTransportCandidateGeneratorFactory.h>
 
#include <Swiften/FileTransfer/DefaultRemoteJingleTransportCandidateSelectorFactory.h>
 
#else
 
#include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h>
 
#endif
 
#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h>
 
#include <Swiften/FileTransfer/SOCKS5BytestreamServer.h>
 
#if !HAVE_SWIFTEN_3
 
#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h>
 
#endif
 

	
 
namespace Transport {
 

	
 
class UserManager;
 
class User;
 
class Component;
 
class Buddy;
 

	
 
class FileTransferManager {
 
	public:
 
		typedef struct Transfer {
 
			boost::shared_ptr<Swift::OutgoingFileTransfer> ft;
 
			Swift::JID from;
 
			Swift::JID to;
 
			boost::shared_ptr<Swift::ReadBytestream> readByteStream;
 
		} Transfer;
 

	
 
		FileTransferManager(Component *component, UserManager *userManager);
 
		virtual ~FileTransferManager();
 
		
 
		FileTransferManager::Transfer sendFile(User *user, Buddy *buddy, boost::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info);
 

	
 
	private:
 
		Component *m_component;
 
		UserManager *m_userManager;
 
		Swift::CombinedOutgoingFileTransferManager* m_outgoingFTManager;
 
		Swift::RemoteJingleTransportCandidateSelectorFactory* m_remoteCandidateSelectorFactory;
 
		Swift::LocalJingleTransportCandidateGeneratorFactory* m_localCandidateGeneratorFactory;
 
		Swift::JingleSessionManager *m_jingleSessionManager;
 
		Swift::SOCKS5BytestreamRegistry* m_bytestreamRegistry;
 
#if HAVE_SWIFTEN_3
 
		Swift::SOCKS5BytestreamServerManager* m_proxyServerManager;
 
		Swift::SOCKS5BytestreamProxiesManager *m_proxyManager;
 
#else
 
		Swift::SOCKS5BytestreamServer* m_bytestreamServer;
 
		Swift::SOCKS5BytestreamProxy* m_bytestreamProxy;
 
		Swift::SOCKS5BytestreamServer *bytestreamServer;
 
		Swift::ConnectivityManager* m_connectivityManager;
 
#endif
 
};
 

	
 
}
include/transport/networkplugin.h
Show inline comments
 
@@ -21,6 +21,7 @@
 
#pragma once
 

	
 
#include <time.h>
 
#undef TYPE_BOOL
 
#include "transport/protocol.pb.h"
 
// #include "conversation.h"
 
#include <iostream>
include/transport/networkpluginserver.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#pragma once
 
 
#include <time.h>
 
#include "Swiften/Presence/PresenceOracle.h"
 
#include "Swiften/Disco/EntityCapsManager.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/Connection.h"
 
#include "Swiften/Elements/ChatState.h"
 
#include "Swiften/Elements/RosterItemPayload.h"
 
#include "Swiften/Elements/VCard.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
 
#include "Swiften/Parser/XMPPParser.h"
 
#include "Swiften/Parser/XMPPParserClient.h"
 
#include "Swiften/Serializer/XMPPSerializer.h"
 
#include "storagebackend.h"
 
#include "transport/filetransfermanager.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Transport {
 
 
class UserManager;
 
class User;
 
class Component;
 
class Buddy;
 
class LocalBuddy;
 
class Config;
 
class NetworkConversation;
 
class VCardResponder;
 
class RosterResponder;
 
class BlockResponder;
 
class DummyReadBytestream;
 
class AdminInterface;
 
class DiscoItemsResponder;
 
 
class NetworkPluginServer : Swift::XMPPParserClient {
 
	public:
 
		struct Backend {
 
			int pongReceived;
 
			std::list<User *> users;
 
			Swift::SafeByteArray data;
 
			boost::shared_ptr<Swift::Connection> connection;
 
			unsigned long res;
 
			unsigned long init_res;
 
			unsigned long shared;
 
			bool acceptUsers;
 
			bool longRun;
 
			bool willDie;
 
			std::string id;
 
		};
 
 
		NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder);
 
 
		virtual ~NetworkPluginServer();
 
 
		void start();
 
 
		void setAdminInterface(AdminInterface *adminInterface) {
 
			m_adminInterface = adminInterface;
 
		}
 
 
		int getBackendCount() {
 
			return m_clients.size();
 
		}
 
 
		const std::list<Backend *> &getBackends() {
 
			return m_clients;
 
		}
 
 
		const std::vector<std::string> &getCrashedBackends() {
 
			return m_crashedBackends;
 
		}
 
 
		void collectBackend();
 
 
		bool moveToLongRunBackend(User *user);
 
 
		void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &message);
 
 
	public:
 
		void handleNewClientConnection(boost::shared_ptr<Swift::Connection> c);
 
		void handleSessionFinished(Backend *c);
 
		void handlePongReceived(Backend *c);
 
		void handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data);
 
 
		void handleConnectedPayload(const std::string &payload);
 
		void handleDisconnectedPayload(const std::string &payload);
 
		void handleBuddyChangedPayload(const std::string &payload);
 
		void handleBuddyRemovedPayload(const std::string &payload);
 
		void handleConvMessagePayload(const std::string &payload, bool subject = false);
 
		void handleConvMessageAckPayload(const std::string &payload);
 
		void handleParticipantChangedPayload(const std::string &payload);
 
		void handleRoomChangedPayload(const std::string &payload);
 
		void handleVCardPayload(const std::string &payload);
 
		void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type);
 
		void handleAuthorizationPayload(const std::string &payload);
 
		void handleAttentionPayload(const std::string &payload);
 
		void handleStatsPayload(Backend *c, const std::string &payload);
 
		void handleFTStartPayload(const std::string &payload);
 
		void handleFTFinishPayload(const std::string &payload);
 
		void handleFTDataPayload(Backend *b, const std::string &payload);
 
		void handleQueryPayload(Backend *b, const std::string &payload);
 
		void handleBackendConfigPayload(const std::string &payload);
 
		void handleRoomListPayload(const std::string &payload);
 
		void handleRawXML(const std::string &xml);
 
 
		void handleUserCreated(User *user);
 
		void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password);
 
		void handleRoomLeft(User *user, const std::string &room);
 
		void handleUserReadyToConnect(User *user);
 
		void handleUserPresenceChanged(User *user, Swift::Presence::ref presence);
 
		void handleUserDestroyed(User *user);
 
 
		void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleBuddyRemoved(Buddy *buddy);
 
		void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleUserBuddyAdded(User *user, Buddy *buddy);
 
		void handleUserBuddyRemoved(User *user, Buddy *buddy);
 
 
		void handleBlockToggled(Buddy *buddy);
 
 
		void handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> vcard);
 
		void handleVCardRequired(User *user, const std::string &name, unsigned int id);
 
 
		void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id);
 
		void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID);
 
		void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size);
 
		void handleFTDataNeeded(Backend *b, unsigned long ftid);
 
 
		void handlePIDTerminated(unsigned long pid);
 
	private:
 
		void send(boost::shared_ptr<Swift::Connection> &, const std::string &data);
 
 
		void pingTimeout();
 
		void sendPing(Backend *c);
 
		Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false);
 
		void connectWaitingUsers();
 
		void loginDelayFinished();
 
		void handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq);
 
		void handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence);
 
 
		void handleStreamStart(const Swift::ProtocolHeader&) {}
 
#if HAVE_SWIFTEN_3
 
		void handleElement(boost::shared_ptr<Swift::ToplevelElement> element);
 
#else
 
		void handleElement(boost::shared_ptr<Swift::Element> element);
 
#endif
 
		void handleStreamEnd() {}
 
 
		UserManager *m_userManager;
 
		VCardResponder *m_vcardResponder;
 
		RosterResponder *m_rosterResponder;
 
		BlockResponder *m_blockResponder;
 
		Config *m_config;
 
		boost::shared_ptr<Swift::ConnectionServer> m_server;
 
		std::list<Backend *>  m_clients;
 
		std::vector<unsigned long> m_pids;
 
		Swift::Timer::ref m_pingTimer;
 
		Swift::Timer::ref m_collectTimer;
 
		Swift::Timer::ref m_loginTimer;
 
		Component *m_component;
 
		std::list<User *> m_waitingUsers;
 
		bool m_isNextLongRun;
 
		std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
 
		FileTransferManager *m_ftManager;
 
		std::vector<std::string> m_crashedBackends;
 
		AdminInterface *m_adminInterface;
 
		bool m_startingBackend;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		time_t m_lastLogin;
 
		Swift::XMPPParser *m_xmppParser;
 
		Swift::FullPayloadParserFactoryCollection m_collection;
 
		Swift::XMPPSerializer *m_serializer;
 
		Swift::FullPayloadSerializerCollection m_collection2;
 
		std::map <std::string, std::string> m_id2resource;
 
};
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <time.h>
 
#include "Swiften/Presence/PresenceOracle.h"
 
#include "Swiften/Disco/EntityCapsManager.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/Connection.h"
 
#include "Swiften/Elements/ChatState.h"
 
#include "Swiften/Elements/RosterItemPayload.h"
 
#include "Swiften/Elements/VCard.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
 
#include "Swiften/Parser/XMPPParser.h"
 
#include "Swiften/Parser/XMPPParserClient.h"
 
#include "Swiften/Serializer/XMPPSerializer.h"
 
#include "storagebackend.h"
 
#include "transport/filetransfermanager.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Transport {
 

	
 
class UserManager;
 
class User;
 
class Component;
 
class Buddy;
 
class LocalBuddy;
 
class Config;
 
class NetworkConversation;
 
class VCardResponder;
 
class RosterResponder;
 
class BlockResponder;
 
class DummyReadBytestream;
 
class AdminInterface;
 
class DiscoItemsResponder;
 

	
 
class NetworkPluginServer : Swift::XMPPParserClient {
 
	public:
 
		struct Backend {
 
			int pongReceived;
 
			std::list<User *> users;
 
			Swift::SafeByteArray data;
 
			boost::shared_ptr<Swift::Connection> connection;
 
			unsigned long res;
 
			unsigned long init_res;
 
			unsigned long shared;
 
			bool acceptUsers;
 
			bool longRun;
 
			bool willDie;
 
			std::string id;
 
		};
 

	
 
		NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder);
 

	
 
		virtual ~NetworkPluginServer();
 

	
 
		void start();
 

	
 
		void setAdminInterface(AdminInterface *adminInterface) {
 
			m_adminInterface = adminInterface;
 
		}
 

	
 
		int getBackendCount() {
 
			return m_clients.size();
 
		}
 

	
 
		const std::list<Backend *> &getBackends() {
 
			return m_clients;
 
		}
 

	
 
		const std::vector<std::string> &getCrashedBackends() {
 
			return m_crashedBackends;
 
		}
 

	
 
		void collectBackend();
 

	
 
		bool moveToLongRunBackend(User *user);
 

	
 
		void handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &message);
 

	
 
	public:
 
		void handleNewClientConnection(boost::shared_ptr<Swift::Connection> c);
 
		void handleSessionFinished(Backend *c);
 
		void handlePongReceived(Backend *c);
 
		void handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data);
 

	
 
		void handleConnectedPayload(const std::string &payload);
 
		void handleDisconnectedPayload(const std::string &payload);
 
		void handleBuddyChangedPayload(const std::string &payload);
 
		void handleBuddyRemovedPayload(const std::string &payload);
 
		void handleConvMessagePayload(const std::string &payload, bool subject = false);
 
		void handleConvMessageAckPayload(const std::string &payload);
 
		void handleParticipantChangedPayload(const std::string &payload);
 
		void handleRoomChangedPayload(const std::string &payload);
 
		void handleVCardPayload(const std::string &payload);
 
		void handleChatStatePayload(const std::string &payload, Swift::ChatState::ChatStateType type);
 
		void handleAuthorizationPayload(const std::string &payload);
 
		void handleAttentionPayload(const std::string &payload);
 
		void handleStatsPayload(Backend *c, const std::string &payload);
 
		void handleFTStartPayload(const std::string &payload);
 
		void handleFTFinishPayload(const std::string &payload);
 
		void handleFTDataPayload(Backend *b, const std::string &payload);
 
		void handleQueryPayload(Backend *b, const std::string &payload);
 
		void handleBackendConfigPayload(const std::string &payload);
 
		void handleRoomListPayload(const std::string &payload);
 
		void handleRawXML(const std::string &xml);
 

	
 
		void handleUserCreated(User *user);
 
		void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password);
 
		void handleRoomLeft(User *user, const std::string &room);
 
		void handleUserReadyToConnect(User *user);
 
		void handleUserPresenceChanged(User *user, Swift::Presence::ref presence);
 
		void handleUserDestroyed(User *user);
 

	
 
		void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleBuddyRemoved(Buddy *buddy);
 
		void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item);
 
		void handleUserBuddyAdded(User *user, Buddy *buddy);
 
		void handleUserBuddyRemoved(User *user, Buddy *buddy);
 

	
 
		void handleBlockToggled(Buddy *buddy);
 

	
 
		void handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> vcard);
 
		void handleVCardRequired(User *user, const std::string &name, unsigned int id);
 

	
 
		void handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id);
 
		void handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID);
 
		void handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size);
 
		void handleFTDataNeeded(Backend *b, unsigned long ftid);
 

	
 
		void handlePIDTerminated(unsigned long pid);
 
	private:
 
		void send(boost::shared_ptr<Swift::Connection> &, const std::string &data);
 

	
 
		void pingTimeout();
 
		void sendPing(Backend *c);
 
		Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false);
 
		void connectWaitingUsers();
 
		void loginDelayFinished();
 
		void handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq);
 
		void handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence);
 

	
 
		void handleStreamStart(const Swift::ProtocolHeader&) {}
 
#if HAVE_SWIFTEN_3
 
		void handleElement(boost::shared_ptr<Swift::ToplevelElement> element);
 
#else
 
		void handleElement(boost::shared_ptr<Swift::Element> element);
 
#endif
 
		void handleStreamEnd() {}
 

	
 
		UserManager *m_userManager;
 
		VCardResponder *m_vcardResponder;
 
		RosterResponder *m_rosterResponder;
 
		BlockResponder *m_blockResponder;
 
		Config *m_config;
 
		boost::shared_ptr<Swift::ConnectionServer> m_server;
 
		std::list<Backend *>  m_clients;
 
		std::vector<unsigned long> m_pids;
 
		Swift::Timer::ref m_pingTimer;
 
		Swift::Timer::ref m_collectTimer;
 
		Swift::Timer::ref m_loginTimer;
 
		Component *m_component;
 
		std::list<User *> m_waitingUsers;
 
		bool m_isNextLongRun;
 
		std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
 
		FileTransferManager *m_ftManager;
 
		std::vector<std::string> m_crashedBackends;
 
		AdminInterface *m_adminInterface;
 
		bool m_startingBackend;
 
		DiscoItemsResponder *m_discoItemsResponder;
 
		time_t m_lastLogin;
 
		Swift::XMPPParser *m_xmppParser;
 
		Swift::FullPayloadParserFactoryCollection m_collection;
 
		Swift::XMPPSerializer *m_serializer;
 
		Swift::FullPayloadSerializerCollection m_collection2;
 
		std::map <std::string, std::string> m_id2resource;
 
};
 

	
 
}
include/transport/settingsadhoccommand.h
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#pragma once
 
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "transport/adhoccommand.h"
 
#include "transport/adhoccommandfactory.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
 
namespace Transport {
 
 
class Component;
 
class UserManager;
 
class StorageBackend;
 
 
class SettingsAdHocCommand : public AdHocCommand {
 
	public:
 
		typedef enum { Init, WaitingForResponse } State;
 
 
		SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to);
 
 
		/// Destructor.
 
		virtual ~SettingsAdHocCommand();
 
 
		virtual boost::shared_ptr<Swift::Command> handleRequest(boost::shared_ptr<Swift::Command> payload);
 
 
	private:
 
		boost::shared_ptr<Swift::Command> getForm();
 
		boost::shared_ptr<Swift::Command> handleResponse(boost::shared_ptr<Swift::Command> payload);
 
		State m_state;
 
};
 
 
class SettingsAdHocCommandFactory : public AdHocCommandFactory {
 
	public:
 
		SettingsAdHocCommandFactory() {
 
			m_userSettings["send_headlines"] = "0";
 
			m_userSettings["stay_connected"] = "0";
 
		}
 
 
		virtual ~SettingsAdHocCommandFactory() {}
 
 
		AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) {
 
			return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to);
 
		}
 
 
		std::string getNode() {
 
			return "settings";
 
		}
 
 
		std::string getName() {
 
			return "Transport settings";
 
		}
 
};
 
 
}
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 
#include "transport/adhoccommand.h"
 
#include "transport/adhoccommandfactory.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 

	
 
namespace Transport {
 

	
 
class Component;
 
class UserManager;
 
class StorageBackend;
 

	
 
class SettingsAdHocCommand : public AdHocCommand {
 
	public:
 
		typedef enum { Init, WaitingForResponse } State;
 

	
 
		SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to);
 

	
 
		/// Destructor.
 
		virtual ~SettingsAdHocCommand();
 

	
 
		virtual boost::shared_ptr<Swift::Command> handleRequest(boost::shared_ptr<Swift::Command> payload);
 

	
 
	private:
 
		boost::shared_ptr<Swift::Command> getForm();
 
		boost::shared_ptr<Swift::Command> handleResponse(boost::shared_ptr<Swift::Command> payload);
 
		State m_state;
 
};
 

	
 
class SettingsAdHocCommandFactory : public AdHocCommandFactory {
 
	public:
 
		SettingsAdHocCommandFactory() {
 
			m_userSettings["send_headlines"] = "0";
 
			m_userSettings["stay_connected"] = "0";
 
		}
 

	
 
		virtual ~SettingsAdHocCommandFactory() {}
 

	
 
		AdHocCommand *createAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) {
 
			return new SettingsAdHocCommand(component, userManager, storageBackend, initiator, to);
 
		}
 

	
 
		std::string getNode() {
 
			return "settings";
 
		}
 

	
 
		std::string getName() {
 
			return "Transport settings";
 
		}
 
};
 

	
 
}
include/transport/userregistration.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#pragma once
 
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/InBandRegistrationPayload.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include <boost/signal.hpp>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
 
namespace Transport {
 
 
struct UserInfo;
 
class Component;
 
class StorageBackend;
 
class UserManager;
 
class Config;
 
 
/// Allows users to register the transport using service discovery.
 
class UserRegistration : public Swift::Responder<Swift::InBandRegistrationPayload> {
 
	public:
 
		/// Creates new UserRegistration handler.
 
		/// \param component Component associated with this class
 
		/// \param userManager UserManager associated with this class
 
		/// \param storageBackend StorageBackend where the registered users will be stored
 
		UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend);
 
 
		/// Destroys UserRegistration.
 
		~UserRegistration();
 
 
		/// Registers new user. This function stores user into database and subscribe user to transport.
 
		/// \param userInfo UserInfo struct with informations about registered user
 
		/// \return false if user is already registered
 
		bool registerUser(const UserInfo &userInfo);
 
 
		/// Unregisters user. This function removes all data about user from databa, unsubscribe all buddies
 
		/// managed by this transport and disconnects user if he's connected.
 
		/// \param barejid bare JID of user to unregister
 
		/// \return false if there is no such user registered
 
		bool unregisterUser(const std::string &barejid);
 
 
		/// Called when new user has been registered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserRegistered;
 
 
		/// Called when user has been unregistered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUnregistered;
 
 
		/// Called when user's registration has been updated.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUpdated;
 
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 
 
		void handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const UserInfo &row);
 
		void handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const std::string &barejid);
 
		
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		UserManager *m_userManager;
 
		Config *m_config;
 
 
};
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include "Swiften/Queries/Responder.h"
 
#include "Swiften/Elements/InBandRegistrationPayload.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include <boost/signal.hpp>
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
namespace Transport {
 

	
 
struct UserInfo;
 
class Component;
 
class StorageBackend;
 
class UserManager;
 
class Config;
 

	
 
/// Allows users to register the transport using service discovery.
 
class UserRegistration : public Swift::Responder<Swift::InBandRegistrationPayload> {
 
	public:
 
		/// Creates new UserRegistration handler.
 
		/// \param component Component associated with this class
 
		/// \param userManager UserManager associated with this class
 
		/// \param storageBackend StorageBackend where the registered users will be stored
 
		UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend);
 

	
 
		/// Destroys UserRegistration.
 
		~UserRegistration();
 

	
 
		/// Registers new user. This function stores user into database and subscribe user to transport.
 
		/// \param userInfo UserInfo struct with informations about registered user
 
		/// \return false if user is already registered
 
		bool registerUser(const UserInfo &userInfo);
 

	
 
		/// Unregisters user. This function removes all data about user from databa, unsubscribe all buddies
 
		/// managed by this transport and disconnects user if he's connected.
 
		/// \param barejid bare JID of user to unregister
 
		/// \return false if there is no such user registered
 
		bool unregisterUser(const std::string &barejid);
 

	
 
		/// Called when new user has been registered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserRegistered;
 

	
 
		/// Called when user has been unregistered.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUnregistered;
 

	
 
		/// Called when user's registration has been updated.
 
		/// \param userInfo UserInfo struct with informations about user
 
		boost::signal<void (const UserInfo &userInfo)> onUserUpdated;
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 
		virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload);
 

	
 
		void handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const UserInfo &row);
 
		void handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref error, const std::string &barejid);
 
		
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		UserManager *m_userManager;
 
		Config *m_config;
 

	
 
};
 

	
 
}
msvc-deps/CMakeLists.txt
Show inline comments
 
ADD_SUBDIRECTORY(sqlite3)
 
ADD_SUBDIRECTORY(sqlite3)
msvc-deps/sqlite3/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SQLITE_SRC *.c *.h)
 
 
ADD_LIBRARY(sqlite3 STATIC ${HEADERS} ${SQLITE_SRC})
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SQLITE_SRC *.c *.h)
 

	
 
ADD_LIBRARY(sqlite3 STATIC ${HEADERS} ${SQLITE_SRC})
 

	
 
INSTALL(TARGETS sqlite3 LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries)
 
\ No newline at end of file
plugin/cpp/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp *.h)
 
FILE(GLOB HEADERS ../include/transport/*.h)
 
 
set(EXTRA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../src/memoryusage.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/logging.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/config.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/util.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc)
 
 
if (NOT WIN32)
 
	ADD_LIBRARY(transport-plugin SHARED ${HEADERS} ${SRC} ${PROTOBUF_SRC} ${PROTOBUF_HDRS} ${EXTRA_SOURCES})
 
else()
 
	ADD_LIBRARY(transport-plugin STATIC ${HEADERS} ${SRC} ${EXTRA_SOURCES} )
 
endif()
 
ADD_DEPENDENCIES(transport-plugin pb)
 
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc PROPERTIES GENERATED 1)
 
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
	if (NOT WIN32)
 
	ADD_DEFINITIONS(-fPIC)
 
	endif()
 
endif()
 
 
if (NOT WIN32)
 
	TARGET_LINK_LIBRARIES(transport-plugin ${PROTOBUF_LIBRARY} ${LOG4CXX_LIBRARIES} ${Boost_LIBRARIES})
 
else()
 
	TARGET_LINK_LIBRARIES(transport-plugin ${PROTOBUF_LIBRARY} ${LOG4CXX_LIBRARIES} ${Boost_LIBRARIES} ws2_32.lib)
 
endif() 
 
 
SET_TARGET_PROPERTIES(transport-plugin PROPERTIES
 
      VERSION ${TRANSPORT_VERSION} SOVERSION ${TRANSPORT_VERSION}
 
)
 
 
INSTALL(TARGETS transport-plugin LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
 
 
#CONFIGURE_FILE(transport.pc.in "${CMAKE_CURRENT_SOURCE_DIR}/transport.pc")
 
#INSTALL(FILES "${CMAKE_CURRENT_SOURCE_DIR}/transport.pc" DESTINATION lib/pkgconfig)
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp *.h)
 
FILE(GLOB HEADERS ../include/transport/*.h)
 

	
 
set(EXTRA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../src/memoryusage.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/logging.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/config.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../src/util.cpp)
 
set(EXTRA_SOURCES ${EXTRA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc)
 

	
 
if (NOT WIN32)
 
	ADD_LIBRARY(transport-plugin SHARED ${HEADERS} ${SRC} ${PROTOBUF_SRC} ${PROTOBUF_HDRS} ${EXTRA_SOURCES})
 
else()
 
	ADD_LIBRARY(transport-plugin STATIC ${HEADERS} ${SRC} ${EXTRA_SOURCES} )
 
endif()
 
ADD_DEPENDENCIES(transport-plugin pb)
 
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc PROPERTIES GENERATED 1)
 

	
 
if (CMAKE_COMPILER_IS_GNUCXX)
 
	if (NOT WIN32)
 
	ADD_DEFINITIONS(-fPIC)
 
	endif()
 
endif()
 

	
 
if (NOT WIN32)
 
	TARGET_LINK_LIBRARIES(transport-plugin ${PROTOBUF_LIBRARY} ${LOG4CXX_LIBRARIES} ${Boost_LIBRARIES})
 
else()
 
	TARGET_LINK_LIBRARIES(transport-plugin ${PROTOBUF_LIBRARY} ${LOG4CXX_LIBRARIES} ${Boost_LIBRARIES} ws2_32.lib)
 
endif() 
 

	
 
SET_TARGET_PROPERTIES(transport-plugin PROPERTIES
 
      VERSION ${TRANSPORT_VERSION} SOVERSION ${TRANSPORT_VERSION}
 
)
 

	
 
INSTALL(TARGETS transport-plugin LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
 

	
 
#CONFIGURE_FILE(transport.pc.in "${CMAKE_CURRENT_SOURCE_DIR}/transport.pc")
 
#INSTALL(FILES "${CMAKE_CURRENT_SOURCE_DIR}/transport.pc" DESTINATION lib/pkgconfig)
plugin/python/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
 
if (PROTOBUF_FOUND)
 
    ADD_CUSTOM_COMMAND(
 
        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocol_pb2.py
 
        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --python_out  ${CMAKE_CURRENT_SOURCE_DIR} --proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/ ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.proto
 
        COMMENT "Running Python protocol buffer compiler on protocol.proto"
 
        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.proto
 
    )
 
    ADD_CUSTOM_TARGET(pb-python ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol_pb2.py)
 
endif()
 
 
 
 
 
cmake_minimum_required(VERSION 2.6)
 

	
 
if (PROTOBUF_FOUND)
 
    ADD_CUSTOM_COMMAND(
 
        OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocol_pb2.py
 
        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --python_out  ${CMAKE_CURRENT_SOURCE_DIR} --proto_path ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/ ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.proto
 
        COMMENT "Running Python protocol buffer compiler on protocol.proto"
 
        DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.proto
 
    )
 
    ADD_CUSTOM_TARGET(pb-python ALL DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol_pb2.py)
 
endif()
 

	
 

	
 

	
 

	
spectrum/src/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 
 
if (WIN32)
 
FILE(GLOB WIN_SRC win32/*.cpp)
 
include_directories(win32)
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
 
ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC})
 
else()
 
ADD_EXECUTABLE(spectrum2 ${SRC})
 
endif()
 
 
 
 
ADD_DEPENDENCIES(spectrum2 spectrum2_libpurple_backend)
 
ADD_DEPENDENCIES(spectrum2 spectrum2_libircclient-qt_backend)
 
 
if (WIN32)
 
target_link_libraries(spectrum2 transport sqlite3 ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY})
 
else ()
 
target_link_libraries(spectrum2 transport ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY})
 
endif()
 
 
INSTALL(TARGETS spectrum2 RUNTIME DESTINATION bin)
 
 
INSTALL(FILES
 
	sample2_gateway.cfg
 
	RENAME spectrum.cfg.example
 
	DESTINATION /etc/spectrum2/transports
 
	)
 
 
INSTALL(FILES
 
	sample2.cfg
 
	RENAME spectrum_server_mode.cfg.example
 
	DESTINATION /etc/spectrum2/transports
 
	)
 
 
INSTALL(FILES
 
	backend-logging.cfg
 
	DESTINATION /etc/spectrum2
 
	)
 
 
INSTALL(FILES
 
	logging.cfg
 
	DESTINATION /etc/spectrum2
 
	)
 
 
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 

	
 
if (WIN32)
 
FILE(GLOB WIN_SRC win32/*.cpp)
 
include_directories(win32)
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
 
ADD_EXECUTABLE(spectrum2 ${SRC} ${WIN_SRC})
 
else()
 
ADD_EXECUTABLE(spectrum2 ${SRC})
 
endif()
 

	
 

	
 

	
 
ADD_DEPENDENCIES(spectrum2 spectrum2_libpurple_backend)
 
ADD_DEPENDENCIES(spectrum2 spectrum2_libircclient-qt_backend)
 

	
 
if (WIN32)
 
target_link_libraries(spectrum2 transport sqlite3 ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY})
 
else ()
 
target_link_libraries(spectrum2 transport ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY})
 
endif()
 

	
 
INSTALL(TARGETS spectrum2 RUNTIME DESTINATION bin)
 

	
 
INSTALL(FILES
 
	sample2_gateway.cfg
 
	RENAME spectrum.cfg.example
 
	DESTINATION /etc/spectrum2/transports
 
	)
 

	
 
INSTALL(FILES
 
	sample2.cfg
 
	RENAME spectrum_server_mode.cfg.example
 
	DESTINATION /etc/spectrum2/transports
 
	)
 

	
 
INSTALL(FILES
 
	backend-logging.cfg
 
	DESTINATION /etc/spectrum2
 
	)
 

	
 
INSTALL(FILES
 
	logging.cfg
 
	DESTINATION /etc/spectrum2
 
	)
 

	
 

	
spectrum_manager/src/CMakeLists.txt
Show inline comments
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp *.c)
 
 
ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp ../../src/util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc)
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp *.c)
 

	
 
ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp ../../src/util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc)
 

	
 
ADD_DEPENDENCIES(spectrum2_manager pb)
 
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc PROPERTIES GENERATED 1)
 

	
 
target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES})
 
target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES})
 

	
 
if(APPLE)
 
target_link_libraries(spectrum2_manager ${APPLE_FRAMEWORKS})
 
endif()
 
INSTALL(TARGETS spectrum2_manager RUNTIME DESTINATION bin)
 
 
INSTALL(FILES
 
	spectrum_manager.cfg
 
	DESTINATION /etc/spectrum2
 
	)
 
INSTALL(TARGETS spectrum2_manager RUNTIME DESTINATION bin)
 

	
 
INSTALL(FILES
 
	spectrum_manager.cfg
 
	DESTINATION /etc/spectrum2
 
	)
spectrum_manager/src/main.cpp
Show inline comments
 
#include "managerconfig.h"
 
#include "methods.h"
 
#include "server.h"
 
#include "transport/config.h"
 
#include "transport/protocol.pb.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 
 
#include <boost/foreach.hpp>
 
#include <iostream>
 
#include <fstream>
 
#include <iterator>
 
#include <algorithm>
 
#include <boost/filesystem.hpp>
 
#include <cstdlib>
 
#include "signal.h"
 
#include "sys/wait.h"
 
 
 
using namespace Transport;
 
 
using namespace boost::filesystem;
 
 
using namespace boost;
 
 
 
// static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) {
 
// 	path p(CONFIG_STRING(config, "service.config_directory"));
 
// 
 
// 	try {
 
// 		if (!exists(p)) {
 
// 			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
// 			exit(6);
 
// 		}
 
// 
 
// 		if (!is_directory(p)) {
 
// 			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
// 			exit(7);
 
// 		}
 
// 
 
// 		directory_iterator end_itr;
 
// 		for (directory_iterator itr(p); itr != end_itr; ++itr) {
 
// 			if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
 
// 				Config cfg;
 
// 				if (cfg.load(itr->path().string()) == false) {
 
// 					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				if (CONFIG_VECTOR(&cfg, "service.admin_jid").empty() || CONFIG_STRING(&cfg, "service.admin_password").empty()) {
 
// 					std::cerr << itr->path().string() << ": service.admin_jid or service.admin_password empty. This server can't be queried over XMPP.\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				finished++;
 
// 				Swift::Client *client = new Swift::Client(CONFIG_VECTOR(&cfg, "service.admin_jid")[0], CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories);
 
// 				client->setAlwaysTrustCertificates();
 
// 				client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onMessageReceived.connect(bind(&handleMessageReceived, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				Swift::ClientOptions opt;
 
// 				opt.allowPLAINWithoutTLS = true;
 
// 				client->connect(opt);
 
// 			}
 
// 		}
 
// 	}
 
// 	catch (const filesystem_error& ex) {
 
// 		std::cerr << "boost filesystem error\n";
 
// 		exit(5);
 
// 	}
 
// }
 
 
 
int main(int argc, char **argv)
 
{
 
	ManagerConfig config;
 
	std::string config_file;
 
	std::vector<std::string> command;
 
	boost::program_options::variables_map vm;
 
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <COMMAND>\n"
 
													 "       spectrum [OPTIONS] <instance_JID> <other>\nCommands:\n"
 
													 " start - start all local Spectrum2 instances\n"
 
													 " stop  - stop all local Spectrum2 instances\n"
 
													 " restart  - restart all local Spectrum2 instances\n"
 
													 " status - status of local Spectrum2 instances\n"
 
													 " <other> - send command to local Spectrum2 instance and print output\n"
 
													 "Allowed options");
 
	desc.add_options()
 
		("help,h", "Show help output")
 
		("config,c", boost::program_options::value<std::string>(&config_file)->default_value("/etc/spectrum2/spectrum_manager.cfg"), "Spectrum manager config file")
 
		("command", boost::program_options::value<std::vector<std::string> >(&command), "Command")
 
		;
 
	try
 
	{
 
		boost::program_options::positional_options_description p;
 
		p.add("command", -1);
 
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
 
          options(desc).positional(p).run(), vm);
 
		boost::program_options::notify(vm);
 
 
		if(vm.count("help"))
 
		{
 
			std::cout << desc << "\n";
 
			return 1;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		return 2;
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		return 3;
 
	}
 
 
	if (!config.load(config_file)) {
 
		std::cerr << "Can't load configuration file.\n";
 
		return 4;
 
	}
 
 
	if (command.empty()) {
 
		std::cout << desc << "\n";
 
		return 1;
 
	}
 
 
	if (command[0] == "start") {
 
		return start_instances(&config);
 
	}
 
	else if (command[0] == "stop") {
 
		stop_instances(&config);
 
	}
 
	else if (command[0] == "status") {
 
		return show_status(&config);
 
	}
 
	else if (command[0] == "list") {
 
		std::vector<std::string> list = show_list(&config);
 
	}
 
	else if (command[0] == "restart") {
 
		return restart_instances(&config);
 
	}
 
	else if (command[0] == "server") {
 
		Server server(&config);
 
		if (server.start() == false) {
 
			std::cerr << "Can't set up server handler.\n";
 
			return 1;
 
		}
 
		while (1) { sleep(10); }
 
	}
 
	else {
 
		if (command.size() < 2) {
 
			std::cout << desc << "\n";
 
			return 11;
 
		}
 
		Swift::SimpleEventLoop eventLoop;
 
		Swift::BoostNetworkFactories networkFactories(&eventLoop);
 
 
		std::string jid = command[0];
 
		command.erase(command.begin());
 
		std::string cmd = boost::algorithm::join(command, " ");
 
 
		if (cmd == "start") {
 
			return start_instances(&config, jid);
 
		}
 
		else if (cmd == "stop") {
 
			stop_instances(&config, jid);
 
			return 0;
 
		}
 
		else if (cmd == "restart") {
 
			return restart_instances(&config, jid);
 
		}
 
 
		ask_local_server(&config, networkFactories, jid, cmd);
 
// 		std::string message = command;
 
// 		m = &message;
 
 
// 		ask_local_server(&config, networkFactories, message);
 
 
		eventLoop.runUntilEvents();
 
 
 
		struct timeval td_start,td_end;
 
		float elapsed = 0; 
 
		gettimeofday(&td_start, NULL);
 
	
 
		time_t started = time(NULL);
 
		while(get_response().empty()) {
 
			eventLoop.runUntilEvents();
 
		}
 
		if (!get_response().empty()) {
 
			gettimeofday(&td_end, NULL);
 
			elapsed = 1000000.0 * (td_end.tv_sec -td_start.tv_sec); \
 
			elapsed += (td_end.tv_usec - td_start.tv_usec); \
 
			elapsed = elapsed / 1000 / 1000; \
 
// 			std::cout << "Response received after " << (elapsed) << " seconds\n";
 
		}
 
	}
 
}
 
#include "managerconfig.h"
 
#include "methods.h"
 
#include "server.h"
 
#include "transport/config.h"
 
#include "transport/protocol.pb.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 

	
 
#include <boost/foreach.hpp>
 
#include <iostream>
 
#include <fstream>
 
#include <iterator>
 
#include <algorithm>
 
#include <boost/filesystem.hpp>
 
#include <cstdlib>
 
#include "signal.h"
 
#include "sys/wait.h"
 

	
 

	
 
using namespace Transport;
 

	
 
using namespace boost::filesystem;
 

	
 
using namespace boost;
 

	
 

	
 
// static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) {
 
// 	path p(CONFIG_STRING(config, "service.config_directory"));
 
// 
 
// 	try {
 
// 		if (!exists(p)) {
 
// 			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
// 			exit(6);
 
// 		}
 
// 
 
// 		if (!is_directory(p)) {
 
// 			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
// 			exit(7);
 
// 		}
 
// 
 
// 		directory_iterator end_itr;
 
// 		for (directory_iterator itr(p); itr != end_itr; ++itr) {
 
// 			if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
 
// 				Config cfg;
 
// 				if (cfg.load(itr->path().string()) == false) {
 
// 					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				if (CONFIG_VECTOR(&cfg, "service.admin_jid").empty() || CONFIG_STRING(&cfg, "service.admin_password").empty()) {
 
// 					std::cerr << itr->path().string() << ": service.admin_jid or service.admin_password empty. This server can't be queried over XMPP.\n";
 
// 					continue;
 
// 				}
 
// 
 
// 				finished++;
 
// 				Swift::Client *client = new Swift::Client(CONFIG_VECTOR(&cfg, "service.admin_jid")[0], CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories);
 
// 				client->setAlwaysTrustCertificates();
 
// 				client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				client->onMessageReceived.connect(bind(&handleMessageReceived, client, _1, CONFIG_STRING(&cfg, "service.jid")));
 
// 				Swift::ClientOptions opt;
 
// 				opt.allowPLAINWithoutTLS = true;
 
// 				client->connect(opt);
 
// 			}
 
// 		}
 
// 	}
 
// 	catch (const filesystem_error& ex) {
 
// 		std::cerr << "boost filesystem error\n";
 
// 		exit(5);
 
// 	}
 
// }
 

	
 

	
 
int main(int argc, char **argv)
 
{
 
	ManagerConfig config;
 
	std::string config_file;
 
	std::vector<std::string> command;
 
	boost::program_options::variables_map vm;
 

	
 
	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <COMMAND>\n"
 
													 "       spectrum [OPTIONS] <instance_JID> <other>\nCommands:\n"
 
													 " start - start all local Spectrum2 instances\n"
 
													 " stop  - stop all local Spectrum2 instances\n"
 
													 " restart  - restart all local Spectrum2 instances\n"
 
													 " status - status of local Spectrum2 instances\n"
 
													 " <other> - send command to local Spectrum2 instance and print output\n"
 
													 "Allowed options");
 
	desc.add_options()
 
		("help,h", "Show help output")
 
		("config,c", boost::program_options::value<std::string>(&config_file)->default_value("/etc/spectrum2/spectrum_manager.cfg"), "Spectrum manager config file")
 
		("command", boost::program_options::value<std::vector<std::string> >(&command), "Command")
 
		;
 
	try
 
	{
 
		boost::program_options::positional_options_description p;
 
		p.add("command", -1);
 
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
 
          options(desc).positional(p).run(), vm);
 
		boost::program_options::notify(vm);
 

	
 
		if(vm.count("help"))
 
		{
 
			std::cout << desc << "\n";
 
			return 1;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		return 2;
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		return 3;
 
	}
 

	
 
	if (!config.load(config_file)) {
 
		std::cerr << "Can't load configuration file.\n";
 
		return 4;
 
	}
 

	
 
	if (command.empty()) {
 
		std::cout << desc << "\n";
 
		return 1;
 
	}
 

	
 
	if (command[0] == "start") {
 
		return start_instances(&config);
 
	}
 
	else if (command[0] == "stop") {
 
		stop_instances(&config);
 
	}
 
	else if (command[0] == "status") {
 
		return show_status(&config);
 
	}
 
	else if (command[0] == "list") {
 
		std::vector<std::string> list = show_list(&config);
 
	}
 
	else if (command[0] == "restart") {
 
		return restart_instances(&config);
 
	}
 
	else if (command[0] == "server") {
 
		Server server(&config);
 
		if (server.start() == false) {
 
			std::cerr << "Can't set up server handler.\n";
 
			return 1;
 
		}
 
		while (1) { sleep(10); }
 
	}
 
	else {
 
		if (command.size() < 2) {
 
			std::cout << desc << "\n";
 
			return 11;
 
		}
 
		Swift::SimpleEventLoop eventLoop;
 
		Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
		std::string jid = command[0];
 
		command.erase(command.begin());
 
		std::string cmd = boost::algorithm::join(command, " ");
 

	
 
		if (cmd == "start") {
 
			return start_instances(&config, jid);
 
		}
 
		else if (cmd == "stop") {
 
			stop_instances(&config, jid);
 
			return 0;
 
		}
 
		else if (cmd == "restart") {
 
			return restart_instances(&config, jid);
 
		}
 

	
 
		ask_local_server(&config, networkFactories, jid, cmd);
 
// 		std::string message = command;
 
// 		m = &message;
 

	
 
// 		ask_local_server(&config, networkFactories, message);
 

	
 
		eventLoop.runUntilEvents();
 

	
 

	
 
		struct timeval td_start,td_end;
 
		float elapsed = 0; 
 
		gettimeofday(&td_start, NULL);
 
	
 
		time_t started = time(NULL);
 
		while(get_response().empty()) {
 
			eventLoop.runUntilEvents();
 
		}
 
		if (!get_response().empty()) {
 
			gettimeofday(&td_end, NULL);
 
			elapsed = 1000000.0 * (td_end.tv_sec -td_start.tv_sec); \
 
			elapsed += (td_end.tv_usec - td_start.tv_usec); \
 
			elapsed = elapsed / 1000 / 1000; \
 
// 			std::cout << "Response received after " << (elapsed) << " seconds\n";
 
		}
 
	}
 
}
spectrum_manager/src/mongoose.c
Show inline comments
 
// Copyright (c) 2004-2012 Sergey Lyubka
 
//
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
// of this software and associated documentation files (the "Software"), to deal
 
// in the Software without restriction, including without limitation the rights
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
// copies of the Software, and to permit persons to whom the Software is
 
// furnished to do so, subject to the following conditions:
 
//
 
// The above copyright notice and this permission notice shall be included in
 
// all copies or substantial portions of the Software.
 
//
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
// THE SOFTWARE.
 
 
#include <netinet/in.h>
 
 
#if defined(_WIN32)
 
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
 
#else
 
#define _XOPEN_SOURCE 600     // For flockfile() on Linux
 
#define _LARGEFILE_SOURCE     // Enable 64-bit file offsets
 
#define __STDC_FORMAT_MACROS  // <inttypes.h> wants this for C++
 
#define __STDC_LIMIT_MACROS   // C++ wants that for INT64_MAX
 
#endif
 
 
#if defined(__SYMBIAN32__)
 
#define NO_SSL // SSL is not supported
 
#define NO_CGI // CGI is not supported
 
#define PATH_MAX FILENAME_MAX
 
#endif // __SYMBIAN32__
 
 
#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
 
#include <sys/types.h>
 
#include <sys/stat.h>
 
#include <errno.h>
 
#include <signal.h>
 
#include <fcntl.h>
 
#endif // !_WIN32_WCE
 
 
#include <time.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 
#include <assert.h>
 
#include <string.h>
 
#include <ctype.h>
 
#include <limits.h>
 
#include <stddef.h>
 
#include <stdio.h>
 
 
#if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific
 
#define _WIN32_WINNT 0x0400 // To make it link in VS2005
 
#include <windows.h>
 
 
#ifndef PATH_MAX
 
#define PATH_MAX MAX_PATH
 
#endif
 
 
#ifndef _WIN32_WCE
 
#include <process.h>
 
#include <direct.h>
 
#include <io.h>
 
#else // _WIN32_WCE
 
#include <winsock2.h>
 
#include <ws2tcpip.h>
 
#define NO_CGI // WinCE has no pipes
 
 
typedef long off_t;
 
 
#define errno   GetLastError()
 
#define strerror(x)  _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
 
#endif // _WIN32_WCE
 
 
#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
 
      ((uint64_t)((uint32_t)(hi))) << 32))
 
#define RATE_DIFF 10000000 // 100 nsecs
 
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
 
#define SYS2UNIX_TIME(lo, hi) \
 
  (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
 
 
// Visual Studio 6 does not know __func__ or __FUNCTION__
 
// The rest of MS compilers use __FUNCTION__, not C99 __func__
 
// Also use _strtoui64 on modern M$ compilers
 
#if defined(_MSC_VER) && _MSC_VER < 1300
 
#define STRX(x) #x
 
#define STR(x) STRX(x)
 
#define __func__ "line " STR(__LINE__)
 
#define strtoull(x, y, z) strtoul(x, y, z)
 
#define strtoll(x, y, z) strtol(x, y, z)
 
#else
 
#define __func__  __FUNCTION__
 
#define strtoull(x, y, z) _strtoui64(x, y, z)
 
#define strtoll(x, y, z) _strtoi64(x, y, z)
 
#endif // _MSC_VER
 
 
#define ERRNO   GetLastError()
 
#define NO_SOCKLEN_T
 
#define SSL_LIB   "ssleay32.dll"
 
#define CRYPTO_LIB  "libeay32.dll"
 
#define DIRSEP '\\'
 
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
 
#define O_NONBLOCK  0
 
#if !defined(EWOULDBLOCK)
 
#define EWOULDBLOCK  WSAEWOULDBLOCK
 
#endif // !EWOULDBLOCK
 
#define _POSIX_
 
#define INT64_FMT  "I64d"
 
 
#define WINCDECL __cdecl
 
#define SHUT_WR 1
 
#define snprintf _snprintf
 
#define vsnprintf _vsnprintf
 
#define mg_sleep(x) Sleep(x)
 
 
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
 
#define popen(x, y) _popen(x, y)
 
#define pclose(x) _pclose(x)
 
#define close(x) _close(x)
 
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
 
#define RTLD_LAZY  0
 
#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
 
#define fdopen(x, y) _fdopen((x), (y))
 
#define write(x, y, z) _write((x), (y), (unsigned) z)
 
#define read(x, y, z) _read((x), (y), (unsigned) z)
 
#define flockfile(x) EnterCriticalSection(&global_log_file_lock)
 
#define funlockfile(x) LeaveCriticalSection(&global_log_file_lock)
 
 
#if !defined(fileno)
 
#define fileno(x) _fileno(x)
 
#endif // !fileno MINGW #defines fileno
 
 
typedef HANDLE pthread_mutex_t;
 
typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
 
typedef DWORD pthread_t;
 
#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
 
 
struct timespec {
 
  long tv_nsec;
 
  long tv_sec;
 
};
 
 
static int pthread_mutex_lock(pthread_mutex_t *);
 
static int pthread_mutex_unlock(pthread_mutex_t *);
 
static FILE *mg_fopen(const char *path, const char *mode);
 
 
#if defined(HAVE_STDINT)
 
#include <stdint.h>
 
#else
 
typedef unsigned int  uint32_t;
 
typedef unsigned short  uint16_t;
 
typedef unsigned __int64 uint64_t;
 
typedef __int64   int64_t;
 
#define INT64_MAX  9223372036854775807
 
#endif // HAVE_STDINT
 
 
// POSIX dirent interface
 
struct dirent {
 
  char d_name[PATH_MAX];
 
};
 
 
typedef struct DIR {
 
  HANDLE   handle;
 
  WIN32_FIND_DATAW info;
 
  struct dirent  result;
 
} DIR;
 
 
#else    // UNIX  specific
 
#include <sys/wait.h>
 
#include <sys/socket.h>
 
#include <sys/select.h>
 
#include <netinet/in.h>
 
#include <arpa/inet.h>
 
#include <sys/time.h>
 
#include <stdint.h>
 
#include <inttypes.h>
 
#include <netdb.h>
 
 
#include <pwd.h>
 
#include <unistd.h>
 
#include <dirent.h>
 
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
 
#include <dlfcn.h>
 
#endif
 
#include <pthread.h>
 
#if defined(__MACH__)
 
#define SSL_LIB   "libssl.dylib"
 
#define CRYPTO_LIB  "libcrypto.dylib"
 
#else
 
#if !defined(SSL_LIB)
 
#define SSL_LIB   "libssl.so"
 
#endif
 
#if !defined(CRYPTO_LIB)
 
#define CRYPTO_LIB  "libcrypto.so"
 
#endif
 
#endif
 
#define DIRSEP   '/'
 
#define IS_DIRSEP_CHAR(c) ((c) == '/')
 
#ifndef O_BINARY
 
#define O_BINARY  0
 
#endif // O_BINARY
 
#define closesocket(a) close(a)
 
#define mg_fopen(x, y) fopen(x, y)
 
#define mg_mkdir(x, y) mkdir(x, y)
 
#define mg_remove(x) remove(x)
 
#define mg_rename(x, y) rename(x, y)
 
#define mg_sleep(x) usleep((x) * 1000)
 
#define ERRNO errno
 
#define INVALID_SOCKET (-1)
 
#define INT64_FMT PRId64
 
typedef int SOCKET;
 
#define WINCDECL
 
 
#endif // End of Windows and UNIX specific includes
 
 
#include "mongoose.h"
 
 
#define MONGOOSE_VERSION "3.3"
 
#define PASSWORDS_FILE_NAME ".htpasswd"
 
#define CGI_ENVIRONMENT_SIZE 4096
 
#define MAX_CGI_ENVIR_VARS 64
 
#define MG_BUF_LEN 8192
 
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
 
 
#ifdef _WIN32
 
static CRITICAL_SECTION global_log_file_lock;
 
static pthread_t pthread_self(void) {
 
  return GetCurrentThreadId();
 
}
 
#endif // _WIN32
 
 
#if defined(DEBUG)
 
#define DEBUG_TRACE(x) do { \
 
  flockfile(stdout); \
 
  printf("*** %lu.%p.%s.%d: ", \
 
         (unsigned long) time(NULL), (void *) pthread_self(), \
 
         __func__, __LINE__); \
 
  printf x; \
 
  putchar('\n'); \
 
  fflush(stdout); \
 
  funlockfile(stdout); \
 
} while (0)
 
#else
 
#define DEBUG_TRACE(x)
 
#endif // DEBUG
 
 
// Darwin prior to 7.0 and Win32 do not have socklen_t
 
#ifdef NO_SOCKLEN_T
 
typedef int socklen_t;
 
#endif // NO_SOCKLEN_T
 
#define _DARWIN_UNLIMITED_SELECT
 
 
#if !defined(MSG_NOSIGNAL)
 
#define MSG_NOSIGNAL 0
 
#endif
 
 
#if !defined(SOMAXCONN)
 
#define SOMAXCONN 100
 
#endif
 
 
static const char *http_500_error = "Internal Server Error";
 
 
// Snatched from OpenSSL includes. I put the prototypes here to be independent
 
// from the OpenSSL source installation. Having this, mongoose + SSL can be
 
// built on any system with binary SSL libraries installed.
 
typedef struct ssl_st SSL;
 
typedef struct ssl_method_st SSL_METHOD;
 
typedef struct ssl_ctx_st SSL_CTX;
 
 
#define SSL_ERROR_WANT_READ 2
 
#define SSL_ERROR_WANT_WRITE 3
 
#define SSL_FILETYPE_PEM 1
 
#define CRYPTO_LOCK  1
 
 
#if defined(NO_SSL_DL)
 
extern void SSL_free(SSL *);
 
extern int SSL_accept(SSL *);
 
extern int SSL_connect(SSL *);
 
extern int SSL_read(SSL *, void *, int);
 
extern int SSL_write(SSL *, const void *, int);
 
extern int SSL_get_error(const SSL *, int);
 
extern int SSL_set_fd(SSL *, int);
 
extern SSL *SSL_new(SSL_CTX *);
 
extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);
 
extern SSL_METHOD *SSLv23_server_method(void);
 
extern SSL_METHOD *SSLv23_client_method(void);
 
extern int SSL_library_init(void);
 
extern void SSL_load_error_strings(void);
 
extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
 
extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);
 
extern int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
 
extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_t);
 
extern void SSL_CTX_free(SSL_CTX *);
 
extern unsigned long ERR_get_error(void);
 
extern char *ERR_error_string(unsigned long, char *);
 
extern int CRYPTO_num_locks(void);
 
extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));
 
extern void CRYPTO_set_id_callback(unsigned long (*)(void));
 
#else
 
// Dynamically loaded SSL functionality
 
struct ssl_func {
 
  const char *name;   // SSL function name
 
  void  (*ptr)(void); // Function pointer
 
};
 
 
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
 
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
 
#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
 
#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
 
#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
 
#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
 
#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
 
#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
 
#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
 
#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
 
#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
 
#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
 
        const char *, int)) ssl_sw[11].ptr)
 
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
 
        const char *, int)) ssl_sw[12].ptr)
 
#define SSL_CTX_set_default_passwd_cb \
 
  (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)
 
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
 
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
 
#define SSL_CTX_use_certificate_chain_file \
 
  (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
 
#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
 
 
#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
 
#define CRYPTO_set_locking_callback \
 
  (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
 
#define CRYPTO_set_id_callback \
 
  (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
 
#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
 
#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
 
 
// set_ssl_option() function updates this array.
 
// It loads SSL library dynamically and changes NULLs to the actual addresses
 
// of respective functions. The macros above (like SSL_connect()) are really
 
// just calling these functions indirectly via the pointer.
 
static struct ssl_func ssl_sw[] = {
 
  {"SSL_free",   NULL},
 
  {"SSL_accept",   NULL},
 
  {"SSL_connect",   NULL},
 
  {"SSL_read",   NULL},
 
  {"SSL_write",   NULL},
 
  {"SSL_get_error",  NULL},
 
  {"SSL_set_fd",   NULL},
 
  {"SSL_new",   NULL},
 
  {"SSL_CTX_new",   NULL},
 
  {"SSLv23_server_method", NULL},
 
  {"SSL_library_init",  NULL},
 
  {"SSL_CTX_use_PrivateKey_file", NULL},
 
  {"SSL_CTX_use_certificate_file",NULL},
 
  {"SSL_CTX_set_default_passwd_cb",NULL},
 
  {"SSL_CTX_free",  NULL},
 
  {"SSL_load_error_strings", NULL},
 
  {"SSL_CTX_use_certificate_chain_file", NULL},
 
  {"SSLv23_client_method", NULL},
 
  {NULL,    NULL}
 
};
 
 
// Similar array as ssl_sw. These functions could be located in different lib.
 
#if !defined(NO_SSL)
 
static struct ssl_func crypto_sw[] = {
 
  {"CRYPTO_num_locks",  NULL},
 
  {"CRYPTO_set_locking_callback", NULL},
 
  {"CRYPTO_set_id_callback", NULL},
 
  {"ERR_get_error",  NULL},
 
  {"ERR_error_string", NULL},
 
  {NULL,    NULL}
 
};
 
#endif // NO_SSL
 
#endif // NO_SSL_DL
 
 
static const char *month_names[] = {
 
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
};
 
 
// Unified socket address. For IPv6 support, add IPv6 address structure
 
// in the union u.
 
union usa {
 
  struct sockaddr sa;
 
  struct sockaddr_in sin;
 
#if defined(USE_IPV6)
 
  struct sockaddr_in6 sin6;
 
#endif
 
};
 
 
// Describes a string (chunk of memory).
 
struct vec {
 
  const char *ptr;
 
  size_t len;
 
};
 
 
// Structure used by mg_stat() function. Uses 64 bit file length.
 
struct mgstat {
 
  int is_directory;  // Directory marker
 
  int64_t size;      // File size
 
  time_t mtime;      // Modification time
 
};
 
 
// Describes listening socket, or socket which was accept()-ed by the master
 
// thread and queued for future handling by the worker thread.
 
struct socket {
 
  struct socket *next;  // Linkage
 
  SOCKET sock;          // Listening socket
 
  union usa lsa;        // Local socket address
 
  union usa rsa;        // Remote socket address
 
  int is_ssl;           // Is socket SSL-ed
 
};
 
 
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
 
enum {
 
  CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
 
  MAX_REQUEST_SIZE, PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS,
 
  ACCESS_LOG_FILE, SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
 
  GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
 
  EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
 
  NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES,
 
  NUM_OPTIONS
 
};
 
 
static const char *config_options[] = {
 
  "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
 
  "E", "cgi_environment", NULL,
 
  "G", "put_delete_passwords_file", NULL,
 
  "I", "cgi_interpreter", NULL,
 
  "M", "max_request_size", "16384",
 
  "P", "protect_uri", NULL,
 
  "R", "authentication_domain", "mydomain.com",
 
  "S", "ssi_pattern", "**.shtml$|**.shtm$",
 
  "a", "access_log_file", NULL,
 
  "c", "ssl_chain_file", NULL,
 
  "d", "enable_directory_listing", "yes",
 
  "e", "error_log_file", NULL,
 
  "g", "global_passwords_file", NULL,
 
  "i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
 
  "k", "enable_keep_alive", "no",
 
  "l", "access_control_list", NULL,
 
  "m", "extra_mime_types", NULL,
 
  "p", "listening_ports", "8080",
 
  "r", "document_root",  ".",
 
  "s", "ssl_certificate", NULL,
 
  "t", "num_threads", "10",
 
  "u", "run_as_user", NULL,
 
  "w", "url_rewrite_patterns", NULL,
 
  "x", "hide_files_patterns", NULL,
 
  NULL
 
};
 
#define ENTRIES_PER_CONFIG_OPTION 3
 
 
struct mg_context {
 
  volatile int stop_flag;       // Should we stop event loop
 
  SSL_CTX *ssl_ctx;             // SSL context
 
  SSL_CTX *client_ssl_ctx;      // Client SSL context
 
  char *config[NUM_OPTIONS];    // Mongoose configuration parameters
 
  mg_callback_t user_callback;  // User-defined callback function
 
  void *user_data;              // User-defined data
 
 
  struct socket *listening_sockets;
 
 
  volatile int num_threads;  // Number of threads
 
  pthread_mutex_t mutex;     // Protects (max|num)_threads
 
  pthread_cond_t  cond;      // Condvar for tracking workers terminations
 
 
  struct socket queue[20];   // Accepted sockets
 
  volatile int sq_head;      // Head of the socket queue
 
  volatile int sq_tail;      // Tail of the socket queue
 
  pthread_cond_t sq_full;    // Signaled when socket is produced
 
  pthread_cond_t sq_empty;   // Signaled when socket is consumed
 
};
 
 
struct mg_connection {
 
  struct mg_request_info request_info;
 
  struct mg_context *ctx;
 
  SSL *ssl;                   // SSL descriptor
 
  struct socket client;       // Connected client
 
  time_t birth_time;          // Time when request was received
 
  int64_t num_bytes_sent;     // Total bytes sent to client
 
  int64_t content_len;        // Content-Length header value
 
  int64_t consumed_content;   // How many bytes of content have been read
 
  char *buf;                  // Buffer for received data
 
  char *path_info;            // PATH_INFO part of the URL
 
  char *body;                 // Pointer to not-read yet buffered body data
 
  char *next_request;         // Pointer to the buffered next request
 
  int must_close;             // 1 if connection must be closed
 
  int buf_size;               // Buffer size
 
  int request_len;            // Size of the request + headers in a buffer
 
  int data_len;               // Total size of data in a buffer
 
};
 
 
const char **mg_get_valid_option_names(void) {
 
  return config_options;
 
}
 
 
static void *call_user(struct mg_connection *conn, enum mg_event event) {
 
  conn->request_info.user_data = conn->ctx->user_data;
 
  return conn->ctx->user_callback == NULL ? NULL :
 
    conn->ctx->user_callback(event, conn);
 
}
 
 
static int get_option_index(const char *name) {
 
  int i;
 
 
  for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
 
    if (strcmp(config_options[i], name) == 0 ||
 
        strcmp(config_options[i + 1], name) == 0) {
 
      return i / ENTRIES_PER_CONFIG_OPTION;
 
    }
 
  }
 
  return -1;
 
}
 
 
const char *mg_get_option(const struct mg_context *ctx, const char *name) {
 
  int i;
 
  if ((i = get_option_index(name)) == -1) {
 
    return NULL;
 
  } else if (ctx->config[i] == NULL) {
 
    return "";
 
  } else {
 
    return ctx->config[i];
 
  }
 
}
 
 
static void sockaddr_to_string(char *buf, size_t len,
 
                                     const union usa *usa) {
 
  buf[0] = '\0';
 
#if defined(USE_IPV6)
 
  inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
 
            (void *) &usa->sin.sin_addr :
 
            (void *) &usa->sin6.sin6_addr, buf, len);
 
#elif defined(_WIN32)
 
  // Only Windoze Vista (and newer) have inet_ntop()
 
  strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
 
#else
 
  inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
 
#endif
 
}
 
 
// Print error message to the opened error log stream.
 
static void cry(struct mg_connection *conn, const char *fmt, ...) {
 
  char buf[MG_BUF_LEN], src_addr[20];
 
  va_list ap;
 
  FILE *fp;
 
  time_t timestamp;
 
 
  va_start(ap, fmt);
 
  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
 
  va_end(ap);
 
 
  // Do not lock when getting the callback value, here and below.
 
  // I suppose this is fine, since function cannot disappear in the
 
  // same way string option can.
 
  conn->request_info.log_message = buf;
 
  if (call_user(conn, MG_EVENT_LOG) == NULL) {
 
    fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
 
      mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
 
 
    if (fp != NULL) {
 
      flockfile(fp);
 
      timestamp = time(NULL);
 
 
      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 
      fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
 
              src_addr);
 
 
      if (conn->request_info.request_method != NULL) {
 
        fprintf(fp, "%s %s: ", conn->request_info.request_method,
 
                conn->request_info.uri);
 
      }
 
 
      (void) fprintf(fp, "%s", buf);
 
      fputc('\n', fp);
 
      funlockfile(fp);
 
      if (fp != stderr) {
 
        fclose(fp);
 
      }
 
    }
 
  }
 
  conn->request_info.log_message = NULL;
 
}
 
 
// Return fake connection structure. Used for logging, if connection
 
// is not applicable at the moment of logging.
 
static struct mg_connection *fc(struct mg_context *ctx) {
 
  static struct mg_connection fake_connection;
 
  fake_connection.ctx = ctx;
 
  return &fake_connection;
 
}
 
 
const char *mg_version(void) {
 
  return MONGOOSE_VERSION;
 
}
 
 
const struct mg_request_info *
 
mg_get_request_info(const struct mg_connection *conn) {
 
  return &conn->request_info;
 
}
 
 
static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
 
  for (; *src != '\0' && n > 1; n--) {
 
    *dst++ = *src++;
 
  }
 
  *dst = '\0';
 
}
 
 
static int lowercase(const char *s) {
 
  return tolower(* (const unsigned char *) s);
 
}
 
 
static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
 
  int diff = 0;
 
 
  if (len > 0)
 
    do {
 
      diff = lowercase(s1++) - lowercase(s2++);
 
    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
 
 
  return diff;
 
}
 
 
static int mg_strcasecmp(const char *s1, const char *s2) {
 
  int diff;
 
 
  do {
 
    diff = lowercase(s1++) - lowercase(s2++);
 
  } while (diff == 0 && s1[-1] != '\0');
 
 
  return diff;
 
}
 
 
static char * mg_strndup(const char *ptr, size_t len) {
 
  char *p;
 
 
  if ((p = (char *) malloc(len + 1)) != NULL) {
 
    mg_strlcpy(p, ptr, len + 1);
 
  }
 
 
  return p;
 
}
 
 
static char * mg_strdup(const char *str) {
 
  return mg_strndup(str, strlen(str));
 
}
 
 
// Like snprintf(), but never returns negative value, or a value
 
// that is larger than a supplied buffer.
 
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
 
// in his audit report.
 
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
 
                        const char *fmt, va_list ap) {
 
  int n;
 
 
  if (buflen == 0)
 
    return 0;
 
 
  n = vsnprintf(buf, buflen, fmt, ap);
 
 
  if (n < 0) {
 
    cry(conn, "vsnprintf error");
 
    n = 0;
 
  } else if (n >= (int) buflen) {
 
    cry(conn, "truncating vsnprintf buffer: [%.*s]",
 
        n > 200 ? 200 : n, buf);
 
    n = (int) buflen - 1;
 
  }
 
  buf[n] = '\0';
 
 
  return n;
 
}
 
 
static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
 
                       const char *fmt, ...) {
 
  va_list ap;
 
  int n;
 
 
  va_start(ap, fmt);
 
  n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
 
  va_end(ap);
 
 
  return n;
 
}
 
 
// Skip the characters until one of the delimiters characters found.
 
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
 
// Advance pointer to buffer to the next word. Return found 0-terminated word.
 
// Delimiters can be quoted with quotechar.
 
static char *skip_quoted(char **buf, const char *delimiters,
 
                         const char *whitespace, char quotechar) {
 
  char *p, *begin_word, *end_word, *end_whitespace;
 
 
  begin_word = *buf;
 
  end_word = begin_word + strcspn(begin_word, delimiters);
 
 
  // Check for quotechar
 
  if (end_word > begin_word) {
 
    p = end_word - 1;
 
    while (*p == quotechar) {
 
      // If there is anything beyond end_word, copy it
 
      if (*end_word == '\0') {
 
        *p = '\0';
 
        break;
 
      } else {
 
        size_t end_off = strcspn(end_word + 1, delimiters);
 
        memmove (p, end_word, end_off + 1);
 
        p += end_off; // p must correspond to end_word - 1
 
        end_word += end_off + 1;
 
      }
 
    }
 
    for (p++; p < end_word; p++) {
 
      *p = '\0';
 
    }
 
  }
 
 
  if (*end_word == '\0') {
 
    *buf = end_word;
 
  } else {
 
    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
 
 
    for (p = end_word; p < end_whitespace; p++) {
 
      *p = '\0';
 
    }
 
 
    *buf = end_whitespace;
 
  }
 
 
  return begin_word;
 
}
 
 
// Simplified version of skip_quoted without quote char
 
// and whitespace == delimiters
 
static char *skip(char **buf, const char *delimiters) {
 
  return skip_quoted(buf, delimiters, delimiters, 0);
 
}
 
 
 
// Return HTTP header value, or NULL if not found.
 
static const char *get_header(const struct mg_request_info *ri,
 
                              const char *name) {
 
  int i;
 
 
  for (i = 0; i < ri->num_headers; i++)
 
    if (!mg_strcasecmp(name, ri->http_headers[i].name))
 
      return ri->http_headers[i].value;
 
 
  return NULL;
 
}
 
 
const char *mg_get_header(const struct mg_connection *conn, const char *name) {
 
  return get_header(&conn->request_info, name);
 
}
 
 
// A helper function for traversing a comma separated list of values.
 
// It returns a list pointer shifted to the next value, or NULL if the end
 
// of the list found.
 
// Value is stored in val vector. If value has form "x=y", then eq_val
 
// vector is initialized to point to the "y" part, and val vector length
 
// is adjusted to point only to "x".
 
static const char *next_option(const char *list, struct vec *val,
 
                               struct vec *eq_val) {
 
  if (list == NULL || *list == '\0') {
 
    // End of the list
 
    list = NULL;
 
  } else {
 
    val->ptr = list;
 
    if ((list = strchr(val->ptr, ',')) != NULL) {
 
      // Comma found. Store length and shift the list ptr
 
      val->len = list - val->ptr;
 
      list++;
 
    } else {
 
      // This value is the last one
 
      list = val->ptr + strlen(val->ptr);
 
      val->len = list - val->ptr;
 
    }
 
 
    if (eq_val != NULL) {
 
      // Value has form "x=y", adjust pointers and lengths
 
      // so that val points to "x", and eq_val points to "y".
 
      eq_val->len = 0;
 
      eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
 
      if (eq_val->ptr != NULL) {
 
        eq_val->ptr++;  // Skip over '=' character
 
        eq_val->len = val->ptr + val->len - eq_val->ptr;
 
        val->len = (eq_val->ptr - val->ptr) - 1;
 
      }
 
    }
 
  }
 
 
  return list;
 
}
 
 
static int match_prefix(const char *pattern, int pattern_len, const char *str) {
 
  const char *or_str;
 
  int i, j, len, res;
 
 
  if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
 
    res = match_prefix(pattern, or_str - pattern, str);
 
    return res > 0 ? res :
 
        match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
 
  }
 
 
  i = j = 0;
 
  res = -1;
 
  for (; i < pattern_len; i++, j++) {
 
    if (pattern[i] == '?' && str[j] != '\0') {
 
      continue;
 
    } else if (pattern[i] == '$') {
 
      return str[j] == '\0' ? j : -1;
 
    } else if (pattern[i] == '*') {
 
      i++;
 
      if (pattern[i] == '*') {
 
        i++;
 
        len = (int) strlen(str + j);
 
      } else {
 
        len = (int) strcspn(str + j, "/");
 
      }
 
      if (i == pattern_len) {
 
        return j + len;
 
      }
 
      do {
 
        res = match_prefix(pattern + i, pattern_len - i, str + j + len);
 
      } while (res == -1 && len-- > 0);
 
      return res == -1 ? -1 : j + res + len;
 
    } else if (pattern[i] != str[j]) {
 
      return -1;
 
    }
 
  }
 
  return j;
 
}
 
 
// HTTP 1.1 assumes keep alive if "Connection:" header is not set
 
// This function must tolerate situations when connection info is not
 
// set up, for example if request parsing failed.
 
static int should_keep_alive(const struct mg_connection *conn) {
 
  const char *http_version = conn->request_info.http_version;
 
  const char *header = mg_get_header(conn, "Connection");
 
  if (conn->must_close ||
 
      conn->request_info.status_code == 401 ||
 
      mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
 
      (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
 
      (header == NULL && http_version && strcmp(http_version, "1.1"))) {
 
    return 0;
 
  }
 
  return 1;
 
}
 
 
static const char *suggest_connection_header(const struct mg_connection *conn) {
 
  return should_keep_alive(conn) ? "keep-alive" : "close";
 
}
 
 
static void send_http_error(struct mg_connection *, int, const char *,
 
                            PRINTF_FORMAT_STRING(const char *fmt), ...)
 
  PRINTF_ARGS(4, 5);
 
 
 
static void send_http_error(struct mg_connection *conn, int status,
 
                            const char *reason, const char *fmt, ...) {
 
  char buf[MG_BUF_LEN];
 
  va_list ap;
 
  int len;
 
 
  conn->request_info.status_code = status;
 
 
  if (call_user(conn, MG_HTTP_ERROR) == NULL) {
 
    buf[0] = '\0';
 
    len = 0;
 
 
    // Errors 1xx, 204 and 304 MUST NOT send a body
 
    if (status > 199 && status != 204 && status != 304) {
 
      len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
 
      buf[len++] = '\n';
 
 
      va_start(ap, fmt);
 
      len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
 
      va_end(ap);
 
    }
 
    DEBUG_TRACE(("[%s]", buf));
 
 
    mg_printf(conn, "HTTP/1.1 %d %s\r\n"
 
              "Content-Type: text/plain\r\n"
 
              "Content-Length: %d\r\n"
 
              "Connection: %s\r\n\r\n", status, reason, len,
 
              suggest_connection_header(conn));
 
    conn->num_bytes_sent += mg_printf(conn, "%s", buf);
 
  }
 
}
 
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
 
  unused = NULL;
 
  *mutex = CreateMutex(NULL, FALSE, NULL);
 
  return *mutex == NULL ? -1 : 0;
 
}
 
 
static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
 
  return CloseHandle(*mutex) == 0 ? -1 : 0;
 
}
 
 
static int pthread_mutex_lock(pthread_mutex_t *mutex) {
 
  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
 
}
 
 
static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
 
  return ReleaseMutex(*mutex) == 0 ? -1 : 0;
 
}
 
 
static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
 
  unused = NULL;
 
  cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
 
  cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
 
  return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
 
}
 
 
static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
 
  HANDLE handles[] = {cv->signal, cv->broadcast};
 
  ReleaseMutex(*mutex);
 
  WaitForMultipleObjects(2, handles, FALSE, INFINITE);
 
  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
 
}
 
 
static int pthread_cond_signal(pthread_cond_t *cv) {
 
  return SetEvent(cv->signal) == 0 ? -1 : 0;
 
}
 
 
static int pthread_cond_broadcast(pthread_cond_t *cv) {
 
  // Implementation with PulseEvent() has race condition, see
 
  // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
 
  return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
 
}
 
 
static int pthread_cond_destroy(pthread_cond_t *cv) {
 
  return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
 
}
 
 
// For Windows, change all slashes to backslashes in path names.
 
static void change_slashes_to_backslashes(char *path) {
 
  int i;
 
 
  for (i = 0; path[i] != '\0'; i++) {
 
    if (path[i] == '/')
 
      path[i] = '\\';
 
    // i > 0 check is to preserve UNC paths, like \\server\file.txt
 
    if (path[i] == '\\' && i > 0)
 
      while (path[i + 1] == '\\' || path[i + 1] == '/')
 
        (void) memmove(path + i + 1,
 
            path + i + 2, strlen(path + i + 1));
 
  }
 
}
 
 
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
 
// wbuf and wbuf_len is a target buffer and its length.
 
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
 
  char buf[PATH_MAX], buf2[PATH_MAX], *p;
 
 
  mg_strlcpy(buf, path, sizeof(buf));
 
  change_slashes_to_backslashes(buf);
 
 
  // Point p to the end of the file name
 
  p = buf + strlen(buf) - 1;
 
 
  // Trim trailing backslash character
 
  while (p > buf && *p == '\\' && p[-1] != ':') {
 
    *p-- = '\0';
 
  }
 
 
   // Protect from CGI code disclosure.
 
   // This is very nasty hole. Windows happily opens files with
 
   // some garbage in the end of file name. So fopen("a.cgi    ", "r")
 
   // actually opens "a.cgi", and does not return an error!
 
  if (*p == 0x20 ||               // No space at the end
 
      (*p == 0x2e && p > buf) ||  // No '.' but allow '.' as full path
 
      *p == 0x2b ||               // No '+'
 
      (*p & ~0x7f)) {             // And generally no non-ASCII chars
 
    (void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
 
    wbuf[0] = L'\0';
 
  } else {
 
    // Convert to Unicode and back. If doubly-converted string does not
 
    // match the original, something is fishy, reject.
 
    memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
 
    MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
 
    WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
 
                        NULL, NULL);
 
    if (strcmp(buf, buf2) != 0) {
 
      wbuf[0] = L'\0';
 
    }
 
  }
 
}
 
 
#if defined(_WIN32_WCE)
 
static time_t time(time_t *ptime) {
 
  time_t t;
 
  SYSTEMTIME st;
 
  FILETIME ft;
 
 
  GetSystemTime(&st);
 
  SystemTimeToFileTime(&st, &ft);
 
  t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
 
 
  if (ptime != NULL) {
 
    *ptime = t;
 
  }
 
 
  return t;
 
}
 
 
static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
 
  int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
 
  FILETIME ft, lft;
 
  SYSTEMTIME st;
 
  TIME_ZONE_INFORMATION tzinfo;
 
 
  if (ptm == NULL) {
 
    return NULL;
 
  }
 
 
  * (int64_t *) &ft = t;
 
  FileTimeToLocalFileTime(&ft, &lft);
 
  FileTimeToSystemTime(&lft, &st);
 
  ptm->tm_year = st.wYear - 1900;
 
  ptm->tm_mon = st.wMonth - 1;
 
  ptm->tm_wday = st.wDayOfWeek;
 
  ptm->tm_mday = st.wDay;
 
  ptm->tm_hour = st.wHour;
 
  ptm->tm_min = st.wMinute;
 
  ptm->tm_sec = st.wSecond;
 
  ptm->tm_yday = 0; // hope nobody uses this
 
  ptm->tm_isdst =
 
    GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
 
 
  return ptm;
 
}
 
 
static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
 
  // FIXME(lsm): fix this.
 
  return localtime(ptime, ptm);
 
}
 
 
static size_t strftime(char *dst, size_t dst_size, const char *fmt,
 
                       const struct tm *tm) {
 
  (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
 
  return 0;
 
}
 
#endif
 
 
static int mg_rename(const char* oldname, const char* newname) {
 
  wchar_t woldbuf[PATH_MAX];
 
  wchar_t wnewbuf[PATH_MAX];
 
 
  to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf));
 
  to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf));
 
 
  return MoveFileW(woldbuf, wnewbuf) ? 0 : -1;
 
}
 
 
 
static FILE *mg_fopen(const char *path, const char *mode) {
 
  wchar_t wbuf[PATH_MAX], wmode[20];
 
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 
  MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
 
 
  return _wfopen(wbuf, wmode);
 
}
 
 
static int mg_stat(const char *path, struct mgstat *stp) {
 
  int ok = -1; // Error
 
  wchar_t wbuf[PATH_MAX];
 
  WIN32_FILE_ATTRIBUTE_DATA info;
 
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 
 
  if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
 
    stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
 
    stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
 
                               info.ftLastWriteTime.dwHighDateTime);
 
    stp->is_directory =
 
      info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
 
    ok = 0;  // Success
 
  }
 
 
  return ok;
 
}
 
 
static int mg_remove(const char *path) {
 
  wchar_t wbuf[PATH_MAX];
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 
  return DeleteFileW(wbuf) ? 0 : -1;
 
}
 
 
static int mg_mkdir(const char *path, int mode) {
 
  char buf[PATH_MAX];
 
  wchar_t wbuf[PATH_MAX];
 
 
  mode = 0; // Unused
 
  mg_strlcpy(buf, path, sizeof(buf));
 
  change_slashes_to_backslashes(buf);
 
 
  (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
 
 
  return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
 
}
 
 
// Implementation of POSIX opendir/closedir/readdir for Windows.
 
static DIR * opendir(const char *name) {
 
  DIR *dir = NULL;
 
  wchar_t wpath[PATH_MAX];
 
  DWORD attrs;
 
 
  if (name == NULL) {
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
 
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
 
  } else {
 
    to_unicode(name, wpath, ARRAY_SIZE(wpath));
 
    attrs = GetFileAttributesW(wpath);
 
    if (attrs != 0xFFFFFFFF &&
 
        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
 
      (void) wcscat(wpath, L"\\*");
 
      dir->handle = FindFirstFileW(wpath, &dir->info);
 
      dir->result.d_name[0] = '\0';
 
    } else {
 
      free(dir);
 
      dir = NULL;
 
    }
 
  }
 
 
  return dir;
 
}
 
 
static int closedir(DIR *dir) {
 
  int result = 0;
 
 
  if (dir != NULL) {
 
    if (dir->handle != INVALID_HANDLE_VALUE)
 
      result = FindClose(dir->handle) ? 0 : -1;
 
 
    free(dir);
 
  } else {
 
    result = -1;
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  }
 
 
  return result;
 
}
 
 
static struct dirent *readdir(DIR *dir) {
 
  struct dirent *result = 0;
 
 
  if (dir) {
 
    if (dir->handle != INVALID_HANDLE_VALUE) {
 
      result = &dir->result;
 
      (void) WideCharToMultiByte(CP_UTF8, 0,
 
          dir->info.cFileName, -1, result->d_name,
 
          sizeof(result->d_name), NULL, NULL);
 
 
      if (!FindNextFileW(dir->handle, &dir->info)) {
 
        (void) FindClose(dir->handle);
 
        dir->handle = INVALID_HANDLE_VALUE;
 
      }
 
 
    } else {
 
      SetLastError(ERROR_FILE_NOT_FOUND);
 
    }
 
  } else {
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  }
 
 
  return result;
 
}
 
 
#define set_close_on_exec(fd) // No FD_CLOEXEC on Windows
 
 
int mg_start_thread(mg_thread_func_t f, void *p) {
 
  return _beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
 
}
 
 
static HANDLE dlopen(const char *dll_name, int flags) {
 
  wchar_t wbuf[PATH_MAX];
 
  flags = 0; // Unused
 
  to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
 
  return LoadLibraryW(wbuf);
 
}
 
 
#if !defined(NO_CGI)
 
#define SIGKILL 0
 
static int kill(pid_t pid, int sig_num) {
 
  (void) TerminateProcess(pid, sig_num);
 
  (void) CloseHandle(pid);
 
  return 0;
 
}
 
 
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
 
                           char *envblk, char *envp[], int fd_stdin,
 
                           int fd_stdout, const char *dir) {
 
  HANDLE me;
 
  char *p, *interp, cmdline[PATH_MAX], buf[PATH_MAX];
 
  FILE *fp;
 
  STARTUPINFOA si = { sizeof(si) };
 
  PROCESS_INFORMATION pi = { 0 };
 
 
  envp = NULL; // Unused
 
 
  // TODO(lsm): redirect CGI errors to the error log file
 
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 
  si.wShowWindow = SW_HIDE;
 
 
  me = GetCurrentProcess();
 
  (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
 
      &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
 
  (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,
 
      &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
 
 
  // If CGI file is a script, try to read the interpreter line
 
  interp = conn->ctx->config[CGI_INTERPRETER];
 
  if (interp == NULL) {
 
    buf[2] = '\0';
 
    mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%c%s", dir, DIRSEP, prog);
 
    if ((fp = fopen(cmdline, "r")) != NULL) {
 
      (void) fgets(buf, sizeof(buf), fp);
 
      if (buf[0] != '#' || buf[1] != '!') {
 
        // First line does not start with "#!". Do not set interpreter.
 
        buf[2] = '\0';
 
      } else {
 
        // Trim whitespace in interpreter name
 
        for (p = &buf[strlen(buf) - 1]; p > buf && isspace(*p); p--) {
 
          *p = '\0';
 
        }
 
      }
 
      (void) fclose(fp);
 
    }
 
    interp = buf + 2;
 
  }
 
 
  (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s%c%s",
 
                     interp, interp[0] == '\0' ? "" : " ", dir, DIRSEP, prog);
 
 
  DEBUG_TRACE(("Running [%s]", cmdline));
 
  if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
 
        CREATE_NEW_PROCESS_GROUP, envblk, dir, &si, &pi) == 0) {
 
    cry(conn, "%s: CreateProcess(%s): %d",
 
        __func__, cmdline, ERRNO);
 
    pi.hProcess = (pid_t) -1;
 
  }
 
 
  // Always close these to prevent handle leakage.
 
  (void) close(fd_stdin);
 
  (void) close(fd_stdout);
 
 
  (void) CloseHandle(si.hStdOutput);
 
  (void) CloseHandle(si.hStdInput);
 
  (void) CloseHandle(pi.hThread);
 
 
  return (pid_t) pi.hProcess;
 
}
 
#endif // !NO_CGI
 
 
static int set_non_blocking_mode(SOCKET sock) {
 
  unsigned long on = 1;
 
  return ioctlsocket(sock, FIONBIO, &on);
 
}
 
 
#else
 
static int mg_stat(const char *path, struct mgstat *stp) {
 
  struct stat st;
 
  int ok;
 
 
  if (stat(path, &st) == 0) {
 
    ok = 0;
 
    stp->size = st.st_size;
 
    stp->mtime = st.st_mtime;
 
    stp->is_directory = S_ISDIR(st.st_mode);
 
  } else {
 
    ok = -1;
 
  }
 
 
  return ok;
 
}
 
 
static void set_close_on_exec(int fd) {
 
  (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 
}
 
 
int mg_start_thread(mg_thread_func_t func, void *param) {
 
  pthread_t thread_id;
 
  pthread_attr_t attr;
 
 
  (void) pthread_attr_init(&attr);
 
  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
  // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
 
  // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
 
 
  return pthread_create(&thread_id, &attr, func, param);
 
}
 
 
#ifndef NO_CGI
 
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
 
                           char *envblk, char *envp[], int fd_stdin,
 
                           int fd_stdout, const char *dir) {
 
  pid_t pid;
 
  const char *interp;
 
 
  envblk = NULL; // Unused
 
 
  if ((pid = fork()) == -1) {
 
    // Parent
 
    send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
 
  } else if (pid == 0) {
 
    // Child
 
    if (chdir(dir) != 0) {
 
      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
 
    } else if (dup2(fd_stdin, 0) == -1) {
 
      cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO));
 
    } else if (dup2(fd_stdout, 1) == -1) {
 
      cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO));
 
    } else {
 
      (void) dup2(fd_stdout, 2);
 
      (void) close(fd_stdin);
 
      (void) close(fd_stdout);
 
 
      interp = conn->ctx->config[CGI_INTERPRETER];
 
      if (interp == NULL) {
 
        (void) execle(prog, prog, NULL, envp);
 
        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
 
      } else {
 
        (void) execle(interp, interp, prog, NULL, envp);
 
        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
 
            strerror(ERRNO));
 
      }
 
    }
 
    exit(EXIT_FAILURE);
 
  }
 
 
  // Parent. Close stdio descriptors
 
  (void) close(fd_stdin);
 
  (void) close(fd_stdout);
 
 
  return pid;
 
}
 
#endif // !NO_CGI
 
 
static int set_non_blocking_mode(SOCKET sock) {
 
  int flags;
 
 
  flags = fcntl(sock, F_GETFL, 0);
 
  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
 
 
  return 0;
 
}
 
#endif // _WIN32
 
 
// Write data to the IO channel - opened file descriptor, socket or SSL
 
// descriptor. Return number of bytes written.
 
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
 
                    int64_t len) {
 
  int64_t sent;
 
  int n, k;
 
 
  sent = 0;
 
  while (sent < len) {
 
 
    // How many bytes we send in this iteration
 
    k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
 
 
    if (ssl != NULL) {
 
      n = SSL_write(ssl, buf + sent, k);
 
    } else if (fp != NULL) {
 
      n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
 
      if (ferror(fp))
 
        n = -1;
 
    } else {
 
      n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
 
    }
 
 
    if (n < 0)
 
      break;
 
 
    sent += n;
 
  }
 
 
  return sent;
 
}
 
 
// This function is needed to prevent Mongoose to be stuck in a blocking
 
// socket read when user requested exit. To do that, we sleep in select
 
// with a timeout, and when returned, check the context for the stop flag.
 
// If it is set, we return 0, and this means that we must not continue
 
// reading, must give up and close the connection and exit serving thread.
 
static int wait_until_socket_is_readable(struct mg_connection *conn) {
 
  int result;
 
  struct timeval tv;
 
  fd_set set;
 
 
  do {
 
    tv.tv_sec = 0;
 
    tv.tv_usec = 300 * 1000;
 
    FD_ZERO(&set);
 
    FD_SET(conn->client.sock, &set);
 
    result = select(conn->client.sock + 1, &set, NULL, NULL, &tv);
 
  } while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
 
           conn->ctx->stop_flag == 0);
 
 
  return conn->ctx->stop_flag || result < 0 ? 0 : 1;
 
}
 
 
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
 
// Return negative value on error, or number of bytes read on success.
 
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
 
  int nread;
 
 
  if (fp != NULL) {
 
    // Use read() instead of fread(), because if we're reading from the CGI
 
    // pipe, fread() may block until IO buffer is filled up. We cannot afford
 
    // to block and must pass all read bytes immediately to the client.
 
    nread = read(fileno(fp), buf, (size_t) len);
 
  } else if (!wait_until_socket_is_readable(conn)) {
 
    nread = -1;
 
  } else if (conn->ssl != NULL) {
 
    nread = SSL_read(conn->ssl, buf, len);
 
  } else {
 
    nread = recv(conn->client.sock, buf, (size_t) len, 0);
 
  }
 
 
  return conn->ctx->stop_flag ? -1 : nread;
 
}
 
 
int mg_read(struct mg_connection *conn, void *buf, size_t len) {
 
  int n, buffered_len, nread;
 
 
  assert(conn->next_request != NULL &&
 
         conn->body != NULL &&
 
         conn->next_request >= conn->body);
 
  nread = 0;
 
  if (conn->consumed_content < conn->content_len) {
 
 
    // Adjust number of bytes to read.
 
    int64_t to_read = conn->content_len - conn->consumed_content;
 
    if (to_read < (int64_t) len) {
 
      len = (size_t) to_read;
 
    }
 
 
    // Return buffered data
 
    buffered_len = conn->next_request - conn->body;
 
    if (buffered_len > 0) {
 
      if (len < (size_t) buffered_len) {
 
        buffered_len = (int) len;
 
      }
 
      memcpy(buf, conn->body, (size_t) buffered_len);
 
      len -= buffered_len;
 
      conn->body += buffered_len;
 
      conn->consumed_content += buffered_len;
 
      nread += buffered_len;
 
      buf = (char *) buf + buffered_len;
 
    }
 
 
    // We have returned all buffered data. Read new data from the remote socket.
 
    while (len > 0) {
 
      n = pull(NULL, conn, (char *) buf, (int) len);
 
      if (n < 0) {
 
        nread = n;  // Propagate the error
 
        break;
 
      } else if (n == 0) {
 
        break;  // No more data to read
 
      } else {
 
        buf = (char *) buf + n;
 
        conn->consumed_content += n;
 
        nread += n;
 
        len -= n;
 
      }
 
    }
 
  }
 
  return nread;
 
}
 
 
int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
 
  return (int) push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
 
                    (int64_t) len);
 
}
 
 
int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
 
  char mem[MG_BUF_LEN], *buf = mem;
 
  int len;
 
  va_list ap;
 
 
  // Print in a local buffer first, hoping that it is large enough to
 
  // hold the whole message
 
  va_start(ap, fmt);
 
  len = vsnprintf(mem, sizeof(mem), fmt, ap);
 
  va_end(ap);
 
 
  if (len <= 0) {
 
    // vsnprintf() error, give up
 
    len = -1;
 
    cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
 
  } else if (len > (int) sizeof(mem) && (buf = malloc(len + 1)) != NULL) {
 
    // Local buffer is not large enough, allocate big buffer on heap
 
    va_start(ap, fmt);
 
    vsnprintf(buf, len + 1, fmt, ap);
 
    va_end(ap);
 
    len = mg_write(conn, buf, (size_t) len);
 
    free(buf);
 
  } else if (len > (int) sizeof(mem)) {
 
    // Failed to allocate large enough buffer, give up
 
    cry(conn, "%s(%s, ...): Can't allocate %d bytes, not printing anything",
 
        __func__, fmt, len);
 
    len = -1;
 
  } else {
 
    // Copy to the local buffer succeeded
 
    len = mg_write(conn, buf, (size_t) len);
 
  }
 
 
  return len;
 
}
 
 
// URL-decode input buffer into destination buffer.
 
// 0-terminate the destination buffer. Return the length of decoded data.
 
// form-url-encoded data differs from URI encoding in a way that it
 
// uses '+' as character for space, see RFC 1866 section 8.2.1
 
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
 
static size_t url_decode(const char *src, size_t src_len, char *dst,
 
                         size_t dst_len, int is_form_url_encoded) {
 
  size_t i, j;
 
  int a, b;
 
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
 
 
  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
 
    if (src[i] == '%' &&
 
        isxdigit(* (const unsigned char *) (src + i + 1)) &&
 
        isxdigit(* (const unsigned char *) (src + i + 2))) {
 
      a = tolower(* (const unsigned char *) (src + i + 1));
 
      b = tolower(* (const unsigned char *) (src + i + 2));
 
      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
 
      i += 2;
 
    } else if (is_form_url_encoded && src[i] == '+') {
 
      dst[j] = ' ';
 
    } else {
 
      dst[j] = src[i];
 
    }
 
  }
 
 
  dst[j] = '\0'; // Null-terminate the destination
 
 
  return j;
 
}
 
 
// Scan given buffer and fetch the value of the given variable.
 
// It can be specified in query string, or in the POST data.
 
// Return -1 if the variable not found, or length of the URL-decoded value
 
// stored in dst. The dst buffer is guaranteed to be NUL-terminated if it
 
// is not NULL or zero-length. If dst is NULL or zero-length, then
 
// -2 is returned.
 
int mg_get_var(const char *buf, size_t buf_len, const char *name,
 
               char *dst, size_t dst_len) {
 
  const char *p, *e, *s;
 
  size_t name_len;
 
  int len;
 
 
  if (dst == NULL || dst_len == 0) {
 
    len = -2;
 
  } else if (buf == NULL || name == NULL || buf_len == 0) {
 
    len = -1;
 
    dst[0] = '\0';
 
  } else {
 
    name_len = strlen(name);
 
    e = buf + buf_len;
 
    len = -1;
 
    dst[0] = '\0';
 
 
    // buf is "var1=val1&var2=val2...". Find variable first
 
    for (p = buf; p + name_len < e; p++) {
 
      if ((p == buf || p[-1] == '&') && p[name_len] == '=' &&
 
          !mg_strncasecmp(name, p, name_len)) {
 
 
        // Point p to variable value
 
        p += name_len + 1;
 
 
        // Point s to the end of the value
 
        s = (const char *) memchr(p, '&', (size_t)(e - p));
 
        if (s == NULL) {
 
          s = e;
 
        }
 
        assert(s >= p);
 
 
        // Decode variable into destination buffer
 
        if ((size_t) (s - p) < dst_len) {
 
          len = (int) url_decode(p, (size_t)(s - p), dst, dst_len, 1);
 
        }
 
        break;
 
      }
 
    }
 
  }
 
 
  return len;
 
}
 
 
int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,
 
                  char *dst, size_t dst_size) {
 
  const char *s, *p, *end;
 
  int name_len, len = -1;
 
 
  dst[0] = '\0';
 
  if ((s = mg_get_header(conn, "Cookie")) == NULL) {
 
    return -1;
 
  }
 
 
  name_len = (int) strlen(cookie_name);
 
  end = s + strlen(s);
 
 
  for (; (s = strstr(s, cookie_name)) != NULL; s += name_len)
 
    if (s[name_len] == '=') {
 
      s += name_len + 1;
 
      if ((p = strchr(s, ' ')) == NULL)
 
        p = end;
 
      if (p[-1] == ';')
 
        p--;
 
      if (*s == '"' && p[-1] == '"' && p > s + 1) {
 
        s++;
 
        p--;
 
      }
 
      if ((size_t) (p - s) < dst_size) {
 
        len = p - s;
 
        mg_strlcpy(dst, s, (size_t) len + 1);
 
      }
 
      break;
 
    }
 
 
  return len;
 
}
 
 
static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
 
                                    size_t buf_len, struct mgstat *st) {
 
  struct vec a, b;
 
  const char *rewrite, *uri = conn->request_info.uri;
 
  char *p;
 
  int match_len, stat_result;
 
 
  buf_len--;  // This is because memmove() for PATH_INFO may shift part
 
              // of the path one byte on the right.
 
  mg_snprintf(conn, buf, buf_len, "%s%s", conn->ctx->config[DOCUMENT_ROOT],
 
              uri);
 
 
  rewrite = conn->ctx->config[REWRITE];
 
  while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
 
    if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
 
      mg_snprintf(conn, buf, buf_len, "%.*s%s", b.len, b.ptr, uri + match_len);
 
      break;
 
    }
 
  }
 
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  //change_slashes_to_backslashes(buf);
 
#endif // _WIN32
 
 
  if ((stat_result = mg_stat(buf, st)) != 0) {
 
    // Support PATH_INFO for CGI scripts.
 
    for (p = buf + strlen(buf); p > buf + 1; p--) {
 
      if (*p == '/') {
 
        *p = '\0';
 
        if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
 
                         strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
 
            (stat_result = mg_stat(buf, st)) == 0) {
 
          // Shift PATH_INFO block one character right, e.g.
 
          //  "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
 
          // conn->path_info is pointing to the local variable "path" declared
 
          // in handle_request(), so PATH_INFO is not valid after
 
          // handle_request returns.
 
          conn->path_info = p + 1;
 
          memmove(p + 2, p + 1, strlen(p + 1) + 1);  // +1 is for trailing \0
 
          p[1] = '/';
 
          break;
 
        } else {
 
          *p = '/';
 
          stat_result = -1;
 
        }
 
      }
 
    }
 
  }
 
 
  return stat_result;
 
}
 
 
static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
 
  return (conn->ssl = SSL_new(s)) != NULL &&
 
    SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
 
    func(conn->ssl) == 1;
 
}
 
 
// Check whether full request is buffered. Return:
 
//   -1  if request is malformed
 
//    0  if request is not yet fully buffered
 
//   >0  actual request length, including last \r\n\r\n
 
static int get_request_len(const char *buf, int buflen) {
 
  const char *s, *e;
 
  int len = 0;
 
 
  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
 
    // Control characters are not allowed but >=128 is.
 
    if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
 
        *s != '\n' && * (const unsigned char *) s < 128) {
 
      len = -1;
 
      break; // [i_a] abort scan as soon as one malformed character is found; don't let subsequent \r\n\r\n win us over anyhow
 
    } else if (s[0] == '\n' && s[1] == '\n') {
 
      len = (int) (s - buf) + 2;
 
    } else if (s[0] == '\n' && &s[1] < e &&
 
        s[1] == '\r' && s[2] == '\n') {
 
      len = (int) (s - buf) + 3;
 
    }
 
 
  return len;
 
}
 
 
// Convert month to the month number. Return -1 on error, or month number
 
static int get_month_index(const char *s) {
 
  size_t i;
 
 
  for (i = 0; i < ARRAY_SIZE(month_names); i++)
 
    if (!strcmp(s, month_names[i]))
 
      return (int) i;
 
 
  return -1;
 
}
 
 
// Parse UTC date-time string, and return the corresponding time_t value.
 
static time_t parse_date_string(const char *datetime) {
 
  static const unsigned short days_before_month[] = {
 
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
 
  };
 
  char month_str[32];
 
  int second, minute, hour, day, month, year, leap_days, days;
 
  time_t result = (time_t) 0;
 
 
  if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%d %3s %d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6)) &&
 
      year > 1970 &&
 
      (month = get_month_index(month_str)) != -1) {
 
    year -= 1970;
 
    leap_days = year / 4 - year / 100 + year / 400;
 
    days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
 
    result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
 
  }
 
 
  return result;
 
}
 
 
// Protect against directory disclosure attack by removing '..',
 
// excessive '/' and '\' characters
 
static void remove_double_dots_and_double_slashes(char *s) {
 
  char *p = s;
 
 
  while (*s != '\0') {
 
    *p++ = *s++;
 
    if (IS_DIRSEP_CHAR(s[-1])) {
 
      // Skip all following slashes and backslashes
 
      while (IS_DIRSEP_CHAR(s[0])) {
 
        s++;
 
      }
 
 
      // Skip all double-dots
 
      while (*s == '.' && s[1] == '.') {
 
        s += 2;
 
      }
 
    }
 
  }
 
  *p = '\0';
 
}
 
 
static const struct {
 
  const char *extension;
 
  size_t ext_len;
 
  const char *mime_type;
 
} builtin_mime_types[] = {
 
  {".html", 5, "text/html"},
 
  {".htm", 4, "text/html"},
 
  {".shtm", 5, "text/html"},
 
  {".shtml", 6, "text/html"},
 
  {".css", 4, "text/css"},
 
  {".js",  3, "application/x-javascript"},
 
  {".ico", 4, "image/x-icon"},
 
  {".gif", 4, "image/gif"},
 
  {".jpg", 4, "image/jpeg"},
 
  {".jpeg", 5, "image/jpeg"},
 
  {".png", 4, "image/png"},
 
  {".svg", 4, "image/svg+xml"},
 
  {".txt", 4, "text/plain"},
 
  {".torrent", 8, "application/x-bittorrent"},
 
  {".wav", 4, "audio/x-wav"},
 
  {".mp3", 4, "audio/x-mp3"},
 
  {".mid", 4, "audio/mid"},
 
  {".m3u", 4, "audio/x-mpegurl"},
 
  {".ram", 4, "audio/x-pn-realaudio"},
 
  {".xml", 4, "text/xml"},
 
  {".json",  5, "text/json"},
 
  {".xslt", 5, "application/xml"},
 
  {".ra",  3, "audio/x-pn-realaudio"},
 
  {".doc", 4, "application/msword"},
 
  {".exe", 4, "application/octet-stream"},
 
  {".zip", 4, "application/x-zip-compressed"},
 
  {".xls", 4, "application/excel"},
 
  {".tgz", 4, "application/x-tar-gz"},
 
  {".tar", 4, "application/x-tar"},
 
  {".gz",  3, "application/x-gunzip"},
 
  {".arj", 4, "application/x-arj-compressed"},
 
  {".rar", 4, "application/x-arj-compressed"},
 
  {".rtf", 4, "application/rtf"},
 
  {".pdf", 4, "application/pdf"},
 
  {".swf", 4, "application/x-shockwave-flash"},
 
  {".mpg", 4, "video/mpeg"},
 
  {".webm", 5, "video/webm"},
 
  {".mpeg", 5, "video/mpeg"},
 
  {".mp4", 4, "video/mp4"},
 
  {".m4v", 4, "video/x-m4v"},
 
  {".asf", 4, "video/x-ms-asf"},
 
  {".avi", 4, "video/x-msvideo"},
 
  {".bmp", 4, "image/bmp"},
 
  {NULL,  0, NULL}
 
};
 
 
const char *mg_get_builtin_mime_type(const char *path) {
 
  const char *ext;
 
  size_t i, path_len;
 
 
  path_len = strlen(path);
 
 
  for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
 
    ext = path + (path_len - builtin_mime_types[i].ext_len);
 
    if (path_len > builtin_mime_types[i].ext_len &&
 
        mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
 
      return builtin_mime_types[i].mime_type;
 
    }
 
  }
 
 
  return "text/plain";
 
}
 
 
// Look at the "path" extension and figure what mime type it has.
 
// Store mime type in the vector.
 
static void get_mime_type(struct mg_context *ctx, const char *path,
 
                          struct vec *vec) {
 
  struct vec ext_vec, mime_vec;
 
  const char *list, *ext;
 
  size_t path_len;
 
 
  path_len = strlen(path);
 
 
  // Scan user-defined mime types first, in case user wants to
 
  // override default mime types.
 
  list = ctx->config[EXTRA_MIME_TYPES];
 
  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
 
    // ext now points to the path suffix
 
    ext = path + path_len - ext_vec.len;
 
    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
 
      *vec = mime_vec;
 
      return;
 
    }
 
  }
 
 
  vec->ptr = mg_get_builtin_mime_type(path);
 
  vec->len = strlen(vec->ptr);
 
}
 
 
#ifndef HAVE_MD5
 
typedef struct MD5Context {
 
  uint32_t buf[4];
 
  uint32_t bits[2];
 
  unsigned char in[64];
 
} MD5_CTX;
 
 
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == 1234)
 
#define byteReverse(buf, len) // Do nothing
 
#else
 
static void byteReverse(unsigned char *buf, unsigned longs) {
 
  uint32_t t;
 
  do {
 
    t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
 
      ((unsigned) buf[1] << 8 | buf[0]);
 
    *(uint32_t *) buf = t;
 
    buf += 4;
 
  } while (--longs);
 
}
 
#endif
 
 
#define F1(x, y, z) (z ^ (x & (y ^ z)))
 
#define F2(x, y, z) F1(z, x, y)
 
#define F3(x, y, z) (x ^ y ^ z)
 
#define F4(x, y, z) (y ^ (x | ~z))
 
 
#define MD5STEP(f, w, x, y, z, data, s) \
 
  ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
 
 
// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 
// initialization constants.
 
static void MD5Init(MD5_CTX *ctx) {
 
  ctx->buf[0] = 0x67452301;
 
  ctx->buf[1] = 0xefcdab89;
 
  ctx->buf[2] = 0x98badcfe;
 
  ctx->buf[3] = 0x10325476;
 
 
  ctx->bits[0] = 0;
 
  ctx->bits[1] = 0;
 
}
 
 
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
 
  register uint32_t a, b, c, d;
 
 
  a = buf[0];
 
  b = buf[1];
 
  c = buf[2];
 
  d = buf[3];
 
 
  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
 
  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
 
  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
 
  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
 
  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
 
  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
 
  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
 
  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
 
  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
 
  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
 
  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
 
  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
 
  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
 
  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
 
  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
 
  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
 
 
  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
 
  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
 
  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
 
  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
 
  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
 
  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
 
  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
 
  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
 
  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
 
  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
 
  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
 
  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
 
  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
 
  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
 
  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
 
  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
 
 
  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
 
  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
 
  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
 
  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
 
  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
 
  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
 
  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
 
  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
 
  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
 
  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
 
  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
 
  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
 
  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
 
  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
 
  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
 
  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
 
 
  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
 
  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
 
  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
 
  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
 
  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
 
  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
 
  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
 
  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
 
  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
 
  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
 
  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
 
  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
 
  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
 
  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
 
  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
 
  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
 
 
  buf[0] += a;
 
  buf[1] += b;
 
  buf[2] += c;
 
  buf[3] += d;
 
}
 
 
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
 
  uint32_t t;
 
 
  t = ctx->bits[0];
 
  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
 
    ctx->bits[1]++;
 
  ctx->bits[1] += len >> 29;
 
 
  t = (t >> 3) & 0x3f;
 
 
  if (t) {
 
    unsigned char *p = (unsigned char *) ctx->in + t;
 
 
    t = 64 - t;
 
    if (len < t) {
 
      memcpy(p, buf, len);
 
      return;
 
    }
 
    memcpy(p, buf, t);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    buf += t;
 
    len -= t;
 
  }
 
 
  while (len >= 64) {
 
    memcpy(ctx->in, buf, 64);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    buf += 64;
 
    len -= 64;
 
  }
 
 
  memcpy(ctx->in, buf, len);
 
}
 
 
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
 
  unsigned count;
 
  unsigned char *p;
 
 
  count = (ctx->bits[0] >> 3) & 0x3F;
 
 
  p = ctx->in + count;
 
  *p++ = 0x80;
 
  count = 64 - 1 - count;
 
  if (count < 8) {
 
    memset(p, 0, count);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    memset(ctx->in, 0, 56);
 
  } else {
 
    memset(p, 0, count - 8);
 
  }
 
  byteReverse(ctx->in, 14);
 
 
  ((uint32_t *) ctx->in)[14] = ctx->bits[0];
 
  ((uint32_t *) ctx->in)[15] = ctx->bits[1];
 
 
  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
  byteReverse((unsigned char *) ctx->buf, 4);
 
  memcpy(digest, ctx->buf, 16);
 
  memset((char *) ctx, 0, sizeof(*ctx));
 
}
 
#endif // !HAVE_MD5
 
 
// Stringify binary data. Output buffer must be twice as big as input,
 
// because each byte takes 2 bytes in string representation
 
static void bin2str(char *to, const unsigned char *p, size_t len) {
 
  static const char *hex = "0123456789abcdef";
 
 
  for (; len--; p++) {
 
    *to++ = hex[p[0] >> 4];
 
    *to++ = hex[p[0] & 0x0f];
 
  }
 
  *to = '\0';
 
}
 
 
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
 
void mg_md5(char buf[33], ...) {
 
  unsigned char hash[16];
 
  const char *p;
 
  va_list ap;
 
  MD5_CTX ctx;
 
 
  MD5Init(&ctx);
 
 
  va_start(ap, buf);
 
  while ((p = va_arg(ap, const char *)) != NULL) {
 
    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
 
  }
 
  va_end(ap);
 
 
  MD5Final(hash, &ctx);
 
  bin2str(buf, hash, sizeof(hash));
 
}
 
 
// Check the user's password, return 1 if OK
 
static int check_password(const char *method, const char *ha1, const char *uri,
 
                          const char *nonce, const char *nc, const char *cnonce,
 
                          const char *qop, const char *response) {
 
  char ha2[32 + 1], expected_response[32 + 1];
 
 
  // Some of the parameters may be NULL
 
  if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
 
      qop == NULL || response == NULL) {
 
    return 0;
 
  }
 
 
  // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
 
  // TODO(lsm): check for authentication timeout
 
  if (// strcmp(dig->uri, c->ouri) != 0 ||
 
      strlen(response) != 32
 
      // || now - strtoul(dig->nonce, NULL, 10) > 3600
 
      ) {
 
    return 0;
 
  }
 
 
  mg_md5(ha2, method, ":", uri, NULL);
 
  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
 
      ":", cnonce, ":", qop, ":", ha2, NULL);
 
 
  return mg_strcasecmp(response, expected_response) == 0;
 
}
 
 
// Use the global passwords file, if specified by auth_gpass option,
 
// or search for .htpasswd in the requested directory.
 
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
 
  struct mg_context *ctx = conn->ctx;
 
  char name[PATH_MAX];
 
  const char *p, *e;
 
  struct mgstat st;
 
  FILE *fp;
 
 
  if (ctx->config[GLOBAL_PASSWORDS_FILE] != NULL) {
 
    // Use global passwords file
 
    fp =  mg_fopen(ctx->config[GLOBAL_PASSWORDS_FILE], "r");
 
    if (fp == NULL)
 
      cry(fc(ctx), "fopen(%s): %s",
 
          ctx->config[GLOBAL_PASSWORDS_FILE], strerror(ERRNO));
 
  } else if (!mg_stat(path, &st) && st.is_directory) {
 
    (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s",
 
        path, DIRSEP, PASSWORDS_FILE_NAME);
 
    fp = mg_fopen(name, "r");
 
  } else {
 
     // Try to find .htpasswd in requested directory.
 
    for (p = path, e = p + strlen(p) - 1; e > p; e--)
 
      if (IS_DIRSEP_CHAR(*e))
 
        break;
 
    (void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s",
 
        (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
 
    fp = mg_fopen(name, "r");
 
  }
 
 
  return fp;
 
}
 
 
// Parsed Authorization header
 
struct ah {
 
  char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
 
};
 
 
// Return 1 on success. Always initializes the ah structure.
 
static int parse_auth_header(struct mg_connection *conn, char *buf,
 
                             size_t buf_size, struct ah *ah) {
 
  char *name, *value, *s;
 
  const char *auth_header;
 
 
  (void) memset(ah, 0, sizeof(*ah));
 
  if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
 
      mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
 
    return 0;
 
  }
 
 
  // Make modifiable copy of the auth header
 
  (void) mg_strlcpy(buf, auth_header + 7, buf_size);
 
  s = buf;
 
 
  // Parse authorization header
 
  for (;;) {
 
    // Gobble initial spaces
 
    while (isspace(* (unsigned char *) s)) {
 
      s++;
 
    }
 
    name = skip_quoted(&s, "=", " ", 0);
 
    // Value is either quote-delimited, or ends at first comma or space.
 
    if (s[0] == '\"') {
 
      s++;
 
      value = skip_quoted(&s, "\"", " ", '\\');
 
      if (s[0] == ',') {
 
        s++;
 
      }
 
    } else {
 
      value = skip_quoted(&s, ", ", " ", 0);  // IE uses commas, FF uses spaces
 
    }
 
    if (*name == '\0') {
 
      break;
 
    }
 
 
    if (!strcmp(name, "username")) {
 
      ah->user = value;
 
    } else if (!strcmp(name, "cnonce")) {
 
      ah->cnonce = value;
 
    } else if (!strcmp(name, "response")) {
 
      ah->response = value;
 
    } else if (!strcmp(name, "uri")) {
 
      ah->uri = value;
 
    } else if (!strcmp(name, "qop")) {
 
      ah->qop = value;
 
    } else if (!strcmp(name, "nc")) {
 
      ah->nc = value;
 
    } else if (!strcmp(name, "nonce")) {
 
      ah->nonce = value;
 
    }
 
  }
 
 
  // CGI needs it as REMOTE_USER
 
  if (ah->user != NULL) {
 
    conn->request_info.remote_user = mg_strdup(ah->user);
 
  } else {
 
    return 0;
 
  }
 
 
  return 1;
 
}
 
 
// Authorize against the opened passwords file. Return 1 if authorized.
 
static int authorize(struct mg_connection *conn, FILE *fp) {
 
  struct ah ah;
 
  char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
 
 
  if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
 
    return 0;
 
  }
 
 
  // Loop over passwords file
 
  while (fgets(line, sizeof(line), fp) != NULL) {
 
    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
 
      continue;
 
    }
 
 
    if (!strcmp(ah.user, f_user) &&
 
        !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
 
      return check_password(
 
            conn->request_info.request_method,
 
            ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop,
 
            ah.response);
 
  }
 
 
  return 0;
 
}
 
 
// Return 1 if request is authorised, 0 otherwise.
 
static int check_authorization(struct mg_connection *conn, const char *path) {
 
  FILE *fp;
 
  char fname[PATH_MAX];
 
  struct vec uri_vec, filename_vec;
 
  const char *list;
 
  int authorized;
 
 
  fp = NULL;
 
  authorized = 1;
 
 
  list = conn->ctx->config[PROTECT_URI];
 
  while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
 
    if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
 
      (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s",
 
          filename_vec.len, filename_vec.ptr);
 
      if ((fp = mg_fopen(fname, "r")) == NULL) {
 
        cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
 
      }
 
      break;
 
    }
 
  }
 
 
  if (fp == NULL) {
 
    fp = open_auth_file(conn, path);
 
  }
 
 
  if (fp != NULL) {
 
    authorized = authorize(conn, fp);
 
    (void) fclose(fp);
 
  }
 
 
  return authorized;
 
}
 
 
static void send_authorization_request(struct mg_connection *conn) {
 
  conn->request_info.status_code = 401;
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 401 Unauthorized\r\n"
 
      "Content-Length: 0\r\n"
 
      "WWW-Authenticate: Digest qop=\"auth\", "
 
      "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
 
      conn->ctx->config[AUTHENTICATION_DOMAIN],
 
      (unsigned long) time(NULL));
 
}
 
 
static int is_authorized_for_put(struct mg_connection *conn) {
 
  FILE *fp;
 
  int ret = 0;
 
 
  fp = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ? NULL :
 
    mg_fopen(conn->ctx->config[PUT_DELETE_PASSWORDS_FILE], "r");
 
 
  if (fp != NULL) {
 
    ret = authorize(conn, fp);
 
    (void) fclose(fp);
 
  }
 
 
  return ret;
 
}
 
 
int mg_modify_passwords_file(const char *fname, const char *domain,
 
                             const char *user, const char *pass) {
 
  int found;
 
  char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
 
  FILE *fp, *fp2;
 
 
  found = 0;
 
  fp = fp2 = NULL;
 
 
  // Regard empty password as no password - remove user record.
 
  if (pass != NULL && pass[0] == '\0') {
 
    pass = NULL;
 
  }
 
 
  (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
 
 
  // Create the file if does not exist
 
  if ((fp = mg_fopen(fname, "a+")) != NULL) {
 
    (void) fclose(fp);
 
  }
 
 
  // Open the given file and temporary file
 
  if ((fp = mg_fopen(fname, "r")) == NULL) {
 
    return 0;
 
  } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) {
 
    fclose(fp);
 
    return 0;
 
  }
 
 
  // Copy the stuff to temporary file
 
  while (fgets(line, sizeof(line), fp) != NULL) {
 
    if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
 
      continue;
 
    }
 
 
    if (!strcmp(u, user) && !strcmp(d, domain)) {
 
      found++;
 
      if (pass != NULL) {
 
        mg_md5(ha1, user, ":", domain, ":", pass, NULL);
 
        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
 
      }
 
    } else {
 
      (void) fprintf(fp2, "%s", line);
 
    }
 
  }
 
 
  // If new user, just add it
 
  if (!found && pass != NULL) {
 
    mg_md5(ha1, user, ":", domain, ":", pass, NULL);
 
    (void) fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
 
  }
 
 
  // Close files
 
  (void) fclose(fp);
 
  (void) fclose(fp2);
 
 
  // Put the temp file in place of real file
 
  (void) mg_remove(fname);
 
  (void) mg_rename(tmp, fname);
 
 
  return 1;
 
}
 
 
struct de {
 
  struct mg_connection *conn;
 
  char *file_name;
 
  struct mgstat st;
 
};
 
 
static void url_encode(const char *src, char *dst, size_t dst_len) {
 
  static const char *dont_escape = "._-$,;~()";
 
  static const char *hex = "0123456789abcdef";
 
  const char *end = dst + dst_len - 1;
 
 
  for (; *src != '\0' && dst < end; src++, dst++) {
 
    if (isalnum(*(const unsigned char *) src) ||
 
        strchr(dont_escape, * (const unsigned char *) src) != NULL) {
 
      *dst = *src;
 
    } else if (dst + 2 < end) {
 
      dst[0] = '%';
 
      dst[1] = hex[(* (const unsigned char *) src) >> 4];
 
      dst[2] = hex[(* (const unsigned char *) src) & 0xf];
 
      dst += 2;
 
    }
 
  }
 
 
  *dst = '\0';
 
}
 
 
static void print_dir_entry(struct de *de) {
 
  char size[64], mod[64], href[PATH_MAX];
 
 
  if (de->st.is_directory) {
 
    (void) mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]");
 
  } else {
 
     // We use (signed) cast below because MSVC 6 compiler cannot
 
     // convert unsigned __int64 to double. Sigh.
 
    if (de->st.size < 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%lu", (unsigned long) de->st.size);
 
    } else if (de->st.size < 1024 * 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fk", (double) de->st.size / 1024.0);
 
    } else if (de->st.size < 1024 * 1024 * 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fM", (double) de->st.size / 1048576);
 
    } else {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fG", (double) de->st.size / 1073741824);
 
    }
 
  }
 
  (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.mtime));
 
  url_encode(de->file_name, href, sizeof(href));
 
  de->conn->num_bytes_sent += mg_printf(de->conn,
 
      "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
 
      "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
 
      de->conn->request_info.uri, href, de->st.is_directory ? "/" : "",
 
      de->file_name, de->st.is_directory ? "/" : "", mod, size);
 
}
 
 
// This function is called from send_directory() and used for
 
// sorting directory entries by size, or name, or modification time.
 
// On windows, __cdecl specification is needed in case if project is built
 
// with __stdcall convention. qsort always requires __cdels callback.
 
static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
 
  const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
 
  const char *query_string = a->conn->request_info.query_string;
 
  int cmp_result = 0;
 
 
  if (query_string == NULL) {
 
    query_string = "na";
 
  }
 
 
  if (a->st.is_directory && !b->st.is_directory) {
 
    return -1;  // Always put directories on top
 
  } else if (!a->st.is_directory && b->st.is_directory) {
 
    return 1;   // Always put directories on top
 
  } else if (*query_string == 'n') {
 
    cmp_result = strcmp(a->file_name, b->file_name);
 
  } else if (*query_string == 's') {
 
    cmp_result = a->st.size == b->st.size ? 0 :
 
      a->st.size > b->st.size ? 1 : -1;
 
  } else if (*query_string == 'd') {
 
    cmp_result = a->st.mtime == b->st.mtime ? 0 :
 
      a->st.mtime > b->st.mtime ? 1 : -1;
 
  }
 
 
  return query_string[1] == 'd' ? -cmp_result : cmp_result;
 
}
 
 
static int must_hide_file(struct mg_connection *conn, const char *path) {
 
  const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
 
  const char *pattern = conn->ctx->config[HIDE_FILES];
 
  return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
 
    (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
 
}
 
 
static int scan_directory(struct mg_connection *conn, const char *dir,
 
                          void *data, void (*cb)(struct de *, void *)) {
 
  char path[PATH_MAX];
 
  struct dirent *dp;
 
  DIR *dirp;
 
  struct de de;
 
 
  if ((dirp = opendir(dir)) == NULL) {
 
    return 0;
 
  } else {
 
    de.conn = conn;
 
 
    while ((dp = readdir(dirp)) != NULL) {
 
      // Do not show current dir and hidden files
 
      if (!strcmp(dp->d_name, ".") ||
 
          !strcmp(dp->d_name, "..") ||
 
          must_hide_file(conn, dp->d_name)) {
 
        continue;
 
      }
 
 
      mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, DIRSEP, dp->d_name);
 
 
      // If we don't memset stat structure to zero, mtime will have
 
      // garbage and strftime() will segfault later on in
 
      // print_dir_entry(). memset is required only if mg_stat()
 
      // fails. For more details, see
 
      // http://code.google.com/p/mongoose/issues/detail?id=79
 
      if (mg_stat(path, &de.st) != 0) {
 
        memset(&de.st, 0, sizeof(de.st));
 
      }
 
      de.file_name = dp->d_name;
 
 
      cb(&de, data);
 
    }
 
    (void) closedir(dirp);
 
  }
 
  return 1;
 
}
 
 
struct dir_scan_data {
 
  struct de *entries;
 
  int num_entries;
 
  int arr_size;
 
};
 
 
static void dir_scan_callback(struct de *de, void *data) {
 
  struct dir_scan_data *dsd = (struct dir_scan_data *) data;
 
 
  if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
 
    dsd->arr_size *= 2;
 
    dsd->entries = (struct de *) realloc(dsd->entries, dsd->arr_size *
 
                                         sizeof(dsd->entries[0]));
 
  }
 
  if (dsd->entries == NULL) {
 
    // TODO(lsm): propagate an error to the caller
 
    dsd->num_entries = 0;
 
  } else {
 
    dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
 
    dsd->entries[dsd->num_entries].st = de->st;
 
    dsd->entries[dsd->num_entries].conn = de->conn;
 
    dsd->num_entries++;
 
  }
 
}
 
 
static void handle_directory_request(struct mg_connection *conn,
 
                                     const char *dir) {
 
  int i, sort_direction;
 
  struct dir_scan_data data = { NULL, 0, 128 };
 
 
  if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
 
    send_http_error(conn, 500, "Cannot open directory",
 
                    "Error: opendir(%s): %s", dir, strerror(ERRNO));
 
    return;
 
  }
 
 
  sort_direction = conn->request_info.query_string != NULL &&
 
    conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
 
 
  conn->must_close = 1;
 
  mg_printf(conn, "%s",
 
            "HTTP/1.1 200 OK\r\n"
 
            "Connection: close\r\n"
 
            "Content-Type: text/html; charset=utf-8\r\n\r\n");
 
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<html><head><title>Index of %s</title>"
 
      "<style>th {text-align: left;}</style></head>"
 
      "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
 
      "<tr><th><a href=\"?n%c\">Name</a></th>"
 
      "<th><a href=\"?d%c\">Modified</a></th>"
 
      "<th><a href=\"?s%c\">Size</a></th></tr>"
 
      "<tr><td colspan=\"3\"><hr></td></tr>",
 
      conn->request_info.uri, conn->request_info.uri,
 
      sort_direction, sort_direction, sort_direction);
 
 
  // Print first entry - link to a parent directory
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<tr><td><a href=\"%s%s\">%s</a></td>"
 
      "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
 
      conn->request_info.uri, "..", "Parent directory", "-", "-");
 
 
  // Sort and print directory entries
 
  qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
 
        compare_dir_entries);
 
  for (i = 0; i < data.num_entries; i++) {
 
    print_dir_entry(&data.entries[i]);
 
    free(data.entries[i].file_name);
 
  }
 
  free(data.entries);
 
 
  conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
 
  conn->request_info.status_code = 200;
 
}
 
 
// Send len bytes from the opened file to the client.
 
static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t len) {
 
  char buf[MG_BUF_LEN];
 
  int to_read, num_read, num_written;
 
 
  while (len > 0) {
 
    // Calculate how much to read from the file in the buffer
 
    to_read = sizeof(buf);
 
    if ((int64_t) to_read > len) {
 
      to_read = (int) len;
 
    }
 
 
    // Read from file, exit the loop on error
 
    if ((num_read = fread(buf, 1, (size_t)to_read, fp)) <= 0) {
 
      break;
 
    }
 
 
    // Send read bytes to the client, exit the loop on error
 
    if ((num_written = mg_write(conn, buf, (size_t)num_read)) != num_read) {
 
      break;
 
    }
 
 
    // Both read and were successful, adjust counters
 
    conn->num_bytes_sent += num_written;
 
    len -= num_written;
 
  }
 
}
 
 
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
 
  return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
 
}
 
 
static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
 
  strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
 
}
 
 
static void construct_etag(char *buf, size_t buf_len,
 
                           const struct mgstat *stp) {
 
  snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
 
           (unsigned long) stp->mtime, stp->size);
 
}
 
 
static void handle_file_request(struct mg_connection *conn, const char *path,
 
                                struct mgstat *stp) {
 
  char date[64], lm[64], etag[64], range[64];
 
  const char *msg = "OK", *hdr;
 
  time_t curtime = time(NULL);
 
  int64_t cl, r1, r2;
 
  struct vec mime_vec;
 
  FILE *fp;
 
  int n;
 
 
  get_mime_type(conn->ctx, path, &mime_vec);
 
  cl = stp->size;
 
  conn->request_info.status_code = 200;
 
  range[0] = '\0';
 
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen(%s): %s", path, strerror(ERRNO));
 
    return;
 
  }
 
  set_close_on_exec(fileno(fp));
 
 
  // If Range: header specified, act accordingly
 
  r1 = r2 = 0;
 
  hdr = mg_get_header(conn, "Range");
 
  if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0) {
 
    conn->request_info.status_code = 206;
 
    (void) fseeko(fp, r1, SEEK_SET);
 
    cl = n == 2 ? r2 - r1 + 1: cl - r1;
 
    (void) mg_snprintf(conn, range, sizeof(range),
 
        "Content-Range: bytes "
 
        "%" INT64_FMT "-%"
 
        INT64_FMT "/%" INT64_FMT "\r\n",
 
        r1, r1 + cl - 1, stp->size);
 
    msg = "Partial Content";
 
  }
 
 
  // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
 
  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
 
  gmt_time_string(date, sizeof(date), &curtime);
 
  gmt_time_string(lm, sizeof(lm), &stp->mtime);
 
  construct_etag(etag, sizeof(etag), stp);
 
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 %d %s\r\n"
 
      "Date: %s\r\n"
 
      "Last-Modified: %s\r\n"
 
      "Etag: %s\r\n"
 
      "Content-Type: %.*s\r\n"
 
      "Content-Length: %" INT64_FMT "\r\n"
 
      "Connection: %s\r\n"
 
      "Accept-Ranges: bytes\r\n"
 
      "%s\r\n",
 
      conn->request_info.status_code, msg, date, lm, etag, (int) mime_vec.len,
 
      mime_vec.ptr, cl, suggest_connection_header(conn), range);
 
 
  if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
 
    send_file_data(conn, fp, cl);
 
  }
 
  (void) fclose(fp);
 
}
 
 
void mg_send_file(struct mg_connection *conn, const char *path) {
 
  struct mgstat st;
 
  if (mg_stat(path, &st) == 0) {
 
    handle_file_request(conn, path, &st);
 
  } else {
 
    send_http_error(conn, 404, "Not Found", "%s", "File not found");
 
  }
 
}
 
 
 
// Parse HTTP headers from the given buffer, advance buffer to the point
 
// where parsing stopped.
 
static void parse_http_headers(char **buf, struct mg_request_info *ri) {
 
  int i;
 
 
  for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
 
    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
 
    ri->http_headers[i].value = skip(buf, "\r\n");
 
    if (ri->http_headers[i].name[0] == '\0')
 
      break;
 
    ri->num_headers = i + 1;
 
  }
 
}
 
 
static int is_valid_http_method(const char *method) {
 
  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
 
    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
 
    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
 
    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND");
 
}
 
 
// Parse HTTP request, fill in mg_request_info structure.
 
// This function modifies the buffer by NUL-terminating
 
// HTTP request components, header names and header values.
 
static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
 
  int request_length = get_request_len(buf, len);
 
  if (request_length > 0) {
 
    // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
 
    ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
 
    ri->num_headers = 0;
 
    ri->status_code = -1;
 
 
    buf[request_length - 1] = '\0';
 
 
    // RFC says that all initial whitespaces should be ingored
 
    while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
 
      buf++;
 
    }
 
    ri->request_method = skip(&buf, " ");
 
    ri->uri = skip(&buf, " ");
 
    ri->http_version = skip(&buf, "\r\n");
 
    parse_http_headers(&buf, ri);
 
  }
 
  return request_length;
 
}
 
 
static int parse_http_request(char *buf, int len, struct mg_request_info *ri) {
 
  int result = parse_http_message(buf, len, ri);
 
  if (result > 0 &&
 
      is_valid_http_method(ri->request_method) &&
 
      !strncmp(ri->http_version, "HTTP/", 5)) {
 
    ri->http_version += 5;   // Skip "HTTP/"
 
  } else {
 
    result = -1;
 
  }
 
  return result;
 
}
 
 
static int parse_http_response(char *buf, int len, struct mg_request_info *ri) {
 
  int result = parse_http_message(buf, len, ri);
 
  return result > 0 && !strncmp(ri->request_method, "HTTP/", 5) ? result : -1;
 
}
 
 
// Keep reading the input (either opened file descriptor fd, or socket sock,
 
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
 
// buffer (which marks the end of HTTP request). Buffer buf may already
 
// have some data. The length of the data is stored in nread.
 
// Upon every read operation, increase nread by the number of bytes read.
 
static int read_request(FILE *fp, struct mg_connection *conn,
 
                        char *buf, int bufsiz, int *nread) {
 
  int request_len, n = 1;
 
 
  request_len = get_request_len(buf, *nread);
 
  while (*nread < bufsiz && request_len == 0 && n > 0) {
 
    n = pull(fp, conn, buf + *nread, bufsiz - *nread);
 
    if (n > 0) {
 
      *nread += n;
 
      request_len = get_request_len(buf, *nread);
 
    }
 
  }
 
 
  if (n < 0) {
 
    // recv() error -> propagate error; do not process a b0rked-with-very-high-probability request
 
    return -1;
 
  }
 
  return request_len;
 
}
 
 
// For given directory path, substitute it to valid index file.
 
// Return 0 if index file has been found, -1 if not found.
 
// If the file is found, it's stats is returned in stp.
 
static int substitute_index_file(struct mg_connection *conn, char *path,
 
                                 size_t path_len, struct mgstat *stp) {
 
  const char *list = conn->ctx->config[INDEX_FILES];
 
  struct mgstat st;
 
  struct vec filename_vec;
 
  size_t n = strlen(path);
 
  int found = 0;
 
 
  // The 'path' given to us points to the directory. Remove all trailing
 
  // directory separator characters from the end of the path, and
 
  // then append single directory separator character.
 
  while (n > 0 && IS_DIRSEP_CHAR(path[n - 1])) {
 
    n--;
 
  }
 
  path[n] = DIRSEP;
 
 
  // Traverse index files list. For each entry, append it to the given
 
  // path and see if the file exists. If it exists, break the loop
 
  while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
 
 
    // Ignore too long entries that may overflow path buffer
 
    if (filename_vec.len > path_len - (n + 2))
 
      continue;
 
 
    // Prepare full path to the index file
 
    (void) mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
 
 
    // Does it exist?
 
    if (mg_stat(path, &st) == 0) {
 
      // Yes it does, break the loop
 
      *stp = st;
 
      found = 1;
 
      break;
 
    }
 
  }
 
 
  // If no index file exists, restore directory path
 
  if (!found) {
 
    path[n] = '\0';
 
  }
 
 
  return found;
 
}
 
 
// Return True if we should reply 304 Not Modified.
 
static int is_not_modified(const struct mg_connection *conn,
 
                           const struct mgstat *stp) {
 
  char etag[64];
 
  const char *ims = mg_get_header(conn, "If-Modified-Since");
 
  const char *inm = mg_get_header(conn, "If-None-Match");
 
  construct_etag(etag, sizeof(etag), stp);
 
  return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
 
    (ims != NULL && stp->mtime <= parse_date_string(ims));
 
}
 
 
static int forward_body_data(struct mg_connection *conn, FILE *fp,
 
                             SOCKET sock, SSL *ssl) {
 
  const char *expect;
 
  char buf[MG_BUF_LEN];
 
  int to_read, nread, buffered_len, success = 0;
 
 
  expect = mg_get_header(conn, "Expect");
 
  assert(fp != NULL);
 
 
  if (conn->content_len == -1) {
 
    send_http_error(conn, 411, "Length Required", "%s", "");
 
  } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
 
    send_http_error(conn, 417, "Expectation Failed", "%s", "");
 
  } else {
 
    if (expect != NULL) {
 
      (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
 
    }
 
 
    buffered_len = conn->next_request - conn->body;
 
    assert(buffered_len >= 0);
 
    assert(conn->consumed_content == 0);
 
 
    if (buffered_len > 0) {
 
      if ((int64_t) buffered_len > conn->content_len) {
 
        buffered_len = (int) conn->content_len;
 
      }
 
      push(fp, sock, ssl, conn->body, (int64_t) buffered_len);
 
      conn->consumed_content += buffered_len;
 
      conn->body += buffered_len;
 
    }
 
 
    nread = 0;
 
    while (conn->consumed_content < conn->content_len) {
 
      to_read = sizeof(buf);
 
      if ((int64_t) to_read > conn->content_len - conn->consumed_content) {
 
        to_read = (int) (conn->content_len - conn->consumed_content);
 
      }
 
      nread = pull(NULL, conn, buf, to_read);
 
      if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
 
        break;
 
      }
 
      conn->consumed_content += nread;
 
    }
 
 
    if (conn->consumed_content == conn->content_len) {
 
      success = nread >= 0;
 
    }
 
 
    // Each error code path in this function must send an error
 
    if (!success) {
 
      send_http_error(conn, 577, http_500_error, "%s", "");
 
    }
 
  }
 
 
  return success;
 
}
 
 
#if !defined(NO_CGI)
 
// This structure helps to create an environment for the spawned CGI program.
 
// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
 
// last element must be NULL.
 
// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
 
// strings must reside in a contiguous buffer. The end of the buffer is
 
// marked by two '\0' characters.
 
// We satisfy both worlds: we create an envp array (which is vars), all
 
// entries are actually pointers inside buf.
 
struct cgi_env_block {
 
  struct mg_connection *conn;
 
  char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
 
  int len; // Space taken
 
  char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
 
  int nvars; // Number of variables
 
};
 
 
static char *addenv(struct cgi_env_block *block,
 
                    PRINTF_FORMAT_STRING(const char *fmt), ...)
 
  PRINTF_ARGS(2, 3);
 
 
// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
 
// pointer into the vars array.
 
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
 
  int n, space;
 
  char *added;
 
  va_list ap;
 
 
  // Calculate how much space is left in the buffer
 
  space = sizeof(block->buf) - block->len - 2;
 
  assert(space >= 0);
 
 
  // Make a pointer to the free space int the buffer
 
  added = block->buf + block->len;
 
 
  // Copy VARIABLE=VALUE\0 string into the free space
 
  va_start(ap, fmt);
 
  n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap);
 
  va_end(ap);
 
 
  // Make sure we do not overflow buffer and the envp array
 
  if (n > 0 && n + 1 < space &&
 
      block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
 
    // Append a pointer to the added string into the envp array
 
    block->vars[block->nvars++] = added;
 
    // Bump up used length counter. Include \0 terminator
 
    block->len += n + 1;
 
  } else {
 
    cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
 
  }
 
 
  return added;
 
}
 
 
static void prepare_cgi_environment(struct mg_connection *conn,
 
                                    const char *prog,
 
                                    struct cgi_env_block *blk) {
 
  const char *s, *slash;
 
  struct vec var_vec;
 
  char *p, src_addr[20];
 
  int  i;
 
 
  blk->len = blk->nvars = 0;
 
  blk->conn = conn;
 
  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 
 
  addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
 
  addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
 
  addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
 
 
  // Prepare the environment block
 
  addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
 
  addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
 
  addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
 
 
  // TODO(lsm): fix this for IPv6 case
 
  addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
 
 
  addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
 
  addenv(blk, "REMOTE_ADDR=%s", src_addr);
 
  addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
 
  addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
 
 
  // SCRIPT_NAME
 
  assert(conn->request_info.uri[0] == '/');
 
  slash = strrchr(conn->request_info.uri, '/');
 
  if ((s = strrchr(prog, '/')) == NULL)
 
    s = prog;
 
  addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri),
 
         conn->request_info.uri, s);
 
 
  addenv(blk, "SCRIPT_FILENAME=%s", prog);
 
  addenv(blk, "PATH_TRANSLATED=%s", prog);
 
  addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
 
 
  if ((s = mg_get_header(conn, "Content-Type")) != NULL)
 
    addenv(blk, "CONTENT_TYPE=%s", s);
 
 
  if (conn->request_info.query_string != NULL)
 
    addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);
 
 
  if ((s = mg_get_header(conn, "Content-Length")) != NULL)
 
    addenv(blk, "CONTENT_LENGTH=%s", s);
 
 
  if ((s = getenv("PATH")) != NULL)
 
    addenv(blk, "PATH=%s", s);
 
 
  if (conn->path_info != NULL) {
 
    addenv(blk, "PATH_INFO=%s", conn->path_info);
 
  }
 
 
#if defined(_WIN32)
 
  if ((s = getenv("COMSPEC")) != NULL) {
 
    addenv(blk, "COMSPEC=%s", s);
 
  }
 
  if ((s = getenv("SYSTEMROOT")) != NULL) {
 
    addenv(blk, "SYSTEMROOT=%s", s);
 
  }
 
  if ((s = getenv("SystemDrive")) != NULL) {
 
    addenv(blk, "SystemDrive=%s", s);
 
  }
 
#else
 
  if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
 
    addenv(blk, "LD_LIBRARY_PATH=%s", s);
 
#endif // _WIN32
 
 
  if ((s = getenv("PERLLIB")) != NULL)
 
    addenv(blk, "PERLLIB=%s", s);
 
 
  if (conn->request_info.remote_user != NULL) {
 
    addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);
 
    addenv(blk, "%s", "AUTH_TYPE=Digest");
 
  }
 
 
  // Add all headers as HTTP_* variables
 
  for (i = 0; i < conn->request_info.num_headers; i++) {
 
    p = addenv(blk, "HTTP_%s=%s",
 
        conn->request_info.http_headers[i].name,
 
        conn->request_info.http_headers[i].value);
 
 
    // Convert variable name into uppercase, and change - to _
 
    for (; *p != '=' && *p != '\0'; p++) {
 
      if (*p == '-')
 
        *p = '_';
 
      *p = (char) toupper(* (unsigned char *) p);
 
    }
 
  }
 
 
  // Add user-specified variables
 
  s = conn->ctx->config[CGI_ENVIRONMENT];
 
  while ((s = next_option(s, &var_vec, NULL)) != NULL) {
 
    addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
 
  }
 
 
  blk->vars[blk->nvars++] = NULL;
 
  blk->buf[blk->len++] = '\0';
 
 
  assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
 
  assert(blk->len > 0);
 
  assert(blk->len < (int) sizeof(blk->buf));
 
}
 
 
static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
 
  int headers_len, data_len, i, fd_stdin[2], fd_stdout[2];
 
  const char *status, *status_text;
 
  char buf[16384], *pbuf, dir[PATH_MAX], *p;
 
  struct mg_request_info ri;
 
  struct cgi_env_block blk;
 
  FILE *in, *out;
 
  pid_t pid;
 
 
  prepare_cgi_environment(conn, prog, &blk);
 
 
  // CGI must be executed in its own directory. 'dir' must point to the
 
  // directory containing executable program, 'p' must point to the
 
  // executable program name relative to 'dir'.
 
  (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog);
 
  if ((p = strrchr(dir, DIRSEP)) != NULL) {
 
    *p++ = '\0';
 
  } else {
 
    dir[0] = '.', dir[1] = '\0';
 
    p = (char *) prog;
 
  }
 
 
  pid = (pid_t) -1;
 
  fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
 
  in = out = NULL;
 
 
  if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
 
    send_http_error(conn, 500, http_500_error,
 
        "Cannot create CGI pipe: %s", strerror(ERRNO));
 
    goto done;
 
  } else if ((pid = spawn_process(conn, p, blk.buf, blk.vars,
 
          fd_stdin[0], fd_stdout[1], dir)) == (pid_t) -1) {
 
    send_http_error(conn, 500, http_500_error,
 
        "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
 
    goto done;
 
  } else if ((in = fdopen(fd_stdin[1], "wb")) == NULL ||
 
      (out = fdopen(fd_stdout[0], "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen: %s", strerror(ERRNO));
 
    goto done;
 
  }
 
 
  setbuf(in, NULL);
 
  setbuf(out, NULL);
 
 
  // spawn_process() must close those!
 
  // If we don't mark them as closed, close() attempt before
 
  // return from this function throws an exception on Windows.
 
  // Windows does not like when closed descriptor is closed again.
 
  fd_stdin[0] = fd_stdout[1] = -1;
 
 
  // Send POST data to the CGI process if needed
 
  if (!strcmp(conn->request_info.request_method, "POST") &&
 
      !forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
 
    goto done;
 
  }
 
  // Close so child gets an EOF.
 
  fclose(in);
 
  in = NULL;
 
 
  // Now read CGI reply into a buffer. We need to set correct
 
  // status code, thus we need to see all HTTP headers first.
 
  // Do not send anything back to client, until we buffer in all
 
  // HTTP headers.
 
  data_len = 0;
 
  headers_len = read_request(out, fc(conn->ctx), buf, sizeof(buf), &data_len);
 
  if (headers_len <= 0) {
 
    send_http_error(conn, 500, http_500_error,
 
                    "CGI program sent malformed or too big (>%u bytes) "
 
                    "HTTP headers: [%.*s]",
 
                    (unsigned) sizeof(buf), data_len, buf);
 
    goto done;
 
  }
 
  pbuf = buf;
 
  buf[headers_len - 1] = '\0';
 
  parse_http_headers(&pbuf, &ri);
 
 
  // Make up and send the status line
 
  status_text = "OK";
 
  if ((status = get_header(&ri, "Status")) != NULL) {
 
    conn->request_info.status_code = atoi(status);
 
    status_text = status;
 
    while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
 
      status_text++;
 
    }
 
  } else if (get_header(&ri, "Location") != NULL) {
 
    conn->request_info.status_code = 302;
 
  } else {
 
    conn->request_info.status_code = 200;
 
  }
 
  if (get_header(&ri, "Connection") != NULL &&
 
      !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
 
    conn->must_close = 1;
 
  }
 
  (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->request_info.status_code,
 
                   status_text);
 
 
  // Send headers
 
  for (i = 0; i < ri.num_headers; i++) {
 
    mg_printf(conn, "%s: %s\r\n",
 
              ri.http_headers[i].name, ri.http_headers[i].value);
 
  }
 
  (void) mg_write(conn, "\r\n", 2);
 
 
  // Send chunk of data that may have been read after the headers
 
  conn->num_bytes_sent += mg_write(conn, buf + headers_len,
 
                                   (size_t)(data_len - headers_len));
 
 
  // Read the rest of CGI output and send to the client
 
  send_file_data(conn, out, INT64_MAX);
 
 
done:
 
  if (pid != (pid_t) -1) {
 
    kill(pid, SIGKILL);
 
  }
 
  if (fd_stdin[0] != -1) {
 
    (void) close(fd_stdin[0]);
 
  }
 
  if (fd_stdout[1] != -1) {
 
    (void) close(fd_stdout[1]);
 
  }
 
 
  if (in != NULL) {
 
    (void) fclose(in);
 
  } else if (fd_stdin[1] != -1) {
 
    (void) close(fd_stdin[1]);
 
  }
 
 
  if (out != NULL) {
 
    (void) fclose(out);
 
  } else if (fd_stdout[0] != -1) {
 
    (void) close(fd_stdout[0]);
 
  }
 
}
 
#endif // !NO_CGI
 
 
// For a given PUT path, create all intermediate subdirectories
 
// for given path. Return 0 if the path itself is a directory,
 
// or -1 on error, 1 if OK.
 
static int put_dir(const char *path) {
 
  char buf[PATH_MAX];
 
  const char *s, *p;
 
  struct mgstat st;
 
  int len, res = 1;
 
 
  for (s = p = path + 2; (p = strchr(s, DIRSEP)) != NULL; s = ++p) {
 
    len = p - path;
 
    if (len >= (int) sizeof(buf)) {
 
      res = -1;
 
      break;
 
    }
 
    memcpy(buf, path, len);
 
    buf[len] = '\0';
 
 
    // Try to create intermediate directory
 
    DEBUG_TRACE(("mkdir(%s)", buf));
 
    if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0) {
 
      res = -1;
 
      break;
 
    }
 
 
    // Is path itself a directory?
 
    if (p[1] == '\0') {
 
      res = 0;
 
    }
 
  }
 
 
  return res;
 
}
 
 
static void put_file(struct mg_connection *conn, const char *path) {
 
  struct mgstat st;
 
  const char *range;
 
  int64_t r1, r2;
 
  FILE *fp;
 
  int rc;
 
 
  conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;
 
 
  if ((rc = put_dir(path)) == 0) {
 
    mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->request_info.status_code);
 
  } else if (rc == -1) {
 
    send_http_error(conn, 500, http_500_error,
 
        "put_dir(%s): %s", path, strerror(ERRNO));
 
  } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen(%s): %s", path, strerror(ERRNO));
 
  } else {
 
    set_close_on_exec(fileno(fp));
 
    range = mg_get_header(conn, "Content-Range");
 
    r1 = r2 = 0;
 
    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
 
      conn->request_info.status_code = 206;
 
      // TODO(lsm): handle seek error
 
      (void) fseeko(fp, r1, SEEK_SET);
 
    }
 
    if (forward_body_data(conn, fp, INVALID_SOCKET, NULL))
 
      (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n",
 
          conn->request_info.status_code);
 
    (void) fclose(fp);
 
  }
 
}
 
 
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
 
 
static void do_ssi_include(struct mg_connection *conn, const char *ssi,
 
                           char *tag, int include_level) {
 
  char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
 
  FILE *fp;
 
 
  // sscanf() is safe here, since send_ssi_file() also uses buffer
 
  // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
 
  if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the webserver root
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s",
 
        conn->ctx->config[DOCUMENT_ROOT], DIRSEP, file_name);
 
  } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the webserver working directory
 
    // or it is absolute system path
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name);
 
  } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the currect document
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi);
 
    if ((p = strrchr(path, DIRSEP)) != NULL) {
 
      p[1] = '\0';
 
    }
 
    (void) mg_snprintf(conn, path + strlen(path),
 
        sizeof(path) - strlen(path), "%s", file_name);
 
  } else {
 
    cry(conn, "Bad SSI #include: [%s]", tag);
 
    return;
 
  }
 
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
 
        tag, path, strerror(ERRNO));
 
  } else {
 
    set_close_on_exec(fileno(fp));
 
    if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
 
                     strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
 
      send_ssi_file(conn, path, fp, include_level + 1);
 
    } else {
 
      send_file_data(conn, fp, INT64_MAX);
 
    }
 
    (void) fclose(fp);
 
  }
 
}
 
 
#if !defined(NO_POPEN)
 
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
 
  char cmd[MG_BUF_LEN];
 
  FILE *fp;
 
 
  if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
 
    cry(conn, "Bad SSI #exec: [%s]", tag);
 
  } else if ((fp = popen(cmd, "r")) == NULL) {
 
    cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
 
  } else {
 
    send_file_data(conn, fp, INT64_MAX);
 
    (void) pclose(fp);
 
  }
 
}
 
#endif // !NO_POPEN
 
 
static void send_ssi_file(struct mg_connection *conn, const char *path,
 
                          FILE *fp, int include_level) {
 
  char buf[MG_BUF_LEN];
 
  int ch, len, in_ssi_tag;
 
 
  if (include_level > 10) {
 
    cry(conn, "SSI #include level is too deep (%s)", path);
 
    return;
 
  }
 
 
  in_ssi_tag = 0;
 
  len = 0;
 
 
  while ((ch = fgetc(fp)) != EOF) {
 
    if (in_ssi_tag && ch == '>') {
 
      in_ssi_tag = 0;
 
      buf[len++] = (char) ch;
 
      buf[len] = '\0';
 
      assert(len <= (int) sizeof(buf));
 
      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
 
        // Not an SSI tag, pass it
 
        (void) mg_write(conn, buf, (size_t)len);
 
      } else {
 
        if (!memcmp(buf + 5, "include", 7)) {
 
          do_ssi_include(conn, path, buf + 12, include_level);
 
#if !defined(NO_POPEN)
 
        } else if (!memcmp(buf + 5, "exec", 4)) {
 
          do_ssi_exec(conn, buf + 9);
 
#endif // !NO_POPEN
 
        } else {
 
          cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
 
        }
 
      }
 
      len = 0;
 
    } else if (in_ssi_tag) {
 
      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
 
        // Not an SSI tag
 
        in_ssi_tag = 0;
 
      } else if (len == (int) sizeof(buf) - 2) {
 
        cry(conn, "%s: SSI tag is too large", path);
 
        len = 0;
 
      }
 
      buf[len++] = ch & 0xff;
 
    } else if (ch == '<') {
 
      in_ssi_tag = 1;
 
      if (len > 0) {
 
        (void) mg_write(conn, buf, (size_t)len);
 
      }
 
      len = 0;
 
      buf[len++] = ch & 0xff;
 
    } else {
 
      buf[len++] = ch & 0xff;
 
      if (len == (int) sizeof(buf)) {
 
        (void) mg_write(conn, buf, (size_t)len);
 
        len = 0;
 
      }
 
    }
 
  }
 
 
  // Send the rest of buffered data
 
  if (len > 0) {
 
    (void) mg_write(conn, buf, (size_t)len);
 
  }
 
}
 
 
static void handle_ssi_file_request(struct mg_connection *conn,
 
                                    const char *path) {
 
  FILE *fp;
 
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
 
                    strerror(ERRNO));
 
  } else {
 
    conn->must_close = 1;
 
    set_close_on_exec(fileno(fp));
 
    mg_printf(conn, "HTTP/1.1 200 OK\r\n"
 
              "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
 
              suggest_connection_header(conn));
 
    send_ssi_file(conn, path, fp, 0);
 
    (void) fclose(fp);
 
  }
 
}
 
 
static void send_options(struct mg_connection *conn) {
 
  conn->request_info.status_code = 200;
 
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 200 OK\r\n"
 
      "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS\r\n"
 
      "DAV: 1\r\n\r\n");
 
}
 
 
// Writes PROPFIND properties for a collection element
 
static void print_props(struct mg_connection *conn, const char* uri,
 
                        struct mgstat* st) {
 
  char mtime[64];
 
  gmt_time_string(mtime, sizeof(mtime), &st->mtime);
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<d:response>"
 
       "<d:href>%s</d:href>"
 
       "<d:propstat>"
 
        "<d:prop>"
 
         "<d:resourcetype>%s</d:resourcetype>"
 
         "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
 
         "<d:getlastmodified>%s</d:getlastmodified>"
 
        "</d:prop>"
 
        "<d:status>HTTP/1.1 200 OK</d:status>"
 
       "</d:propstat>"
 
      "</d:response>\n",
 
      uri,
 
      st->is_directory ? "<d:collection/>" : "",
 
      st->size,
 
      mtime);
 
}
 
 
static void print_dav_dir_entry(struct de *de, void *data) {
 
  char href[PATH_MAX];
 
  struct mg_connection *conn = (struct mg_connection *) data;
 
  mg_snprintf(conn, href, sizeof(href), "%s%s",
 
              conn->request_info.uri, de->file_name);
 
  print_props(conn, href, &de->st);
 
}
 
 
static void handle_propfind(struct mg_connection *conn, const char* path,
 
                            struct mgstat* st) {
 
  const char *depth = mg_get_header(conn, "Depth");
 
 
  conn->must_close = 1;
 
  conn->request_info.status_code = 207;
 
  mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
 
            "Connection: close\r\n"
 
            "Content-Type: text/xml; charset=utf-8\r\n\r\n");
 
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 
      "<d:multistatus xmlns:d='DAV:'>\n");
 
 
  // Print properties for the requested resource itself
 
  print_props(conn, conn->request_info.uri, st);
 
 
  // If it is a directory, print directory entries too if Depth is not 0
 
  if (st->is_directory &&
 
      !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
 
      (depth == NULL || strcmp(depth, "0") != 0)) {
 
    scan_directory(conn, path, conn, &print_dav_dir_entry);
 
  }
 
 
  conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
 
}
 
 
// This is the heart of the Mongoose's logic.
 
// This function is called when the request is read, parsed and validated,
 
// and Mongoose must decide what action to take: serve a file, or
 
// a directory, or call embedded function, etcetera.
 
static void handle_request(struct mg_connection *conn) {
 
  struct mg_request_info *ri = &conn->request_info;
 
  char path[PATH_MAX];
 
  int stat_result, uri_len;
 
  struct mgstat st;
 
 
  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
 
    *conn->request_info.query_string++ = '\0';
 
  }
 
  uri_len = (int) strlen(ri->uri);
 
  url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);
 
  remove_double_dots_and_double_slashes(ri->uri);
 
  stat_result = convert_uri_to_file_name(conn, path, sizeof(path), &st);
 
 
  DEBUG_TRACE(("%s", ri->uri));
 
  if (!check_authorization(conn, path)) {
 
    send_authorization_request(conn);
 
  } else if (call_user(conn, MG_NEW_REQUEST) != NULL) {
 
    // Do nothing, callback has served the request
 
  } else if (!strcmp(ri->request_method, "OPTIONS")) {
 
    send_options(conn);
 
  } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
 
    send_http_error(conn, 404, "Not Found", "Not Found");
 
  } else if ((!strcmp(ri->request_method, "PUT") ||
 
        !strcmp(ri->request_method, "DELETE")) &&
 
      (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ||
 
       is_authorized_for_put(conn) != 1)) {
 
    send_authorization_request(conn);
 
  } else if (!strcmp(ri->request_method, "PUT")) {
 
    put_file(conn, path);
 
  } else if (!strcmp(ri->request_method, "DELETE")) {
 
    if (mg_remove(path) == 0) {
 
      send_http_error(conn, 200, "OK", "%s", "");
 
    } else {
 
      send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
 
                      strerror(ERRNO));
 
    }
 
  } else if (stat_result != 0 || must_hide_file(conn, path)) {
 
    send_http_error(conn, 404, "Not Found", "%s", "File not found");
 
  } else if (st.is_directory && ri->uri[uri_len - 1] != '/') {
 
    (void) mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
 
                     "Location: %s/\r\n\r\n", ri->uri);
 
  } else if (!strcmp(ri->request_method, "PROPFIND")) {
 
    handle_propfind(conn, path, &st);
 
  } else if (st.is_directory &&
 
             !substitute_index_file(conn, path, sizeof(path), &st)) {
 
    if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
 
      handle_directory_request(conn, path);
 
    } else {
 
      send_http_error(conn, 403, "Directory Listing Denied",
 
          "Directory listing denied");
 
    }
 
#if !defined(NO_CGI)
 
  } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
 
                          strlen(conn->ctx->config[CGI_EXTENSIONS]),
 
                          path) > 0) {
 
    if (strcmp(ri->request_method, "POST") &&
 
        strcmp(ri->request_method, "GET")) {
 
      send_http_error(conn, 501, "Not Implemented",
 
                      "Method %s is not implemented", ri->request_method);
 
    } else {
 
      handle_cgi_request(conn, path);
 
    }
 
#endif // !NO_CGI
 
  } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
 
                          strlen(conn->ctx->config[SSI_EXTENSIONS]),
 
                          path) > 0) {
 
    handle_ssi_file_request(conn, path);
 
  } else if (is_not_modified(conn, &st)) {
 
    send_http_error(conn, 304, "Not Modified", "%s", "");
 
  } else {
 
    handle_file_request(conn, path, &st);
 
  }
 
}
 
 
static void close_all_listening_sockets(struct mg_context *ctx) {
 
  struct socket *sp, *tmp;
 
  for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
 
    tmp = sp->next;
 
    (void) closesocket(sp->sock);
 
    free(sp);
 
  }
 
}
 
 
// Valid listening port specification is: [ip_address:]port[s]
 
// Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
 
// TODO(lsm): add parsing of the IPv6 address
 
static int parse_port_string(const struct vec *vec, struct socket *so) {
 
  int a, b, c, d, port, len;
 
 
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
 
  // Also, all-zeroes in the socket address means binding to all addresses
 
  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
 
  memset(so, 0, sizeof(*so));
 
 
  if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) == 5) {
 
    // Bind to a specific IPv4 address
 
    so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
 
  } else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
 
             len <= 0 ||
 
             len > (int) vec->len ||
 
             (vec->ptr[len] && vec->ptr[len] != 's' && vec->ptr[len] != ',')) {
 
    return 0;
 
  }
 
 
  so->is_ssl = vec->ptr[len] == 's';
 
#if defined(USE_IPV6)
 
  so->lsa.sin6.sin6_family = AF_INET6;
 
  so->lsa.sin6.sin6_port = htons((uint16_t) port);
 
#else
 
  so->lsa.sin.sin_family = AF_INET;
 
  so->lsa.sin.sin_port = htons((uint16_t) port);
 
#endif
 
 
  return 1;
 
}
 
 
static int set_ports_option(struct mg_context *ctx) {
 
  const char *list = ctx->config[LISTENING_PORTS];
 
  int on = 1, success = 1;
 
  SOCKET sock;
 
  struct vec vec;
 
  struct socket so, *listener;
 
 
  while (success && (list = next_option(list, &vec, NULL)) != NULL) {
 
    if (!parse_port_string(&vec, &so)) {
 
      cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
 
          __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
 
      success = 0;
 
    } else if (so.is_ssl &&
 
               (ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
 
      cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
 
      success = 0;
 
    } else if ((sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
 
               INVALID_SOCKET ||
 
#if !defined(_WIN32)
 
               // On Windows, SO_REUSEADDR is recommended only for
 
               // broadcast UDP sockets
 
               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
 
                          sizeof(on)) != 0 ||
 
#endif // !_WIN32
 
               // Set TCP keep-alive. This is needed because if HTTP-level
 
               // keep-alive is enabled, and client resets the connection,
 
               // server won't get TCP FIN or RST and will keep the connection
 
               // open forever. With TCP keep-alive, next keep-alive
 
               // handshake will figure out that the client is down and
 
               // will close the server end.
 
               // Thanks to Igor Klopov who suggested the patch.
 
               setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on,
 
                          sizeof(on)) != 0 ||
 
               bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
 
               listen(sock, SOMAXCONN) != 0) {
 
      closesocket(sock);
 
      cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
 
          vec.len, vec.ptr, strerror(ERRNO));
 
      success = 0;
 
    } else if ((listener = (struct socket *)
 
                calloc(1, sizeof(*listener))) == NULL) {
 
      // NOTE(lsm): order is important: call cry before closesocket(),
 
      // cause closesocket() alters the errno.
 
      cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
 
      closesocket(sock);
 
      success = 0;
 
    } else {
 
      *listener = so;
 
      listener->sock = sock;
 
      set_close_on_exec(listener->sock);
 
      listener->next = ctx->listening_sockets;
 
      ctx->listening_sockets = listener;
 
    }
 
  }
 
 
  if (!success) {
 
    close_all_listening_sockets(ctx);
 
  }
 
 
  return success;
 
}
 
 
static void log_header(const struct mg_connection *conn, const char *header,
 
                       FILE *fp) {
 
  const char *header_value;
 
 
  if ((header_value = mg_get_header(conn, header)) == NULL) {
 
    (void) fprintf(fp, "%s", " -");
 
  } else {
 
    (void) fprintf(fp, " \"%s\"", header_value);
 
  }
 
}
 
 
static void log_access(const struct mg_connection *conn) {
 
  const struct mg_request_info *ri;
 
  FILE *fp;
 
  char date[64], src_addr[20];
 
 
  fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ?  NULL :
 
    mg_fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
 
 
  if (fp == NULL)
 
    return;
 
 
  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
 
           localtime(&conn->birth_time));
 
 
  ri = &conn->request_info;
 
  flockfile(fp);
 
 
  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 
  fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
 
          src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
 
          ri->request_method ? ri->request_method : "-",
 
          ri->uri ? ri->uri : "-", ri->http_version,
 
          conn->request_info.status_code, conn->num_bytes_sent);
 
  log_header(conn, "Referer", fp);
 
  log_header(conn, "User-Agent", fp);
 
  fputc('\n', fp);
 
  fflush(fp);
 
 
  funlockfile(fp);
 
  fclose(fp);
 
}
 
 
static int isbyte(int n) {
 
  return n >= 0 && n <= 255;
 
}
 
 
// Verify given socket address against the ACL.
 
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
 
static int check_acl(struct mg_context *ctx, const union usa *usa) {
 
  int a, b, c, d, n, mask, allowed;
 
  char flag;
 
  uint32_t acl_subnet, acl_mask, remote_ip;
 
  struct vec vec;
 
  const char *list = ctx->config[ACCESS_CONTROL_LIST];
 
 
  if (list == NULL) {
 
    return 1;
 
  }
 
 
  (void) memcpy(&remote_ip, &usa->sin.sin_addr, sizeof(remote_ip));
 
 
  // If any ACL is set, deny by default
 
  allowed = '-';
 
 
  while ((list = next_option(list, &vec, NULL)) != NULL) {
 
    mask = 32;
 
 
    if (sscanf(vec.ptr, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n) != 5) {
 
      cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
 
      return -1;
 
    } else if (flag != '+' && flag != '-') {
 
      cry(fc(ctx), "%s: flag must be + or -: [%s]", __func__, vec.ptr);
 
      return -1;
 
    } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
 
      cry(fc(ctx), "%s: bad ip address: [%s]", __func__, vec.ptr);
 
      return -1;
 
    } else if (sscanf(vec.ptr + n, "/%d", &mask) == 0) {
 
      // Do nothing, no mask specified
 
    } else if (mask < 0 || mask > 32) {
 
      cry(fc(ctx), "%s: bad subnet mask: %d [%s]", __func__, n, vec.ptr);
 
      return -1;
 
    }
 
 
    acl_subnet = (a << 24) | (b << 16) | (c << 8) | d;
 
    acl_mask = mask ? 0xffffffffU << (32 - mask) : 0;
 
 
    if (acl_subnet == (ntohl(remote_ip) & acl_mask)) {
 
      allowed = flag;
 
    }
 
  }
 
 
  return allowed == '+';
 
}
 
 
static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
 
  FD_SET(fd, set);
 
  if (fd > (SOCKET) *max_fd) {
 
    *max_fd = (int) fd;
 
  }
 
}
 
 
#if !defined(_WIN32)
 
static int set_uid_option(struct mg_context *ctx) {
 
  struct passwd *pw;
 
  const char *uid = ctx->config[RUN_AS_USER];
 
  int success = 0;
 
 
  if (uid == NULL) {
 
    success = 1;
 
  } else {
 
    if ((pw = getpwnam(uid)) == NULL) {
 
      cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
 
    } else if (setgid(pw->pw_gid) == -1) {
 
      cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
 
    } else if (setuid(pw->pw_uid) == -1) {
 
      cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
 
    } else {
 
      success = 1;
 
    }
 
  }
 
 
  return success;
 
}
 
#endif // !_WIN32
 
 
#if !defined(NO_SSL)
 
static pthread_mutex_t *ssl_mutexes;
 
 
// Return OpenSSL error message
 
static const char *ssl_error(void) {
 
  unsigned long err;
 
  err = ERR_get_error();
 
  return err == 0 ? "" : ERR_error_string(err, NULL);
 
}
 
 
static void ssl_locking_callback(int mode, int mutex_num, const char *file,
 
                                 int line) {
 
  line = 0;    // Unused
 
  file = NULL; // Unused
 
 
  if (mode & CRYPTO_LOCK) {
 
    (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
 
  } else {
 
    (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
 
  }
 
}
 
 
static unsigned long ssl_id_callback(void) {
 
  return (unsigned long) pthread_self();
 
}
 
 
#if !defined(NO_SSL_DL)
 
static int load_dll(struct mg_context *ctx, const char *dll_name,
 
                    struct ssl_func *sw) {
 
  union {void *p; void (*fp)(void);} u;
 
  void  *dll_handle;
 
  struct ssl_func *fp;
 
 
  if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
 
    cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
 
    return 0;
 
  }
 
 
  for (fp = sw; fp->name != NULL; fp++) {
 
#ifdef _WIN32
 
    // GetProcAddress() returns pointer to function
 
    u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
 
#else
 
    // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
 
    // function pointers. We need to use a union to make a cast.
 
    u.p = dlsym(dll_handle, fp->name);
 
#endif // _WIN32
 
    if (u.fp == NULL) {
 
      cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
 
      return 0;
 
    } else {
 
      fp->ptr = u.fp;
 
    }
 
  }
 
 
  return 1;
 
}
 
#endif // NO_SSL_DL
 
 
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
 
static int set_ssl_option(struct mg_context *ctx) {
 
  struct mg_request_info request_info;
 
  int i, size;
 
  const char *pem = ctx->config[SSL_CERTIFICATE];
 
  const char *chain = ctx->config[SSL_CHAIN_FILE];
 
 
#if !defined(NO_SSL_DL)
 
  if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
 
      !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
 
    return 0;
 
  }
 
#endif // NO_SSL_DL
 
 
  // Initialize SSL crap
 
  SSL_library_init();
 
  SSL_load_error_strings();
 
 
  if ((ctx->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
 
    cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
 
  }
 
 
  if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
 
    cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
 
  } else if (ctx->user_callback != NULL) {
 
    memset(&request_info, 0, sizeof(request_info));
 
    request_info.user_data = ctx->user_data;
 
    ctx->user_callback(MG_INIT_SSL, (struct mg_connection *) ctx->ssl_ctx);
 
  }
 
 
  if (ctx->ssl_ctx != NULL && pem != NULL &&
 
      SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, SSL_FILETYPE_PEM) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
 
    return 0;
 
  }
 
  if (ctx->ssl_ctx != NULL && pem != NULL &&
 
      SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, SSL_FILETYPE_PEM) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
 
    return 0;
 
  }
 
  if (ctx->ssl_ctx != NULL && chain != NULL &&
 
      SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, chain) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, chain, ssl_error());
 
    return 0;
 
  }
 
 
  // Initialize locking callbacks, needed for thread safety.
 
  // http://www.openssl.org/support/faq.html#PROG1
 
  size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
 
  if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
 
    cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
 
    return 0;
 
  }
 
 
  for (i = 0; i < CRYPTO_num_locks(); i++) {
 
    pthread_mutex_init(&ssl_mutexes[i], NULL);
 
  }
 
 
  CRYPTO_set_locking_callback(&ssl_locking_callback);
 
  CRYPTO_set_id_callback(&ssl_id_callback);
 
 
  return 1;
 
}
 
 
static void uninitialize_ssl(struct mg_context *ctx) {
 
  int i;
 
  if (ctx->ssl_ctx != NULL) {
 
    CRYPTO_set_locking_callback(NULL);
 
    for (i = 0; i < CRYPTO_num_locks(); i++) {
 
      pthread_mutex_destroy(&ssl_mutexes[i]);
 
    }
 
    CRYPTO_set_locking_callback(NULL);
 
    CRYPTO_set_id_callback(NULL);
 
  }
 
}
 
#endif // !NO_SSL
 
 
static int set_gpass_option(struct mg_context *ctx) {
 
  struct mgstat mgstat;
 
  const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
 
  return path == NULL || mg_stat(path, &mgstat) == 0;
 
}
 
 
static int set_acl_option(struct mg_context *ctx) {
 
  union usa fake;
 
  return check_acl(ctx, &fake) != -1;
 
}
 
 
static void reset_per_request_attributes(struct mg_connection *conn) {
 
  conn->path_info = conn->body = conn->next_request = NULL;
 
  conn->num_bytes_sent = conn->consumed_content = 0;
 
  conn->content_len = -1;
 
  conn->request_len = conn->data_len = 0;
 
  conn->must_close = 0;
 
}
 
 
static void close_socket_gracefully(struct mg_connection *conn) {
 
  char buf[MG_BUF_LEN];
 
  struct linger linger;
 
  int n, sock = conn->client.sock;
 
 
  // Set linger option to avoid socket hanging out after close. This prevent
 
  // ephemeral port exhaust problem under high QPS.
 
  linger.l_onoff = 1;
 
  linger.l_linger = 1;
 
  setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
 
 
  // Send FIN to the client
 
  (void) shutdown(sock, SHUT_WR);
 
  set_non_blocking_mode(sock);
 
 
  // Read and discard pending incoming data. If we do not do that and close the
 
  // socket, the data in the send buffer may be discarded. This
 
  // behaviour is seen on Windows, when client keeps sending data
 
  // when server decides to close the connection; then when client
 
  // does recv() it gets no data back.
 
  do {
 
    n = pull(NULL, conn, buf, sizeof(buf));
 
  } while (n > 0);
 
 
  // Now we know that our FIN is ACK-ed, safe to close
 
  (void) closesocket(sock);
 
}
 
 
static void close_connection(struct mg_connection *conn) {
 
  if (conn->ssl) {
 
    SSL_free(conn->ssl);
 
    conn->ssl = NULL;
 
  }
 
 
  if (conn->client.sock != INVALID_SOCKET) {
 
    close_socket_gracefully(conn);
 
  }
 
}
 
 
void mg_close_connection(struct mg_connection *conn) {
 
  close_connection(conn);
 
  free(conn);
 
}
 
 
struct mg_connection *mg_connect(struct mg_context *ctx,
 
                                 const char *host, int port, int use_ssl) {
 
  struct mg_connection *newconn = NULL;
 
  struct sockaddr_in sin;
 
  struct hostent *he;
 
  int sock;
 
 
  if (ctx->client_ssl_ctx == NULL && use_ssl) {
 
    cry(fc(ctx), "%s: SSL is not initialized", __func__);
 
  } else if ((he = gethostbyname(host)) == NULL) {
 
    cry(fc(ctx), "%s: gethostbyname(%s): %s", __func__, host, strerror(ERRNO));
 
  } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
 
    cry(fc(ctx), "%s: socket: %s", __func__, strerror(ERRNO));
 
  } else {
 
    sin.sin_family = AF_INET;
 
    sin.sin_port = htons((uint16_t) port);
 
    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
 
    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
 
      cry(fc(ctx), "%s: connect(%s:%d): %s", __func__, host, port,
 
          strerror(ERRNO));
 
      closesocket(sock);
 
    } else if ((newconn = (struct mg_connection *)
 
                calloc(1, sizeof(*newconn))) == NULL) {
 
      cry(fc(ctx), "%s: calloc: %s", __func__, strerror(ERRNO));
 
      closesocket(sock);
 
    } else {
 
      newconn->ctx = ctx;
 
      newconn->client.sock = sock;
 
      newconn->client.rsa.sin = sin;
 
      newconn->client.is_ssl = use_ssl;
 
      if (use_ssl) {
 
        sslize(newconn, ctx->client_ssl_ctx, SSL_connect);
 
      }
 
    }
 
  }
 
 
  return newconn;
 
}
 
 
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
 
               char *buf, size_t buf_len, struct mg_request_info *ri) {
 
  struct mg_connection *newconn;
 
  int n, req_length, data_length, port;
 
  char host[1025], proto[10], buf2[MG_BUF_LEN];
 
  FILE *fp = NULL;
 
 
  if (sscanf(url, "%9[htps]://%1024[^:]:%d/%n", proto, host, &port, &n) == 3) {
 
  } else if (sscanf(url, "%9[htps]://%1024[^/]/%n", proto, host, &n) == 2) {
 
    port = mg_strcasecmp(proto, "https") == 0 ? 443 : 80;
 
  } else {
 
    cry(fc(ctx), "%s: invalid URL: [%s]", __func__, url);
 
    return NULL;
 
  }
 
 
  if ((newconn = mg_connect(ctx, host, port,
 
                            !strcmp(proto, "https"))) == NULL) {
 
    cry(fc(ctx), "%s: mg_connect(%s): %s", __func__, url, strerror(ERRNO));
 
  } else {
 
    mg_printf(newconn, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", url + n, host);
 
    data_length = 0;
 
    req_length = read_request(NULL, newconn, buf, buf_len, &data_length);
 
    if (req_length <= 0) {
 
      cry(fc(ctx), "%s(%s): invalid HTTP reply", __func__, url);
 
    } else if (parse_http_response(buf, req_length, ri) <= 0) {
 
      cry(fc(ctx), "%s(%s): cannot parse HTTP headers", __func__, url);
 
    } else if ((fp = fopen(path, "w+b")) == NULL) {
 
      cry(fc(ctx), "%s: fopen(%s): %s", __func__, path, strerror(ERRNO));
 
    } else {
 
      // Write chunk of data that may be in the user's buffer
 
      data_length -= req_length;
 
      if (data_length > 0 &&
 
        fwrite(buf + req_length, 1, data_length, fp) != (size_t) data_length) {
 
        cry(fc(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
 
        fclose(fp);
 
        fp = NULL;
 
      }
 
      // Read the rest of the response and write it to the file. Do not use
 
      // mg_read() cause we didn't set newconn->content_len properly.
 
      while (fp && (data_length = pull(0, newconn, buf2, sizeof(buf2))) > 0) {
 
        if (fwrite(buf2, 1, data_length, fp) != (size_t) data_length) {
 
          cry(fc(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
 
          fclose(fp);
 
          fp = NULL;
 
          break;
 
        }
 
      }
 
    }
 
    mg_close_connection(newconn);
 
  }
 
 
  return fp;
 
}
 
 
static int is_valid_uri(const char *uri) {
 
  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
 
  // URI can be an asterisk (*) or should start with slash.
 
  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
 
}
 
 
static void process_new_connection(struct mg_connection *conn) {
 
  struct mg_request_info *ri = &conn->request_info;
 
  int keep_alive_enabled, buffered_len;
 
  const char *cl;
 
 
  keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
 
 
  do {
 
    reset_per_request_attributes(conn);
 
    conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
 
                                     &conn->data_len);
 
    assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
 
    if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
 
      send_http_error(conn, 413, "Request Too Large", "%s", "");
 
      return;
 
    } if (conn->request_len <= 0) {
 
      return;  // Remote end closed the connection
 
    }
 
    conn->body = conn->next_request = conn->buf + conn->request_len;
 
 
    if (parse_http_request(conn->buf, conn->buf_size, ri) <= 0 ||
 
        !is_valid_uri(ri->uri)) {
 
      // Do not put garbage in the access log, just send it back to the client
 
      send_http_error(conn, 400, "Bad Request",
 
          "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);
 
      conn->must_close = 1;
 
    } else if (strcmp(ri->http_version, "1.0") &&
 
               strcmp(ri->http_version, "1.1")) {
 
      // Request seems valid, but HTTP version is strange
 
      send_http_error(conn, 505, "HTTP version not supported", "%s", "");
 
      log_access(conn);
 
    } else {
 
      // Request is valid, handle it
 
      cl = get_header(ri, "Content-Length");
 
      conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
 
 
      // Set pointer to the next buffered request
 
      buffered_len = conn->data_len - conn->request_len;
 
      assert(buffered_len >= 0);
 
      if (conn->content_len <= 0) {
 
      } else if (conn->content_len < (int64_t) buffered_len) {
 
        conn->next_request += conn->content_len;
 
      } else {
 
        conn->next_request += buffered_len;
 
      }
 
 
      conn->birth_time = time(NULL);
 
      handle_request(conn);
 
      call_user(conn, MG_REQUEST_COMPLETE);
 
      log_access(conn);
 
    }
 
    if (ri->remote_user != NULL) {
 
      free((void *) ri->remote_user);
 
    }
 
 
    // Discard all buffered data for this request
 
    assert(conn->next_request >= conn->buf);
 
    assert(conn->data_len >= conn->next_request - conn->buf);
 
    conn->data_len -= conn->next_request - conn->buf;
 
    memmove(conn->buf, conn->next_request, (size_t) conn->data_len);
 
  } while (conn->ctx->stop_flag == 0 &&
 
           keep_alive_enabled &&
 
           should_keep_alive(conn));
 
}
 
 
// Worker threads take accepted socket from the queue
 
static int consume_socket(struct mg_context *ctx, struct socket *sp) {
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  DEBUG_TRACE(("going idle"));
 
 
  // If the queue is empty, wait. We're idle at this point.
 
  while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
 
    pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
 
  }
 
 
  // If we're stopping, sq_head may be equal to sq_tail.
 
  if (ctx->sq_head > ctx->sq_tail) {
 
    // Copy socket from the queue and increment tail
 
    *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
 
    ctx->sq_tail++;
 
    DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
 
 
    // Wrap pointers if needed
 
    while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
 
      ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
 
      ctx->sq_head -= ARRAY_SIZE(ctx->queue);
 
    }
 
  }
 
 
  (void) pthread_cond_signal(&ctx->sq_empty);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 
 
  return !ctx->stop_flag;
 
}
 
 
static void worker_thread(struct mg_context *ctx) {
 
  struct mg_connection *conn;
 
  int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]);
 
 
  conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
 
  if (conn == NULL) {
 
    cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
 
  } else {
 
    conn->buf_size = buf_size;
 
    conn->buf = (char *) (conn + 1);
 
 
    // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
 
    // sq_empty condvar to wake up the master waiting in produce_socket()
 
    while (consume_socket(ctx, &conn->client)) {
 
      conn->birth_time = time(NULL);
 
      conn->ctx = ctx;
 
 
      // Fill in IP, port info early so even if SSL setup below fails,
 
      // error handler would have the corresponding info.
 
      // Thanks to Johannes Winkelmann for the patch.
 
      // TODO(lsm): Fix IPv6 case
 
      conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
 
      memcpy(&conn->request_info.remote_ip,
 
             &conn->client.rsa.sin.sin_addr.s_addr, 4);
 
      conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
 
      conn->request_info.is_ssl = conn->client.is_ssl;
 
 
      if (!conn->client.is_ssl ||
 
          (conn->client.is_ssl &&
 
           sslize(conn, conn->ctx->ssl_ctx, SSL_accept))) {
 
        process_new_connection(conn);
 
      }
 
 
      close_connection(conn);
 
    }
 
    free(conn);
 
  }
 
 
  // Signal master that we're done with connection and exiting
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  ctx->num_threads--;
 
  (void) pthread_cond_signal(&ctx->cond);
 
  assert(ctx->num_threads >= 0);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 
 
  DEBUG_TRACE(("exiting"));
 
}
 
 
// Master thread adds accepted socket to a queue
 
static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
 
  // If the queue is full, wait
 
  while (ctx->stop_flag == 0 &&
 
         ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
 
    (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
 
  }
 
 
  if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
 
    // Copy socket to the queue and increment head
 
    ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
 
    ctx->sq_head++;
 
    DEBUG_TRACE(("queued socket %d", sp->sock));
 
  }
 
 
  (void) pthread_cond_signal(&ctx->sq_full);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 
}
 
 
static void accept_new_connection(const struct socket *listener,
 
                                  struct mg_context *ctx) {
 
  struct socket accepted;
 
  char src_addr[20];
 
  socklen_t len;
 
  int allowed;
 
 
  len = sizeof(accepted.rsa);
 
  accepted.lsa = listener->lsa;
 
  accepted.sock = accept(listener->sock, &accepted.rsa.sa, &len);
 
  if (accepted.sock != INVALID_SOCKET) {
 
    allowed = check_acl(ctx, &accepted.rsa);
 
    if (allowed) {
 
      // Put accepted socket structure into the queue
 
      DEBUG_TRACE(("accepted socket %d", accepted.sock));
 
      accepted.is_ssl = listener->is_ssl;
 
      produce_socket(ctx, &accepted);
 
    } else {
 
      sockaddr_to_string(src_addr, sizeof(src_addr), &accepted.rsa);
 
      cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
 
      (void) closesocket(accepted.sock);
 
    }
 
  }
 
}
 
 
static void master_thread(struct mg_context *ctx) {
 
  fd_set read_set;
 
  struct timeval tv;
 
  struct socket *sp;
 
  int max_fd;
 
 
  // Increase priority of the master thread
 
#if defined(_WIN32)
 
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
 
#endif
 
 
#if defined(ISSUE_317)
 
  struct sched_param sched_param;
 
  sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
 
  pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
 
#endif
 
 
  while (ctx->stop_flag == 0) {
 
    FD_ZERO(&read_set);
 
    max_fd = -1;
 
 
    // Add listening sockets to the read set
 
    for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
 
      add_to_set(sp->sock, &read_set, &max_fd);
 
    }
 
 
    tv.tv_sec = 0;
 
    tv.tv_usec = 200 * 1000;
 
 
    if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
 
#ifdef _WIN32
 
      // On windows, if read_set and write_set are empty,
 
      // select() returns "Invalid parameter" error
 
      // (at least on my Windows XP Pro). So in this case, we sleep here.
 
      mg_sleep(1000);
 
#endif // _WIN32
 
    } else {
 
      for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
 
        if (ctx->stop_flag == 0 && FD_ISSET(sp->sock, &read_set)) {
 
          accept_new_connection(sp, ctx);
 
        }
 
      }
 
    }
 
  }
 
  DEBUG_TRACE(("stopping workers"));
 
 
  // Stop signal received: somebody called mg_stop. Quit.
 
  close_all_listening_sockets(ctx);
 
 
  // Wakeup workers that are waiting for connections to handle.
 
  pthread_cond_broadcast(&ctx->sq_full);
 
 
  // Wait until all threads finish
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  while (ctx->num_threads > 0) {
 
    (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
 
  }
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 
 
  // All threads exited, no sync is needed. Destroy mutex and condvars
 
  (void) pthread_mutex_destroy(&ctx->mutex);
 
  (void) pthread_cond_destroy(&ctx->cond);
 
  (void) pthread_cond_destroy(&ctx->sq_empty);
 
  (void) pthread_cond_destroy(&ctx->sq_full);
 
 
#if !defined(NO_SSL)
 
  uninitialize_ssl(ctx);
 
#endif
 
  DEBUG_TRACE(("exiting"));
 
 
  // Signal mg_stop() that we're done.
 
  // WARNING: This must be the very last thing this
 
  // thread does, as ctx becomes invalid after this line.
 
  ctx->stop_flag = 2;
 
}
 
 
static void free_context(struct mg_context *ctx) {
 
  int i;
 
 
  // Deallocate config parameters
 
  for (i = 0; i < NUM_OPTIONS; i++) {
 
    if (ctx->config[i] != NULL)
 
      free(ctx->config[i]);
 
  }
 
 
  // Deallocate SSL context
 
  if (ctx->ssl_ctx != NULL) {
 
    SSL_CTX_free(ctx->ssl_ctx);
 
  }
 
  if (ctx->client_ssl_ctx != NULL) {
 
    SSL_CTX_free(ctx->client_ssl_ctx);
 
  }
 
#ifndef NO_SSL
 
  if (ssl_mutexes != NULL) {
 
    free(ssl_mutexes);
 
  }
 
#endif // !NO_SSL
 
 
  // Deallocate context itself
 
  free(ctx);
 
}
 
 
void mg_stop(struct mg_context *ctx) {
 
  ctx->stop_flag = 1;
 
 
  // Wait until mg_fini() stops
 
  while (ctx->stop_flag != 2) {
 
    (void) mg_sleep(10);
 
  }
 
  free_context(ctx);
 
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  (void) WSACleanup();
 
#endif // _WIN32
 
}
 
 
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
 
                            const char **options) {
 
  struct mg_context *ctx;
 
  const char *name, *value, *default_value;
 
  int i;
 
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  WSADATA data;
 
  WSAStartup(MAKEWORD(2,2), &data);
 
  InitializeCriticalSection(&global_log_file_lock);
 
#endif // _WIN32
 
 
  // Allocate context and initialize reasonable general case defaults.
 
  // TODO(lsm): do proper error handling here.
 
  if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
 
    return NULL;
 
  }
 
  ctx->user_callback = user_callback;
 
  ctx->user_data = user_data;
 
 
  while (options && (name = *options++) != NULL) {
 
    if ((i = get_option_index(name)) == -1) {
 
      cry(fc(ctx), "Invalid option: %s", name);
 
      free_context(ctx);
 
      return NULL;
 
    } else if ((value = *options++) == NULL) {
 
      cry(fc(ctx), "%s: option value cannot be NULL", name);
 
      free_context(ctx);
 
      return NULL;
 
    }
 
    if (ctx->config[i] != NULL) {
 
      cry(fc(ctx), "warning: %s: duplicate option", name);
 
    }
 
    ctx->config[i] = mg_strdup(value);
 
    DEBUG_TRACE(("[%s] -> [%s]", name, value));
 
  }
 
 
  // Set default value if needed
 
  for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
 
    default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
 
    if (ctx->config[i] == NULL && default_value != NULL) {
 
      ctx->config[i] = mg_strdup(default_value);
 
      DEBUG_TRACE(("Setting default: [%s] -> [%s]",
 
                   config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
 
                   default_value));
 
    }
 
  }
 
 
  // NOTE(lsm): order is important here. SSL certificates must
 
  // be initialized before listening ports. UID must be set last.
 
  if (!set_gpass_option(ctx) ||
 
#if !defined(NO_SSL)
 
      !set_ssl_option(ctx) ||
 
#endif
 
      !set_ports_option(ctx) ||
 
#if !defined(_WIN32)
 
      !set_uid_option(ctx) ||
 
#endif
 
      !set_acl_option(ctx)) {
 
    free_context(ctx);
 
    return NULL;
 
  }
 
 
#if !defined(_WIN32) && !defined(__SYMBIAN32__)
 
  // Ignore SIGPIPE signal, so if browser cancels the request, it
 
  // won't kill the whole process.
 
  (void) signal(SIGPIPE, SIG_IGN);
 
  // Also ignoring SIGCHLD to let the OS to reap zombies properly.
 
  (void) signal(SIGCHLD, SIG_IGN);
 
#endif // !_WIN32
 
 
  (void) pthread_mutex_init(&ctx->mutex, NULL);
 
  (void) pthread_cond_init(&ctx->cond, NULL);
 
  (void) pthread_cond_init(&ctx->sq_empty, NULL);
 
  (void) pthread_cond_init(&ctx->sq_full, NULL);
 
 
  // Start master (listening) thread
 
  mg_start_thread((mg_thread_func_t) master_thread, ctx);
 
 
  // Start worker threads
 
  for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
 
    if (mg_start_thread((mg_thread_func_t) worker_thread, ctx) != 0) {
 
      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
 
    } else {
 
      ctx->num_threads++;
 
    }
 
  }
 
 
  return ctx;
 
}
 
// Copyright (c) 2004-2012 Sergey Lyubka
 
//
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
// of this software and associated documentation files (the "Software"), to deal
 
// in the Software without restriction, including without limitation the rights
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
// copies of the Software, and to permit persons to whom the Software is
 
// furnished to do so, subject to the following conditions:
 
//
 
// The above copyright notice and this permission notice shall be included in
 
// all copies or substantial portions of the Software.
 
//
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
// THE SOFTWARE.
 

	
 
#include <netinet/in.h>
 

	
 
#if defined(_WIN32)
 
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005
 
#else
 
#define _XOPEN_SOURCE 600     // For flockfile() on Linux
 
#define _LARGEFILE_SOURCE     // Enable 64-bit file offsets
 
#define __STDC_FORMAT_MACROS  // <inttypes.h> wants this for C++
 
#define __STDC_LIMIT_MACROS   // C++ wants that for INT64_MAX
 
#endif
 

	
 
#if defined(__SYMBIAN32__)
 
#define NO_SSL // SSL is not supported
 
#define NO_CGI // CGI is not supported
 
#define PATH_MAX FILENAME_MAX
 
#endif // __SYMBIAN32__
 

	
 
#ifndef _WIN32_WCE // Some ANSI #includes are not available on Windows CE
 
#include <sys/types.h>
 
#include <sys/stat.h>
 
#include <errno.h>
 
#include <signal.h>
 
#include <fcntl.h>
 
#endif // !_WIN32_WCE
 

	
 
#include <time.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 
#include <assert.h>
 
#include <string.h>
 
#include <ctype.h>
 
#include <limits.h>
 
#include <stddef.h>
 
#include <stdio.h>
 

	
 
#if defined(_WIN32) && !defined(__SYMBIAN32__) // Windows specific
 
#define _WIN32_WINNT 0x0400 // To make it link in VS2005
 
#include <windows.h>
 

	
 
#ifndef PATH_MAX
 
#define PATH_MAX MAX_PATH
 
#endif
 

	
 
#ifndef _WIN32_WCE
 
#include <process.h>
 
#include <direct.h>
 
#include <io.h>
 
#else // _WIN32_WCE
 
#include <winsock2.h>
 
#include <ws2tcpip.h>
 
#define NO_CGI // WinCE has no pipes
 

	
 
typedef long off_t;
 

	
 
#define errno   GetLastError()
 
#define strerror(x)  _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
 
#endif // _WIN32_WCE
 

	
 
#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
 
      ((uint64_t)((uint32_t)(hi))) << 32))
 
#define RATE_DIFF 10000000 // 100 nsecs
 
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
 
#define SYS2UNIX_TIME(lo, hi) \
 
  (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)
 

	
 
// Visual Studio 6 does not know __func__ or __FUNCTION__
 
// The rest of MS compilers use __FUNCTION__, not C99 __func__
 
// Also use _strtoui64 on modern M$ compilers
 
#if defined(_MSC_VER) && _MSC_VER < 1300
 
#define STRX(x) #x
 
#define STR(x) STRX(x)
 
#define __func__ "line " STR(__LINE__)
 
#define strtoull(x, y, z) strtoul(x, y, z)
 
#define strtoll(x, y, z) strtol(x, y, z)
 
#else
 
#define __func__  __FUNCTION__
 
#define strtoull(x, y, z) _strtoui64(x, y, z)
 
#define strtoll(x, y, z) _strtoi64(x, y, z)
 
#endif // _MSC_VER
 

	
 
#define ERRNO   GetLastError()
 
#define NO_SOCKLEN_T
 
#define SSL_LIB   "ssleay32.dll"
 
#define CRYPTO_LIB  "libeay32.dll"
 
#define DIRSEP '\\'
 
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
 
#define O_NONBLOCK  0
 
#if !defined(EWOULDBLOCK)
 
#define EWOULDBLOCK  WSAEWOULDBLOCK
 
#endif // !EWOULDBLOCK
 
#define _POSIX_
 
#define INT64_FMT  "I64d"
 

	
 
#define WINCDECL __cdecl
 
#define SHUT_WR 1
 
#define snprintf _snprintf
 
#define vsnprintf _vsnprintf
 
#define mg_sleep(x) Sleep(x)
 

	
 
#define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY)
 
#define popen(x, y) _popen(x, y)
 
#define pclose(x) _pclose(x)
 
#define close(x) _close(x)
 
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
 
#define RTLD_LAZY  0
 
#define fseeko(x, y, z) _lseeki64(_fileno(x), (y), (z))
 
#define fdopen(x, y) _fdopen((x), (y))
 
#define write(x, y, z) _write((x), (y), (unsigned) z)
 
#define read(x, y, z) _read((x), (y), (unsigned) z)
 
#define flockfile(x) EnterCriticalSection(&global_log_file_lock)
 
#define funlockfile(x) LeaveCriticalSection(&global_log_file_lock)
 

	
 
#if !defined(fileno)
 
#define fileno(x) _fileno(x)
 
#endif // !fileno MINGW #defines fileno
 

	
 
typedef HANDLE pthread_mutex_t;
 
typedef struct {HANDLE signal, broadcast;} pthread_cond_t;
 
typedef DWORD pthread_t;
 
#define pid_t HANDLE // MINGW typedefs pid_t to int. Using #define here.
 

	
 
struct timespec {
 
  long tv_nsec;
 
  long tv_sec;
 
};
 

	
 
static int pthread_mutex_lock(pthread_mutex_t *);
 
static int pthread_mutex_unlock(pthread_mutex_t *);
 
static FILE *mg_fopen(const char *path, const char *mode);
 

	
 
#if defined(HAVE_STDINT)
 
#include <stdint.h>
 
#else
 
typedef unsigned int  uint32_t;
 
typedef unsigned short  uint16_t;
 
typedef unsigned __int64 uint64_t;
 
typedef __int64   int64_t;
 
#define INT64_MAX  9223372036854775807
 
#endif // HAVE_STDINT
 

	
 
// POSIX dirent interface
 
struct dirent {
 
  char d_name[PATH_MAX];
 
};
 

	
 
typedef struct DIR {
 
  HANDLE   handle;
 
  WIN32_FIND_DATAW info;
 
  struct dirent  result;
 
} DIR;
 

	
 
#else    // UNIX  specific
 
#include <sys/wait.h>
 
#include <sys/socket.h>
 
#include <sys/select.h>
 
#include <netinet/in.h>
 
#include <arpa/inet.h>
 
#include <sys/time.h>
 
#include <stdint.h>
 
#include <inttypes.h>
 
#include <netdb.h>
 

	
 
#include <pwd.h>
 
#include <unistd.h>
 
#include <dirent.h>
 
#if !defined(NO_SSL_DL) && !defined(NO_SSL)
 
#include <dlfcn.h>
 
#endif
 
#include <pthread.h>
 
#if defined(__MACH__)
 
#define SSL_LIB   "libssl.dylib"
 
#define CRYPTO_LIB  "libcrypto.dylib"
 
#else
 
#if !defined(SSL_LIB)
 
#define SSL_LIB   "libssl.so"
 
#endif
 
#if !defined(CRYPTO_LIB)
 
#define CRYPTO_LIB  "libcrypto.so"
 
#endif
 
#endif
 
#define DIRSEP   '/'
 
#define IS_DIRSEP_CHAR(c) ((c) == '/')
 
#ifndef O_BINARY
 
#define O_BINARY  0
 
#endif // O_BINARY
 
#define closesocket(a) close(a)
 
#define mg_fopen(x, y) fopen(x, y)
 
#define mg_mkdir(x, y) mkdir(x, y)
 
#define mg_remove(x) remove(x)
 
#define mg_rename(x, y) rename(x, y)
 
#define mg_sleep(x) usleep((x) * 1000)
 
#define ERRNO errno
 
#define INVALID_SOCKET (-1)
 
#define INT64_FMT PRId64
 
typedef int SOCKET;
 
#define WINCDECL
 

	
 
#endif // End of Windows and UNIX specific includes
 

	
 
#include "mongoose.h"
 

	
 
#define MONGOOSE_VERSION "3.3"
 
#define PASSWORDS_FILE_NAME ".htpasswd"
 
#define CGI_ENVIRONMENT_SIZE 4096
 
#define MAX_CGI_ENVIR_VARS 64
 
#define MG_BUF_LEN 8192
 
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
 

	
 
#ifdef _WIN32
 
static CRITICAL_SECTION global_log_file_lock;
 
static pthread_t pthread_self(void) {
 
  return GetCurrentThreadId();
 
}
 
#endif // _WIN32
 

	
 
#if defined(DEBUG)
 
#define DEBUG_TRACE(x) do { \
 
  flockfile(stdout); \
 
  printf("*** %lu.%p.%s.%d: ", \
 
         (unsigned long) time(NULL), (void *) pthread_self(), \
 
         __func__, __LINE__); \
 
  printf x; \
 
  putchar('\n'); \
 
  fflush(stdout); \
 
  funlockfile(stdout); \
 
} while (0)
 
#else
 
#define DEBUG_TRACE(x)
 
#endif // DEBUG
 

	
 
// Darwin prior to 7.0 and Win32 do not have socklen_t
 
#ifdef NO_SOCKLEN_T
 
typedef int socklen_t;
 
#endif // NO_SOCKLEN_T
 
#define _DARWIN_UNLIMITED_SELECT
 

	
 
#if !defined(MSG_NOSIGNAL)
 
#define MSG_NOSIGNAL 0
 
#endif
 

	
 
#if !defined(SOMAXCONN)
 
#define SOMAXCONN 100
 
#endif
 

	
 
static const char *http_500_error = "Internal Server Error";
 

	
 
// Snatched from OpenSSL includes. I put the prototypes here to be independent
 
// from the OpenSSL source installation. Having this, mongoose + SSL can be
 
// built on any system with binary SSL libraries installed.
 
typedef struct ssl_st SSL;
 
typedef struct ssl_method_st SSL_METHOD;
 
typedef struct ssl_ctx_st SSL_CTX;
 

	
 
#define SSL_ERROR_WANT_READ 2
 
#define SSL_ERROR_WANT_WRITE 3
 
#define SSL_FILETYPE_PEM 1
 
#define CRYPTO_LOCK  1
 

	
 
#if defined(NO_SSL_DL)
 
extern void SSL_free(SSL *);
 
extern int SSL_accept(SSL *);
 
extern int SSL_connect(SSL *);
 
extern int SSL_read(SSL *, void *, int);
 
extern int SSL_write(SSL *, const void *, int);
 
extern int SSL_get_error(const SSL *, int);
 
extern int SSL_set_fd(SSL *, int);
 
extern SSL *SSL_new(SSL_CTX *);
 
extern SSL_CTX *SSL_CTX_new(SSL_METHOD *);
 
extern SSL_METHOD *SSLv23_server_method(void);
 
extern SSL_METHOD *SSLv23_client_method(void);
 
extern int SSL_library_init(void);
 
extern void SSL_load_error_strings(void);
 
extern int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int);
 
extern int SSL_CTX_use_certificate_file(SSL_CTX *, const char *, int);
 
extern int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *);
 
extern void SSL_CTX_set_default_passwd_cb(SSL_CTX *, mg_callback_t);
 
extern void SSL_CTX_free(SSL_CTX *);
 
extern unsigned long ERR_get_error(void);
 
extern char *ERR_error_string(unsigned long, char *);
 
extern int CRYPTO_num_locks(void);
 
extern void CRYPTO_set_locking_callback(void (*)(int, int, const char *, int));
 
extern void CRYPTO_set_id_callback(unsigned long (*)(void));
 
#else
 
// Dynamically loaded SSL functionality
 
struct ssl_func {
 
  const char *name;   // SSL function name
 
  void  (*ptr)(void); // Function pointer
 
};
 

	
 
#define SSL_free (* (void (*)(SSL *)) ssl_sw[0].ptr)
 
#define SSL_accept (* (int (*)(SSL *)) ssl_sw[1].ptr)
 
#define SSL_connect (* (int (*)(SSL *)) ssl_sw[2].ptr)
 
#define SSL_read (* (int (*)(SSL *, void *, int)) ssl_sw[3].ptr)
 
#define SSL_write (* (int (*)(SSL *, const void *,int)) ssl_sw[4].ptr)
 
#define SSL_get_error (* (int (*)(SSL *, int)) ssl_sw[5].ptr)
 
#define SSL_set_fd (* (int (*)(SSL *, SOCKET)) ssl_sw[6].ptr)
 
#define SSL_new (* (SSL * (*)(SSL_CTX *)) ssl_sw[7].ptr)
 
#define SSL_CTX_new (* (SSL_CTX * (*)(SSL_METHOD *)) ssl_sw[8].ptr)
 
#define SSLv23_server_method (* (SSL_METHOD * (*)(void)) ssl_sw[9].ptr)
 
#define SSL_library_init (* (int (*)(void)) ssl_sw[10].ptr)
 
#define SSL_CTX_use_PrivateKey_file (* (int (*)(SSL_CTX *, \
 
        const char *, int)) ssl_sw[11].ptr)
 
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
 
        const char *, int)) ssl_sw[12].ptr)
 
#define SSL_CTX_set_default_passwd_cb \
 
  (* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr)
 
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
 
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
 
#define SSL_CTX_use_certificate_chain_file \
 
  (* (int (*)(SSL_CTX *, const char *)) ssl_sw[16].ptr)
 
#define SSLv23_client_method (* (SSL_METHOD * (*)(void)) ssl_sw[17].ptr)
 

	
 
#define CRYPTO_num_locks (* (int (*)(void)) crypto_sw[0].ptr)
 
#define CRYPTO_set_locking_callback \
 
  (* (void (*)(void (*)(int, int, const char *, int))) crypto_sw[1].ptr)
 
#define CRYPTO_set_id_callback \
 
  (* (void (*)(unsigned long (*)(void))) crypto_sw[2].ptr)
 
#define ERR_get_error (* (unsigned long (*)(void)) crypto_sw[3].ptr)
 
#define ERR_error_string (* (char * (*)(unsigned long,char *)) crypto_sw[4].ptr)
 

	
 
// set_ssl_option() function updates this array.
 
// It loads SSL library dynamically and changes NULLs to the actual addresses
 
// of respective functions. The macros above (like SSL_connect()) are really
 
// just calling these functions indirectly via the pointer.
 
static struct ssl_func ssl_sw[] = {
 
  {"SSL_free",   NULL},
 
  {"SSL_accept",   NULL},
 
  {"SSL_connect",   NULL},
 
  {"SSL_read",   NULL},
 
  {"SSL_write",   NULL},
 
  {"SSL_get_error",  NULL},
 
  {"SSL_set_fd",   NULL},
 
  {"SSL_new",   NULL},
 
  {"SSL_CTX_new",   NULL},
 
  {"SSLv23_server_method", NULL},
 
  {"SSL_library_init",  NULL},
 
  {"SSL_CTX_use_PrivateKey_file", NULL},
 
  {"SSL_CTX_use_certificate_file",NULL},
 
  {"SSL_CTX_set_default_passwd_cb",NULL},
 
  {"SSL_CTX_free",  NULL},
 
  {"SSL_load_error_strings", NULL},
 
  {"SSL_CTX_use_certificate_chain_file", NULL},
 
  {"SSLv23_client_method", NULL},
 
  {NULL,    NULL}
 
};
 

	
 
// Similar array as ssl_sw. These functions could be located in different lib.
 
#if !defined(NO_SSL)
 
static struct ssl_func crypto_sw[] = {
 
  {"CRYPTO_num_locks",  NULL},
 
  {"CRYPTO_set_locking_callback", NULL},
 
  {"CRYPTO_set_id_callback", NULL},
 
  {"ERR_get_error",  NULL},
 
  {"ERR_error_string", NULL},
 
  {NULL,    NULL}
 
};
 
#endif // NO_SSL
 
#endif // NO_SSL_DL
 

	
 
static const char *month_names[] = {
 
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
};
 

	
 
// Unified socket address. For IPv6 support, add IPv6 address structure
 
// in the union u.
 
union usa {
 
  struct sockaddr sa;
 
  struct sockaddr_in sin;
 
#if defined(USE_IPV6)
 
  struct sockaddr_in6 sin6;
 
#endif
 
};
 

	
 
// Describes a string (chunk of memory).
 
struct vec {
 
  const char *ptr;
 
  size_t len;
 
};
 

	
 
// Structure used by mg_stat() function. Uses 64 bit file length.
 
struct mgstat {
 
  int is_directory;  // Directory marker
 
  int64_t size;      // File size
 
  time_t mtime;      // Modification time
 
};
 

	
 
// Describes listening socket, or socket which was accept()-ed by the master
 
// thread and queued for future handling by the worker thread.
 
struct socket {
 
  struct socket *next;  // Linkage
 
  SOCKET sock;          // Listening socket
 
  union usa lsa;        // Local socket address
 
  union usa rsa;        // Remote socket address
 
  int is_ssl;           // Is socket SSL-ed
 
};
 

	
 
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
 
enum {
 
  CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
 
  MAX_REQUEST_SIZE, PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS,
 
  ACCESS_LOG_FILE, SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
 
  GLOBAL_PASSWORDS_FILE, INDEX_FILES, ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST,
 
  EXTRA_MIME_TYPES, LISTENING_PORTS, DOCUMENT_ROOT, SSL_CERTIFICATE,
 
  NUM_THREADS, RUN_AS_USER, REWRITE, HIDE_FILES,
 
  NUM_OPTIONS
 
};
 

	
 
static const char *config_options[] = {
 
  "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
 
  "E", "cgi_environment", NULL,
 
  "G", "put_delete_passwords_file", NULL,
 
  "I", "cgi_interpreter", NULL,
 
  "M", "max_request_size", "16384",
 
  "P", "protect_uri", NULL,
 
  "R", "authentication_domain", "mydomain.com",
 
  "S", "ssi_pattern", "**.shtml$|**.shtm$",
 
  "a", "access_log_file", NULL,
 
  "c", "ssl_chain_file", NULL,
 
  "d", "enable_directory_listing", "yes",
 
  "e", "error_log_file", NULL,
 
  "g", "global_passwords_file", NULL,
 
  "i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
 
  "k", "enable_keep_alive", "no",
 
  "l", "access_control_list", NULL,
 
  "m", "extra_mime_types", NULL,
 
  "p", "listening_ports", "8080",
 
  "r", "document_root",  ".",
 
  "s", "ssl_certificate", NULL,
 
  "t", "num_threads", "10",
 
  "u", "run_as_user", NULL,
 
  "w", "url_rewrite_patterns", NULL,
 
  "x", "hide_files_patterns", NULL,
 
  NULL
 
};
 
#define ENTRIES_PER_CONFIG_OPTION 3
 

	
 
struct mg_context {
 
  volatile int stop_flag;       // Should we stop event loop
 
  SSL_CTX *ssl_ctx;             // SSL context
 
  SSL_CTX *client_ssl_ctx;      // Client SSL context
 
  char *config[NUM_OPTIONS];    // Mongoose configuration parameters
 
  mg_callback_t user_callback;  // User-defined callback function
 
  void *user_data;              // User-defined data
 

	
 
  struct socket *listening_sockets;
 

	
 
  volatile int num_threads;  // Number of threads
 
  pthread_mutex_t mutex;     // Protects (max|num)_threads
 
  pthread_cond_t  cond;      // Condvar for tracking workers terminations
 

	
 
  struct socket queue[20];   // Accepted sockets
 
  volatile int sq_head;      // Head of the socket queue
 
  volatile int sq_tail;      // Tail of the socket queue
 
  pthread_cond_t sq_full;    // Signaled when socket is produced
 
  pthread_cond_t sq_empty;   // Signaled when socket is consumed
 
};
 

	
 
struct mg_connection {
 
  struct mg_request_info request_info;
 
  struct mg_context *ctx;
 
  SSL *ssl;                   // SSL descriptor
 
  struct socket client;       // Connected client
 
  time_t birth_time;          // Time when request was received
 
  int64_t num_bytes_sent;     // Total bytes sent to client
 
  int64_t content_len;        // Content-Length header value
 
  int64_t consumed_content;   // How many bytes of content have been read
 
  char *buf;                  // Buffer for received data
 
  char *path_info;            // PATH_INFO part of the URL
 
  char *body;                 // Pointer to not-read yet buffered body data
 
  char *next_request;         // Pointer to the buffered next request
 
  int must_close;             // 1 if connection must be closed
 
  int buf_size;               // Buffer size
 
  int request_len;            // Size of the request + headers in a buffer
 
  int data_len;               // Total size of data in a buffer
 
};
 

	
 
const char **mg_get_valid_option_names(void) {
 
  return config_options;
 
}
 

	
 
static void *call_user(struct mg_connection *conn, enum mg_event event) {
 
  conn->request_info.user_data = conn->ctx->user_data;
 
  return conn->ctx->user_callback == NULL ? NULL :
 
    conn->ctx->user_callback(event, conn);
 
}
 

	
 
static int get_option_index(const char *name) {
 
  int i;
 

	
 
  for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
 
    if (strcmp(config_options[i], name) == 0 ||
 
        strcmp(config_options[i + 1], name) == 0) {
 
      return i / ENTRIES_PER_CONFIG_OPTION;
 
    }
 
  }
 
  return -1;
 
}
 

	
 
const char *mg_get_option(const struct mg_context *ctx, const char *name) {
 
  int i;
 
  if ((i = get_option_index(name)) == -1) {
 
    return NULL;
 
  } else if (ctx->config[i] == NULL) {
 
    return "";
 
  } else {
 
    return ctx->config[i];
 
  }
 
}
 

	
 
static void sockaddr_to_string(char *buf, size_t len,
 
                                     const union usa *usa) {
 
  buf[0] = '\0';
 
#if defined(USE_IPV6)
 
  inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
 
            (void *) &usa->sin.sin_addr :
 
            (void *) &usa->sin6.sin6_addr, buf, len);
 
#elif defined(_WIN32)
 
  // Only Windoze Vista (and newer) have inet_ntop()
 
  strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
 
#else
 
  inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
 
#endif
 
}
 

	
 
// Print error message to the opened error log stream.
 
static void cry(struct mg_connection *conn, const char *fmt, ...) {
 
  char buf[MG_BUF_LEN], src_addr[20];
 
  va_list ap;
 
  FILE *fp;
 
  time_t timestamp;
 

	
 
  va_start(ap, fmt);
 
  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
 
  va_end(ap);
 

	
 
  // Do not lock when getting the callback value, here and below.
 
  // I suppose this is fine, since function cannot disappear in the
 
  // same way string option can.
 
  conn->request_info.log_message = buf;
 
  if (call_user(conn, MG_EVENT_LOG) == NULL) {
 
    fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
 
      mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
 

	
 
    if (fp != NULL) {
 
      flockfile(fp);
 
      timestamp = time(NULL);
 

	
 
      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 
      fprintf(fp, "[%010lu] [error] [client %s] ", (unsigned long) timestamp,
 
              src_addr);
 

	
 
      if (conn->request_info.request_method != NULL) {
 
        fprintf(fp, "%s %s: ", conn->request_info.request_method,
 
                conn->request_info.uri);
 
      }
 

	
 
      (void) fprintf(fp, "%s", buf);
 
      fputc('\n', fp);
 
      funlockfile(fp);
 
      if (fp != stderr) {
 
        fclose(fp);
 
      }
 
    }
 
  }
 
  conn->request_info.log_message = NULL;
 
}
 

	
 
// Return fake connection structure. Used for logging, if connection
 
// is not applicable at the moment of logging.
 
static struct mg_connection *fc(struct mg_context *ctx) {
 
  static struct mg_connection fake_connection;
 
  fake_connection.ctx = ctx;
 
  return &fake_connection;
 
}
 

	
 
const char *mg_version(void) {
 
  return MONGOOSE_VERSION;
 
}
 

	
 
const struct mg_request_info *
 
mg_get_request_info(const struct mg_connection *conn) {
 
  return &conn->request_info;
 
}
 

	
 
static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
 
  for (; *src != '\0' && n > 1; n--) {
 
    *dst++ = *src++;
 
  }
 
  *dst = '\0';
 
}
 

	
 
static int lowercase(const char *s) {
 
  return tolower(* (const unsigned char *) s);
 
}
 

	
 
static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
 
  int diff = 0;
 

	
 
  if (len > 0)
 
    do {
 
      diff = lowercase(s1++) - lowercase(s2++);
 
    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
 

	
 
  return diff;
 
}
 

	
 
static int mg_strcasecmp(const char *s1, const char *s2) {
 
  int diff;
 

	
 
  do {
 
    diff = lowercase(s1++) - lowercase(s2++);
 
  } while (diff == 0 && s1[-1] != '\0');
 

	
 
  return diff;
 
}
 

	
 
static char * mg_strndup(const char *ptr, size_t len) {
 
  char *p;
 

	
 
  if ((p = (char *) malloc(len + 1)) != NULL) {
 
    mg_strlcpy(p, ptr, len + 1);
 
  }
 

	
 
  return p;
 
}
 

	
 
static char * mg_strdup(const char *str) {
 
  return mg_strndup(str, strlen(str));
 
}
 

	
 
// Like snprintf(), but never returns negative value, or a value
 
// that is larger than a supplied buffer.
 
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
 
// in his audit report.
 
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
 
                        const char *fmt, va_list ap) {
 
  int n;
 

	
 
  if (buflen == 0)
 
    return 0;
 

	
 
  n = vsnprintf(buf, buflen, fmt, ap);
 

	
 
  if (n < 0) {
 
    cry(conn, "vsnprintf error");
 
    n = 0;
 
  } else if (n >= (int) buflen) {
 
    cry(conn, "truncating vsnprintf buffer: [%.*s]",
 
        n > 200 ? 200 : n, buf);
 
    n = (int) buflen - 1;
 
  }
 
  buf[n] = '\0';
 

	
 
  return n;
 
}
 

	
 
static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
 
                       const char *fmt, ...) {
 
  va_list ap;
 
  int n;
 

	
 
  va_start(ap, fmt);
 
  n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
 
  va_end(ap);
 

	
 
  return n;
 
}
 

	
 
// Skip the characters until one of the delimiters characters found.
 
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
 
// Advance pointer to buffer to the next word. Return found 0-terminated word.
 
// Delimiters can be quoted with quotechar.
 
static char *skip_quoted(char **buf, const char *delimiters,
 
                         const char *whitespace, char quotechar) {
 
  char *p, *begin_word, *end_word, *end_whitespace;
 

	
 
  begin_word = *buf;
 
  end_word = begin_word + strcspn(begin_word, delimiters);
 

	
 
  // Check for quotechar
 
  if (end_word > begin_word) {
 
    p = end_word - 1;
 
    while (*p == quotechar) {
 
      // If there is anything beyond end_word, copy it
 
      if (*end_word == '\0') {
 
        *p = '\0';
 
        break;
 
      } else {
 
        size_t end_off = strcspn(end_word + 1, delimiters);
 
        memmove (p, end_word, end_off + 1);
 
        p += end_off; // p must correspond to end_word - 1
 
        end_word += end_off + 1;
 
      }
 
    }
 
    for (p++; p < end_word; p++) {
 
      *p = '\0';
 
    }
 
  }
 

	
 
  if (*end_word == '\0') {
 
    *buf = end_word;
 
  } else {
 
    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);
 

	
 
    for (p = end_word; p < end_whitespace; p++) {
 
      *p = '\0';
 
    }
 

	
 
    *buf = end_whitespace;
 
  }
 

	
 
  return begin_word;
 
}
 

	
 
// Simplified version of skip_quoted without quote char
 
// and whitespace == delimiters
 
static char *skip(char **buf, const char *delimiters) {
 
  return skip_quoted(buf, delimiters, delimiters, 0);
 
}
 

	
 

	
 
// Return HTTP header value, or NULL if not found.
 
static const char *get_header(const struct mg_request_info *ri,
 
                              const char *name) {
 
  int i;
 

	
 
  for (i = 0; i < ri->num_headers; i++)
 
    if (!mg_strcasecmp(name, ri->http_headers[i].name))
 
      return ri->http_headers[i].value;
 

	
 
  return NULL;
 
}
 

	
 
const char *mg_get_header(const struct mg_connection *conn, const char *name) {
 
  return get_header(&conn->request_info, name);
 
}
 

	
 
// A helper function for traversing a comma separated list of values.
 
// It returns a list pointer shifted to the next value, or NULL if the end
 
// of the list found.
 
// Value is stored in val vector. If value has form "x=y", then eq_val
 
// vector is initialized to point to the "y" part, and val vector length
 
// is adjusted to point only to "x".
 
static const char *next_option(const char *list, struct vec *val,
 
                               struct vec *eq_val) {
 
  if (list == NULL || *list == '\0') {
 
    // End of the list
 
    list = NULL;
 
  } else {
 
    val->ptr = list;
 
    if ((list = strchr(val->ptr, ',')) != NULL) {
 
      // Comma found. Store length and shift the list ptr
 
      val->len = list - val->ptr;
 
      list++;
 
    } else {
 
      // This value is the last one
 
      list = val->ptr + strlen(val->ptr);
 
      val->len = list - val->ptr;
 
    }
 

	
 
    if (eq_val != NULL) {
 
      // Value has form "x=y", adjust pointers and lengths
 
      // so that val points to "x", and eq_val points to "y".
 
      eq_val->len = 0;
 
      eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
 
      if (eq_val->ptr != NULL) {
 
        eq_val->ptr++;  // Skip over '=' character
 
        eq_val->len = val->ptr + val->len - eq_val->ptr;
 
        val->len = (eq_val->ptr - val->ptr) - 1;
 
      }
 
    }
 
  }
 

	
 
  return list;
 
}
 

	
 
static int match_prefix(const char *pattern, int pattern_len, const char *str) {
 
  const char *or_str;
 
  int i, j, len, res;
 

	
 
  if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
 
    res = match_prefix(pattern, or_str - pattern, str);
 
    return res > 0 ? res :
 
        match_prefix(or_str + 1, (pattern + pattern_len) - (or_str + 1), str);
 
  }
 

	
 
  i = j = 0;
 
  res = -1;
 
  for (; i < pattern_len; i++, j++) {
 
    if (pattern[i] == '?' && str[j] != '\0') {
 
      continue;
 
    } else if (pattern[i] == '$') {
 
      return str[j] == '\0' ? j : -1;
 
    } else if (pattern[i] == '*') {
 
      i++;
 
      if (pattern[i] == '*') {
 
        i++;
 
        len = (int) strlen(str + j);
 
      } else {
 
        len = (int) strcspn(str + j, "/");
 
      }
 
      if (i == pattern_len) {
 
        return j + len;
 
      }
 
      do {
 
        res = match_prefix(pattern + i, pattern_len - i, str + j + len);
 
      } while (res == -1 && len-- > 0);
 
      return res == -1 ? -1 : j + res + len;
 
    } else if (pattern[i] != str[j]) {
 
      return -1;
 
    }
 
  }
 
  return j;
 
}
 

	
 
// HTTP 1.1 assumes keep alive if "Connection:" header is not set
 
// This function must tolerate situations when connection info is not
 
// set up, for example if request parsing failed.
 
static int should_keep_alive(const struct mg_connection *conn) {
 
  const char *http_version = conn->request_info.http_version;
 
  const char *header = mg_get_header(conn, "Connection");
 
  if (conn->must_close ||
 
      conn->request_info.status_code == 401 ||
 
      mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 ||
 
      (header != NULL && mg_strcasecmp(header, "keep-alive") != 0) ||
 
      (header == NULL && http_version && strcmp(http_version, "1.1"))) {
 
    return 0;
 
  }
 
  return 1;
 
}
 

	
 
static const char *suggest_connection_header(const struct mg_connection *conn) {
 
  return should_keep_alive(conn) ? "keep-alive" : "close";
 
}
 

	
 
static void send_http_error(struct mg_connection *, int, const char *,
 
                            PRINTF_FORMAT_STRING(const char *fmt), ...)
 
  PRINTF_ARGS(4, 5);
 

	
 

	
 
static void send_http_error(struct mg_connection *conn, int status,
 
                            const char *reason, const char *fmt, ...) {
 
  char buf[MG_BUF_LEN];
 
  va_list ap;
 
  int len;
 

	
 
  conn->request_info.status_code = status;
 

	
 
  if (call_user(conn, MG_HTTP_ERROR) == NULL) {
 
    buf[0] = '\0';
 
    len = 0;
 

	
 
    // Errors 1xx, 204 and 304 MUST NOT send a body
 
    if (status > 199 && status != 204 && status != 304) {
 
      len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
 
      buf[len++] = '\n';
 

	
 
      va_start(ap, fmt);
 
      len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
 
      va_end(ap);
 
    }
 
    DEBUG_TRACE(("[%s]", buf));
 

	
 
    mg_printf(conn, "HTTP/1.1 %d %s\r\n"
 
              "Content-Type: text/plain\r\n"
 
              "Content-Length: %d\r\n"
 
              "Connection: %s\r\n\r\n", status, reason, len,
 
              suggest_connection_header(conn));
 
    conn->num_bytes_sent += mg_printf(conn, "%s", buf);
 
  }
 
}
 

	
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
static int pthread_mutex_init(pthread_mutex_t *mutex, void *unused) {
 
  unused = NULL;
 
  *mutex = CreateMutex(NULL, FALSE, NULL);
 
  return *mutex == NULL ? -1 : 0;
 
}
 

	
 
static int pthread_mutex_destroy(pthread_mutex_t *mutex) {
 
  return CloseHandle(*mutex) == 0 ? -1 : 0;
 
}
 

	
 
static int pthread_mutex_lock(pthread_mutex_t *mutex) {
 
  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
 
}
 

	
 
static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
 
  return ReleaseMutex(*mutex) == 0 ? -1 : 0;
 
}
 

	
 
static int pthread_cond_init(pthread_cond_t *cv, const void *unused) {
 
  unused = NULL;
 
  cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
 
  cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
 
  return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
 
}
 

	
 
static int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) {
 
  HANDLE handles[] = {cv->signal, cv->broadcast};
 
  ReleaseMutex(*mutex);
 
  WaitForMultipleObjects(2, handles, FALSE, INFINITE);
 
  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0? 0 : -1;
 
}
 

	
 
static int pthread_cond_signal(pthread_cond_t *cv) {
 
  return SetEvent(cv->signal) == 0 ? -1 : 0;
 
}
 

	
 
static int pthread_cond_broadcast(pthread_cond_t *cv) {
 
  // Implementation with PulseEvent() has race condition, see
 
  // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
 
  return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
 
}
 

	
 
static int pthread_cond_destroy(pthread_cond_t *cv) {
 
  return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
 
}
 

	
 
// For Windows, change all slashes to backslashes in path names.
 
static void change_slashes_to_backslashes(char *path) {
 
  int i;
 

	
 
  for (i = 0; path[i] != '\0'; i++) {
 
    if (path[i] == '/')
 
      path[i] = '\\';
 
    // i > 0 check is to preserve UNC paths, like \\server\file.txt
 
    if (path[i] == '\\' && i > 0)
 
      while (path[i + 1] == '\\' || path[i + 1] == '/')
 
        (void) memmove(path + i + 1,
 
            path + i + 2, strlen(path + i + 1));
 
  }
 
}
 

	
 
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
 
// wbuf and wbuf_len is a target buffer and its length.
 
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
 
  char buf[PATH_MAX], buf2[PATH_MAX], *p;
 

	
 
  mg_strlcpy(buf, path, sizeof(buf));
 
  change_slashes_to_backslashes(buf);
 

	
 
  // Point p to the end of the file name
 
  p = buf + strlen(buf) - 1;
 

	
 
  // Trim trailing backslash character
 
  while (p > buf && *p == '\\' && p[-1] != ':') {
 
    *p-- = '\0';
 
  }
 

	
 
   // Protect from CGI code disclosure.
 
   // This is very nasty hole. Windows happily opens files with
 
   // some garbage in the end of file name. So fopen("a.cgi    ", "r")
 
   // actually opens "a.cgi", and does not return an error!
 
  if (*p == 0x20 ||               // No space at the end
 
      (*p == 0x2e && p > buf) ||  // No '.' but allow '.' as full path
 
      *p == 0x2b ||               // No '+'
 
      (*p & ~0x7f)) {             // And generally no non-ASCII chars
 
    (void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
 
    wbuf[0] = L'\0';
 
  } else {
 
    // Convert to Unicode and back. If doubly-converted string does not
 
    // match the original, something is fishy, reject.
 
    memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
 
    MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
 
    WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
 
                        NULL, NULL);
 
    if (strcmp(buf, buf2) != 0) {
 
      wbuf[0] = L'\0';
 
    }
 
  }
 
}
 

	
 
#if defined(_WIN32_WCE)
 
static time_t time(time_t *ptime) {
 
  time_t t;
 
  SYSTEMTIME st;
 
  FILETIME ft;
 

	
 
  GetSystemTime(&st);
 
  SystemTimeToFileTime(&st, &ft);
 
  t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime);
 

	
 
  if (ptime != NULL) {
 
    *ptime = t;
 
  }
 

	
 
  return t;
 
}
 

	
 
static struct tm *localtime(const time_t *ptime, struct tm *ptm) {
 
  int64_t t = ((int64_t) *ptime) * RATE_DIFF + EPOCH_DIFF;
 
  FILETIME ft, lft;
 
  SYSTEMTIME st;
 
  TIME_ZONE_INFORMATION tzinfo;
 

	
 
  if (ptm == NULL) {
 
    return NULL;
 
  }
 

	
 
  * (int64_t *) &ft = t;
 
  FileTimeToLocalFileTime(&ft, &lft);
 
  FileTimeToSystemTime(&lft, &st);
 
  ptm->tm_year = st.wYear - 1900;
 
  ptm->tm_mon = st.wMonth - 1;
 
  ptm->tm_wday = st.wDayOfWeek;
 
  ptm->tm_mday = st.wDay;
 
  ptm->tm_hour = st.wHour;
 
  ptm->tm_min = st.wMinute;
 
  ptm->tm_sec = st.wSecond;
 
  ptm->tm_yday = 0; // hope nobody uses this
 
  ptm->tm_isdst =
 
    GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0;
 

	
 
  return ptm;
 
}
 

	
 
static struct tm *gmtime(const time_t *ptime, struct tm *ptm) {
 
  // FIXME(lsm): fix this.
 
  return localtime(ptime, ptm);
 
}
 

	
 
static size_t strftime(char *dst, size_t dst_size, const char *fmt,
 
                       const struct tm *tm) {
 
  (void) snprintf(dst, dst_size, "implement strftime() for WinCE");
 
  return 0;
 
}
 
#endif
 

	
 
static int mg_rename(const char* oldname, const char* newname) {
 
  wchar_t woldbuf[PATH_MAX];
 
  wchar_t wnewbuf[PATH_MAX];
 

	
 
  to_unicode(oldname, woldbuf, ARRAY_SIZE(woldbuf));
 
  to_unicode(newname, wnewbuf, ARRAY_SIZE(wnewbuf));
 

	
 
  return MoveFileW(woldbuf, wnewbuf) ? 0 : -1;
 
}
 

	
 

	
 
static FILE *mg_fopen(const char *path, const char *mode) {
 
  wchar_t wbuf[PATH_MAX], wmode[20];
 

	
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 
  MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));
 

	
 
  return _wfopen(wbuf, wmode);
 
}
 

	
 
static int mg_stat(const char *path, struct mgstat *stp) {
 
  int ok = -1; // Error
 
  wchar_t wbuf[PATH_MAX];
 
  WIN32_FILE_ATTRIBUTE_DATA info;
 

	
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 

	
 
  if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
 
    stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
 
    stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
 
                               info.ftLastWriteTime.dwHighDateTime);
 
    stp->is_directory =
 
      info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
 
    ok = 0;  // Success
 
  }
 

	
 
  return ok;
 
}
 

	
 
static int mg_remove(const char *path) {
 
  wchar_t wbuf[PATH_MAX];
 
  to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
 
  return DeleteFileW(wbuf) ? 0 : -1;
 
}
 

	
 
static int mg_mkdir(const char *path, int mode) {
 
  char buf[PATH_MAX];
 
  wchar_t wbuf[PATH_MAX];
 

	
 
  mode = 0; // Unused
 
  mg_strlcpy(buf, path, sizeof(buf));
 
  change_slashes_to_backslashes(buf);
 

	
 
  (void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
 

	
 
  return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
 
}
 

	
 
// Implementation of POSIX opendir/closedir/readdir for Windows.
 
static DIR * opendir(const char *name) {
 
  DIR *dir = NULL;
 
  wchar_t wpath[PATH_MAX];
 
  DWORD attrs;
 

	
 
  if (name == NULL) {
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
 
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
 
  } else {
 
    to_unicode(name, wpath, ARRAY_SIZE(wpath));
 
    attrs = GetFileAttributesW(wpath);
 
    if (attrs != 0xFFFFFFFF &&
 
        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
 
      (void) wcscat(wpath, L"\\*");
 
      dir->handle = FindFirstFileW(wpath, &dir->info);
 
      dir->result.d_name[0] = '\0';
 
    } else {
 
      free(dir);
 
      dir = NULL;
 
    }
 
  }
 

	
 
  return dir;
 
}
 

	
 
static int closedir(DIR *dir) {
 
  int result = 0;
 

	
 
  if (dir != NULL) {
 
    if (dir->handle != INVALID_HANDLE_VALUE)
 
      result = FindClose(dir->handle) ? 0 : -1;
 

	
 
    free(dir);
 
  } else {
 
    result = -1;
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  }
 

	
 
  return result;
 
}
 

	
 
static struct dirent *readdir(DIR *dir) {
 
  struct dirent *result = 0;
 

	
 
  if (dir) {
 
    if (dir->handle != INVALID_HANDLE_VALUE) {
 
      result = &dir->result;
 
      (void) WideCharToMultiByte(CP_UTF8, 0,
 
          dir->info.cFileName, -1, result->d_name,
 
          sizeof(result->d_name), NULL, NULL);
 

	
 
      if (!FindNextFileW(dir->handle, &dir->info)) {
 
        (void) FindClose(dir->handle);
 
        dir->handle = INVALID_HANDLE_VALUE;
 
      }
 

	
 
    } else {
 
      SetLastError(ERROR_FILE_NOT_FOUND);
 
    }
 
  } else {
 
    SetLastError(ERROR_BAD_ARGUMENTS);
 
  }
 

	
 
  return result;
 
}
 

	
 
#define set_close_on_exec(fd) // No FD_CLOEXEC on Windows
 

	
 
int mg_start_thread(mg_thread_func_t f, void *p) {
 
  return _beginthread((void (__cdecl *)(void *)) f, 0, p) == -1L ? -1 : 0;
 
}
 

	
 
static HANDLE dlopen(const char *dll_name, int flags) {
 
  wchar_t wbuf[PATH_MAX];
 
  flags = 0; // Unused
 
  to_unicode(dll_name, wbuf, ARRAY_SIZE(wbuf));
 
  return LoadLibraryW(wbuf);
 
}
 

	
 
#if !defined(NO_CGI)
 
#define SIGKILL 0
 
static int kill(pid_t pid, int sig_num) {
 
  (void) TerminateProcess(pid, sig_num);
 
  (void) CloseHandle(pid);
 
  return 0;
 
}
 

	
 
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
 
                           char *envblk, char *envp[], int fd_stdin,
 
                           int fd_stdout, const char *dir) {
 
  HANDLE me;
 
  char *p, *interp, cmdline[PATH_MAX], buf[PATH_MAX];
 
  FILE *fp;
 
  STARTUPINFOA si = { sizeof(si) };
 
  PROCESS_INFORMATION pi = { 0 };
 

	
 
  envp = NULL; // Unused
 

	
 
  // TODO(lsm): redirect CGI errors to the error log file
 
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
 
  si.wShowWindow = SW_HIDE;
 

	
 
  me = GetCurrentProcess();
 
  (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me,
 
      &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
 
  (void) DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me,
 
      &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
 

	
 
  // If CGI file is a script, try to read the interpreter line
 
  interp = conn->ctx->config[CGI_INTERPRETER];
 
  if (interp == NULL) {
 
    buf[2] = '\0';
 
    mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%c%s", dir, DIRSEP, prog);
 
    if ((fp = fopen(cmdline, "r")) != NULL) {
 
      (void) fgets(buf, sizeof(buf), fp);
 
      if (buf[0] != '#' || buf[1] != '!') {
 
        // First line does not start with "#!". Do not set interpreter.
 
        buf[2] = '\0';
 
      } else {
 
        // Trim whitespace in interpreter name
 
        for (p = &buf[strlen(buf) - 1]; p > buf && isspace(*p); p--) {
 
          *p = '\0';
 
        }
 
      }
 
      (void) fclose(fp);
 
    }
 
    interp = buf + 2;
 
  }
 

	
 
  (void) mg_snprintf(conn, cmdline, sizeof(cmdline), "%s%s%s%c%s",
 
                     interp, interp[0] == '\0' ? "" : " ", dir, DIRSEP, prog);
 

	
 
  DEBUG_TRACE(("Running [%s]", cmdline));
 
  if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
 
        CREATE_NEW_PROCESS_GROUP, envblk, dir, &si, &pi) == 0) {
 
    cry(conn, "%s: CreateProcess(%s): %d",
 
        __func__, cmdline, ERRNO);
 
    pi.hProcess = (pid_t) -1;
 
  }
 

	
 
  // Always close these to prevent handle leakage.
 
  (void) close(fd_stdin);
 
  (void) close(fd_stdout);
 

	
 
  (void) CloseHandle(si.hStdOutput);
 
  (void) CloseHandle(si.hStdInput);
 
  (void) CloseHandle(pi.hThread);
 

	
 
  return (pid_t) pi.hProcess;
 
}
 
#endif // !NO_CGI
 

	
 
static int set_non_blocking_mode(SOCKET sock) {
 
  unsigned long on = 1;
 
  return ioctlsocket(sock, FIONBIO, &on);
 
}
 

	
 
#else
 
static int mg_stat(const char *path, struct mgstat *stp) {
 
  struct stat st;
 
  int ok;
 

	
 
  if (stat(path, &st) == 0) {
 
    ok = 0;
 
    stp->size = st.st_size;
 
    stp->mtime = st.st_mtime;
 
    stp->is_directory = S_ISDIR(st.st_mode);
 
  } else {
 
    ok = -1;
 
  }
 

	
 
  return ok;
 
}
 

	
 
static void set_close_on_exec(int fd) {
 
  (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
 
}
 

	
 
int mg_start_thread(mg_thread_func_t func, void *param) {
 
  pthread_t thread_id;
 
  pthread_attr_t attr;
 

	
 
  (void) pthread_attr_init(&attr);
 
  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
  // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
 
  // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);
 

	
 
  return pthread_create(&thread_id, &attr, func, param);
 
}
 

	
 
#ifndef NO_CGI
 
static pid_t spawn_process(struct mg_connection *conn, const char *prog,
 
                           char *envblk, char *envp[], int fd_stdin,
 
                           int fd_stdout, const char *dir) {
 
  pid_t pid;
 
  const char *interp;
 

	
 
  envblk = NULL; // Unused
 

	
 
  if ((pid = fork()) == -1) {
 
    // Parent
 
    send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
 
  } else if (pid == 0) {
 
    // Child
 
    if (chdir(dir) != 0) {
 
      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
 
    } else if (dup2(fd_stdin, 0) == -1) {
 
      cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO));
 
    } else if (dup2(fd_stdout, 1) == -1) {
 
      cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO));
 
    } else {
 
      (void) dup2(fd_stdout, 2);
 
      (void) close(fd_stdin);
 
      (void) close(fd_stdout);
 

	
 
      interp = conn->ctx->config[CGI_INTERPRETER];
 
      if (interp == NULL) {
 
        (void) execle(prog, prog, NULL, envp);
 
        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
 
      } else {
 
        (void) execle(interp, interp, prog, NULL, envp);
 
        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
 
            strerror(ERRNO));
 
      }
 
    }
 
    exit(EXIT_FAILURE);
 
  }
 

	
 
  // Parent. Close stdio descriptors
 
  (void) close(fd_stdin);
 
  (void) close(fd_stdout);
 

	
 
  return pid;
 
}
 
#endif // !NO_CGI
 

	
 
static int set_non_blocking_mode(SOCKET sock) {
 
  int flags;
 

	
 
  flags = fcntl(sock, F_GETFL, 0);
 
  (void) fcntl(sock, F_SETFL, flags | O_NONBLOCK);
 

	
 
  return 0;
 
}
 
#endif // _WIN32
 

	
 
// Write data to the IO channel - opened file descriptor, socket or SSL
 
// descriptor. Return number of bytes written.
 
static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
 
                    int64_t len) {
 
  int64_t sent;
 
  int n, k;
 

	
 
  sent = 0;
 
  while (sent < len) {
 

	
 
    // How many bytes we send in this iteration
 
    k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);
 

	
 
    if (ssl != NULL) {
 
      n = SSL_write(ssl, buf + sent, k);
 
    } else if (fp != NULL) {
 
      n = (int) fwrite(buf + sent, 1, (size_t) k, fp);
 
      if (ferror(fp))
 
        n = -1;
 
    } else {
 
      n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
 
    }
 

	
 
    if (n < 0)
 
      break;
 

	
 
    sent += n;
 
  }
 

	
 
  return sent;
 
}
 

	
 
// This function is needed to prevent Mongoose to be stuck in a blocking
 
// socket read when user requested exit. To do that, we sleep in select
 
// with a timeout, and when returned, check the context for the stop flag.
 
// If it is set, we return 0, and this means that we must not continue
 
// reading, must give up and close the connection and exit serving thread.
 
static int wait_until_socket_is_readable(struct mg_connection *conn) {
 
  int result;
 
  struct timeval tv;
 
  fd_set set;
 

	
 
  do {
 
    tv.tv_sec = 0;
 
    tv.tv_usec = 300 * 1000;
 
    FD_ZERO(&set);
 
    FD_SET(conn->client.sock, &set);
 
    result = select(conn->client.sock + 1, &set, NULL, NULL, &tv);
 
  } while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
 
           conn->ctx->stop_flag == 0);
 

	
 
  return conn->ctx->stop_flag || result < 0 ? 0 : 1;
 
}
 

	
 
// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
 
// Return negative value on error, or number of bytes read on success.
 
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
 
  int nread;
 

	
 
  if (fp != NULL) {
 
    // Use read() instead of fread(), because if we're reading from the CGI
 
    // pipe, fread() may block until IO buffer is filled up. We cannot afford
 
    // to block and must pass all read bytes immediately to the client.
 
    nread = read(fileno(fp), buf, (size_t) len);
 
  } else if (!wait_until_socket_is_readable(conn)) {
 
    nread = -1;
 
  } else if (conn->ssl != NULL) {
 
    nread = SSL_read(conn->ssl, buf, len);
 
  } else {
 
    nread = recv(conn->client.sock, buf, (size_t) len, 0);
 
  }
 

	
 
  return conn->ctx->stop_flag ? -1 : nread;
 
}
 

	
 
int mg_read(struct mg_connection *conn, void *buf, size_t len) {
 
  int n, buffered_len, nread;
 

	
 
  assert(conn->next_request != NULL &&
 
         conn->body != NULL &&
 
         conn->next_request >= conn->body);
 
  nread = 0;
 
  if (conn->consumed_content < conn->content_len) {
 

	
 
    // Adjust number of bytes to read.
 
    int64_t to_read = conn->content_len - conn->consumed_content;
 
    if (to_read < (int64_t) len) {
 
      len = (size_t) to_read;
 
    }
 

	
 
    // Return buffered data
 
    buffered_len = conn->next_request - conn->body;
 
    if (buffered_len > 0) {
 
      if (len < (size_t) buffered_len) {
 
        buffered_len = (int) len;
 
      }
 
      memcpy(buf, conn->body, (size_t) buffered_len);
 
      len -= buffered_len;
 
      conn->body += buffered_len;
 
      conn->consumed_content += buffered_len;
 
      nread += buffered_len;
 
      buf = (char *) buf + buffered_len;
 
    }
 

	
 
    // We have returned all buffered data. Read new data from the remote socket.
 
    while (len > 0) {
 
      n = pull(NULL, conn, (char *) buf, (int) len);
 
      if (n < 0) {
 
        nread = n;  // Propagate the error
 
        break;
 
      } else if (n == 0) {
 
        break;  // No more data to read
 
      } else {
 
        buf = (char *) buf + n;
 
        conn->consumed_content += n;
 
        nread += n;
 
        len -= n;
 
      }
 
    }
 
  }
 
  return nread;
 
}
 

	
 
int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
 
  return (int) push(NULL, conn->client.sock, conn->ssl, (const char *) buf,
 
                    (int64_t) len);
 
}
 

	
 
int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
 
  char mem[MG_BUF_LEN], *buf = mem;
 
  int len;
 
  va_list ap;
 

	
 
  // Print in a local buffer first, hoping that it is large enough to
 
  // hold the whole message
 
  va_start(ap, fmt);
 
  len = vsnprintf(mem, sizeof(mem), fmt, ap);
 
  va_end(ap);
 

	
 
  if (len <= 0) {
 
    // vsnprintf() error, give up
 
    len = -1;
 
    cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
 
  } else if (len > (int) sizeof(mem) && (buf = malloc(len + 1)) != NULL) {
 
    // Local buffer is not large enough, allocate big buffer on heap
 
    va_start(ap, fmt);
 
    vsnprintf(buf, len + 1, fmt, ap);
 
    va_end(ap);
 
    len = mg_write(conn, buf, (size_t) len);
 
    free(buf);
 
  } else if (len > (int) sizeof(mem)) {
 
    // Failed to allocate large enough buffer, give up
 
    cry(conn, "%s(%s, ...): Can't allocate %d bytes, not printing anything",
 
        __func__, fmt, len);
 
    len = -1;
 
  } else {
 
    // Copy to the local buffer succeeded
 
    len = mg_write(conn, buf, (size_t) len);
 
  }
 

	
 
  return len;
 
}
 

	
 
// URL-decode input buffer into destination buffer.
 
// 0-terminate the destination buffer. Return the length of decoded data.
 
// form-url-encoded data differs from URI encoding in a way that it
 
// uses '+' as character for space, see RFC 1866 section 8.2.1
 
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
 
static size_t url_decode(const char *src, size_t src_len, char *dst,
 
                         size_t dst_len, int is_form_url_encoded) {
 
  size_t i, j;
 
  int a, b;
 
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
 

	
 
  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
 
    if (src[i] == '%' &&
 
        isxdigit(* (const unsigned char *) (src + i + 1)) &&
 
        isxdigit(* (const unsigned char *) (src + i + 2))) {
 
      a = tolower(* (const unsigned char *) (src + i + 1));
 
      b = tolower(* (const unsigned char *) (src + i + 2));
 
      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
 
      i += 2;
 
    } else if (is_form_url_encoded && src[i] == '+') {
 
      dst[j] = ' ';
 
    } else {
 
      dst[j] = src[i];
 
    }
 
  }
 

	
 
  dst[j] = '\0'; // Null-terminate the destination
 

	
 
  return j;
 
}
 

	
 
// Scan given buffer and fetch the value of the given variable.
 
// It can be specified in query string, or in the POST data.
 
// Return -1 if the variable not found, or length of the URL-decoded value
 
// stored in dst. The dst buffer is guaranteed to be NUL-terminated if it
 
// is not NULL or zero-length. If dst is NULL or zero-length, then
 
// -2 is returned.
 
int mg_get_var(const char *buf, size_t buf_len, const char *name,
 
               char *dst, size_t dst_len) {
 
  const char *p, *e, *s;
 
  size_t name_len;
 
  int len;
 

	
 
  if (dst == NULL || dst_len == 0) {
 
    len = -2;
 
  } else if (buf == NULL || name == NULL || buf_len == 0) {
 
    len = -1;
 
    dst[0] = '\0';
 
  } else {
 
    name_len = strlen(name);
 
    e = buf + buf_len;
 
    len = -1;
 
    dst[0] = '\0';
 

	
 
    // buf is "var1=val1&var2=val2...". Find variable first
 
    for (p = buf; p + name_len < e; p++) {
 
      if ((p == buf || p[-1] == '&') && p[name_len] == '=' &&
 
          !mg_strncasecmp(name, p, name_len)) {
 

	
 
        // Point p to variable value
 
        p += name_len + 1;
 

	
 
        // Point s to the end of the value
 
        s = (const char *) memchr(p, '&', (size_t)(e - p));
 
        if (s == NULL) {
 
          s = e;
 
        }
 
        assert(s >= p);
 

	
 
        // Decode variable into destination buffer
 
        if ((size_t) (s - p) < dst_len) {
 
          len = (int) url_decode(p, (size_t)(s - p), dst, dst_len, 1);
 
        }
 
        break;
 
      }
 
    }
 
  }
 

	
 
  return len;
 
}
 

	
 
int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,
 
                  char *dst, size_t dst_size) {
 
  const char *s, *p, *end;
 
  int name_len, len = -1;
 

	
 
  dst[0] = '\0';
 
  if ((s = mg_get_header(conn, "Cookie")) == NULL) {
 
    return -1;
 
  }
 

	
 
  name_len = (int) strlen(cookie_name);
 
  end = s + strlen(s);
 

	
 
  for (; (s = strstr(s, cookie_name)) != NULL; s += name_len)
 
    if (s[name_len] == '=') {
 
      s += name_len + 1;
 
      if ((p = strchr(s, ' ')) == NULL)
 
        p = end;
 
      if (p[-1] == ';')
 
        p--;
 
      if (*s == '"' && p[-1] == '"' && p > s + 1) {
 
        s++;
 
        p--;
 
      }
 
      if ((size_t) (p - s) < dst_size) {
 
        len = p - s;
 
        mg_strlcpy(dst, s, (size_t) len + 1);
 
      }
 
      break;
 
    }
 

	
 
  return len;
 
}
 

	
 
static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
 
                                    size_t buf_len, struct mgstat *st) {
 
  struct vec a, b;
 
  const char *rewrite, *uri = conn->request_info.uri;
 
  char *p;
 
  int match_len, stat_result;
 

	
 
  buf_len--;  // This is because memmove() for PATH_INFO may shift part
 
              // of the path one byte on the right.
 
  mg_snprintf(conn, buf, buf_len, "%s%s", conn->ctx->config[DOCUMENT_ROOT],
 
              uri);
 

	
 
  rewrite = conn->ctx->config[REWRITE];
 
  while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
 
    if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
 
      mg_snprintf(conn, buf, buf_len, "%.*s%s", b.len, b.ptr, uri + match_len);
 
      break;
 
    }
 
  }
 

	
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  //change_slashes_to_backslashes(buf);
 
#endif // _WIN32
 

	
 
  if ((stat_result = mg_stat(buf, st)) != 0) {
 
    // Support PATH_INFO for CGI scripts.
 
    for (p = buf + strlen(buf); p > buf + 1; p--) {
 
      if (*p == '/') {
 
        *p = '\0';
 
        if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
 
                         strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
 
            (stat_result = mg_stat(buf, st)) == 0) {
 
          // Shift PATH_INFO block one character right, e.g.
 
          //  "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00"
 
          // conn->path_info is pointing to the local variable "path" declared
 
          // in handle_request(), so PATH_INFO is not valid after
 
          // handle_request returns.
 
          conn->path_info = p + 1;
 
          memmove(p + 2, p + 1, strlen(p + 1) + 1);  // +1 is for trailing \0
 
          p[1] = '/';
 
          break;
 
        } else {
 
          *p = '/';
 
          stat_result = -1;
 
        }
 
      }
 
    }
 
  }
 

	
 
  return stat_result;
 
}
 

	
 
static int sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) {
 
  return (conn->ssl = SSL_new(s)) != NULL &&
 
    SSL_set_fd(conn->ssl, conn->client.sock) == 1 &&
 
    func(conn->ssl) == 1;
 
}
 

	
 
// Check whether full request is buffered. Return:
 
//   -1  if request is malformed
 
//    0  if request is not yet fully buffered
 
//   >0  actual request length, including last \r\n\r\n
 
static int get_request_len(const char *buf, int buflen) {
 
  const char *s, *e;
 
  int len = 0;
 

	
 
  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
 
    // Control characters are not allowed but >=128 is.
 
    if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
 
        *s != '\n' && * (const unsigned char *) s < 128) {
 
      len = -1;
 
      break; // [i_a] abort scan as soon as one malformed character is found; don't let subsequent \r\n\r\n win us over anyhow
 
    } else if (s[0] == '\n' && s[1] == '\n') {
 
      len = (int) (s - buf) + 2;
 
    } else if (s[0] == '\n' && &s[1] < e &&
 
        s[1] == '\r' && s[2] == '\n') {
 
      len = (int) (s - buf) + 3;
 
    }
 

	
 
  return len;
 
}
 

	
 
// Convert month to the month number. Return -1 on error, or month number
 
static int get_month_index(const char *s) {
 
  size_t i;
 

	
 
  for (i = 0; i < ARRAY_SIZE(month_names); i++)
 
    if (!strcmp(s, month_names[i]))
 
      return (int) i;
 

	
 
  return -1;
 
}
 

	
 
// Parse UTC date-time string, and return the corresponding time_t value.
 
static time_t parse_date_string(const char *datetime) {
 
  static const unsigned short days_before_month[] = {
 
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
 
  };
 
  char month_str[32];
 
  int second, minute, hour, day, month, year, leap_days, days;
 
  time_t result = (time_t) 0;
 

	
 
  if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%d %3s %d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
 
       (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
 
               &day, month_str, &year, &hour, &minute, &second) == 6)) &&
 
      year > 1970 &&
 
      (month = get_month_index(month_str)) != -1) {
 
    year -= 1970;
 
    leap_days = year / 4 - year / 100 + year / 400;
 
    days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
 
    result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
 
  }
 

	
 
  return result;
 
}
 

	
 
// Protect against directory disclosure attack by removing '..',
 
// excessive '/' and '\' characters
 
static void remove_double_dots_and_double_slashes(char *s) {
 
  char *p = s;
 

	
 
  while (*s != '\0') {
 
    *p++ = *s++;
 
    if (IS_DIRSEP_CHAR(s[-1])) {
 
      // Skip all following slashes and backslashes
 
      while (IS_DIRSEP_CHAR(s[0])) {
 
        s++;
 
      }
 

	
 
      // Skip all double-dots
 
      while (*s == '.' && s[1] == '.') {
 
        s += 2;
 
      }
 
    }
 
  }
 
  *p = '\0';
 
}
 

	
 
static const struct {
 
  const char *extension;
 
  size_t ext_len;
 
  const char *mime_type;
 
} builtin_mime_types[] = {
 
  {".html", 5, "text/html"},
 
  {".htm", 4, "text/html"},
 
  {".shtm", 5, "text/html"},
 
  {".shtml", 6, "text/html"},
 
  {".css", 4, "text/css"},
 
  {".js",  3, "application/x-javascript"},
 
  {".ico", 4, "image/x-icon"},
 
  {".gif", 4, "image/gif"},
 
  {".jpg", 4, "image/jpeg"},
 
  {".jpeg", 5, "image/jpeg"},
 
  {".png", 4, "image/png"},
 
  {".svg", 4, "image/svg+xml"},
 
  {".txt", 4, "text/plain"},
 
  {".torrent", 8, "application/x-bittorrent"},
 
  {".wav", 4, "audio/x-wav"},
 
  {".mp3", 4, "audio/x-mp3"},
 
  {".mid", 4, "audio/mid"},
 
  {".m3u", 4, "audio/x-mpegurl"},
 
  {".ram", 4, "audio/x-pn-realaudio"},
 
  {".xml", 4, "text/xml"},
 
  {".json",  5, "text/json"},
 
  {".xslt", 5, "application/xml"},
 
  {".ra",  3, "audio/x-pn-realaudio"},
 
  {".doc", 4, "application/msword"},
 
  {".exe", 4, "application/octet-stream"},
 
  {".zip", 4, "application/x-zip-compressed"},
 
  {".xls", 4, "application/excel"},
 
  {".tgz", 4, "application/x-tar-gz"},
 
  {".tar", 4, "application/x-tar"},
 
  {".gz",  3, "application/x-gunzip"},
 
  {".arj", 4, "application/x-arj-compressed"},
 
  {".rar", 4, "application/x-arj-compressed"},
 
  {".rtf", 4, "application/rtf"},
 
  {".pdf", 4, "application/pdf"},
 
  {".swf", 4, "application/x-shockwave-flash"},
 
  {".mpg", 4, "video/mpeg"},
 
  {".webm", 5, "video/webm"},
 
  {".mpeg", 5, "video/mpeg"},
 
  {".mp4", 4, "video/mp4"},
 
  {".m4v", 4, "video/x-m4v"},
 
  {".asf", 4, "video/x-ms-asf"},
 
  {".avi", 4, "video/x-msvideo"},
 
  {".bmp", 4, "image/bmp"},
 
  {NULL,  0, NULL}
 
};
 

	
 
const char *mg_get_builtin_mime_type(const char *path) {
 
  const char *ext;
 
  size_t i, path_len;
 

	
 
  path_len = strlen(path);
 

	
 
  for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
 
    ext = path + (path_len - builtin_mime_types[i].ext_len);
 
    if (path_len > builtin_mime_types[i].ext_len &&
 
        mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
 
      return builtin_mime_types[i].mime_type;
 
    }
 
  }
 

	
 
  return "text/plain";
 
}
 

	
 
// Look at the "path" extension and figure what mime type it has.
 
// Store mime type in the vector.
 
static void get_mime_type(struct mg_context *ctx, const char *path,
 
                          struct vec *vec) {
 
  struct vec ext_vec, mime_vec;
 
  const char *list, *ext;
 
  size_t path_len;
 

	
 
  path_len = strlen(path);
 

	
 
  // Scan user-defined mime types first, in case user wants to
 
  // override default mime types.
 
  list = ctx->config[EXTRA_MIME_TYPES];
 
  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
 
    // ext now points to the path suffix
 
    ext = path + path_len - ext_vec.len;
 
    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
 
      *vec = mime_vec;
 
      return;
 
    }
 
  }
 

	
 
  vec->ptr = mg_get_builtin_mime_type(path);
 
  vec->len = strlen(vec->ptr);
 
}
 

	
 
#ifndef HAVE_MD5
 
typedef struct MD5Context {
 
  uint32_t buf[4];
 
  uint32_t bits[2];
 
  unsigned char in[64];
 
} MD5_CTX;
 

	
 
#if defined(__BYTE_ORDER) && (__BYTE_ORDER == 1234)
 
#define byteReverse(buf, len) // Do nothing
 
#else
 
static void byteReverse(unsigned char *buf, unsigned longs) {
 
  uint32_t t;
 
  do {
 
    t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
 
      ((unsigned) buf[1] << 8 | buf[0]);
 
    *(uint32_t *) buf = t;
 
    buf += 4;
 
  } while (--longs);
 
}
 
#endif
 

	
 
#define F1(x, y, z) (z ^ (x & (y ^ z)))
 
#define F2(x, y, z) F1(z, x, y)
 
#define F3(x, y, z) (x ^ y ^ z)
 
#define F4(x, y, z) (y ^ (x | ~z))
 

	
 
#define MD5STEP(f, w, x, y, z, data, s) \
 
  ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
 

	
 
// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
 
// initialization constants.
 
static void MD5Init(MD5_CTX *ctx) {
 
  ctx->buf[0] = 0x67452301;
 
  ctx->buf[1] = 0xefcdab89;
 
  ctx->buf[2] = 0x98badcfe;
 
  ctx->buf[3] = 0x10325476;
 

	
 
  ctx->bits[0] = 0;
 
  ctx->bits[1] = 0;
 
}
 

	
 
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
 
  register uint32_t a, b, c, d;
 

	
 
  a = buf[0];
 
  b = buf[1];
 
  c = buf[2];
 
  d = buf[3];
 

	
 
  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
 
  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
 
  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
 
  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
 
  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
 
  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
 
  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
 
  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
 
  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
 
  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
 
  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
 
  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
 
  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
 
  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
 
  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
 
  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
 

	
 
  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
 
  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
 
  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
 
  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
 
  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
 
  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
 
  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
 
  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
 
  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
 
  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
 
  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
 
  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
 
  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
 
  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
 
  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
 
  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
 

	
 
  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
 
  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
 
  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
 
  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
 
  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
 
  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
 
  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
 
  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
 
  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
 
  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
 
  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
 
  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
 
  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
 
  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
 
  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
 
  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
 

	
 
  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
 
  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
 
  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
 
  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
 
  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
 
  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
 
  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
 
  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
 
  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
 
  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
 
  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
 
  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
 
  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
 
  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
 
  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
 
  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
 

	
 
  buf[0] += a;
 
  buf[1] += b;
 
  buf[2] += c;
 
  buf[3] += d;
 
}
 

	
 
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
 
  uint32_t t;
 

	
 
  t = ctx->bits[0];
 
  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
 
    ctx->bits[1]++;
 
  ctx->bits[1] += len >> 29;
 

	
 
  t = (t >> 3) & 0x3f;
 

	
 
  if (t) {
 
    unsigned char *p = (unsigned char *) ctx->in + t;
 

	
 
    t = 64 - t;
 
    if (len < t) {
 
      memcpy(p, buf, len);
 
      return;
 
    }
 
    memcpy(p, buf, t);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    buf += t;
 
    len -= t;
 
  }
 

	
 
  while (len >= 64) {
 
    memcpy(ctx->in, buf, 64);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    buf += 64;
 
    len -= 64;
 
  }
 

	
 
  memcpy(ctx->in, buf, len);
 
}
 

	
 
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
 
  unsigned count;
 
  unsigned char *p;
 

	
 
  count = (ctx->bits[0] >> 3) & 0x3F;
 

	
 
  p = ctx->in + count;
 
  *p++ = 0x80;
 
  count = 64 - 1 - count;
 
  if (count < 8) {
 
    memset(p, 0, count);
 
    byteReverse(ctx->in, 16);
 
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
    memset(ctx->in, 0, 56);
 
  } else {
 
    memset(p, 0, count - 8);
 
  }
 
  byteReverse(ctx->in, 14);
 

	
 
  ((uint32_t *) ctx->in)[14] = ctx->bits[0];
 
  ((uint32_t *) ctx->in)[15] = ctx->bits[1];
 

	
 
  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
 
  byteReverse((unsigned char *) ctx->buf, 4);
 
  memcpy(digest, ctx->buf, 16);
 
  memset((char *) ctx, 0, sizeof(*ctx));
 
}
 
#endif // !HAVE_MD5
 

	
 
// Stringify binary data. Output buffer must be twice as big as input,
 
// because each byte takes 2 bytes in string representation
 
static void bin2str(char *to, const unsigned char *p, size_t len) {
 
  static const char *hex = "0123456789abcdef";
 

	
 
  for (; len--; p++) {
 
    *to++ = hex[p[0] >> 4];
 
    *to++ = hex[p[0] & 0x0f];
 
  }
 
  *to = '\0';
 
}
 

	
 
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
 
void mg_md5(char buf[33], ...) {
 
  unsigned char hash[16];
 
  const char *p;
 
  va_list ap;
 
  MD5_CTX ctx;
 

	
 
  MD5Init(&ctx);
 

	
 
  va_start(ap, buf);
 
  while ((p = va_arg(ap, const char *)) != NULL) {
 
    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
 
  }
 
  va_end(ap);
 

	
 
  MD5Final(hash, &ctx);
 
  bin2str(buf, hash, sizeof(hash));
 
}
 

	
 
// Check the user's password, return 1 if OK
 
static int check_password(const char *method, const char *ha1, const char *uri,
 
                          const char *nonce, const char *nc, const char *cnonce,
 
                          const char *qop, const char *response) {
 
  char ha2[32 + 1], expected_response[32 + 1];
 

	
 
  // Some of the parameters may be NULL
 
  if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
 
      qop == NULL || response == NULL) {
 
    return 0;
 
  }
 

	
 
  // NOTE(lsm): due to a bug in MSIE, we do not compare the URI
 
  // TODO(lsm): check for authentication timeout
 
  if (// strcmp(dig->uri, c->ouri) != 0 ||
 
      strlen(response) != 32
 
      // || now - strtoul(dig->nonce, NULL, 10) > 3600
 
      ) {
 
    return 0;
 
  }
 

	
 
  mg_md5(ha2, method, ":", uri, NULL);
 
  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
 
      ":", cnonce, ":", qop, ":", ha2, NULL);
 

	
 
  return mg_strcasecmp(response, expected_response) == 0;
 
}
 

	
 
// Use the global passwords file, if specified by auth_gpass option,
 
// or search for .htpasswd in the requested directory.
 
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
 
  struct mg_context *ctx = conn->ctx;
 
  char name[PATH_MAX];
 
  const char *p, *e;
 
  struct mgstat st;
 
  FILE *fp;
 

	
 
  if (ctx->config[GLOBAL_PASSWORDS_FILE] != NULL) {
 
    // Use global passwords file
 
    fp =  mg_fopen(ctx->config[GLOBAL_PASSWORDS_FILE], "r");
 
    if (fp == NULL)
 
      cry(fc(ctx), "fopen(%s): %s",
 
          ctx->config[GLOBAL_PASSWORDS_FILE], strerror(ERRNO));
 
  } else if (!mg_stat(path, &st) && st.is_directory) {
 
    (void) mg_snprintf(conn, name, sizeof(name), "%s%c%s",
 
        path, DIRSEP, PASSWORDS_FILE_NAME);
 
    fp = mg_fopen(name, "r");
 
  } else {
 
     // Try to find .htpasswd in requested directory.
 
    for (p = path, e = p + strlen(p) - 1; e > p; e--)
 
      if (IS_DIRSEP_CHAR(*e))
 
        break;
 
    (void) mg_snprintf(conn, name, sizeof(name), "%.*s%c%s",
 
        (int) (e - p), p, DIRSEP, PASSWORDS_FILE_NAME);
 
    fp = mg_fopen(name, "r");
 
  }
 

	
 
  return fp;
 
}
 

	
 
// Parsed Authorization header
 
struct ah {
 
  char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
 
};
 

	
 
// Return 1 on success. Always initializes the ah structure.
 
static int parse_auth_header(struct mg_connection *conn, char *buf,
 
                             size_t buf_size, struct ah *ah) {
 
  char *name, *value, *s;
 
  const char *auth_header;
 

	
 
  (void) memset(ah, 0, sizeof(*ah));
 
  if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
 
      mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
 
    return 0;
 
  }
 

	
 
  // Make modifiable copy of the auth header
 
  (void) mg_strlcpy(buf, auth_header + 7, buf_size);
 
  s = buf;
 

	
 
  // Parse authorization header
 
  for (;;) {
 
    // Gobble initial spaces
 
    while (isspace(* (unsigned char *) s)) {
 
      s++;
 
    }
 
    name = skip_quoted(&s, "=", " ", 0);
 
    // Value is either quote-delimited, or ends at first comma or space.
 
    if (s[0] == '\"') {
 
      s++;
 
      value = skip_quoted(&s, "\"", " ", '\\');
 
      if (s[0] == ',') {
 
        s++;
 
      }
 
    } else {
 
      value = skip_quoted(&s, ", ", " ", 0);  // IE uses commas, FF uses spaces
 
    }
 
    if (*name == '\0') {
 
      break;
 
    }
 

	
 
    if (!strcmp(name, "username")) {
 
      ah->user = value;
 
    } else if (!strcmp(name, "cnonce")) {
 
      ah->cnonce = value;
 
    } else if (!strcmp(name, "response")) {
 
      ah->response = value;
 
    } else if (!strcmp(name, "uri")) {
 
      ah->uri = value;
 
    } else if (!strcmp(name, "qop")) {
 
      ah->qop = value;
 
    } else if (!strcmp(name, "nc")) {
 
      ah->nc = value;
 
    } else if (!strcmp(name, "nonce")) {
 
      ah->nonce = value;
 
    }
 
  }
 

	
 
  // CGI needs it as REMOTE_USER
 
  if (ah->user != NULL) {
 
    conn->request_info.remote_user = mg_strdup(ah->user);
 
  } else {
 
    return 0;
 
  }
 

	
 
  return 1;
 
}
 

	
 
// Authorize against the opened passwords file. Return 1 if authorized.
 
static int authorize(struct mg_connection *conn, FILE *fp) {
 
  struct ah ah;
 
  char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
 

	
 
  if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
 
    return 0;
 
  }
 

	
 
  // Loop over passwords file
 
  while (fgets(line, sizeof(line), fp) != NULL) {
 
    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
 
      continue;
 
    }
 

	
 
    if (!strcmp(ah.user, f_user) &&
 
        !strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
 
      return check_password(
 
            conn->request_info.request_method,
 
            ha1, ah.uri, ah.nonce, ah.nc, ah.cnonce, ah.qop,
 
            ah.response);
 
  }
 

	
 
  return 0;
 
}
 

	
 
// Return 1 if request is authorised, 0 otherwise.
 
static int check_authorization(struct mg_connection *conn, const char *path) {
 
  FILE *fp;
 
  char fname[PATH_MAX];
 
  struct vec uri_vec, filename_vec;
 
  const char *list;
 
  int authorized;
 

	
 
  fp = NULL;
 
  authorized = 1;
 

	
 
  list = conn->ctx->config[PROTECT_URI];
 
  while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
 
    if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
 
      (void) mg_snprintf(conn, fname, sizeof(fname), "%.*s",
 
          filename_vec.len, filename_vec.ptr);
 
      if ((fp = mg_fopen(fname, "r")) == NULL) {
 
        cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
 
      }
 
      break;
 
    }
 
  }
 

	
 
  if (fp == NULL) {
 
    fp = open_auth_file(conn, path);
 
  }
 

	
 
  if (fp != NULL) {
 
    authorized = authorize(conn, fp);
 
    (void) fclose(fp);
 
  }
 

	
 
  return authorized;
 
}
 

	
 
static void send_authorization_request(struct mg_connection *conn) {
 
  conn->request_info.status_code = 401;
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 401 Unauthorized\r\n"
 
      "Content-Length: 0\r\n"
 
      "WWW-Authenticate: Digest qop=\"auth\", "
 
      "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
 
      conn->ctx->config[AUTHENTICATION_DOMAIN],
 
      (unsigned long) time(NULL));
 
}
 

	
 
static int is_authorized_for_put(struct mg_connection *conn) {
 
  FILE *fp;
 
  int ret = 0;
 

	
 
  fp = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ? NULL :
 
    mg_fopen(conn->ctx->config[PUT_DELETE_PASSWORDS_FILE], "r");
 

	
 
  if (fp != NULL) {
 
    ret = authorize(conn, fp);
 
    (void) fclose(fp);
 
  }
 

	
 
  return ret;
 
}
 

	
 
int mg_modify_passwords_file(const char *fname, const char *domain,
 
                             const char *user, const char *pass) {
 
  int found;
 
  char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
 
  FILE *fp, *fp2;
 

	
 
  found = 0;
 
  fp = fp2 = NULL;
 

	
 
  // Regard empty password as no password - remove user record.
 
  if (pass != NULL && pass[0] == '\0') {
 
    pass = NULL;
 
  }
 

	
 
  (void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
 

	
 
  // Create the file if does not exist
 
  if ((fp = mg_fopen(fname, "a+")) != NULL) {
 
    (void) fclose(fp);
 
  }
 

	
 
  // Open the given file and temporary file
 
  if ((fp = mg_fopen(fname, "r")) == NULL) {
 
    return 0;
 
  } else if ((fp2 = mg_fopen(tmp, "w+")) == NULL) {
 
    fclose(fp);
 
    return 0;
 
  }
 

	
 
  // Copy the stuff to temporary file
 
  while (fgets(line, sizeof(line), fp) != NULL) {
 
    if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
 
      continue;
 
    }
 

	
 
    if (!strcmp(u, user) && !strcmp(d, domain)) {
 
      found++;
 
      if (pass != NULL) {
 
        mg_md5(ha1, user, ":", domain, ":", pass, NULL);
 
        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
 
      }
 
    } else {
 
      (void) fprintf(fp2, "%s", line);
 
    }
 
  }
 

	
 
  // If new user, just add it
 
  if (!found && pass != NULL) {
 
    mg_md5(ha1, user, ":", domain, ":", pass, NULL);
 
    (void) fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
 
  }
 

	
 
  // Close files
 
  (void) fclose(fp);
 
  (void) fclose(fp2);
 

	
 
  // Put the temp file in place of real file
 
  (void) mg_remove(fname);
 
  (void) mg_rename(tmp, fname);
 

	
 
  return 1;
 
}
 

	
 
struct de {
 
  struct mg_connection *conn;
 
  char *file_name;
 
  struct mgstat st;
 
};
 

	
 
static void url_encode(const char *src, char *dst, size_t dst_len) {
 
  static const char *dont_escape = "._-$,;~()";
 
  static const char *hex = "0123456789abcdef";
 
  const char *end = dst + dst_len - 1;
 

	
 
  for (; *src != '\0' && dst < end; src++, dst++) {
 
    if (isalnum(*(const unsigned char *) src) ||
 
        strchr(dont_escape, * (const unsigned char *) src) != NULL) {
 
      *dst = *src;
 
    } else if (dst + 2 < end) {
 
      dst[0] = '%';
 
      dst[1] = hex[(* (const unsigned char *) src) >> 4];
 
      dst[2] = hex[(* (const unsigned char *) src) & 0xf];
 
      dst += 2;
 
    }
 
  }
 

	
 
  *dst = '\0';
 
}
 

	
 
static void print_dir_entry(struct de *de) {
 
  char size[64], mod[64], href[PATH_MAX];
 

	
 
  if (de->st.is_directory) {
 
    (void) mg_snprintf(de->conn, size, sizeof(size), "%s", "[DIRECTORY]");
 
  } else {
 
     // We use (signed) cast below because MSVC 6 compiler cannot
 
     // convert unsigned __int64 to double. Sigh.
 
    if (de->st.size < 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%lu", (unsigned long) de->st.size);
 
    } else if (de->st.size < 1024 * 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fk", (double) de->st.size / 1024.0);
 
    } else if (de->st.size < 1024 * 1024 * 1024) {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fM", (double) de->st.size / 1048576);
 
    } else {
 
      (void) mg_snprintf(de->conn, size, sizeof(size),
 
          "%.1fG", (double) de->st.size / 1073741824);
 
    }
 
  }
 
  (void) strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.mtime));
 
  url_encode(de->file_name, href, sizeof(href));
 
  de->conn->num_bytes_sent += mg_printf(de->conn,
 
      "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
 
      "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
 
      de->conn->request_info.uri, href, de->st.is_directory ? "/" : "",
 
      de->file_name, de->st.is_directory ? "/" : "", mod, size);
 
}
 

	
 
// This function is called from send_directory() and used for
 
// sorting directory entries by size, or name, or modification time.
 
// On windows, __cdecl specification is needed in case if project is built
 
// with __stdcall convention. qsort always requires __cdels callback.
 
static int WINCDECL compare_dir_entries(const void *p1, const void *p2) {
 
  const struct de *a = (const struct de *) p1, *b = (const struct de *) p2;
 
  const char *query_string = a->conn->request_info.query_string;
 
  int cmp_result = 0;
 

	
 
  if (query_string == NULL) {
 
    query_string = "na";
 
  }
 

	
 
  if (a->st.is_directory && !b->st.is_directory) {
 
    return -1;  // Always put directories on top
 
  } else if (!a->st.is_directory && b->st.is_directory) {
 
    return 1;   // Always put directories on top
 
  } else if (*query_string == 'n') {
 
    cmp_result = strcmp(a->file_name, b->file_name);
 
  } else if (*query_string == 's') {
 
    cmp_result = a->st.size == b->st.size ? 0 :
 
      a->st.size > b->st.size ? 1 : -1;
 
  } else if (*query_string == 'd') {
 
    cmp_result = a->st.mtime == b->st.mtime ? 0 :
 
      a->st.mtime > b->st.mtime ? 1 : -1;
 
  }
 

	
 
  return query_string[1] == 'd' ? -cmp_result : cmp_result;
 
}
 

	
 
static int must_hide_file(struct mg_connection *conn, const char *path) {
 
  const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
 
  const char *pattern = conn->ctx->config[HIDE_FILES];
 
  return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
 
    (pattern != NULL && match_prefix(pattern, strlen(pattern), path) > 0);
 
}
 

	
 
static int scan_directory(struct mg_connection *conn, const char *dir,
 
                          void *data, void (*cb)(struct de *, void *)) {
 
  char path[PATH_MAX];
 
  struct dirent *dp;
 
  DIR *dirp;
 
  struct de de;
 

	
 
  if ((dirp = opendir(dir)) == NULL) {
 
    return 0;
 
  } else {
 
    de.conn = conn;
 

	
 
    while ((dp = readdir(dirp)) != NULL) {
 
      // Do not show current dir and hidden files
 
      if (!strcmp(dp->d_name, ".") ||
 
          !strcmp(dp->d_name, "..") ||
 
          must_hide_file(conn, dp->d_name)) {
 
        continue;
 
      }
 

	
 
      mg_snprintf(conn, path, sizeof(path), "%s%c%s", dir, DIRSEP, dp->d_name);
 

	
 
      // If we don't memset stat structure to zero, mtime will have
 
      // garbage and strftime() will segfault later on in
 
      // print_dir_entry(). memset is required only if mg_stat()
 
      // fails. For more details, see
 
      // http://code.google.com/p/mongoose/issues/detail?id=79
 
      if (mg_stat(path, &de.st) != 0) {
 
        memset(&de.st, 0, sizeof(de.st));
 
      }
 
      de.file_name = dp->d_name;
 

	
 
      cb(&de, data);
 
    }
 
    (void) closedir(dirp);
 
  }
 
  return 1;
 
}
 

	
 
struct dir_scan_data {
 
  struct de *entries;
 
  int num_entries;
 
  int arr_size;
 
};
 

	
 
static void dir_scan_callback(struct de *de, void *data) {
 
  struct dir_scan_data *dsd = (struct dir_scan_data *) data;
 

	
 
  if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) {
 
    dsd->arr_size *= 2;
 
    dsd->entries = (struct de *) realloc(dsd->entries, dsd->arr_size *
 
                                         sizeof(dsd->entries[0]));
 
  }
 
  if (dsd->entries == NULL) {
 
    // TODO(lsm): propagate an error to the caller
 
    dsd->num_entries = 0;
 
  } else {
 
    dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name);
 
    dsd->entries[dsd->num_entries].st = de->st;
 
    dsd->entries[dsd->num_entries].conn = de->conn;
 
    dsd->num_entries++;
 
  }
 
}
 

	
 
static void handle_directory_request(struct mg_connection *conn,
 
                                     const char *dir) {
 
  int i, sort_direction;
 
  struct dir_scan_data data = { NULL, 0, 128 };
 

	
 
  if (!scan_directory(conn, dir, &data, dir_scan_callback)) {
 
    send_http_error(conn, 500, "Cannot open directory",
 
                    "Error: opendir(%s): %s", dir, strerror(ERRNO));
 
    return;
 
  }
 

	
 
  sort_direction = conn->request_info.query_string != NULL &&
 
    conn->request_info.query_string[1] == 'd' ? 'a' : 'd';
 

	
 
  conn->must_close = 1;
 
  mg_printf(conn, "%s",
 
            "HTTP/1.1 200 OK\r\n"
 
            "Connection: close\r\n"
 
            "Content-Type: text/html; charset=utf-8\r\n\r\n");
 

	
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<html><head><title>Index of %s</title>"
 
      "<style>th {text-align: left;}</style></head>"
 
      "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
 
      "<tr><th><a href=\"?n%c\">Name</a></th>"
 
      "<th><a href=\"?d%c\">Modified</a></th>"
 
      "<th><a href=\"?s%c\">Size</a></th></tr>"
 
      "<tr><td colspan=\"3\"><hr></td></tr>",
 
      conn->request_info.uri, conn->request_info.uri,
 
      sort_direction, sort_direction, sort_direction);
 

	
 
  // Print first entry - link to a parent directory
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<tr><td><a href=\"%s%s\">%s</a></td>"
 
      "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
 
      conn->request_info.uri, "..", "Parent directory", "-", "-");
 

	
 
  // Sort and print directory entries
 
  qsort(data.entries, (size_t) data.num_entries, sizeof(data.entries[0]),
 
        compare_dir_entries);
 
  for (i = 0; i < data.num_entries; i++) {
 
    print_dir_entry(&data.entries[i]);
 
    free(data.entries[i].file_name);
 
  }
 
  free(data.entries);
 

	
 
  conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>");
 
  conn->request_info.status_code = 200;
 
}
 

	
 
// Send len bytes from the opened file to the client.
 
static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t len) {
 
  char buf[MG_BUF_LEN];
 
  int to_read, num_read, num_written;
 

	
 
  while (len > 0) {
 
    // Calculate how much to read from the file in the buffer
 
    to_read = sizeof(buf);
 
    if ((int64_t) to_read > len) {
 
      to_read = (int) len;
 
    }
 

	
 
    // Read from file, exit the loop on error
 
    if ((num_read = fread(buf, 1, (size_t)to_read, fp)) <= 0) {
 
      break;
 
    }
 

	
 
    // Send read bytes to the client, exit the loop on error
 
    if ((num_written = mg_write(conn, buf, (size_t)num_read)) != num_read) {
 
      break;
 
    }
 

	
 
    // Both read and were successful, adjust counters
 
    conn->num_bytes_sent += num_written;
 
    len -= num_written;
 
  }
 
}
 

	
 
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
 
  return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
 
}
 

	
 
static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
 
  strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
 
}
 

	
 
static void construct_etag(char *buf, size_t buf_len,
 
                           const struct mgstat *stp) {
 
  snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
 
           (unsigned long) stp->mtime, stp->size);
 
}
 

	
 
static void handle_file_request(struct mg_connection *conn, const char *path,
 
                                struct mgstat *stp) {
 
  char date[64], lm[64], etag[64], range[64];
 
  const char *msg = "OK", *hdr;
 
  time_t curtime = time(NULL);
 
  int64_t cl, r1, r2;
 
  struct vec mime_vec;
 
  FILE *fp;
 
  int n;
 

	
 
  get_mime_type(conn->ctx, path, &mime_vec);
 
  cl = stp->size;
 
  conn->request_info.status_code = 200;
 
  range[0] = '\0';
 

	
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen(%s): %s", path, strerror(ERRNO));
 
    return;
 
  }
 
  set_close_on_exec(fileno(fp));
 

	
 
  // If Range: header specified, act accordingly
 
  r1 = r2 = 0;
 
  hdr = mg_get_header(conn, "Range");
 
  if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0) {
 
    conn->request_info.status_code = 206;
 
    (void) fseeko(fp, r1, SEEK_SET);
 
    cl = n == 2 ? r2 - r1 + 1: cl - r1;
 
    (void) mg_snprintf(conn, range, sizeof(range),
 
        "Content-Range: bytes "
 
        "%" INT64_FMT "-%"
 
        INT64_FMT "/%" INT64_FMT "\r\n",
 
        r1, r1 + cl - 1, stp->size);
 
    msg = "Partial Content";
 
  }
 

	
 
  // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
 
  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
 
  gmt_time_string(date, sizeof(date), &curtime);
 
  gmt_time_string(lm, sizeof(lm), &stp->mtime);
 
  construct_etag(etag, sizeof(etag), stp);
 

	
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 %d %s\r\n"
 
      "Date: %s\r\n"
 
      "Last-Modified: %s\r\n"
 
      "Etag: %s\r\n"
 
      "Content-Type: %.*s\r\n"
 
      "Content-Length: %" INT64_FMT "\r\n"
 
      "Connection: %s\r\n"
 
      "Accept-Ranges: bytes\r\n"
 
      "%s\r\n",
 
      conn->request_info.status_code, msg, date, lm, etag, (int) mime_vec.len,
 
      mime_vec.ptr, cl, suggest_connection_header(conn), range);
 

	
 
  if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
 
    send_file_data(conn, fp, cl);
 
  }
 
  (void) fclose(fp);
 
}
 

	
 
void mg_send_file(struct mg_connection *conn, const char *path) {
 
  struct mgstat st;
 
  if (mg_stat(path, &st) == 0) {
 
    handle_file_request(conn, path, &st);
 
  } else {
 
    send_http_error(conn, 404, "Not Found", "%s", "File not found");
 
  }
 
}
 

	
 

	
 
// Parse HTTP headers from the given buffer, advance buffer to the point
 
// where parsing stopped.
 
static void parse_http_headers(char **buf, struct mg_request_info *ri) {
 
  int i;
 

	
 
  for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
 
    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
 
    ri->http_headers[i].value = skip(buf, "\r\n");
 
    if (ri->http_headers[i].name[0] == '\0')
 
      break;
 
    ri->num_headers = i + 1;
 
  }
 
}
 

	
 
static int is_valid_http_method(const char *method) {
 
  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
 
    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
 
    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
 
    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND");
 
}
 

	
 
// Parse HTTP request, fill in mg_request_info structure.
 
// This function modifies the buffer by NUL-terminating
 
// HTTP request components, header names and header values.
 
static int parse_http_message(char *buf, int len, struct mg_request_info *ri) {
 
  int request_length = get_request_len(buf, len);
 
  if (request_length > 0) {
 
    // Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
 
    ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
 
    ri->num_headers = 0;
 
    ri->status_code = -1;
 

	
 
    buf[request_length - 1] = '\0';
 

	
 
    // RFC says that all initial whitespaces should be ingored
 
    while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
 
      buf++;
 
    }
 
    ri->request_method = skip(&buf, " ");
 
    ri->uri = skip(&buf, " ");
 
    ri->http_version = skip(&buf, "\r\n");
 
    parse_http_headers(&buf, ri);
 
  }
 
  return request_length;
 
}
 

	
 
static int parse_http_request(char *buf, int len, struct mg_request_info *ri) {
 
  int result = parse_http_message(buf, len, ri);
 
  if (result > 0 &&
 
      is_valid_http_method(ri->request_method) &&
 
      !strncmp(ri->http_version, "HTTP/", 5)) {
 
    ri->http_version += 5;   // Skip "HTTP/"
 
  } else {
 
    result = -1;
 
  }
 
  return result;
 
}
 

	
 
static int parse_http_response(char *buf, int len, struct mg_request_info *ri) {
 
  int result = parse_http_message(buf, len, ri);
 
  return result > 0 && !strncmp(ri->request_method, "HTTP/", 5) ? result : -1;
 
}
 

	
 
// Keep reading the input (either opened file descriptor fd, or socket sock,
 
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
 
// buffer (which marks the end of HTTP request). Buffer buf may already
 
// have some data. The length of the data is stored in nread.
 
// Upon every read operation, increase nread by the number of bytes read.
 
static int read_request(FILE *fp, struct mg_connection *conn,
 
                        char *buf, int bufsiz, int *nread) {
 
  int request_len, n = 1;
 

	
 
  request_len = get_request_len(buf, *nread);
 
  while (*nread < bufsiz && request_len == 0 && n > 0) {
 
    n = pull(fp, conn, buf + *nread, bufsiz - *nread);
 
    if (n > 0) {
 
      *nread += n;
 
      request_len = get_request_len(buf, *nread);
 
    }
 
  }
 

	
 
  if (n < 0) {
 
    // recv() error -> propagate error; do not process a b0rked-with-very-high-probability request
 
    return -1;
 
  }
 
  return request_len;
 
}
 

	
 
// For given directory path, substitute it to valid index file.
 
// Return 0 if index file has been found, -1 if not found.
 
// If the file is found, it's stats is returned in stp.
 
static int substitute_index_file(struct mg_connection *conn, char *path,
 
                                 size_t path_len, struct mgstat *stp) {
 
  const char *list = conn->ctx->config[INDEX_FILES];
 
  struct mgstat st;
 
  struct vec filename_vec;
 
  size_t n = strlen(path);
 
  int found = 0;
 

	
 
  // The 'path' given to us points to the directory. Remove all trailing
 
  // directory separator characters from the end of the path, and
 
  // then append single directory separator character.
 
  while (n > 0 && IS_DIRSEP_CHAR(path[n - 1])) {
 
    n--;
 
  }
 
  path[n] = DIRSEP;
 

	
 
  // Traverse index files list. For each entry, append it to the given
 
  // path and see if the file exists. If it exists, break the loop
 
  while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
 

	
 
    // Ignore too long entries that may overflow path buffer
 
    if (filename_vec.len > path_len - (n + 2))
 
      continue;
 

	
 
    // Prepare full path to the index file
 
    (void) mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1);
 

	
 
    // Does it exist?
 
    if (mg_stat(path, &st) == 0) {
 
      // Yes it does, break the loop
 
      *stp = st;
 
      found = 1;
 
      break;
 
    }
 
  }
 

	
 
  // If no index file exists, restore directory path
 
  if (!found) {
 
    path[n] = '\0';
 
  }
 

	
 
  return found;
 
}
 

	
 
// Return True if we should reply 304 Not Modified.
 
static int is_not_modified(const struct mg_connection *conn,
 
                           const struct mgstat *stp) {
 
  char etag[64];
 
  const char *ims = mg_get_header(conn, "If-Modified-Since");
 
  const char *inm = mg_get_header(conn, "If-None-Match");
 
  construct_etag(etag, sizeof(etag), stp);
 
  return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
 
    (ims != NULL && stp->mtime <= parse_date_string(ims));
 
}
 

	
 
static int forward_body_data(struct mg_connection *conn, FILE *fp,
 
                             SOCKET sock, SSL *ssl) {
 
  const char *expect;
 
  char buf[MG_BUF_LEN];
 
  int to_read, nread, buffered_len, success = 0;
 

	
 
  expect = mg_get_header(conn, "Expect");
 
  assert(fp != NULL);
 

	
 
  if (conn->content_len == -1) {
 
    send_http_error(conn, 411, "Length Required", "%s", "");
 
  } else if (expect != NULL && mg_strcasecmp(expect, "100-continue")) {
 
    send_http_error(conn, 417, "Expectation Failed", "%s", "");
 
  } else {
 
    if (expect != NULL) {
 
      (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
 
    }
 

	
 
    buffered_len = conn->next_request - conn->body;
 
    assert(buffered_len >= 0);
 
    assert(conn->consumed_content == 0);
 

	
 
    if (buffered_len > 0) {
 
      if ((int64_t) buffered_len > conn->content_len) {
 
        buffered_len = (int) conn->content_len;
 
      }
 
      push(fp, sock, ssl, conn->body, (int64_t) buffered_len);
 
      conn->consumed_content += buffered_len;
 
      conn->body += buffered_len;
 
    }
 

	
 
    nread = 0;
 
    while (conn->consumed_content < conn->content_len) {
 
      to_read = sizeof(buf);
 
      if ((int64_t) to_read > conn->content_len - conn->consumed_content) {
 
        to_read = (int) (conn->content_len - conn->consumed_content);
 
      }
 
      nread = pull(NULL, conn, buf, to_read);
 
      if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
 
        break;
 
      }
 
      conn->consumed_content += nread;
 
    }
 

	
 
    if (conn->consumed_content == conn->content_len) {
 
      success = nread >= 0;
 
    }
 

	
 
    // Each error code path in this function must send an error
 
    if (!success) {
 
      send_http_error(conn, 577, http_500_error, "%s", "");
 
    }
 
  }
 

	
 
  return success;
 
}
 

	
 
#if !defined(NO_CGI)
 
// This structure helps to create an environment for the spawned CGI program.
 
// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
 
// last element must be NULL.
 
// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
 
// strings must reside in a contiguous buffer. The end of the buffer is
 
// marked by two '\0' characters.
 
// We satisfy both worlds: we create an envp array (which is vars), all
 
// entries are actually pointers inside buf.
 
struct cgi_env_block {
 
  struct mg_connection *conn;
 
  char buf[CGI_ENVIRONMENT_SIZE]; // Environment buffer
 
  int len; // Space taken
 
  char *vars[MAX_CGI_ENVIR_VARS]; // char **envp
 
  int nvars; // Number of variables
 
};
 

	
 
static char *addenv(struct cgi_env_block *block,
 
                    PRINTF_FORMAT_STRING(const char *fmt), ...)
 
  PRINTF_ARGS(2, 3);
 

	
 
// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
 
// pointer into the vars array.
 
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
 
  int n, space;
 
  char *added;
 
  va_list ap;
 

	
 
  // Calculate how much space is left in the buffer
 
  space = sizeof(block->buf) - block->len - 2;
 
  assert(space >= 0);
 

	
 
  // Make a pointer to the free space int the buffer
 
  added = block->buf + block->len;
 

	
 
  // Copy VARIABLE=VALUE\0 string into the free space
 
  va_start(ap, fmt);
 
  n = mg_vsnprintf(block->conn, added, (size_t) space, fmt, ap);
 
  va_end(ap);
 

	
 
  // Make sure we do not overflow buffer and the envp array
 
  if (n > 0 && n + 1 < space &&
 
      block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
 
    // Append a pointer to the added string into the envp array
 
    block->vars[block->nvars++] = added;
 
    // Bump up used length counter. Include \0 terminator
 
    block->len += n + 1;
 
  } else {
 
    cry(block->conn, "%s: CGI env buffer truncated for [%s]", __func__, fmt);
 
  }
 

	
 
  return added;
 
}
 

	
 
static void prepare_cgi_environment(struct mg_connection *conn,
 
                                    const char *prog,
 
                                    struct cgi_env_block *blk) {
 
  const char *s, *slash;
 
  struct vec var_vec;
 
  char *p, src_addr[20];
 
  int  i;
 

	
 
  blk->len = blk->nvars = 0;
 
  blk->conn = conn;
 
  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 

	
 
  addenv(blk, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]);
 
  addenv(blk, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
 
  addenv(blk, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]);
 

	
 
  // Prepare the environment block
 
  addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
 
  addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
 
  addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
 

	
 
  // TODO(lsm): fix this for IPv6 case
 
  addenv(blk, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port));
 

	
 
  addenv(blk, "REQUEST_METHOD=%s", conn->request_info.request_method);
 
  addenv(blk, "REMOTE_ADDR=%s", src_addr);
 
  addenv(blk, "REMOTE_PORT=%d", conn->request_info.remote_port);
 
  addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
 

	
 
  // SCRIPT_NAME
 
  assert(conn->request_info.uri[0] == '/');
 
  slash = strrchr(conn->request_info.uri, '/');
 
  if ((s = strrchr(prog, '/')) == NULL)
 
    s = prog;
 
  addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri),
 
         conn->request_info.uri, s);
 

	
 
  addenv(blk, "SCRIPT_FILENAME=%s", prog);
 
  addenv(blk, "PATH_TRANSLATED=%s", prog);
 
  addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
 

	
 
  if ((s = mg_get_header(conn, "Content-Type")) != NULL)
 
    addenv(blk, "CONTENT_TYPE=%s", s);
 

	
 
  if (conn->request_info.query_string != NULL)
 
    addenv(blk, "QUERY_STRING=%s", conn->request_info.query_string);
 

	
 
  if ((s = mg_get_header(conn, "Content-Length")) != NULL)
 
    addenv(blk, "CONTENT_LENGTH=%s", s);
 

	
 
  if ((s = getenv("PATH")) != NULL)
 
    addenv(blk, "PATH=%s", s);
 

	
 
  if (conn->path_info != NULL) {
 
    addenv(blk, "PATH_INFO=%s", conn->path_info);
 
  }
 

	
 
#if defined(_WIN32)
 
  if ((s = getenv("COMSPEC")) != NULL) {
 
    addenv(blk, "COMSPEC=%s", s);
 
  }
 
  if ((s = getenv("SYSTEMROOT")) != NULL) {
 
    addenv(blk, "SYSTEMROOT=%s", s);
 
  }
 
  if ((s = getenv("SystemDrive")) != NULL) {
 
    addenv(blk, "SystemDrive=%s", s);
 
  }
 
#else
 
  if ((s = getenv("LD_LIBRARY_PATH")) != NULL)
 
    addenv(blk, "LD_LIBRARY_PATH=%s", s);
 
#endif // _WIN32
 

	
 
  if ((s = getenv("PERLLIB")) != NULL)
 
    addenv(blk, "PERLLIB=%s", s);
 

	
 
  if (conn->request_info.remote_user != NULL) {
 
    addenv(blk, "REMOTE_USER=%s", conn->request_info.remote_user);
 
    addenv(blk, "%s", "AUTH_TYPE=Digest");
 
  }
 

	
 
  // Add all headers as HTTP_* variables
 
  for (i = 0; i < conn->request_info.num_headers; i++) {
 
    p = addenv(blk, "HTTP_%s=%s",
 
        conn->request_info.http_headers[i].name,
 
        conn->request_info.http_headers[i].value);
 

	
 
    // Convert variable name into uppercase, and change - to _
 
    for (; *p != '=' && *p != '\0'; p++) {
 
      if (*p == '-')
 
        *p = '_';
 
      *p = (char) toupper(* (unsigned char *) p);
 
    }
 
  }
 

	
 
  // Add user-specified variables
 
  s = conn->ctx->config[CGI_ENVIRONMENT];
 
  while ((s = next_option(s, &var_vec, NULL)) != NULL) {
 
    addenv(blk, "%.*s", (int) var_vec.len, var_vec.ptr);
 
  }
 

	
 
  blk->vars[blk->nvars++] = NULL;
 
  blk->buf[blk->len++] = '\0';
 

	
 
  assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
 
  assert(blk->len > 0);
 
  assert(blk->len < (int) sizeof(blk->buf));
 
}
 

	
 
static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
 
  int headers_len, data_len, i, fd_stdin[2], fd_stdout[2];
 
  const char *status, *status_text;
 
  char buf[16384], *pbuf, dir[PATH_MAX], *p;
 
  struct mg_request_info ri;
 
  struct cgi_env_block blk;
 
  FILE *in, *out;
 
  pid_t pid;
 

	
 
  prepare_cgi_environment(conn, prog, &blk);
 

	
 
  // CGI must be executed in its own directory. 'dir' must point to the
 
  // directory containing executable program, 'p' must point to the
 
  // executable program name relative to 'dir'.
 
  (void) mg_snprintf(conn, dir, sizeof(dir), "%s", prog);
 
  if ((p = strrchr(dir, DIRSEP)) != NULL) {
 
    *p++ = '\0';
 
  } else {
 
    dir[0] = '.', dir[1] = '\0';
 
    p = (char *) prog;
 
  }
 

	
 
  pid = (pid_t) -1;
 
  fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
 
  in = out = NULL;
 

	
 
  if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
 
    send_http_error(conn, 500, http_500_error,
 
        "Cannot create CGI pipe: %s", strerror(ERRNO));
 
    goto done;
 
  } else if ((pid = spawn_process(conn, p, blk.buf, blk.vars,
 
          fd_stdin[0], fd_stdout[1], dir)) == (pid_t) -1) {
 
    send_http_error(conn, 500, http_500_error,
 
        "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
 
    goto done;
 
  } else if ((in = fdopen(fd_stdin[1], "wb")) == NULL ||
 
      (out = fdopen(fd_stdout[0], "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen: %s", strerror(ERRNO));
 
    goto done;
 
  }
 

	
 
  setbuf(in, NULL);
 
  setbuf(out, NULL);
 

	
 
  // spawn_process() must close those!
 
  // If we don't mark them as closed, close() attempt before
 
  // return from this function throws an exception on Windows.
 
  // Windows does not like when closed descriptor is closed again.
 
  fd_stdin[0] = fd_stdout[1] = -1;
 

	
 
  // Send POST data to the CGI process if needed
 
  if (!strcmp(conn->request_info.request_method, "POST") &&
 
      !forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
 
    goto done;
 
  }
 
  // Close so child gets an EOF.
 
  fclose(in);
 
  in = NULL;
 

	
 
  // Now read CGI reply into a buffer. We need to set correct
 
  // status code, thus we need to see all HTTP headers first.
 
  // Do not send anything back to client, until we buffer in all
 
  // HTTP headers.
 
  data_len = 0;
 
  headers_len = read_request(out, fc(conn->ctx), buf, sizeof(buf), &data_len);
 
  if (headers_len <= 0) {
 
    send_http_error(conn, 500, http_500_error,
 
                    "CGI program sent malformed or too big (>%u bytes) "
 
                    "HTTP headers: [%.*s]",
 
                    (unsigned) sizeof(buf), data_len, buf);
 
    goto done;
 
  }
 
  pbuf = buf;
 
  buf[headers_len - 1] = '\0';
 
  parse_http_headers(&pbuf, &ri);
 

	
 
  // Make up and send the status line
 
  status_text = "OK";
 
  if ((status = get_header(&ri, "Status")) != NULL) {
 
    conn->request_info.status_code = atoi(status);
 
    status_text = status;
 
    while (isdigit(* (unsigned char *) status_text) || *status_text == ' ') {
 
      status_text++;
 
    }
 
  } else if (get_header(&ri, "Location") != NULL) {
 
    conn->request_info.status_code = 302;
 
  } else {
 
    conn->request_info.status_code = 200;
 
  }
 
  if (get_header(&ri, "Connection") != NULL &&
 
      !mg_strcasecmp(get_header(&ri, "Connection"), "keep-alive")) {
 
    conn->must_close = 1;
 
  }
 
  (void) mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->request_info.status_code,
 
                   status_text);
 

	
 
  // Send headers
 
  for (i = 0; i < ri.num_headers; i++) {
 
    mg_printf(conn, "%s: %s\r\n",
 
              ri.http_headers[i].name, ri.http_headers[i].value);
 
  }
 
  (void) mg_write(conn, "\r\n", 2);
 

	
 
  // Send chunk of data that may have been read after the headers
 
  conn->num_bytes_sent += mg_write(conn, buf + headers_len,
 
                                   (size_t)(data_len - headers_len));
 

	
 
  // Read the rest of CGI output and send to the client
 
  send_file_data(conn, out, INT64_MAX);
 

	
 
done:
 
  if (pid != (pid_t) -1) {
 
    kill(pid, SIGKILL);
 
  }
 
  if (fd_stdin[0] != -1) {
 
    (void) close(fd_stdin[0]);
 
  }
 
  if (fd_stdout[1] != -1) {
 
    (void) close(fd_stdout[1]);
 
  }
 

	
 
  if (in != NULL) {
 
    (void) fclose(in);
 
  } else if (fd_stdin[1] != -1) {
 
    (void) close(fd_stdin[1]);
 
  }
 

	
 
  if (out != NULL) {
 
    (void) fclose(out);
 
  } else if (fd_stdout[0] != -1) {
 
    (void) close(fd_stdout[0]);
 
  }
 
}
 
#endif // !NO_CGI
 

	
 
// For a given PUT path, create all intermediate subdirectories
 
// for given path. Return 0 if the path itself is a directory,
 
// or -1 on error, 1 if OK.
 
static int put_dir(const char *path) {
 
  char buf[PATH_MAX];
 
  const char *s, *p;
 
  struct mgstat st;
 
  int len, res = 1;
 

	
 
  for (s = p = path + 2; (p = strchr(s, DIRSEP)) != NULL; s = ++p) {
 
    len = p - path;
 
    if (len >= (int) sizeof(buf)) {
 
      res = -1;
 
      break;
 
    }
 
    memcpy(buf, path, len);
 
    buf[len] = '\0';
 

	
 
    // Try to create intermediate directory
 
    DEBUG_TRACE(("mkdir(%s)", buf));
 
    if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0) {
 
      res = -1;
 
      break;
 
    }
 

	
 
    // Is path itself a directory?
 
    if (p[1] == '\0') {
 
      res = 0;
 
    }
 
  }
 

	
 
  return res;
 
}
 

	
 
static void put_file(struct mg_connection *conn, const char *path) {
 
  struct mgstat st;
 
  const char *range;
 
  int64_t r1, r2;
 
  FILE *fp;
 
  int rc;
 

	
 
  conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;
 

	
 
  if ((rc = put_dir(path)) == 0) {
 
    mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->request_info.status_code);
 
  } else if (rc == -1) {
 
    send_http_error(conn, 500, http_500_error,
 
        "put_dir(%s): %s", path, strerror(ERRNO));
 
  } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
 
    send_http_error(conn, 500, http_500_error,
 
        "fopen(%s): %s", path, strerror(ERRNO));
 
  } else {
 
    set_close_on_exec(fileno(fp));
 
    range = mg_get_header(conn, "Content-Range");
 
    r1 = r2 = 0;
 
    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
 
      conn->request_info.status_code = 206;
 
      // TODO(lsm): handle seek error
 
      (void) fseeko(fp, r1, SEEK_SET);
 
    }
 
    if (forward_body_data(conn, fp, INVALID_SOCKET, NULL))
 
      (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n",
 
          conn->request_info.status_code);
 
    (void) fclose(fp);
 
  }
 
}
 

	
 
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);
 

	
 
static void do_ssi_include(struct mg_connection *conn, const char *ssi,
 
                           char *tag, int include_level) {
 
  char file_name[MG_BUF_LEN], path[PATH_MAX], *p;
 
  FILE *fp;
 

	
 
  // sscanf() is safe here, since send_ssi_file() also uses buffer
 
  // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
 
  if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the webserver root
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s%c%s",
 
        conn->ctx->config[DOCUMENT_ROOT], DIRSEP, file_name);
 
  } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the webserver working directory
 
    // or it is absolute system path
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name);
 
  } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
 
    // File name is relative to the currect document
 
    (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi);
 
    if ((p = strrchr(path, DIRSEP)) != NULL) {
 
      p[1] = '\0';
 
    }
 
    (void) mg_snprintf(conn, path + strlen(path),
 
        sizeof(path) - strlen(path), "%s", file_name);
 
  } else {
 
    cry(conn, "Bad SSI #include: [%s]", tag);
 
    return;
 
  }
 

	
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
 
        tag, path, strerror(ERRNO));
 
  } else {
 
    set_close_on_exec(fileno(fp));
 
    if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
 
                     strlen(conn->ctx->config[SSI_EXTENSIONS]), path) > 0) {
 
      send_ssi_file(conn, path, fp, include_level + 1);
 
    } else {
 
      send_file_data(conn, fp, INT64_MAX);
 
    }
 
    (void) fclose(fp);
 
  }
 
}
 

	
 
#if !defined(NO_POPEN)
 
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
 
  char cmd[MG_BUF_LEN];
 
  FILE *fp;
 

	
 
  if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
 
    cry(conn, "Bad SSI #exec: [%s]", tag);
 
  } else if ((fp = popen(cmd, "r")) == NULL) {
 
    cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
 
  } else {
 
    send_file_data(conn, fp, INT64_MAX);
 
    (void) pclose(fp);
 
  }
 
}
 
#endif // !NO_POPEN
 

	
 
static void send_ssi_file(struct mg_connection *conn, const char *path,
 
                          FILE *fp, int include_level) {
 
  char buf[MG_BUF_LEN];
 
  int ch, len, in_ssi_tag;
 

	
 
  if (include_level > 10) {
 
    cry(conn, "SSI #include level is too deep (%s)", path);
 
    return;
 
  }
 

	
 
  in_ssi_tag = 0;
 
  len = 0;
 

	
 
  while ((ch = fgetc(fp)) != EOF) {
 
    if (in_ssi_tag && ch == '>') {
 
      in_ssi_tag = 0;
 
      buf[len++] = (char) ch;
 
      buf[len] = '\0';
 
      assert(len <= (int) sizeof(buf));
 
      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
 
        // Not an SSI tag, pass it
 
        (void) mg_write(conn, buf, (size_t)len);
 
      } else {
 
        if (!memcmp(buf + 5, "include", 7)) {
 
          do_ssi_include(conn, path, buf + 12, include_level);
 
#if !defined(NO_POPEN)
 
        } else if (!memcmp(buf + 5, "exec", 4)) {
 
          do_ssi_exec(conn, buf + 9);
 
#endif // !NO_POPEN
 
        } else {
 
          cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
 
        }
 
      }
 
      len = 0;
 
    } else if (in_ssi_tag) {
 
      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
 
        // Not an SSI tag
 
        in_ssi_tag = 0;
 
      } else if (len == (int) sizeof(buf) - 2) {
 
        cry(conn, "%s: SSI tag is too large", path);
 
        len = 0;
 
      }
 
      buf[len++] = ch & 0xff;
 
    } else if (ch == '<') {
 
      in_ssi_tag = 1;
 
      if (len > 0) {
 
        (void) mg_write(conn, buf, (size_t)len);
 
      }
 
      len = 0;
 
      buf[len++] = ch & 0xff;
 
    } else {
 
      buf[len++] = ch & 0xff;
 
      if (len == (int) sizeof(buf)) {
 
        (void) mg_write(conn, buf, (size_t)len);
 
        len = 0;
 
      }
 
    }
 
  }
 

	
 
  // Send the rest of buffered data
 
  if (len > 0) {
 
    (void) mg_write(conn, buf, (size_t)len);
 
  }
 
}
 

	
 
static void handle_ssi_file_request(struct mg_connection *conn,
 
                                    const char *path) {
 
  FILE *fp;
 

	
 
  if ((fp = mg_fopen(path, "rb")) == NULL) {
 
    send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
 
                    strerror(ERRNO));
 
  } else {
 
    conn->must_close = 1;
 
    set_close_on_exec(fileno(fp));
 
    mg_printf(conn, "HTTP/1.1 200 OK\r\n"
 
              "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
 
              suggest_connection_header(conn));
 
    send_ssi_file(conn, path, fp, 0);
 
    (void) fclose(fp);
 
  }
 
}
 

	
 
static void send_options(struct mg_connection *conn) {
 
  conn->request_info.status_code = 200;
 

	
 
  (void) mg_printf(conn,
 
      "HTTP/1.1 200 OK\r\n"
 
      "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS\r\n"
 
      "DAV: 1\r\n\r\n");
 
}
 

	
 
// Writes PROPFIND properties for a collection element
 
static void print_props(struct mg_connection *conn, const char* uri,
 
                        struct mgstat* st) {
 
  char mtime[64];
 
  gmt_time_string(mtime, sizeof(mtime), &st->mtime);
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<d:response>"
 
       "<d:href>%s</d:href>"
 
       "<d:propstat>"
 
        "<d:prop>"
 
         "<d:resourcetype>%s</d:resourcetype>"
 
         "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
 
         "<d:getlastmodified>%s</d:getlastmodified>"
 
        "</d:prop>"
 
        "<d:status>HTTP/1.1 200 OK</d:status>"
 
       "</d:propstat>"
 
      "</d:response>\n",
 
      uri,
 
      st->is_directory ? "<d:collection/>" : "",
 
      st->size,
 
      mtime);
 
}
 

	
 
static void print_dav_dir_entry(struct de *de, void *data) {
 
  char href[PATH_MAX];
 
  struct mg_connection *conn = (struct mg_connection *) data;
 
  mg_snprintf(conn, href, sizeof(href), "%s%s",
 
              conn->request_info.uri, de->file_name);
 
  print_props(conn, href, &de->st);
 
}
 

	
 
static void handle_propfind(struct mg_connection *conn, const char* path,
 
                            struct mgstat* st) {
 
  const char *depth = mg_get_header(conn, "Depth");
 

	
 
  conn->must_close = 1;
 
  conn->request_info.status_code = 207;
 
  mg_printf(conn, "HTTP/1.1 207 Multi-Status\r\n"
 
            "Connection: close\r\n"
 
            "Content-Type: text/xml; charset=utf-8\r\n\r\n");
 

	
 
  conn->num_bytes_sent += mg_printf(conn,
 
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 
      "<d:multistatus xmlns:d='DAV:'>\n");
 

	
 
  // Print properties for the requested resource itself
 
  print_props(conn, conn->request_info.uri, st);
 

	
 
  // If it is a directory, print directory entries too if Depth is not 0
 
  if (st->is_directory &&
 
      !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") &&
 
      (depth == NULL || strcmp(depth, "0") != 0)) {
 
    scan_directory(conn, path, conn, &print_dav_dir_entry);
 
  }
 

	
 
  conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>");
 
}
 

	
 
// This is the heart of the Mongoose's logic.
 
// This function is called when the request is read, parsed and validated,
 
// and Mongoose must decide what action to take: serve a file, or
 
// a directory, or call embedded function, etcetera.
 
static void handle_request(struct mg_connection *conn) {
 
  struct mg_request_info *ri = &conn->request_info;
 
  char path[PATH_MAX];
 
  int stat_result, uri_len;
 
  struct mgstat st;
 

	
 
  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
 
    *conn->request_info.query_string++ = '\0';
 
  }
 
  uri_len = (int) strlen(ri->uri);
 
  url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);
 
  remove_double_dots_and_double_slashes(ri->uri);
 
  stat_result = convert_uri_to_file_name(conn, path, sizeof(path), &st);
 

	
 
  DEBUG_TRACE(("%s", ri->uri));
 
  if (!check_authorization(conn, path)) {
 
    send_authorization_request(conn);
 
  } else if (call_user(conn, MG_NEW_REQUEST) != NULL) {
 
    // Do nothing, callback has served the request
 
  } else if (!strcmp(ri->request_method, "OPTIONS")) {
 
    send_options(conn);
 
  } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
 
    send_http_error(conn, 404, "Not Found", "Not Found");
 
  } else if ((!strcmp(ri->request_method, "PUT") ||
 
        !strcmp(ri->request_method, "DELETE")) &&
 
      (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ||
 
       is_authorized_for_put(conn) != 1)) {
 
    send_authorization_request(conn);
 
  } else if (!strcmp(ri->request_method, "PUT")) {
 
    put_file(conn, path);
 
  } else if (!strcmp(ri->request_method, "DELETE")) {
 
    if (mg_remove(path) == 0) {
 
      send_http_error(conn, 200, "OK", "%s", "");
 
    } else {
 
      send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
 
                      strerror(ERRNO));
 
    }
 
  } else if (stat_result != 0 || must_hide_file(conn, path)) {
 
    send_http_error(conn, 404, "Not Found", "%s", "File not found");
 
  } else if (st.is_directory && ri->uri[uri_len - 1] != '/') {
 
    (void) mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
 
                     "Location: %s/\r\n\r\n", ri->uri);
 
  } else if (!strcmp(ri->request_method, "PROPFIND")) {
 
    handle_propfind(conn, path, &st);
 
  } else if (st.is_directory &&
 
             !substitute_index_file(conn, path, sizeof(path), &st)) {
 
    if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
 
      handle_directory_request(conn, path);
 
    } else {
 
      send_http_error(conn, 403, "Directory Listing Denied",
 
          "Directory listing denied");
 
    }
 
#if !defined(NO_CGI)
 
  } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
 
                          strlen(conn->ctx->config[CGI_EXTENSIONS]),
 
                          path) > 0) {
 
    if (strcmp(ri->request_method, "POST") &&
 
        strcmp(ri->request_method, "GET")) {
 
      send_http_error(conn, 501, "Not Implemented",
 
                      "Method %s is not implemented", ri->request_method);
 
    } else {
 
      handle_cgi_request(conn, path);
 
    }
 
#endif // !NO_CGI
 
  } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS],
 
                          strlen(conn->ctx->config[SSI_EXTENSIONS]),
 
                          path) > 0) {
 
    handle_ssi_file_request(conn, path);
 
  } else if (is_not_modified(conn, &st)) {
 
    send_http_error(conn, 304, "Not Modified", "%s", "");
 
  } else {
 
    handle_file_request(conn, path, &st);
 
  }
 
}
 

	
 
static void close_all_listening_sockets(struct mg_context *ctx) {
 
  struct socket *sp, *tmp;
 
  for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
 
    tmp = sp->next;
 
    (void) closesocket(sp->sock);
 
    free(sp);
 
  }
 
}
 

	
 
// Valid listening port specification is: [ip_address:]port[s]
 
// Examples: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s
 
// TODO(lsm): add parsing of the IPv6 address
 
static int parse_port_string(const struct vec *vec, struct socket *so) {
 
  int a, b, c, d, port, len;
 

	
 
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
 
  // Also, all-zeroes in the socket address means binding to all addresses
 
  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
 
  memset(so, 0, sizeof(*so));
 

	
 
  if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) == 5) {
 
    // Bind to a specific IPv4 address
 
    so->lsa.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
 
  } else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
 
             len <= 0 ||
 
             len > (int) vec->len ||
 
             (vec->ptr[len] && vec->ptr[len] != 's' && vec->ptr[len] != ',')) {
 
    return 0;
 
  }
 

	
 
  so->is_ssl = vec->ptr[len] == 's';
 
#if defined(USE_IPV6)
 
  so->lsa.sin6.sin6_family = AF_INET6;
 
  so->lsa.sin6.sin6_port = htons((uint16_t) port);
 
#else
 
  so->lsa.sin.sin_family = AF_INET;
 
  so->lsa.sin.sin_port = htons((uint16_t) port);
 
#endif
 

	
 
  return 1;
 
}
 

	
 
static int set_ports_option(struct mg_context *ctx) {
 
  const char *list = ctx->config[LISTENING_PORTS];
 
  int on = 1, success = 1;
 
  SOCKET sock;
 
  struct vec vec;
 
  struct socket so, *listener;
 

	
 
  while (success && (list = next_option(list, &vec, NULL)) != NULL) {
 
    if (!parse_port_string(&vec, &so)) {
 
      cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
 
          __func__, (int) vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
 
      success = 0;
 
    } else if (so.is_ssl &&
 
               (ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
 
      cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
 
      success = 0;
 
    } else if ((sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
 
               INVALID_SOCKET ||
 
#if !defined(_WIN32)
 
               // On Windows, SO_REUSEADDR is recommended only for
 
               // broadcast UDP sockets
 
               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
 
                          sizeof(on)) != 0 ||
 
#endif // !_WIN32
 
               // Set TCP keep-alive. This is needed because if HTTP-level
 
               // keep-alive is enabled, and client resets the connection,
 
               // server won't get TCP FIN or RST and will keep the connection
 
               // open forever. With TCP keep-alive, next keep-alive
 
               // handshake will figure out that the client is down and
 
               // will close the server end.
 
               // Thanks to Igor Klopov who suggested the patch.
 
               setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &on,
 
                          sizeof(on)) != 0 ||
 
               bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
 
               listen(sock, SOMAXCONN) != 0) {
 
      closesocket(sock);
 
      cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
 
          vec.len, vec.ptr, strerror(ERRNO));
 
      success = 0;
 
    } else if ((listener = (struct socket *)
 
                calloc(1, sizeof(*listener))) == NULL) {
 
      // NOTE(lsm): order is important: call cry before closesocket(),
 
      // cause closesocket() alters the errno.
 
      cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
 
      closesocket(sock);
 
      success = 0;
 
    } else {
 
      *listener = so;
 
      listener->sock = sock;
 
      set_close_on_exec(listener->sock);
 
      listener->next = ctx->listening_sockets;
 
      ctx->listening_sockets = listener;
 
    }
 
  }
 

	
 
  if (!success) {
 
    close_all_listening_sockets(ctx);
 
  }
 

	
 
  return success;
 
}
 

	
 
static void log_header(const struct mg_connection *conn, const char *header,
 
                       FILE *fp) {
 
  const char *header_value;
 

	
 
  if ((header_value = mg_get_header(conn, header)) == NULL) {
 
    (void) fprintf(fp, "%s", " -");
 
  } else {
 
    (void) fprintf(fp, " \"%s\"", header_value);
 
  }
 
}
 

	
 
static void log_access(const struct mg_connection *conn) {
 
  const struct mg_request_info *ri;
 
  FILE *fp;
 
  char date[64], src_addr[20];
 

	
 
  fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ?  NULL :
 
    mg_fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");
 

	
 
  if (fp == NULL)
 
    return;
 

	
 
  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
 
           localtime(&conn->birth_time));
 

	
 
  ri = &conn->request_info;
 
  flockfile(fp);
 

	
 
  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
 
  fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
 
          src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
 
          ri->request_method ? ri->request_method : "-",
 
          ri->uri ? ri->uri : "-", ri->http_version,
 
          conn->request_info.status_code, conn->num_bytes_sent);
 
  log_header(conn, "Referer", fp);
 
  log_header(conn, "User-Agent", fp);
 
  fputc('\n', fp);
 
  fflush(fp);
 

	
 
  funlockfile(fp);
 
  fclose(fp);
 
}
 

	
 
static int isbyte(int n) {
 
  return n >= 0 && n <= 255;
 
}
 

	
 
// Verify given socket address against the ACL.
 
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
 
static int check_acl(struct mg_context *ctx, const union usa *usa) {
 
  int a, b, c, d, n, mask, allowed;
 
  char flag;
 
  uint32_t acl_subnet, acl_mask, remote_ip;
 
  struct vec vec;
 
  const char *list = ctx->config[ACCESS_CONTROL_LIST];
 

	
 
  if (list == NULL) {
 
    return 1;
 
  }
 

	
 
  (void) memcpy(&remote_ip, &usa->sin.sin_addr, sizeof(remote_ip));
 

	
 
  // If any ACL is set, deny by default
 
  allowed = '-';
 

	
 
  while ((list = next_option(list, &vec, NULL)) != NULL) {
 
    mask = 32;
 

	
 
    if (sscanf(vec.ptr, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n) != 5) {
 
      cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
 
      return -1;
 
    } else if (flag != '+' && flag != '-') {
 
      cry(fc(ctx), "%s: flag must be + or -: [%s]", __func__, vec.ptr);
 
      return -1;
 
    } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
 
      cry(fc(ctx), "%s: bad ip address: [%s]", __func__, vec.ptr);
 
      return -1;
 
    } else if (sscanf(vec.ptr + n, "/%d", &mask) == 0) {
 
      // Do nothing, no mask specified
 
    } else if (mask < 0 || mask > 32) {
 
      cry(fc(ctx), "%s: bad subnet mask: %d [%s]", __func__, n, vec.ptr);
 
      return -1;
 
    }
 

	
 
    acl_subnet = (a << 24) | (b << 16) | (c << 8) | d;
 
    acl_mask = mask ? 0xffffffffU << (32 - mask) : 0;
 

	
 
    if (acl_subnet == (ntohl(remote_ip) & acl_mask)) {
 
      allowed = flag;
 
    }
 
  }
 

	
 
  return allowed == '+';
 
}
 

	
 
static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
 
  FD_SET(fd, set);
 
  if (fd > (SOCKET) *max_fd) {
 
    *max_fd = (int) fd;
 
  }
 
}
 

	
 
#if !defined(_WIN32)
 
static int set_uid_option(struct mg_context *ctx) {
 
  struct passwd *pw;
 
  const char *uid = ctx->config[RUN_AS_USER];
 
  int success = 0;
 

	
 
  if (uid == NULL) {
 
    success = 1;
 
  } else {
 
    if ((pw = getpwnam(uid)) == NULL) {
 
      cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
 
    } else if (setgid(pw->pw_gid) == -1) {
 
      cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
 
    } else if (setuid(pw->pw_uid) == -1) {
 
      cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
 
    } else {
 
      success = 1;
 
    }
 
  }
 

	
 
  return success;
 
}
 
#endif // !_WIN32
 

	
 
#if !defined(NO_SSL)
 
static pthread_mutex_t *ssl_mutexes;
 

	
 
// Return OpenSSL error message
 
static const char *ssl_error(void) {
 
  unsigned long err;
 
  err = ERR_get_error();
 
  return err == 0 ? "" : ERR_error_string(err, NULL);
 
}
 

	
 
static void ssl_locking_callback(int mode, int mutex_num, const char *file,
 
                                 int line) {
 
  line = 0;    // Unused
 
  file = NULL; // Unused
 

	
 
  if (mode & CRYPTO_LOCK) {
 
    (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
 
  } else {
 
    (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
 
  }
 
}
 

	
 
static unsigned long ssl_id_callback(void) {
 
  return (unsigned long) pthread_self();
 
}
 

	
 
#if !defined(NO_SSL_DL)
 
static int load_dll(struct mg_context *ctx, const char *dll_name,
 
                    struct ssl_func *sw) {
 
  union {void *p; void (*fp)(void);} u;
 
  void  *dll_handle;
 
  struct ssl_func *fp;
 

	
 
  if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
 
    cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
 
    return 0;
 
  }
 

	
 
  for (fp = sw; fp->name != NULL; fp++) {
 
#ifdef _WIN32
 
    // GetProcAddress() returns pointer to function
 
    u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
 
#else
 
    // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
 
    // function pointers. We need to use a union to make a cast.
 
    u.p = dlsym(dll_handle, fp->name);
 
#endif // _WIN32
 
    if (u.fp == NULL) {
 
      cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
 
      return 0;
 
    } else {
 
      fp->ptr = u.fp;
 
    }
 
  }
 

	
 
  return 1;
 
}
 
#endif // NO_SSL_DL
 

	
 
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
 
static int set_ssl_option(struct mg_context *ctx) {
 
  struct mg_request_info request_info;
 
  int i, size;
 
  const char *pem = ctx->config[SSL_CERTIFICATE];
 
  const char *chain = ctx->config[SSL_CHAIN_FILE];
 

	
 
#if !defined(NO_SSL_DL)
 
  if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
 
      !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
 
    return 0;
 
  }
 
#endif // NO_SSL_DL
 

	
 
  // Initialize SSL crap
 
  SSL_library_init();
 
  SSL_load_error_strings();
 

	
 
  if ((ctx->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
 
    cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
 
  }
 

	
 
  if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
 
    cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
 
  } else if (ctx->user_callback != NULL) {
 
    memset(&request_info, 0, sizeof(request_info));
 
    request_info.user_data = ctx->user_data;
 
    ctx->user_callback(MG_INIT_SSL, (struct mg_connection *) ctx->ssl_ctx);
 
  }
 

	
 
  if (ctx->ssl_ctx != NULL && pem != NULL &&
 
      SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, SSL_FILETYPE_PEM) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
 
    return 0;
 
  }
 
  if (ctx->ssl_ctx != NULL && pem != NULL &&
 
      SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, SSL_FILETYPE_PEM) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
 
    return 0;
 
  }
 
  if (ctx->ssl_ctx != NULL && chain != NULL &&
 
      SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, chain) == 0) {
 
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, chain, ssl_error());
 
    return 0;
 
  }
 

	
 
  // Initialize locking callbacks, needed for thread safety.
 
  // http://www.openssl.org/support/faq.html#PROG1
 
  size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
 
  if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
 
    cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
 
    return 0;
 
  }
 

	
 
  for (i = 0; i < CRYPTO_num_locks(); i++) {
 
    pthread_mutex_init(&ssl_mutexes[i], NULL);
 
  }
 

	
 
  CRYPTO_set_locking_callback(&ssl_locking_callback);
 
  CRYPTO_set_id_callback(&ssl_id_callback);
 

	
 
  return 1;
 
}
 

	
 
static void uninitialize_ssl(struct mg_context *ctx) {
 
  int i;
 
  if (ctx->ssl_ctx != NULL) {
 
    CRYPTO_set_locking_callback(NULL);
 
    for (i = 0; i < CRYPTO_num_locks(); i++) {
 
      pthread_mutex_destroy(&ssl_mutexes[i]);
 
    }
 
    CRYPTO_set_locking_callback(NULL);
 
    CRYPTO_set_id_callback(NULL);
 
  }
 
}
 
#endif // !NO_SSL
 

	
 
static int set_gpass_option(struct mg_context *ctx) {
 
  struct mgstat mgstat;
 
  const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
 
  return path == NULL || mg_stat(path, &mgstat) == 0;
 
}
 

	
 
static int set_acl_option(struct mg_context *ctx) {
 
  union usa fake;
 
  return check_acl(ctx, &fake) != -1;
 
}
 

	
 
static void reset_per_request_attributes(struct mg_connection *conn) {
 
  conn->path_info = conn->body = conn->next_request = NULL;
 
  conn->num_bytes_sent = conn->consumed_content = 0;
 
  conn->content_len = -1;
 
  conn->request_len = conn->data_len = 0;
 
  conn->must_close = 0;
 
}
 

	
 
static void close_socket_gracefully(struct mg_connection *conn) {
 
  char buf[MG_BUF_LEN];
 
  struct linger linger;
 
  int n, sock = conn->client.sock;
 

	
 
  // Set linger option to avoid socket hanging out after close. This prevent
 
  // ephemeral port exhaust problem under high QPS.
 
  linger.l_onoff = 1;
 
  linger.l_linger = 1;
 
  setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
 

	
 
  // Send FIN to the client
 
  (void) shutdown(sock, SHUT_WR);
 
  set_non_blocking_mode(sock);
 

	
 
  // Read and discard pending incoming data. If we do not do that and close the
 
  // socket, the data in the send buffer may be discarded. This
 
  // behaviour is seen on Windows, when client keeps sending data
 
  // when server decides to close the connection; then when client
 
  // does recv() it gets no data back.
 
  do {
 
    n = pull(NULL, conn, buf, sizeof(buf));
 
  } while (n > 0);
 

	
 
  // Now we know that our FIN is ACK-ed, safe to close
 
  (void) closesocket(sock);
 
}
 

	
 
static void close_connection(struct mg_connection *conn) {
 
  if (conn->ssl) {
 
    SSL_free(conn->ssl);
 
    conn->ssl = NULL;
 
  }
 

	
 
  if (conn->client.sock != INVALID_SOCKET) {
 
    close_socket_gracefully(conn);
 
  }
 
}
 

	
 
void mg_close_connection(struct mg_connection *conn) {
 
  close_connection(conn);
 
  free(conn);
 
}
 

	
 
struct mg_connection *mg_connect(struct mg_context *ctx,
 
                                 const char *host, int port, int use_ssl) {
 
  struct mg_connection *newconn = NULL;
 
  struct sockaddr_in sin;
 
  struct hostent *he;
 
  int sock;
 

	
 
  if (ctx->client_ssl_ctx == NULL && use_ssl) {
 
    cry(fc(ctx), "%s: SSL is not initialized", __func__);
 
  } else if ((he = gethostbyname(host)) == NULL) {
 
    cry(fc(ctx), "%s: gethostbyname(%s): %s", __func__, host, strerror(ERRNO));
 
  } else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
 
    cry(fc(ctx), "%s: socket: %s", __func__, strerror(ERRNO));
 
  } else {
 
    sin.sin_family = AF_INET;
 
    sin.sin_port = htons((uint16_t) port);
 
    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
 
    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
 
      cry(fc(ctx), "%s: connect(%s:%d): %s", __func__, host, port,
 
          strerror(ERRNO));
 
      closesocket(sock);
 
    } else if ((newconn = (struct mg_connection *)
 
                calloc(1, sizeof(*newconn))) == NULL) {
 
      cry(fc(ctx), "%s: calloc: %s", __func__, strerror(ERRNO));
 
      closesocket(sock);
 
    } else {
 
      newconn->ctx = ctx;
 
      newconn->client.sock = sock;
 
      newconn->client.rsa.sin = sin;
 
      newconn->client.is_ssl = use_ssl;
 
      if (use_ssl) {
 
        sslize(newconn, ctx->client_ssl_ctx, SSL_connect);
 
      }
 
    }
 
  }
 

	
 
  return newconn;
 
}
 

	
 
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
 
               char *buf, size_t buf_len, struct mg_request_info *ri) {
 
  struct mg_connection *newconn;
 
  int n, req_length, data_length, port;
 
  char host[1025], proto[10], buf2[MG_BUF_LEN];
 
  FILE *fp = NULL;
 

	
 
  if (sscanf(url, "%9[htps]://%1024[^:]:%d/%n", proto, host, &port, &n) == 3) {
 
  } else if (sscanf(url, "%9[htps]://%1024[^/]/%n", proto, host, &n) == 2) {
 
    port = mg_strcasecmp(proto, "https") == 0 ? 443 : 80;
 
  } else {
 
    cry(fc(ctx), "%s: invalid URL: [%s]", __func__, url);
 
    return NULL;
 
  }
 

	
 
  if ((newconn = mg_connect(ctx, host, port,
 
                            !strcmp(proto, "https"))) == NULL) {
 
    cry(fc(ctx), "%s: mg_connect(%s): %s", __func__, url, strerror(ERRNO));
 
  } else {
 
    mg_printf(newconn, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", url + n, host);
 
    data_length = 0;
 
    req_length = read_request(NULL, newconn, buf, buf_len, &data_length);
 
    if (req_length <= 0) {
 
      cry(fc(ctx), "%s(%s): invalid HTTP reply", __func__, url);
 
    } else if (parse_http_response(buf, req_length, ri) <= 0) {
 
      cry(fc(ctx), "%s(%s): cannot parse HTTP headers", __func__, url);
 
    } else if ((fp = fopen(path, "w+b")) == NULL) {
 
      cry(fc(ctx), "%s: fopen(%s): %s", __func__, path, strerror(ERRNO));
 
    } else {
 
      // Write chunk of data that may be in the user's buffer
 
      data_length -= req_length;
 
      if (data_length > 0 &&
 
        fwrite(buf + req_length, 1, data_length, fp) != (size_t) data_length) {
 
        cry(fc(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
 
        fclose(fp);
 
        fp = NULL;
 
      }
 
      // Read the rest of the response and write it to the file. Do not use
 
      // mg_read() cause we didn't set newconn->content_len properly.
 
      while (fp && (data_length = pull(0, newconn, buf2, sizeof(buf2))) > 0) {
 
        if (fwrite(buf2, 1, data_length, fp) != (size_t) data_length) {
 
          cry(fc(ctx), "%s: fwrite(%s): %s", __func__, path, strerror(ERRNO));
 
          fclose(fp);
 
          fp = NULL;
 
          break;
 
        }
 
      }
 
    }
 
    mg_close_connection(newconn);
 
  }
 

	
 
  return fp;
 
}
 

	
 
static int is_valid_uri(const char *uri) {
 
  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
 
  // URI can be an asterisk (*) or should start with slash.
 
  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
 
}
 

	
 
static void process_new_connection(struct mg_connection *conn) {
 
  struct mg_request_info *ri = &conn->request_info;
 
  int keep_alive_enabled, buffered_len;
 
  const char *cl;
 

	
 
  keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
 

	
 
  do {
 
    reset_per_request_attributes(conn);
 
    conn->request_len = read_request(NULL, conn, conn->buf, conn->buf_size,
 
                                     &conn->data_len);
 
    assert(conn->request_len < 0 || conn->data_len >= conn->request_len);
 
    if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
 
      send_http_error(conn, 413, "Request Too Large", "%s", "");
 
      return;
 
    } if (conn->request_len <= 0) {
 
      return;  // Remote end closed the connection
 
    }
 
    conn->body = conn->next_request = conn->buf + conn->request_len;
 

	
 
    if (parse_http_request(conn->buf, conn->buf_size, ri) <= 0 ||
 
        !is_valid_uri(ri->uri)) {
 
      // Do not put garbage in the access log, just send it back to the client
 
      send_http_error(conn, 400, "Bad Request",
 
          "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);
 
      conn->must_close = 1;
 
    } else if (strcmp(ri->http_version, "1.0") &&
 
               strcmp(ri->http_version, "1.1")) {
 
      // Request seems valid, but HTTP version is strange
 
      send_http_error(conn, 505, "HTTP version not supported", "%s", "");
 
      log_access(conn);
 
    } else {
 
      // Request is valid, handle it
 
      cl = get_header(ri, "Content-Length");
 
      conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
 

	
 
      // Set pointer to the next buffered request
 
      buffered_len = conn->data_len - conn->request_len;
 
      assert(buffered_len >= 0);
 
      if (conn->content_len <= 0) {
 
      } else if (conn->content_len < (int64_t) buffered_len) {
 
        conn->next_request += conn->content_len;
 
      } else {
 
        conn->next_request += buffered_len;
 
      }
 

	
 
      conn->birth_time = time(NULL);
 
      handle_request(conn);
 
      call_user(conn, MG_REQUEST_COMPLETE);
 
      log_access(conn);
 
    }
 
    if (ri->remote_user != NULL) {
 
      free((void *) ri->remote_user);
 
    }
 

	
 
    // Discard all buffered data for this request
 
    assert(conn->next_request >= conn->buf);
 
    assert(conn->data_len >= conn->next_request - conn->buf);
 
    conn->data_len -= conn->next_request - conn->buf;
 
    memmove(conn->buf, conn->next_request, (size_t) conn->data_len);
 
  } while (conn->ctx->stop_flag == 0 &&
 
           keep_alive_enabled &&
 
           should_keep_alive(conn));
 
}
 

	
 
// Worker threads take accepted socket from the queue
 
static int consume_socket(struct mg_context *ctx, struct socket *sp) {
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  DEBUG_TRACE(("going idle"));
 

	
 
  // If the queue is empty, wait. We're idle at this point.
 
  while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
 
    pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
 
  }
 

	
 
  // If we're stopping, sq_head may be equal to sq_tail.
 
  if (ctx->sq_head > ctx->sq_tail) {
 
    // Copy socket from the queue and increment tail
 
    *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
 
    ctx->sq_tail++;
 
    DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));
 

	
 
    // Wrap pointers if needed
 
    while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
 
      ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
 
      ctx->sq_head -= ARRAY_SIZE(ctx->queue);
 
    }
 
  }
 

	
 
  (void) pthread_cond_signal(&ctx->sq_empty);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 

	
 
  return !ctx->stop_flag;
 
}
 

	
 
static void worker_thread(struct mg_context *ctx) {
 
  struct mg_connection *conn;
 
  int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]);
 

	
 
  conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
 
  if (conn == NULL) {
 
    cry(fc(ctx), "%s", "Cannot create new connection struct, OOM");
 
  } else {
 
    conn->buf_size = buf_size;
 
    conn->buf = (char *) (conn + 1);
 

	
 
    // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
 
    // sq_empty condvar to wake up the master waiting in produce_socket()
 
    while (consume_socket(ctx, &conn->client)) {
 
      conn->birth_time = time(NULL);
 
      conn->ctx = ctx;
 

	
 
      // Fill in IP, port info early so even if SSL setup below fails,
 
      // error handler would have the corresponding info.
 
      // Thanks to Johannes Winkelmann for the patch.
 
      // TODO(lsm): Fix IPv6 case
 
      conn->request_info.remote_port = ntohs(conn->client.rsa.sin.sin_port);
 
      memcpy(&conn->request_info.remote_ip,
 
             &conn->client.rsa.sin.sin_addr.s_addr, 4);
 
      conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
 
      conn->request_info.is_ssl = conn->client.is_ssl;
 

	
 
      if (!conn->client.is_ssl ||
 
          (conn->client.is_ssl &&
 
           sslize(conn, conn->ctx->ssl_ctx, SSL_accept))) {
 
        process_new_connection(conn);
 
      }
 

	
 
      close_connection(conn);
 
    }
 
    free(conn);
 
  }
 

	
 
  // Signal master that we're done with connection and exiting
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  ctx->num_threads--;
 
  (void) pthread_cond_signal(&ctx->cond);
 
  assert(ctx->num_threads >= 0);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 

	
 
  DEBUG_TRACE(("exiting"));
 
}
 

	
 
// Master thread adds accepted socket to a queue
 
static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
 
  (void) pthread_mutex_lock(&ctx->mutex);
 

	
 
  // If the queue is full, wait
 
  while (ctx->stop_flag == 0 &&
 
         ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
 
    (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
 
  }
 

	
 
  if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
 
    // Copy socket to the queue and increment head
 
    ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
 
    ctx->sq_head++;
 
    DEBUG_TRACE(("queued socket %d", sp->sock));
 
  }
 

	
 
  (void) pthread_cond_signal(&ctx->sq_full);
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 
}
 

	
 
static void accept_new_connection(const struct socket *listener,
 
                                  struct mg_context *ctx) {
 
  struct socket accepted;
 
  char src_addr[20];
 
  socklen_t len;
 
  int allowed;
 

	
 
  len = sizeof(accepted.rsa);
 
  accepted.lsa = listener->lsa;
 
  accepted.sock = accept(listener->sock, &accepted.rsa.sa, &len);
 
  if (accepted.sock != INVALID_SOCKET) {
 
    allowed = check_acl(ctx, &accepted.rsa);
 
    if (allowed) {
 
      // Put accepted socket structure into the queue
 
      DEBUG_TRACE(("accepted socket %d", accepted.sock));
 
      accepted.is_ssl = listener->is_ssl;
 
      produce_socket(ctx, &accepted);
 
    } else {
 
      sockaddr_to_string(src_addr, sizeof(src_addr), &accepted.rsa);
 
      cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr);
 
      (void) closesocket(accepted.sock);
 
    }
 
  }
 
}
 

	
 
static void master_thread(struct mg_context *ctx) {
 
  fd_set read_set;
 
  struct timeval tv;
 
  struct socket *sp;
 
  int max_fd;
 

	
 
  // Increase priority of the master thread
 
#if defined(_WIN32)
 
  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
 
#endif
 

	
 
#if defined(ISSUE_317)
 
  struct sched_param sched_param;
 
  sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
 
  pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
 
#endif
 

	
 
  while (ctx->stop_flag == 0) {
 
    FD_ZERO(&read_set);
 
    max_fd = -1;
 

	
 
    // Add listening sockets to the read set
 
    for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
 
      add_to_set(sp->sock, &read_set, &max_fd);
 
    }
 

	
 
    tv.tv_sec = 0;
 
    tv.tv_usec = 200 * 1000;
 

	
 
    if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
 
#ifdef _WIN32
 
      // On windows, if read_set and write_set are empty,
 
      // select() returns "Invalid parameter" error
 
      // (at least on my Windows XP Pro). So in this case, we sleep here.
 
      mg_sleep(1000);
 
#endif // _WIN32
 
    } else {
 
      for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
 
        if (ctx->stop_flag == 0 && FD_ISSET(sp->sock, &read_set)) {
 
          accept_new_connection(sp, ctx);
 
        }
 
      }
 
    }
 
  }
 
  DEBUG_TRACE(("stopping workers"));
 

	
 
  // Stop signal received: somebody called mg_stop. Quit.
 
  close_all_listening_sockets(ctx);
 

	
 
  // Wakeup workers that are waiting for connections to handle.
 
  pthread_cond_broadcast(&ctx->sq_full);
 

	
 
  // Wait until all threads finish
 
  (void) pthread_mutex_lock(&ctx->mutex);
 
  while (ctx->num_threads > 0) {
 
    (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
 
  }
 
  (void) pthread_mutex_unlock(&ctx->mutex);
 

	
 
  // All threads exited, no sync is needed. Destroy mutex and condvars
 
  (void) pthread_mutex_destroy(&ctx->mutex);
 
  (void) pthread_cond_destroy(&ctx->cond);
 
  (void) pthread_cond_destroy(&ctx->sq_empty);
 
  (void) pthread_cond_destroy(&ctx->sq_full);
 

	
 
#if !defined(NO_SSL)
 
  uninitialize_ssl(ctx);
 
#endif
 
  DEBUG_TRACE(("exiting"));
 

	
 
  // Signal mg_stop() that we're done.
 
  // WARNING: This must be the very last thing this
 
  // thread does, as ctx becomes invalid after this line.
 
  ctx->stop_flag = 2;
 
}
 

	
 
static void free_context(struct mg_context *ctx) {
 
  int i;
 

	
 
  // Deallocate config parameters
 
  for (i = 0; i < NUM_OPTIONS; i++) {
 
    if (ctx->config[i] != NULL)
 
      free(ctx->config[i]);
 
  }
 

	
 
  // Deallocate SSL context
 
  if (ctx->ssl_ctx != NULL) {
 
    SSL_CTX_free(ctx->ssl_ctx);
 
  }
 
  if (ctx->client_ssl_ctx != NULL) {
 
    SSL_CTX_free(ctx->client_ssl_ctx);
 
  }
 
#ifndef NO_SSL
 
  if (ssl_mutexes != NULL) {
 
    free(ssl_mutexes);
 
  }
 
#endif // !NO_SSL
 

	
 
  // Deallocate context itself
 
  free(ctx);
 
}
 

	
 
void mg_stop(struct mg_context *ctx) {
 
  ctx->stop_flag = 1;
 

	
 
  // Wait until mg_fini() stops
 
  while (ctx->stop_flag != 2) {
 
    (void) mg_sleep(10);
 
  }
 
  free_context(ctx);
 

	
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  (void) WSACleanup();
 
#endif // _WIN32
 
}
 

	
 
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
 
                            const char **options) {
 
  struct mg_context *ctx;
 
  const char *name, *value, *default_value;
 
  int i;
 

	
 
#if defined(_WIN32) && !defined(__SYMBIAN32__)
 
  WSADATA data;
 
  WSAStartup(MAKEWORD(2,2), &data);
 
  InitializeCriticalSection(&global_log_file_lock);
 
#endif // _WIN32
 

	
 
  // Allocate context and initialize reasonable general case defaults.
 
  // TODO(lsm): do proper error handling here.
 
  if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
 
    return NULL;
 
  }
 
  ctx->user_callback = user_callback;
 
  ctx->user_data = user_data;
 

	
 
  while (options && (name = *options++) != NULL) {
 
    if ((i = get_option_index(name)) == -1) {
 
      cry(fc(ctx), "Invalid option: %s", name);
 
      free_context(ctx);
 
      return NULL;
 
    } else if ((value = *options++) == NULL) {
 
      cry(fc(ctx), "%s: option value cannot be NULL", name);
 
      free_context(ctx);
 
      return NULL;
 
    }
 
    if (ctx->config[i] != NULL) {
 
      cry(fc(ctx), "warning: %s: duplicate option", name);
 
    }
 
    ctx->config[i] = mg_strdup(value);
 
    DEBUG_TRACE(("[%s] -> [%s]", name, value));
 
  }
 

	
 
  // Set default value if needed
 
  for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
 
    default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
 
    if (ctx->config[i] == NULL && default_value != NULL) {
 
      ctx->config[i] = mg_strdup(default_value);
 
      DEBUG_TRACE(("Setting default: [%s] -> [%s]",
 
                   config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
 
                   default_value));
 
    }
 
  }
 

	
 
  // NOTE(lsm): order is important here. SSL certificates must
 
  // be initialized before listening ports. UID must be set last.
 
  if (!set_gpass_option(ctx) ||
 
#if !defined(NO_SSL)
 
      !set_ssl_option(ctx) ||
 
#endif
 
      !set_ports_option(ctx) ||
 
#if !defined(_WIN32)
 
      !set_uid_option(ctx) ||
 
#endif
 
      !set_acl_option(ctx)) {
 
    free_context(ctx);
 
    return NULL;
 
  }
 

	
 
#if !defined(_WIN32) && !defined(__SYMBIAN32__)
 
  // Ignore SIGPIPE signal, so if browser cancels the request, it
 
  // won't kill the whole process.
 
  (void) signal(SIGPIPE, SIG_IGN);
 
  // Also ignoring SIGCHLD to let the OS to reap zombies properly.
 
  (void) signal(SIGCHLD, SIG_IGN);
 
#endif // !_WIN32
 

	
 
  (void) pthread_mutex_init(&ctx->mutex, NULL);
 
  (void) pthread_cond_init(&ctx->cond, NULL);
 
  (void) pthread_cond_init(&ctx->sq_empty, NULL);
 
  (void) pthread_cond_init(&ctx->sq_full, NULL);
 

	
 
  // Start master (listening) thread
 
  mg_start_thread((mg_thread_func_t) master_thread, ctx);
 

	
 
  // Start worker threads
 
  for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
 
    if (mg_start_thread((mg_thread_func_t) worker_thread, ctx) != 0) {
 
      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
 
    } else {
 
      ctx->num_threads++;
 
    }
 
  }
 

	
 
  return ctx;
 
}
spectrum_manager/src/mongoose.h
Show inline comments
 
// Copyright (c) 2004-2012 Sergey Lyubka
 
//
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
// of this software and associated documentation files (the "Software"), to deal
 
// in the Software without restriction, including without limitation the rights
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
// copies of the Software, and to permit persons to whom the Software is
 
// furnished to do so, subject to the following conditions:
 
//
 
// The above copyright notice and this permission notice shall be included in
 
// all copies or substantial portions of the Software.
 
//
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
// THE SOFTWARE.
 
 
#ifndef MONGOOSE_HEADER_INCLUDED
 
#define  MONGOOSE_HEADER_INCLUDED
 
 
#include <stdio.h>
 
#include <stddef.h>
 
 
#ifdef __cplusplus
 
extern "C" {
 
#endif // __cplusplus
 
 
struct mg_context;     // Handle for the HTTP service itself
 
struct mg_connection;  // Handle for the individual connection
 
 
 
// This structure contains information about the HTTP request.
 
struct mg_request_info {
 
  void *user_data;       // User-defined pointer passed to mg_start()
 
  char *request_method;  // "GET", "POST", etc
 
  char *uri;             // URL-decoded URI
 
  char *http_version;    // E.g. "1.0", "1.1"
 
  char *query_string;    // URL part after '?' (not including '?') or NULL
 
  char *remote_user;     // Authenticated user, or NULL if no auth used
 
  char *log_message;     // Mongoose error log message, MG_EVENT_LOG only
 
  long remote_ip;        // Client's IP address
 
  int remote_port;       // Client's port
 
  int status_code;       // HTTP reply status code, e.g. 200
 
  int is_ssl;            // 1 if SSL-ed, 0 if not
 
  int num_headers;       // Number of headers
 
  struct mg_header {
 
    char *name;          // HTTP header name
 
    char *value;         // HTTP header value
 
  } http_headers[64];    // Maximum 64 headers
 
};
 
 
// Various events on which user-defined function is called by Mongoose.
 
enum mg_event {
 
  MG_NEW_REQUEST,       // New HTTP request has arrived from the client
 
  MG_REQUEST_COMPLETE,  // Mongoose has finished handling the request
 
  MG_HTTP_ERROR,        // HTTP error must be returned to the client
 
  MG_EVENT_LOG,         // Mongoose logs an event, request_info.log_message
 
  MG_INIT_SSL           // Mongoose initializes SSL. Instead of mg_connection *,
 
                        // SSL context is passed to the callback function.
 
};
 
 
// Prototype for the user-defined function. Mongoose calls this function
 
// on every MG_* event.
 
//
 
// Parameters:
 
//   event: which event has been triggered.
 
//   conn: opaque connection handler. Could be used to read, write data to the
 
//         client, etc. See functions below that have "mg_connection *" arg.
 
//
 
// Return:
 
//   If handler returns non-NULL, that means that handler has processed the
 
//   request by sending appropriate HTTP reply to the client. Mongoose treats
 
//   the request as served.
 
//   If handler returns NULL, that means that handler has not processed
 
//   the request. Handler must not send any data to the client in this case.
 
//   Mongoose proceeds with request handling as if nothing happened.
 
typedef void * (*mg_callback_t)(enum mg_event event,
 
                                struct mg_connection *conn);
 
 
 
// Start web server.
 
//
 
// Parameters:
 
//   callback: user defined event handling function or NULL.
 
//   options: NULL terminated list of option_name, option_value pairs that
 
//            specify Mongoose configuration parameters.
 
//
 
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
 
//    processing is required for these, signal handlers must be set up
 
//    after calling mg_start().
 
//
 
//
 
// Example:
 
//   const char *options[] = {
 
//     "document_root", "/var/www",
 
//     "listening_ports", "80,443s",
 
//     NULL
 
//   };
 
//   struct mg_context *ctx = mg_start(&my_func, NULL, options);
 
//
 
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
 
// for the list of valid option and their possible values.
 
//
 
// Return:
 
//   web server context, or NULL on error.
 
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
 
                            const char **options);
 
 
 
// Stop the web server.
 
//
 
// Must be called last, when an application wants to stop the web server and
 
// release all associated resources. This function blocks until all Mongoose
 
// threads are stopped. Context pointer becomes invalid.
 
void mg_stop(struct mg_context *);
 
 
 
// Get the value of particular configuration parameter.
 
// The value returned is read-only. Mongoose does not allow changing
 
// configuration at run time.
 
// If given parameter name is not valid, NULL is returned. For valid
 
// names, return value is guaranteed to be non-NULL. If parameter is not
 
// set, zero-length string is returned.
 
const char *mg_get_option(const struct mg_context *ctx, const char *name);
 
 
 
// Return array of strings that represent valid configuration options.
 
// For each option, a short name, long name, and default value is returned.
 
// Array is NULL terminated.
 
const char **mg_get_valid_option_names(void);
 
 
 
// Add, edit or delete the entry in the passwords file.
 
//
 
// This function allows an application to manipulate .htpasswd files on the
 
// fly by adding, deleting and changing user records. This is one of the
 
// several ways of implementing authentication on the server side. For another,
 
// cookie-based way please refer to the examples/chat.c in the source tree.
 
//
 
// If password is not NULL, entry is added (or modified if already exists).
 
// If password is NULL, entry is deleted.
 
//
 
// Return:
 
//   1 on success, 0 on error.
 
int mg_modify_passwords_file(const char *passwords_file_name,
 
                             const char *domain,
 
                             const char *user,
 
                             const char *password);
 
 
 
// Return mg_request_info structure associated with the request.
 
// Always succeeds.
 
const struct mg_request_info *mg_get_request_info(const struct mg_connection *);
 
 
 
// Send data to the client.
 
// Return:
 
//  0   when the connection has been closed
 
//  -1  on error
 
//  number of bytes written on success
 
int mg_write(struct mg_connection *, const void *buf, size_t len);
 
 
 
// Send data to the browser using printf() semantics.
 
//
 
// Works exactly like mg_write(), but allows to do message formatting.
 
// Below are the macros for enabling compiler-specific checks for
 
// printf-like arguments.
 
 
#undef PRINTF_FORMAT_STRING
 
#if _MSC_VER >= 1400
 
#include <sal.h>
 
#if _MSC_VER > 1400
 
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
 
#else
 
#define PRINTF_FORMAT_STRING(s) __format_string s
 
#endif
 
#else
 
#define PRINTF_FORMAT_STRING(s) s
 
#endif
 
 
#ifdef __GNUC__
 
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
 
#else
 
#define PRINTF_ARGS(x, y)
 
#endif
 
 
int mg_printf(struct mg_connection *,
 
              PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
 
 
 
// Send contents of the entire file together with HTTP headers.
 
void mg_send_file(struct mg_connection *conn, const char *path);
 
 
 
// Read data from the remote end, return number of bytes read.
 
int mg_read(struct mg_connection *, void *buf, size_t len);
 
 
 
// Get the value of particular HTTP header.
 
//
 
// This is a helper function. It traverses request_info->http_headers array,
 
// and if the header is present in the array, returns its value. If it is
 
// not present, NULL is returned.
 
const char *mg_get_header(const struct mg_connection *, const char *name);
 
 
 
// Get a value of particular form variable.
 
//
 
// Parameters:
 
//   data: pointer to form-uri-encoded buffer. This could be either POST data,
 
//         or request_info.query_string.
 
//   data_len: length of the encoded data.
 
//   var_name: variable name to decode from the buffer
 
//   buf: destination buffer for the decoded variable
 
//   buf_len: length of the destination buffer
 
//
 
// Return:
 
//   On success, length of the decoded variable.
 
//   On error:
 
//      -1 (variable not found, or destination buffer is too small).
 
//      -2 (destination buffer is NULL or zero length).
 
//
 
// Destination buffer is guaranteed to be '\0' - terminated if it is not
 
// NULL or zero length. In case of failure, dst[0] == '\0'.
 
int mg_get_var(const char *data, size_t data_len,
 
               const char *var_name, char *buf, size_t buf_len);
 
 
// Fetch value of certain cookie variable into the destination buffer.
 
//
 
// Destination buffer is guaranteed to be '\0' - terminated. In case of
 
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
 
// parameter. This function returns only first occurrence.
 
//
 
// Return:
 
//   On success, value length.
 
//   On error, -1 (either "Cookie:" header is not present at all, or the
 
//   requested parameter is not found, or destination buffer is too small
 
//   to hold the value).
 
int mg_get_cookie(const struct mg_connection *,
 
                  const char *cookie_name, char *buf, size_t buf_len);
 
 
 
// Connect to the remote web server.
 
// Return:
 
//   On success, valid pointer to the new connection
 
//   On error, NULL
 
struct mg_connection *mg_connect(struct mg_context *ctx,
 
                                 const char *host, int port, int use_ssl);
 
 
 
// Close the connection opened by mg_connect().
 
void mg_close_connection(struct mg_connection *conn);
 
 
 
// Download given URL to a given file.
 
//   url: URL to download
 
//   path: file name where to save the data
 
//   request_info: pointer to a structure that will hold parsed reply headers
 
//   buf, bul_len: a buffer for the reply headers
 
// Return:
 
//   On error, NULL
 
//   On success, opened file stream to the downloaded contents. The stream
 
//   is positioned to the end of the file. It is the user's responsibility
 
//   to fclose() the opened file stream.
 
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
 
               char *buf, size_t buf_len, struct mg_request_info *request_info);
 
 
 
// Convenience function -- create detached thread.
 
// Return: 0 on success, non-0 on error.
 
typedef void * (*mg_thread_func_t)(void *);
 
int mg_start_thread(mg_thread_func_t f, void *p);
 
 
 
// Return builtin mime type for the given file name.
 
// For unrecognized extensions, "text/plain" is returned.
 
const char *mg_get_builtin_mime_type(const char *file_name);
 
 
 
// Return Mongoose version.
 
const char *mg_version(void);
 
 
 
// MD5 hash given strings.
 
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
 
// ASCIIz strings. When function returns, buf will contain human-readable
 
// MD5 hash. Example:
 
//   char buf[33];
 
//   mg_md5(buf, "aa", "bb", NULL);
 
void mg_md5(char buf[33], ...);
 
 
 
#ifdef __cplusplus
 
}
 
#endif // __cplusplus
 
 
#endif // MONGOOSE_HEADER_INCLUDED
 
// Copyright (c) 2004-2012 Sergey Lyubka
 
//
 
// Permission is hereby granted, free of charge, to any person obtaining a copy
 
// of this software and associated documentation files (the "Software"), to deal
 
// in the Software without restriction, including without limitation the rights
 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
// copies of the Software, and to permit persons to whom the Software is
 
// furnished to do so, subject to the following conditions:
 
//
 
// The above copyright notice and this permission notice shall be included in
 
// all copies or substantial portions of the Software.
 
//
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
// THE SOFTWARE.
 

	
 
#ifndef MONGOOSE_HEADER_INCLUDED
 
#define  MONGOOSE_HEADER_INCLUDED
 

	
 
#include <stdio.h>
 
#include <stddef.h>
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif // __cplusplus
 

	
 
struct mg_context;     // Handle for the HTTP service itself
 
struct mg_connection;  // Handle for the individual connection
 

	
 

	
 
// This structure contains information about the HTTP request.
 
struct mg_request_info {
 
  void *user_data;       // User-defined pointer passed to mg_start()
 
  char *request_method;  // "GET", "POST", etc
 
  char *uri;             // URL-decoded URI
 
  char *http_version;    // E.g. "1.0", "1.1"
 
  char *query_string;    // URL part after '?' (not including '?') or NULL
 
  char *remote_user;     // Authenticated user, or NULL if no auth used
 
  char *log_message;     // Mongoose error log message, MG_EVENT_LOG only
 
  long remote_ip;        // Client's IP address
 
  int remote_port;       // Client's port
 
  int status_code;       // HTTP reply status code, e.g. 200
 
  int is_ssl;            // 1 if SSL-ed, 0 if not
 
  int num_headers;       // Number of headers
 
  struct mg_header {
 
    char *name;          // HTTP header name
 
    char *value;         // HTTP header value
 
  } http_headers[64];    // Maximum 64 headers
 
};
 

	
 
// Various events on which user-defined function is called by Mongoose.
 
enum mg_event {
 
  MG_NEW_REQUEST,       // New HTTP request has arrived from the client
 
  MG_REQUEST_COMPLETE,  // Mongoose has finished handling the request
 
  MG_HTTP_ERROR,        // HTTP error must be returned to the client
 
  MG_EVENT_LOG,         // Mongoose logs an event, request_info.log_message
 
  MG_INIT_SSL           // Mongoose initializes SSL. Instead of mg_connection *,
 
                        // SSL context is passed to the callback function.
 
};
 

	
 
// Prototype for the user-defined function. Mongoose calls this function
 
// on every MG_* event.
 
//
 
// Parameters:
 
//   event: which event has been triggered.
 
//   conn: opaque connection handler. Could be used to read, write data to the
 
//         client, etc. See functions below that have "mg_connection *" arg.
 
//
 
// Return:
 
//   If handler returns non-NULL, that means that handler has processed the
 
//   request by sending appropriate HTTP reply to the client. Mongoose treats
 
//   the request as served.
 
//   If handler returns NULL, that means that handler has not processed
 
//   the request. Handler must not send any data to the client in this case.
 
//   Mongoose proceeds with request handling as if nothing happened.
 
typedef void * (*mg_callback_t)(enum mg_event event,
 
                                struct mg_connection *conn);
 

	
 

	
 
// Start web server.
 
//
 
// Parameters:
 
//   callback: user defined event handling function or NULL.
 
//   options: NULL terminated list of option_name, option_value pairs that
 
//            specify Mongoose configuration parameters.
 
//
 
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
 
//    processing is required for these, signal handlers must be set up
 
//    after calling mg_start().
 
//
 
//
 
// Example:
 
//   const char *options[] = {
 
//     "document_root", "/var/www",
 
//     "listening_ports", "80,443s",
 
//     NULL
 
//   };
 
//   struct mg_context *ctx = mg_start(&my_func, NULL, options);
 
//
 
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
 
// for the list of valid option and their possible values.
 
//
 
// Return:
 
//   web server context, or NULL on error.
 
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
 
                            const char **options);
 

	
 

	
 
// Stop the web server.
 
//
 
// Must be called last, when an application wants to stop the web server and
 
// release all associated resources. This function blocks until all Mongoose
 
// threads are stopped. Context pointer becomes invalid.
 
void mg_stop(struct mg_context *);
 

	
 

	
 
// Get the value of particular configuration parameter.
 
// The value returned is read-only. Mongoose does not allow changing
 
// configuration at run time.
 
// If given parameter name is not valid, NULL is returned. For valid
 
// names, return value is guaranteed to be non-NULL. If parameter is not
 
// set, zero-length string is returned.
 
const char *mg_get_option(const struct mg_context *ctx, const char *name);
 

	
 

	
 
// Return array of strings that represent valid configuration options.
 
// For each option, a short name, long name, and default value is returned.
 
// Array is NULL terminated.
 
const char **mg_get_valid_option_names(void);
 

	
 

	
 
// Add, edit or delete the entry in the passwords file.
 
//
 
// This function allows an application to manipulate .htpasswd files on the
 
// fly by adding, deleting and changing user records. This is one of the
 
// several ways of implementing authentication on the server side. For another,
 
// cookie-based way please refer to the examples/chat.c in the source tree.
 
//
 
// If password is not NULL, entry is added (or modified if already exists).
 
// If password is NULL, entry is deleted.
 
//
 
// Return:
 
//   1 on success, 0 on error.
 
int mg_modify_passwords_file(const char *passwords_file_name,
 
                             const char *domain,
 
                             const char *user,
 
                             const char *password);
 

	
 

	
 
// Return mg_request_info structure associated with the request.
 
// Always succeeds.
 
const struct mg_request_info *mg_get_request_info(const struct mg_connection *);
 

	
 

	
 
// Send data to the client.
 
// Return:
 
//  0   when the connection has been closed
 
//  -1  on error
 
//  number of bytes written on success
 
int mg_write(struct mg_connection *, const void *buf, size_t len);
 

	
 

	
 
// Send data to the browser using printf() semantics.
 
//
 
// Works exactly like mg_write(), but allows to do message formatting.
 
// Below are the macros for enabling compiler-specific checks for
 
// printf-like arguments.
 

	
 
#undef PRINTF_FORMAT_STRING
 
#if _MSC_VER >= 1400
 
#include <sal.h>
 
#if _MSC_VER > 1400
 
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
 
#else
 
#define PRINTF_FORMAT_STRING(s) __format_string s
 
#endif
 
#else
 
#define PRINTF_FORMAT_STRING(s) s
 
#endif
 

	
 
#ifdef __GNUC__
 
#define PRINTF_ARGS(x, y) __attribute__((format(printf, x, y)))
 
#else
 
#define PRINTF_ARGS(x, y)
 
#endif
 

	
 
int mg_printf(struct mg_connection *,
 
              PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
 

	
 

	
 
// Send contents of the entire file together with HTTP headers.
 
void mg_send_file(struct mg_connection *conn, const char *path);
 

	
 

	
 
// Read data from the remote end, return number of bytes read.
 
int mg_read(struct mg_connection *, void *buf, size_t len);
 

	
 

	
 
// Get the value of particular HTTP header.
 
//
 
// This is a helper function. It traverses request_info->http_headers array,
 
// and if the header is present in the array, returns its value. If it is
 
// not present, NULL is returned.
 
const char *mg_get_header(const struct mg_connection *, const char *name);
 

	
 

	
 
// Get a value of particular form variable.
 
//
 
// Parameters:
 
//   data: pointer to form-uri-encoded buffer. This could be either POST data,
 
//         or request_info.query_string.
 
//   data_len: length of the encoded data.
 
//   var_name: variable name to decode from the buffer
 
//   buf: destination buffer for the decoded variable
 
//   buf_len: length of the destination buffer
 
//
 
// Return:
 
//   On success, length of the decoded variable.
 
//   On error:
 
//      -1 (variable not found, or destination buffer is too small).
 
//      -2 (destination buffer is NULL or zero length).
 
//
 
// Destination buffer is guaranteed to be '\0' - terminated if it is not
 
// NULL or zero length. In case of failure, dst[0] == '\0'.
 
int mg_get_var(const char *data, size_t data_len,
 
               const char *var_name, char *buf, size_t buf_len);
 

	
 
// Fetch value of certain cookie variable into the destination buffer.
 
//
 
// Destination buffer is guaranteed to be '\0' - terminated. In case of
 
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
 
// parameter. This function returns only first occurrence.
 
//
 
// Return:
 
//   On success, value length.
 
//   On error, -1 (either "Cookie:" header is not present at all, or the
 
//   requested parameter is not found, or destination buffer is too small
 
//   to hold the value).
 
int mg_get_cookie(const struct mg_connection *,
 
                  const char *cookie_name, char *buf, size_t buf_len);
 

	
 

	
 
// Connect to the remote web server.
 
// Return:
 
//   On success, valid pointer to the new connection
 
//   On error, NULL
 
struct mg_connection *mg_connect(struct mg_context *ctx,
 
                                 const char *host, int port, int use_ssl);
 

	
 

	
 
// Close the connection opened by mg_connect().
 
void mg_close_connection(struct mg_connection *conn);
 

	
 

	
 
// Download given URL to a given file.
 
//   url: URL to download
 
//   path: file name where to save the data
 
//   request_info: pointer to a structure that will hold parsed reply headers
 
//   buf, bul_len: a buffer for the reply headers
 
// Return:
 
//   On error, NULL
 
//   On success, opened file stream to the downloaded contents. The stream
 
//   is positioned to the end of the file. It is the user's responsibility
 
//   to fclose() the opened file stream.
 
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
 
               char *buf, size_t buf_len, struct mg_request_info *request_info);
 

	
 

	
 
// Convenience function -- create detached thread.
 
// Return: 0 on success, non-0 on error.
 
typedef void * (*mg_thread_func_t)(void *);
 
int mg_start_thread(mg_thread_func_t f, void *p);
 

	
 

	
 
// Return builtin mime type for the given file name.
 
// For unrecognized extensions, "text/plain" is returned.
 
const char *mg_get_builtin_mime_type(const char *file_name);
 

	
 

	
 
// Return Mongoose version.
 
const char *mg_version(void);
 

	
 

	
 
// MD5 hash given strings.
 
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
 
// ASCIIz strings. When function returns, buf will contain human-readable
 
// MD5 hash. Example:
 
//   char buf[33];
 
//   mg_md5(buf, "aa", "bb", NULL);
 
void mg_md5(char buf[33], ...);
 

	
 

	
 
#ifdef __cplusplus
 
}
 
#endif // __cplusplus
 

	
 
#endif // MONGOOSE_HEADER_INCLUDED
src/discoinforesponder.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "discoinforesponder.h"
 
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include <boost/algorithm/string.hpp>
 
#include "Swiften/Disco/DiscoInfoResponder.h"
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "transport/config.h"
 
#include "transport/logging.h"
 
#include "Swiften/Disco/CapsInfoGenerator.h"
 
 
using namespace Swift;
 
using namespace boost;
 
 
DEFINE_LOGGER(logger, "DiscoInfoResponder");
 
 
namespace Transport {
 
 
DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder<DiscoInfo>(router) {
 
	m_config = config;
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this));
 
	m_buddyInfo = NULL;
 
	m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"),
 
													CONFIG_STRING(m_config, "identity.category"),
 
													CONFIG_STRING(m_config, "identity.type")));
 
#if HAVE_SWIFTEN_3
 
	crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create());
 
#endif
 
 
	updateFeatures();
 
}
 
 
DiscoInfoResponder::~DiscoInfoResponder() {
 
	delete m_buddyInfo;
 
}
 
 
void DiscoInfoResponder::updateFeatures() {
 
	std::list<std::string> features2;
 
	features2.push_back("jabber:iq:register");
 
	features2.push_back("jabber:iq:gateway");
 
	features2.push_back("jabber:iq:private");
 
	features2.push_back("http://jabber.org/protocol/disco#info");
 
	features2.push_back("http://jabber.org/protocol/commands");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) {
 
		features2.push_back("http://jabber.org/protocol/muc");
 
	}
 
	setTransportFeatures(features2);
 
 
	std::list<std::string> features;
 
	features.push_back("http://jabber.org/protocol/disco#items");
 
	features.push_back("http://jabber.org/protocol/disco#info");
 
	features.push_back("http://jabber.org/protocol/chatstates");
 
	features.push_back("http://jabber.org/protocol/xhtml-im");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.receipts", false)) {
 
		features.push_back("urn:xmpp:receipts");
 
	}
 
	setBuddyFeatures(features);
 
}
 
 
void DiscoInfoResponder::setTransportFeatures(std::list<std::string> &features) {
 
	for (std::list<std::string>::iterator it = features.begin(); it != features.end(); it++) {
 
		if (!m_transportInfo.hasFeature(*it)) {
 
			m_transportInfo.addFeature(*it);
 
		}
 
	}
 
}
 
 
void DiscoInfoResponder::setBuddyFeatures(std::list<std::string> &f) {
 
	delete m_buddyInfo;
 
	m_buddyInfo = new Swift::DiscoInfo;
 
	m_buddyInfo->addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), "client", "pc"));
 
 
	for (std::list<std::string>::iterator it = f.begin(); it != f.end(); it++) {
 
		if (!m_buddyInfo->hasFeature(*it)) {
 
			m_buddyInfo->addFeature(*it);
 
		}
 
	}
 
#if HAVE_SWIFTEN_3
 
	CapsInfoGenerator caps("spectrum", crypto.get());
 
#else
 
	CapsInfoGenerator caps("spectrum");
 
#endif
 
	m_capsInfo = caps.generateCapsInfo(*m_buddyInfo);
 
	onBuddyCapsInfoChanged(m_capsInfo);
 
}
 
 
void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) {
 
	std::string j = jid;
 
	boost::algorithm::to_lower(j);
 
	m_rooms[j] = name;
 
}
 
 
void DiscoInfoResponder::clearRooms() {
 
	m_rooms.clear();
 
}
 
 
void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) {
 
	m_commands[node] = node;
 
}
 
 
bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	// disco#info for transport
 
	if (to.getNode().empty()) {
 
		// Adhoc command
 
		if (m_commands.find(info->getNode()) != m_commands.end()) {
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
			res->addFeature("http://jabber.org/protocol/commands");
 
			res->addFeature("jabber:x:data");
 
			res->addIdentity(DiscoInfo::Identity(m_commands[info->getNode()], "automation", "command-node"));
 
			res->setNode(info->getNode());
 
			sendResponse(from, to, id, res);
 
		}
 
		else if (info->getNode() == "http://jabber.org/protocol/commands") {
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
			res->addIdentity(DiscoInfo::Identity("Commands", "automation", "command-list"));
 
			res->setNode(info->getNode());
 
			sendResponse(from, to, id, res);
 
		}
 
		else {
 
			if (!info->getNode().empty()) {
 
				sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
 
				return true;
 
			}
 
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo(m_transportInfo));
 
			res->setNode(info->getNode());
 
			sendResponse(from, id, res);
 
		}
 
	}
 
	// disco#info for room
 
	else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) {
 
		boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
		res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text"));
 
		res->setNode(info->getNode());
 
		sendResponse(from, to, id, res);
 
	}
 
	// disco#info for buddy
 
	else {
 
		boost::shared_ptr<DiscoInfo> res(new DiscoInfo(*m_buddyInfo));
 
		res->setNode(info->getNode());
 
		sendResponse(from, to, id, res);
 
	}
 
	return true;
 
}
 
 
}
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "discoinforesponder.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
#include <boost/algorithm/string.hpp>
 
#include "Swiften/Disco/DiscoInfoResponder.h"
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "transport/config.h"
 
#include "transport/logging.h"
 
#include "Swiften/Disco/CapsInfoGenerator.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
DEFINE_LOGGER(logger, "DiscoInfoResponder");
 

	
 
namespace Transport {
 

	
 
DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder<DiscoInfo>(router) {
 
	m_config = config;
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this));
 
	m_buddyInfo = NULL;
 
	m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"),
 
													CONFIG_STRING(m_config, "identity.category"),
 
													CONFIG_STRING(m_config, "identity.type")));
 
#if HAVE_SWIFTEN_3
 
	crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create());
 
#endif
 

	
 
	updateFeatures();
 
}
 

	
 
DiscoInfoResponder::~DiscoInfoResponder() {
 
	delete m_buddyInfo;
 
}
 

	
 
void DiscoInfoResponder::updateFeatures() {
 
	std::list<std::string> features2;
 
	features2.push_back("jabber:iq:register");
 
	features2.push_back("jabber:iq:gateway");
 
	features2.push_back("jabber:iq:private");
 
	features2.push_back("http://jabber.org/protocol/disco#info");
 
	features2.push_back("http://jabber.org/protocol/commands");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) {
 
		features2.push_back("http://jabber.org/protocol/muc");
 
	}
 
	setTransportFeatures(features2);
 

	
 
	std::list<std::string> features;
 
	features.push_back("http://jabber.org/protocol/disco#items");
 
	features.push_back("http://jabber.org/protocol/disco#info");
 
	features.push_back("http://jabber.org/protocol/chatstates");
 
	features.push_back("http://jabber.org/protocol/xhtml-im");
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.receipts", false)) {
 
		features.push_back("urn:xmpp:receipts");
 
	}
 
	setBuddyFeatures(features);
 
}
 

	
 
void DiscoInfoResponder::setTransportFeatures(std::list<std::string> &features) {
 
	for (std::list<std::string>::iterator it = features.begin(); it != features.end(); it++) {
 
		if (!m_transportInfo.hasFeature(*it)) {
 
			m_transportInfo.addFeature(*it);
 
		}
 
	}
 
}
 

	
 
void DiscoInfoResponder::setBuddyFeatures(std::list<std::string> &f) {
 
	delete m_buddyInfo;
 
	m_buddyInfo = new Swift::DiscoInfo;
 
	m_buddyInfo->addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"), "client", "pc"));
 

	
 
	for (std::list<std::string>::iterator it = f.begin(); it != f.end(); it++) {
 
		if (!m_buddyInfo->hasFeature(*it)) {
 
			m_buddyInfo->addFeature(*it);
 
		}
 
	}
 
#if HAVE_SWIFTEN_3
 
	CapsInfoGenerator caps("spectrum", crypto.get());
 
#else
 
	CapsInfoGenerator caps("spectrum");
 
#endif
 
	m_capsInfo = caps.generateCapsInfo(*m_buddyInfo);
 
	onBuddyCapsInfoChanged(m_capsInfo);
 
}
 

	
 
void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) {
 
	std::string j = jid;
 
	boost::algorithm::to_lower(j);
 
	m_rooms[j] = name;
 
}
 

	
 
void DiscoInfoResponder::clearRooms() {
 
	m_rooms.clear();
 
}
 

	
 
void DiscoInfoResponder::addAdHocCommand(const std::string &node, const std::string &name) {
 
	m_commands[node] = node;
 
}
 

	
 
bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	// disco#info for transport
 
	if (to.getNode().empty()) {
 
		// Adhoc command
 
		if (m_commands.find(info->getNode()) != m_commands.end()) {
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
			res->addFeature("http://jabber.org/protocol/commands");
 
			res->addFeature("jabber:x:data");
 
			res->addIdentity(DiscoInfo::Identity(m_commands[info->getNode()], "automation", "command-node"));
 
			res->setNode(info->getNode());
 
			sendResponse(from, to, id, res);
 
		}
 
		else if (info->getNode() == "http://jabber.org/protocol/commands") {
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
			res->addIdentity(DiscoInfo::Identity("Commands", "automation", "command-list"));
 
			res->setNode(info->getNode());
 
			sendResponse(from, to, id, res);
 
		}
 
		else {
 
			if (!info->getNode().empty()) {
 
				sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
 
				return true;
 
			}
 

	
 
			boost::shared_ptr<DiscoInfo> res(new DiscoInfo(m_transportInfo));
 
			res->setNode(info->getNode());
 
			sendResponse(from, id, res);
 
		}
 
	}
 
	// disco#info for room
 
	else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) {
 
		boost::shared_ptr<DiscoInfo> res(new DiscoInfo());
 
		res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text"));
 
		res->setNode(info->getNode());
 
		sendResponse(from, to, id, res);
 
	}
 
	// disco#info for buddy
 
	else {
 
		boost::shared_ptr<DiscoInfo> res(new DiscoInfo(*m_buddyInfo));
 
		res->setNode(info->getNode());
 
		sendResponse(from, to, id, res);
 
	}
 
	return true;
 
}
 

	
 
}
src/discoinforesponder.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#pragma once
 
 
#include <vector>
 
#include <list>
 
#include <boost/signal.hpp>
 
#include "Swiften/Queries/GetResponder.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "Swiften/Elements/CapsInfo.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Crypto/CryptoProvider.h>
 
#include <Swiften/Crypto/PlatformCryptoProvider.h>
 
#endif
 
 
 
namespace Transport {
 
 
class Config;
 
 
class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
 
	public:
 
		DiscoInfoResponder(Swift::IQRouter *router, Config *config);
 
		~DiscoInfoResponder();
 
 
		void setTransportFeatures(std::list<std::string> &features);
 
		void setBuddyFeatures(std::list<std::string> &features);
 
 
		void addRoom(const std::string &jid, const std::string &name);
 
		void clearRooms();
 
 
		void addAdHocCommand(const std::string &node, const std::string &name);
 
 
		boost::signal<void (const Swift::CapsInfo &capsInfo)> onBuddyCapsInfoChanged;
 
 
		Swift::CapsInfo &getBuddyCapsInfo() {
 
				return m_capsInfo;
 
		}
 
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> payload);
 
		void updateFeatures();
 
 
		Swift::DiscoInfo m_transportInfo;
 
		Swift::DiscoInfo *m_buddyInfo;
 
		Config *m_config;
 
		Swift::CapsInfo m_capsInfo;
 
		std::map<std::string, std::string> m_rooms;
 
		std::map<std::string, std::string> m_commands;
 
#if HAVE_SWIFTEN_3
 
		boost::shared_ptr<Swift::CryptoProvider> crypto;
 
#endif
 
};
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <vector>
 
#include <list>
 
#include <boost/signal.hpp>
 
#include "Swiften/Queries/GetResponder.h"
 
#include "Swiften/Elements/DiscoInfo.h"
 
#include "Swiften/Elements/CapsInfo.h"
 
#include <Swiften/Version.h>
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Crypto/CryptoProvider.h>
 
#include <Swiften/Crypto/PlatformCryptoProvider.h>
 
#endif
 

	
 

	
 
namespace Transport {
 

	
 
class Config;
 

	
 
class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
 
	public:
 
		DiscoInfoResponder(Swift::IQRouter *router, Config *config);
 
		~DiscoInfoResponder();
 

	
 
		void setTransportFeatures(std::list<std::string> &features);
 
		void setBuddyFeatures(std::list<std::string> &features);
 

	
 
		void addRoom(const std::string &jid, const std::string &name);
 
		void clearRooms();
 

	
 
		void addAdHocCommand(const std::string &node, const std::string &name);
 

	
 
		boost::signal<void (const Swift::CapsInfo &capsInfo)> onBuddyCapsInfoChanged;
 

	
 
		Swift::CapsInfo &getBuddyCapsInfo() {
 
				return m_capsInfo;
 
		}
 

	
 
	private:
 
		virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> payload);
 
		void updateFeatures();
 

	
 
		Swift::DiscoInfo m_transportInfo;
 
		Swift::DiscoInfo *m_buddyInfo;
 
		Config *m_config;
 
		Swift::CapsInfo m_capsInfo;
 
		std::map<std::string, std::string> m_rooms;
 
		std::map<std::string, std::string> m_commands;
 
#if HAVE_SWIFTEN_3
 
		boost::shared_ptr<Swift::CryptoProvider> crypto;
 
#endif
 
};
 

	
 
}
src/filetransfermanager.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/filetransfermanager.h"
 
#include "transport/transport.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/buddy.h"
 
#include "transport/logging.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 
 
namespace Transport {
 
 
DEFINE_LOGGER(logger, "FileTransferManager");
 
 
FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) {
 
	m_component = component;
 
	m_userManager = userManager;
 
 
	m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter());
 
#if !HAVE_SWIFTEN_3
 
	m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser());
 
#endif
 
	m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry();
 
#if !HAVE_SWIFTEN_3
 
	m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 
	m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused");
 
	m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 
#else
 
	m_proxyManager = new Swift::SOCKS5BytestreamProxiesManager(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory(), m_component->getNetworkFactories()->getDomainNameResolver(), m_component->getIQRouter(), "bar.com");
 
#endif
 
	boost::shared_ptr<Swift::ConnectionServer> server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645);
 
	server->start();
 
#if HAVE_SWIFTEN_3
 
	m_proxyServerManager = new Swift::SOCKS5BytestreamServerManager(m_bytestreamRegistry, m_component->getNetworkFactories()->getConnectionServerFactory(), m_component->getNetworkFactories()->getNetworkEnvironment(), m_component->getNetworkFactories()->getNATTraverser());
 
#else
 
	m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry);
 
	m_bytestreamServer->start();
 
	m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(),
 
		m_userManager, m_remoteCandidateSelectorFactory,
 
		m_localCandidateGeneratorFactory, m_bytestreamRegistry,
 
		m_bytestreamProxy, m_component->getPresenceOracle(),
 
		m_bytestreamServer);
 
#endif
 
 
	
 
 
// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT
 
// 	m_connectivityManager->addListeningPort(19645);
 
}
 
 
FileTransferManager::~FileTransferManager() {
 
#if !HAVE_SWIFTEN_3
 
	m_bytestreamServer->stop();
 
	delete m_remoteCandidateSelectorFactory;
 
	delete m_localCandidateGeneratorFactory;
 
#endif
 
	delete m_outgoingFTManager;
 
	delete m_jingleSessionManager;
 
	delete m_bytestreamRegistry;
 
#if !HAVE_SWIFTEN_3
 
	delete m_bytestreamServer;
 
	delete m_bytestreamProxy;
 
	delete m_connectivityManager;
 
#endif
 
}
 
 
FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, boost::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info) {
 
	FileTransferManager::Transfer transfer;
 
	transfer.from = buddy->getJID();
 
	transfer.to = user->getJID();
 
	transfer.readByteStream = byteStream;
 
 
	LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'")
 
 
	transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info);
 
// 	if (transfer.ft) {
 
// 		m_filetransfers.push_back(ft);
 
// 		ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id));
 
// 		transfer.ft->start();
 
// 	}
 
	return transfer;
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/filetransfermanager.h"
 
#include "transport/transport.h"
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/buddy.h"
 
#include "transport/logging.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "FileTransferManager");
 

	
 
FileTransferManager::FileTransferManager(Component *component, UserManager *userManager) {
 
	m_component = component;
 
	m_userManager = userManager;
 

	
 
	m_jingleSessionManager = new Swift::JingleSessionManager(m_component->getIQRouter());
 
#if !HAVE_SWIFTEN_3
 
	m_connectivityManager = new Swift::ConnectivityManager(m_component->getNetworkFactories()->getNATTraverser());
 
#endif
 
	m_bytestreamRegistry = new Swift::SOCKS5BytestreamRegistry();
 
#if !HAVE_SWIFTEN_3
 
	m_bytestreamProxy = new Swift::SOCKS5BytestreamProxy(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 
	m_localCandidateGeneratorFactory = new Swift::DefaultLocalJingleTransportCandidateGeneratorFactory(m_connectivityManager, m_bytestreamRegistry, m_bytestreamProxy, "thishouldnotbeused");
 
	m_remoteCandidateSelectorFactory = new Swift::DefaultRemoteJingleTransportCandidateSelectorFactory(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory());
 
#else
 
	m_proxyManager = new Swift::SOCKS5BytestreamProxiesManager(m_component->getNetworkFactories()->getConnectionFactory(), m_component->getNetworkFactories()->getTimerFactory(), m_component->getNetworkFactories()->getDomainNameResolver(), m_component->getIQRouter(), "bar.com");
 
#endif
 
	boost::shared_ptr<Swift::ConnectionServer> server = m_component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(19645);
 
	server->start();
 
#if HAVE_SWIFTEN_3
 
	m_proxyServerManager = new Swift::SOCKS5BytestreamServerManager(m_bytestreamRegistry, m_component->getNetworkFactories()->getConnectionServerFactory(), m_component->getNetworkFactories()->getNetworkEnvironment(), m_component->getNetworkFactories()->getNATTraverser());
 
#else
 
	m_bytestreamServer = new Swift::SOCKS5BytestreamServer(server, m_bytestreamRegistry);
 
	m_bytestreamServer->start();
 
	m_outgoingFTManager = new Swift::CombinedOutgoingFileTransferManager(m_jingleSessionManager, m_component->getIQRouter(),
 
		m_userManager, m_remoteCandidateSelectorFactory,
 
		m_localCandidateGeneratorFactory, m_bytestreamRegistry,
 
		m_bytestreamProxy, m_component->getPresenceOracle(),
 
		m_bytestreamServer);
 
#endif
 

	
 
	
 

	
 
// WARNING: Swiften crashes when this is uncommented... But we probably need it for working Jingle FT
 
// 	m_connectivityManager->addListeningPort(19645);
 
}
 

	
 
FileTransferManager::~FileTransferManager() {
 
#if !HAVE_SWIFTEN_3
 
	m_bytestreamServer->stop();
 
	delete m_remoteCandidateSelectorFactory;
 
	delete m_localCandidateGeneratorFactory;
 
#endif
 
	delete m_outgoingFTManager;
 
	delete m_jingleSessionManager;
 
	delete m_bytestreamRegistry;
 
#if !HAVE_SWIFTEN_3
 
	delete m_bytestreamServer;
 
	delete m_bytestreamProxy;
 
	delete m_connectivityManager;
 
#endif
 
}
 

	
 
FileTransferManager::Transfer FileTransferManager::sendFile(User *user, Buddy *buddy, boost::shared_ptr<Swift::ReadBytestream> byteStream, const Swift::StreamInitiationFileInfo &info) {
 
	FileTransferManager::Transfer transfer;
 
	transfer.from = buddy->getJID();
 
	transfer.to = user->getJID();
 
	transfer.readByteStream = byteStream;
 

	
 
	LOG4CXX_INFO(logger, "Starting FT from '" << transfer.from << "' to '" << transfer.to << "'")
 

	
 
	transfer.ft = m_outgoingFTManager->createOutgoingFileTransfer(transfer.from, transfer.to, transfer.readByteStream, info);
 
// 	if (transfer.ft) {
 
// 		m_filetransfers.push_back(ft);
 
// 		ft->onStateChange.connect(boost::bind(&User::handleFTStateChanged, this, _1, Buddy::JIDToLegacyName(from), info.getName(), info.getSize(), id));
 
// 		transfer.ft->start();
 
// 	}
 
	return transfer;
 
}
 

	
 
}
src/networkpluginserver.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/networkpluginserver.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/localbuddy.h"
 
#include "transport/config.h"
 
#include "transport/conversation.h"
 
#include "transport/vcardresponder.h"
 
#include "transport/rosterresponder.h"
 
#include "transport/memoryreadbytestream.h"
 
#include "transport/logging.h"
 
#include "transport/admininterface.h"
 
#include "blockresponder.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 
#include "Swiften/Elements/AttentionPayload.h"
 
#include "Swiften/Elements/XHTMLIMPayload.h"
 
#include "Swiften/Elements/Delay.h"
 
#include "Swiften/Elements/DeliveryReceipt.h"
 
#include "Swiften/Elements/DeliveryReceiptRequest.h"
 
#include "Swiften/Elements/InvisiblePayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include "transport/protocol.pb.h"
 
#include "transport/util.h"
 
#include "transport/discoitemsresponder.h"
 
 
#include "boost/date_time/posix_time/posix_time.hpp"
 
#include "boost/signal.hpp"
 
 
#include "transport/utf8.h"
 
 
#include <Swiften/FileTransfer/ReadBytestream.h>
 
#include <Swiften/Elements/StreamInitiationFileInfo.h>
 
 
#ifdef _WIN32
 
#include "windows.h"
 
#include <stdint.h>
 
#else
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#include <sys/types.h>
 
#include <signal.h>
 
#include "popt.h"
 
#endif
 
 
using namespace Transport::Util;
 
 
namespace Transport {
 
 
static unsigned long backend_id;
 
static unsigned long bytestream_id;
 
 
DEFINE_LOGGER(logger, "NetworkPluginServer");
 
 
static NetworkPluginServer *_server;
 
 
class NetworkConversation : public Conversation {
 
	public:
 
		NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) {
 
		}
 
 
		// Called when there's new message to legacy network from XMPP network
 
		void sendMessage(boost::shared_ptr<Swift::Message> &message) {
 
			onMessageToSend(this, message);
 
		}
 
 
		boost::signal<void (NetworkConversation *, boost::shared_ptr<Swift::Message> &)> onMessageToSend;
 
};
 
 
class NetworkFactory : public Factory {
 
	public:
 
		NetworkFactory(NetworkPluginServer *nps) {
 
			m_nps = nps;
 
		}
 
 
		virtual ~NetworkFactory() {}
 
 
		// Creates new conversation (NetworkConversation in this case)
 
		Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc) {
 
			NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName, isMuc);
 
			nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2));
 
			return nc;
 
		}
 
 
		// Creates new LocalBuddy
 
		Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) {
 
			LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags);
 
			if (!buddy->isValid()) {
 
				delete buddy;
 
				return NULL;
 
			}
 
			if (buddyInfo.subscription == "both") {
 
				buddy->setSubscription(Buddy::Both);
 
			}
 
			else {
 
				buddy->setSubscription(Buddy::Ask);
 
			}
 
			if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end())
 
				buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s);
 
			return buddy;
 
		}
 
 
	private:
 
		NetworkPluginServer *m_nps;
 
};
 
 
// Wraps google protobuf payload into WrapperMessage and serialize it to string
 
#define WRAP(MESSAGE, TYPE) 	pbnetwork::WrapperMessage wrap; \
 
	wrap.set_type(TYPE); \
 
	wrap.set_payload(MESSAGE); \
 
	wrap.SerializeToString(&MESSAGE);
 
 
// Executes new backend
 
static unsigned long exec_(const std::string& exePath, const char *host, const char *port, const char *log_id, const char *cmdlineArgs) {
 
	// BACKEND_ID is replaced with unique ID. The ID is increasing for every backend.
 
	std::string finalExePath = boost::replace_all_copy(exePath, "BACKEND_ID", boost::lexical_cast<std::string>(backend_id++));	
 
 
#ifdef _WIN32
 
	// Add host and port.
 
	std::ostringstream fullCmdLine;
 
	fullCmdLine << "\"" << finalExePath << "\" --host " << host << " --port " << port;
 
 
	if (cmdlineArgs)
 
		fullCmdLine << " " << cmdlineArgs;
 
 
	LOG4CXX_INFO(logger, "Starting new backend " << fullCmdLine.str());
 
 
	// We must provide a non-const buffer to CreateProcess below
 
	std::vector<wchar_t> rawCommandLineArgs( fullCmdLine.str().size() + 1 );
 
	wcscpy_s(&rawCommandLineArgs[0], rawCommandLineArgs.size(), utf8ToUtf16(fullCmdLine.str()).c_str());
 
 
	STARTUPINFO         si;
 
	PROCESS_INFORMATION pi;
 
 
	ZeroMemory (&si, sizeof(si));
 
	si.cb=sizeof (si);
 
 
	if (! CreateProcess(
 
		utf8ToUtf16(finalExePath).c_str(),
 
		&rawCommandLineArgs[0],
 
		0,                    // process attributes
 
		0,                    // thread attributes
 
		0,                    // inherit handles
 
		0,                    // creation flags
 
		0,                    // environment
 
		0,                    // cwd
 
		&si,
 
		&pi
 
		)
 
	)  {
 
		LOG4CXX_ERROR(logger, "Could not start process");
 
	}
 
 
	return 0;
 
#else
 
	// Add host and port.
 
	finalExePath += std::string(" --host ") + host + " --port " + port + " --service.backend_id=" + log_id + " " + cmdlineArgs;
 
	LOG4CXX_INFO(logger, "Starting new backend " << finalExePath);
 
 
	// Create array of char * from string using -lpopt library
 
	char *p = (char *) malloc(finalExePath.size() + 1);
 
	strcpy(p, finalExePath.c_str());
 
	int argc;
 
	char **argv;
 
	poptParseArgvString(p, &argc, (const char ***) &argv);
 
 
	// fork and exec
 
	pid_t pid = fork();
 
	if ( pid == 0 ) {
 
		setsid();
 
		// close all files
 
		int maxfd=sysconf(_SC_OPEN_MAX);
 
		for(int fd=3; fd<maxfd; fd++) {
 
			close(fd);
 
		}
 
		// child process
 
		errno = 0;
 
		int ret = execv(argv[0], argv);
 
		if (ret == -1) {
 
			exit(errno);
 
		}
 
		exit(0);
 
	} else if ( pid < 0 ) {
 
		LOG4CXX_ERROR(logger, "Fork failed");
 
	}
 
	free(p);
 
 
	return (unsigned long) pid;
 
#endif
 
}
 
 
#ifndef _WIN32
 
static void SigCatcher(int n) {
 
	pid_t result;
 
	int status;
 
	// Read exit code from all children to not have zombies arround
 
	// WARNING: Do not put LOG4CXX_ here, because it can lead to deadlock
 
	while ((result = waitpid(-1, &status, WNOHANG)) > 0) {
 
		if (result != 0) {
 
			_server->handlePIDTerminated((unsigned long)result);
 
			if (WIFEXITED(status)) {
 
				if (WEXITSTATUS(status) != 0) {
 
// 					LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status));
 
				}
 
			}
 
			else {
 
// 				LOG4CXX_ERROR(logger, "Backend can not be started");
 
			}
 
		}
 
	}
 
}
 
#endif
 
 
static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payload) {
 
	// Set alias only if it's not empty. Backends are allowed to send empty alias if it has
 
	// not changed.
 
	if (!payload.alias().empty()) {
 
		buddy->setAlias(payload.alias());
 
	}
 
 
	// Change groups if it's not empty. The same as above...
 
	std::vector<std::string> groups;
 
	for (int i = 0; i < payload.group_size(); i++) {
 
		std::string group;
 
		utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), std::back_inserter(group), '_');
 
		groups.push_back(group);
 
	}
 
	if (!groups.empty()) {
 
		buddy->setGroups(groups);
 
	}
 
 
	buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
	buddy->setIconHash(payload.iconhash());
 
	buddy->setBlocked(payload.blocked());
 
}
 
 
NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder) {
 
	_server = this;
 
	m_ftManager = ftManager;
 
	m_userManager = userManager;
 
	m_config = config;
 
	m_component = component;
 
	m_isNextLongRun = false;
 
	m_adminInterface = NULL;
 
	m_startingBackend = false;
 
	m_lastLogin = 0;
 
	m_xmppParser = new Swift::XMPPParser(this, &m_collection, component->getNetworkFactories()->getXMLParserFactory());
 
	m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 
#if HAVE_SWIFTEN_3
 
	m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType, false);
 
#else
 
	m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType);
 
#endif
 
	m_discoItemsResponder = discoItemsResponder;
 
	m_component->m_factory = new NetworkFactory(this);
 
	m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1));
 
	m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1));
 
 
	m_component->onRawIQReceived.connect(boost::bind(&NetworkPluginServer::handleRawIQReceived, this, _1));
 
 
	m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000);
 
	m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this));
 
	m_pingTimer->start();
 
 
	m_loginTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(config, "service.login_delay") * 1000);
 
	m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this));
 
	m_loginTimer->start();
 
 
	if (CONFIG_INT(m_config, "service.memory_collector_time") != 0) {
 
		m_collectTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(m_config, "service.memory_collector_time"));
 
		m_collectTimer->onTick.connect(boost::bind(&NetworkPluginServer::collectBackend, this));
 
		m_collectTimer->start();
 
	}
 
 
	m_vcardResponder = new VCardResponder(component->getIQRouter(), component->getNetworkFactories(), userManager);
 
	m_vcardResponder->onVCardRequired.connect(boost::bind(&NetworkPluginServer::handleVCardRequired, this, _1, _2, _3));
 
	m_vcardResponder->onVCardUpdated.connect(boost::bind(&NetworkPluginServer::handleVCardUpdated, this, _1, _2));
 
	m_vcardResponder->start();
 
 
	m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager);
 
	m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2));
 
	m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1));
 
	m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2));
 
	m_rosterResponder->start();
 
 
	m_blockResponder = new BlockResponder(component->getIQRouter(), userManager);
 
	m_blockResponder->onBlockToggled.connect(boost::bind(&NetworkPluginServer::handleBlockToggled, this, _1));
 
	m_blockResponder->start();
 
 
	m_server = component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(Swift::HostAddress(CONFIG_STRING(m_config, "service.backend_host")), boost::lexical_cast<int>(CONFIG_STRING(m_config, "service.backend_port")));
 
	m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1));
 
}
 
 
NetworkPluginServer::~NetworkPluginServer() {
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		LOG4CXX_INFO(logger, "Stopping backend " << *it);
 
		std::string message;
 
		pbnetwork::WrapperMessage wrap;
 
		wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT);
 
		wrap.SerializeToString(&message);
 
 
		Backend *c = (Backend *) *it;
 
		send(c->connection, message);
 
	}
 
 
	m_pingTimer->stop();
 
	m_server->stop();
 
	m_server.reset();
 
	delete m_component->m_factory;
 
	delete m_vcardResponder;
 
	delete m_rosterResponder;
 
	delete m_blockResponder;
 
}
 
 
void NetworkPluginServer::start() {
 
	m_server->start();
 
 
	LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port"));
 
 
	while (true) {
 
		unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), "1", m_config->getCommandLineArgs().c_str());
 
		LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid);
 
		LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects");
 
 
#ifndef _WIN32
 
		// wait if the backend process will still be alive after 1 second
 
		sleep(1);
 
		pid_t result;
 
		int status;
 
		result = waitpid(-1, &status, WNOHANG);
 
		if (result != 0) {
 
			if (WIFEXITED(status)) {
 
				if (WEXITSTATUS(status) != 0) {
 
					if (status == 254) {
 
						LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured.");
 
					}
 
					else {
 
						LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status)));
 
						if (WEXITSTATUS(status) == ENOENT) {
 
							LOG4CXX_ERROR(logger, "This usually means the path to backend executable defined in config file as '[service] backend=\"...\"' is wrong or the executable does not exists.");
 
						}
 
						
 
					}
 
					LOG4CXX_ERROR(logger, "Check backend log for more details");
 
					continue;
 
				}
 
			}
 
			else {
 
				LOG4CXX_ERROR(logger, "Backend can not be started");
 
				continue;
 
			}
 
		}
 
 
		m_pids.push_back(pid);
 
 
		signal(SIGCHLD, SigCatcher);
 
#endif
 
		// quit the while loop
 
		break;
 
	}
 
}
 
 
void NetworkPluginServer::loginDelayFinished() {
 
	m_loginTimer->stop();
 
	connectWaitingUsers();
 
}
 
 
void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Connection> c) {
 
	// Create new Backend instance
 
	Backend *client = new Backend;
 
	client->pongReceived = -1;
 
	client->connection = c;
 
	client->res = 0;
 
	client->init_res = 0;
 
	client->shared = 0;
 
	// Until we receive first PONG from backend, backend is in willDie state.
 
	client->willDie = true;
 
	// Backend does not accept new clients automatically if it's long-running
 
	client->acceptUsers = !m_isNextLongRun;
 
	client->longRun = m_isNextLongRun;
 
 
	m_startingBackend = false;
 
 
	LOG4CXX_INFO(logger, "New" + (client->longRun ? std::string(" long-running") : "") +  " backend " << client << " connected. Current backend count=" << (m_clients.size() + 1));
 
 
	m_clients.push_front(client);
 
 
	c->onDisconnected.connect(boost::bind(&NetworkPluginServer::handleSessionFinished, this, client));
 
	c->onDataRead.connect(boost::bind(&NetworkPluginServer::handleDataRead, this, client, _1));
 
	sendPing(client);
 
 
	// sendPing sets pongReceived to 0, but we want to have it -1 to ignore this backend
 
	// in first ::pingTimeout call, because it can be called right after this function
 
	// and backend wouldn't have any time to response to ping.
 
	client->pongReceived = -1;
 
}
 
 
void NetworkPluginServer::handleSessionFinished(Backend *c) {
 
	LOG4CXX_INFO(logger, "Backend " << c << " (ID=" << c->id << ") disconnected. Current backend count=" << (m_clients.size() - 1));
 
 
	// This backend will do, so we can't reconnect users to it in User::handleDisconnected call
 
	c->willDie = true;
 
 
	// If there are users associated with this backend, it must have crashed, so print error output
 
	// and disconnect users
 
	if (!c->users.empty()) {
 
		m_crashedBackends.push_back(c->id);
 
	}
 
 
	for (std::list<User *>::const_iterator it = c->users.begin(); it != c->users.end(); it++) {
 
		LOG4CXX_ERROR(logger, "Backend " << c << " (ID=" << c->id << ") disconnected (probably crashed) with active user " << (*it)->getJID().toString());
 
		(*it)->setData(NULL);
 
		(*it)->handleDisconnected("Internal Server Error, please reconnect.");
 
	}
 
 
	std::string message;
 
	pbnetwork::WrapperMessage wrap;
 
	wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT);
 
	wrap.SerializeToString(&message);
 
 
	send(c->connection, message);
 
 
	c->connection->onDisconnected.disconnect_all_slots();
 
	c->connection->onDataRead.disconnect_all_slots();
 
	c->connection->disconnect();
 
	c->connection.reset();
 
 
	m_clients.remove(c);
 
	delete c;
 
}
 
 
void NetworkPluginServer::handleConnectedPayload(const std::string &data) {
 
	pbnetwork::Connected payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.user());
 
	if (!user) {
 
		LOG4CXX_ERROR(logger, "Connected payload received for unknown user " << payload.user());
 
		return;
 
	}
 
 
	user->setConnected(true);
 
	m_component->m_userRegistry->onPasswordValid(payload.user());
 
}
 
 
void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) {
 
	pbnetwork::Disconnected payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	m_component->m_userRegistry->onPasswordInvalid(payload.user(), payload.message());
 
 
	User *user = m_userManager->getUser(payload.user());
 
	if (!user) {
 
		return;
 
	}
 
	user->handleDisconnected(payload.message(), (Swift::SpectrumErrorPayload::Error) payload.error());
 
}
 
 
void NetworkPluginServer::handleVCardPayload(const std::string &data) {
 
	pbnetwork::VCard payload;
 
	if (payload.ParseFromString(data) == false) {
 
		std::cout << "PARSING ERROR\n";
 
		// TODO: ERROR
 
		return;
 
	}
 
	std::string field;
 
 
	boost::shared_ptr<Swift::VCard> vcard(new Swift::VCard());
 
 
	utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), std::back_inserter(field), '_');
 
	vcard->setFullName(field);
 
 
	field.clear();
 
 
	utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), std::back_inserter(field), '_');
 
	vcard->setNickname(field);
 
 
	vcard->setPhoto(Swift::createByteArray(payload.photo()));
 
 
	m_vcardResponder->sendVCard(payload.id(), vcard);
 
}
 
 
void NetworkPluginServer::handleAuthorizationPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	// Create subscribe presence and forward it to XMPP side
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(user->getJID());
 
	std::string name = payload.buddyname();
 
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
		name = Swift::JID::getEscapedNode(name);
 
	}
 
	else {
 
		if (name.find_last_of("@") != std::string::npos) {
 
			name.replace(name.find_last_of("@"), 1, "%");
 
		}
 
	}
 
 
	response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
	response->setType(Swift::Presence::Subscribe);
 
	m_component->getStanzaChannel()->sendPresence(response);
 
}
 
 
void NetworkPluginServer::handleChatStatePayload(const std::string &data, Swift::ChatState::ChatStateType type) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	// We're not creating new Conversation just because of chatstates.
 
	// Some networks/clients spams with chatstates a lot and it leads to bigger memory usage.
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		return;
 
	}
 
 
	// Forward chatstate
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::ChatState>(type));
 
 
	conv->handleMessage(msg);
 
}
 
 
void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
	if (buddy) {
 
		handleBuddyPayload(buddy, payload);
 
	}
 
	else {
 
		if (payload.buddyname() == user->getUserInfo().uin) {
 
			return;
 
		}
 
 
		std::vector<std::string> groups;
 
		for (int i = 0; i < payload.group_size(); i++) {
 
			groups.push_back(payload.group(i));
 
		}
 
		if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING);
 
		}
 
		else {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_NO_FLAG);
 
		}
 
		if (!buddy->isValid()) {
 
			delete buddy;
 
			return;
 
		}
 
 
		buddy->setBlocked(payload.blocked());
 
		user->getRosterManager()->setBuddy(buddy);
 
		buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
		buddy->setIconHash(payload.iconhash());
 
	}
 
}
 
 
void NetworkPluginServer::handleBuddyRemovedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	user->getRosterManager()->removeBuddy(payload.buddyname());
 
}
 
 
void NetworkPluginServer::handleParticipantChangedPayload(const std::string &data) {
 
	pbnetwork::Participant payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 
 
	conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
 
}
 
 
void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) {
 
	pbnetwork::Room payload;
 
	if (payload.ParseFromString(data) == false) {
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 
 
	conv->setNickname(payload.nickname());
 
}
 
 
void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool subject) {
 
	pbnetwork::ConversationMessage payload;
 
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	// Message from legacy network triggers network acticity
 
	user->updateLastActivity();
 
 
	// Set proper body.
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	if (subject) {
 
		msg->setSubject(payload.message());
 
	}
 
	else {
 
		msg->setBody(payload.message());
 
	}
 
 
	if (payload.headline()) {
 
		msg->setType(Swift::Message::Headline);
 
	}
 
 
	// Add xhtml-im payload.
 
	if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) {
 
		msg->addPayload(boost::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));
 
	}
 
 
	if (!payload.timestamp().empty()) {
 
		boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp());
 
		boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
 
		delay->setStamp(timestamp);
 
		msg->addPayload(delay);
 
	}
 
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
 
	// We can't create Conversation for payload with nickname, because this means the message is from room,
 
	// but this user is not in any room, so it's OK to just reject this message
 
	if (!conv && !payload.nickname().empty()) {
 
		return;
 
	}
 
 
	if (conv && payload.pm()) {
 
		conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname());
 
		if (!conv) {
 
			conv = new NetworkConversation(user->getConversationManager(), payload.nickname());
 
			std::string name = payload.buddyname();
 
			conv->setRoom(name);
 
			conv->setNickname(payload.buddyname() + "/" + payload.nickname());
 
 
			user->getConversationManager()->addConversation(conv);
 
			conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
		}
 
	}
 
 
	// Create new Conversation if it does not exist
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 
 
	// Forward it
 
	conv->handleMessage(msg, payload.nickname());
 
	m_userManager->messageToXMPPSent();
 
}
 
 
void NetworkPluginServer::handleConvMessageAckPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	if (payload.id().empty()) {
 
		LOG4CXX_WARN(logger, "Received message ack with empty ID, not forwarding to XMPP.");
 
		return;
 
	}
 
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::DeliveryReceipt>(payload.id()));
 
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
 
	// Receipts don't create conversation
 
	if (!conv) {
 
		return;
 
	}
 
 
	// Forward it
 
	conv->handleMessage(msg);
 
}
 
 
void NetworkPluginServer::handleAttentionPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.message());
 
	msg->addPayload(boost::make_shared<Swift::AttentionPayload>());
 
 
	// Attentions trigger new Conversation creation
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 
 
	conv->handleMessage(msg);
 
}
 
 
void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data) {
 
	pbnetwork::Stats payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	c->res = payload.res();
 
	c->init_res = payload.init_res();
 
	c->shared = payload.shared();
 
	c->id = payload.id();
 
}
 
 
void NetworkPluginServer::handleFTStartPayload(const std::string &data) {
 
	pbnetwork::File payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 
 
	LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname());
 
	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
	if (!buddy) {
 
		// TODO: escape? reject?
 
		return;
 
	}
 
 
	Swift::StreamInitiationFileInfo fileInfo;
 
	fileInfo.setSize(payload.size());
 
	fileInfo.setName(payload.filename());
 
 
	Backend *c = (Backend *) user->getData();
 
	boost::shared_ptr<MemoryReadBytestream> bytestream(new MemoryReadBytestream(payload.size()));
 
	bytestream->onDataNeeded.connect(boost::bind(&NetworkPluginServer::handleFTDataNeeded, this, c, bytestream_id + 1));
 
 
	LOG4CXX_INFO(logger, "jid=" << buddy->getJID());
 
 
	FileTransferManager::Transfer transfer = m_ftManager->sendFile(user, buddy, bytestream, fileInfo);
 
	if (!transfer.ft) {
 
		handleFTRejected(user, payload.buddyname(), payload.filename(), payload.size());
 
		return;
 
	}
 
 
	m_filetransfers[++bytestream_id] = transfer;
 
#if !HAVE_SWIFTEN_3
 
	transfer.ft->onStateChange.connect(boost::bind(&NetworkPluginServer::handleFTStateChanged, this, _1, payload.username(), payload.buddyname(), payload.filename(), payload.size(), bytestream_id));
 
	transfer.ft->start();
 
#endif
 
}
 
 
void NetworkPluginServer::handleFTFinishPayload(const std::string &data) {
 
	pbnetwork::File payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	if (payload.has_ftid()) {
 
		if (m_filetransfers.find(payload.ftid()) != m_filetransfers.end()) {
 
			FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
 
			transfer.ft->cancel();
 
		}
 
		else {
 
			LOG4CXX_ERROR(logger, "FTFinishPayload for unknown ftid=" << payload.ftid());
 
		}
 
	}
 
 
}
 
 
void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &data) {
 
	pbnetwork::FileTransferData payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
// 	User *user = m_userManager->getUser(payload.username());
 
// 	if (!user)
 
// 		return;
 
 
	if (m_filetransfers.find(payload.ftid()) == m_filetransfers.end()) {
 
		LOG4CXX_ERROR(logger, "Uknown filetransfer with id " << payload.ftid());
 
		return;
 
	}
 
 
	FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
 
	MemoryReadBytestream *bytestream = (MemoryReadBytestream *) transfer.readByteStream.get();
 
 
	if (bytestream->appendData(payload.data()) > 5000000) {
 
		pbnetwork::FileTransferData f;
 
		f.set_ftid(payload.ftid());
 
		f.set_data("");
 
 
		std::string message;
 
		f.SerializeToString(&message);
 
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE);
 
 
		send(b->connection, message);
 
	}
 
}
 
 
void NetworkPluginServer::handleFTDataNeeded(Backend *b, unsigned long ftid) {
 
	pbnetwork::FileTransferData f;
 
	f.set_ftid(ftid);
 
	f.set_data("");
 
 
	std::string message;
 
	f.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE);
 
 
	send(b->connection, message);
 
}
 
 
void NetworkPluginServer::connectWaitingUsers() {
 
	// some users are in queue waiting for this backend
 
	while(!m_waitingUsers.empty()) {
 
		// There's no new backend, so stop associating users and wait for new backend,
 
		// which has been already spawned in getFreeClient() call.
 
		if (getFreeClient(true, false, true) == NULL)
 
			break;
 
 
		User *u = m_waitingUsers.front();
 
		m_waitingUsers.pop_front();
 
 
		LOG4CXX_INFO(logger, "Associating " << u->getJID().toString() << " with this backend");
 
 
		// associate backend with user
 
		handleUserCreated(u);
 
 
		// connect user if it's ready
 
		if (u->isReadyToConnect()) {
 
			handleUserReadyToConnect(u);
 
		}
 
	}
 
}
 
 
void NetworkPluginServer::handlePongReceived(Backend *c) {
 
	// This could be first PONG from the backend
 
	if (c->pongReceived == -1) {
 
		// Backend is fully ready to handle requests
 
		c->willDie = false;
 
 
		if (m_clients.size() == 1) {
 
			// first backend connected, start the server, we're ready.
 
			m_component->start();
 
		}
 
 
		connectWaitingUsers();
 
	}
 
 
	c->pongReceived = true;
 
}
 
 
void NetworkPluginServer::handleQueryPayload(Backend *b, const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	if (!m_adminInterface) {
 
		return;
 
	}
 
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.config());
 
	m_adminInterface->handleQuery(msg);
 
 
	pbnetwork::BackendConfig response;
 
	response.set_config(msg->getBody());
 
 
	std::string message;
 
	response.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY);
 
 
	send(b->connection, message);
 
}
 
 
void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	m_config->updateBackendConfig(payload.config());
 
}
 
 
void NetworkPluginServer::handleRoomListPayload(const std::string &data) {
 
	pbnetwork::RoomList payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 
 
	m_discoItemsResponder->clearRooms();
 
	for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) {
 
		m_discoItemsResponder->addRoom(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i));
 
	}
 
}
 
#if HAVE_SWIFTEN_3
 
void NetworkPluginServer::handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
 
#else
 
void NetworkPluginServer::handleElement(boost::shared_ptr<Swift::Element> element) {
 
#endif
 
	boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
 
	if (!stanza) {
 
		return;
 
	}
 
 
	User *user = m_userManager->getUser(stanza->getTo().toBare());
 
	if (!user)
 
		return;
 
 
	Swift::JID originalJID = stanza->getFrom();
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(originalJID.toBare());
 
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(stanza->getFrom().toBare());
 
	if (buddy) {
 
		const Swift::JID &jid = buddy->getJID();
 
		if (stanza->getFrom().getResource().empty()) {
 
			stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain()));
 
		}
 
		else {
 
			stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain(), stanza->getFrom().getResource()));
 
		}
 
	}
 
	else {
 
		std::string name = stanza->getFrom().toBare();
 
		if (conv && conv->isMUC()) {
 
			if (name.find_last_of("@") != std::string::npos) {
 
				name.replace(name.find_last_of("@"), 1, "%");
 
			}
 
		}
 
		else {
 
			if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
				name = Swift::JID::getEscapedNode(name);
 
			}
 
			else {
 
				if (name.find_last_of("@") != std::string::npos) {
 
					name.replace(name.find_last_of("@"), 1, "%");
 
				}
 
			}
 
		}
 
		if (stanza->getFrom().getResource().empty()) {
 
			stanza->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
		}
 
		else {
 
			stanza->setFrom(Swift::JID(name, m_component->getJID().toString(), stanza->getFrom().getResource()));
 
		}
 
	}
 
 
	boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
 
	if (message) {
 
		if (conv) {
 
			conv->handleRawMessage(message);
 
			return;
 
		}
 
 
		m_component->getStanzaChannel()->sendMessage(message);
 
		return;
 
	}
 
 
	boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
 
	if (presence) {
 
		if (buddy) {
 
			if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) {
 
				buddy->m_status.setType(Swift::StatusShow::Online);
 
			}
 
			buddy->handleRawPresence(presence);
 
		}
 
		else if (conv) {
 
			conv->handleRawPresence(presence);
 
		}
 
		else {
 
			m_component->getStanzaChannel()->sendPresence(presence);
 
		}
 
 
		return;
 
	}
 
 
	boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
 
	if (iq) {
 
		if (m_id2resource.find(stanza->getTo().toBare().toString() + stanza->getID()) != m_id2resource.end()) {
 
			iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_id2resource[stanza->getTo().toBare().toString() + stanza->getID()]));
 
			m_id2resource.erase(stanza->getTo().toBare().toString() + stanza->getID());
 
		}
 
		else {
 
			Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(user->getJID());
 
			if (highest) {
 
			    iq->setTo(highest->getFrom());
 
			} else {
 
			    iq->setTo(user->getJID());
 
			}
 
		}
 
		m_component->getIQRouter()->sendIQ(iq);
 
		return;
 
	}
 
}
 
 
void NetworkPluginServer::handleRawXML(const std::string &xml) {
 
	m_xmppParser->parse(xml);
 
}
 
 
void NetworkPluginServer::handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence) {
 
	User *user = m_userManager->getUser(presence->getFrom().toBare());
 
	if (!user)
 
		return;
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
 
	Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(presence->getTo()));
 
	if (!presence->getTo().getResource().empty()) {
 
		presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), presence->getTo().getResource()));
 
	}
 
	else {
 
		presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
	}
 
 
	std::string xml = safeByteArrayToString(m_serializer->serializeElement(presence));
 
	WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
	send(c->connection, xml);
 
}
 
 
void NetworkPluginServer::handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq) {
 
	User *user = m_userManager->getUser(iq->getFrom().toBare());
 
	if (!user)
 
		return;
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
 
	if (iq->getType() == Swift::IQ::Get) {
 
		m_id2resource[iq->getFrom().toBare().toString() + iq->getID()] = iq->getFrom().getResource();
 
	}
 
 
	Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(iq->getTo()));
 
	if (!iq->getTo().getResource().empty()) {
 
		iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), iq->getTo().getResource()));
 
	}
 
	else {
 
		iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
	}
 
 
	std::string xml = safeByteArrayToString(m_serializer->serializeElement(iq));
 
	WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
	send(c->connection, xml);
 
}
 
 
void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data) {
 
	// Append data to buffer
 
	c->data.insert(c->data.end(), data->begin(), data->end());
 
 
	// Parse data while there are some
 
	while (c->data.size() != 0) {
 
		// expected_size of wrapper message
 
		unsigned int expected_size;
 
 
		// if data is >= 4, we have whole header and we can
 
		// read expected_size.
 
		if (c->data.size() >= 4) {
 
			expected_size = *((unsigned int*) &c->data[0]);
 
			expected_size = ntohl(expected_size);
 
			// If we don't have whole wrapper message, wait for next
 
			// handleDataRead call.
 
			if (c->data.size() - 4 < expected_size)
 
				return;
 
		}
 
		else {
 
			return;
 
		}
 
 
		// Parse wrapper message and erase it from buffer.
 
		pbnetwork::WrapperMessage wrapper;
 
		if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) {
 
			std::cout << "PARSING ERROR " << expected_size << "\n";
 
			c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 
			continue;
 
		}
 
		c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 
 
		// If backend is slow and it is sending us lot of message, there is possibility
 
		// that we don't receive PONG response before timeout. However, if we received
 
		// at least some data, it means backend is not dead and we can treat it as
 
		// PONG received event.
 
		if (c->pongReceived == false) {
 
			c->pongReceived = true;
 
		}
 
 
		// Handle payload in wrapper message
 
		switch(wrapper.type()) {
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONNECTED:
 
				handleConnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_DISCONNECTED:
 
				handleDisconnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED:
 
				handleBuddyChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE:
 
				handleConvMessagePayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED:
 
				handleConvMessagePayload(wrapper.payload(), true);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PONG:
 
				handlePongReceived(c);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PARTICIPANT_CHANGED:
 
				handleParticipantChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_NICKNAME_CHANGED:
 
				handleRoomChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_VCARD:
 
				handleVCardPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Composing);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST:
 
				handleAuthorizationPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION:
 
				handleAttentionPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_STATS:
 
				handleStatsPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_START:
 
				handleFTStartPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH:
 
				handleFTFinishPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA:
 
				handleFTDataPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED:
 
				handleBuddyRemovedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_QUERY:
 
				handleQueryPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG:
 
				handleBackendConfigPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST:
 
				handleRoomListPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK:
 
				handleConvMessageAckPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML:
 
				handleRawXML(wrapper.payload());
 
				break;
 
			default:
 
				return;
 
		}
 
	}
 
}
 
 
void NetworkPluginServer::send(boost::shared_ptr<Swift::Connection> &c, const std::string &data) {
 
	// generate header - size of wrapper message
 
	uint32_t size = htonl(data.size());
 
	char *header = (char *) &size;
 
 
	// send header together with wrapper message
 
	c->write(Swift::createSafeByteArray(std::string(header, 4) + data));
 
}
 
 
void NetworkPluginServer::pingTimeout() {
 
	// TODO: move to separate timer, those 2 loops could be expensive
 
	// Some users are connected for weeks and they are blocking backend to be destroyed and its memory
 
	// to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and
 
	// reconnect them to long-running backend, where they can idle hapilly till the end of ages.
 
	time_t now = time(NULL);
 
	std::vector<User *> usersToMove;
 
	unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time");
 
	if (diff != 0) {
 
		for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
			// Users from long-running backends can't be moved
 
			if ((*it)->longRun) {
 
				continue;
 
			}
 
 
			// Find users which are inactive for more than 'diff'
 
			BOOST_FOREACH(User *u, (*it)->users) {
 
				if (now - u->getLastActivity() > diff) {
 
					usersToMove.push_back(u);
 
				}
 
			}
 
		}
 
 
		// Move inactive users to long-running backend.
 
		BOOST_FOREACH(User *u, usersToMove) {
 
			LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
 
			if (!moveToLongRunBackend(u))
 
				break;
 
		}
 
	}
 
 
	// We have to remove startingBackend flag otherwise 1 broken backend start could
 
	// block the backend.
 
	m_startingBackend = false;
 
 
	// check ping responses
 
	std::vector<Backend *> toRemove;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		// pong has been received OR backend just connected and did not have time to answer the ping
 
		// request.
 
		if ((*it)->pongReceived || (*it)->pongReceived == -1) {
 
			// Don't send another ping if pongReceived == -1, because we've already sent one
 
			// when registering backend.
 
			if ((*it)->pongReceived) {
 
				sendPing((*it));
 
			}
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). PING response not received.");
 
			toRemove.push_back(*it);
 
 
#ifndef WIN32
 
			// generate coredump for this backend to find out why it wasn't able to respond to PING
 
			std::string pid = (*it)->id;
 
			if (!pid.empty()) {
 
				try {
 
					kill(boost::lexical_cast<int>(pid), SIGABRT);
 
				}
 
				catch (...) { }
 
			}
 
#endif
 
		}
 
 
		if ((*it)->users.size() == 0) {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users.");
 
			toRemove.push_back(*it);
 
		}
 
	}
 
 
	BOOST_FOREACH(Backend *b, toRemove) {
 
		handleSessionFinished(b);
 
	}
 
 
	m_pingTimer->start();
 
}
 
 
void NetworkPluginServer::collectBackend() {
 
	// Stop accepting new users to backend with the biggest memory usage. This prevents backends
 
	// which are leaking to eat whole memory by connectin new users to legacy network.
 
	LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die");
 
	unsigned long max = 0;
 
	Backend *backend = NULL;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->res > max) {
 
			max = (*it)->res;
 
			backend = (*it);
 
		}
 
	}
 
 
	if (backend) {
 
		if (m_collectTimer) {
 
			m_collectTimer->start();
 
		}
 
		LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die");
 
		backend->acceptUsers = false;
 
	}
 
}
 
 
bool NetworkPluginServer::moveToLongRunBackend(User *user) {
 
	// Check if user has already some backend
 
	Backend *old = (Backend *) user->getData();
 
	if (!old) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving.");
 
		return true;
 
	}
 
 
	// if he's already on long run, do nothing
 
	if (old->longRun) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving.");
 
		return true;
 
	}
 
 
	// Get free longrun backend, if there's no longrun backend, create one and wait
 
	// for its connection
 
	Backend *backend = getFreeClient(false, true);
 
	if (!backend) {
 
		LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later");
 
		return false;
 
	}
 
 
	// old backend will trigger disconnection which has to be ignored to keep user online
 
	user->setIgnoreDisconnect(true);
 
 
	// remove user from the old backend
 
	// If backend is empty, it will be collected by pingTimeout
 
	old->users.remove(user);
 
 
	// switch to new backend and connect
 
	user->setData(backend);
 
	backend->users.push_back(user);
 
 
	// connect him
 
	handleUserReadyToConnect(user);
 
	return true;
 
}
 
 
void NetworkPluginServer::handleUserCreated(User *user) {
 
	// Get free backend to handle this user or spawn new one if there's no free one.
 
	Backend *c = getFreeClient();
 
 
	// Add user to queue if there's no free backend to handle him so far.
 
	if (!c) {
 
		LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue.");
 
		m_waitingUsers.push_back(user);
 
		return;
 
	}
 
 
	// Associate users with backend
 
	user->setData(c);
 
	c->users.push_back(user);
 
 
	// Don't forget to disconnect these in handleUserDestroyed!!!
 
	user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRawPresenceReceived.connect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
 
	user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 
 
	user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 
}
 
 
void NetworkPluginServer::handleUserReadyToConnect(User *user) {
 
	UserInfo userInfo = user->getUserInfo();
 
 
	pbnetwork::Login login;
 
	login.set_user(user->getJID().toBare());
 
	login.set_legacyname(userInfo.uin);
 
	login.set_password(userInfo.password);
 
 
	std::string message;
 
	login.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
	if (presence->getShow() == Swift::StatusShow::None)
 
		return;
 
 
	handleRawPresenceReceived(presence);
 
 
	UserInfo userInfo = user->getUserInfo();
 
 
	pbnetwork::Status status;
 
	status.set_username(user->getJID().toBare());
 
 
	bool isInvisible = presence->getPayload<Swift::InvisiblePayload>() != NULL;
 
	if (isInvisible) {
 
		LOG4CXX_INFO(logger, "This presence is invisible");
 
		status.set_status((pbnetwork::STATUS_INVISIBLE));
 
	}
 
	else {
 
		status.set_status((pbnetwork::StatusType) presence->getShow());
 
	}
 
 
	status.set_statusmessage(presence->getStatus());
 
 
	std::string message;
 
	status.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) {
 
	UserInfo userInfo = user->getUserInfo();
 
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname(nickname);
 
	room.set_room(r);
 
	room.set_password(password);
 
 
	std::string message;
 
	room.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) {
 
	UserInfo userInfo = user->getUserInfo();
 
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname("");
 
	room.set_room(r);
 
	room.set_password("");
 
 
	std::string message;
 
	room.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleUserDestroyed(User *user) {
 
	m_waitingUsers.remove(user);
 
	UserInfo userInfo = user->getUserInfo();
 
 
	user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRawPresenceReceived.disconnect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
 
	user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 
 
	user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 
 
	pbnetwork::Logout logout;
 
	logout.set_user(user->getJID().toBare());
 
	logout.set_legacyname(userInfo.uin);
 
 
	std::string message;
 
	logout.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
	c->users.remove(user);
 
 
	// If backend should handle only one user, it must not accept another one before 
 
	// we kill it, so set up willDie to true
 
	if (c->users.size() == 0 && CONFIG_INT(m_config, "service.users_per_backend") == 1) {
 
		LOG4CXX_INFO(logger, "Backend " << c->id << " will die, because the last user disconnected");
 
		c->willDie = true;
 
	}
 
}
 
 
void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &msg) {
 
	conv->getConversationManager()->getUser()->updateLastActivity();
 
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(msg->getTo()));
 
		if (!msg->getTo().getResource().empty()) {
 
			msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), msg->getTo().getResource()));
 
		}
 
		else {
 
			msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
		}
 
		std::string xml = safeByteArrayToString(m_serializer->serializeElement(msg));
 
		WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
		send(c->connection, xml);
 
		return;
 
	}
 
 
	boost::shared_ptr<Swift::ChatState> statePayload = msg->getPayload<Swift::ChatState>();
 
	if (statePayload) {
 
		pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED;
 
		switch (statePayload->getChatState()) {
 
			case Swift::ChatState::Active:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING;
 
				break;
 
			case Swift::ChatState::Composing:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING;
 
				break;
 
			case Swift::ChatState::Paused:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED;
 
				break;
 
			default:
 
				break;
 
		}
 
		if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) {
 
			pbnetwork::Buddy buddy;
 
			buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
			buddy.set_buddyname(conv->getLegacyName());
 
 
			std::string message;
 
			buddy.SerializeToString(&message);
 
 
			WRAP(message, type);
 
 
			Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
			if (!c) {
 
				return;
 
			}
 
			send(c->connection, message);
 
		}
 
	}
 
 
	boost::shared_ptr<Swift::AttentionPayload> attentionPayload = msg->getPayload<Swift::AttentionPayload>();
 
	if (attentionPayload) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 
 
		std::string message;
 
		m.SerializeToString(&message);
 
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION);
 
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 
 
	if (!msg->getSubject().empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getSubject());
 
 
		std::string message;
 
		m.SerializeToString(&message);
 
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED);
 
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 
	
 
 
	std::string xhtml;
 
	boost::shared_ptr<Swift::XHTMLIMPayload> xhtmlPayload = msg->getPayload<Swift::XHTMLIMPayload>();
 
	if (xhtmlPayload) {
 
		xhtml = xhtmlPayload->getBody();
 
	}
 
 
	// Send normal message
 
	if (!msg->getBody().empty() || !xhtml.empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 
		m.set_xhtml(xhtml);
 
		boost::shared_ptr<Swift::DeliveryReceiptRequest> receiptPayload = msg->getPayload<Swift::DeliveryReceiptRequest>();
 
		if (receiptPayload && !msg->getID().empty()) {
 
			m.set_id(msg->getID());
 
		}
 
 
		std::string message;
 
		m.SerializeToString(&message);
 
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE);
 
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		send(c->connection, message);
 
	}
 
}
 
 
void NetworkPluginServer::handleBuddyRemoved(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
 
	std::string message;
 
	buddy.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) {
 
	User *user = b->getRosterManager()->getUser();
 
 
	dynamic_cast<LocalBuddy *>(b)->setAlias(item.getName());
 
	dynamic_cast<LocalBuddy *>(b)->setGroups(item.getGroups());
 
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
 
	std::string message;
 
	buddy.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) {
 
	handleBuddyUpdated(buddy, item);
 
}
 
 
void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) {
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
 
	std::string message;
 
	buddy.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) {
 
	handleBuddyRemoved(b);
 
}
 
 
void NetworkPluginServer::handleBlockToggled(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
	buddy.set_blocked(!b->isBlocked());
 
 
	std::string message;
 
	buddy.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
 
void NetworkPluginServer::handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> v) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname("");
 
	vcard.set_id(0);
 
	vcard.set_photo(&v->getPhoto()[0], v->getPhoto().size());
 
	vcard.set_nickname(v->getNickname());
 
 
	std::string message;
 
	vcard.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname(name);
 
	vcard.set_id(id);
 
 
	std::string message;
 
	vcard.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(ftID);
 
 
	std::string message;
 
	f.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(0);
 
 
	std::string message;
 
	f.SerializeToString(&message);
 
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH);
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 
 
void NetworkPluginServer::handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id) {
 
	User *user = m_userManager->getUser(userName);
 
	if (!user) {
 
		// TODO: FIXME We have to remove filetransfer when use disconnects
 
		return;
 
	}
 
#if !HAVE_SWIFTEN_3
 
	if (state.state == Swift::FileTransfer::State::Transferring) {
 
		handleFTAccepted(user, buddyName, fileName, size, id);
 
	}
 
	else if (state.state == Swift::FileTransfer::State::Canceled) {
 
		handleFTRejected(user, buddyName, fileName, size);
 
	}
 
#endif
 
}
 
 
void NetworkPluginServer::sendPing(Backend *c) {
 
 
	std::string message;
 
	pbnetwork::WrapperMessage wrap;
 
	wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_PING);
 
	wrap.SerializeToString(&message);
 
 
	if (c->connection) {
 
		LOG4CXX_INFO(logger, "PING to " << c << " (ID=" << c->id << ")");
 
		send(c->connection, message);
 
		c->pongReceived = false;
 
	}
 
// 	LOG4CXX_INFO(logger, "PING to " << c);
 
}
 
 
void NetworkPluginServer::handlePIDTerminated(unsigned long pid) {
 
	std::vector<unsigned long>::iterator log_id_it;
 
	log_id_it = std::find(m_pids.begin(), m_pids.end(), pid);
 
	if (log_id_it != m_pids.end()) {
 
		*log_id_it = 0;
 
	}
 
}
 
 
#ifndef _WIN32
 
 
static int sig_block_count = 0;
 
static sigset_t block_mask;
 
 
static void __block_signals ( void )
 
{
 
  static int init_done = 0;
 
 
  if ( (sig_block_count++) != 1 ) return;
 
 
  if ( init_done == 0 ) {
 
    sigemptyset ( &block_mask );
 
    sigaddset ( &block_mask, SIGPIPE );
 
    sigaddset ( &block_mask, SIGHUP );
 
    sigaddset ( &block_mask, SIGINT );
 
    sigaddset ( &block_mask, SIGQUIT );
 
    sigaddset ( &block_mask, SIGTERM );
 
    sigaddset ( &block_mask, SIGABRT );
 
    sigaddset ( &block_mask, SIGCHLD );
 
    init_done = 1;
 
  }
 
 
  sigprocmask ( SIG_BLOCK, &block_mask, NULL );
 
  return;
 
}
 
 
static void __unblock_signals ( void )
 
{
 
  sigset_t sigset;
 
 
  if ( (sig_block_count--) != 0 ) return;
 
  sigprocmask ( SIG_UNBLOCK, &block_mask, NULL );
 
 
  if ( sigpending ( &sigset ) == 0 ) {
 
    if ( sigismember ( &sigset, SIGCHLD ) ) {
 
      raise ( SIGCHLD );
 
    }
 
  }
 
}
 
 
#endif
 
 
NetworkPluginServer::Backend *NetworkPluginServer::getFreeClient(bool acceptUsers, bool longRun, bool check) {
 
	NetworkPluginServer::Backend *c = NULL;
 
 
	unsigned long diff = CONFIG_INT(m_config, "service.login_delay");
 
	time_t now = time(NULL);
 
	if (diff && (now - m_lastLogin < diff)) {
 
		m_loginTimer->stop();
 
		m_loginTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer((diff - (now - m_lastLogin)) * 1000);
 
		m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this));
 
		m_loginTimer->start();
 
		LOG4CXX_INFO(logger, "Postponing login because of service.login_delay setting");
 
		return NULL;
 
	}
 
 
	if (!check) {
 
		m_lastLogin = time(NULL);
 
	}
 
 
	// Check all backends and find free one
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->willDie == false && (*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*it)->longRun == longRun) {
 
			c = *it;
 
			// if we're not reusing all backends and backend is full, stop accepting new users on this backend
 
			if (!CONFIG_BOOL(m_config, "service.reuse_old_backends")) {
 
				if (!check && c->users.size() + 1 >= CONFIG_INT(m_config, "service.users_per_backend")) {
 
					c->acceptUsers = false;
 
				}
 
			}
 
			break;
 
		}
 
	}
 
 
	// there's no free backend, so spawn one.
 
	if (c == NULL && !m_startingBackend) {
 
		m_isNextLongRun = longRun;
 
		m_startingBackend = true;
 
 
#ifndef _WIN32
 
		__block_signals();
 
#endif
 
		std::vector<unsigned long>::iterator log_id_it;
 
		log_id_it = std::find(m_pids.begin(), m_pids.end(), 0);
 
		std::string log_id = "";
 
		if (log_id_it == m_pids.end()) {
 
			log_id = boost::lexical_cast<std::string>(m_pids.size() + 1);
 
		}
 
		else {
 
			log_id = boost::lexical_cast<std::string>(log_id_it - m_pids.begin() + 1);
 
		}
 
		unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), log_id.c_str(), m_config->getCommandLineArgs().c_str());
 
		if (log_id_it == m_pids.end()) {
 
			m_pids.push_back(pid);
 
		}
 
		else {
 
			*log_id_it = pid;
 
		}
 
#ifndef _WIN32
 
		__unblock_signals();
 
#endif
 
	}
 
 
	return c;
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/networkpluginserver.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/localbuddy.h"
 
#include "transport/config.h"
 
#include "transport/conversation.h"
 
#include "transport/vcardresponder.h"
 
#include "transport/rosterresponder.h"
 
#include "transport/memoryreadbytestream.h"
 
#include "transport/logging.h"
 
#include "transport/admininterface.h"
 
#include "blockresponder.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Network/ConnectionServerFactory.h"
 
#include "Swiften/Elements/AttentionPayload.h"
 
#include "Swiften/Elements/XHTMLIMPayload.h"
 
#include "Swiften/Elements/Delay.h"
 
#include "Swiften/Elements/DeliveryReceipt.h"
 
#include "Swiften/Elements/DeliveryReceiptRequest.h"
 
#include "Swiften/Elements/InvisiblePayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include "transport/protocol.pb.h"
 
#include "transport/util.h"
 
#include "transport/discoitemsresponder.h"
 

	
 
#include "boost/date_time/posix_time/posix_time.hpp"
 
#include "boost/signal.hpp"
 

	
 
#include "transport/utf8.h"
 

	
 
#include <Swiften/FileTransfer/ReadBytestream.h>
 
#include <Swiften/Elements/StreamInitiationFileInfo.h>
 

	
 
#ifdef _WIN32
 
#include "windows.h"
 
#include <stdint.h>
 
#else
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#include <sys/types.h>
 
#include <signal.h>
 
#include "popt.h"
 
#endif
 

	
 
using namespace Transport::Util;
 

	
 
namespace Transport {
 

	
 
static unsigned long backend_id;
 
static unsigned long bytestream_id;
 

	
 
DEFINE_LOGGER(logger, "NetworkPluginServer");
 

	
 
static NetworkPluginServer *_server;
 

	
 
class NetworkConversation : public Conversation {
 
	public:
 
		NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) {
 
		}
 

	
 
		// Called when there's new message to legacy network from XMPP network
 
		void sendMessage(boost::shared_ptr<Swift::Message> &message) {
 
			onMessageToSend(this, message);
 
		}
 

	
 
		boost::signal<void (NetworkConversation *, boost::shared_ptr<Swift::Message> &)> onMessageToSend;
 
};
 

	
 
class NetworkFactory : public Factory {
 
	public:
 
		NetworkFactory(NetworkPluginServer *nps) {
 
			m_nps = nps;
 
		}
 

	
 
		virtual ~NetworkFactory() {}
 

	
 
		// Creates new conversation (NetworkConversation in this case)
 
		Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMuc) {
 
			NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName, isMuc);
 
			nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2));
 
			return nc;
 
		}
 

	
 
		// Creates new LocalBuddy
 
		Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) {
 
			LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags);
 
			if (!buddy->isValid()) {
 
				delete buddy;
 
				return NULL;
 
			}
 
			if (buddyInfo.subscription == "both") {
 
				buddy->setSubscription(Buddy::Both);
 
			}
 
			else {
 
				buddy->setSubscription(Buddy::Ask);
 
			}
 
			if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end())
 
				buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s);
 
			return buddy;
 
		}
 

	
 
	private:
 
		NetworkPluginServer *m_nps;
 
};
 

	
 
// Wraps google protobuf payload into WrapperMessage and serialize it to string
 
#define WRAP(MESSAGE, TYPE) 	pbnetwork::WrapperMessage wrap; \
 
	wrap.set_type(TYPE); \
 
	wrap.set_payload(MESSAGE); \
 
	wrap.SerializeToString(&MESSAGE);
 

	
 
// Executes new backend
 
static unsigned long exec_(const std::string& exePath, const char *host, const char *port, const char *log_id, const char *cmdlineArgs) {
 
	// BACKEND_ID is replaced with unique ID. The ID is increasing for every backend.
 
	std::string finalExePath = boost::replace_all_copy(exePath, "BACKEND_ID", boost::lexical_cast<std::string>(backend_id++));	
 

	
 
#ifdef _WIN32
 
	// Add host and port.
 
	std::ostringstream fullCmdLine;
 
	fullCmdLine << "\"" << finalExePath << "\" --host " << host << " --port " << port;
 

	
 
	if (cmdlineArgs)
 
		fullCmdLine << " " << cmdlineArgs;
 

	
 
	LOG4CXX_INFO(logger, "Starting new backend " << fullCmdLine.str());
 

	
 
	// We must provide a non-const buffer to CreateProcess below
 
	std::vector<wchar_t> rawCommandLineArgs( fullCmdLine.str().size() + 1 );
 
	wcscpy_s(&rawCommandLineArgs[0], rawCommandLineArgs.size(), utf8ToUtf16(fullCmdLine.str()).c_str());
 

	
 
	STARTUPINFO         si;
 
	PROCESS_INFORMATION pi;
 

	
 
	ZeroMemory (&si, sizeof(si));
 
	si.cb=sizeof (si);
 

	
 
	if (! CreateProcess(
 
		utf8ToUtf16(finalExePath).c_str(),
 
		&rawCommandLineArgs[0],
 
		0,                    // process attributes
 
		0,                    // thread attributes
 
		0,                    // inherit handles
 
		0,                    // creation flags
 
		0,                    // environment
 
		0,                    // cwd
 
		&si,
 
		&pi
 
		)
 
	)  {
 
		LOG4CXX_ERROR(logger, "Could not start process");
 
	}
 

	
 
	return 0;
 
#else
 
	// Add host and port.
 
	finalExePath += std::string(" --host ") + host + " --port " + port + " --service.backend_id=" + log_id + " " + cmdlineArgs;
 
	LOG4CXX_INFO(logger, "Starting new backend " << finalExePath);
 

	
 
	// Create array of char * from string using -lpopt library
 
	char *p = (char *) malloc(finalExePath.size() + 1);
 
	strcpy(p, finalExePath.c_str());
 
	int argc;
 
	char **argv;
 
	poptParseArgvString(p, &argc, (const char ***) &argv);
 

	
 
	// fork and exec
 
	pid_t pid = fork();
 
	if ( pid == 0 ) {
 
		setsid();
 
		// close all files
 
		int maxfd=sysconf(_SC_OPEN_MAX);
 
		for(int fd=3; fd<maxfd; fd++) {
 
			close(fd);
 
		}
 
		// child process
 
		errno = 0;
 
		int ret = execv(argv[0], argv);
 
		if (ret == -1) {
 
			exit(errno);
 
		}
 
		exit(0);
 
	} else if ( pid < 0 ) {
 
		LOG4CXX_ERROR(logger, "Fork failed");
 
	}
 
	free(p);
 

	
 
	return (unsigned long) pid;
 
#endif
 
}
 

	
 
#ifndef _WIN32
 
static void SigCatcher(int n) {
 
	pid_t result;
 
	int status;
 
	// Read exit code from all children to not have zombies arround
 
	// WARNING: Do not put LOG4CXX_ here, because it can lead to deadlock
 
	while ((result = waitpid(-1, &status, WNOHANG)) > 0) {
 
		if (result != 0) {
 
			_server->handlePIDTerminated((unsigned long)result);
 
			if (WIFEXITED(status)) {
 
				if (WEXITSTATUS(status) != 0) {
 
// 					LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status));
 
				}
 
			}
 
			else {
 
// 				LOG4CXX_ERROR(logger, "Backend can not be started");
 
			}
 
		}
 
	}
 
}
 
#endif
 

	
 
static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payload) {
 
	// Set alias only if it's not empty. Backends are allowed to send empty alias if it has
 
	// not changed.
 
	if (!payload.alias().empty()) {
 
		buddy->setAlias(payload.alias());
 
	}
 

	
 
	// Change groups if it's not empty. The same as above...
 
	std::vector<std::string> groups;
 
	for (int i = 0; i < payload.group_size(); i++) {
 
		std::string group;
 
		utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), std::back_inserter(group), '_');
 
		groups.push_back(group);
 
	}
 
	if (!groups.empty()) {
 
		buddy->setGroups(groups);
 
	}
 

	
 
	buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
	buddy->setIconHash(payload.iconhash());
 
	buddy->setBlocked(payload.blocked());
 
}
 

	
 
NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager, DiscoItemsResponder *discoItemsResponder) {
 
	_server = this;
 
	m_ftManager = ftManager;
 
	m_userManager = userManager;
 
	m_config = config;
 
	m_component = component;
 
	m_isNextLongRun = false;
 
	m_adminInterface = NULL;
 
	m_startingBackend = false;
 
	m_lastLogin = 0;
 
	m_xmppParser = new Swift::XMPPParser(this, &m_collection, component->getNetworkFactories()->getXMLParserFactory());
 
	m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
 
#if HAVE_SWIFTEN_3
 
	m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType, false);
 
#else
 
	m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType);
 
#endif
 
	m_discoItemsResponder = discoItemsResponder;
 
	m_component->m_factory = new NetworkFactory(this);
 
	m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1));
 
	m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1));
 

	
 
	m_component->onRawIQReceived.connect(boost::bind(&NetworkPluginServer::handleRawIQReceived, this, _1));
 

	
 
	m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000);
 
	m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this));
 
	m_pingTimer->start();
 

	
 
	m_loginTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(config, "service.login_delay") * 1000);
 
	m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this));
 
	m_loginTimer->start();
 

	
 
	if (CONFIG_INT(m_config, "service.memory_collector_time") != 0) {
 
		m_collectTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(CONFIG_INT(m_config, "service.memory_collector_time"));
 
		m_collectTimer->onTick.connect(boost::bind(&NetworkPluginServer::collectBackend, this));
 
		m_collectTimer->start();
 
	}
 

	
 
	m_vcardResponder = new VCardResponder(component->getIQRouter(), component->getNetworkFactories(), userManager);
 
	m_vcardResponder->onVCardRequired.connect(boost::bind(&NetworkPluginServer::handleVCardRequired, this, _1, _2, _3));
 
	m_vcardResponder->onVCardUpdated.connect(boost::bind(&NetworkPluginServer::handleVCardUpdated, this, _1, _2));
 
	m_vcardResponder->start();
 

	
 
	m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager);
 
	m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2));
 
	m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1));
 
	m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2));
 
	m_rosterResponder->start();
 

	
 
	m_blockResponder = new BlockResponder(component->getIQRouter(), userManager);
 
	m_blockResponder->onBlockToggled.connect(boost::bind(&NetworkPluginServer::handleBlockToggled, this, _1));
 
	m_blockResponder->start();
 

	
 
	m_server = component->getNetworkFactories()->getConnectionServerFactory()->createConnectionServer(Swift::HostAddress(CONFIG_STRING(m_config, "service.backend_host")), boost::lexical_cast<int>(CONFIG_STRING(m_config, "service.backend_port")));
 
	m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1));
 
}
 

	
 
NetworkPluginServer::~NetworkPluginServer() {
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		LOG4CXX_INFO(logger, "Stopping backend " << *it);
 
		std::string message;
 
		pbnetwork::WrapperMessage wrap;
 
		wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT);
 
		wrap.SerializeToString(&message);
 

	
 
		Backend *c = (Backend *) *it;
 
		send(c->connection, message);
 
	}
 

	
 
	m_pingTimer->stop();
 
	m_server->stop();
 
	m_server.reset();
 
	delete m_component->m_factory;
 
	delete m_vcardResponder;
 
	delete m_rosterResponder;
 
	delete m_blockResponder;
 
}
 

	
 
void NetworkPluginServer::start() {
 
	m_server->start();
 

	
 
	LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port"));
 

	
 
	while (true) {
 
		unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), "1", m_config->getCommandLineArgs().c_str());
 
		LOG4CXX_INFO(logger, "Tried to spawn first backend with pid " << pid);
 
		LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects");
 

	
 
#ifndef _WIN32
 
		// wait if the backend process will still be alive after 1 second
 
		sleep(1);
 
		pid_t result;
 
		int status;
 
		result = waitpid(-1, &status, WNOHANG);
 
		if (result != 0) {
 
			if (WIFEXITED(status)) {
 
				if (WEXITSTATUS(status) != 0) {
 
					if (status == 254) {
 
						LOG4CXX_ERROR(logger, "Backend can not be started, because it needs database to store data, but the database backend is not configured.");
 
					}
 
					else {
 
						LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status)));
 
						if (WEXITSTATUS(status) == ENOENT) {
 
							LOG4CXX_ERROR(logger, "This usually means the path to backend executable defined in config file as '[service] backend=\"...\"' is wrong or the executable does not exists.");
 
						}
 
						
 
					}
 
					LOG4CXX_ERROR(logger, "Check backend log for more details");
 
					continue;
 
				}
 
			}
 
			else {
 
				LOG4CXX_ERROR(logger, "Backend can not be started");
 
				continue;
 
			}
 
		}
 

	
 
		m_pids.push_back(pid);
 

	
 
		signal(SIGCHLD, SigCatcher);
 
#endif
 
		// quit the while loop
 
		break;
 
	}
 
}
 

	
 
void NetworkPluginServer::loginDelayFinished() {
 
	m_loginTimer->stop();
 
	connectWaitingUsers();
 
}
 

	
 
void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Connection> c) {
 
	// Create new Backend instance
 
	Backend *client = new Backend;
 
	client->pongReceived = -1;
 
	client->connection = c;
 
	client->res = 0;
 
	client->init_res = 0;
 
	client->shared = 0;
 
	// Until we receive first PONG from backend, backend is in willDie state.
 
	client->willDie = true;
 
	// Backend does not accept new clients automatically if it's long-running
 
	client->acceptUsers = !m_isNextLongRun;
 
	client->longRun = m_isNextLongRun;
 

	
 
	m_startingBackend = false;
 

	
 
	LOG4CXX_INFO(logger, "New" + (client->longRun ? std::string(" long-running") : "") +  " backend " << client << " connected. Current backend count=" << (m_clients.size() + 1));
 

	
 
	m_clients.push_front(client);
 

	
 
	c->onDisconnected.connect(boost::bind(&NetworkPluginServer::handleSessionFinished, this, client));
 
	c->onDataRead.connect(boost::bind(&NetworkPluginServer::handleDataRead, this, client, _1));
 
	sendPing(client);
 

	
 
	// sendPing sets pongReceived to 0, but we want to have it -1 to ignore this backend
 
	// in first ::pingTimeout call, because it can be called right after this function
 
	// and backend wouldn't have any time to response to ping.
 
	client->pongReceived = -1;
 
}
 

	
 
void NetworkPluginServer::handleSessionFinished(Backend *c) {
 
	LOG4CXX_INFO(logger, "Backend " << c << " (ID=" << c->id << ") disconnected. Current backend count=" << (m_clients.size() - 1));
 

	
 
	// This backend will do, so we can't reconnect users to it in User::handleDisconnected call
 
	c->willDie = true;
 

	
 
	// If there are users associated with this backend, it must have crashed, so print error output
 
	// and disconnect users
 
	if (!c->users.empty()) {
 
		m_crashedBackends.push_back(c->id);
 
	}
 

	
 
	for (std::list<User *>::const_iterator it = c->users.begin(); it != c->users.end(); it++) {
 
		LOG4CXX_ERROR(logger, "Backend " << c << " (ID=" << c->id << ") disconnected (probably crashed) with active user " << (*it)->getJID().toString());
 
		(*it)->setData(NULL);
 
		(*it)->handleDisconnected("Internal Server Error, please reconnect.");
 
	}
 

	
 
	std::string message;
 
	pbnetwork::WrapperMessage wrap;
 
	wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT);
 
	wrap.SerializeToString(&message);
 

	
 
	send(c->connection, message);
 

	
 
	c->connection->onDisconnected.disconnect_all_slots();
 
	c->connection->onDataRead.disconnect_all_slots();
 
	c->connection->disconnect();
 
	c->connection.reset();
 

	
 
	m_clients.remove(c);
 
	delete c;
 
}
 

	
 
void NetworkPluginServer::handleConnectedPayload(const std::string &data) {
 
	pbnetwork::Connected payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.user());
 
	if (!user) {
 
		LOG4CXX_ERROR(logger, "Connected payload received for unknown user " << payload.user());
 
		return;
 
	}
 

	
 
	user->setConnected(true);
 
	m_component->m_userRegistry->onPasswordValid(payload.user());
 
}
 

	
 
void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) {
 
	pbnetwork::Disconnected payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	m_component->m_userRegistry->onPasswordInvalid(payload.user(), payload.message());
 

	
 
	User *user = m_userManager->getUser(payload.user());
 
	if (!user) {
 
		return;
 
	}
 
	user->handleDisconnected(payload.message(), (Swift::SpectrumErrorPayload::Error) payload.error());
 
}
 

	
 
void NetworkPluginServer::handleVCardPayload(const std::string &data) {
 
	pbnetwork::VCard payload;
 
	if (payload.ParseFromString(data) == false) {
 
		std::cout << "PARSING ERROR\n";
 
		// TODO: ERROR
 
		return;
 
	}
 
	std::string field;
 

	
 
	boost::shared_ptr<Swift::VCard> vcard(new Swift::VCard());
 

	
 
	utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), std::back_inserter(field), '_');
 
	vcard->setFullName(field);
 

	
 
	field.clear();
 

	
 
	utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), std::back_inserter(field), '_');
 
	vcard->setNickname(field);
 

	
 
	vcard->setPhoto(Swift::createByteArray(payload.photo()));
 

	
 
	m_vcardResponder->sendVCard(payload.id(), vcard);
 
}
 

	
 
void NetworkPluginServer::handleAuthorizationPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	// Create subscribe presence and forward it to XMPP side
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(user->getJID());
 
	std::string name = payload.buddyname();
 

	
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
		name = Swift::JID::getEscapedNode(name);
 
	}
 
	else {
 
		if (name.find_last_of("@") != std::string::npos) {
 
			name.replace(name.find_last_of("@"), 1, "%");
 
		}
 
	}
 

	
 
	response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
	response->setType(Swift::Presence::Subscribe);
 
	m_component->getStanzaChannel()->sendPresence(response);
 
}
 

	
 
void NetworkPluginServer::handleChatStatePayload(const std::string &data, Swift::ChatState::ChatStateType type) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	// We're not creating new Conversation just because of chatstates.
 
	// Some networks/clients spams with chatstates a lot and it leads to bigger memory usage.
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	// Forward chatstate
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::ChatState>(type));
 

	
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
	if (buddy) {
 
		handleBuddyPayload(buddy, payload);
 
	}
 
	else {
 
		if (payload.buddyname() == user->getUserInfo().uin) {
 
			return;
 
		}
 

	
 
		std::vector<std::string> groups;
 
		for (int i = 0; i < payload.group_size(); i++) {
 
			groups.push_back(payload.group(i));
 
		}
 
		if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_JID_ESCAPING);
 
		}
 
		else {
 
			buddy = new LocalBuddy(user->getRosterManager(), -1, payload.buddyname(), payload.alias(), groups, BUDDY_NO_FLAG);
 
		}
 
		if (!buddy->isValid()) {
 
			delete buddy;
 
			return;
 
		}
 

	
 
		buddy->setBlocked(payload.blocked());
 
		user->getRosterManager()->setBuddy(buddy);
 
		buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
		buddy->setIconHash(payload.iconhash());
 
	}
 
}
 

	
 
void NetworkPluginServer::handleBuddyRemovedPayload(const std::string &data) {
 
	pbnetwork::Buddy payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	user->getRosterManager()->removeBuddy(payload.buddyname());
 
}
 

	
 
void NetworkPluginServer::handleParticipantChangedPayload(const std::string &data) {
 
	pbnetwork::Participant payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
 
}
 

	
 
void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) {
 
	pbnetwork::Room payload;
 
	if (payload.ParseFromString(data) == false) {
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.room());
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	conv->setNickname(payload.nickname());
 
}
 

	
 
void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool subject) {
 
	pbnetwork::ConversationMessage payload;
 

	
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	// Message from legacy network triggers network acticity
 
	user->updateLastActivity();
 

	
 
	// Set proper body.
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	if (subject) {
 
		msg->setSubject(payload.message());
 
	}
 
	else {
 
		msg->setBody(payload.message());
 
	}
 

	
 
	if (payload.headline()) {
 
		msg->setType(Swift::Message::Headline);
 
	}
 

	
 
	// Add xhtml-im payload.
 
	if (CONFIG_BOOL(m_config, "service.enable_xhtml") && !payload.xhtml().empty()) {
 
		msg->addPayload(boost::make_shared<Swift::XHTMLIMPayload>(payload.xhtml()));
 
	}
 

	
 
	if (!payload.timestamp().empty()) {
 
		boost::posix_time::ptime timestamp = boost::posix_time::from_iso_string(payload.timestamp());
 
		boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
 
		delay->setStamp(timestamp);
 
		msg->addPayload(delay);
 
	}
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 

	
 
	// We can't create Conversation for payload with nickname, because this means the message is from room,
 
	// but this user is not in any room, so it's OK to just reject this message
 
	if (!conv && !payload.nickname().empty()) {
 
		return;
 
	}
 

	
 
	if (conv && payload.pm()) {
 
		conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname());
 
		if (!conv) {
 
			conv = new NetworkConversation(user->getConversationManager(), payload.nickname());
 
			std::string name = payload.buddyname();
 
			conv->setRoom(name);
 
			conv->setNickname(payload.buddyname() + "/" + payload.nickname());
 

	
 
			user->getConversationManager()->addConversation(conv);
 
			conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
		}
 
	}
 

	
 
	// Create new Conversation if it does not exist
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 

	
 
	// Forward it
 
	conv->handleMessage(msg, payload.nickname());
 
	m_userManager->messageToXMPPSent();
 
}
 

	
 
void NetworkPluginServer::handleConvMessageAckPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 

	
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	if (payload.id().empty()) {
 
		LOG4CXX_WARN(logger, "Received message ack with empty ID, not forwarding to XMPP.");
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->addPayload(boost::make_shared<Swift::DeliveryReceipt>(payload.id()));
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 

	
 
	// Receipts don't create conversation
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	// Forward it
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleAttentionPayload(const std::string &data) {
 
	pbnetwork::ConversationMessage payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.message());
 
	msg->addPayload(boost::make_shared<Swift::AttentionPayload>());
 

	
 
	// Attentions trigger new Conversation creation
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
 
	if (!conv) {
 
		conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
 
		user->getConversationManager()->addConversation(conv);
 
		conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	}
 

	
 
	conv->handleMessage(msg);
 
}
 

	
 
void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data) {
 
	pbnetwork::Stats payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	c->res = payload.res();
 
	c->init_res = payload.init_res();
 
	c->shared = payload.shared();
 
	c->id = payload.id();
 
}
 

	
 
void NetworkPluginServer::handleFTStartPayload(const std::string &data) {
 
	pbnetwork::File payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(payload.username());
 
	if (!user)
 
		return;
 

	
 
	LOG4CXX_INFO(logger, "handleFTStartPayload " << payload.filename() << " " << payload.buddyname());
 
	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname());
 
	if (!buddy) {
 
		// TODO: escape? reject?
 
		return;
 
	}
 

	
 
	Swift::StreamInitiationFileInfo fileInfo;
 
	fileInfo.setSize(payload.size());
 
	fileInfo.setName(payload.filename());
 

	
 
	Backend *c = (Backend *) user->getData();
 
	boost::shared_ptr<MemoryReadBytestream> bytestream(new MemoryReadBytestream(payload.size()));
 
	bytestream->onDataNeeded.connect(boost::bind(&NetworkPluginServer::handleFTDataNeeded, this, c, bytestream_id + 1));
 

	
 
	LOG4CXX_INFO(logger, "jid=" << buddy->getJID());
 

	
 
	FileTransferManager::Transfer transfer = m_ftManager->sendFile(user, buddy, bytestream, fileInfo);
 
	if (!transfer.ft) {
 
		handleFTRejected(user, payload.buddyname(), payload.filename(), payload.size());
 
		return;
 
	}
 

	
 
	m_filetransfers[++bytestream_id] = transfer;
 
#if !HAVE_SWIFTEN_3
 
	transfer.ft->onStateChange.connect(boost::bind(&NetworkPluginServer::handleFTStateChanged, this, _1, payload.username(), payload.buddyname(), payload.filename(), payload.size(), bytestream_id));
 
	transfer.ft->start();
 
#endif
 
}
 

	
 
void NetworkPluginServer::handleFTFinishPayload(const std::string &data) {
 
	pbnetwork::File payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	if (payload.has_ftid()) {
 
		if (m_filetransfers.find(payload.ftid()) != m_filetransfers.end()) {
 
			FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
 
			transfer.ft->cancel();
 
		}
 
		else {
 
			LOG4CXX_ERROR(logger, "FTFinishPayload for unknown ftid=" << payload.ftid());
 
		}
 
	}
 

	
 
}
 

	
 
void NetworkPluginServer::handleFTDataPayload(Backend *b, const std::string &data) {
 
	pbnetwork::FileTransferData payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
// 	User *user = m_userManager->getUser(payload.username());
 
// 	if (!user)
 
// 		return;
 

	
 
	if (m_filetransfers.find(payload.ftid()) == m_filetransfers.end()) {
 
		LOG4CXX_ERROR(logger, "Uknown filetransfer with id " << payload.ftid());
 
		return;
 
	}
 

	
 
	FileTransferManager::Transfer &transfer = m_filetransfers[payload.ftid()];
 
	MemoryReadBytestream *bytestream = (MemoryReadBytestream *) transfer.readByteStream.get();
 

	
 
	if (bytestream->appendData(payload.data()) > 5000000) {
 
		pbnetwork::FileTransferData f;
 
		f.set_ftid(payload.ftid());
 
		f.set_data("");
 

	
 
		std::string message;
 
		f.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_PAUSE);
 

	
 
		send(b->connection, message);
 
	}
 
}
 

	
 
void NetworkPluginServer::handleFTDataNeeded(Backend *b, unsigned long ftid) {
 
	pbnetwork::FileTransferData f;
 
	f.set_ftid(ftid);
 
	f.set_data("");
 

	
 
	std::string message;
 
	f.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_CONTINUE);
 

	
 
	send(b->connection, message);
 
}
 

	
 
void NetworkPluginServer::connectWaitingUsers() {
 
	// some users are in queue waiting for this backend
 
	while(!m_waitingUsers.empty()) {
 
		// There's no new backend, so stop associating users and wait for new backend,
 
		// which has been already spawned in getFreeClient() call.
 
		if (getFreeClient(true, false, true) == NULL)
 
			break;
 

	
 
		User *u = m_waitingUsers.front();
 
		m_waitingUsers.pop_front();
 

	
 
		LOG4CXX_INFO(logger, "Associating " << u->getJID().toString() << " with this backend");
 

	
 
		// associate backend with user
 
		handleUserCreated(u);
 

	
 
		// connect user if it's ready
 
		if (u->isReadyToConnect()) {
 
			handleUserReadyToConnect(u);
 
		}
 
	}
 
}
 

	
 
void NetworkPluginServer::handlePongReceived(Backend *c) {
 
	// This could be first PONG from the backend
 
	if (c->pongReceived == -1) {
 
		// Backend is fully ready to handle requests
 
		c->willDie = false;
 

	
 
		if (m_clients.size() == 1) {
 
			// first backend connected, start the server, we're ready.
 
			m_component->start();
 
		}
 

	
 
		connectWaitingUsers();
 
	}
 

	
 
	c->pongReceived = true;
 
}
 

	
 
void NetworkPluginServer::handleQueryPayload(Backend *b, const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	if (!m_adminInterface) {
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(payload.config());
 
	m_adminInterface->handleQuery(msg);
 

	
 
	pbnetwork::BackendConfig response;
 
	response.set_config(msg->getBody());
 

	
 
	std::string message;
 
	response.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_QUERY);
 

	
 
	send(b->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBackendConfigPayload(const std::string &data) {
 
	pbnetwork::BackendConfig payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	m_config->updateBackendConfig(payload.config());
 
}
 

	
 
void NetworkPluginServer::handleRoomListPayload(const std::string &data) {
 
	pbnetwork::RoomList payload;
 
	if (payload.ParseFromString(data) == false) {
 
		// TODO: ERROR
 
		return;
 
	}
 

	
 
	m_discoItemsResponder->clearRooms();
 
	for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) {
 
		m_discoItemsResponder->addRoom(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i));
 
	}
 
}
 
#if HAVE_SWIFTEN_3
 
void NetworkPluginServer::handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
 
#else
 
void NetworkPluginServer::handleElement(boost::shared_ptr<Swift::Element> element) {
 
#endif
 
	boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
 
	if (!stanza) {
 
		return;
 
	}
 

	
 
	User *user = m_userManager->getUser(stanza->getTo().toBare());
 
	if (!user)
 
		return;
 

	
 
	Swift::JID originalJID = stanza->getFrom();
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(originalJID.toBare());
 

	
 
	LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(stanza->getFrom().toBare());
 
	if (buddy) {
 
		const Swift::JID &jid = buddy->getJID();
 
		if (stanza->getFrom().getResource().empty()) {
 
			stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain()));
 
		}
 
		else {
 
			stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain(), stanza->getFrom().getResource()));
 
		}
 
	}
 
	else {
 
		std::string name = stanza->getFrom().toBare();
 
		if (conv && conv->isMUC()) {
 
			if (name.find_last_of("@") != std::string::npos) {
 
				name.replace(name.find_last_of("@"), 1, "%");
 
			}
 
		}
 
		else {
 
			if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
 
				name = Swift::JID::getEscapedNode(name);
 
			}
 
			else {
 
				if (name.find_last_of("@") != std::string::npos) {
 
					name.replace(name.find_last_of("@"), 1, "%");
 
				}
 
			}
 
		}
 
		if (stanza->getFrom().getResource().empty()) {
 
			stanza->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
		}
 
		else {
 
			stanza->setFrom(Swift::JID(name, m_component->getJID().toString(), stanza->getFrom().getResource()));
 
		}
 
	}
 

	
 
	boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
 
	if (message) {
 
		if (conv) {
 
			conv->handleRawMessage(message);
 
			return;
 
		}
 

	
 
		m_component->getStanzaChannel()->sendMessage(message);
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
 
	if (presence) {
 
		if (buddy) {
 
			if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) {
 
				buddy->m_status.setType(Swift::StatusShow::Online);
 
			}
 
			buddy->handleRawPresence(presence);
 
		}
 
		else if (conv) {
 
			conv->handleRawPresence(presence);
 
		}
 
		else {
 
			m_component->getStanzaChannel()->sendPresence(presence);
 
		}
 

	
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
 
	if (iq) {
 
		if (m_id2resource.find(stanza->getTo().toBare().toString() + stanza->getID()) != m_id2resource.end()) {
 
			iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_id2resource[stanza->getTo().toBare().toString() + stanza->getID()]));
 
			m_id2resource.erase(stanza->getTo().toBare().toString() + stanza->getID());
 
		}
 
		else {
 
			Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(user->getJID());
 
			if (highest) {
 
			    iq->setTo(highest->getFrom());
 
			} else {
 
			    iq->setTo(user->getJID());
 
			}
 
		}
 
		m_component->getIQRouter()->sendIQ(iq);
 
		return;
 
	}
 
}
 

	
 
void NetworkPluginServer::handleRawXML(const std::string &xml) {
 
	m_xmppParser->parse(xml);
 
}
 

	
 
void NetworkPluginServer::handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence) {
 
	User *user = m_userManager->getUser(presence->getFrom().toBare());
 
	if (!user)
 
		return;
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 

	
 
	Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(presence->getTo()));
 
	if (!presence->getTo().getResource().empty()) {
 
		presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), presence->getTo().getResource()));
 
	}
 
	else {
 
		presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
	}
 

	
 
	std::string xml = safeByteArrayToString(m_serializer->serializeElement(presence));
 
	WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
	send(c->connection, xml);
 
}
 

	
 
void NetworkPluginServer::handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq) {
 
	User *user = m_userManager->getUser(iq->getFrom().toBare());
 
	if (!user)
 
		return;
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 

	
 
	if (iq->getType() == Swift::IQ::Get) {
 
		m_id2resource[iq->getFrom().toBare().toString() + iq->getID()] = iq->getFrom().getResource();
 
	}
 

	
 
	Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(iq->getTo()));
 
	if (!iq->getTo().getResource().empty()) {
 
		iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), iq->getTo().getResource()));
 
	}
 
	else {
 
		iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
	}
 

	
 
	std::string xml = safeByteArrayToString(m_serializer->serializeElement(iq));
 
	WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
	send(c->connection, xml);
 
}
 

	
 
void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data) {
 
	// Append data to buffer
 
	c->data.insert(c->data.end(), data->begin(), data->end());
 

	
 
	// Parse data while there are some
 
	while (c->data.size() != 0) {
 
		// expected_size of wrapper message
 
		unsigned int expected_size;
 

	
 
		// if data is >= 4, we have whole header and we can
 
		// read expected_size.
 
		if (c->data.size() >= 4) {
 
			expected_size = *((unsigned int*) &c->data[0]);
 
			expected_size = ntohl(expected_size);
 
			// If we don't have whole wrapper message, wait for next
 
			// handleDataRead call.
 
			if (c->data.size() - 4 < expected_size)
 
				return;
 
		}
 
		else {
 
			return;
 
		}
 

	
 
		// Parse wrapper message and erase it from buffer.
 
		pbnetwork::WrapperMessage wrapper;
 
		if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) {
 
			std::cout << "PARSING ERROR " << expected_size << "\n";
 
			c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 
			continue;
 
		}
 
		c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size);
 

	
 
		// If backend is slow and it is sending us lot of message, there is possibility
 
		// that we don't receive PONG response before timeout. However, if we received
 
		// at least some data, it means backend is not dead and we can treat it as
 
		// PONG received event.
 
		if (c->pongReceived == false) {
 
			c->pongReceived = true;
 
		}
 

	
 
		// Handle payload in wrapper message
 
		switch(wrapper.type()) {
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONNECTED:
 
				handleConnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_DISCONNECTED:
 
				handleDisconnectedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED:
 
				handleBuddyChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE:
 
				handleConvMessagePayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED:
 
				handleConvMessagePayload(wrapper.payload(), true);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PONG:
 
				handlePongReceived(c);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_PARTICIPANT_CHANGED:
 
				handleParticipantChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_NICKNAME_CHANGED:
 
				handleRoomChangedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_VCARD:
 
				handleVCardPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Composing);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST:
 
				handleAuthorizationPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION:
 
				handleAttentionPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_STATS:
 
				handleStatsPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_START:
 
				handleFTStartPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH:
 
				handleFTFinishPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_FT_DATA:
 
				handleFTDataPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED:
 
				handleBuddyRemovedPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_QUERY:
 
				handleQueryPayload(c, wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BACKEND_CONFIG:
 
				handleBackendConfigPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ROOM_LIST:
 
				handleRoomListPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK:
 
				handleConvMessageAckPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML:
 
				handleRawXML(wrapper.payload());
 
				break;
 
			default:
 
				return;
 
		}
 
	}
 
}
 

	
 
void NetworkPluginServer::send(boost::shared_ptr<Swift::Connection> &c, const std::string &data) {
 
	// generate header - size of wrapper message
 
	uint32_t size = htonl(data.size());
 
	char *header = (char *) &size;
 

	
 
	// send header together with wrapper message
 
	c->write(Swift::createSafeByteArray(std::string(header, 4) + data));
 
}
 

	
 
void NetworkPluginServer::pingTimeout() {
 
	// TODO: move to separate timer, those 2 loops could be expensive
 
	// Some users are connected for weeks and they are blocking backend to be destroyed and its memory
 
	// to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and
 
	// reconnect them to long-running backend, where they can idle hapilly till the end of ages.
 
	time_t now = time(NULL);
 
	std::vector<User *> usersToMove;
 
	unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time");
 
	if (diff != 0) {
 
		for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
			// Users from long-running backends can't be moved
 
			if ((*it)->longRun) {
 
				continue;
 
			}
 

	
 
			// Find users which are inactive for more than 'diff'
 
			BOOST_FOREACH(User *u, (*it)->users) {
 
				if (now - u->getLastActivity() > diff) {
 
					usersToMove.push_back(u);
 
				}
 
			}
 
		}
 

	
 
		// Move inactive users to long-running backend.
 
		BOOST_FOREACH(User *u, usersToMove) {
 
			LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
 
			if (!moveToLongRunBackend(u))
 
				break;
 
		}
 
	}
 

	
 
	// We have to remove startingBackend flag otherwise 1 broken backend start could
 
	// block the backend.
 
	m_startingBackend = false;
 

	
 
	// check ping responses
 
	std::vector<Backend *> toRemove;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		// pong has been received OR backend just connected and did not have time to answer the ping
 
		// request.
 
		if ((*it)->pongReceived || (*it)->pongReceived == -1) {
 
			// Don't send another ping if pongReceived == -1, because we've already sent one
 
			// when registering backend.
 
			if ((*it)->pongReceived) {
 
				sendPing((*it));
 
			}
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). PING response not received.");
 
			toRemove.push_back(*it);
 

	
 
#ifndef WIN32
 
			// generate coredump for this backend to find out why it wasn't able to respond to PING
 
			std::string pid = (*it)->id;
 
			if (!pid.empty()) {
 
				try {
 
					kill(boost::lexical_cast<int>(pid), SIGABRT);
 
				}
 
				catch (...) { }
 
			}
 
#endif
 
		}
 

	
 
		if ((*it)->users.size() == 0) {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users.");
 
			toRemove.push_back(*it);
 
		}
 
	}
 

	
 
	BOOST_FOREACH(Backend *b, toRemove) {
 
		handleSessionFinished(b);
 
	}
 

	
 
	m_pingTimer->start();
 
}
 

	
 
void NetworkPluginServer::collectBackend() {
 
	// Stop accepting new users to backend with the biggest memory usage. This prevents backends
 
	// which are leaking to eat whole memory by connectin new users to legacy network.
 
	LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die");
 
	unsigned long max = 0;
 
	Backend *backend = NULL;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->res > max) {
 
			max = (*it)->res;
 
			backend = (*it);
 
		}
 
	}
 

	
 
	if (backend) {
 
		if (m_collectTimer) {
 
			m_collectTimer->start();
 
		}
 
		LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die");
 
		backend->acceptUsers = false;
 
	}
 
}
 

	
 
bool NetworkPluginServer::moveToLongRunBackend(User *user) {
 
	// Check if user has already some backend
 
	Backend *old = (Backend *) user->getData();
 
	if (!old) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// if he's already on long run, do nothing
 
	if (old->longRun) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// Get free longrun backend, if there's no longrun backend, create one and wait
 
	// for its connection
 
	Backend *backend = getFreeClient(false, true);
 
	if (!backend) {
 
		LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later");
 
		return false;
 
	}
 

	
 
	// old backend will trigger disconnection which has to be ignored to keep user online
 
	user->setIgnoreDisconnect(true);
 

	
 
	// remove user from the old backend
 
	// If backend is empty, it will be collected by pingTimeout
 
	old->users.remove(user);
 

	
 
	// switch to new backend and connect
 
	user->setData(backend);
 
	backend->users.push_back(user);
 

	
 
	// connect him
 
	handleUserReadyToConnect(user);
 
	return true;
 
}
 

	
 
void NetworkPluginServer::handleUserCreated(User *user) {
 
	// Get free backend to handle this user or spawn new one if there's no free one.
 
	Backend *c = getFreeClient();
 

	
 
	// Add user to queue if there's no free backend to handle him so far.
 
	if (!c) {
 
		LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue.");
 
		m_waitingUsers.push_back(user);
 
		return;
 
	}
 

	
 
	// Associate users with backend
 
	user->setData(c);
 
	c->users.push_back(user);
 

	
 
	// Don't forget to disconnect these in handleUserDestroyed!!!
 
	user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRawPresenceReceived.connect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
 
	user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 

	
 
	user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 
}
 

	
 
void NetworkPluginServer::handleUserReadyToConnect(User *user) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Login login;
 
	login.set_user(user->getJID().toBare());
 
	login.set_legacyname(userInfo.uin);
 
	login.set_password(userInfo.password);
 

	
 
	std::string message;
 
	login.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
	if (presence->getShow() == Swift::StatusShow::None)
 
		return;
 

	
 
	handleRawPresenceReceived(presence);
 

	
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Status status;
 
	status.set_username(user->getJID().toBare());
 

	
 
	bool isInvisible = presence->getPayload<Swift::InvisiblePayload>() != NULL;
 
	if (isInvisible) {
 
		LOG4CXX_INFO(logger, "This presence is invisible");
 
		status.set_status((pbnetwork::STATUS_INVISIBLE));
 
	}
 
	else {
 
		status.set_status((pbnetwork::StatusType) presence->getShow());
 
	}
 

	
 
	status.set_statusmessage(presence->getStatus());
 

	
 
	std::string message;
 
	status.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname(nickname);
 
	room.set_room(r);
 
	room.set_password(password);
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname("");
 
	room.set_room(r);
 
	room.set_password("");
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserDestroyed(User *user) {
 
	m_waitingUsers.remove(user);
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
 
	user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
 
	user->onRawPresenceReceived.disconnect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
 
	user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
 
	user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 

	
 
	user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
 
	user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
 

	
 
	pbnetwork::Logout logout;
 
	logout.set_user(user->getJID().toBare());
 
	logout.set_legacyname(userInfo.uin);
 

	
 
	std::string message;
 
	logout.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
	c->users.remove(user);
 

	
 
	// If backend should handle only one user, it must not accept another one before 
 
	// we kill it, so set up willDie to true
 
	if (c->users.size() == 0 && CONFIG_INT(m_config, "service.users_per_backend") == 1) {
 
		LOG4CXX_INFO(logger, "Backend " << c->id << " will die, because the last user disconnected");
 
		c->willDie = true;
 
	}
 
}
 

	
 
void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &msg) {
 
	conv->getConversationManager()->getUser()->updateLastActivity();
 

	
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(msg->getTo()));
 
		if (!msg->getTo().getResource().empty()) {
 
			msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), msg->getTo().getResource()));
 
		}
 
		else {
 
			msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
 
		}
 
		std::string xml = safeByteArrayToString(m_serializer->serializeElement(msg));
 
		WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
 
		send(c->connection, xml);
 
		return;
 
	}
 

	
 
	boost::shared_ptr<Swift::ChatState> statePayload = msg->getPayload<Swift::ChatState>();
 
	if (statePayload) {
 
		pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED;
 
		switch (statePayload->getChatState()) {
 
			case Swift::ChatState::Active:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING;
 
				break;
 
			case Swift::ChatState::Composing:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING;
 
				break;
 
			case Swift::ChatState::Paused:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED;
 
				break;
 
			default:
 
				break;
 
		}
 
		if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) {
 
			pbnetwork::Buddy buddy;
 
			buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
			buddy.set_buddyname(conv->getLegacyName());
 

	
 
			std::string message;
 
			buddy.SerializeToString(&message);
 

	
 
			WRAP(message, type);
 

	
 
			Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
			if (!c) {
 
				return;
 
			}
 
			send(c->connection, message);
 
		}
 
	}
 

	
 
	boost::shared_ptr<Swift::AttentionPayload> attentionPayload = msg->getPayload<Swift::AttentionPayload>();
 
	if (attentionPayload) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 

	
 
	if (!msg->getSubject().empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getSubject());
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 
	
 

	
 
	std::string xhtml;
 
	boost::shared_ptr<Swift::XHTMLIMPayload> xhtmlPayload = msg->getPayload<Swift::XHTMLIMPayload>();
 
	if (xhtmlPayload) {
 
		xhtml = xhtmlPayload->getBody();
 
	}
 

	
 
	// Send normal message
 
	if (!msg->getBody().empty() || !xhtml.empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 
		m.set_xhtml(xhtml);
 
		boost::shared_ptr<Swift::DeliveryReceiptRequest> receiptPayload = msg->getPayload<Swift::DeliveryReceiptRequest>();
 
		if (receiptPayload && !msg->getID().empty()) {
 
			m.set_id(msg->getID());
 
		}
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		send(c->connection, message);
 
	}
 
}
 

	
 
void NetworkPluginServer::handleBuddyRemoved(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_REMOVED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	dynamic_cast<LocalBuddy *>(b)->setAlias(item.getName());
 
	dynamic_cast<LocalBuddy *>(b)->setGroups(item.getGroups());
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) {
 
	handleBuddyUpdated(buddy, item);
 
}
 

	
 
void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) {
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) {
 
	handleBuddyRemoved(b);
 
}
 

	
 
void NetworkPluginServer::handleBlockToggled(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	BOOST_FOREACH(const std::string &g, b->getGroups()) {
 
		buddy.add_group(g);
 
	}
 
	buddy.set_status(pbnetwork::STATUS_NONE);
 
	buddy.set_blocked(!b->isBlocked());
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 

	
 
void NetworkPluginServer::handleVCardUpdated(User *user, boost::shared_ptr<Swift::VCard> v) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname("");
 
	vcard.set_id(0);
 
	vcard.set_photo(&v->getPhoto()[0], v->getPhoto().size());
 
	vcard.set_nickname(v->getNickname());
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) {
 
	pbnetwork::VCard vcard;
 
	vcard.set_username(user->getJID().toBare());
 
	vcard.set_buddyname(name);
 
	vcard.set_id(id);
 

	
 
	std::string message;
 
	vcard.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_VCARD);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleFTAccepted(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(ftID);
 

	
 
	std::string message;
 
	f.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_START);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleFTRejected(User *user, const std::string &buddyName, const std::string &fileName, unsigned long size) {
 
	pbnetwork::File f;
 
	f.set_username(user->getJID().toBare());
 
	f.set_buddyname(buddyName);
 
	f.set_filename(fileName);
 
	f.set_size(size);
 
	f.set_ftid(0);
 

	
 
	std::string message;
 
	f.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_FT_FINISH);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleFTStateChanged(Swift::FileTransfer::State state, const std::string &userName, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long id) {
 
	User *user = m_userManager->getUser(userName);
 
	if (!user) {
 
		// TODO: FIXME We have to remove filetransfer when use disconnects
 
		return;
 
	}
 
#if !HAVE_SWIFTEN_3
 
	if (state.state == Swift::FileTransfer::State::Transferring) {
 
		handleFTAccepted(user, buddyName, fileName, size, id);
 
	}
 
	else if (state.state == Swift::FileTransfer::State::Canceled) {
 
		handleFTRejected(user, buddyName, fileName, size);
 
	}
 
#endif
 
}
 

	
 
void NetworkPluginServer::sendPing(Backend *c) {
 

	
 
	std::string message;
 
	pbnetwork::WrapperMessage wrap;
 
	wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_PING);
 
	wrap.SerializeToString(&message);
 

	
 
	if (c->connection) {
 
		LOG4CXX_INFO(logger, "PING to " << c << " (ID=" << c->id << ")");
 
		send(c->connection, message);
 
		c->pongReceived = false;
 
	}
 
// 	LOG4CXX_INFO(logger, "PING to " << c);
 
}
 

	
 
void NetworkPluginServer::handlePIDTerminated(unsigned long pid) {
 
	std::vector<unsigned long>::iterator log_id_it;
 
	log_id_it = std::find(m_pids.begin(), m_pids.end(), pid);
 
	if (log_id_it != m_pids.end()) {
 
		*log_id_it = 0;
 
	}
 
}
 

	
 
#ifndef _WIN32
 

	
 
static int sig_block_count = 0;
 
static sigset_t block_mask;
 

	
 
static void __block_signals ( void )
 
{
 
  static int init_done = 0;
 

	
 
  if ( (sig_block_count++) != 1 ) return;
 

	
 
  if ( init_done == 0 ) {
 
    sigemptyset ( &block_mask );
 
    sigaddset ( &block_mask, SIGPIPE );
 
    sigaddset ( &block_mask, SIGHUP );
 
    sigaddset ( &block_mask, SIGINT );
 
    sigaddset ( &block_mask, SIGQUIT );
 
    sigaddset ( &block_mask, SIGTERM );
 
    sigaddset ( &block_mask, SIGABRT );
 
    sigaddset ( &block_mask, SIGCHLD );
 
    init_done = 1;
 
  }
 

	
 
  sigprocmask ( SIG_BLOCK, &block_mask, NULL );
 
  return;
 
}
 

	
 
static void __unblock_signals ( void )
 
{
 
  sigset_t sigset;
 

	
 
  if ( (sig_block_count--) != 0 ) return;
 
  sigprocmask ( SIG_UNBLOCK, &block_mask, NULL );
 

	
 
  if ( sigpending ( &sigset ) == 0 ) {
 
    if ( sigismember ( &sigset, SIGCHLD ) ) {
 
      raise ( SIGCHLD );
 
    }
 
  }
 
}
 

	
 
#endif
 

	
 
NetworkPluginServer::Backend *NetworkPluginServer::getFreeClient(bool acceptUsers, bool longRun, bool check) {
 
	NetworkPluginServer::Backend *c = NULL;
 

	
 
	unsigned long diff = CONFIG_INT(m_config, "service.login_delay");
 
	time_t now = time(NULL);
 
	if (diff && (now - m_lastLogin < diff)) {
 
		m_loginTimer->stop();
 
		m_loginTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer((diff - (now - m_lastLogin)) * 1000);
 
		m_loginTimer->onTick.connect(boost::bind(&NetworkPluginServer::loginDelayFinished, this));
 
		m_loginTimer->start();
 
		LOG4CXX_INFO(logger, "Postponing login because of service.login_delay setting");
 
		return NULL;
 
	}
 

	
 
	if (!check) {
 
		m_lastLogin = time(NULL);
 
	}
 

	
 
	// Check all backends and find free one
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->willDie == false && (*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*it)->longRun == longRun) {
 
			c = *it;
 
			// if we're not reusing all backends and backend is full, stop accepting new users on this backend
 
			if (!CONFIG_BOOL(m_config, "service.reuse_old_backends")) {
 
				if (!check && c->users.size() + 1 >= CONFIG_INT(m_config, "service.users_per_backend")) {
 
					c->acceptUsers = false;
 
				}
 
			}
 
			break;
 
		}
 
	}
 

	
 
	// there's no free backend, so spawn one.
 
	if (c == NULL && !m_startingBackend) {
 
		m_isNextLongRun = longRun;
 
		m_startingBackend = true;
 

	
 
#ifndef _WIN32
 
		__block_signals();
 
#endif
 
		std::vector<unsigned long>::iterator log_id_it;
 
		log_id_it = std::find(m_pids.begin(), m_pids.end(), 0);
 
		std::string log_id = "";
 
		if (log_id_it == m_pids.end()) {
 
			log_id = boost::lexical_cast<std::string>(m_pids.size() + 1);
 
		}
 
		else {
 
			log_id = boost::lexical_cast<std::string>(log_id_it - m_pids.begin() + 1);
 
		}
 
		unsigned long pid = exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), log_id.c_str(), m_config->getCommandLineArgs().c_str());
 
		if (log_id_it == m_pids.end()) {
 
			m_pids.push_back(pid);
 
		}
 
		else {
 
			*log_id_it = pid;
 
		}
 
#ifndef _WIN32
 
		__unblock_signals();
 
#endif
 
	}
 

	
 
	return c;
 
}
 

	
 
}
src/settingsadhoccommand.cpp
Show inline comments
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2012, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/settingsadhoccommand.h"
 
#include "transport/conversation.h"
 
#include "transport/usermanager.h"
 
#include "transport/buddy.h"
 
#include "transport/factory.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "transport/storagebackend.h"
 
 
 
namespace Transport {
 
 
DEFINE_LOGGER(logger, "SettingsAdHocCommand");
 
 
SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) : AdHocCommand(component, userManager, storageBackend, initiator, to) {
 
	m_state = Init;
 
#if HAVE_SWIFTEN_3
 
	Swift::FormField::ref field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, "1");
 
#else
 
	Swift::BooleanFormField::ref field;
 
 
	field = Swift::BooleanFormField::create(true);
 
#endif
 
	field->setName("enable_transport");
 
	field->setLabel("Enable transport");
 
	addFormField(field);
 
#if HAVE_SWIFTEN_3
 
	field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0"));
 
#else
 
	field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1");
 
#endif
 
	field->setName("send_headlines");
 
	field->setLabel("Allow sending messages as headlines");
 
	addFormField(field);
 
#if HAVE_SWIFTEN_3
 
	field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0"));
 
#else
 
	field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0") == "1");
 
#endif
 
	field->setName("stay_connected");
 
	field->setLabel("Stay connected to legacy network when offline on XMPP");
 
	addFormField(field);
 
}
 
 
SettingsAdHocCommand::~SettingsAdHocCommand() {
 
}
 
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::getForm() {
 
	if (!m_storageBackend) {
 
		boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
		boost::shared_ptr<Swift::Form> form(new Swift::Form());
 
#if HAVE_SWIFTEN_3
 
		form->addField(boost::make_shared<Swift::FormField>(Swift::FormField::FixedType, "This server does not support transport settings. There is no storage backend configured"));
 
#else
 
		form->addField(Swift::FixedFormField::create("This server does not support transport settings. There is no storage backend configured"));
 
#endif
 
		response->setForm(form);
 
		return response;
 
	}
 
 
	UserInfo user;
 
	if (m_storageBackend->getUser(m_initiator.toBare().toString(), user) == false) {
 
		boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
		boost::shared_ptr<Swift::Form> form(new Swift::Form());
 
#if HAVE_SWIFTEN_3
 
		form->addField(boost::make_shared<Swift::FormField>(Swift::FormField::FixedType, "You are not registered."));
 
#else
 
		form->addField(Swift::FixedFormField::create("You are not registered."));
 
#endif
 
		response->setForm(form);
 
		return response;
 
	}
 
 
	boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Executing));
 
	boost::shared_ptr<Swift::Form> form(new Swift::Form());
 
 
	BOOST_FOREACH(Swift::FormField::ref field, m_fields) {
 
		// FIXME: Support for more types than boolean
 
#if HAVE_SWIFTEN_3
 
		if (field->getType() == Swift::FormField::BooleanType) {
 
			std::string value = field->getBoolValue() ? "1" : "0";
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(user.id, field->getName(), type, value);
 
			field->setBoolValue(value == "1");
 
		}
 
#else
 
		if (boost::dynamic_pointer_cast<Swift::BooleanFormField>(field)) {
 
			Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast<Swift::BooleanFormField>(field));
 
			std::string value = f->getValue() ? "1" : "0";
 
			int type = (int)TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(user.id, f->getName(), type, value);
 
			f->setValue(value == "1");
 
		}
 
#endif			
 
 
		form->addField(field);
 
	}
 
 
	response->setForm(form);
 
	return response;
 
}
 
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::handleResponse(boost::shared_ptr<Swift::Command> payload) {
 
	UserInfo user;
 
	bool registered = m_storageBackend->getUser(m_initiator.toBare().toString(), user);
 
 
	if (registered && payload->getForm()) {
 
		BOOST_FOREACH(Swift::FormField::ref field, m_fields) {
 
			Swift::FormField::ref received = payload->getForm()->getField(field->getName());
 
			if (!received) {
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			if (received->getType() == Swift::FormField::BooleanType) {
 
				std::string value = received->getBoolValue() ? "1" : "0";
 
				m_storageBackend->updateUserSetting(user.id, received->getName(), value);
 
			} else if (received->getType() == Swift::FormField::TextSingleType) {
 
				m_storageBackend->updateUserSetting(user.id, received->getName(), received->getTextSingleValue());
 
			}
 
#else
 
			// FIXME: Support for more types than boolean
 
			if (boost::dynamic_pointer_cast<Swift::BooleanFormField>(received)) {
 
				Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast<Swift::BooleanFormField>(received));
 
				std::string value = f->getValue() ? "1" : "0";
 
				m_storageBackend->updateUserSetting(user.id, f->getName(), value);
 
			}
 
			else if (boost::dynamic_pointer_cast<Swift::TextSingleFormField>(received)) {
 
				Swift::TextSingleFormField::ref f(boost::dynamic_pointer_cast<Swift::TextSingleFormField>(received));
 
				m_storageBackend->updateUserSetting(user.id, f->getName(), f->getValue());
 
			}
 
#endif
 
		}
 
	}
 
 
	boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
	return response;
 
}
 
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::handleRequest(boost::shared_ptr<Swift::Command> payload) {
 
	boost::shared_ptr<Swift::Command> response;
 
	if (payload->getAction() == Swift::Command::Cancel) {
 
		response = boost::shared_ptr<Swift::Command>(new Swift::Command("settings", m_id, Swift::Command::Canceled));
 
		return response;
 
	}
 
 
	switch (m_state) {
 
		case Init:
 
			response = getForm();
 
			m_state = WaitingForResponse;
 
			break;
 
		case WaitingForResponse:
 
			response = handleResponse(payload);
 
			break;
 
		default:
 
			break;
 
	}
 
	
 
	return response;
 
}
 
 
}
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2012, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/settingsadhoccommand.h"
 
#include "transport/conversation.h"
 
#include "transport/usermanager.h"
 
#include "transport/buddy.h"
 
#include "transport/factory.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "transport/storagebackend.h"
 

	
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "SettingsAdHocCommand");
 

	
 
SettingsAdHocCommand::SettingsAdHocCommand(Component *component, UserManager *userManager, StorageBackend *storageBackend, const Swift::JID &initiator, const Swift::JID &to) : AdHocCommand(component, userManager, storageBackend, initiator, to) {
 
	m_state = Init;
 
#if HAVE_SWIFTEN_3
 
	Swift::FormField::ref field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, "1");
 
#else
 
	Swift::BooleanFormField::ref field;
 

	
 
	field = Swift::BooleanFormField::create(true);
 
#endif
 
	field->setName("enable_transport");
 
	field->setLabel("Enable transport");
 
	addFormField(field);
 
#if HAVE_SWIFTEN_3
 
	field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0"));
 
#else
 
	field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.send_headlines", "0") == "1");
 
#endif
 
	field->setName("send_headlines");
 
	field->setLabel("Allow sending messages as headlines");
 
	addFormField(field);
 
#if HAVE_SWIFTEN_3
 
	field = boost::make_shared<Swift::FormField>(Swift::FormField::BooleanType, CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0"));
 
#else
 
	field = Swift::BooleanFormField::create(CONFIG_STRING_DEFAULTED(component->getConfig(), "settings.stay_connected", "0") == "1");
 
#endif
 
	field->setName("stay_connected");
 
	field->setLabel("Stay connected to legacy network when offline on XMPP");
 
	addFormField(field);
 
}
 

	
 
SettingsAdHocCommand::~SettingsAdHocCommand() {
 
}
 

	
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::getForm() {
 
	if (!m_storageBackend) {
 
		boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
		boost::shared_ptr<Swift::Form> form(new Swift::Form());
 
#if HAVE_SWIFTEN_3
 
		form->addField(boost::make_shared<Swift::FormField>(Swift::FormField::FixedType, "This server does not support transport settings. There is no storage backend configured"));
 
#else
 
		form->addField(Swift::FixedFormField::create("This server does not support transport settings. There is no storage backend configured"));
 
#endif
 
		response->setForm(form);
 
		return response;
 
	}
 

	
 
	UserInfo user;
 
	if (m_storageBackend->getUser(m_initiator.toBare().toString(), user) == false) {
 
		boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
		boost::shared_ptr<Swift::Form> form(new Swift::Form());
 
#if HAVE_SWIFTEN_3
 
		form->addField(boost::make_shared<Swift::FormField>(Swift::FormField::FixedType, "You are not registered."));
 
#else
 
		form->addField(Swift::FixedFormField::create("You are not registered."));
 
#endif
 
		response->setForm(form);
 
		return response;
 
	}
 

	
 
	boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Executing));
 
	boost::shared_ptr<Swift::Form> form(new Swift::Form());
 

	
 
	BOOST_FOREACH(Swift::FormField::ref field, m_fields) {
 
		// FIXME: Support for more types than boolean
 
#if HAVE_SWIFTEN_3
 
		if (field->getType() == Swift::FormField::BooleanType) {
 
			std::string value = field->getBoolValue() ? "1" : "0";
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(user.id, field->getName(), type, value);
 
			field->setBoolValue(value == "1");
 
		}
 
#else
 
		if (boost::dynamic_pointer_cast<Swift::BooleanFormField>(field)) {
 
			Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast<Swift::BooleanFormField>(field));
 
			std::string value = f->getValue() ? "1" : "0";
 
			int type = (int)TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(user.id, f->getName(), type, value);
 
			f->setValue(value == "1");
 
		}
 
#endif			
 

	
 
		form->addField(field);
 
	}
 

	
 
	response->setForm(form);
 
	return response;
 
}
 

	
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::handleResponse(boost::shared_ptr<Swift::Command> payload) {
 
	UserInfo user;
 
	bool registered = m_storageBackend->getUser(m_initiator.toBare().toString(), user);
 

	
 
	if (registered && payload->getForm()) {
 
		BOOST_FOREACH(Swift::FormField::ref field, m_fields) {
 
			Swift::FormField::ref received = payload->getForm()->getField(field->getName());
 
			if (!received) {
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			if (received->getType() == Swift::FormField::BooleanType) {
 
				std::string value = received->getBoolValue() ? "1" : "0";
 
				m_storageBackend->updateUserSetting(user.id, received->getName(), value);
 
			} else if (received->getType() == Swift::FormField::TextSingleType) {
 
				m_storageBackend->updateUserSetting(user.id, received->getName(), received->getTextSingleValue());
 
			}
 
#else
 
			// FIXME: Support for more types than boolean
 
			if (boost::dynamic_pointer_cast<Swift::BooleanFormField>(received)) {
 
				Swift::BooleanFormField::ref f(boost::dynamic_pointer_cast<Swift::BooleanFormField>(received));
 
				std::string value = f->getValue() ? "1" : "0";
 
				m_storageBackend->updateUserSetting(user.id, f->getName(), value);
 
			}
 
			else if (boost::dynamic_pointer_cast<Swift::TextSingleFormField>(received)) {
 
				Swift::TextSingleFormField::ref f(boost::dynamic_pointer_cast<Swift::TextSingleFormField>(received));
 
				m_storageBackend->updateUserSetting(user.id, f->getName(), f->getValue());
 
			}
 
#endif
 
		}
 
	}
 

	
 
	boost::shared_ptr<Swift::Command> response(new Swift::Command("settings", m_id, Swift::Command::Completed));
 
	return response;
 
}
 

	
 
boost::shared_ptr<Swift::Command> SettingsAdHocCommand::handleRequest(boost::shared_ptr<Swift::Command> payload) {
 
	boost::shared_ptr<Swift::Command> response;
 
	if (payload->getAction() == Swift::Command::Cancel) {
 
		response = boost::shared_ptr<Swift::Command>(new Swift::Command("settings", m_id, Swift::Command::Canceled));
 
		return response;
 
	}
 

	
 
	switch (m_state) {
 
		case Init:
 
			response = getForm();
 
			m_state = WaitingForResponse;
 
			break;
 
		case WaitingForResponse:
 
			response = handleResponse(payload);
 
			break;
 
		default:
 
			break;
 
	}
 
	
 
	return response;
 
}
 

	
 
}
src/tests/basictest.cpp
Show inline comments
 
@@ -150,8 +150,11 @@ void BasicTest::dumpReceived() {
 
	std::cout << "Stream2:\n";
 
	std::cout << receivedData2 << "\n";
 
}
 

	
 
#if HAVE_SWIFTEN_3
 
void BasicTest::handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
 
#else
 
void BasicTest::handleElement(boost::shared_ptr<Swift::Element> element) {
 
#endif
 
	if (stream1_active) {
 
		received.push_back(element);
 
	}
src/tests/basictest.h
Show inline comments
 
@@ -45,6 +45,7 @@
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Server/ServerFromClientSession.h"
 
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000
 

	
 
using namespace Transport;
 

	
 
@@ -217,9 +218,11 @@ class BasicTest : public Swift::XMPPParserClient {
 
	void handleDataReceived2(const Swift::SafeByteArray &data);
 

	
 
	void handleStreamStart(const Swift::ProtocolHeader&);
 

	
 
#if HAVE_SWIFTEN_3
 
	void handleElement(boost::shared_ptr<Swift::ToplevelElement> element);
 
#else
 
	void handleElement(boost::shared_ptr<Swift::Element> element);
 

	
 
#endif
 
	void handleStreamEnd();
 

	
 
	void injectPresence(boost::shared_ptr<Swift::Presence> &response);
src/tests/filetransfermanager.cpp
Show inline comments
 
deleted file
src/tests/settingsadhoccommand.cpp
Show inline comments
 
@@ -147,7 +147,7 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 

	
 
			// set enabled_transport = 0
 
			Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("enable_transport");
 
			boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->setValue(false);
 
			f->setBoolValue(false);
 

	
 
			std::string sessionId = getStanza(received[0])->getPayload<Swift::Command>()->getSessionID();
 

	
 
@@ -207,7 +207,7 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 

	
 
			// set enabled_transport = 0
 
			f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("enable_transport");
 
			CPPUNIT_ASSERT_EQUAL(false, boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->getValue());
 
			CPPUNIT_ASSERT_EQUAL(false, f->getBoolValue());
 
		}
 

	
 
		void executeTwoCommands() {
 
@@ -355,7 +355,7 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 

	
 
			// set enabled_transport = 0
 
			Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
 
			boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->setValue(true);
 
			f->setBoolValue(true);
 

	
 
			std::string sessionId = getStanza(received[0])->getPayload<Swift::Command>()->getSessionID();
 

	
 
@@ -397,7 +397,7 @@ class SettingsAdHocCommandTest : public CPPUNIT_NS :: TestFixture, public BasicT
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm());
 
			CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::Command>()->getForm()->getField("send_headlines"));
 
			Swift::FormField::ref f = getStanza(received[0])->getPayload<Swift:: Command>()->getForm()->getField("send_headlines");
 
			CPPUNIT_ASSERT_EQUAL(true, boost::dynamic_pointer_cast<Swift::BooleanFormField>(f)->getValue());
 
			CPPUNIT_ASSERT_EQUAL(true, f->getBoolValue());
 
		}
 

	
 
};
src/transport.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/transport.h"
 
#include <boost/bind.hpp>
 
#include <boost/smart_ptr/make_shared.hpp>
 
#include <boost/algorithm/string/predicate.hpp>
 
#include "transport/storagebackend.h"
 
#include "transport/factory.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "storageparser.h"
 
#ifdef _WIN32
 
#include <Swiften/TLS/CAPICertificate.h>
 
#include "Swiften/TLS/Schannel/SchannelServerContext.h"
 
#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h"
 
#else
 
#include "Swiften/TLS/PKCS12Certificate.h"
 
#include "Swiften/TLS/CertificateWithKey.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContextFactory.h"
 
#endif
 
#include "Swiften/Parser/PayloadParsers/AttentionParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/StatsParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h"
 
#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/MUCPayloadParser.h"
 
#include "transport/BlockParser.h"
 
#include "transport/BlockSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/InvisibleParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h"
 
#include "Swiften/Parser/GenericPayloadParserFactory.h"
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Elements/InBandRegistrationPayload.h"
 
 
using namespace Swift;
 
using namespace boost;
 
 
namespace Transport {
 
	
 
DEFINE_LOGGER(logger, "Component");
 
DEFINE_LOGGER(logger_xml, "Component.XML");
 
 
Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry) {
 
	m_component = NULL;
 
	m_userRegistry = NULL;
 
	m_server = NULL;
 
	m_reconnectCount = 0;
 
	m_config = config;
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&Component::handleBackendConfigChanged, this));
 
	m_factory = factory;
 
	m_loop = loop;
 
	m_userRegistry = userRegistry;
 
	m_rawXML = false;
 
 
	m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid"));
 
 
	m_factories = factories;
 
 
	m_reconnectTimer = m_factories->getTimerFactory()->createTimer(3000);
 
	m_reconnectTimer->onTick.connect(bind(&Component::start, this)); 
 
 
	if (CONFIG_BOOL(m_config, "service.server_mode")) {
 
		LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
#ifndef _WIN32
 
//TODO: fix
 
			LOG4CXX_INFO(logger, "Using PKCS#12 certificate " << CONFIG_STRING(m_config, "service.cert"));
 
			LOG4CXX_INFO(logger, "SSLv23_server_method used.");
 
			TLSServerContextFactory *f = new OpenSSLServerContextFactory();
 
			CertificateWithKey::ref certificate = boost::make_shared<PKCS12Certificate>(CONFIG_STRING(m_config, "service.cert"), createSafeByteArray(CONFIG_STRING(m_config, "service.cert_password")));
 
			m_server->addTLSEncryption(f, certificate);
 
#endif
 
			
 
		}
 
		else {
 
			LOG4CXX_WARN(logger, "No PKCS#12 certificate used. TLS is disabled.");
 
		}
 
// 		m_server->start();
 
		m_stanzaChannel = m_server->getStanzaChannel();
 
		m_iqRouter = m_server->getIQRouter();
 
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 
 
		m_server->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_server->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_server->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_server->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 
 
		m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, "Creating component in gateway mode");
 
#if HAVE_SWIFTEN_3
 
		m_component = new Swift::Component(m_jid, CONFIG_STRING(m_config, "service.password"), m_factories);
 
#else
 
		m_component = new Swift::Component(loop, m_factories, m_jid, CONFIG_STRING(m_config, "service.password"));
 
#endif
 
		m_component->setSoftwareVersion("Spectrum", SPECTRUM_VERSION);
 
		m_component->onConnected.connect(bind(&Component::handleConnected, this));
 
		m_component->onError.connect(boost::bind(&Component::handleConnectionError, this, _1));
 
		m_component->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_component->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 
 
		m_component->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_component->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_component->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_component->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 
 
		m_stanzaChannel = m_component->getStanzaChannel();
 
		m_iqRouter = m_component->getIQRouter();
 
	}
 
 
	m_capsMemoryStorage = new CapsMemoryStorage();
 
#if HAVE_SWIFTEN_3
 
	m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter, m_factories->getCryptoProvider());
 
#else
 
	m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter);
 
#endif
 
	m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel);
 
 	m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1));
 
	
 
	m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel);
 
	m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1));
 
 
 
 
// 
 
// 	m_registerHandler = new SpectrumRegisterHandler(m_component);
 
// 	m_registerHandler->start();
 
}
 
 
Component::~Component() {
 
	delete m_presenceOracle;
 
	delete m_entityCapsManager;
 
	delete m_capsManager;
 
	delete m_capsMemoryStorage;
 
	if (m_component)
 
		delete m_component;
 
	if (m_server) {
 
		m_server->stop();
 
		delete m_server;
 
	}
 
}
 
 
bool Component::handleIQ(boost::shared_ptr<Swift::IQ> iq) {
 
	if (!m_rawXML) {
 
		return false;
 
	}
 
 
	if (iq->getPayload<Swift::RosterPayload>() != NULL) { return false; }
 
	if (iq->getPayload<Swift::InBandRegistrationPayload>() != NULL) { return false; }
 
	if (iq->getPayload<Swift::StatsPayload>() != NULL) { return false; }
 
 
	if (iq->getTo().getNode().empty()) {
 
		return false;
 
	}
 
 
	onRawIQReceived(iq);
 
	return true;
 
}
 
 
void Component::handleBackendConfigChanged() {
 
	if (!m_rawXML && CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
 
		m_rawXML = true;
 
		m_iqRouter->addHandler(this);
 
	}
 
}
 
 
Swift::StanzaChannel *Component::getStanzaChannel() {
 
	return m_stanzaChannel;
 
}
 
 
Transport::PresenceOracle *Component::getPresenceOracle() {
 
	return m_presenceOracle;
 
}
 
 
void Component::start() {
 
	if (m_component && !m_component->isAvailable()) {
 
		LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port"));
 
		if (CONFIG_INT(m_config, "service.port") == 5222) {
 
			LOG4CXX_WARN(logger, "Port 5222 is usually used for client connections, not for component connections! Are you sure you are using right port?");
 
		}
 
		m_reconnectCount++;
 
		m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->start();
 
 
		//Type casting to BoostConnectionServer since onStopped signal is not defined in ConnectionServer
 
		//Ideally, onStopped must be defined in ConnectionServer
 
		if (boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())) {
 
			boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())->onStopped.connect(boost::bind(&Component::handleServerStopped, this, _1));
 
		}
 
		
 
		// We're connected right here, because we're in server mode...
 
		handleConnected();
 
	}
 
}
 
 
void Component::stop() {
 
	if (m_component) {
 
		m_reconnectCount = 0;
 
		// TODO: Call this once swiften will fix assert(!session_);
 
// 		m_component->disconnect();
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->stop();
 
	}
 
}
 
 
void Component::handleConnected() {
 
	onConnected();
 
	m_reconnectCount = 0;
 
	m_reconnectTimer->stop();
 
}
 
 
void Component::handleServerStopped(boost::optional<Swift::BoostConnectionServer::Error> e) {
 
	if(e != NULL ) {
 
		if(*e == Swift::BoostConnectionServer::Conflict) {
 
			LOG4CXX_INFO(logger, "Port "<< CONFIG_INT(m_config, "service.port") << " already in use! Stopping server..");
 
			if (CONFIG_INT(m_config, "service.port") == 5347) {
 
				LOG4CXX_INFO(logger, "Port 5347 is usually used for components. You are using server_mode=1. Are you sure you don't want to use server_mode=0 and run spectrum as component?");
 
			}
 
		}
 
		if(*e == Swift::BoostConnectionServer::UnknownError)
 
			LOG4CXX_INFO(logger, "Unknown error occured! Stopping server..");
 
		exit(1);
 
	}
 
}
 
 
 
void Component::handleConnectionError(const ComponentError &error) {
 
	onConnectionError(error);
 
// 	if (m_reconnectCount == 2)
 
// 		Component::instance()->userManager()->removeAllUsers();
 
	std::string str = "Unknown error";
 
	switch (error.getType()) {
 
		case ComponentError::UnknownError: str = "Unknown error"; break;
 
		case ComponentError::ConnectionError: str = "Connection error"; break;
 
		case ComponentError::ConnectionReadError: str = "Connection read error"; break;
 
		case ComponentError::ConnectionWriteError: str = "Connection write error"; break;
 
		case ComponentError::XMLError: str = "XML Error"; break;
 
		case ComponentError::AuthenticationFailedError: str = "Authentication failed error"; break;
 
		case ComponentError::UnexpectedElementError: str = "Unexpected element error"; break;
 
	}
 
	LOG4CXX_INFO(logger, "Disconnected from XMPP server. Error: " << str);
 
 
	m_reconnectTimer->start();
 
}
 
 
void Component::handleDataRead(const Swift::SafeByteArray &data) {
 
	std::string d = safeByteArrayToString(data);
 
	if (!boost::starts_with(d, "<auth")) {
 
		LOG4CXX_INFO(logger_xml, "XML IN " << d);
 
	}
 
}
 
 
void Component::handleDataWritten(const Swift::SafeByteArray &data) {
 
	LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
 
}
 
 
void Component::handlePresence(Swift::Presence::ref presence) {
 
	// filter out login/logout presence spam
 
	if (!presence->getTo().getNode().empty())
 
		return;
 
 
	// filter out bad presences
 
	if (!presence->getFrom().isValid()) {
 
		return;
 
	}
 
 
	switch (presence->getType()) {
 
		case Presence::Error:
 
		case Presence::Subscribe:
 
		case Presence::Subscribed:
 
		case Presence::Unsubscribe:
 
		case Presence::Unsubscribed:
 
			return;
 
		default:
 
			break;
 
	};
 
 
	// check if we have this client's capabilities and ask for them
 
	if (presence->getType() != Swift::Presence::Unavailable) {
 
		boost::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>();
 
		if (capsInfo && capsInfo->getHash() == "sha-1") {
 
			/*haveFeatures = */m_entityCapsManager->getCaps(presence->getFrom()) != DiscoInfo::ref();
 
		}
 
#ifdef SUPPORT_LEGACY_CAPS
 
		else {
 
			GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_iqRouter);
 
			discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom()));
 
			discoInfoRequest->send();
 
		}
 
#endif
 
	}
 
 
	onUserPresenceReceived(presence);
 
}
 
 
void Component::handleDiscoInfoResponse(boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
	onUserDiscoInfoReceived(jid, info);
 
#endif
 
}
 
 
void Component::handleCapsChanged(const Swift::JID& jid) {
 
	onUserDiscoInfoReceived(jid, m_entityCapsManager->getCaps(jid));
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/transport.h"
 
#include <boost/bind.hpp>
 
#include <boost/smart_ptr/make_shared.hpp>
 
#include <boost/algorithm/string/predicate.hpp>
 
#include "transport/storagebackend.h"
 
#include "transport/factory.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "storageparser.h"
 
#ifdef _WIN32
 
#include <Swiften/TLS/CAPICertificate.h>
 
#include "Swiften/TLS/Schannel/SchannelServerContext.h"
 
#include "Swiften/TLS/Schannel/SchannelServerContextFactory.h"
 
#else
 
#include "Swiften/TLS/PKCS12Certificate.h"
 
#include "Swiften/TLS/CertificateWithKey.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h"
 
#include "Swiften/TLS/OpenSSL/OpenSSLServerContextFactory.h"
 
#endif
 
#include "Swiften/Parser/PayloadParsers/AttentionParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/StatsParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h"
 
#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/MUCPayloadParser.h"
 
#include "transport/BlockParser.h"
 
#include "transport/BlockSerializer.h"
 
#include "Swiften/Parser/PayloadParsers/InvisibleParser.h"
 
#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h"
 
#include "Swiften/Parser/GenericPayloadParserFactory.h"
 
#include "Swiften/Queries/IQRouter.h"
 
#include "Swiften/Elements/RosterPayload.h"
 
#include "Swiften/Elements/InBandRegistrationPayload.h"
 

	
 
using namespace Swift;
 
using namespace boost;
 

	
 
namespace Transport {
 
	
 
DEFINE_LOGGER(logger, "Component");
 
DEFINE_LOGGER(logger_xml, "Component.XML");
 

	
 
Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry) {
 
	m_component = NULL;
 
	m_userRegistry = NULL;
 
	m_server = NULL;
 
	m_reconnectCount = 0;
 
	m_config = config;
 
	m_config->onBackendConfigUpdated.connect(boost::bind(&Component::handleBackendConfigChanged, this));
 
	m_factory = factory;
 
	m_loop = loop;
 
	m_userRegistry = userRegistry;
 
	m_rawXML = false;
 

	
 
	m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid"));
 

	
 
	m_factories = factories;
 

	
 
	m_reconnectTimer = m_factories->getTimerFactory()->createTimer(3000);
 
	m_reconnectTimer->onTick.connect(bind(&Component::start, this)); 
 

	
 
	if (CONFIG_BOOL(m_config, "service.server_mode")) {
 
		LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
#ifndef _WIN32
 
//TODO: fix
 
			LOG4CXX_INFO(logger, "Using PKCS#12 certificate " << CONFIG_STRING(m_config, "service.cert"));
 
			LOG4CXX_INFO(logger, "SSLv23_server_method used.");
 
			TLSServerContextFactory *f = new OpenSSLServerContextFactory();
 
			CertificateWithKey::ref certificate = boost::make_shared<PKCS12Certificate>(CONFIG_STRING(m_config, "service.cert"), createSafeByteArray(CONFIG_STRING(m_config, "service.cert_password")));
 
			m_server->addTLSEncryption(f, certificate);
 
#endif
 
			
 
		}
 
		else {
 
			LOG4CXX_WARN(logger, "No PKCS#12 certificate used. TLS is disabled.");
 
		}
 
// 		m_server->start();
 
		m_stanzaChannel = m_server->getStanzaChannel();
 
		m_iqRouter = m_server->getIQRouter();
 

	
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_server->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 

	
 
		m_server->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_server->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_server->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_server->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 

	
 
		m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, "Creating component in gateway mode");
 
#if HAVE_SWIFTEN_3
 
		m_component = new Swift::Component(m_jid, CONFIG_STRING(m_config, "service.password"), m_factories);
 
#else
 
		m_component = new Swift::Component(loop, m_factories, m_jid, CONFIG_STRING(m_config, "service.password"));
 
#endif
 
		m_component->setSoftwareVersion("Spectrum", SPECTRUM_VERSION);
 
		m_component->onConnected.connect(bind(&Component::handleConnected, this));
 
		m_component->onError.connect(boost::bind(&Component::handleConnectionError, this, _1));
 
		m_component->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1));
 
		m_component->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1));
 

	
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<StorageParser>("private", "jabber:iq:private"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::AttentionParser>("attention", "urn:xmpp:attention:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::XHTMLIMParser>("html", "http://jabber.org/protocol/xhtml-im"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Transport::BlockParser>("block", "urn:xmpp:block:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::InvisibleParser>("invisible", "urn:xmpp:invisible:0"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::StatsParser>("query", "http://jabber.org/protocol/stats"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::GatewayPayloadParser>("query", "jabber:iq:gateway"));
 
		m_component->addPayloadParserFactory(new GenericPayloadParserFactory<Swift::MUCPayloadParser>("x", "http://jabber.org/protocol/muc"));
 

	
 
		m_component->addPayloadSerializer(new Swift::AttentionSerializer());
 
		m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer());
 
		m_component->addPayloadSerializer(new Transport::BlockSerializer());
 
		m_component->addPayloadSerializer(new Swift::InvisibleSerializer());
 
		m_component->addPayloadSerializer(new Swift::StatsSerializer());
 
		m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer());
 
		m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer());
 

	
 
		m_stanzaChannel = m_component->getStanzaChannel();
 
		m_iqRouter = m_component->getIQRouter();
 
	}
 

	
 
	m_capsMemoryStorage = new CapsMemoryStorage();
 
#if HAVE_SWIFTEN_3
 
	m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter, m_factories->getCryptoProvider());
 
#else
 
	m_capsManager = new CapsManager(m_capsMemoryStorage, m_stanzaChannel, m_iqRouter);
 
#endif
 
	m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel);
 
 	m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1));
 
	
 
	m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel);
 
	m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1));
 

	
 

	
 

	
 
// 
 
// 	m_registerHandler = new SpectrumRegisterHandler(m_component);
 
// 	m_registerHandler->start();
 
}
 

	
 
Component::~Component() {
 
	delete m_presenceOracle;
 
	delete m_entityCapsManager;
 
	delete m_capsManager;
 
	delete m_capsMemoryStorage;
 
	if (m_component)
 
		delete m_component;
 
	if (m_server) {
 
		m_server->stop();
 
		delete m_server;
 
	}
 
}
 

	
 
bool Component::handleIQ(boost::shared_ptr<Swift::IQ> iq) {
 
	if (!m_rawXML) {
 
		return false;
 
	}
 

	
 
	if (iq->getPayload<Swift::RosterPayload>() != NULL) { return false; }
 
	if (iq->getPayload<Swift::InBandRegistrationPayload>() != NULL) { return false; }
 
	if (iq->getPayload<Swift::StatsPayload>() != NULL) { return false; }
 

	
 
	if (iq->getTo().getNode().empty()) {
 
		return false;
 
	}
 

	
 
	onRawIQReceived(iq);
 
	return true;
 
}
 

	
 
void Component::handleBackendConfigChanged() {
 
	if (!m_rawXML && CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
 
		m_rawXML = true;
 
		m_iqRouter->addHandler(this);
 
	}
 
}
 

	
 
Swift::StanzaChannel *Component::getStanzaChannel() {
 
	return m_stanzaChannel;
 
}
 

	
 
Transport::PresenceOracle *Component::getPresenceOracle() {
 
	return m_presenceOracle;
 
}
 

	
 
void Component::start() {
 
	if (m_component && !m_component->isAvailable()) {
 
		LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port"));
 
		if (CONFIG_INT(m_config, "service.port") == 5222) {
 
			LOG4CXX_WARN(logger, "Port 5222 is usually used for client connections, not for component connections! Are you sure you are using right port?");
 
		}
 
		m_reconnectCount++;
 
		m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->start();
 

	
 
		//Type casting to BoostConnectionServer since onStopped signal is not defined in ConnectionServer
 
		//Ideally, onStopped must be defined in ConnectionServer
 
		if (boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())) {
 
			boost::dynamic_pointer_cast<Swift::BoostConnectionServer>(m_server->getConnectionServer())->onStopped.connect(boost::bind(&Component::handleServerStopped, this, _1));
 
		}
 
		
 
		// We're connected right here, because we're in server mode...
 
		handleConnected();
 
	}
 
}
 

	
 
void Component::stop() {
 
	if (m_component) {
 
		m_reconnectCount = 0;
 
		// TODO: Call this once swiften will fix assert(!session_);
 
// 		m_component->disconnect();
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->stop();
 
	}
 
}
 

	
 
void Component::handleConnected() {
 
	onConnected();
 
	m_reconnectCount = 0;
 
	m_reconnectTimer->stop();
 
}
 

	
 
void Component::handleServerStopped(boost::optional<Swift::BoostConnectionServer::Error> e) {
 
	if(e != NULL ) {
 
		if(*e == Swift::BoostConnectionServer::Conflict) {
 
			LOG4CXX_INFO(logger, "Port "<< CONFIG_INT(m_config, "service.port") << " already in use! Stopping server..");
 
			if (CONFIG_INT(m_config, "service.port") == 5347) {
 
				LOG4CXX_INFO(logger, "Port 5347 is usually used for components. You are using server_mode=1. Are you sure you don't want to use server_mode=0 and run spectrum as component?");
 
			}
 
		}
 
		if(*e == Swift::BoostConnectionServer::UnknownError)
 
			LOG4CXX_INFO(logger, "Unknown error occured! Stopping server..");
 
		exit(1);
 
	}
 
}
 

	
 

	
 
void Component::handleConnectionError(const ComponentError &error) {
 
	onConnectionError(error);
 
// 	if (m_reconnectCount == 2)
 
// 		Component::instance()->userManager()->removeAllUsers();
 
	std::string str = "Unknown error";
 
	switch (error.getType()) {
 
		case ComponentError::UnknownError: str = "Unknown error"; break;
 
		case ComponentError::ConnectionError: str = "Connection error"; break;
 
		case ComponentError::ConnectionReadError: str = "Connection read error"; break;
 
		case ComponentError::ConnectionWriteError: str = "Connection write error"; break;
 
		case ComponentError::XMLError: str = "XML Error"; break;
 
		case ComponentError::AuthenticationFailedError: str = "Authentication failed error"; break;
 
		case ComponentError::UnexpectedElementError: str = "Unexpected element error"; break;
 
	}
 
	LOG4CXX_INFO(logger, "Disconnected from XMPP server. Error: " << str);
 

	
 
	m_reconnectTimer->start();
 
}
 

	
 
void Component::handleDataRead(const Swift::SafeByteArray &data) {
 
	std::string d = safeByteArrayToString(data);
 
	if (!boost::starts_with(d, "<auth")) {
 
		LOG4CXX_INFO(logger_xml, "XML IN " << d);
 
	}
 
}
 

	
 
void Component::handleDataWritten(const Swift::SafeByteArray &data) {
 
	LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
 
}
 

	
 
void Component::handlePresence(Swift::Presence::ref presence) {
 
	// filter out login/logout presence spam
 
	if (!presence->getTo().getNode().empty())
 
		return;
 

	
 
	// filter out bad presences
 
	if (!presence->getFrom().isValid()) {
 
		return;
 
	}
 

	
 
	switch (presence->getType()) {
 
		case Presence::Error:
 
		case Presence::Subscribe:
 
		case Presence::Subscribed:
 
		case Presence::Unsubscribe:
 
		case Presence::Unsubscribed:
 
			return;
 
		default:
 
			break;
 
	};
 

	
 
	// check if we have this client's capabilities and ask for them
 
	if (presence->getType() != Swift::Presence::Unavailable) {
 
		boost::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>();
 
		if (capsInfo && capsInfo->getHash() == "sha-1") {
 
			/*haveFeatures = */m_entityCapsManager->getCaps(presence->getFrom()) != DiscoInfo::ref();
 
		}
 
#ifdef SUPPORT_LEGACY_CAPS
 
		else {
 
			GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(presence->getFrom(), m_iqRouter);
 
			discoInfoRequest->onResponse.connect(boost::bind(&Component::handleDiscoInfoResponse, this, _1, _2, presence->getFrom()));
 
			discoInfoRequest->send();
 
		}
 
#endif
 
	}
 

	
 
	onUserPresenceReceived(presence);
 
}
 

	
 
void Component::handleDiscoInfoResponse(boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
	onUserDiscoInfoReceived(jid, info);
 
#endif
 
}
 

	
 
void Component::handleCapsChanged(const Swift::JID& jid) {
 
	onUserDiscoInfoReceived(jid, m_entityCapsManager->getCaps(jid));
 
}
 

	
 
}
src/user.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/presenceoracle.h"
 
#include "transport/logging.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include <boost/foreach.hpp>
 
#include <stdio.h>
 
#include <stdlib.h>
 
 
using namespace boost;
 
 
#define foreach         BOOST_FOREACH
 
 
namespace Transport {
 
 
DEFINE_LOGGER(logger, "User");
 
 
User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) {
 
	m_jid = jid.toBare();
 
	m_data = NULL;
 
 
	m_cacheMessages = false;
 
	m_component = component;
 
	m_presenceOracle = component->m_presenceOracle;
 
	m_entityCapsManager = component->m_entityCapsManager;
 
	m_userManager = userManager;
 
	m_userInfo = userInfo;
 
	m_connected = false;
 
	m_readyForConnect = false;
 
	m_ignoreDisconnect = false;
 
	m_resources = 0;
 
	m_reconnectCounter = 0;
 
 
	m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000);
 
	m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); 
 
 
	m_rosterManager = new RosterManager(this, m_component);
 
	m_conversationManager = new ConversationManager(this, m_component);
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Created");
 
	updateLastActivity();
 
}
 
 
User::~User(){
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Destroying");
 
	if (m_component->inServerMode()) {
 
#if HAVE_SWIFTEN_3
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::ToplevelElement>());
 
#else
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::Element>());
 
#endif
 
	}
 
 
	m_reconnectTimer->stop();
 
	delete m_rosterManager;
 
	delete m_conversationManager;
 
}
 
 
const Swift::JID &User::getJID() {
 
	return m_jid;
 
}
 
 
std::vector<Swift::JID> User::getJIDWithFeature(const std::string &feature) {
 
	std::vector<Swift::JID> jid;
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable)
 
			continue;
 
 
		Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(presence->getFrom());
 
		if (!discoInfo) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
			if (m_legacyCaps.find(presence->getFrom()) != m_legacyCaps.end()) {
 
				discoInfo = m_legacyCaps[presence->getFrom()];
 
			}
 
			else {
 
				continue;
 
			}
 
 
			if (!discoInfo) {
 
				continue;
 
			}
 
#else
 
			continue;
 
#endif
 
		}
 
 
		if (discoInfo->hasFeature(feature)) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Found JID with " << feature << " feature: " << presence->getFrom().toString());
 
			jid.push_back(presence->getFrom());
 
		}
 
	}
 
 
	if (jid.empty()) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": No JID with " << feature << " feature " << m_legacyCaps.size());
 
	}
 
	return jid;
 
}
 
 
Swift::DiscoInfo::ref User::getCaps(const Swift::JID &jid) const {
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
#ifdef SUPPORT_LEGACY_CAPS
 
	if (!discoInfo) {
 
		std::map<Swift::JID, Swift::DiscoInfo::ref>::const_iterator it = m_legacyCaps.find(jid);
 
		if (it != m_legacyCaps.end()) {
 
			discoInfo = it->second;
 
		}
 
	}
 
#endif
 
	return discoInfo;
 
}
 
 
void User::sendCurrentPresence() {
 
	if (m_component->inServerMode()) {
 
		return;
 
	}
 
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			continue;
 
		}
 
 
		if (m_connected) {
 
			Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
			if (highest) {
 
				Swift::Presence::ref response = Swift::Presence::create(highest);
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
			else {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
		}
 
		else {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			response->setStatus("Connecting");
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
}
 
 
void User::setConnected(bool connected) {
 
	m_connected = connected;
 
	m_reconnectCounter = 0;
 
	setIgnoreDisconnect(false);
 
	updateLastActivity();
 
 
	sendCurrentPresence();
 
 
	if (m_connected) {
 
		BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) {
 
			handlePresence(presence, true);
 
		}
 
	}
 
}
 
 
void User::setCacheMessages(bool cacheMessages) {
 
	if (m_component->inServerMode() && !m_cacheMessages && cacheMessages) {
 
		m_conversationManager->sendCachedChatMessages();
 
	}
 
	m_cacheMessages = cacheMessages;
 
}
 
 
void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
 
	LOG4CXX_INFO(logger, "PRESENCE " << presence->getFrom().toString() << " " << presence->getTo().toString());
 
	if (!m_connected) {
 
		// we are not connected to legacy network, so we should do it when disco#info arrive :)
 
		if (m_readyForConnect == false) {
 
			
 
			// Forward status message to legacy network, but only if it's sent from active resource
 
// 					if (m_activeResource == presence->getFrom().getResource().getUTF8String()) {
 
// 						forwardStatus(presenceShow, stanzaStatus);
 
// 					}
 
			boost::shared_ptr<Swift::CapsInfo> capsInfo = presence->getPayload<Swift::CapsInfo>();
 
			if (capsInfo && capsInfo->getHash() == "sha-1") {
 
				if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
					m_readyForConnect = true;
 
					onReadyToConnect();
 
				}
 
				else {
 
					m_reconnectTimer->start();
 
				}
 
			}
 
			else if (m_component->inServerMode()) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 
			else {
 
				m_reconnectTimer->start();
 
			}
 
		}
 
	}
 
 
	
 
	if (!presence->getTo().getNode().empty()) {
 
		bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			Conversation *conv = m_conversationManager->getConversation(room);
 
			if (conv) {
 
				conv->removeJID(presence->getFrom());
 
				if (!conv->getJIDs().empty()) {
 
					return;
 
				}
 
			}
 
			else {
 
				return;
 
			}
 
 
			if (getUserSetting("stay_connected") != "1") {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room);
 
				onRawPresenceReceived(presence);
 
				onRoomLeft(room);
 
 
				BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
 
					if (p->getTo() == presence->getTo()) {
 
						m_joinedRooms.remove(p);
 
						break;
 
					}
 
				}
 
 
				if (conv) {
 
					m_conversationManager->removeConversation(conv);
 
					delete conv;
 
				}
 
			}
 
 
			return;
 
		}
 
		else if (isMUC) {
 
			// force connection to legacy network to let backend to handle auto-join on connect.
 
			if (!m_readyForConnect) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			std::string password = "";
 
			if (presence->getPayload<Swift::MUCPayload>() != NULL) {
 
				password = presence->getPayload<Swift::MUCPayload>()->getPassword() ? *presence->getPayload<Swift::MUCPayload>()->getPassword() : "";
 
			}
 
 
			Conversation *conv = m_conversationManager->getConversation(room);
 
			if (conv != NULL) {
 
				if (std::find(conv->getJIDs().begin(), conv->getJIDs().end(), presence->getFrom()) != conv->getJIDs().end()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource());
 
				}
 
				else {
 
					conv->addJID(presence->getFrom());
 
					conv->sendParticipants(presence->getFrom());
 
					conv->sendCachedMessages(presence->getFrom());
 
				}
 
 
				if (forceJoin) {
 
					onRawPresenceReceived(presence);
 
					onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 
				}
 
				return;
 
			}
 
 
			bool isInJoined = false;
 
			BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
 
				if (p->getTo() == presence->getTo()) {
 
					isInJoined = true;
 
				}
 
			}
 
			if (!isInJoined) {
 
				m_joinedRooms.push_back(presence);
 
			}
 
 
			if (!m_connected) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Joining room " << room << " postponed, because use is not connected to legacy network yet.");
 
				return;
 
			}
 
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource());
 
 
			conv = m_component->getFactory()->createConversation(m_conversationManager, room, true);
 
			m_conversationManager->addConversation(conv);
 
			conv->setNickname(presence->getTo().getResource());
 
			conv->addJID(presence->getFrom());
 
 
			onRawPresenceReceived(presence);
 
			onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 
 
			return;
 
		}
 
 
		onRawPresenceReceived(presence);
 
	}
 
 
	int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();
 
 
	m_conversationManager->resetResources();
 
 
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		m_conversationManager->removeJID(presence->getFrom());
 
 
		std::string presences;
 
		std::vector<Swift::Presence::ref> ps = m_presenceOracle->getAllPresence(m_jid);
 
		BOOST_FOREACH(Swift::Presence::ref p, ps) {
 
			if (p != presence) {
 
				presences += p->getFrom().toString() + " ";
 
			}
 
		};
 
 
		if (!presences.empty()) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences);
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected");
 
		}
 
	}
 
 
 
	// User wants to disconnect this resource
 
	if (!m_component->inServerMode()) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
				// Send unavailable presences for online contacts
 
				m_rosterManager->sendUnavailablePresences(presence->getFrom());
 
 
				// Send unavailable presence for transport contact itself
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		else {
 
			sendCurrentPresence();
 
		}
 
	}
 
 
	// This resource is new, so we have to send buddies presences
 
	if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) {
 
		m_rosterManager->sendCurrentPresences(presence->getFrom());
 
	}
 
 
	m_resources = currentResourcesCount;
 
 
	// Change legacy network presence
 
	if (m_readyForConnect) {
 
		Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
		if (highest) {
 
			if (highest->getType() == Swift::Presence::Unavailable && getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create(highest);
 
			response->setTo(m_jid);
 
			response->setFrom(m_component->getJID());
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << response->getType());
 
			onPresenceChanged(highest);
 
			setCacheMessages(false);
 
		}
 
		else {
 
			if (getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(m_jid.toBare());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			onPresenceChanged(response);
 
		}
 
	}
 
}
 
 
void User::handleSubscription(Swift::Presence::ref presence) {
 
	m_rosterManager->handleSubscription(presence);
 
}
 
 
void User::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	LOG4CXX_INFO(logger, jid.toString() << ": got disco#info");
 
#ifdef SUPPORT_LEGACY_CAPS
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
	// This is old legacy cap which is not stored in entityCapsManager,
 
	// we have to store it in our user class.
 
	if (!discoInfo) {
 
		LOG4CXX_INFO(logger, jid.toString() << ": LEGACY");
 
		m_legacyCaps[jid] = info;
 
	}
 
#endif
 
 
	onConnectingTimeout();
 
}
 
 
void User::onConnectingTimeout() {
 
	if (m_connected || m_readyForConnect)
 
		return;
 
	m_reconnectTimer->stop();
 
	m_readyForConnect = true;
 
	onReadyToConnect();
 
 
	Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
	if (highest) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << highest->getType());
 
		onPresenceChanged(highest);
 
	}
 
}
 
 
void User::setIgnoreDisconnect(bool ignoreDisconnect) {
 
	m_ignoreDisconnect = ignoreDisconnect;
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Setting ignoreDisconnect=" << m_ignoreDisconnect);
 
}
 
 
void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e) {
 
	if (m_ignoreDisconnect) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network ignored (probably moving between backends)");
 
		return;
 
	}
 
 
	if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) {
 
		if (m_reconnectCounter < 3) {
 
			m_reconnectCounter++;
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network " << error << ", trying to reconnect automatically.");
 
			// Simulate destruction/resurrection :)
 
			// TODO: If this stops working, create onReconnect signal
 
			m_userManager->onUserDestroyed(this);
 
			m_userManager->onUserCreated(this);
 
			onReadyToConnect();
 
			return;
 
		}
 
	}
 
 
	if (error.empty()) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network");
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network with error " << error);
 
	}
 
	onDisconnected();
 
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(error);
 
	msg->setTo(m_jid.toBare());
 
	msg->setFrom(m_component->getJID());
 
	msg->addPayload(boost::make_shared<Swift::SpectrumErrorPayload>(e));
 
	m_component->getStanzaChannel()->sendMessage(msg);
 
 
	// In server mode, server finishes the session and pass unavailable session to userManager if we're connected to legacy network,
 
	// so we can't removeUser() in server mode, because it would be removed twice.
 
	// Once in finishSession and once in m_userManager->removeUser.
 
	if (m_component->inServerMode()) {
 
		// Remove user later just to be sure there won't be double-free.
 
		// We can't be sure finishSession sends unavailable presence everytime, so check if user gets removed
 
		// in finishSession(...) call and if not, remove it here.
 
		std::string jid = m_jid.toBare().toString();
 
#if HAVE_SWIFTEN_3
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::ToplevelElement>(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error)));
 
#else
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::Element>(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error)));
 
#endif
 
		if (m_userManager->getUser(jid) != NULL) {
 
			m_userManager->removeUser(this);
 
		}
 
	}
 
	else {
 
		m_userManager->removeUser(this);
 
	}
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/presenceoracle.h"
 
#include "transport/logging.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/SpectrumErrorPayload.h"
 
#include <boost/foreach.hpp>
 
#include <stdio.h>
 
#include <stdlib.h>
 

	
 
using namespace boost;
 

	
 
#define foreach         BOOST_FOREACH
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "User");
 

	
 
User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) {
 
	m_jid = jid.toBare();
 
	m_data = NULL;
 

	
 
	m_cacheMessages = false;
 
	m_component = component;
 
	m_presenceOracle = component->m_presenceOracle;
 
	m_entityCapsManager = component->m_entityCapsManager;
 
	m_userManager = userManager;
 
	m_userInfo = userInfo;
 
	m_connected = false;
 
	m_readyForConnect = false;
 
	m_ignoreDisconnect = false;
 
	m_resources = 0;
 
	m_reconnectCounter = 0;
 

	
 
	m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(5000);
 
	m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); 
 

	
 
	m_rosterManager = new RosterManager(this, m_component);
 
	m_conversationManager = new ConversationManager(this, m_component);
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Created");
 
	updateLastActivity();
 
}
 

	
 
User::~User(){
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Destroying");
 
	if (m_component->inServerMode()) {
 
#if HAVE_SWIFTEN_3
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::ToplevelElement>());
 
#else
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::Element>());
 
#endif
 
	}
 

	
 
	m_reconnectTimer->stop();
 
	delete m_rosterManager;
 
	delete m_conversationManager;
 
}
 

	
 
const Swift::JID &User::getJID() {
 
	return m_jid;
 
}
 

	
 
std::vector<Swift::JID> User::getJIDWithFeature(const std::string &feature) {
 
	std::vector<Swift::JID> jid;
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 

	
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable)
 
			continue;
 

	
 
		Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(presence->getFrom());
 
		if (!discoInfo) {
 
#ifdef SUPPORT_LEGACY_CAPS
 
			if (m_legacyCaps.find(presence->getFrom()) != m_legacyCaps.end()) {
 
				discoInfo = m_legacyCaps[presence->getFrom()];
 
			}
 
			else {
 
				continue;
 
			}
 

	
 
			if (!discoInfo) {
 
				continue;
 
			}
 
#else
 
			continue;
 
#endif
 
		}
 

	
 
		if (discoInfo->hasFeature(feature)) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Found JID with " << feature << " feature: " << presence->getFrom().toString());
 
			jid.push_back(presence->getFrom());
 
		}
 
	}
 

	
 
	if (jid.empty()) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": No JID with " << feature << " feature " << m_legacyCaps.size());
 
	}
 
	return jid;
 
}
 

	
 
Swift::DiscoInfo::ref User::getCaps(const Swift::JID &jid) const {
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
#ifdef SUPPORT_LEGACY_CAPS
 
	if (!discoInfo) {
 
		std::map<Swift::JID, Swift::DiscoInfo::ref>::const_iterator it = m_legacyCaps.find(jid);
 
		if (it != m_legacyCaps.end()) {
 
			discoInfo = it->second;
 
		}
 
	}
 
#endif
 
	return discoInfo;
 
}
 

	
 
void User::sendCurrentPresence() {
 
	if (m_component->inServerMode()) {
 
		return;
 
	}
 

	
 
	std::vector<Swift::Presence::ref> presences = m_presenceOracle->getAllPresence(m_jid);
 
	foreach(Swift::Presence::ref presence, presences) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			continue;
 
		}
 

	
 
		if (m_connected) {
 
			Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
			if (highest) {
 
				Swift::Presence::ref response = Swift::Presence::create(highest);
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
			else {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
		}
 
		else {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			response->setStatus("Connecting");
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
}
 

	
 
void User::setConnected(bool connected) {
 
	m_connected = connected;
 
	m_reconnectCounter = 0;
 
	setIgnoreDisconnect(false);
 
	updateLastActivity();
 

	
 
	sendCurrentPresence();
 

	
 
	if (m_connected) {
 
		BOOST_FOREACH(Swift::Presence::ref &presence, m_joinedRooms) {
 
			handlePresence(presence, true);
 
		}
 
	}
 
}
 

	
 
void User::setCacheMessages(bool cacheMessages) {
 
	if (m_component->inServerMode() && !m_cacheMessages && cacheMessages) {
 
		m_conversationManager->sendCachedChatMessages();
 
	}
 
	m_cacheMessages = cacheMessages;
 
}
 

	
 
void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
 
	LOG4CXX_INFO(logger, "PRESENCE " << presence->getFrom().toString() << " " << presence->getTo().toString());
 
	if (!m_connected) {
 
		// we are not connected to legacy network, so we should do it when disco#info arrive :)
 
		if (m_readyForConnect == false) {
 
			
 
			// Forward status message to legacy network, but only if it's sent from active resource
 
// 					if (m_activeResource == presence->getFrom().getResource().getUTF8String()) {
 
// 						forwardStatus(presenceShow, stanzaStatus);
 
// 					}
 
			boost::shared_ptr<Swift::CapsInfo> capsInfo = presence->getPayload<Swift::CapsInfo>();
 
			if (capsInfo && capsInfo->getHash() == "sha-1") {
 
				if (m_entityCapsManager->getCaps(presence->getFrom()) != Swift::DiscoInfo::ref()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
					m_readyForConnect = true;
 
					onReadyToConnect();
 
				}
 
				else {
 
					m_reconnectTimer->start();
 
				}
 
			}
 
			else if (m_component->inServerMode()) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 
			else {
 
				m_reconnectTimer->start();
 
			}
 
		}
 
	}
 

	
 
	
 
	if (!presence->getTo().getNode().empty()) {
 
		bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			Conversation *conv = m_conversationManager->getConversation(room);
 
			if (conv) {
 
				conv->removeJID(presence->getFrom());
 
				if (!conv->getJIDs().empty()) {
 
					return;
 
				}
 
			}
 
			else {
 
				return;
 
			}
 

	
 
			if (getUserSetting("stay_connected") != "1") {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room);
 
				onRawPresenceReceived(presence);
 
				onRoomLeft(room);
 

	
 
				BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
 
					if (p->getTo() == presence->getTo()) {
 
						m_joinedRooms.remove(p);
 
						break;
 
					}
 
				}
 

	
 
				if (conv) {
 
					m_conversationManager->removeConversation(conv);
 
					delete conv;
 
				}
 
			}
 

	
 
			return;
 
		}
 
		else if (isMUC) {
 
			// force connection to legacy network to let backend to handle auto-join on connect.
 
			if (!m_readyForConnect) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
 
				m_readyForConnect = true;
 
				onReadyToConnect();
 
			}
 

	
 
			std::string room = Buddy::JIDToLegacyName(presence->getTo());
 
			std::string password = "";
 
			if (presence->getPayload<Swift::MUCPayload>() != NULL) {
 
				password = presence->getPayload<Swift::MUCPayload>()->getPassword() ? *presence->getPayload<Swift::MUCPayload>()->getPassword() : "";
 
			}
 

	
 
			Conversation *conv = m_conversationManager->getConversation(room);
 
			if (conv != NULL) {
 
				if (std::find(conv->getJIDs().begin(), conv->getJIDs().end(), presence->getFrom()) != conv->getJIDs().end()) {
 
					LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource());
 
				}
 
				else {
 
					conv->addJID(presence->getFrom());
 
					conv->sendParticipants(presence->getFrom());
 
					conv->sendCachedMessages(presence->getFrom());
 
				}
 

	
 
				if (forceJoin) {
 
					onRawPresenceReceived(presence);
 
					onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 
				}
 
				return;
 
			}
 

	
 
			bool isInJoined = false;
 
			BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
 
				if (p->getTo() == presence->getTo()) {
 
					isInJoined = true;
 
				}
 
			}
 
			if (!isInJoined) {
 
				m_joinedRooms.push_back(presence);
 
			}
 

	
 
			if (!m_connected) {
 
				LOG4CXX_INFO(logger, m_jid.toString() << ": Joining room " << room << " postponed, because use is not connected to legacy network yet.");
 
				return;
 
			}
 

	
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource());
 

	
 
			conv = m_component->getFactory()->createConversation(m_conversationManager, room, true);
 
			m_conversationManager->addConversation(conv);
 
			conv->setNickname(presence->getTo().getResource());
 
			conv->addJID(presence->getFrom());
 

	
 
			onRawPresenceReceived(presence);
 
			onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
 

	
 
			return;
 
		}
 

	
 
		onRawPresenceReceived(presence);
 
	}
 

	
 
	int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();
 

	
 
	m_conversationManager->resetResources();
 

	
 

	
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		m_conversationManager->removeJID(presence->getFrom());
 

	
 
		std::string presences;
 
		std::vector<Swift::Presence::ref> ps = m_presenceOracle->getAllPresence(m_jid);
 
		BOOST_FOREACH(Swift::Presence::ref p, ps) {
 
			if (p != presence) {
 
				presences += p->getFrom().toString() + " ";
 
			}
 
		};
 

	
 
		if (!presences.empty()) {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences);
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected");
 
		}
 
	}
 

	
 

	
 
	// User wants to disconnect this resource
 
	if (!m_component->inServerMode()) {
 
		if (presence->getType() == Swift::Presence::Unavailable) {
 
				// Send unavailable presences for online contacts
 
				m_rosterManager->sendUnavailablePresences(presence->getFrom());
 

	
 
				// Send unavailable presence for transport contact itself
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				response->setType(Swift::Presence::Unavailable);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		else {
 
			sendCurrentPresence();
 
		}
 
	}
 

	
 
	// This resource is new, so we have to send buddies presences
 
	if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) {
 
		m_rosterManager->sendCurrentPresences(presence->getFrom());
 
	}
 

	
 
	m_resources = currentResourcesCount;
 

	
 
	// Change legacy network presence
 
	if (m_readyForConnect) {
 
		Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
		if (highest) {
 
			if (highest->getType() == Swift::Presence::Unavailable && getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create(highest);
 
			response->setTo(m_jid);
 
			response->setFrom(m_component->getJID());
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << response->getType());
 
			onPresenceChanged(highest);
 
			setCacheMessages(false);
 
		}
 
		else {
 
			if (getUserSetting("stay_connected") == "1") {
 
				m_resources = 0;
 
				m_conversationManager->clearJIDs();
 
				setCacheMessages(true);
 
				return;
 
			}
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(m_jid.toBare());
 
			response->setFrom(m_component->getJID());
 
			response->setType(Swift::Presence::Unavailable);
 
			onPresenceChanged(response);
 
		}
 
	}
 
}
 

	
 
void User::handleSubscription(Swift::Presence::ref presence) {
 
	m_rosterManager->handleSubscription(presence);
 
}
 

	
 
void User::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	LOG4CXX_INFO(logger, jid.toString() << ": got disco#info");
 
#ifdef SUPPORT_LEGACY_CAPS
 
	Swift::DiscoInfo::ref discoInfo = m_entityCapsManager->getCaps(jid);
 
	// This is old legacy cap which is not stored in entityCapsManager,
 
	// we have to store it in our user class.
 
	if (!discoInfo) {
 
		LOG4CXX_INFO(logger, jid.toString() << ": LEGACY");
 
		m_legacyCaps[jid] = info;
 
	}
 
#endif
 

	
 
	onConnectingTimeout();
 
}
 

	
 
void User::onConnectingTimeout() {
 
	if (m_connected || m_readyForConnect)
 
		return;
 
	m_reconnectTimer->stop();
 
	m_readyForConnect = true;
 
	onReadyToConnect();
 

	
 
	Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare());
 
	if (highest) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Changing legacy network presence to " << highest->getType());
 
		onPresenceChanged(highest);
 
	}
 
}
 

	
 
void User::setIgnoreDisconnect(bool ignoreDisconnect) {
 
	m_ignoreDisconnect = ignoreDisconnect;
 
	LOG4CXX_INFO(logger, m_jid.toString() << ": Setting ignoreDisconnect=" << m_ignoreDisconnect);
 
}
 

	
 
void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e) {
 
	if (m_ignoreDisconnect) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network ignored (probably moving between backends)");
 
		return;
 
	}
 

	
 
	if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) {
 
		if (m_reconnectCounter < 3) {
 
			m_reconnectCounter++;
 
			LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network " << error << ", trying to reconnect automatically.");
 
			// Simulate destruction/resurrection :)
 
			// TODO: If this stops working, create onReconnect signal
 
			m_userManager->onUserDestroyed(this);
 
			m_userManager->onUserCreated(this);
 
			onReadyToConnect();
 
			return;
 
		}
 
	}
 

	
 
	if (error.empty()) {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network");
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network with error " << error);
 
	}
 
	onDisconnected();
 

	
 
	boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
	msg->setBody(error);
 
	msg->setTo(m_jid.toBare());
 
	msg->setFrom(m_component->getJID());
 
	msg->addPayload(boost::make_shared<Swift::SpectrumErrorPayload>(e));
 
	m_component->getStanzaChannel()->sendMessage(msg);
 

	
 
	// In server mode, server finishes the session and pass unavailable session to userManager if we're connected to legacy network,
 
	// so we can't removeUser() in server mode, because it would be removed twice.
 
	// Once in finishSession and once in m_userManager->removeUser.
 
	if (m_component->inServerMode()) {
 
		// Remove user later just to be sure there won't be double-free.
 
		// We can't be sure finishSession sends unavailable presence everytime, so check if user gets removed
 
		// in finishSession(...) call and if not, remove it here.
 
		std::string jid = m_jid.toBare().toString();
 
#if HAVE_SWIFTEN_3
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::ToplevelElement>(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error)));
 
#else
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(m_jid, boost::shared_ptr<Swift::Element>(new Swift::StreamError(Swift::StreamError::UndefinedCondition, error)));
 
#endif
 
		if (m_userManager->getUser(jid) != NULL) {
 
			m_userManager->removeUser(this);
 
		}
 
	}
 
	else {
 
		m_userManager->removeUser(this);
 
	}
 
}
 

	
 
}
src/usermanager.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "transport/discoitemsresponder.h"
 
#include "storageresponder.h"
 
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/ChatState.h"
 
#ifndef __FreeBSD__ 
 
#ifndef __MACH__
 
#include "malloc.h"
 
#endif
 
#endif
 
// #include "valgrind/memcheck.h"
 
 
namespace Transport {
 
 
DEFINE_LOGGER(logger, "UserManager");
 
 
UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) {
 
	m_cachedUser = NULL;
 
	m_onlineBuddies = 0;
 
	m_sentToXMPP = 0;
 
	m_sentToBackend = 0;
 
	m_component = component;
 
	m_storageBackend = storageBackend;
 
	m_storageResponder = NULL;
 
	m_userRegistry = userRegistry;
 
	m_discoItemsResponder = discoItemsResponder;
 
 
	if (m_storageBackend) {
 
		m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this);
 
		m_storageResponder->start();
 
	}
 
 
	component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1));
 
	component->onUserDiscoInfoReceived.connect(bind(&UserManager::handleDiscoInfo, this, _1, _2));
 
	m_component->getStanzaChannel()->onMessageReceived.connect(bind(&UserManager::handleMessageReceived, this, _1));
 
	m_component->getStanzaChannel()->onPresenceReceived.connect(bind(&UserManager::handleGeneralPresenceReceived, this, _1));
 
 
	m_userRegistry->onConnectUser.connect(bind(&UserManager::connectUser, this, _1));
 
	m_userRegistry->onDisconnectUser.connect(bind(&UserManager::disconnectUser, this, _1));
 
 
	m_removeTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1);
 
}
 
 
UserManager::~UserManager(){
 
	if (m_storageResponder) {
 
		m_storageResponder->stop();
 
		delete m_storageResponder;
 
	}
 
}
 
 
void UserManager::addUser(User *user) {
 
	m_users[user->getJID().toBare().toString()] = user;
 
	if (m_storageBackend) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, true);
 
	}
 
	onUserCreated(user);
 
}
 
 
User *UserManager::getUser(const std::string &barejid){
 
	if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) {
 
		return m_cachedUser;
 
	}
 
 
	if (m_users.find(barejid) != m_users.end()) {
 
		User *user = m_users[barejid];
 
		m_cachedUser = user;
 
		return user;
 
	}
 
	return NULL;
 
}
 
 
Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const {
 
	std::map<std::string, User *>::const_iterator it = m_users.find(jid.toBare().toString());
 
	if (it == m_users.end()) {
 
		return Swift::DiscoInfo::ref();
 
	}
 
 
	User *user = it->second;
 
	return user->getCaps(jid);
 
}
 
 
void UserManager::removeUser(User *user, bool onUserBehalf) {
 
	m_users.erase(user->getJID().toBare().toString());
 
	if (m_cachedUser == user)
 
		m_cachedUser = NULL;
 
 
	if (m_component->inServerMode()) {
 
		disconnectUser(user->getJID());
 
	}
 
	else {
 
		// User could be disconnected by User::handleDisconnect() method, but
 
		// Transport::PresenceOracle could still contain his last presence.
 
		// We have to clear all received presences for this user in PresenceOracle.
 
		m_component->getPresenceOracle()->clearPresences(user->getJID().toBare());
 
	}
 
 
	if (m_storageBackend && onUserBehalf) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, false);
 
	}
 
 
	onUserDestroyed(user);
 
	delete user;
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
	malloc_trim(0);
 
#endif
 
#endif
 
#endif
 
// 	VALGRIND_DO_LEAK_CHECK;
 
}
 
 
void UserManager::removeAllUsers(bool onUserBehalf) {
 
	while(m_users.begin() != m_users.end()) {
 
		removeUser((*m_users.begin()).second, onUserBehalf);
 
	}
 
}
 
 
int UserManager::getUserCount() {
 
	return m_users.size();
 
}
 
 
void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	User *user = getUser(jid.toBare().toString());
 
	if (!user) {
 
		return;
 
	}
 
 
	user->handleDiscoInfo(jid, info);
 
}
 
 
void UserManager::handlePresence(Swift::Presence::ref presence) {
 
	std::string barejid = presence->getTo().toBare().toString();
 
	std::string userkey = presence->getFrom().toBare().toString();
 
 
	User *user = getUser(userkey);
 
	// Create user class if it's not there
 
	if (!user) {
 
		// Admin user is not legacy network user, so do not create User class instance for him
 
		if (m_component->inServerMode()) {
 
			std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"service.admin_jid");
 
			if (std::find(x.begin(), x.end(), presence->getFrom().toBare().toString()) != x.end()) {
 
				// Send admin contact to the user.
 
				Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
				Swift::RosterItemPayload item;
 
				item.setJID(m_component->getJID());
 
				item.setName("Admin");
 
				item.setSubscription(Swift::RosterItemPayload::Both);
 
				payload->addItem(item);
 
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter());
 
				request->send();
 
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
				return;
 
		    }
 
		}
 
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
 
		// No user and unavailable presence -> answer with unavailable
 
		if (presence->getType() == Swift::Presence::Unavailable || presence->getType() == Swift::Presence::Probe) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(presence->getTo());
 
			response->setType(Swift::Presence::Unavailable);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
 
			// bother him with probe presence, just to be
 
			// sure he is subscribed to us.
 
			if (/*registered && */presence->getType() == Swift::Presence::Probe) {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(presence->getTo());
 
				response->setType(Swift::Presence::Probe);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 
 
			// Set user offline in database
 
			if (m_storageBackend) {
 
				UserInfo res;
 
				bool registered = m_storageBackend->getUser(userkey, res);
 
				if (registered) {
 
					m_storageBackend->setUserOnline(res.id, false);
 
				}
 
			}
 
			return;
 
		}
 
 
		// In server mode, we don't need registration normally, but for networks like IRC
 
		// or Twitter where there's no real authorization using password, we have to force
 
		// registration otherwise some data (like bookmarked rooms) could leak.
 
		if (m_component->inServerMode()) {
 
			if (!registered) {
 
				// If we need registration, stop login process because user is not registered
 
				if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", false)) {
 
					m_userRegistry->onPasswordInvalid(presence->getFrom());
 
					return;
 
				}
 
				res.password = "";
 
				res.uin = presence->getFrom().getNode();
 
				res.jid = userkey;
 
				while (res.uin.find_last_of("%") != std::string::npos) { // OK
 
					res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK
 
				}
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
			res.password = m_userRegistry->getUserPassword(userkey);
 
		}
 
 
		// We allow auto_register feature in gateway-mode. This allows IRC user to register
 
		// the transport just by joining the room.
 
		if (!m_component->inServerMode()) {
 
			if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register")
 
				/*!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)*/)) {
 
				res.password = "";
 
				res.jid = userkey;
 
 
				bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
				if (isMUC) {
 
					res.uin = presence->getTo().getResource();
 
				}
 
				else {
 
					res.uin = presence->getFrom().toString();
 
				}
 
				LOG4CXX_INFO(logger, "Auto-registering user " << userkey << " with uin=" << res.uin);
 
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
		}
 
 
		// Unregistered users are not able to login
 
		if (!registered) {
 
			LOG4CXX_WARN(logger, "Unregistered user " << userkey << " tried to login");
 
			return;
 
		}
 
 
		if (CONFIG_BOOL(m_component->getConfig(), "service.vip_only") && res.vip == false) {
 
			if (!CONFIG_STRING(m_component->getConfig(), "service.vip_message").empty()) {
 
				boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
				msg->setBody(CONFIG_STRING(m_component->getConfig(), "service.vip_message"));
 
				msg->setTo(presence->getFrom());
 
				msg->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendMessage(msg);
 
			}
 
 
			LOG4CXX_WARN(logger, "Non VIP user " << userkey << " tried to login");
 
			if (m_component->inServerMode()) {
 
				m_userRegistry->onPasswordInvalid(presence->getFrom());
 
			}
 
			return;
 
		}
 
 
		bool transport_enabled = true;
 
		if (m_storageBackend) {
 
			std::string value = "1";
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(res.id, "enable_transport", type, value);
 
			transport_enabled = value == "1";
 
		}
 
		// User can disabled the transport using adhoc commands
 
		if (!transport_enabled) {
 
			LOG4CXX_INFO(logger, "User " << userkey << " has disabled transport, not logging");
 
			return;
 
		}
 
 
		// Create new user class and set storagebackend
 
		user = new User(presence->getFrom(), res, m_component, this);
 
		user->getRosterManager()->setStorageBackend(m_storageBackend);
 
		addUser(user);
 
	}
 
 
	// User can be handleDisconnected in addUser callback, so refresh the pointer
 
	user = getUser(userkey);
 
	if (!user) {
 
		m_userRegistry->onPasswordInvalid(presence->getFrom());
 
		return;
 
	}
 
 
	// Handle this presence
 
	user->handlePresence(presence);
 
 
	// Unavailable MUC presence should not trigger whole account disconnection, so block it here.
 
	bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	if (isMUC)
 
		return;
 
 
	// Unavailable presence could remove this user, because he could be unavailable
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		if (user) {
 
			if (user->getUserSetting("stay_connected") == "1") {
 
				return;
 
			}
 
 
			Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(presence->getFrom().toBare());
 
			// There's no presence for this user, so disconnect
 
			if (!highest || (highest && highest->getType() == Swift::Presence::Unavailable)) {
 
				m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user->getJID().toBare().toString(), user, false));
 
				m_removeTimer->start();
 
			}
 
		}
 
	}
 
}
 
 
void UserManager::handleRemoveTimeout(const std::string jid, User *u, bool reconnect) {
 
	m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, jid, u, reconnect));
 
 
	// Maybe this User instance has been deleted in mean time and we would remove new one,
 
	// so better check for it and ignore deletion if "u" does not exist anymore.
 
	User *user = getUser(jid);
 
	if (user != u) {
 
		return;
 
	}
 
 
	// Remove user
 
	if (user) {
 
		removeUser(user);
 
	}
 
 
	// Connect the user again when we're reconnecting.
 
	if (reconnect) {
 
		connectUser(jid);
 
	}
 
}
 
 
void UserManager::handleMessageReceived(Swift::Message::ref message) {
 
	if (message->getType() == Swift::Message::Error) {
 
		return;
 
	}
 
 
	// Do not count chatstate notification...
 
	boost::shared_ptr<Swift::ChatState> statePayload = message->getPayload<Swift::ChatState>();
 
	if (!statePayload) {
 
		messageToBackendSent();
 
	}
 
 
	if (message->getBody().empty() && !statePayload && message->getSubject().empty()) {
 
		return;
 
	}
 
 
	User *user = getUser(message->getFrom().toBare().toString());
 
	if (!user){
 
		return;
 
	}
 
 
	user->getConversationManager()->handleMessageReceived(message);
 
}
 
 
void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) {
 
	switch(presence->getType()) {
 
		case Swift::Presence::Subscribe:
 
		case Swift::Presence::Subscribed:
 
		case Swift::Presence::Unsubscribe:
 
		case Swift::Presence::Unsubscribed:
 
			handleSubscription(presence);
 
			break;
 
		case Swift::Presence::Available:
 
		case Swift::Presence::Unavailable:
 
			handleMUCPresence(presence);
 
			break;
 
		case Swift::Presence::Probe:
 
			handleProbePresence(presence);
 
			break;
 
		case Swift::Presence::Error:
 
			handleErrorPresence(presence);
 
			break;
 
		default:
 
			break;
 
	};
 
}
 
 
void UserManager::handleMUCPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 
 
	if (presence->getType() == Swift::Presence::Available) {
 
		handlePresence(presence);
 
	}
 
	else if (presence->getType() == Swift::Presence::Unavailable) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		User *user = getUser(userkey);
 
		if (user) {
 
			user->handlePresence(presence);
 
		}
 
	}
 
}
 
 
void UserManager::handleProbePresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 
 
	User *user = getUser(presence->getFrom().toBare().toString());
 
 
 	if (user) {
 
 		user->getRosterManager()->sendCurrentPresence(presence->getTo(), presence->getFrom());
 
 	}
 
 	else {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unavailable);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
}
 
 
void UserManager::handleErrorPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (!presence->getTo().getNode().empty()) {
 
		return;
 
	}
 
 
	if (!presence->getPayload<Swift::ErrorPayload>()) {
 
		return;
 
	}
 
 
	if (presence->getPayload<Swift::ErrorPayload>()->getCondition() != Swift::ErrorPayload::SubscriptionRequired) {
 
		return;
 
	}
 
 
	std::string userkey = presence->getFrom().toBare().toString();
 
	UserInfo res;
 
	bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
	if (registered) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo().toBare());
 
		response->setTo(presence->getFrom().toBare());
 
		response->setType(Swift::Presence::Subscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
}
 
 
void UserManager::handleSubscription(Swift::Presence::ref presence) {
 
	
 
	// answer to subscibe for transport itself
 
	if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().empty()) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo().toBare());
 
		response->setTo(presence->getFrom().toBare());
 
		response->setType(Swift::Presence::Subscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
 
// 		response = Swift::Presence::create();
 
// 		response->setFrom(presence->getTo());
 
// 		response->setTo(presence->getFrom());
 
// 		response->setType(Swift::Presence::Subscribe);
 
// 		m_component->getStanzaChannel()->sendPresence(response);
 
		return;
 
	}
 
	else if (presence->getType() == Swift::Presence::Unsubscribed && presence->getTo().getNode().empty()) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
		if (registered) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(presence->getTo().toBare());
 
			response->setTo(presence->getFrom().toBare());
 
			response->setType(Swift::Presence::Subscribe);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		return;
 
	}
 
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 
 
	User *user = getUser(presence->getFrom().toBare().toString());
 
 
 	if (user) {
 
 		user->handleSubscription(presence);
 
 	}
 
 	else if (presence->getType() == Swift::Presence::Unsubscribe) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unsubscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
// 	else {
 
// // 		Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in");
 
// 	}
 
}
 
 
void UserManager::connectUser(const Swift::JID &user) {
 
	// Called by UserRegistry in server mode when user connects the server and wants
 
	// to connect legacy network
 
	if (m_users.find(user.toBare().toString()) != m_users.end()) {
 
		if (!m_component->inServerMode()) {
 
			return;
 
		}
 
 
		User *u = m_users[user.toBare().toString()];
 
		if (u->isConnected()) {
 
			// User is already logged in, so his password is OK, but this new user has different password => bad password.
 
			// We can't call m_userRegistry->onPasswordInvalid() here, because this fuction is called from Swift::Parser
 
			// and onPasswordInvalid destroys whole session together with parser itself, which leads to crash.
 
			if (m_userRegistry->getUserPassword(user.toBare().toString()) != u->getUserInfo().password) {
 
				m_userRegistry->removeLater(user);
 
				return;
 
			}
 
			if (CONFIG_BOOL(m_component->getConfig(), "service.more_resources")) {
 
				m_userRegistry->onPasswordValid(user);
 
			}
 
			else {
 
				// Send message to currently logged in session
 
				boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
				msg->setBody("You have signed on from another location.");
 
				msg->setTo(user);
 
				msg->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendMessage(msg);
 
 
				// Switch the session = accept new one, disconnect old one.
 
				// Unavailable presence from old session has to be ignored, otherwise it would disconnect the user from legacy network.
 
				m_userRegistry->onPasswordValid(user);
 
				m_component->onUserPresenceReceived.disconnect(bind(&UserManager::handlePresence, this, _1));
 
#if HAVE_SWIFTEN_3
 
				dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr<Swift::ToplevelElement>(new Swift::StreamError()), true);
 
#else				
 
				dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr<Swift::Element>(new Swift::StreamError()), true);
 
#endif
 
				m_component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1));
 
			}
 
		}
 
		else {
 
			// User is created, but not connected => he's loggin in or he just logged out, but hasn't been deleted yet.
 
			// Stop deletion process if there's any
 
			m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], false));
 
 
			// Delete old User instance but create new one immediatelly
 
			m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], true));
 
			m_removeTimer->start();
 
		}
 
	}
 
	else {
 
		// simulate initial available presence to start connecting this user.
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo(m_component->getJID());
 
		response->setFrom(user);
 
		response->setType(Swift::Presence::Available);
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->onPresenceReceived(response);
 
	}
 
}
 
 
 
void UserManager::disconnectUser(const Swift::JID &user) {
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(m_component->getJID());
 
	response->setFrom(user);
 
	response->setType(Swift::Presence::Unavailable);
 
	dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->onPresenceReceived(response);
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/usermanager.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/rostermanager.h"
 
#include "transport/userregistry.h"
 
#include "transport/logging.h"
 
#include "transport/discoitemsresponder.h"
 
#include "storageresponder.h"
 

	
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Elements/MUCPayload.h"
 
#include "Swiften/Elements/ChatState.h"
 
#ifndef __FreeBSD__ 
 
#ifndef __MACH__
 
#include "malloc.h"
 
#endif
 
#endif
 
// #include "valgrind/memcheck.h"
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserManager");
 

	
 
UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) {
 
	m_cachedUser = NULL;
 
	m_onlineBuddies = 0;
 
	m_sentToXMPP = 0;
 
	m_sentToBackend = 0;
 
	m_component = component;
 
	m_storageBackend = storageBackend;
 
	m_storageResponder = NULL;
 
	m_userRegistry = userRegistry;
 
	m_discoItemsResponder = discoItemsResponder;
 

	
 
	if (m_storageBackend) {
 
		m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this);
 
		m_storageResponder->start();
 
	}
 

	
 
	component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1));
 
	component->onUserDiscoInfoReceived.connect(bind(&UserManager::handleDiscoInfo, this, _1, _2));
 
	m_component->getStanzaChannel()->onMessageReceived.connect(bind(&UserManager::handleMessageReceived, this, _1));
 
	m_component->getStanzaChannel()->onPresenceReceived.connect(bind(&UserManager::handleGeneralPresenceReceived, this, _1));
 

	
 
	m_userRegistry->onConnectUser.connect(bind(&UserManager::connectUser, this, _1));
 
	m_userRegistry->onDisconnectUser.connect(bind(&UserManager::disconnectUser, this, _1));
 

	
 
	m_removeTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1);
 
}
 

	
 
UserManager::~UserManager(){
 
	if (m_storageResponder) {
 
		m_storageResponder->stop();
 
		delete m_storageResponder;
 
	}
 
}
 

	
 
void UserManager::addUser(User *user) {
 
	m_users[user->getJID().toBare().toString()] = user;
 
	if (m_storageBackend) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, true);
 
	}
 
	onUserCreated(user);
 
}
 

	
 
User *UserManager::getUser(const std::string &barejid){
 
	if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) {
 
		return m_cachedUser;
 
	}
 

	
 
	if (m_users.find(barejid) != m_users.end()) {
 
		User *user = m_users[barejid];
 
		m_cachedUser = user;
 
		return user;
 
	}
 
	return NULL;
 
}
 

	
 
Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const {
 
	std::map<std::string, User *>::const_iterator it = m_users.find(jid.toBare().toString());
 
	if (it == m_users.end()) {
 
		return Swift::DiscoInfo::ref();
 
	}
 

	
 
	User *user = it->second;
 
	return user->getCaps(jid);
 
}
 

	
 
void UserManager::removeUser(User *user, bool onUserBehalf) {
 
	m_users.erase(user->getJID().toBare().toString());
 
	if (m_cachedUser == user)
 
		m_cachedUser = NULL;
 

	
 
	if (m_component->inServerMode()) {
 
		disconnectUser(user->getJID());
 
	}
 
	else {
 
		// User could be disconnected by User::handleDisconnect() method, but
 
		// Transport::PresenceOracle could still contain his last presence.
 
		// We have to clear all received presences for this user in PresenceOracle.
 
		m_component->getPresenceOracle()->clearPresences(user->getJID().toBare());
 
	}
 

	
 
	if (m_storageBackend && onUserBehalf) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, false);
 
	}
 

	
 
	onUserDestroyed(user);
 
	delete user;
 
#ifndef WIN32
 
#ifndef __FreeBSD__
 
#ifndef __MACH__
 
	malloc_trim(0);
 
#endif
 
#endif
 
#endif
 
// 	VALGRIND_DO_LEAK_CHECK;
 
}
 

	
 
void UserManager::removeAllUsers(bool onUserBehalf) {
 
	while(m_users.begin() != m_users.end()) {
 
		removeUser((*m_users.begin()).second, onUserBehalf);
 
	}
 
}
 

	
 
int UserManager::getUserCount() {
 
	return m_users.size();
 
}
 

	
 
void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	User *user = getUser(jid.toBare().toString());
 
	if (!user) {
 
		return;
 
	}
 

	
 
	user->handleDiscoInfo(jid, info);
 
}
 

	
 
void UserManager::handlePresence(Swift::Presence::ref presence) {
 
	std::string barejid = presence->getTo().toBare().toString();
 
	std::string userkey = presence->getFrom().toBare().toString();
 

	
 
	User *user = getUser(userkey);
 
	// Create user class if it's not there
 
	if (!user) {
 
		// Admin user is not legacy network user, so do not create User class instance for him
 
		if (m_component->inServerMode()) {
 
			std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"service.admin_jid");
 
			if (std::find(x.begin(), x.end(), presence->getFrom().toBare().toString()) != x.end()) {
 
				// Send admin contact to the user.
 
				Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
				Swift::RosterItemPayload item;
 
				item.setJID(m_component->getJID());
 
				item.setName("Admin");
 
				item.setSubscription(Swift::RosterItemPayload::Both);
 
				payload->addItem(item);
 

	
 
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter());
 
				request->send();
 

	
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendPresence(response);
 
				return;
 
		    }
 
		}
 

	
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 

	
 
		// No user and unavailable presence -> answer with unavailable
 
		if (presence->getType() == Swift::Presence::Unavailable || presence->getType() == Swift::Presence::Probe) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setTo(presence->getFrom());
 
			response->setFrom(presence->getTo());
 
			response->setType(Swift::Presence::Unavailable);
 
			m_component->getStanzaChannel()->sendPresence(response);
 

	
 
			// bother him with probe presence, just to be
 
			// sure he is subscribed to us.
 
			if (/*registered && */presence->getType() == Swift::Presence::Probe) {
 
				Swift::Presence::ref response = Swift::Presence::create();
 
				response->setTo(presence->getFrom());
 
				response->setFrom(presence->getTo());
 
				response->setType(Swift::Presence::Probe);
 
				m_component->getStanzaChannel()->sendPresence(response);
 
			}
 

	
 
			// Set user offline in database
 
			if (m_storageBackend) {
 
				UserInfo res;
 
				bool registered = m_storageBackend->getUser(userkey, res);
 
				if (registered) {
 
					m_storageBackend->setUserOnline(res.id, false);
 
				}
 
			}
 
			return;
 
		}
 

	
 
		// In server mode, we don't need registration normally, but for networks like IRC
 
		// or Twitter where there's no real authorization using password, we have to force
 
		// registration otherwise some data (like bookmarked rooms) could leak.
 
		if (m_component->inServerMode()) {
 
			if (!registered) {
 
				// If we need registration, stop login process because user is not registered
 
				if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", false)) {
 
					m_userRegistry->onPasswordInvalid(presence->getFrom());
 
					return;
 
				}
 
				res.password = "";
 
				res.uin = presence->getFrom().getNode();
 
				res.jid = userkey;
 
				while (res.uin.find_last_of("%") != std::string::npos) { // OK
 
					res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK
 
				}
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
			res.password = m_userRegistry->getUserPassword(userkey);
 
		}
 

	
 
		// We allow auto_register feature in gateway-mode. This allows IRC user to register
 
		// the transport just by joining the room.
 
		if (!m_component->inServerMode()) {
 
			if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register")
 
				/*!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)*/)) {
 
				res.password = "";
 
				res.jid = userkey;
 

	
 
				bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
				if (isMUC) {
 
					res.uin = presence->getTo().getResource();
 
				}
 
				else {
 
					res.uin = presence->getFrom().toString();
 
				}
 
				LOG4CXX_INFO(logger, "Auto-registering user " << userkey << " with uin=" << res.uin);
 

	
 
				if (m_storageBackend) {
 
					// store user and getUser again to get user ID.
 
					m_storageBackend->setUser(res);
 
					registered = m_storageBackend->getUser(userkey, res);
 
				}
 
				else {
 
					registered = true;
 
				}
 
			}
 
		}
 

	
 
		// Unregistered users are not able to login
 
		if (!registered) {
 
			LOG4CXX_WARN(logger, "Unregistered user " << userkey << " tried to login");
 
			return;
 
		}
 

	
 
		if (CONFIG_BOOL(m_component->getConfig(), "service.vip_only") && res.vip == false) {
 
			if (!CONFIG_STRING(m_component->getConfig(), "service.vip_message").empty()) {
 
				boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
				msg->setBody(CONFIG_STRING(m_component->getConfig(), "service.vip_message"));
 
				msg->setTo(presence->getFrom());
 
				msg->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendMessage(msg);
 
			}
 

	
 
			LOG4CXX_WARN(logger, "Non VIP user " << userkey << " tried to login");
 
			if (m_component->inServerMode()) {
 
				m_userRegistry->onPasswordInvalid(presence->getFrom());
 
			}
 
			return;
 
		}
 

	
 
		bool transport_enabled = true;
 
		if (m_storageBackend) {
 
			std::string value = "1";
 
			int type = (int) TYPE_BOOLEAN;
 
			m_storageBackend->getUserSetting(res.id, "enable_transport", type, value);
 
			transport_enabled = value == "1";
 
		}
 
		// User can disabled the transport using adhoc commands
 
		if (!transport_enabled) {
 
			LOG4CXX_INFO(logger, "User " << userkey << " has disabled transport, not logging");
 
			return;
 
		}
 

	
 
		// Create new user class and set storagebackend
 
		user = new User(presence->getFrom(), res, m_component, this);
 
		user->getRosterManager()->setStorageBackend(m_storageBackend);
 
		addUser(user);
 
	}
 

	
 
	// User can be handleDisconnected in addUser callback, so refresh the pointer
 
	user = getUser(userkey);
 
	if (!user) {
 
		m_userRegistry->onPasswordInvalid(presence->getFrom());
 
		return;
 
	}
 

	
 
	// Handle this presence
 
	user->handlePresence(presence);
 

	
 
	// Unavailable MUC presence should not trigger whole account disconnection, so block it here.
 
	bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
 
	if (isMUC)
 
		return;
 

	
 
	// Unavailable presence could remove this user, because he could be unavailable
 
	if (presence->getType() == Swift::Presence::Unavailable) {
 
		if (user) {
 
			if (user->getUserSetting("stay_connected") == "1") {
 
				return;
 
			}
 

	
 
			Swift::Presence::ref highest = m_component->getPresenceOracle()->getHighestPriorityPresence(presence->getFrom().toBare());
 
			// There's no presence for this user, so disconnect
 
			if (!highest || (highest && highest->getType() == Swift::Presence::Unavailable)) {
 
				m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user->getJID().toBare().toString(), user, false));
 
				m_removeTimer->start();
 
			}
 
		}
 
	}
 
}
 

	
 
void UserManager::handleRemoveTimeout(const std::string jid, User *u, bool reconnect) {
 
	m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, jid, u, reconnect));
 

	
 
	// Maybe this User instance has been deleted in mean time and we would remove new one,
 
	// so better check for it and ignore deletion if "u" does not exist anymore.
 
	User *user = getUser(jid);
 
	if (user != u) {
 
		return;
 
	}
 

	
 
	// Remove user
 
	if (user) {
 
		removeUser(user);
 
	}
 

	
 
	// Connect the user again when we're reconnecting.
 
	if (reconnect) {
 
		connectUser(jid);
 
	}
 
}
 

	
 
void UserManager::handleMessageReceived(Swift::Message::ref message) {
 
	if (message->getType() == Swift::Message::Error) {
 
		return;
 
	}
 

	
 
	// Do not count chatstate notification...
 
	boost::shared_ptr<Swift::ChatState> statePayload = message->getPayload<Swift::ChatState>();
 
	if (!statePayload) {
 
		messageToBackendSent();
 
	}
 

	
 
	if (message->getBody().empty() && !statePayload && message->getSubject().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(message->getFrom().toBare().toString());
 
	if (!user){
 
		return;
 
	}
 

	
 
	user->getConversationManager()->handleMessageReceived(message);
 
}
 

	
 
void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) {
 
	switch(presence->getType()) {
 
		case Swift::Presence::Subscribe:
 
		case Swift::Presence::Subscribed:
 
		case Swift::Presence::Unsubscribe:
 
		case Swift::Presence::Unsubscribed:
 
			handleSubscription(presence);
 
			break;
 
		case Swift::Presence::Available:
 
		case Swift::Presence::Unavailable:
 
			handleMUCPresence(presence);
 
			break;
 
		case Swift::Presence::Probe:
 
			handleProbePresence(presence);
 
			break;
 
		case Swift::Presence::Error:
 
			handleErrorPresence(presence);
 
			break;
 
		default:
 
			break;
 
	};
 
}
 

	
 
void UserManager::handleMUCPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	if (presence->getType() == Swift::Presence::Available) {
 
		handlePresence(presence);
 
	}
 
	else if (presence->getType() == Swift::Presence::Unavailable) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		User *user = getUser(userkey);
 
		if (user) {
 
			user->handlePresence(presence);
 
		}
 
	}
 
}
 

	
 
void UserManager::handleProbePresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(presence->getFrom().toBare().toString());
 

	
 
 	if (user) {
 
 		user->getRosterManager()->sendCurrentPresence(presence->getTo(), presence->getFrom());
 
 	}
 
 	else {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unavailable);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
}
 

	
 
void UserManager::handleErrorPresence(Swift::Presence::ref presence) {
 
	// Don't let RosterManager to handle presences for us
 
	if (!presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	if (!presence->getPayload<Swift::ErrorPayload>()) {
 
		return;
 
	}
 

	
 
	if (presence->getPayload<Swift::ErrorPayload>()->getCondition() != Swift::ErrorPayload::SubscriptionRequired) {
 
		return;
 
	}
 

	
 
	std::string userkey = presence->getFrom().toBare().toString();
 
	UserInfo res;
 
	bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
	if (registered) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo().toBare());
 
		response->setTo(presence->getFrom().toBare());
 
		response->setType(Swift::Presence::Subscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
}
 

	
 
void UserManager::handleSubscription(Swift::Presence::ref presence) {
 
	
 
	// answer to subscibe for transport itself
 
	if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().empty()) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo().toBare());
 
		response->setTo(presence->getFrom().toBare());
 
		response->setType(Swift::Presence::Subscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 

	
 
// 		response = Swift::Presence::create();
 
// 		response->setFrom(presence->getTo());
 
// 		response->setTo(presence->getFrom());
 
// 		response->setType(Swift::Presence::Subscribe);
 
// 		m_component->getStanzaChannel()->sendPresence(response);
 
		return;
 
	}
 
	else if (presence->getType() == Swift::Presence::Unsubscribed && presence->getTo().getNode().empty()) {
 
		std::string userkey = presence->getFrom().toBare().toString();
 
		UserInfo res;
 
		bool registered = m_storageBackend ? m_storageBackend->getUser(userkey, res) : false;
 
		if (registered) {
 
			Swift::Presence::ref response = Swift::Presence::create();
 
			response->setFrom(presence->getTo().toBare());
 
			response->setTo(presence->getFrom().toBare());
 
			response->setType(Swift::Presence::Subscribe);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
		return;
 
	}
 

	
 
	// Don't let RosterManager to handle presences for us
 
	if (presence->getTo().getNode().empty()) {
 
		return;
 
	}
 

	
 
	User *user = getUser(presence->getFrom().toBare().toString());
 

	
 
 	if (user) {
 
 		user->handleSubscription(presence);
 
 	}
 
 	else if (presence->getType() == Swift::Presence::Unsubscribe) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(presence->getTo());
 
		response->setTo(presence->getFrom());
 
		response->setType(Swift::Presence::Unsubscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
// 	else {
 
// // 		Log(presence->getFrom().toString().getUTF8String(), "Subscribe presence received, but this user is not logged in");
 
// 	}
 
}
 

	
 
void UserManager::connectUser(const Swift::JID &user) {
 
	// Called by UserRegistry in server mode when user connects the server and wants
 
	// to connect legacy network
 
	if (m_users.find(user.toBare().toString()) != m_users.end()) {
 
		if (!m_component->inServerMode()) {
 
			return;
 
		}
 

	
 
		User *u = m_users[user.toBare().toString()];
 
		if (u->isConnected()) {
 
			// User is already logged in, so his password is OK, but this new user has different password => bad password.
 
			// We can't call m_userRegistry->onPasswordInvalid() here, because this fuction is called from Swift::Parser
 
			// and onPasswordInvalid destroys whole session together with parser itself, which leads to crash.
 
			if (m_userRegistry->getUserPassword(user.toBare().toString()) != u->getUserInfo().password) {
 
				m_userRegistry->removeLater(user);
 
				return;
 
			}
 
			if (CONFIG_BOOL(m_component->getConfig(), "service.more_resources")) {
 
				m_userRegistry->onPasswordValid(user);
 
			}
 
			else {
 
				// Send message to currently logged in session
 
				boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
				msg->setBody("You have signed on from another location.");
 
				msg->setTo(user);
 
				msg->setFrom(m_component->getJID());
 
				m_component->getStanzaChannel()->sendMessage(msg);
 

	
 
				// Switch the session = accept new one, disconnect old one.
 
				// Unavailable presence from old session has to be ignored, otherwise it would disconnect the user from legacy network.
 
				m_userRegistry->onPasswordValid(user);
 
				m_component->onUserPresenceReceived.disconnect(bind(&UserManager::handlePresence, this, _1));
 
#if HAVE_SWIFTEN_3
 
				dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr<Swift::ToplevelElement>(new Swift::StreamError()), true);
 
#else				
 
				dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->finishSession(user, boost::shared_ptr<Swift::Element>(new Swift::StreamError()), true);
 
#endif
 
				m_component->onUserPresenceReceived.connect(bind(&UserManager::handlePresence, this, _1));
 
			}
 
		}
 
		else {
 
			// User is created, but not connected => he's loggin in or he just logged out, but hasn't been deleted yet.
 
			// Stop deletion process if there's any
 
			m_removeTimer->onTick.disconnect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], false));
 

	
 
			// Delete old User instance but create new one immediatelly
 
			m_removeTimer->onTick.connect(boost::bind(&UserManager::handleRemoveTimeout, this, user.toBare().toString(), m_users[user.toBare().toString()], true));
 
			m_removeTimer->start();
 
		}
 
	}
 
	else {
 
		// simulate initial available presence to start connecting this user.
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setTo(m_component->getJID());
 
		response->setFrom(user);
 
		response->setType(Swift::Presence::Available);
 
		dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->onPresenceReceived(response);
 
	}
 
}
 

	
 

	
 
void UserManager::disconnectUser(const Swift::JID &user) {
 
	Swift::Presence::ref response = Swift::Presence::create();
 
	response->setTo(m_component->getJID());
 
	response->setFrom(user);
 
	response->setType(Swift::Presence::Unavailable);
 
	dynamic_cast<Swift::ServerStanzaChannel *>(m_component->getStanzaChannel())->onPresenceReceived(response);
 
}
 

	
 
}
src/userregistration.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 
 
#include "transport/userregistration.h"
 
#include "transport/usermanager.h"
 
#include "transport/storagebackend.h"
 
#include "transport/transport.h"
 
#include "transport/rostermanager.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "Swiften/Elements/ErrorPayload.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 
#include "Swiften/Network/BoostNetworkFactories.h"
 
#include "Swiften/Client/Client.h"
 
#include <boost/shared_ptr.hpp>
 
#include <boost/thread.hpp>
 
#include <boost/date_time/posix_time/posix_time.hpp>
 
#include <boost/regex.hpp> 
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Elements/Form.h>
 
#endif
 
 
using namespace Swift;
 
 
namespace Transport {
 
 
DEFINE_LOGGER(logger, "UserRegistration");
 
 
UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::Responder<Swift::InBandRegistrationPayload>(component->m_iqRouter) {
 
	m_component = component;
 
	m_config = m_component->m_config;
 
	m_storageBackend = storageBackend;
 
	m_userManager = userManager;
 
}
 
 
UserRegistration::~UserRegistration(){
 
}
 
 
bool UserRegistration::registerUser(const UserInfo &row) {
 
	UserInfo user;
 
	bool registered = m_storageBackend->getUser(row.jid, user);
 
	// This user is already registered
 
	if (registered)
 
		return false;
 
 
	m_storageBackend->setUser(row);
 
 
	//same as in unregisterUser but here we have to pass UserInfo to handleRegisterRRResponse
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(),row.jid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleRegisterRemoteRosterResponse, this, _1, _2, row));
 
	request->send();
 
 
	return true;
 
}
 
 
bool UserRegistration::unregisterUser(const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return false;
 
 
	onUserUnregistered(userInfo);
 
 
	// We have to check if server supports remoteroster XEP and use it if it's supported or fallback to unsubscribe otherwise
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), barejid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleUnregisterRemoteRosterResponse, this, _1, _2, barejid));
 
	request->send();
 
 
	return true;
 
}
 
 
void UserRegistration::handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const UserInfo &row){
 
	if (remoteRosterNotSupported || !payload) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(m_component->getJID());
 
		response->setTo(Swift::JID(row.jid));
 
		response->setType(Swift::Presence::Subscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
	else{
 
		Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
		Swift::RosterItemPayload item;
 
		item.setJID(m_component->getJID());
 
		item.setSubscription(Swift::RosterItemPayload::Both);
 
		payload->addItem(item);
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, row.jid, m_component->getIQRouter());
 
		request->send();
 
	}
 
	onUserRegistered(row);
 
 
	std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid");
 
	BOOST_FOREACH(const std::string &notify_jid, x) {
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody(std::string("registered: ") + row.jid);
 
		msg->setTo(notify_jid);
 
		msg->setFrom(m_component->getJID());
 
		m_component->getStanzaChannel()->sendMessage(msg);
 
	}
 
}
 
 
void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return;
 
 
	if (remoteRosterNotSupported || !payload) {
 
		std::list <BuddyInfo> roster;
 
		m_storageBackend->getBuddies(userInfo.id, roster);
 
		for(std::list<BuddyInfo>::iterator u = roster.begin(); u != roster.end() ; u++){
 
			std::string name = (*u).legacyName;
 
			if ((*u).flags & BUDDY_JID_ESCAPING) {
 
				name = Swift::JID::getEscapedNode((*u).legacyName);
 
			}
 
			else {
 
				if (name.find_last_of("@") != std::string::npos) {
 
					name.replace(name.find_last_of("@"), 1, "%");
 
				}
 
			}
 
 
			Swift::Presence::ref response;
 
			response = Swift::Presence::create();
 
			response->setTo(Swift::JID(barejid));
 
			response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
			response->setType(Swift::Presence::Unsubscribe);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
 
			response = Swift::Presence::create();
 
			response->setTo(Swift::JID(barejid));
 
			response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
			response->setType(Swift::Presence::Unsubscribed);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
	else {
 
		BOOST_FOREACH(Swift::RosterItemPayload it, payload->getItems()) {
 
			Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
			Swift::RosterItemPayload item;
 
			item.setJID(it.getJID());
 
			item.setSubscription(Swift::RosterItemPayload::Remove);
 
 
			p->addItem(item);
 
 
			Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, barejid, m_component->getIQRouter());
 
			request->send();
 
		}
 
	}
 
 
	// Remove user from database
 
	m_storageBackend->removeUser(userInfo.id);
 
 
	// Disconnect the user
 
	User *user = m_userManager->getUser(barejid);
 
	if (user) {
 
		m_userManager->removeUser(user);
 
	}
 
 
	if (remoteRosterNotSupported || !payload) {
 
		Swift::Presence::ref response;
 
		response = Swift::Presence::create();
 
		response->setTo(Swift::JID(barejid));
 
		response->setFrom(m_component->getJID());
 
		response->setType(Swift::Presence::Unsubscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
 
		response = Swift::Presence::create();
 
		response->setTo(Swift::JID(barejid));
 
		response->setFrom(m_component->getJID());
 
		response->setType(Swift::Presence::Unsubscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
	else {
 
		Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
		Swift::RosterItemPayload item;
 
		item.setJID(m_component->getJID());
 
		item.setSubscription(Swift::RosterItemPayload::Remove);
 
		payload->addItem(item);
 
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, barejid, m_component->getIQRouter());
 
		request->send();
 
	}
 
 
	std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid");
 
	BOOST_FOREACH(const std::string &notify_jid, x) {
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody(std::string("unregistered: ") + barejid);
 
		msg->setTo(notify_jid);
 
		msg->setFrom(m_component->getJID());
 
		m_component->getStanzaChannel()->sendMessage(msg);
 
	}
 
}
 
 
bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload) {
 
	// TODO: backend should say itself if registration is needed or not...
 
	if (CONFIG_STRING(m_config, "service.protocol") == "irc") {
 
		sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
		return true;
 
	}
 
 
	std::string barejid = from.toBare().toString();
 
 
	if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) {
 
		std::vector<std::string> const &x = CONFIG_VECTOR(m_config,"service.allowed_servers");
 
		if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) {
 
			LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account")
 
			sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 
 
	boost::shared_ptr<InBandRegistrationPayload> reg(new InBandRegistrationPayload());
 
 
	UserInfo res;
 
	bool registered = m_storageBackend->getUser(barejid, res);
 
 
	std::string instructions = CONFIG_STRING(m_config, "registration.instructions");
 
	std::string usernameField = CONFIG_STRING(m_config, "registration.username_label");
 
 
	// normal jabber:iq:register
 
	reg->setInstructions(instructions);
 
	reg->setRegistered(registered);
 
	reg->setUsername(res.uin);
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) {
 
		reg->setPassword("");
 
	}
 
 
 
	// form
 
	Form::ref form(new Form(Form::FormType));
 
	form->setTitle((("Registration")));
 
	form->setInstructions((instructions));
 
#if HAVE_SWIFTEN_3
 
	FormField::ref type = boost::make_shared<FormField>(FormField::HiddenType, "jabber:iq:register");	
 
#else
 
	HiddenFormField::ref type = HiddenFormField::create();
 
	type->setValue("jabber:iq:register");
 
#endif
 
	type->setName("FORM_TYPE");
 
	form->addField(type);
 
#if HAVE_SWIFTEN_3
 
	FormField::ref username = boost::make_shared<FormField>(FormField::TextSingleType, res.uin);
 
#else
 
	TextSingleFormField::ref username = TextSingleFormField::create();
 
	username->setValue(res.uin);
 
#endif
 
	username->setName("username");
 
	username->setLabel((usernameField));
 
	username->setRequired(true);
 
	form->addField(username);
 
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) {
 
#if HAVE_SWIFTEN_3
 
		FormField::ref password = boost::make_shared<FormField>(FormField::TextPrivateType);
 
#else
 
		TextPrivateFormField::ref password = TextPrivateFormField::create();
 
#endif
 
		password->setName("password");
 
		password->setLabel((("Password")));
 
		password->setRequired(true);
 
		form->addField(password);
 
	}
 
#if HAVE_SWIFTEN_3
 
	FormField::ref language = boost::make_shared<FormField>(FormField::ListSingleType);
 
#else
 
	ListSingleFormField::ref language = ListSingleFormField::create();
 
#endif
 
	language->setName("language");
 
	language->setLabel((("Language")));
 
	language->addOption(Swift::FormField::Option(CONFIG_STRING(m_config, "registration.language"), CONFIG_STRING(m_config, "registration.language")));
 
	if (registered)
 
#if HAVE_SWIFTEN_3
 
		language->addValue(res.language);
 
#else
 
		language->setValue(res.language);
 
#endif
 
	else
 
#if HAVE_SWIFTEN_3
 
		language->addValue(CONFIG_STRING(m_config, "registration.language"));
 
#else
 
		language->setValue(CONFIG_STRING(m_config, "registration.language"));
 
#endif
 
	form->addField(language);
 
 
//	TextSingleFormField::ref encoding = TextSingleFormField::create();
 
//	encoding->setName("encoding");
 
//	encoding->setLabel((("Encoding")));
 
//	if (registered)
 
//		encoding->setValue(res.encoding);
 
//	else
 
//		encoding->setValue(CONFIG_STRING(m_config, "registration.encoding"));
 
//	form->addField(encoding);
 
 
	if (registered) {
 
#if HAVE_SWIFTEN_3
 
		FormField::ref boolean = boost::make_shared<FormField>(FormField::BooleanType, "0");
 
#else
 
		BooleanFormField::ref boolean = BooleanFormField::create();
 
		boolean->setValue(0);
 
#endif
 
		boolean->setName("unregister");
 
		boolean->setLabel((("Remove your registration")));		
 
		form->addField(boolean);
 
	} else {
 
		if (CONFIG_BOOL(m_config,"registration.require_local_account")) {
 
			std::string localUsernameField = CONFIG_STRING(m_config, "registration.local_username_label");
 
#if HAVE_SWIFTEN_3
 
			FormField::ref local_username = boost::make_shared<FormField>(FormField::TextSingleType);
 
#else
 
			TextSingleFormField::ref local_username = TextSingleFormField::create();
 
#endif
 
			local_username->setName("local_username");
 
			local_username->setLabel((localUsernameField));
 
			local_username->setRequired(true);
 
			form->addField(local_username);
 
#if HAVE_SWIFTEN_3
 
			FormField::ref local_password = boost::make_shared<FormField>(FormField::TextPrivateType);
 
#else
 
			TextPrivateFormField::ref local_password = TextPrivateFormField::create();
 
#endif
 
			local_password->setName("local_password");
 
			local_password->setLabel((("Local Password")));
 
			local_password->setRequired(true);
 
			form->addField(local_password);
 
		}
 
	}
 
 
	reg->setForm(form);
 
 
	sendResponse(from, id, reg);
 
 
	return true;
 
}
 
 
bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload) {
 
	// TODO: backend should say itself if registration is needed or not...
 
	if (CONFIG_STRING(m_config, "service.protocol") == "irc") {
 
		sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
		return true;
 
	}
 
 
	std::string barejid = from.toBare().toString();
 
 
	if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) {
 
		std::vector<std::string> const &x = CONFIG_VECTOR(m_config,"service.allowed_servers");
 
		if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) {
 
			LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account")
 
			sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 
 
	UserInfo res;
 
	bool registered = m_storageBackend->getUser(barejid, res);
 
 
	std::string encoding;
 
	std::string language;
 
	std::string local_username("");
 
	std::string local_password("");
 
 
	Form::ref form = payload->getForm();
 
	if (form) {
 
		const std::vector<FormField::ref> fields = form->getFields();
 
		for (std::vector<FormField::ref>::const_iterator it = fields.begin(); it != fields.end(); it++) {
 
#if HAVE_SWIFTEN_3
 
			FormField::ref textSingle = *it;
 
			if (textSingle->getType() == FormField::TextSingleType) {
 
#else
 
			TextSingleFormField::ref textSingle = boost::dynamic_pointer_cast<TextSingleFormField>(*it);
 
			if (textSingle) {
 
#endif
 
				if (textSingle->getName() == "username") {
 
#if HAVE_SWIFTEN_3
 
					payload->setUsername(textSingle->getTextSingleValue());
 
#else
 
					payload->setUsername(textSingle->getValue());
 
#endif
 
				}
 
				else if (textSingle->getName() == "encoding") {
 
#if HAVE_SWIFTEN_3
 
					encoding = textSingle->getTextSingleValue();
 
#else
 
					encoding = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "password") {
 
#if HAVE_SWIFTEN_3
 
					payload->setPassword(textSingle->getTextSingleValue());
 
#else
 
					payload->setPassword(textSingle->getValue());
 
#endif
 
				}
 
				else if (textSingle->getName() == "local_username") {
 
#if HAVE_SWIFTEN_3
 
					local_username = textSingle->getTextSingleValue();
 
#else
 
					local_username = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "local_password") {
 
#if HAVE_SWIFTEN_3
 
					local_password = textSingle->getTextSingleValue();
 
#else
 
					local_password = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "unregister") {
 
#if HAVE_SWIFTEN_3
 
					if (textSingle->getTextSingleValue() == "1" || textSingle->getTextSingleValue() == "true") {
 
#else
 
					if (textSingle->getValue() == "1" || textSingle->getValue() == "true") {
 
#endif
 
						payload->setRemove(true);
 
					}
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref textPrivate = *it;
 
			if (textPrivate->getType() == FormField::TextPrivateType) {
 
#else
 
			TextPrivateFormField::ref textPrivate = boost::dynamic_pointer_cast<TextPrivateFormField>(*it);
 
			if (textPrivate) {
 
#endif
 
				if (textPrivate->getName() == "password") {
 
#if HAVE_SWIFTEN_3
 
					payload->setPassword(textPrivate->getTextPrivateValue());
 
#else
 
					payload->setPassword(textPrivate->getValue());
 
#endif
 
				}
 
				else if (textPrivate->getName() == "local_password") {
 
#if HAVE_SWIFTEN_3
 
					local_password = textPrivate->getTextPrivateValue();
 
#else
 
					local_password = textPrivate->getValue();
 
#endif
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref listSingle = *it;
 
			if (listSingle->getType() == FormField::ListSingleType) {
 
#else
 
			ListSingleFormField::ref listSingle = boost::dynamic_pointer_cast<ListSingleFormField>(*it);
 
			if (listSingle) {
 
#endif
 
				if (listSingle->getName() == "language") {
 
#if HAVE_SWIFTEN_3
 
					language = listSingle->getValues()[0];
 
#else
 
					language = listSingle->getValue();
 
#endif
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref boolean = *it;
 
			if (boolean->getType() == FormField::BooleanType) {
 
#else
 
			BooleanFormField::ref boolean = boost::dynamic_pointer_cast<BooleanFormField>(*it);
 
			if (boolean) {
 
#endif
 
				if (boolean->getName() == "unregister") {
 
#if HAVE_SWIFTEN_3
 
					if (boolean->getBoolValue()) {
 
#else
 
					if (boolean->getValue()) {
 
#endif
 
						payload->setRemove(true);
 
					}
 
				}
 
				continue;
 
			}
 
		}
 
	}
 
 
	if (payload->isRemove()) {
 
		unregisterUser(barejid);
 
		sendResponse(from, id, InBandRegistrationPayload::ref());
 
		return true;
 
	}
 
 
	if (CONFIG_BOOL(m_config,"registration.require_local_account")) {
 
	/*	if (!local_username || !local_password) {
 
			sendResponse(from, id, InBandRegistrationPayload::ref());
 
			return true
 
		} else */ if (local_username == "" || local_password == "") {
 
			sendResponse(from, id, InBandRegistrationPayload::ref());
 
			return true;
 
		} 
 
//		Swift::logging = true;
 
		bool validLocal = false;
 
		std::string localLookupServer = CONFIG_STRING(m_config, "registration.local_account_server");
 
		std::string localLookupJID = local_username + std::string("@") + localLookupServer;
 
		SimpleEventLoop localLookupEventLoop;
 
		BoostNetworkFactories localLookupNetworkFactories(&localLookupEventLoop);
 
		Client localLookupClient(localLookupJID, local_password, &localLookupNetworkFactories);
 
		
 
		// TODO: this is neccessary on my server ... but should maybe omitted
 
		localLookupClient.setAlwaysTrustCertificates();
 
		localLookupClient.connect();
 
 
		class SimpleLoopRunner {
 
			public:
 
				SimpleLoopRunner() {};
 
 
				static void run(SimpleEventLoop * loop) {
 
					loop->run();
 
				};
 
		};
 
 
		// TODO: Really ugly and hacky solution, any other ideas more than welcome!
 
		boost::thread thread(boost::bind(&(SimpleLoopRunner::run), &localLookupEventLoop));
 
		thread.timed_join(boost::posix_time::millisec(CONFIG_INT(m_config, "registration.local_account_server_timeout")));
 
		localLookupEventLoop.stop();
 
		thread.join();
 
		validLocal = localLookupClient.isAvailable();
 
		localLookupClient.disconnect();
 
		if (!validLocal) {
 
			sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 
 
	if (!payload->getUsername() || (!payload->getPassword() && CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true))) {
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
		return true;
 
	}
 
 
	if (!payload->getPassword()) {
 
		payload->setPassword("");
 
	}
 
 
	// Register or change password
 
	if (payload->getUsername()->empty()) {
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
		return true;
 
	}
 
 
	// TODO: Move this check to backend somehow
 
	if (CONFIG_STRING(m_config, "service.protocol") == "prpl-jabber") {
 
		// User tries to register himself.
 
		if ((Swift::JID(*payload->getUsername()).toBare() == from.toBare())) {
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 
 
		// User tries to register someone who's already registered.
 
		UserInfo user_row;
 
		bool registered = m_storageBackend->getUser(Swift::JID(*payload->getUsername()).toBare().toString(), user_row);
 
		if (registered) {
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 
 
	std::string username = *payload->getUsername();
 
 
	std::string newUsername(username);
 
	if (!CONFIG_STRING(m_config, "registration.username_mask").empty()) {
 
		newUsername = CONFIG_STRING(m_config, "registration.username_mask");
 
		boost::replace_all(newUsername, "$username", username);
 
	}
 
 
//TODO: Part of spectrum1 registration stuff, this should be potentially rewritten for S2 too
 
// 	if (!m_component->protocol()->isValidUsername(newUsername)) {
 
// 		Log("UserRegistration", "This is not valid username: "<< newUsername);
 
// 		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
// 		return true;
 
// 	}
 
 
	if (!CONFIG_STRING(m_config, "registration.allowed_usernames").empty()) {
 
		boost::regex expression(CONFIG_STRING(m_config, "registration.allowed_usernames"));
 
		if (!regex_match(newUsername, expression)) {
 
			LOG4CXX_INFO(logger, "This is not valid username: " << newUsername);
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 
 
	if (!registered) {
 
		res.jid = barejid;
 
		res.uin = newUsername;
 
		res.password = *payload->getPassword();
 
		res.language = language;
 
		res.encoding = encoding;
 
		res.vip = 0;
 
		res.id = 0;
 
		registerUser(res);
 
	}
 
	else {
 
		res.jid = barejid;
 
		res.uin = newUsername;
 
		res.password = *payload->getPassword();
 
		res.language = language;
 
		res.encoding = encoding;
 
		m_storageBackend->setUser(res);
 
		onUserUpdated(res);
 
	}
 
 
	sendResponse(from, id, InBandRegistrationPayload::ref());
 
	return true;
 
}
 
 
}
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * This program is free software; you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation; either version 2 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "transport/userregistration.h"
 
#include "transport/usermanager.h"
 
#include "transport/storagebackend.h"
 
#include "transport/transport.h"
 
#include "transport/rostermanager.h"
 
#include "transport/user.h"
 
#include "transport/logging.h"
 
#include "Swiften/Elements/ErrorPayload.h"
 
#include "Swiften/EventLoop/SimpleEventLoop.h"
 
#include "Swiften/Network/BoostNetworkFactories.h"
 
#include "Swiften/Client/Client.h"
 
#include <boost/shared_ptr.hpp>
 
#include <boost/thread.hpp>
 
#include <boost/date_time/posix_time/posix_time.hpp>
 
#include <boost/regex.hpp> 
 
#if HAVE_SWIFTEN_3
 
#include <Swiften/Elements/Form.h>
 
#endif
 

	
 
using namespace Swift;
 

	
 
namespace Transport {
 

	
 
DEFINE_LOGGER(logger, "UserRegistration");
 

	
 
UserRegistration::UserRegistration(Component *component, UserManager *userManager, StorageBackend *storageBackend) : Swift::Responder<Swift::InBandRegistrationPayload>(component->m_iqRouter) {
 
	m_component = component;
 
	m_config = m_component->m_config;
 
	m_storageBackend = storageBackend;
 
	m_userManager = userManager;
 
}
 

	
 
UserRegistration::~UserRegistration(){
 
}
 

	
 
bool UserRegistration::registerUser(const UserInfo &row) {
 
	UserInfo user;
 
	bool registered = m_storageBackend->getUser(row.jid, user);
 
	// This user is already registered
 
	if (registered)
 
		return false;
 

	
 
	m_storageBackend->setUser(row);
 

	
 
	//same as in unregisterUser but here we have to pass UserInfo to handleRegisterRRResponse
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(),row.jid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleRegisterRemoteRosterResponse, this, _1, _2, row));
 
	request->send();
 

	
 
	return true;
 
}
 

	
 
bool UserRegistration::unregisterUser(const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return false;
 

	
 
	onUserUnregistered(userInfo);
 

	
 
	// We have to check if server supports remoteroster XEP and use it if it's supported or fallback to unsubscribe otherwise
 
	AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), barejid));
 
	request->onResponse.connect(boost::bind(&UserRegistration::handleUnregisterRemoteRosterResponse, this, _1, _2, barejid));
 
	request->send();
 

	
 
	return true;
 
}
 

	
 
void UserRegistration::handleRegisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const UserInfo &row){
 
	if (remoteRosterNotSupported || !payload) {
 
		Swift::Presence::ref response = Swift::Presence::create();
 
		response->setFrom(m_component->getJID());
 
		response->setTo(Swift::JID(row.jid));
 
		response->setType(Swift::Presence::Subscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
	else{
 
		Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
		Swift::RosterItemPayload item;
 
		item.setJID(m_component->getJID());
 
		item.setSubscription(Swift::RosterItemPayload::Both);
 
		payload->addItem(item);
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, row.jid, m_component->getIQRouter());
 
		request->send();
 
	}
 
	onUserRegistered(row);
 

	
 
	std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid");
 
	BOOST_FOREACH(const std::string &notify_jid, x) {
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody(std::string("registered: ") + row.jid);
 
		msg->setTo(notify_jid);
 
		msg->setFrom(m_component->getJID());
 
		m_component->getStanzaChannel()->sendMessage(msg);
 
	}
 
}
 

	
 
void UserRegistration::handleUnregisterRemoteRosterResponse(boost::shared_ptr<Swift::RosterPayload> payload, Swift::ErrorPayload::ref remoteRosterNotSupported /*error*/, const std::string &barejid) {
 
	UserInfo userInfo;
 
	bool registered = m_storageBackend->getUser(barejid, userInfo);
 
	// This user is not registered
 
	if (!registered)
 
		return;
 

	
 
	if (remoteRosterNotSupported || !payload) {
 
		std::list <BuddyInfo> roster;
 
		m_storageBackend->getBuddies(userInfo.id, roster);
 
		for(std::list<BuddyInfo>::iterator u = roster.begin(); u != roster.end() ; u++){
 
			std::string name = (*u).legacyName;
 
			if ((*u).flags & BUDDY_JID_ESCAPING) {
 
				name = Swift::JID::getEscapedNode((*u).legacyName);
 
			}
 
			else {
 
				if (name.find_last_of("@") != std::string::npos) {
 
					name.replace(name.find_last_of("@"), 1, "%");
 
				}
 
			}
 

	
 
			Swift::Presence::ref response;
 
			response = Swift::Presence::create();
 
			response->setTo(Swift::JID(barejid));
 
			response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
			response->setType(Swift::Presence::Unsubscribe);
 
			m_component->getStanzaChannel()->sendPresence(response);
 

	
 
			response = Swift::Presence::create();
 
			response->setTo(Swift::JID(barejid));
 
			response->setFrom(Swift::JID(name, m_component->getJID().toString()));
 
			response->setType(Swift::Presence::Unsubscribed);
 
			m_component->getStanzaChannel()->sendPresence(response);
 
		}
 
	}
 
	else {
 
		BOOST_FOREACH(Swift::RosterItemPayload it, payload->getItems()) {
 
			Swift::RosterPayload::ref p = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
			Swift::RosterItemPayload item;
 
			item.setJID(it.getJID());
 
			item.setSubscription(Swift::RosterItemPayload::Remove);
 

	
 
			p->addItem(item);
 

	
 
			Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(p, barejid, m_component->getIQRouter());
 
			request->send();
 
		}
 
	}
 

	
 
	// Remove user from database
 
	m_storageBackend->removeUser(userInfo.id);
 

	
 
	// Disconnect the user
 
	User *user = m_userManager->getUser(barejid);
 
	if (user) {
 
		m_userManager->removeUser(user);
 
	}
 

	
 
	if (remoteRosterNotSupported || !payload) {
 
		Swift::Presence::ref response;
 
		response = Swift::Presence::create();
 
		response->setTo(Swift::JID(barejid));
 
		response->setFrom(m_component->getJID());
 
		response->setType(Swift::Presence::Unsubscribe);
 
		m_component->getStanzaChannel()->sendPresence(response);
 

	
 
		response = Swift::Presence::create();
 
		response->setTo(Swift::JID(barejid));
 
		response->setFrom(m_component->getJID());
 
		response->setType(Swift::Presence::Unsubscribed);
 
		m_component->getStanzaChannel()->sendPresence(response);
 
	}
 
	else {
 
		Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
 
		Swift::RosterItemPayload item;
 
		item.setJID(m_component->getJID());
 
		item.setSubscription(Swift::RosterItemPayload::Remove);
 
		payload->addItem(item);
 

	
 
		Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, barejid, m_component->getIQRouter());
 
		request->send();
 
	}
 

	
 
	std::vector<std::string> const &x = CONFIG_VECTOR(m_component->getConfig(),"registration.notify_jid");
 
	BOOST_FOREACH(const std::string &notify_jid, x) {
 
		boost::shared_ptr<Swift::Message> msg(new Swift::Message());
 
		msg->setBody(std::string("unregistered: ") + barejid);
 
		msg->setTo(notify_jid);
 
		msg->setFrom(m_component->getJID());
 
		m_component->getStanzaChannel()->sendMessage(msg);
 
	}
 
}
 

	
 
bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload) {
 
	// TODO: backend should say itself if registration is needed or not...
 
	if (CONFIG_STRING(m_config, "service.protocol") == "irc") {
 
		sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
		return true;
 
	}
 

	
 
	std::string barejid = from.toBare().toString();
 

	
 
	if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) {
 
		std::vector<std::string> const &x = CONFIG_VECTOR(m_config,"service.allowed_servers");
 
		if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) {
 
			LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account")
 
			sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 

	
 
	boost::shared_ptr<InBandRegistrationPayload> reg(new InBandRegistrationPayload());
 

	
 
	UserInfo res;
 
	bool registered = m_storageBackend->getUser(barejid, res);
 

	
 
	std::string instructions = CONFIG_STRING(m_config, "registration.instructions");
 
	std::string usernameField = CONFIG_STRING(m_config, "registration.username_label");
 

	
 
	// normal jabber:iq:register
 
	reg->setInstructions(instructions);
 
	reg->setRegistered(registered);
 
	reg->setUsername(res.uin);
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) {
 
		reg->setPassword("");
 
	}
 

	
 

	
 
	// form
 
	Form::ref form(new Form(Form::FormType));
 
	form->setTitle((("Registration")));
 
	form->setInstructions((instructions));
 
#if HAVE_SWIFTEN_3
 
	FormField::ref type = boost::make_shared<FormField>(FormField::HiddenType, "jabber:iq:register");	
 
#else
 
	HiddenFormField::ref type = HiddenFormField::create();
 
	type->setValue("jabber:iq:register");
 
#endif
 
	type->setName("FORM_TYPE");
 
	form->addField(type);
 
#if HAVE_SWIFTEN_3
 
	FormField::ref username = boost::make_shared<FormField>(FormField::TextSingleType, res.uin);
 
#else
 
	TextSingleFormField::ref username = TextSingleFormField::create();
 
	username->setValue(res.uin);
 
#endif
 
	username->setName("username");
 
	username->setLabel((usernameField));
 
	username->setRequired(true);
 
	form->addField(username);
 

	
 
	if (CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true)) {
 
#if HAVE_SWIFTEN_3
 
		FormField::ref password = boost::make_shared<FormField>(FormField::TextPrivateType);
 
#else
 
		TextPrivateFormField::ref password = TextPrivateFormField::create();
 
#endif
 
		password->setName("password");
 
		password->setLabel((("Password")));
 
		password->setRequired(true);
 
		form->addField(password);
 
	}
 
#if HAVE_SWIFTEN_3
 
	FormField::ref language = boost::make_shared<FormField>(FormField::ListSingleType);
 
#else
 
	ListSingleFormField::ref language = ListSingleFormField::create();
 
#endif
 
	language->setName("language");
 
	language->setLabel((("Language")));
 
	language->addOption(Swift::FormField::Option(CONFIG_STRING(m_config, "registration.language"), CONFIG_STRING(m_config, "registration.language")));
 
	if (registered)
 
#if HAVE_SWIFTEN_3
 
		language->addValue(res.language);
 
#else
 
		language->setValue(res.language);
 
#endif
 
	else
 
#if HAVE_SWIFTEN_3
 
		language->addValue(CONFIG_STRING(m_config, "registration.language"));
 
#else
 
		language->setValue(CONFIG_STRING(m_config, "registration.language"));
 
#endif
 
	form->addField(language);
 

	
 
//	TextSingleFormField::ref encoding = TextSingleFormField::create();
 
//	encoding->setName("encoding");
 
//	encoding->setLabel((("Encoding")));
 
//	if (registered)
 
//		encoding->setValue(res.encoding);
 
//	else
 
//		encoding->setValue(CONFIG_STRING(m_config, "registration.encoding"));
 
//	form->addField(encoding);
 

	
 
	if (registered) {
 
#if HAVE_SWIFTEN_3
 
		FormField::ref boolean = boost::make_shared<FormField>(FormField::BooleanType, "0");
 
#else
 
		BooleanFormField::ref boolean = BooleanFormField::create();
 
		boolean->setValue(0);
 
#endif
 
		boolean->setName("unregister");
 
		boolean->setLabel((("Remove your registration")));		
 
		form->addField(boolean);
 
	} else {
 
		if (CONFIG_BOOL(m_config,"registration.require_local_account")) {
 
			std::string localUsernameField = CONFIG_STRING(m_config, "registration.local_username_label");
 
#if HAVE_SWIFTEN_3
 
			FormField::ref local_username = boost::make_shared<FormField>(FormField::TextSingleType);
 
#else
 
			TextSingleFormField::ref local_username = TextSingleFormField::create();
 
#endif
 
			local_username->setName("local_username");
 
			local_username->setLabel((localUsernameField));
 
			local_username->setRequired(true);
 
			form->addField(local_username);
 
#if HAVE_SWIFTEN_3
 
			FormField::ref local_password = boost::make_shared<FormField>(FormField::TextPrivateType);
 
#else
 
			TextPrivateFormField::ref local_password = TextPrivateFormField::create();
 
#endif
 
			local_password->setName("local_password");
 
			local_password->setLabel((("Local Password")));
 
			local_password->setRequired(true);
 
			form->addField(local_password);
 
		}
 
	}
 

	
 
	reg->setForm(form);
 

	
 
	sendResponse(from, id, reg);
 

	
 
	return true;
 
}
 

	
 
bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::InBandRegistrationPayload> payload) {
 
	// TODO: backend should say itself if registration is needed or not...
 
	if (CONFIG_STRING(m_config, "service.protocol") == "irc") {
 
		sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
		return true;
 
	}
 

	
 
	std::string barejid = from.toBare().toString();
 

	
 
	if (!CONFIG_BOOL(m_config,"registration.enable_public_registration")) {
 
		std::vector<std::string> const &x = CONFIG_VECTOR(m_config,"service.allowed_servers");
 
		if (std::find(x.begin(), x.end(), from.getDomain()) == x.end()) {
 
			LOG4CXX_INFO(logger, barejid << ": This user has no permissions to register an account")
 
			sendError(from, id, ErrorPayload::BadRequest, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 

	
 
	UserInfo res;
 
	bool registered = m_storageBackend->getUser(barejid, res);
 

	
 
	std::string encoding;
 
	std::string language;
 
	std::string local_username("");
 
	std::string local_password("");
 

	
 
	Form::ref form = payload->getForm();
 
	if (form) {
 
		const std::vector<FormField::ref> fields = form->getFields();
 
		for (std::vector<FormField::ref>::const_iterator it = fields.begin(); it != fields.end(); it++) {
 
#if HAVE_SWIFTEN_3
 
			FormField::ref textSingle = *it;
 
			if (textSingle->getType() == FormField::TextSingleType) {
 
#else
 
			TextSingleFormField::ref textSingle = boost::dynamic_pointer_cast<TextSingleFormField>(*it);
 
			if (textSingle) {
 
#endif
 
				if (textSingle->getName() == "username") {
 
#if HAVE_SWIFTEN_3
 
					payload->setUsername(textSingle->getTextSingleValue());
 
#else
 
					payload->setUsername(textSingle->getValue());
 
#endif
 
				}
 
				else if (textSingle->getName() == "encoding") {
 
#if HAVE_SWIFTEN_3
 
					encoding = textSingle->getTextSingleValue();
 
#else
 
					encoding = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "password") {
 
#if HAVE_SWIFTEN_3
 
					payload->setPassword(textSingle->getTextSingleValue());
 
#else
 
					payload->setPassword(textSingle->getValue());
 
#endif
 
				}
 
				else if (textSingle->getName() == "local_username") {
 
#if HAVE_SWIFTEN_3
 
					local_username = textSingle->getTextSingleValue();
 
#else
 
					local_username = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "local_password") {
 
#if HAVE_SWIFTEN_3
 
					local_password = textSingle->getTextSingleValue();
 
#else
 
					local_password = textSingle->getValue();
 
#endif
 
				}
 
				// Pidgin sends it as textSingle, not sure why...
 
				else if (textSingle->getName() == "unregister") {
 
#if HAVE_SWIFTEN_3
 
					if (textSingle->getTextSingleValue() == "1" || textSingle->getTextSingleValue() == "true") {
 
#else
 
					if (textSingle->getValue() == "1" || textSingle->getValue() == "true") {
 
#endif
 
						payload->setRemove(true);
 
					}
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref textPrivate = *it;
 
			if (textPrivate->getType() == FormField::TextPrivateType) {
 
#else
 
			TextPrivateFormField::ref textPrivate = boost::dynamic_pointer_cast<TextPrivateFormField>(*it);
 
			if (textPrivate) {
 
#endif
 
				if (textPrivate->getName() == "password") {
 
#if HAVE_SWIFTEN_3
 
					payload->setPassword(textPrivate->getTextPrivateValue());
 
#else
 
					payload->setPassword(textPrivate->getValue());
 
#endif
 
				}
 
				else if (textPrivate->getName() == "local_password") {
 
#if HAVE_SWIFTEN_3
 
					local_password = textPrivate->getTextPrivateValue();
 
#else
 
					local_password = textPrivate->getValue();
 
#endif
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref listSingle = *it;
 
			if (listSingle->getType() == FormField::ListSingleType) {
 
#else
 
			ListSingleFormField::ref listSingle = boost::dynamic_pointer_cast<ListSingleFormField>(*it);
 
			if (listSingle) {
 
#endif
 
				if (listSingle->getName() == "language") {
 
#if HAVE_SWIFTEN_3
 
					language = listSingle->getValues()[0];
 
#else
 
					language = listSingle->getValue();
 
#endif
 
				}
 
				continue;
 
			}
 
#if HAVE_SWIFTEN_3
 
			FormField::ref boolean = *it;
 
			if (boolean->getType() == FormField::BooleanType) {
 
#else
 
			BooleanFormField::ref boolean = boost::dynamic_pointer_cast<BooleanFormField>(*it);
 
			if (boolean) {
 
#endif
 
				if (boolean->getName() == "unregister") {
 
#if HAVE_SWIFTEN_3
 
					if (boolean->getBoolValue()) {
 
#else
 
					if (boolean->getValue()) {
 
#endif
 
						payload->setRemove(true);
 
					}
 
				}
 
				continue;
 
			}
 
		}
 
	}
 

	
 
	if (payload->isRemove()) {
 
		unregisterUser(barejid);
 
		sendResponse(from, id, InBandRegistrationPayload::ref());
 
		return true;
 
	}
 

	
 
	if (CONFIG_BOOL(m_config,"registration.require_local_account")) {
 
	/*	if (!local_username || !local_password) {
 
			sendResponse(from, id, InBandRegistrationPayload::ref());
 
			return true
 
		} else */ if (local_username == "" || local_password == "") {
 
			sendResponse(from, id, InBandRegistrationPayload::ref());
 
			return true;
 
		} 
 
//		Swift::logging = true;
 
		bool validLocal = false;
 
		std::string localLookupServer = CONFIG_STRING(m_config, "registration.local_account_server");
 
		std::string localLookupJID = local_username + std::string("@") + localLookupServer;
 
		SimpleEventLoop localLookupEventLoop;
 
		BoostNetworkFactories localLookupNetworkFactories(&localLookupEventLoop);
 
		Client localLookupClient(localLookupJID, local_password, &localLookupNetworkFactories);
 
		
 
		// TODO: this is neccessary on my server ... but should maybe omitted
 
		localLookupClient.setAlwaysTrustCertificates();
 
		localLookupClient.connect();
 

	
 
		class SimpleLoopRunner {
 
			public:
 
				SimpleLoopRunner() {};
 

	
 
				static void run(SimpleEventLoop * loop) {
 
					loop->run();
 
				};
 
		};
 

	
 
		// TODO: Really ugly and hacky solution, any other ideas more than welcome!
 
		boost::thread thread(boost::bind(&(SimpleLoopRunner::run), &localLookupEventLoop));
 
		thread.timed_join(boost::posix_time::millisec(CONFIG_INT(m_config, "registration.local_account_server_timeout")));
 
		localLookupEventLoop.stop();
 
		thread.join();
 
		validLocal = localLookupClient.isAvailable();
 
		localLookupClient.disconnect();
 
		if (!validLocal) {
 
			sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 

	
 
	if (!payload->getUsername() || (!payload->getPassword() && CONFIG_BOOL_DEFAULTED(m_config, "registration.needPassword", true))) {
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
		return true;
 
	}
 

	
 
	if (!payload->getPassword()) {
 
		payload->setPassword("");
 
	}
 

	
 
	// Register or change password
 
	if (payload->getUsername()->empty()) {
 
		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
		return true;
 
	}
 

	
 
	// TODO: Move this check to backend somehow
 
	if (CONFIG_STRING(m_config, "service.protocol") == "prpl-jabber") {
 
		// User tries to register himself.
 
		if ((Swift::JID(*payload->getUsername()).toBare() == from.toBare())) {
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 

	
 
		// User tries to register someone who's already registered.
 
		UserInfo user_row;
 
		bool registered = m_storageBackend->getUser(Swift::JID(*payload->getUsername()).toBare().toString(), user_row);
 
		if (registered) {
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 

	
 
	std::string username = *payload->getUsername();
 

	
 
	std::string newUsername(username);
 
	if (!CONFIG_STRING(m_config, "registration.username_mask").empty()) {
 
		newUsername = CONFIG_STRING(m_config, "registration.username_mask");
 
		boost::replace_all(newUsername, "$username", username);
 
	}
 

	
 
//TODO: Part of spectrum1 registration stuff, this should be potentially rewritten for S2 too
 
// 	if (!m_component->protocol()->isValidUsername(newUsername)) {
 
// 		Log("UserRegistration", "This is not valid username: "<< newUsername);
 
// 		sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
// 		return true;
 
// 	}
 

	
 
	if (!CONFIG_STRING(m_config, "registration.allowed_usernames").empty()) {
 
		boost::regex expression(CONFIG_STRING(m_config, "registration.allowed_usernames"));
 
		if (!regex_match(newUsername, expression)) {
 
			LOG4CXX_INFO(logger, "This is not valid username: " << newUsername);
 
			sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify);
 
			return true;
 
		}
 
	}
 

	
 
	if (!registered) {
 
		res.jid = barejid;
 
		res.uin = newUsername;
 
		res.password = *payload->getPassword();
 
		res.language = language;
 
		res.encoding = encoding;
 
		res.vip = 0;
 
		res.id = 0;
 
		registerUser(res);
 
	}
 
	else {
 
		res.jid = barejid;
 
		res.uin = newUsername;
 
		res.password = *payload->getPassword();
 
		res.language = language;
 
		res.encoding = encoding;
 
		m_storageBackend->setUser(res);
 
		onUserUpdated(res);
 
	}
 

	
 
	sendResponse(from, id, InBandRegistrationPayload::ref());
 
	return true;
 
}
 

	
 
}
0 comments (0 inline, 0 general)