diff options
498 files changed, 9552 insertions, 1610 deletions
@@ -21,6 +21,7 @@  *.ilk  *.res  *.moc +*.patch  *~  _CL_*  *.manifest diff --git a/3rdParty/Boost/SConscript b/3rdParty/Boost/SConscript index 562b1b0..fc53b45 100644 --- a/3rdParty/Boost/SConscript +++ b/3rdParty/Boost/SConscript @@ -90,7 +90,6 @@ elif env.get("BOOST_BUNDLED", False) :  				"src/libs/program_options/src/config_file.cpp",  				"src/libs/program_options/src/convert.cpp",  				"src/libs/program_options/src/options_description.cpp", -				"src/libs/program_options/src/parsers.cpp",  				"src/libs/program_options/src/positional_options.cpp",  				"src/libs/program_options/src/split.cpp",  				"src/libs/program_options/src/program_options_utf8_codecvt_facet.cpp", @@ -98,6 +97,11 @@ elif env.get("BOOST_BUNDLED", False) :  				"src/libs/program_options/src/variables_map.cpp",  				"src/libs/program_options/src/winmain.cpp"] +		if env["PLATFORM"] != "darwin" or env["target"] == "native" : +			sources += [ +					"src/libs/program_options/src/parsers.cpp", +				] +  		if env["PLATFORM"] != "win32" :  			sources += [  					"src/libs/thread/src/pthread/once.cpp", diff --git a/3rdParty/Lua/SConscript b/3rdParty/Lua/SConscript index 3baa2d8..68f4e36 100644 --- a/3rdParty/Lua/SConscript +++ b/3rdParty/Lua/SConscript @@ -30,6 +30,10 @@ if env.get("LUA_BUNDLED", False) :  	if env["SCONS_STAGE"] == "build" :  		myenv = env.Clone() +		if env["PLATFORM"] == "win32" : +			myenv.Append(CFLAGS = ["/TP"]) +		else : +			myenv.Append(CFLAGS = ["-x", "c++"])  		# Remove warn flags  		myenv.Replace(CCFLAGS = [flag for flag in env["CCFLAGS"] if flag not in ["-W", "-Wall"]]) diff --git a/BuildTools/CLang/.gitignore b/BuildTools/CLang/.gitignore new file mode 100644 index 0000000..df682c0 --- /dev/null +++ b/BuildTools/CLang/.gitignore @@ -0,0 +1,4 @@ +CLangDiagnosticsFlags +CLangDiagnosticsFlagsTool.sh +CLangDiagnosticsFlagsTool +clang-diagnostics-overview.* diff --git a/BuildTools/CLang/CLangDiagnosticsFlagsTool.cpp b/BuildTools/CLang/CLangDiagnosticsFlagsTool.cpp new file mode 100644 index 0000000..82cc902 --- /dev/null +++ b/BuildTools/CLang/CLangDiagnosticsFlagsTool.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <iostream> +#include <set> +#include <vector> +#include <cassert> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/adjacency_list.hpp> +#include <boost/graph/topological_sort.hpp> +#include <boost/graph/topological_sort.hpp> +#include <boost/graph/graphviz.hpp> + +// ----------------------------------------------------------------------------- +// Include diagnostics data from CLang +// ----------------------------------------------------------------------------- + +#define DIAG(name, a, b, c, d, e, f, g) name, +	 +namespace diag { +	enum LexKinds { +#include <clang/Basic/DiagnosticLexKinds.inc> +#include <clang/Basic/DiagnosticParseKinds.inc> +#include <clang/Basic/DiagnosticCommonKinds.inc> +#include <clang/Basic/DiagnosticDriverKinds.inc> +#include <clang/Basic/DiagnosticFrontendKinds.inc> +#include <clang/Basic/DiagnosticSemaKinds.inc> +	}; +} + +#define GET_DIAG_ARRAYS +#include <clang/Basic/DiagnosticGroups.inc> +#undef GET_DIAG_ARRAYS + +struct DiagTableEntry { +	const char* name; +	const short* array; +	const short* group; +}; + +static const DiagTableEntry diagnostics[] = { +#define GET_DIAG_TABLE +#include <clang/Basic/DiagnosticGroups.inc> +#undef GET_DIAG_TABLE +}; +static const size_t diagnostics_count = sizeof(diagnostics) / sizeof(diagnostics[0]); + +// ----------------------------------------------------------------------------- + +using namespace boost; + +struct Properties { +	Properties() : missing(false), redundant(false) { +	} + +	std::string name; +	bool have; +	bool implicitHave; +	bool dontWant; +	bool implicitDontWant; +	bool ignored; +	bool available; +	bool missing; +	bool redundant; +	bool alreadyCovered; +}; + +class GraphVizLabelWriter { +	public: +		GraphVizLabelWriter(const std::vector<Properties>& properties) : properties(properties) { +		}	 +		 +		template <class VertexOrEdge> +		void operator()(std::ostream& out, const VertexOrEdge& v) const { +			std::string color; +			if (properties[v].missing) { +				color = "orange"; +			} +			else if (properties[v].redundant) { +				color = "lightblue"; +			} +			else if (properties[v].have) { +				color = "darkgreen"; +			} +			else if (properties[v].implicitHave) { +				color = "green"; +			} +			else if (properties[v].dontWant) { +				color = "red"; +			} +			else if (properties[v].implicitDontWant) { +				color = "pink"; +			} +			else if (properties[v].ignored) { +				color = "white"; +			} +			else if (properties[v].available) { +				color = "yellow"; +			} +			else { +				assert(false); +			} +			out << "[label=" << escape_dot_string(properties[v].name) << " fillcolor=\"" << color << "\" style=filled]"; +		} + +	private: +		const std::vector<Properties> properties; +}; + +int main(int argc, char* argv[]) { +	// Parse command-line arguments +	std::set<std::string> have; +	std::set<std::string> dontWant; +	std::string outputDir; +	for (int i = 1; i < argc; ++i) { +		std::string arg(argv[i]); +		if (starts_with(arg, "-W")) { +			have.insert(arg.substr(2, arg.npos)); +		} +		else if (starts_with(arg, "-w")) { +			dontWant.insert(arg.substr(2, arg.npos)); +		} +		else if (starts_with(arg, "-O")) { +			outputDir = arg.substr(2, arg.npos) + "/"; +		} +	} + +	// Build the graph and initialize properties +	typedef adjacency_list<vecS, vecS, bidirectionalS> Graph; +	typedef graph_traits<Graph>::vertex_descriptor Vertex; +	Graph g(diagnostics_count); +	std::vector<Properties> properties(num_vertices(g)); +	for (size_t i = 0; i < diagnostics_count; ++i) { +		std::string name(diagnostics[i].name); +		properties[i].name = name; +		properties[i].implicitHave = properties[i].have = have.find(name) != have.end(); +		properties[i].implicitDontWant = properties[i].dontWant = dontWant.find(name) != dontWant.end(); +		properties[i].ignored = diagnostics[i].group == 0 && diagnostics[i].array == 0; +		properties[i].alreadyCovered = false; +		properties[i].available = true; +		for (const short* j = diagnostics[i].group; j && *j != -1; ++j) { +			add_edge(i, *j, g); +		} +	} + +	// Sort the diagnostics +	std::list<Vertex> sortedDiagnostics; +	boost::topological_sort(g, std::front_inserter(sortedDiagnostics)); + +	// Propagate dontWant and have properties down +	for(std::list<Vertex>::const_iterator i = sortedDiagnostics.begin(); i != sortedDiagnostics.end(); ++i) { +		graph_traits<Graph>::adjacency_iterator adjacentIt, adjacentEnd; +		for (tie(adjacentIt, adjacentEnd) = adjacent_vertices(*i, g); adjacentIt != adjacentEnd; ++adjacentIt) { +			properties[*adjacentIt].implicitDontWant = properties[*i].implicitDontWant || properties[*adjacentIt].implicitDontWant; +			properties[*adjacentIt].implicitHave = properties[*i].implicitHave || properties[*adjacentIt].implicitHave; +		} +	} + +	// Propagate 'available' property upwards +	for(std::list<Vertex>::const_reverse_iterator i = sortedDiagnostics.rbegin(); i != sortedDiagnostics.rend(); ++i) { +		properties[*i].available = properties[*i].available && !properties[*i].implicitDontWant; +		graph_traits<Graph>::in_edge_iterator edgesIt, edgesEnd; +		graph_traits<Graph>::edge_descriptor edge; +		for (tie(edgesIt, edgesEnd) = in_edges(*i, g); edgesIt != edgesEnd; ++edgesIt) { +			properties[source(*edgesIt, g)].available = properties[source(*edgesIt, g)].available && properties[*i].available; +		} +	} + +	// Collect missing & redundant flags +	std::set<std::string> missing; +	std::set<std::string> redundant; +	for(std::list<Vertex>::const_iterator i = sortedDiagnostics.begin(); i != sortedDiagnostics.end(); ++i) { +		bool markChildrenCovered = true; +		if (properties[*i].alreadyCovered) { +			if (properties[*i].have) { +				properties[*i].redundant = true; +				redundant.insert(properties[*i].name); +			} +		} +		else { +			if (properties[*i].available) { +				if (!properties[*i].implicitHave && !properties[*i].ignored) { +					properties[*i].missing = true; +					missing.insert(properties[*i].name); +				} +			} +			else { +				markChildrenCovered = false; +			} +		} +		if (markChildrenCovered) { +			graph_traits<Graph>::adjacency_iterator adjacentIt, adjacentEnd; +			for (tie(adjacentIt, adjacentEnd) = adjacent_vertices(*i, g); adjacentIt != adjacentEnd; ++adjacentIt) { +				properties[*adjacentIt].alreadyCovered = true; +			} +		} +	} + +	// Write information +	if (!missing.empty()) { +		std::cout << "Missing diagnostic flags: "; +		for(std::set<std::string>::const_iterator i = missing.begin(); i != missing.end(); ++i) { +			std::cout << "-W" << *i << " "; +		} +		std::cout<< std::endl; +	} + +	if (!redundant.empty()) { +		std::cout << "Redundant diagnostic flags: "; +		for(std::set<std::string>::const_iterator i = redundant.begin(); i != redundant.end(); ++i) { +			std::cout << "-W" << *i << " "; +		} +		std::cout<< std::endl; +	} + +	// Write graphviz file +	if (!outputDir.empty()) { +		std::ofstream f((outputDir + "clang-diagnostics-overview.dot").c_str()); +		write_graphviz(f, g, GraphVizLabelWriter(properties)); +		f.close(); +	} + +	return 0; +} diff --git a/BuildTools/CLang/SConscript b/BuildTools/CLang/SConscript new file mode 100644 index 0000000..850c35c --- /dev/null +++ b/BuildTools/CLang/SConscript @@ -0,0 +1,15 @@ +Import("env") + +#myenv = Environment() +#myenv.Append(CPPPATH = ["."]) +#myenv.Program("CLangDiagnosticsFlagsTool", ["CLangDiagnosticsFlagsTool.cpp"]) +# +#disabledDiagnostics = ["-wunreachable-code", "-wunused-macros", "-wmissing-noreturn", "-wlong-long", "-wcast-align", "-wglobal-constructors", "-wmissing-prototypes", "-wpadded", "-wshadow"] +#clangDiagnosticsFlagsToolCommand = "BuildTools/CLang/CLangDiagnosticsFlagsTool -O" + env.Dir(".").abspath + " " + " ".join(disabledDiagnostics) + " " +#clangDiagnosticsFlagsToolCommand += " ".join([flag for flag in env["CXXFLAGS"] if flag.startswith("-W")]) +#clangDiagnosticsFlagsToolCommand += "\n" +#clangDiagnosticsFlagsToolCommand += "dot -Tpng " + env.Dir(".").abspath + "/clang-diagnostics-overview.dot > " + env.Dir(".").abspath + "/clang-diagnostics-overview.png\n" +#v = env.WriteVal("#/BuildTools/CLang/CLangDiagnosticsFlagsTool.sh", env.Value(clangDiagnosticsFlagsToolCommand)) +#env.AddPostAction(v, Chmod(v[0], 0755)) +# +# diff --git a/BuildTools/CheckHeaders.py b/BuildTools/CheckHeaders.py new file mode 100755 index 0000000..73f49db --- /dev/null +++ b/BuildTools/CheckHeaders.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import os, sys + +foundBadHeaders = False + +for (path, dirs, files) in os.walk(".") : +  if "3rdParty" in path or ".sconf" in path or ".framework" in path : +    continue +  if not "Swiften" in path : +    continue + +  for filename in [os.path.join(path, file) for file in files if file.endswith(".h")] : +    file = open(filename, "r") +    for line in file.readlines() : +      for include in ["iostream", "algorithm", "cassert", "boost/bind.hpp", "boost/filesystem.hpp", "Base/foreach.h", "Base/Log.h", "boost/date_time/date_time.hpp", "boost/filesystem/filesystem.hpp"] : +        if "#include" in line and include in line and not "Base/Log" in filename : +          print "Found " + include + " include in " + filename +          foundBadHeaders = True + +sys.exit(foundBadHeaders) diff --git a/BuildTools/Copyrighter.py b/BuildTools/Copyrighter.py index a768f0d..8916316 100755 --- a/BuildTools/Copyrighter.py +++ b/BuildTools/Copyrighter.py @@ -136,7 +136,7 @@ elif sys.argv[1] == "check-all-copyrights" :    for (path, dirs, files) in os.walk(".") :      if "3rdParty" in path or ".sconf" in path or "Swift.app" in path :        continue -    for filename in [os.path.join(path, file) for file in files if (file.endswith(".cpp") or file.endswith(".h")) and not "ui_" in file and not "moc_" in file and not "qrc_" in file and not "BuildVersion.h" in file and not "Swiften.h" in file and not "Version.h" in file and not "swiften-config.h" in file] : +    for filename in [os.path.join(path, file) for file in files if (file.endswith(".cpp") or file.endswith(".h")) and not "ui_" in file and not "moc_" in file and not "qrc_" in file and not "BuildVersion.h" in file and not "Swiften.h" in file and not "Version.h" in file and not "swiften-config.h" in file and not "linit.cpp" in file ] :        ok &= check_copyright(filename)     if not ok :      sys.exit(-1) diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct index bc7781b..2d9d200 100644 --- a/BuildTools/SCons/SConstruct +++ b/BuildTools/SCons/SConstruct @@ -42,6 +42,9 @@ vars.Add("expat_libname", "Expat library name", "libexpat" if os.name == "nt" el  vars.Add(PathVariable("libidn_includedir", "LibIDN headers location", None, PathVariable.PathAccept))  vars.Add(PathVariable("libidn_libdir", "LibIDN library location", None, PathVariable.PathAccept))  vars.Add("libidn_libname", "LibIDN library name", "libidn" if os.name == "nt" else "idn") +vars.Add(PathVariable("sqlite_includedir", "SQLite headers location", None, PathVariable.PathAccept)) +vars.Add(PathVariable("sqlite_libdir", "SQLite library location", None, PathVariable.PathAccept)) +vars.Add("sqlite_libname", "SQLite library name", "libsqlite3" if os.name == "nt" else "sqlite3")  vars.Add(PathVariable("avahi_includedir", "Avahi headers location", None, PathVariable.PathAccept))  vars.Add(PathVariable("avahi_libdir", "Avahi library location", None, PathVariable.PathAccept))  vars.Add(PathVariable("qt", "Qt location", "", PathVariable.PathAccept)) @@ -178,7 +181,14 @@ else :  		env.Append(CXXFLAGS = ["-Werror"])  	gccVersion = env["CCVERSION"].split(".")  	if gccVersion >= ["4", "5", "0"] : -		env.Append(CCFLAGS = ["-Wlogical-op"]) +		env.Append(CXXFLAGS = ["-Wlogical-op"]) +	if "clang" in env["CC"] : +		env.Append(CXXFLAGS = ["-W#warnings", "-W-Wc++0x-compat", "-Wc++0x-compat", "-Waddress-of-temporary", "-Wambiguous-member-template", "-Warray-bounds", "-Watomic-properties", "-Wbind-to-temporary-copy", "-Wbuiltin-macro-redefined", "-Wc++-compat", "-Wc++0x-extensions", "-Wcomments", "-Wconditional-uninitialized", "-Wconstant-logical-operand", "-Wdeclaration-after-statement", "-Wdeprecated", "-Wdeprecated-implementations", "-Wdeprecated-writable-strings", "-Wduplicate-method-arg", "-Wempty-body", "-Wendif-labels", "-Wenum-compare", "-Wformat=2", "-Wfour-char-constants", "-Wgnu", "-Wincomplete-implementation", "-Winvalid-noreturn", "-Winvalid-offsetof", "-Winvalid-token-paste", "-Wlocal-type-template-args", "-Wmethod-signatures", "-Wmicrosoft", "-Wmissing-declarations", "-Wnon-pod-varargs", "-Wnonfragile-abi2", "-Wnull-dereference", "-Wout-of-line-declaration", "-Woverlength-strings", "-Wpacked", "-Wpointer-arith", "-Wpointer-sign", "-Wprotocol", "-Wreadonly-setter-attrs", "-Wselector", "-Wshift-overflow", "-Wshift-sign-overflow", "-Wstrict-selector-match", "-Wsuper-class-method-mismatch", "-Wtautological-compare", "-Wtypedef-redefinition", "-Wundeclared-selector", "-Wunknown-attributes", "-Wunknown-warning-option", "-Wunnamed-type-template-args", "-Wunused-exception-parameter", "-Wunused-member-function", "-Wused-but-marked-unused", "-Wvariadic-macros"]) +# To enable:  +# "-Wheader-hygiene" +#	"-Wnon-gcc", +# "-Wweak-vtables", +# "-Wlarge-by-value-copy",  if env.get("coverage", 0) :  	assert(env["PLATFORM"] != "win32") @@ -186,7 +196,7 @@ if env.get("coverage", 0) :  	env.Append(LINKFLAGS = ["-fprofile-arcs", "-ftest-coverage"])  if env["PLATFORM"] == "win32" : -	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "ws2_32", "wsock32"]) +	env.Append(LIBS = ["user32", "crypt32", "dnsapi", "ws2_32", "wsock32", "Advapi32"])  	env.Append(CCFLAGS = ["/EHsc", "/nologo"])  	# FIXME: We should find a decent solution for MSVS 10  	if int(env["MSVS_VERSION"].split(".")[0]) < 10 : @@ -194,7 +204,7 @@ if env["PLATFORM"] == "win32" :  		env["SHLINKCOM"] = [env["SHLINKCOM"], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;2']  if env["PLATFORM"] == "darwin" and not env["target"] in ["iphone-device", "iphone-simulator", "xcode"] : -	env.Append(FRAMEWORKS = ["IOKit", "AppKit"]) +	env.Append(FRAMEWORKS = ["IOKit", "AppKit", "SystemConfiguration"])  # Testing  env["TEST_TYPE"] = env["test"] @@ -205,6 +215,7 @@ env["TEST"] = (env["TEST_TYPE"] != "none") or env.GetOption("clean")  if env.get("valgrind", 0) :  	env["TEST_RUNNER"] = "valgrind --suppressions=QA/valgrind.supp -q --leak-check=full --track-origins=yes "  env["TEST_IGNORE_RESULT"] = "ignore_test_result" in ARGUMENTS +env["TEST_CREATE_LIBRARIES"] = "create_test_libraries" in ARGUMENTS  # Packaging  env["DIST"] = "dist" in ARGUMENTS or env.GetOption("clean") @@ -231,12 +242,12 @@ if target in ["iphone-device", "iphone-simulator", "xcode"] :  		# Hard code values  		env["XCODE_PLATFORM_DEVELOPER_BIN_DIR"] = "/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin"  		if target == "iphone-device": -			env["XCODE_ARCH_FLAGS"] = ["-arch", "armv6"] +			env["XCODE_ARCH_FLAGS"] = ["-arch", "armv6", "-arch", "armv7"]  			sdkPart = "iPhoneOS"  		else :  			env["XCODE_ARCH_FLAGS"] = ["-arch", "i386"]  			sdkPart = "iPhoneSimulator" -		sdkVer = "4.0" +		sdkVer = "4.3"  		env["XCODE_SDKROOT"] = "/Developer/Platforms/" + sdkPart + ".platform/Developer/SDKs/" + sdkPart + sdkVer + ".sdk"  	# Set the build flags @@ -244,7 +255,7 @@ if target in ["iphone-device", "iphone-simulator", "xcode"] :  	env["CXX"] = "$XCODE_PLATFORM_DEVELOPER_BIN_DIR/g++"  	env["OBJCCFLAGS"] = ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"]  	env["LD"] = env["CC"] -	env.Append(CCFLAGS = env["XCODE_ARCH_FLAGS"]) +	env.Append(CCFLAGS = env["XCODE_ARCH_FLAGS"] + ["-fvisibility=hidden"])  	env.Append(LINKFLAGS = env["XCODE_ARCH_FLAGS"])  	env.Append(CPPFLAGS = ["-isysroot", "$XCODE_SDKROOT"])  	env.Append(FRAMEWORKS = ["CoreFoundation", "Foundation", "UIKit", "CoreGraphics"]) @@ -320,6 +331,12 @@ def checkObjCHeader(context, header) :  if ARGUMENTS.get("force-configure", 0) :    SCons.SConf.SetCacheMode("force") +def CheckPKG(context, name): +	context.Message( 'Checking for package %s... ' % name ) +	ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0] +	context.Result( ret ) +	return ret +  conf = Configure(conf_env)  if not conf.CheckCXX() or not conf.CheckCC() : @@ -414,6 +431,31 @@ if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" :  		env["XSS_FLAGS"] = xss_flags  	conf.Finish() +# GConf +env["HAVE_GCONF"] = 0 +if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : +	gconf_env = conf_env.Clone() +	conf = Configure(gconf_env, custom_tests = {"CheckPKG": CheckPKG}) +	if conf.CheckPKG("gconf-2.0") : +		gconf_bare_env = Environment() +		gconf_bare_env.ParseConfig('pkg-config --cflags gconf-2.0 --libs gconf-2.0') +		gconf_flags = { +				"LIBS": gconf_bare_env["LIBS"], +				"CCFLAGS": gconf_bare_env["CCFLAGS"], +				"CPPPATH": gconf_bare_env["CPPPATH"], +				"CPPDEFINES": gconf_bare_env["CPPDEFINES"], +			} +		gconf_env.MergeFlags(gconf_flags) +		if conf.CheckCHeader("gconf/gconf-client.h") and conf.CheckLib("gconf-2") : +			env["HAVE_GCONF"] = 1 +			env["GCONF_FLAGS"] = { +				"LIBS": gconf_env["LIBS"], +				"CCFLAGS": gconf_env["CCFLAGS"], +				"CPPPATH": gconf_env["CPPPATH"], +				"CPPDEFINES": gconf_env["CPPDEFINES"], +			} +	conf.Finish() +  # Sparkle  env["HAVE_SPARKLE"] = 0  if env["PLATFORM"] == "darwin" : @@ -507,6 +549,23 @@ else :  	env["LIBIDN_BUNDLED"] = 1  conf.Finish() +# SQLite +sqlite_conf_env = conf_env.Clone() +sqlite_flags = {} +if env.get("sqlite_libdir", None) : +	sqlite_flags["LIBPATH"] = [env["sqlite_libdir"]] +if env.get("sqlite_includedir", None) : +	sqlite_flags["CPPPATH"] = [env["sqlite_includedir"]] +sqlite_conf_env.MergeFlags(sqlite_flags) +conf = Configure(sqlite_conf_env) +if conf.CheckCHeader("sqlite3.h") and conf.CheckLib(env["sqlite_libname"]) : +	env["HAVE_SQLITE"] = 1 +	env["SQLITE_FLAGS"] = { "LIBS": [env["sqlite_libname"]] } +	env["SQLITE_FLAGS"].update(sqlite_flags) +else : +	env["SQLITE_BUNDLED"] = 1 +conf.Finish() +  # Lua  env["LUA_BUNDLED"] = 1 @@ -621,6 +680,9 @@ if env.Dir("#/.git").exists() :  # Project files  ################################################################################ +# Build tools +env.SConscript(dirs = ["#/BuildTools/CLang"]) +  # Modules  modules = []  for dir in os.listdir(Dir("#/3rdParty").abspath) : diff --git a/BuildTools/SCons/Tools/AppBundle.py b/BuildTools/SCons/Tools/AppBundle.py index c271575..6a343f6 100644 --- a/BuildTools/SCons/Tools/AppBundle.py +++ b/BuildTools/SCons/Tools/AppBundle.py @@ -1,7 +1,7 @@  import SCons.Util, os.path  def generate(env) : -  def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}) : +  def createAppBundle(env, bundle, version = "1.0", resources = [], frameworks = [], info = {}, handlesXMPPURIs = False) :      bundleDir = bundle + ".app"      bundleContentsDir = bundleDir + "/Contents"      resourcesDir = bundleContentsDir + "/Resources" @@ -32,6 +32,18 @@ def generate(env) :      for key, value in infoDict.items() :        plist += "<key>" + key + "</key>\n"        plist += "<string>" + value.encode("utf-8") + "</string>\n" +    if handlesXMPPURIs : +      plist += """<key>CFBundleURLTypes</key> +<array> +    <dict> +        <key>CFBundleURLName</key> +        <string>XMPP URL</string> +        <key>CFBundleURLSchemes</key> +        <array> +            <string>xmpp</string> +        </array> +    </dict> +</array>\n"""      plist += """</dict>    </plist>    """ @@ -825,6 +825,7 @@ Swift contains some code contributed under the BSD License, under the following  --- START OF BSD LICENSE  Copyright (c) 2011, Arnt Gulbrandsen  Copyright (c) 2011, Thilo Cestonaro +Copyright (c) 2011, Vlad Voicu  All rights reserved.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Documentation/BuildingOnUnix.txt b/Documentation/BuildingOnUnix.txt index 8de73c8..fa5759e 100644 --- a/Documentation/BuildingOnUnix.txt +++ b/Documentation/BuildingOnUnix.txt @@ -20,6 +20,9 @@ Running tests  -------------  - Run  		./scons test=unit -	for running the unit tests, or -		./scons test=all -	for running all tests. +	for running the unit tests. + +Installing +---------- +- To install swift in /usr/local, run +		./scons SWIFT_INSTALLDIR=/usr/local /usr/local diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayload.h b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayload.h index 7533a1e..62ea495 100644 --- a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayload.h +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayload.h @@ -8,9 +8,8 @@  #include <Swiften/Swiften.h> -using namespace Swift;  //... -class EchoPayload : public Payload { +class EchoPayload : public Swift::Payload {  	public:  		EchoPayload() {} diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadParserFactory.h b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadParserFactory.h index 9cbb795..33a8c41 100644 --- a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadParserFactory.h +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadParserFactory.h @@ -9,9 +9,7 @@  #include <Swiften/Swiften.h>  #include "EchoPayload.h" -using namespace Swift; - -class EchoPayloadParser : public GenericPayloadParser<EchoPayload> { +class EchoPayloadParser : public Swift::GenericPayloadParser<EchoPayload> {  	public:  		EchoPayloadParser() : currentDepth(0) {} @@ -36,7 +34,7 @@ class EchoPayloadParser : public GenericPayloadParser<EchoPayload> {  		std::string currentText;  }; -class EchoPayloadParserFactory : public GenericPayloadParserFactory<EchoPayloadParser> { +class EchoPayloadParserFactory : public Swift::GenericPayloadParserFactory<EchoPayloadParser> {  	public:  		EchoPayloadParserFactory() :  			GenericPayloadParserFactory<EchoPayloadParser>("echo", "http://swift.im/echo") {} diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadSerializer.h b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadSerializer.h index 85e8e67..068113c 100644 --- a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadSerializer.h +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/EchoPayloadSerializer.h @@ -9,9 +9,7 @@  #include <Swiften/Swiften.h>  #include "EchoPayload.h" -using namespace Swift; - -class EchoPayloadSerializer : public GenericPayloadSerializer<EchoPayload> { +class EchoPayloadSerializer : public Swift::GenericPayloadSerializer<EchoPayload> {  	public:  		std::string serializePayload(boost::shared_ptr<EchoPayload> payload) const {  			XMLElement element("echo", "http://swift.im/protocol/echo"); diff --git a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript index 1960609..c6349bd 100644 --- a/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript +++ b/Documentation/SwiftenDevelopersGuide/Examples/EchoBot/SConscript @@ -15,11 +15,11 @@ if env["PLATFORM"] == "win32" :  	if int(env["MSVS_VERSION"].split(".")[0]) >= 10 :  		cpp0x = True  else : -	pass -# Disabling C++0x compilation, because older boosts are not compliant yet -#	if env["CCVERSION"].split(".") >= ["4", "5", "0"] : -#		cpp0x = True -#		cpp0x_env.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if flag != "-Werror"]) -#		cpp0x_env.Append(CXXFLAGS = ["-std=c++0x"]) +	if env["CCVERSION"].split(".") >= ["4", "5", "0"] : +		# Temporarily disabling c++0x mode because of problems with boost::thread +		# on some platforms +		#cpp0x = True +		cpp0x_env.Replace(CXXFLAGS = [flag for flag in env["CXXFLAGS"] if flag != "-Werror"]) +		cpp0x_env.Append(CXXFLAGS = ["-std=c++0x"])  if cpp0x :  	cpp0x_env.Program("EchoBot0x", "EchoBot0x.cpp") diff --git a/QA/UnitTest/SConscript b/QA/UnitTest/SConscript index 25e9b05..e5923ce 100644 --- a/QA/UnitTest/SConscript +++ b/QA/UnitTest/SConscript @@ -26,8 +26,12 @@ if env["TEST"] :  			myenv.Append(CPPDEFINES = ["HAVE_LIBXML"])  		if env.get("HAVE_EXPAT") :  			myenv.Append(CPPDEFINES = ["HAVE_EXPAT"]) -		checker = myenv.Program("checker", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"]) -		for i in ["HOME", "USERPROFILE", "APPDATA"]: -			if os.environ.get(i, "") : -				myenv["ENV"][i] = os.environ[i] -		myenv.Test(checker, is_checker = True) +		if env["TEST_CREATE_LIBRARIES"] : +			lib = myenv.StaticLibrary("Swift_UnitTests", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"]) +			myenv.Program("checker", lib) +		else : +			checker = myenv.Program("checker", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"]) +			for i in ["HOME", "USERPROFILE", "APPDATA"]: +				if os.environ.get(i, "") : +					myenv["ENV"][i] = os.environ[i] +			myenv.Test(checker, is_checker = True) diff --git a/Slimber/Cocoa/CocoaMenulet.h b/Slimber/Cocoa/CocoaMenulet.h index 7f2758b..5c7c33e 100644 --- a/Slimber/Cocoa/CocoaMenulet.h +++ b/Slimber/Cocoa/CocoaMenulet.h @@ -9,7 +9,7 @@  #include <Cocoa/Cocoa.h>  #include "Slimber/Menulet.h" -#include "Slimber/Cocoa/CocoaAction.h" +#include <SwifTools/Cocoa/CocoaAction.h>  class CocoaMenulet : public Menulet {  	public: diff --git a/Slimber/Cocoa/SConscript b/Slimber/Cocoa/SConscript index e2d8221..d664846 100644 --- a/Slimber/Cocoa/SConscript +++ b/Slimber/Cocoa/SConscript @@ -16,7 +16,6 @@ myenv.Program("Slimber", [  		"main.mm",  		"CocoaController.mm",  		"CocoaMenulet.mm", -		"CocoaAction.mm"  	])  myenv.Nib("MainMenu") diff --git a/Slimber/Server.cpp b/Slimber/Server.cpp index 380ce6a..809c56a 100644 --- a/Slimber/Server.cpp +++ b/Slimber/Server.cpp @@ -8,7 +8,9 @@  #include <string>  #include <boost/bind.hpp> +#include <iostream> +#include <Swiften/Base/foreach.h>  #include "Swiften/Base/String.h"  #include "Swiften/LinkLocal/LinkLocalConnector.h"  #include "Swiften/Network/Connection.h" diff --git a/Sluift/Lua/Value.cpp b/Sluift/Lua/Value.cpp index c03e633..3164ec6 100644 --- a/Sluift/Lua/Value.cpp +++ b/Sluift/Lua/Value.cpp @@ -6,9 +6,7 @@  #include "Value.h" -extern "C" { -	#include <lualib.h> -} +#include <lualib.h>  #include <boost/variant/apply_visitor.hpp>  #include <Swiften/Base/foreach.h> diff --git a/Sluift/SConscript b/Sluift/SConscript index 576eae5..7c434de 100644 --- a/Sluift/SConscript +++ b/Sluift/SConscript @@ -22,6 +22,10 @@ if env["SCONS_STAGE"] == "build" :  		myenv.Append(CPPDEFINES = ["SLUIFT_BUILD_DLL"])  	elif myenv["PLATFORM"] == "darwin" :  		myenv["SHLIBSUFFIX"] = ".so" +	if env["PLATFORM"] == "win32" : +		myenv.Append(CFLAGS = ["/TP"]) +	else : +		myenv.Append(CFLAGS = ["-x", "c++"])  	myenv["SLUIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "sluift")  	def patchLua(env, target, source) : diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index 49cfec4..b911772 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -4,16 +4,15 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -extern "C" { -	#include "sluift.h" -	#include <lauxlib.h> -} +#include "sluift.h" +#include <lauxlib.h>  #include <iostream>  #include <string>  #include <deque>  #include <boost/assign/list_of.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Swiften.h>  #include "Watchdog.h" @@ -63,6 +62,10 @@ class SluiftClient {  			return client;  		} +		ClientOptions& getOptions() { +			return options; +		} +  		void connect() {  			rosterReceived = false;  			client->connect(); @@ -179,6 +182,7 @@ class SluiftClient {  	private:  		Client* client; +		ClientOptions options;  		ClientXMLTracer* tracer;  		bool rosterReceived;  		std::deque<Stanza::ref> pendingEvents; @@ -453,12 +457,12 @@ static int sluift_client_set_options(lua_State* L) {  	luaL_checktype(L, 2, LUA_TTABLE);  	lua_getfield(L, 2, "compress");  	if (!lua_isnil(L, -1)) { -		client->getClient()->setUseStreamCompression(lua_toboolean(L, -1)); +		client->getOptions().useStreamCompression = lua_toboolean(L, -1);  	}  	lua_getfield(L, 2, "tls");  	if (!lua_isnil(L, -1)) {  		bool useTLS = lua_toboolean(L, -1); -		client->getClient()->setUseTLS(useTLS ? Client::UseTLSWhenAvailable : Client::NeverUseTLS); +		client->getOptions().useTLS = (useTLS ? ClientOptions::UseTLSWhenAvailable : ClientOptions::NeverUseTLS);  	}  	lua_pushvalue(L, 1);  	return 0; diff --git a/SwifTools/Application/SConscript b/SwifTools/Application/SConscript index 0097bca..32924fc 100644 --- a/SwifTools/Application/SConscript +++ b/SwifTools/Application/SConscript @@ -21,6 +21,7 @@ else :  objects = swiftools_env.StaticObject(sources)  swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) -env.Append(UNITTEST_SOURCES = [ -		File("UnitTest/ApplicationPathProviderTest.cpp") -	]) +if swiftools_env["PLATFORM"] != "darwin" or swiftools_env["target"] == "native" : +	env.Append(UNITTEST_SOURCES = [ +			File("UnitTest/ApplicationPathProviderTest.cpp") +		]) diff --git a/Slimber/Cocoa/CocoaAction.h b/SwifTools/Cocoa/CocoaAction.h index a46ef7c..a46ef7c 100644 --- a/Slimber/Cocoa/CocoaAction.h +++ b/SwifTools/Cocoa/CocoaAction.h diff --git a/Slimber/Cocoa/CocoaAction.mm b/SwifTools/Cocoa/CocoaAction.mm index 15498a1..d560787 100644 --- a/Slimber/Cocoa/CocoaAction.mm +++ b/SwifTools/Cocoa/CocoaAction.mm @@ -1,4 +1,10 @@ -#include "Slimber/Cocoa/CocoaAction.h" +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <SwifTools/Cocoa/CocoaAction.h>  @implementation CocoaAction diff --git a/SwifTools/Cocoa/SConscript b/SwifTools/Cocoa/SConscript new file mode 100644 index 0000000..4ae4a07 --- /dev/null +++ b/SwifTools/Cocoa/SConscript @@ -0,0 +1,8 @@ +Import("swiftools_env", "env") + +sources = [] +if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : +	sources += ["CocoaAction.mm"] + +objects = swiftools_env.StaticObject(sources) +swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) diff --git a/SwifTools/Idle/PlatformIdleQuerier.cpp b/SwifTools/Idle/PlatformIdleQuerier.cpp index a28e701..6735348 100644 --- a/SwifTools/Idle/PlatformIdleQuerier.cpp +++ b/SwifTools/Idle/PlatformIdleQuerier.cpp @@ -7,7 +7,7 @@  #include "SwifTools/Idle/PlatformIdleQuerier.h"  #include "Swiften/Base/Platform.h" -#if defined(SWIFTEN_PLATFORM_MACOSX) && defined(HAVE_IOKIT) +#if defined(SWIFTEN_PLATFORM_MACOSX) && defined(HAVE_IOKIT) && !defined(SWIFTEN_PLATFORM_IPHONE)  #include "SwifTools/Idle/MacOSXIdleQuerier.h"  #elif defined(SWIFTEN_PLATFORM_WINDOWS)  #include "SwifTools/Idle/WindowsIdleQuerier.h" @@ -23,7 +23,7 @@ namespace Swift {  PlatformIdleQuerier::PlatformIdleQuerier() : querier(NULL) {  #if defined(SWIFTEN_PLATFORM_MACOSX) -#if defined(HAVE_IOKIT) +#if defined(HAVE_IOKIT) && !defined(SWIFTEN_PLATFORM_IPHONE)  	querier = new MacOSXIdleQuerier();  #else  	querier = new DummyIdleQuerier(); diff --git a/SwifTools/LastLineTracker.cpp b/SwifTools/LastLineTracker.cpp new file mode 100644 index 0000000..a7360a8 --- /dev/null +++ b/SwifTools/LastLineTracker.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "LastLineTracker.h" + +using namespace Swift; + +LastLineTracker::LastLineTracker() { +	lastFocus = true; +	shouldMove = false; +} + +void LastLineTracker::setHasFocus(bool focus) { +	if (!focus && lastFocus) { +			shouldMove = true; +			lastFocus = focus; +			return; +	} +	shouldMove = false; +	lastFocus = focus; +} + +bool LastLineTracker::getShouldMoveLastLine() { +	bool ret = shouldMove; +	shouldMove = false; +	return ret; +} diff --git a/SwifTools/LastLineTracker.h b/SwifTools/LastLineTracker.h new file mode 100644 index 0000000..b7c9a3b --- /dev/null +++ b/SwifTools/LastLineTracker.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +namespace Swift { +	class LastLineTracker { +		public: +			LastLineTracker(); +			void setHasFocus(bool focus); +			bool getShouldMoveLastLine(); +		private: +			bool lastFocus; +			bool shouldMove; +	}; +} diff --git a/SwifTools/Linkify.cpp b/SwifTools/Linkify.cpp index 91c713f..a5deccb 100644 --- a/SwifTools/Linkify.cpp +++ b/SwifTools/Linkify.cpp @@ -12,7 +12,7 @@  namespace Swift { -static boost::regex linkifyRegexp("^https?://.*"); +static boost::regex linkifyRegexp("^(https?://|xmpp:).*");  std::string Linkify::linkify(const std::string& input) {  	std::ostringstream result; diff --git a/SwifTools/SConscript b/SwifTools/SConscript index d4747db..e5085cc 100644 --- a/SwifTools/SConscript +++ b/SwifTools/SConscript @@ -27,6 +27,7 @@ if env["SCONS_STAGE"] == "build" :  			"AutoUpdater/PlatformAutoUpdaterFactory.cpp",  			"Linkify.cpp",  			"TabComplete.cpp", +			"LastLineTracker.cpp",  		]  	if swiftools_env.get("HAVE_SPARKLE", 0) : @@ -50,8 +51,10 @@ if env["SCONS_STAGE"] == "build" :  			"Application",  			"Dock",  			"Notifier", +			"URIHandler",  			"Idle/IdleQuerierTest",  			"Idle/UnitTest", +			"Cocoa",  			"UnitTest"  		]) diff --git a/SwifTools/URIHandler/MacOSXURIHandler.h b/SwifTools/URIHandler/MacOSXURIHandler.h new file mode 100644 index 0000000..f803420 --- /dev/null +++ b/SwifTools/URIHandler/MacOSXURIHandler.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <SwifTools/URIHandler/URIHandler.h> + +namespace Swift { +	class MacOSXURIHandler : public URIHandler { +		public: +			MacOSXURIHandler(); +			virtual ~MacOSXURIHandler(); + +			virtual void start(); +			virtual void stop(); + +		private: +			class Private; +			Private* p; +	}; +} diff --git a/SwifTools/URIHandler/MacOSXURIHandler.mm b/SwifTools/URIHandler/MacOSXURIHandler.mm new file mode 100644 index 0000000..3542e2f --- /dev/null +++ b/SwifTools/URIHandler/MacOSXURIHandler.mm @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <SwifTools/URIHandler/MacOSXURIHandler.h> + +#include <Cocoa/Cocoa.h> +#include <iostream> + +using namespace Swift; + +@interface MacOSXURIEventHandler : NSObject { +	URIHandler* handler; +} +- (id) initWithHandler: (URIHandler*) handler; +- (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent; + +@end +@implementation MacOSXURIEventHandler +	- (id) initWithHandler: (URIHandler*) h { +		if ([super init]) { +			handler = h; +		} +		return self; +	} + +	- (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent { +		(void) replyEvent; +		NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; +		handler->onURI(std::string([url UTF8String])); +	} +@end + +class MacOSXURIHandler::Private { +	public: +		MacOSXURIEventHandler* eventHandler; +}; + +MacOSXURIHandler::MacOSXURIHandler() { +	p = new Private(); +	p->eventHandler = [[MacOSXURIEventHandler alloc] initWithHandler: this];  +} + +MacOSXURIHandler::~MacOSXURIHandler() { +	[p->eventHandler release]; +	delete p; +} + +void MacOSXURIHandler::start() { +	[[NSAppleEventManager sharedAppleEventManager] setEventHandler:p->eventHandler andSelector:@selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; +	NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; +	LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID); +} + +void MacOSXURIHandler::stop() { +	[[NSAppleEventManager sharedAppleEventManager] removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; +} diff --git a/Swiften/Elements/JingleTransport.h b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.h index ecd2a34..5a2db7a 100644 --- a/Swiften/Elements/JingleTransport.h +++ b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.h @@ -6,9 +6,6 @@  #pragma once -#include <Swiften/Elements/Payload.h> -  namespace Swift { -	class JingleTransport : public Payload { -	}; +	void registerAppAsDefaultXMPPURIHandler();  } diff --git a/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm new file mode 100644 index 0000000..dca91b8 --- /dev/null +++ b/SwifTools/URIHandler/MacOSXURIHandlerHelpers.mm @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <SwifTools/URIHandler/MacOSXURIHandlerHelpers.h> + +#include <Cocoa/Cocoa.h> + +namespace Swift { +	void registerAppAsDefaultXMPPURIHandler() { +		NSString* bundleID = [[NSBundle mainBundle] bundleIdentifier]; +		LSSetDefaultHandlerForURLScheme((CFStringRef)@"xmpp", (CFStringRef)bundleID); +	} +} diff --git a/SwifTools/URIHandler/NullURIHandler.h b/SwifTools/URIHandler/NullURIHandler.h new file mode 100644 index 0000000..28c35bb --- /dev/null +++ b/SwifTools/URIHandler/NullURIHandler.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <SwifTools/URIHandler/URIHandler.h> + +namespace Swift { +	class NullURIHandler : public URIHandler { +		public: +			virtual void start() { +			} + +			virtual void stop() { +			} +	}; +} diff --git a/SwifTools/URIHandler/SConscript b/SwifTools/URIHandler/SConscript new file mode 100644 index 0000000..42c6ca8 --- /dev/null +++ b/SwifTools/URIHandler/SConscript @@ -0,0 +1,23 @@ +Import("swiftools_env", "env") + +sources = [ +		"XMPPURI.cpp", +		"URIHandler.cpp", +	] + +if swiftools_env["PLATFORM"] == "darwin" and swiftools_env["target"] == "native" : +	sources += [ +			"MacOSXURIHandler.mm", +			"MacOSXURIHandlerHelpers.mm", +		] +elif swiftools_env["PLATFORM"] == "win32" : +	sources += [] +else : +	sources += [] + +objects = swiftools_env.StaticObject(sources) +swiftools_env.Append(SWIFTOOLS_OBJECTS = [objects]) + +env.Append(UNITTEST_SOURCES = [ +		File("UnitTest/XMPPURITest.cpp"), +	]) diff --git a/SwifTools/URIHandler/URIHandler.cpp b/SwifTools/URIHandler/URIHandler.cpp new file mode 100644 index 0000000..91e54e5 --- /dev/null +++ b/SwifTools/URIHandler/URIHandler.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <SwifTools/URIHandler/URIHandler.h> + +using namespace Swift; + +URIHandler::URIHandler() { +} + +URIHandler::~URIHandler() { +} diff --git a/SwifTools/URIHandler/URIHandler.h b/SwifTools/URIHandler/URIHandler.h new file mode 100644 index 0000000..9dd13a8 --- /dev/null +++ b/SwifTools/URIHandler/URIHandler.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { +	class URIHandler { +		public: +			URIHandler(); +			virtual ~URIHandler(); + +			boost::signal<void (const std::string&)> onURI; +	}; +} diff --git a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp new file mode 100644 index 0000000..8d03b60 --- /dev/null +++ b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <SwifTools/URIHandler/XMPPURI.h> + +using namespace Swift; + +class XMPPURITest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(XMPPURITest); +		CPPUNIT_TEST(testFromString_Authority); +		CPPUNIT_TEST(testFromString_AuthorityWithPath); +		CPPUNIT_TEST(testFromString_AuthorityWithFragment); +		CPPUNIT_TEST(testFromString_AuthorityWithPathAndFragment); +		CPPUNIT_TEST(testFromString_AuthorityWithIntlChars); +		CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParameters); +		CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParameters); +		CPPUNIT_TEST(testFromString_AuthorityWithQueryWithoutParametersWithFragment); +		CPPUNIT_TEST(testFromString_AuthorityWithQueryWithParametersWithFragment); +		CPPUNIT_TEST(testFromString_Path); +		CPPUNIT_TEST(testFromString_PathWithFragment); +		CPPUNIT_TEST(testFromString_PathWithIntlChars); +		CPPUNIT_TEST(testFromString_PathWithInvalidEscapedChar); +		CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar); +		CPPUNIT_TEST(testFromString_PathWithIncompleteEscapedChar2); +		CPPUNIT_TEST(testFromString_PathWithQueryWithoutParameters); +		CPPUNIT_TEST(testFromString_PathWithQueryWithParameters); +		CPPUNIT_TEST(testFromString_PathWithQueryWithoutParametersWithFragment); +		CPPUNIT_TEST(testFromString_PathWithQueryWithParametersWithFragment); +		CPPUNIT_TEST(testFromString_NoPrefix); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testFromString_Authority() { +			XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com"); + +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); +		} + +		void testFromString_AuthorityWithPath() { +			XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com"); + +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); +		} + +		void testFromString_AuthorityWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_AuthorityWithPathAndFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp://foo@bar.com/baz@example.com#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_AuthorityWithIntlChars() { +			XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); + +			CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); +		} + +		void testFromString_AuthorityWithQueryWithoutParameters() { +			XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +		} + +		void testFromString_AuthorityWithQueryWithParameters() { +			XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); +			CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); +		} + +		void testFromString_AuthorityWithQueryWithoutParametersWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_AuthorityWithQueryWithParametersWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp://test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getAuthority()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); +			CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_Path() { +			XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com"); + +			CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); +		} + +		void testFromString_PathWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp:baz@example.com#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("baz@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_PathWithIntlChars() { +			XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); + +			CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); +		} + +		void testFromString_PathWithInvalidEscapedChar() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test%%@example.com"); + +			CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); +		} + +		void testFromString_PathWithIncompleteEscapedChar() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%"); + +			CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); +		} + +		void testFromString_PathWithIncompleteEscapedChar2() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com%1"); + +			CPPUNIT_ASSERT_EQUAL(JID(), testling.getPath()); +		} + +		void testFromString_PathWithQueryWithoutParameters() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +		} + +		void testFromString_PathWithQueryWithParameters() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); +			CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); +		} + +		void testFromString_PathWithQueryWithoutParametersWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_PathWithQueryWithParametersWithFragment() { +			XMPPURI testling = XMPPURI::fromString("xmpp:test@example.com?message;subject=Test%20Message;body=Here%27s%20a%20test%20message#myfragment"); + +			CPPUNIT_ASSERT_EQUAL(JID("test@example.com"), testling.getPath()); +			CPPUNIT_ASSERT_EQUAL(std::string("message"), testling.getQueryType()); +			CPPUNIT_ASSERT_EQUAL(std::string("Test Message"), get(testling.getQueryParameters(), "subject")); +			CPPUNIT_ASSERT_EQUAL(std::string("Here's a test message"), get(testling.getQueryParameters(), "body")); +			CPPUNIT_ASSERT_EQUAL(std::string("myfragment"), testling.getFragment()); +		} + +		void testFromString_NoPrefix() { +			XMPPURI testling = XMPPURI::fromString("baz@example.com"); + +			CPPUNIT_ASSERT(testling.isNull()); +		} + +	private: +		std::string get(const std::map<std::string, std::string>& m, const std::string& k) { +			std::map<std::string, std::string>::const_iterator i = m.find(k); +			return i == m.end() ? "" : i->second; +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(XMPPURITest); diff --git a/SwifTools/URIHandler/XMPPURI.cpp b/SwifTools/URIHandler/XMPPURI.cpp new file mode 100644 index 0000000..de83ac2 --- /dev/null +++ b/SwifTools/URIHandler/XMPPURI.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <SwifTools/URIHandler/XMPPURI.h> + +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/find_format.hpp> +#include <boost/algorithm/string/formatter.hpp> +#include <boost/algorithm/string/find_iterator.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <sstream> +#include <stdexcept> +#include <vector> + +using namespace Swift; + +// Should be in anonymous namespace, but older GCCs complain if we do that +struct PercentEncodedCharacterFinder { +	template<typename Iterator> +	boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { +		boost::iterator_range<Iterator> r = boost::first_finder("%")(begin, end); +		if (r.end() == end) { +			return r; +		} +		else { +			if (r.end() + 1 == end || r.end() + 2 == end) { +				throw std::runtime_error("Incomplete escape character"); +			} +			else { +				r.advance_end(2); +				return r; +			} +		} +	} +}; + +struct PercentUnencodeFormatter { +	template<typename FindResult> +	std::string operator()(const FindResult& match) const { +		std::stringstream s; +		s << std::hex << std::string(match.begin() + 1, match.end()); +		unsigned int value; +		s >> value; +		if (s.fail() || s.bad()) { +			throw std::runtime_error("Invalid escape character"); +		} +		unsigned char charValue = static_cast<unsigned char>(value); +		return std::string(reinterpret_cast<const char*>(&charValue), 1); +	} +}; + +namespace { +	std::string unescape(const std::string& s) { +		try { +			return boost::find_format_all_copy(s, PercentEncodedCharacterFinder(), PercentUnencodeFormatter()); +		} +		catch (const std::exception&) { +			return ""; +		} +	} +} + +XMPPURI::XMPPURI() { +} + +XMPPURI XMPPURI::fromString(const std::string& s) { +	XMPPURI result; +	if (boost::starts_with(s, "xmpp:")) { +		std::string uri = s.substr(5, s.npos); +		bool parsePath = true; +		bool parseQuery = true; +		bool parseFragment = true; + +		// Parse authority +		if (boost::starts_with(uri, "//")) { +			size_t i = uri.find_first_of("/#?", 2); +			result.setAuthority(JID(unescape(uri.substr(2, i - 2)))); +			if (i == uri.npos) { +				uri = ""; +				parsePath = parseQuery = parseFragment = false; +			} +			else { +				if (uri[i] == '?') { +					parsePath = false; +				} +				else if (uri[i] == '#') { +					parseQuery = parsePath = false; +				} +				uri = uri.substr(i + 1, uri.npos); +			} +		} + +		// Parse path +		if (parsePath) { +			size_t i = uri.find_first_of("#?"); +			result.setPath(JID(unescape(uri.substr(0, i)))); +			if (i == uri.npos) { +				uri = ""; +				parseQuery = parseFragment = false; +			} +			else { +				if (uri[i] == '#') { +					parseQuery = false; +				} +				uri = uri.substr(i + 1, uri.npos); +			} +		} + +		// Parse query +		if (parseQuery) { +			size_t end = uri.find_first_of("#"); +			std::string query = uri.substr(0, end); +			bool haveType = false; +			typedef boost::split_iterator<std::string::iterator> split_iterator; +	    for (split_iterator it = boost::make_split_iterator(query, boost::first_finder(";")); it != split_iterator(); ++it) { +	    	if (haveType) { +	    		std::vector<std::string> keyValue; +	    		boost::split(keyValue, *it, boost::is_any_of("=")); +	    		if (keyValue.size() == 1) { +	    			result.addQueryParameter(unescape(keyValue[0]), ""); +	    		} +	    		else if (keyValue.size() >= 2) { +	    			result.addQueryParameter(unescape(keyValue[0]), unescape(keyValue[1])); +	    		} +	    	} +	    	else { +	    		result.setQueryType(unescape(boost::copy_range<std::string>(*it))); +	    		haveType = true; +	    	} +	    } +	    uri = (end == uri.npos ? "" : uri.substr(end + 1, uri.npos)); +		} + +		// Parse fragment +		if (parseFragment) { +			result.setFragment(unescape(uri)); +		} +	} +	return result; +} diff --git a/SwifTools/URIHandler/XMPPURI.h b/SwifTools/URIHandler/XMPPURI.h new file mode 100644 index 0000000..266b79b --- /dev/null +++ b/SwifTools/URIHandler/XMPPURI.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <map> + +#include <Swiften/JID/JID.h> + +namespace Swift { +	class XMPPURI { +		public: +			XMPPURI(); + +			const JID& getAuthority() const { +				return authority; +			} + +			void setAuthority(const JID& j) { +				authority = j; +			} + +			const JID& getPath() const { +				return path; +			} + +			void setPath(const JID& j) { +				path = j; +			} + +			const std::string& getQueryType() const { +				return queryType; +			} + +			void setQueryType(const std::string& q) { +				queryType = q; +			} + +			const std::map<std::string, std::string>& getQueryParameters() const { +				return queryParameters; +			} + +			void addQueryParameter(const std::string& key, const std::string& path) { +				queryParameters[key] = path; +			} + +			const std::string& getFragment() const { +				return fragment; +			} + +			void setFragment(const std::string& f) { +				fragment = f; +			} + +			bool isNull() const { +				return !authority.isValid() && !path.isValid(); +			} + +			static XMPPURI fromString(const std::string&); + +		private: +			JID authority; +			JID path; +			std::string fragment; +			std::string queryType; +			std::map<std::string, std::string> queryParameters; +	}; +} diff --git a/SwifTools/UnitTest/LastLineTrackerTest.cpp b/SwifTools/UnitTest/LastLineTrackerTest.cpp new file mode 100644 index 0000000..374f4de --- /dev/null +++ b/SwifTools/UnitTest/LastLineTrackerTest.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "SwifTools/LastLineTracker.h" + +using namespace Swift; + +class LastLineTrackerTest : public CppUnit::TestFixture { +	CPPUNIT_TEST_SUITE(LastLineTrackerTest); +	CPPUNIT_TEST(testFocusNormal); +	CPPUNIT_TEST(testFocusOut); +	CPPUNIT_TEST(testFocusOtherTab); +	CPPUNIT_TEST(testRepeatedFocusOut); +	CPPUNIT_TEST(testRepeatedFocusIn); +	CPPUNIT_TEST_SUITE_END(); +	public: +	LastLineTrackerTest () { +	}; +	void testFocusNormal() { +		LastLineTracker testling; +		testling.setHasFocus(true); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +	} +	void testFocusOut() { +		LastLineTracker testling; +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +	} +	void testFocusOtherTab() { +		LastLineTracker testling; +		testling.setHasFocus(true); +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +	} + +	void testRepeatedFocusOut() { +		LastLineTracker testling; +		testling.setHasFocus(true); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(false, testling.getShouldMoveLastLine()); +	} +	void testRepeatedFocusIn() { +		LastLineTracker testling; +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); +		testling.setHasFocus(true); +		testling.setHasFocus(false); +		CPPUNIT_ASSERT_EQUAL(true, testling.getShouldMoveLastLine()); +	} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(LastLineTrackerTest); diff --git a/SwifTools/UnitTest/SConscript b/SwifTools/UnitTest/SConscript index 8034905..e469deb 100644 --- a/SwifTools/UnitTest/SConscript +++ b/SwifTools/UnitTest/SConscript @@ -2,5 +2,6 @@ Import("env")  env.Append(UNITTEST_SOURCES = [  		File("LinkifyTest.cpp"), -		File("TabCompleteTest.cpp") +		File("TabCompleteTest.cpp"), +		File("LastLineTrackerTest.cpp"),  	]) diff --git a/Swift/Controllers/AdHocManager.cpp b/Swift/Controllers/AdHocManager.cpp new file mode 100644 index 0000000..0fa63a1 --- /dev/null +++ b/Swift/Controllers/AdHocManager.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/AdHocManager.h> + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> +#include <Swift/Controllers/UIInterfaces/MainWindow.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> + +namespace Swift { + +AdHocManager::AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow) : jid_(jid) { +	iqRouter_ = iqRouter; +	uiEventStream_ = uiEventStream; +	mainWindow_ = mainWindow; +	factory_ = factory; + +	uiEventStream_->onUIEvent.connect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); +} + +AdHocManager::~AdHocManager() { +	uiEventStream_->onUIEvent.disconnect(boost::bind(&AdHocManager::handleUIEvent, this, _1)); +} + +void AdHocManager::setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info) { +	if (iqRouter_->isAvailable() && info->hasFeature(DiscoInfo::CommandsFeature)) { +		if (discoItemsRequest_) { +			discoItemsRequest_->onResponse.disconnect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); +			discoItemsRequest_.reset(); +		} +		discoItemsRequest_ = GetDiscoItemsRequest::create(JID(jid_.getDomain()), DiscoInfo::CommandsFeature, iqRouter_); +		discoItemsRequest_->onResponse.connect(boost::bind(&AdHocManager::handleServerDiscoItemsResponse, this, _1, _2)); +		discoItemsRequest_->send(); +	} else { +		mainWindow_->setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	} + +} + +void AdHocManager::handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error) { +	std::vector<DiscoItems::Item> commands; +	if (!error) { +		foreach (DiscoItems::Item item, items->getItems()) { +			if (item.getNode() != "http://isode.com/xmpp/commands#test") { +				commands.push_back(item); +			} +		} +	} +	mainWindow_->setAvailableAdHocCommands(commands); +} + +void AdHocManager::handleUIEvent(boost::shared_ptr<UIEvent> event) { +	boost::shared_ptr<RequestAdHocUIEvent> adHocEvent = boost::dynamic_pointer_cast<RequestAdHocUIEvent>(event); +	if (adHocEvent) { +		factory_->createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession>(new OutgoingAdHocCommandSession(adHocEvent->getCommand(), factory_, iqRouter_))); +	} +} + +} diff --git a/Swift/Controllers/AdHocManager.h b/Swift/Controllers/AdHocManager.h new file mode 100644 index 0000000..47b03cd --- /dev/null +++ b/Swift/Controllers/AdHocManager.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> +#include <vector> + +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Elements/ErrorPayload.h> +#include <Swiften/Disco/GetDiscoItemsRequest.h> +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class IQRouter; +	class MainWindow; +	class UIEventStream; +	class AdHocCommandWindowFactory; +	class AdHocManager { +		public: +			AdHocManager(const JID& jid, AdHocCommandWindowFactory* factory, IQRouter* iqRouter, UIEventStream* uiEventStream, MainWindow* mainWindow); +			~AdHocManager(); +			void setServerDiscoInfo(boost::shared_ptr<DiscoInfo> info); +		private: +			void handleUIEvent(boost::shared_ptr<UIEvent> event); +			void handleServerDiscoItemsResponse(boost::shared_ptr<DiscoItems>, ErrorPayload::ref error); +			JID jid_; +			IQRouter* iqRouter_; +			UIEventStream* uiEventStream_; +			MainWindow* mainWindow_; +			AdHocCommandWindowFactory* factory_; +			GetDiscoItemsRequest::ref discoItemsRequest_; +	}; +} diff --git a/Swift/Controllers/CertificateMemoryStorageFactory.h b/Swift/Controllers/CertificateMemoryStorageFactory.h new file mode 100644 index 0000000..adbce80 --- /dev/null +++ b/Swift/Controllers/CertificateMemoryStorageFactory.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/Storages/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateMemoryStorage.h> + +namespace Swift { +	class CertificateFactory; + +	class CertificateMemoryStorageFactory : public CertificateStorageFactory { +		public: +			CertificateMemoryStorageFactory() { +			} + +			virtual CertificateStorage* createCertificateStorage(const JID&) const { +				return new CertificateMemoryStorage(); +			} + +		private: +			boost::filesystem::path basePath; +			CertificateFactory* certificateFactory; +	}; +} diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp index 22ef68d..9767f8d 100644 --- a/Swift/Controllers/Chat/ChatController.cpp +++ b/Swift/Controllers/Chat/ChatController.cpp @@ -102,8 +102,23 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me  			setToJID(from);  		}  	} -	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>()); +	boost::shared_ptr<Replace> replace = message->getPayload<Replace>(); +	if (replace) { +		// Determine the timestamp +		boost::posix_time::ptime timeStamp = boost::posix_time::microsec_clock::universal_time(); +		boost::optional<boost::posix_time::ptime> messageTimeStamp = getMessageTimestamp(message); +		if (messageTimeStamp) { +			timeStamp = *messageTimeStamp; +		} +		std::string body = message->getBody(); +		chatWindow_->replaceMessage(body, lastMessageUIID_, timeStamp); +		replacedMessage_ = true; +	} +	else { +		replacedMessage_ = false; +	}  	chatStateTracker_->handleMessageReceived(message); +	chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());  }  void ChatController::postHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) { @@ -116,21 +131,30 @@ void ChatController::preSendMessageRequest(boost::shared_ptr<Message> message) {  }  void ChatController::postSendMessage(const std::string& body, boost::shared_ptr<Stanza> sentStanza) { -	std::string id = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); -	if (stanzaChannel_->getStreamManagementEnabled()) { -		chatWindow_->setAckState(id, ChatWindow::Pending); -		unackedStanzas_[sentStanza] = id; +	if (stanzaChannel_->getStreamManagementEnabled() && !myLastMessageUIID_.empty()) { +		chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +		unackedStanzas_[sentStanza] = myLastMessageUIID_; +	} +	boost::shared_ptr<Replace> replace = sentStanza->getPayload<Replace>(); +	if (replace) { +		chatWindow_->replaceMessage(body, myLastMessageUIID_, boost::posix_time::microsec_clock::universal_time()); +	} else { +		myLastMessageUIID_ = addMessage(body, QT_TRANSLATE_NOOP("", "me"), true, labelsEnabled_ ? chatWindow_->getSelectedSecurityLabel().getLabel() : boost::shared_ptr<SecurityLabel>(), std::string(avatarManager_->getAvatarPath(selfJID_).string()), boost::posix_time::microsec_clock::universal_time()); +		if (stanzaChannel_->getStreamManagementEnabled()) { +			chatWindow_->setAckState(myLastMessageUIID_, ChatWindow::Pending); +			unackedStanzas_[sentStanza] = myLastMessageUIID_; +		}  	}  	lastWasPresence_ = false;  	chatStateNotifier_->userSentMessage();  }  void ChatController::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { -	std::string id = unackedStanzas_[stanza]; -	if (id != "") { -		chatWindow_->setAckState(id, ChatWindow::Received); +	std::map<boost::shared_ptr<Stanza>, std::string>::iterator unackedStanza = unackedStanzas_.find(stanza); +	if (unackedStanza != unackedStanzas_.end()) { +		chatWindow_->setAckState(unackedStanza->second, ChatWindow::Received); +		unackedStanzas_.erase(unackedStanza);  	} -	unackedStanzas_.erase(unackedStanzas_.find(stanza));  }  void ChatController::setOnline(bool online) { diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h index dd4bf90..4fafb44 100644 --- a/Swift/Controllers/Chat/ChatController.h +++ b/Swift/Controllers/Chat/ChatController.h @@ -40,6 +40,7 @@ namespace Swift {  			NickResolver* nickResolver_;  			ChatStateNotifier* chatStateNotifier_;  			ChatStateTracker* chatStateTracker_; +			std::string myLastMessageUIID_;  			bool isInMUC_;  			bool lastWasPresence_;  			std::string lastStatusChangeString_; diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index 281d968..14e17cd 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -30,9 +30,10 @@ namespace Swift {  ChatControllerBase::ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory) : selfJID_(self), stanzaChannel_(stanzaChannel), iqRouter_(iqRouter), chatWindowFactory_(chatWindowFactory), toJID_(toJID), labelsEnabled_(false), presenceOracle_(presenceOracle), avatarManager_(avatarManager), useDelayForLatency_(useDelayForLatency), eventController_(eventController), timerFactory_(timerFactory) {  	chatWindow_ = chatWindowFactory_->createChatWindow(toJID, eventStream);  	chatWindow_->onAllMessagesRead.connect(boost::bind(&ChatControllerBase::handleAllMessagesRead, this)); -	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1)); +	chatWindow_->onSendMessageRequest.connect(boost::bind(&ChatControllerBase::handleSendMessageRequest, this, _1, _2));  	setOnline(stanzaChannel->isAvailable() && iqRouter->isAvailable());  	createDayChangeTimer(); +	replacedMessage_ = false;  }  ChatControllerBase::~ChatControllerBase() { @@ -88,7 +89,7 @@ void ChatControllerBase::handleAllMessagesRead() {  	chatWindow_->setUnreadMessageCount(0);  } -void ChatControllerBase::handleSendMessageRequest(const std::string &body) { +void ChatControllerBase::handleSendMessageRequest(const std::string &body, bool isCorrectionMessage) {  	if (!stanzaChannel_->isAvailable() || body.empty()) {  		return;  	} @@ -104,8 +105,13 @@ void ChatControllerBase::handleSendMessageRequest(const std::string &body) {  		boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();  		message->addPayload(boost::shared_ptr<Delay>(new Delay(now, selfJID_)));  	} +	if (isCorrectionMessage) { +		message->addPayload(boost::shared_ptr<Replace> (new Replace(lastSentMessageStanzaID_))); +	} +	message->setID(lastSentMessageStanzaID_ = idGenerator_.generateID());  	stanzaChannel_->sendMessage(message);  	postSendMessage(message->getBody(), boost::dynamic_pointer_cast<Stanza>(message)); +	onActivity(message->getBody());  }  void ChatControllerBase::handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog> catalog, ErrorPayload::ref error) { @@ -163,7 +169,7 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		JID from = message->getFrom();  		std::vector<boost::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>();  		for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { -			if (!delayPayloads[i]->getFrom()) {  +			if (!delayPayloads[i]->getFrom()) {  				continue;  			}  			boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); @@ -179,8 +185,10 @@ void ChatControllerBase::handleIncomingMessage(boost::shared_ptr<MessageEvent> m  		if (messageTimeStamp) {  			timeStamp = *messageTimeStamp;  		} - -		addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +		onActivity(body); +		if (!replacedMessage_) { +			lastMessageUIID_ = addMessage(body, senderDisplayNameFromMessage(from), isIncomingMessageFromMe(message), label, std::string(avatarManager_->getAvatarPath(from).string()), timeStamp); +		}  	}  	chatWindow_->show();  	chatWindow_->setUnreadMessageCount(unreadMessages_.size()); diff --git a/Swift/Controllers/Chat/ChatControllerBase.h b/Swift/Controllers/Chat/ChatControllerBase.h index 9573b1b..e0f1b94 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.h +++ b/Swift/Controllers/Chat/ChatControllerBase.h @@ -26,6 +26,7 @@  #include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/Presence/PresenceOracle.h"  #include "Swiften/Queries/IQRouter.h" +#include "Swiften/Base/IDGenerator.h"  namespace Swift {  	class IQRouter; @@ -47,6 +48,8 @@ namespace Swift {  			virtual void setOnline(bool online);  			virtual void setEnabled(bool enabled);  			virtual void setToJID(const JID& jid) {toJID_ = jid;}; +			/** Used for determining when something is recent.*/ +			boost::signal<void (const std::string& /*activity*/)> onActivity;  		protected:  			ChatControllerBase(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &toJID, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory); @@ -64,8 +67,10 @@ namespace Swift {  			virtual void dayTicked() {};  		private: +			IDGenerator idGenerator_; +			std::string lastSentMessageStanzaID_;  			void createDayChangeTimer(); -			void handleSendMessageRequest(const std::string &body); +			void handleSendMessageRequest(const std::string &body, bool isCorrectionMessage);  			void handleAllMessagesRead();  			void handleSecurityLabelsCatalogResponse(boost::shared_ptr<SecurityLabelsCatalog>, ErrorPayload::ref error);  			std::string getErrorMessage(boost::shared_ptr<ErrorPayload>); @@ -80,6 +85,8 @@ namespace Swift {  			ChatWindow* chatWindow_;  			JID toJID_;  			bool labelsEnabled_; +			bool replacedMessage_; +			std::string lastMessageUIID_;  			PresenceOracle* presenceOracle_;  			AvatarManager* avatarManager_;  			bool useDelayForLatency_; diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 94d4b9a..b60f9a3 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -7,7 +7,9 @@  #include "Swift/Controllers/Chat/ChatsManager.h"  #include <boost/bind.hpp> +#include <boost/algorithm/string.hpp> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/Chat/ChatController.h"  #include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/XMPPEvents/EventController.h" @@ -26,12 +28,15 @@  #include "Swiften/MUC/MUCManager.h"  #include "Swiften/Elements/ChatState.h"  #include "Swiften/MUC/MUCBookmarkManager.h" +#include <Swift/Controllers/ProfileSettingsProvider.h>  namespace Swift {  typedef std::pair<JID, ChatController*> JIDChatControllerPair;  typedef std::pair<JID, MUCController*> JIDMUCControllerPair; +#define RECENT_CHATS "recent_chats" +  ChatsManager::ChatsManager(  		JID jid, StanzaChannel* stanzaChannel,   		IQRouter* iqRouter,  @@ -49,7 +54,7 @@ ChatsManager::ChatsManager(  		EntityCapsProvider* entityCapsProvider,   		MUCManager* mucManager,  		MUCSearchWindowFactory* mucSearchWindowFactory, -		SettingsProvider* settings) :  +		ProfileSettingsProvider* settings) :  			jid_(jid),   			joinMUCWindowFactory_(joinMUCWindowFactory),   			useDelayForLatency_(useDelayForLatency),  @@ -68,6 +73,7 @@ ChatsManager::ChatsManager(  	presenceSender_ = presenceSender;  	uiEventStream_ = uiEventStream;  	mucBookmarkManager_ = NULL; +	profileSettings_ = settings;  	presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));  	uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));  	chatListWindow_ = chatListWindowFactory->createChatListWindow(uiEventStream_); @@ -75,6 +81,7 @@ ChatsManager::ChatsManager(  	mucSearchController_ = new MUCSearchController(jid_, mucSearchWindowFactory, iqRouter, settings);  	mucSearchController_->onMUCSelected.connect(boost::bind(&ChatsManager::handleMUCSelectedAfterSearch, this, _1));  	setupBookmarks(); +	loadRecents();  }  ChatsManager::~ChatsManager() { @@ -89,6 +96,44 @@ ChatsManager::~ChatsManager() {  	delete mucSearchController_;  } +void ChatsManager::saveRecents() { +	std::string recents; +	foreach (ChatListWindow::Chat chat, recentChats_) { +		std::vector<std::string> activity; +		boost::split(activity, chat.activity, boost::is_any_of("\t\n")); +		std::string recent = chat.jid.toString() + "\t" + activity[0] + "\t" + (chat.isMUC ? "true" : "false") +  "\t" + chat.nick; +		recents += recent + "\n"; +	} +	profileSettings_->storeString(RECENT_CHATS, recents); +} + +void ChatsManager::loadRecents() { +	std::string recentsString(profileSettings_->getStringSetting(RECENT_CHATS)); +	std::vector<std::string> recents; +	boost::split(recents, recentsString, boost::is_any_of("\n")); +	int i = 0; +	foreach (std::string recentString, recents) { +		if (i++ > 30) { +			break; +		} +		std::vector<std::string> recent; +		boost::split(recent, recentString, boost::is_any_of("\t")); +		if (recent.size() < 4) { +			continue; +		} +		JID jid(recent[0]); +		if (!jid.isValid()) { +			continue; +		} +		std::string activity(recent[1]); +		bool isMUC = recent[2] == "true"; +		std::string nick(recent[3]); +		ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, isMUC, nick); +		recentChats_.push_back(chat); +		chatListWindow_->setRecents(recentChats_); +	} +} +  void ChatsManager::setupBookmarks() {  	if (!mucBookmarkManager_) {  		mucBookmarkManager_ = new MUCBookmarkManager(iqRouter_); @@ -98,7 +143,7 @@ void ChatsManager::setupBookmarks() {  		if (chatListWindow_) {  			chatListWindow_->setBookmarksEnabled(false); -			chatListWindow_->clear(); +			chatListWindow_->clearBookmarks();  		}  	}  } @@ -122,6 +167,16 @@ void ChatsManager::handleMUCBookmarkRemoved(const MUCBookmark& bookmark) {  	chatListWindow_->removeMUCBookmark(bookmark);  } +void ChatsManager::handleChatActivity(const JID& jid, const std::string& activity) { +	/* FIXME: MUC use requires changes here. */ +	ChatListWindow::Chat chat(jid, nickResolver_->jidToNick(jid), activity, false); +	/* FIXME: handle nick changes */ +	recentChats_.erase(std::remove(recentChats_.begin(), recentChats_.end(), chat), recentChats_.end()); +	recentChats_.push_front(chat); +	chatListWindow_->setRecents(recentChats_); +	saveRecents(); +} +  void ChatsManager::handleUserLeftMUC(MUCController* mucController) {  	std::map<JID, MUCController*>::iterator it;  	for (it = mucControllers_.begin(); it != mucControllers_.end(); it++) { @@ -157,13 +212,13 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {  	else if (JoinMUCUIEvent::ref joinEvent = boost::dynamic_pointer_cast<JoinMUCUIEvent>(event)) {  		handleJoinMUCRequest(joinEvent->getJID(), joinEvent->getNick(), false);  	} -	else if (boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) { +	else if (boost::shared_ptr<RequestJoinMUCUIEvent> joinEvent = boost::dynamic_pointer_cast<RequestJoinMUCUIEvent>(event)) {  		if (!joinMUCWindow_) {  			joinMUCWindow_ = joinMUCWindowFactory_->createJoinMUCWindow();  			joinMUCWindow_->onJoinMUC.connect(boost::bind(&ChatsManager::handleJoinMUCRequest, this, _1, _2, _3));  			joinMUCWindow_->onSearchMUC.connect(boost::bind(&ChatsManager::handleSearchMUCRequest, this));  		} -		joinMUCWindow_->setMUC(""); +		joinMUCWindow_->setMUC(joinEvent->getRoom());  		joinMUCWindow_->setNick(nickResolver_->jidToNick(jid_));  		joinMUCWindow_->show();  	} @@ -244,6 +299,7 @@ ChatController* ChatsManager::createNewChatController(const JID& contact) {  	ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsProvider_);  	chatControllers_[contact] = controller;  	controller->setAvailableServerFeatures(serverDiscoInfo_); +	controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, contact, _1));  	return controller;  } @@ -302,6 +358,7 @@ void ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::optional  		controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));  	}  	mucControllers_[mucJID]->activateChatWindow(); +	/* FIXME: handleChatActivity connection for recents, and changes to that method.*/  }  void ChatsManager::handleSearchMUCRequest() { diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h index 3740186..f489034 100644 --- a/Swift/Controllers/Chat/ChatsManager.h +++ b/Swift/Controllers/Chat/ChatsManager.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,13 +11,14 @@  #include <boost/shared_ptr.hpp>  #include <string> -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUCRegistry.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> +#include <Swiften/JID/JID.h> +#include <Swiften/MUC/MUCRegistry.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> +#include <Swiften/MUC/MUCBookmark.h>  namespace Swift {  	class EventController; @@ -34,18 +35,17 @@ namespace Swift {  	class IQRouter;  	class PresenceSender;  	class MUCBookmarkManager; -	class ChatListWindow;  	class ChatListWindowFactory;  	class TimerFactory;  	class EntityCapsProvider;  	class DirectedPresenceSender;  	class MUCSearchWindowFactory; -	class SettingsProvider; +	class ProfileSettingsProvider;  	class MUCSearchController;  	class ChatsManager {  		public: -			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, SettingsProvider* settings); +			ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* settings);  			virtual ~ChatsManager();  			void setAvatarManager(AvatarManager* avatarManager);  			void setOnline(bool enabled); @@ -63,7 +63,11 @@ namespace Swift {  			void handleMUCBookmarkRemoved(const MUCBookmark& bookmark);  			void handleUserLeftMUC(MUCController* mucController);  			void handleBookmarksReady(); +			void handleChatActivity(const JID& jid, const std::string& activity);  			void setupBookmarks(); +			void loadRecents(); +			void saveRecents(); +			void handleChatMadeRecent();  			ChatController* getChatControllerOrFindAnother(const JID &contact);  			ChatController* createNewChatController(const JID &contact);  			ChatController* getChatControllerOrCreate(const JID &contact); @@ -92,5 +96,7 @@ namespace Swift {  			EntityCapsProvider* entityCapsProvider_;  			MUCManager* mucManager;  			MUCSearchController* mucSearchController_; +			std::list<ChatListWindow::Chat> recentChats_; +			ProfileSettingsProvider* profileSettings_;  	};  } diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 2914116..0604dee 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/Controllers/Chat/MUCController.h" +#include <Swift/Controllers/Chat/MUCController.h>  #include <boost/bind.hpp>  #include <boost/regex.hpp> @@ -12,23 +12,24 @@  #include <Swift/Controllers/Intl.h>  #include <Swiften/Base/format.h> -#include "Swiften/Network/Timer.h" -#include "Swiften/Network/TimerFactory.h" -#include "Swiften/Base/foreach.h" -#include "SwifTools/TabComplete.h" -#include "Swiften/Base/foreach.h" -#include "Swift/Controllers/XMPPEvents/EventController.h" -#include "Swift/Controllers/UIInterfaces/ChatWindow.h" -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h" -#include "Swiften/Avatars/AvatarManager.h" -#include "Swiften/Elements/Delay.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/Client/StanzaChannel.h" -#include "Swift/Controllers/Roster/Roster.h" -#include "Swift/Controllers/Roster/SetAvatar.h" -#include "Swift/Controllers/Roster/SetPresence.h" +#include <Swiften/Network/Timer.h> +#include <Swiften/Network/TimerFactory.h> +#include <Swiften/Base/foreach.h> +#include <SwifTools/TabComplete.h> +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> +#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swiften/Avatars/AvatarManager.h> +#include <Swiften/Elements/Delay.h> +#include <Swiften/MUC/MUC.h> +#include <Swiften/Client/StanzaChannel.h> +#include <Swift/Controllers/Roster/Roster.h> +#include <Swift/Controllers/Roster/SetAvatar.h> +#include <Swift/Controllers/Roster/SetPresence.h>  #define MUC_JOIN_WARNING_TIMEOUT_MILLISECONDS 60000 @@ -206,7 +207,12 @@ void MUCController::handleOccupantJoined(const MUCOccupant& occupant) {  	currentOccupants_.insert(occupant.getNick());  	NickJoinPart event(occupant.getNick(), Join);  	appendToJoinParts(joinParts_, event); -	roster_->addContact(jid, realJID, occupant.getNick(), roleToGroupName(occupant.getRole()), avatarManager_->getAvatarPath(jid).string()); +	std::string groupName(roleToGroupName(occupant.getRole())); +	roster_->addContact(jid, realJID, occupant.getNick(), groupName, avatarManager_->getAvatarPath(jid).string()); +	if (addedRosterGroups_.count(groupName) == 0) { +		roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole())); +		addedRosterGroups_.insert(groupName); +	}  	if (joined_) {  		std::string joinString;  		MUCOccupant::Role role = occupant.getRole(); @@ -248,6 +254,16 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) {  	return "";  } +std::string MUCController::roleToSortName(MUCOccupant::Role role) { +	switch (role) { +	case MUCOccupant::Moderator: return "1"; +	case MUCOccupant::Participant: return "2"; +	case MUCOccupant::Visitor: return "3"; +	case MUCOccupant::NoRole: return "4"; +	} +	return "5"; +} +  JID MUCController::nickToJID(const std::string& nick) {  	return JID(toJID_.getNode(), toJID_.getDomain(), nick);  } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index ebdd6cd..e876791 100644 --- a/Swift/Controllers/Chat/MUCController.h +++ b/Swift/Controllers/Chat/MUCController.h @@ -7,18 +7,18 @@  #pragma once  #include <boost/shared_ptr.hpp> -#include "Swiften/Base/boost_bsignals.h" +#include <Swiften/Base/boost_bsignals.h>  #include <boost/signals/connection.hpp>  #include <set>  #include <string> -#include "Swiften/Network/Timer.h" -#include "Swift/Controllers/Chat/ChatControllerBase.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/DiscoInfo.h" -#include "Swiften/JID/JID.h" -#include "Swiften/MUC/MUC.h" -#include "Swiften/Elements/MUCOccupant.h" +#include <Swiften/Network/Timer.h> +#include <Swift/Controllers/Chat/ChatControllerBase.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/JID/JID.h> +#include <Swiften/MUC/MUC.h> +#include <Swiften/Elements/MUCOccupant.h>  namespace Swift {  	class StanzaChannel; @@ -71,6 +71,7 @@ namespace Swift {  			void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);  			void handleJoinTimeoutTick();  			std::string roleToGroupName(MUCOccupant::Role role); +			std::string roleToSortName(MUCOccupant::Role role);  			JID nickToJID(const std::string& nick);  			std::string roleToFriendlyName(MUCOccupant::Role role);  			void receivedActivity(); @@ -97,6 +98,7 @@ namespace Swift {  			std::set<std::string> currentOccupants_;  			std::vector<NickJoinPart> joinParts_;  			boost::posix_time::ptime lastActivity_; +			std::set<std::string> addedRosterGroups_;  	};  } diff --git a/Swift/Controllers/Chat/MUCSearchController.cpp b/Swift/Controllers/Chat/MUCSearchController.cpp index 743aabb..2cb89b4 100644 --- a/Swift/Controllers/Chat/MUCSearchController.cpp +++ b/Swift/Controllers/Chat/MUCSearchController.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,6 +11,7 @@  #include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Disco/GetDiscoItemsRequest.h>  #include <Swiften/Base/Log.h>  #include <Swiften/Base/String.h> @@ -23,7 +24,7 @@ namespace Swift {  static const std::string SEARCHED_SERVICES = "searchedServices"; -MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, SettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) { +MUCSearchController::MUCSearchController(const JID& jid, MUCSearchWindowFactory* factory, IQRouter* iqRouter, ProfileSettingsProvider* settings) : jid_(jid), factory_(factory), iqRouter_(iqRouter), settings_(settings), window_(NULL), walker_(NULL) {  	itemsInProgress_ = 0;  	loadSavedServices();  } diff --git a/Swift/Controllers/Chat/MUCSearchController.h b/Swift/Controllers/Chat/MUCSearchController.h index c8040ed..f90e4a7 100644 --- a/Swift/Controllers/Chat/MUCSearchController.h +++ b/Swift/Controllers/Chat/MUCSearchController.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -16,7 +16,7 @@  #include "Swiften/JID/JID.h"  #include "Swift/Controllers/UIEvents/UIEvent.h" -#include "Swift/Controllers/Settings/SettingsProvider.h" +#include "Swift/Controllers/ProfileSettingsProvider.h"  #include "Swiften/Elements/DiscoInfo.h"  #include "Swiften/Elements/DiscoItems.h"  #include "Swiften/Elements/ErrorPayload.h" @@ -87,7 +87,7 @@ namespace Swift {  	class MUCSearchController {  		public: -			MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, SettingsProvider* settings); +			MUCSearchController(const JID& jid, MUCSearchWindowFactory* mucSearchWindowFactory, IQRouter* iqRouter, ProfileSettingsProvider* settings);  			~MUCSearchController();  			void openSearchWindow(); @@ -112,7 +112,7 @@ namespace Swift {  			JID jid_;  			MUCSearchWindowFactory* factory_;  			IQRouter* iqRouter_; -			SettingsProvider* settings_; +			ProfileSettingsProvider* settings_;  			MUCSearchWindow* window_;  			DiscoServiceWalker* walker_;  			std::list<JID> services_; diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 40f7445..f8fda9a 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -10,6 +10,7 @@  #include "Swift/Controllers/Chat/ChatsManager.h" +#include "Swift/Controllers/Chat/UnitTest/MockChatListWindow.h"  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "Swift/Controllers/Settings/DummySettingsProvider.h"  #include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" @@ -38,6 +39,7 @@  #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h"  #include "Swift/Controllers/UIEvents/UIEventStream.h" +#include <Swift/Controllers/ProfileSettingsProvider.h>  using namespace Swift; @@ -82,8 +84,10 @@ public:  		chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();  		mucSearchWindowFactory_ = mocks_->InterfaceMock<MUCSearchWindowFactory>();  		settings_ = new DummySettingsProvider(); -		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(NULL); -		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, settings_); +		profileSettings_ = new ProfileSettingsProvider("a", settings_); +		chatListWindow_ = new MockChatListWindow(); +		mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); +		manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_, mucManager_, mucSearchWindowFactory_, profileSettings_);  		avatarManager_ = new NullAvatarManager();  		manager_->setAvatarManager(avatarManager_); @@ -92,6 +96,7 @@ public:  	void tearDown() {  		//delete chatListWindowFactory_;  		delete settings_; +		delete profileSettings_;  		delete mocks_;  		delete avatarManager_;  		delete manager_; @@ -109,6 +114,7 @@ public:  		delete xmppRoster_;  		delete entityCapsManager_;  		delete capsProvider_; +		delete chatListWindow_;  	}  	void testFirstOpenWindowIncoming() { @@ -346,6 +352,8 @@ private:  	CapsProvider* capsProvider_;  	MUCManager* mucManager_;  	DummySettingsProvider* settings_; +	ProfileSettingsProvider* profileSettings_; +	ChatListWindow* chatListWindow_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest); diff --git a/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h new file mode 100644 index 0000000..408a490 --- /dev/null +++ b/Swift/Controllers/Chat/UnitTest/MockChatListWindow.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swift/Controllers/UIInterfaces/ChatListWindow.h" + +namespace Swift { + +	class MockChatListWindow : public ChatListWindow { +		public: +			MockChatListWindow() {}; +			virtual ~MockChatListWindow() {}; +			void addMUCBookmark(const MUCBookmark& /*bookmark*/) {} +			void removeMUCBookmark(const MUCBookmark& /*bookmark*/) {} +			void setBookmarksEnabled(bool /*enabled*/) {} +			void setRecents(const std::list<ChatListWindow::Chat>& /*recents*/) {} +			void clearBookmarks() {} +	}; + +} diff --git a/Swift/Controllers/Chat/UserSearchController.cpp b/Swift/Controllers/Chat/UserSearchController.cpp index b3403d7..deac2f9 100644 --- a/Swift/Controllers/Chat/UserSearchController.cpp +++ b/Swift/Controllers/Chat/UserSearchController.cpp @@ -9,9 +9,9 @@  #include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/foreach.h>  #include <Swiften/Disco/GetDiscoInfoRequest.h>  #include <Swiften/Disco/GetDiscoItemsRequest.h> -  #include <Swift/Controllers/DiscoServiceWalker.h>  #include <Swift/Controllers/UIEvents/UIEventStream.h>  #include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> diff --git a/Swift/Controllers/ChatMessageSummarizer.cpp b/Swift/Controllers/ChatMessageSummarizer.cpp new file mode 100644 index 0000000..682d88b --- /dev/null +++ b/Swift/Controllers/ChatMessageSummarizer.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/ChatMessageSummarizer.h> + +#include <Swiften/Base/format.h> +#include <Swift/Controllers/Intl.h> +#include <Swiften/Base/foreach.h> + +using namespace Swift; +using namespace std; + +string ChatMessageSummarizer::getSummary(const string& current, const vector<UnreadPair>& unreads) { +	vector<UnreadPair> others; +	int currentUnread = 0; +	int otherCount = 0; +	foreach (UnreadPair unread, unreads) { +		if (unread.first == current) { +			currentUnread += unread.second; +		} else { +			if (unread.second > 0) { +				otherCount += unread.second; +				others.push_back(unread); +			} +		} +	} +	string myString(current); + +	if (currentUnread > 0) { +		string result(QT_TRANSLATE_NOOP("", "%1% (%2%)")); +		myString = str(format(result) % current % currentUnread); +	} + +	if (others.size() > 1) { +		string result(QT_TRANSLATE_NOOP("", "%1% and %2% others (%3%)")); +		myString = str(format(result) % myString % others.size() % otherCount); +	} else if (others.size() > 0) { +		string result(QT_TRANSLATE_NOOP("", "%1%, %2% (%3%)")); +		myString = str(format(result) % myString % others[0].first % otherCount); +	} +	return myString; +} diff --git a/Swift/Controllers/ChatMessageSummarizer.h b/Swift/Controllers/ChatMessageSummarizer.h new file mode 100644 index 0000000..d4ff188 --- /dev/null +++ b/Swift/Controllers/ChatMessageSummarizer.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> +#include <utility> +#include <string> + +namespace Swift { +typedef std::pair<std::string, int> UnreadPair; + +	class ChatMessageSummarizer { +		public: +			std::string getSummary(const std::string& current, const std::vector<UnreadPair>& unreads); +	}; +} diff --git a/Swift/Controllers/DiscoServiceWalker.cpp b/Swift/Controllers/DiscoServiceWalker.cpp index ce29927..6aed6eb 100644 --- a/Swift/Controllers/DiscoServiceWalker.cpp +++ b/Swift/Controllers/DiscoServiceWalker.cpp @@ -6,6 +6,7 @@  #include <Swift/Controllers/DiscoServiceWalker.h>  #include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h>  #include <boost/bind.hpp> diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index 9a35cc1..c00c080 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -18,10 +18,8 @@  #include <Swift/Controllers/UIInterfaces/UIFactory.h>  #include "Swiften/Network/TimerFactory.h"  #include "Swift/Controllers/BuildVersion.h" -#include "Swift/Controllers/StoragesFactory.h"  #include "Swiften/Client/Storages.h"  #include "Swiften/VCards/VCardManager.h" -#include "Swift/Controllers/Chat/MUCSearchController.h"  #include "Swift/Controllers/Chat/UserSearchController.h"  #include "Swift/Controllers/Chat/ChatsManager.h"  #include "Swift/Controllers/XMPPEvents/EventController.h" @@ -41,6 +39,7 @@  #include "Swift/Controllers/UIEvents/UIEventStream.h"  #include "Swift/Controllers/PresenceNotifier.h"  #include "Swift/Controllers/EventNotifier.h" +#include "Swift/Controllers/Storages/StoragesFactory.h"  #include "SwifTools/Dock/Dock.h"  #include "SwifTools/Notifier/TogglableNotifier.h"  #include "Swiften/Base/foreach.h" @@ -60,11 +59,13 @@  #include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/ToggleNotificationsUIEvent.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/CertificateStorageFactory.h" -#include "Swift/Controllers/CertificateStorageTrustChecker.h" +#include "Swift/Controllers/Storages/CertificateStorageFactory.h" +#include "Swift/Controllers/Storages/CertificateStorageTrustChecker.h"  #include "Swiften/Network/NetworkFactories.h"  #include <Swift/Controllers/ProfileController.h>  #include <Swift/Controllers/ContactEditController.h> +#include <Swift/Controllers/XMPPURIController.h> +#include "Swift/Controllers/AdHocManager.h"  namespace Swift { @@ -84,6 +85,7 @@ MainController::MainController(  		CertificateStorageFactory* certificateStorageFactory,  		Dock* dock,  		Notifier* notifier, +		URIHandler* uriHandler,  		bool useDelayForLatency) :  			eventLoop_(eventLoop),  			networkFactories_(networkFactories), @@ -92,6 +94,7 @@ MainController::MainController(  			storagesFactory_(storagesFactory),  			certificateStorageFactory_(certificateStorageFactory),  			settings_(settings), +			uriHandler_(uriHandler),  			loginWindow_(NULL) ,  			useDelayForLatency_(useDelayForLatency) {  	storages_ = NULL; @@ -121,6 +124,8 @@ MainController::MainController(  	loginWindow_ = uiFactory_->createLoginWindow(uiEventStream_);  	soundEventController_ = new SoundEventController(eventController_, soundPlayer, settings, uiEventStream_); +	xmppURIController_ = new XMPPURIController(uriHandler_, uiEventStream_); +  	std::string selectedLoginJID = settings_->getStringSetting("lastLoginJID");  	bool loginAutomatically = settings_->getBoolSetting("loginAutomatically", false);  	std::string cachedPassword; @@ -167,6 +172,7 @@ MainController::~MainController() {  	resetClient();  	delete xmlConsoleController_; +	delete xmppURIController_;  	delete soundEventController_;  	delete systemTrayController_;  	delete eventController_; @@ -247,7 +253,7 @@ void MainController::handleConnected() {  		contactEditController_ = new ContactEditController(rosterController_, uiFactory_, uiEventStream_); -		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, settings_); +		chatsManager_ = new ChatsManager(jid_, client_->getStanzaChannel(), client_->getIQRouter(), eventController_, uiFactory_, uiFactory_, client_->getNickResolver(), client_->getPresenceOracle(), client_->getPresenceSender(), uiEventStream_, uiFactory_, useDelayForLatency_, networkFactories_->getTimerFactory(), client_->getMUCRegistry(), client_->getEntityCapsProvider(), client_->getMUCManager(), uiFactory_, profileSettings_);  		client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));  		chatsManager_->setAvatarManager(client_->getAvatarManager()); @@ -262,14 +268,14 @@ void MainController::handleConnected() {  		client_->getDiscoManager()->setCapsNode(CLIENT_NODE);  		client_->getDiscoManager()->setDiscoInfo(discoInfo); -  		userSearchControllerChat_ = new UserSearchController(UserSearchController::StartChat, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_);  		userSearchControllerAdd_ = new UserSearchController(UserSearchController::AddContact, jid_, uiEventStream_, uiFactory_, client_->getIQRouter(), rosterController_); +		adHocManager_ = new AdHocManager(boundJID_, uiFactory_, client_->getIQRouter(), uiEventStream_, rosterController_->getWindow());  	}  	client_->requestRoster(); -	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(JID(), client_->getIQRouter()); +	GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(boundJID_.toBare(), client_->getIQRouter());  	discoInfoRequest->onResponse.connect(boost::bind(&MainController::handleServerDiscoInfoResponse, this, _1, _2));  	discoInfoRequest->send(); @@ -356,19 +362,25 @@ void MainController::handleInputIdleChanged(bool idle) {  }  void MainController::handleLoginRequest(const std::string &username, const std::string &password, const std::string& certificateFile, bool remember, bool loginAutomatically) { -	loginWindow_->setMessage(""); -	loginWindow_->setIsLoggingIn(true); -	profileSettings_ = new ProfileSettingsProvider(username, settings_); -	profileSettings_->storeString("jid", username); -	profileSettings_->storeString("certificate", certificateFile); -	profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); -	settings_->storeString("lastLoginJID", username); -	settings_->storeBool("loginAutomatically", loginAutomatically); -	loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate"));  	jid_ = JID(username); -	password_ = password; -	certificateFile_ = certificateFile; -	performLoginFromCachedCredentials(); +	if (!jid_.isValid() || jid_.getNode().empty()) { +		loginWindow_->setMessage(QT_TRANSLATE_NOOP("", "User address invalid. User address should be of the form 'alice@wonderland.lit'")); +		loginWindow_->setIsLoggingIn(false); +	} else { +		loginWindow_->setMessage(""); +		loginWindow_->setIsLoggingIn(true); +		profileSettings_ = new ProfileSettingsProvider(username, settings_); +		profileSettings_->storeString("jid", username); +		profileSettings_->storeString("certificate", certificateFile); +		profileSettings_->storeString("pass", (remember || loginAutomatically) ? password : ""); +		settings_->storeString("lastLoginJID", username); +		settings_->storeBool("loginAutomatically", loginAutomatically); +		loginWindow_->addAvailableAccount(profileSettings_->getStringSetting("jid"), profileSettings_->getStringSetting("pass"), profileSettings_->getStringSetting("certificate")); + +		password_ = password; +		certificateFile_ = certificateFile; +		performLoginFromCachedCredentials(); +	}  }  void MainController::handlePurgeSavedLoginRequest(const std::string& username) { @@ -483,6 +495,7 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro  		else if (!rosterController_) { //hasn't been logged in yet  			signOut();  			loginWindow_->setMessage(message); +			loginWindow_->setIsLoggingIn(false);  		} else {  			logout();  			setReconnectTimer(); @@ -496,6 +509,9 @@ void MainController::handleDisconnected(const boost::optional<ClientError>& erro  			eventController_->handleIncomingEvent(lastDisconnectError_);  		}  	} +	else if (!rosterController_) { //hasn't been logged in yet +		loginWindow_->setIsLoggingIn(false); +	}  }  void MainController::setReconnectTimer() { @@ -554,6 +570,7 @@ void MainController::setManagersOffline() {  void MainController::handleServerDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error) {  	if (!error) {  		chatsManager_->setServerDiscoInfo(info); +		adHocManager_->setServerDiscoInfo(info);  	}  } diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h index f402f8f..757abb8 100644 --- a/Swift/Controllers/MainController.h +++ b/Swift/Controllers/MainController.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -62,6 +62,10 @@ namespace Swift {  	class Storages;  	class StoragesFactory;  	class NetworkFactories; +	class URIHandler; +	class XMPPURIController; +	class AdHocManager; +	class AdHocCommandWindowFactory;  	class MainController {  		public: @@ -76,6 +80,7 @@ namespace Swift {  					CertificateStorageFactory* certificateStorageFactory,  					Dock* dock,  					Notifier* notifier, +					URIHandler* uriHandler,  					bool useDelayForLatency);  			~MainController(); @@ -123,12 +128,14 @@ namespace Swift {  			SettingsProvider *settings_;  			ProfileSettingsProvider* profileSettings_;  			Dock* dock_; +			URIHandler* uriHandler_;  			TogglableNotifier* notifier_;  			PresenceNotifier* presenceNotifier_;  			EventNotifier* eventNotifier_;  			RosterController* rosterController_;  			EventController* eventController_;  			EventWindowController* eventWindowController_; +			AdHocManager* adHocManager_;  			LoginWindow* loginWindow_;  			UIEventStream* uiEventStream_;  			XMLConsoleController* xmlConsoleController_; @@ -139,6 +146,7 @@ namespace Swift {  			JID boundJID_;  			SystemTrayController* systemTrayController_;  			SoundEventController* soundEventController_; +			XMPPURIController* xmppURIController_;  			std::string vCardPhotoHash_;  			std::string password_;  			std::string certificateFile_; diff --git a/Swift/Controllers/ProfileSettingsProvider.h b/Swift/Controllers/ProfileSettingsProvider.h index 74bcd12..8ba250c 100644 --- a/Swift/Controllers/ProfileSettingsProvider.h +++ b/Swift/Controllers/ProfileSettingsProvider.h @@ -7,6 +7,7 @@  #pragma once  #include "Swift/Controllers/Settings/SettingsProvider.h" +#include <Swiften/Base/foreach.h>  namespace Swift { diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp index 0fe88aa..894f64b 100644 --- a/Swift/Controllers/Roster/ContactRosterItem.cpp +++ b/Swift/Controllers/Roster/ContactRosterItem.cpp @@ -7,6 +7,8 @@  #include "Swift/Controllers/Roster/ContactRosterItem.h"  #include "Swift/Controllers/Roster/GroupRosterItem.h" +#include <Swiften/Base/foreach.h> +  namespace Swift { diff --git a/Swift/Controllers/Roster/GroupRosterItem.cpp b/Swift/Controllers/Roster/GroupRosterItem.cpp index f0a377a..b8fad07 100644 --- a/Swift/Controllers/Roster/GroupRosterItem.cpp +++ b/Swift/Controllers/Roster/GroupRosterItem.cpp @@ -12,7 +12,7 @@  namespace Swift { -GroupRosterItem::GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus) : RosterItem(name, parent), sortByStatus_(sortByStatus) { +GroupRosterItem::GroupRosterItem(const std::string& name, GroupRosterItem* parent, bool sortByStatus) : RosterItem(name, parent), sortByStatus_(sortByStatus), manualSort_(false) {  	expanded_ = true;  } @@ -20,6 +20,19 @@ GroupRosterItem::~GroupRosterItem() {  } +void GroupRosterItem::setManualSort(const std::string& manualSortValue) { +	manualSort_ = true; +	bool changed = manualSortValue_ != manualSortValue; +	manualSortValue_ = manualSortValue; +	if (changed) { +		onDataChanged(); +	} +} + +const std::string& GroupRosterItem::getSortableDisplayName() const { +	return manualSort_ ? manualSortValue_ : RosterItem::getSortableDisplayName(); +} +  bool GroupRosterItem::isExpanded() const {  	return expanded_;  } diff --git a/Swift/Controllers/Roster/GroupRosterItem.h b/Swift/Controllers/Roster/GroupRosterItem.h index 57fa9fa..beb7705 100644 --- a/Swift/Controllers/Roster/GroupRosterItem.h +++ b/Swift/Controllers/Roster/GroupRosterItem.h @@ -31,6 +31,8 @@ class GroupRosterItem : public RosterItem {  		void setExpanded(bool expanded);  		bool isExpanded() const;  		boost::signal<void (bool)> onExpandedChanged; +		void setManualSort(const std::string& manualSortValue); +		virtual const std::string& getSortableDisplayName() const;  	private:  		void handleChildrenChanged(GroupRosterItem* group);  		void handleDataChanged(RosterItem* item); @@ -40,6 +42,8 @@ class GroupRosterItem : public RosterItem {  		std::vector<RosterItem*> children_;  		std::vector<RosterItem*> displayedChildren_;  		bool sortByStatus_; +		bool manualSort_; +		std::string manualSortValue_;  };  } diff --git a/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp b/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp index c1045ee..0a242ae 100644 --- a/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp +++ b/Swift/Controllers/Roster/RosterGroupExpandinessPersister.cpp @@ -9,6 +9,7 @@  #include <boost/bind.hpp>  #include <vector> +#include <Swiften/Base/foreach.h>  #include "Swiften/Base/String.h"  #include "Swift/Controllers/Roster/GroupRosterItem.h" diff --git a/Swift/Controllers/Roster/RosterItem.cpp b/Swift/Controllers/Roster/RosterItem.cpp index 3f130bb..77db8a3 100644 --- a/Swift/Controllers/Roster/RosterItem.cpp +++ b/Swift/Controllers/Roster/RosterItem.cpp @@ -33,11 +33,11 @@ void RosterItem::setDisplayName(const std::string& name) {  	onDataChanged();  } -std::string RosterItem::getDisplayName() const { +const std::string& RosterItem::getDisplayName() const {  	return name_;  } -std::string RosterItem::getSortableDisplayName() const { +const std::string& RosterItem::getSortableDisplayName() const {  	return sortableDisplayName_;  } diff --git a/Swift/Controllers/Roster/RosterItem.h b/Swift/Controllers/Roster/RosterItem.h index ed8cb16..769f72d 100644 --- a/Swift/Controllers/Roster/RosterItem.h +++ b/Swift/Controllers/Roster/RosterItem.h @@ -20,8 +20,8 @@ class RosterItem {  		boost::signal<void ()> onDataChanged;  		GroupRosterItem* getParent() const;  		void setDisplayName(const std::string& name); -		std::string getDisplayName() const; -		std::string getSortableDisplayName() const; +		const std::string& getDisplayName() const; +		virtual const std::string& getSortableDisplayName() const;  	private:  		std::string name_;  		std::string sortableDisplayName_; diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp index 16f2745..ca74dbb 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp @@ -8,6 +8,7 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/Roster/RosterController.h"  #include "Swift/Controllers/UnitTest/MockMainWindowFactory.h"  // #include "Swiften/Elements/Payload.h" diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript index 61da9fb..eed1f4d 100644 --- a/Swift/Controllers/SConscript +++ b/Swift/Controllers/SConscript @@ -44,16 +44,25 @@ if env["SCONS_STAGE"] == "build" :  			"StatusTracker.cpp",  			"PresenceNotifier.cpp",  			"EventNotifier.cpp", +			"AdHocManager.cpp",  			"XMPPEvents/EventController.cpp",  			"UIEvents/UIEvent.cpp",  			"UIInterfaces/XMLConsoleWidget.cpp",  			"UIInterfaces/ChatListWindow.cpp",  			"PreviousStatusStore.cpp", -			"CertificateStorageFactory.cpp", -			"CertificateStorage.cpp", -			"CertificateFileStorage.cpp", +			"Storages/CertificateStorageFactory.cpp", +			"Storages/CertificateStorage.cpp", +			"Storages/CertificateFileStorage.cpp", +			"Storages/CertificateMemoryStorage.cpp", +			"Storages/AvatarFileStorage.cpp", +			"Storages/FileStorages.cpp", +			"Storages/RosterFileStorage.cpp", +			"Storages/CapsFileStorage.cpp", +			"Storages/VCardFileStorage.cpp",  			"StatusUtil.cpp",  			"Translator.cpp", +			"XMPPURIController.cpp", +			"ChatMessageSummarizer.cpp",  		])  	env.Append(UNITTEST_SOURCES = [ @@ -64,4 +73,5 @@ if env["SCONS_STAGE"] == "build" :  			File("Chat/UnitTest/ChatsManagerTest.cpp"),  			File("Chat/UnitTest/MUCControllerTest.cpp"),  			File("UnitTest/MockChatWindow.cpp"), +			File("UnitTest/ChatMessageSummarizerTest.cpp"),  		]) diff --git a/Swiften/Avatars/AvatarFileStorage.cpp b/Swift/Controllers/Storages/AvatarFileStorage.cpp index 4f76c80..288f6fd 100644 --- a/Swiften/Avatars/AvatarFileStorage.cpp +++ b/Swift/Controllers/Storages/AvatarFileStorage.cpp @@ -4,10 +4,11 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <Swiften/Avatars/AvatarFileStorage.h> +#include <Swift/Controllers/Storages/AvatarFileStorage.h>  #include <iostream>  #include <boost/filesystem/fstream.hpp> +#include <boost/filesystem.hpp>  #include <Swiften/Base/foreach.h>  #include <Swiften/Base/String.h> @@ -58,7 +59,7 @@ void AvatarFileStorage::addAvatar(const std::string& hash, const ByteArray& avat  		}  	}  	boost::filesystem::ofstream file(avatarPath, boost::filesystem::ofstream::binary|boost::filesystem::ofstream::out); -	file.write(reinterpret_cast<const char*>(avatar.getData()), avatar.getSize()); +	file.write(reinterpret_cast<const char*>(avatar.getData()), static_cast<std::streamsize>(avatar.getSize()));  	file.close();  } diff --git a/Swiften/Avatars/AvatarFileStorage.h b/Swift/Controllers/Storages/AvatarFileStorage.h index e736230..b7e73f5 100644 --- a/Swiften/Avatars/AvatarFileStorage.h +++ b/Swift/Controllers/Storages/AvatarFileStorage.h @@ -8,7 +8,7 @@  #include <map>  #include <string> -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <Swiften/JID/JID.h>  #include "Swiften/Base/ByteArray.h" diff --git a/Swift/Controllers/Storages/CapsFileStorage.cpp b/Swift/Controllers/Storages/CapsFileStorage.cpp new file mode 100644 index 0000000..b7593fd --- /dev/null +++ b/Swift/Controllers/Storages/CapsFileStorage.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swift/Controllers/Storages/CapsFileStorage.h" + +#include <Swiften/Entity/GenericPayloadPersister.h> +#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h" +#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h" +#include "Swiften/StringCodecs/Hexify.h" +#include "Swiften/StringCodecs/Base64.h" + +using namespace Swift; + +typedef GenericPayloadPersister<DiscoInfo, DiscoInfoParser, DiscoInfoSerializer> DiscoInfoPersister; + +CapsFileStorage::CapsFileStorage(const boost::filesystem::path& path) : path(path) { +} + +DiscoInfo::ref CapsFileStorage::getDiscoInfo(const std::string& hash) const { +	return DiscoInfoPersister().loadPayloadGeneric(getCapsPath(hash)); +} + +void CapsFileStorage::setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { +	DiscoInfo::ref bareDiscoInfo(new DiscoInfo(*discoInfo.get())); +	bareDiscoInfo->setNode(""); +	DiscoInfoPersister().savePayload(bareDiscoInfo, getCapsPath(hash)); +} + +boost::filesystem::path CapsFileStorage::getCapsPath(const std::string& hash) const { +	return path / (Hexify::hexify(Base64::decode(hash)) + ".xml"); +} diff --git a/Swiften/Disco/CapsFileStorage.h b/Swift/Controllers/Storages/CapsFileStorage.h index 5faf08b..b3757e0 100644 --- a/Swiften/Disco/CapsFileStorage.h +++ b/Swift/Controllers/Storages/CapsFileStorage.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include "Swiften/Disco/CapsStorage.h"  #include <string> diff --git a/Swift/Controllers/CertificateFileStorage.cpp b/Swift/Controllers/Storages/CertificateFileStorage.cpp index cf924ee..31af949 100644 --- a/Swift/Controllers/CertificateFileStorage.cpp +++ b/Swift/Controllers/Storages/CertificateFileStorage.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <Swift/Controllers/CertificateFileStorage.h> +#include <Swift/Controllers/Storages/CertificateFileStorage.h>  #include <iostream>  #include <boost/filesystem/fstream.hpp> diff --git a/Swift/Controllers/CertificateFileStorage.h b/Swift/Controllers/Storages/CertificateFileStorage.h index 2b853ed..f7a60b9 100644 --- a/Swift/Controllers/CertificateFileStorage.h +++ b/Swift/Controllers/Storages/CertificateFileStorage.h @@ -8,7 +8,7 @@  #include <boost/filesystem.hpp> -#include "Swift/Controllers/CertificateStorage.h" +#include "Swift/Controllers/Storages/CertificateStorage.h"  namespace Swift {  	class CertificateFactory; diff --git a/Swift/Controllers/CertificateFileStorageFactory.h b/Swift/Controllers/Storages/CertificateFileStorageFactory.h index 7ed8287..b215165 100644 --- a/Swift/Controllers/CertificateFileStorageFactory.h +++ b/Swift/Controllers/Storages/CertificateFileStorageFactory.h @@ -6,8 +6,8 @@  #pragma once -#include <Swift/Controllers/CertificateStorageFactory.h> -#include <Swift/Controllers/CertificateFileStorage.h> +#include <Swift/Controllers/Storages/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateFileStorage.h>  namespace Swift {  	class CertificateFactory; diff --git a/Swift/Controllers/Storages/CertificateMemoryStorage.cpp b/Swift/Controllers/Storages/CertificateMemoryStorage.cpp new file mode 100644 index 0000000..71d7c4a --- /dev/null +++ b/Swift/Controllers/Storages/CertificateMemoryStorage.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/Storages/CertificateMemoryStorage.h> + +#include <Swiften/Base/foreach.h> + +using namespace Swift; + +CertificateMemoryStorage::CertificateMemoryStorage() { +} + +bool CertificateMemoryStorage::hasCertificate(Certificate::ref certificate) const { +	foreach(Certificate::ref storedCert, certificates) { +		if (storedCert->toDER() == certificate->toDER()) { +			return true; +		} +	} +	return false; +} + +void CertificateMemoryStorage::addCertificate(Certificate::ref certificate) { +	certificates.push_back(certificate); +} diff --git a/Swift/Controllers/Storages/CertificateMemoryStorage.h b/Swift/Controllers/Storages/CertificateMemoryStorage.h new file mode 100644 index 0000000..5c0333d --- /dev/null +++ b/Swift/Controllers/Storages/CertificateMemoryStorage.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> + +#include <Swift/Controllers/Storages/CertificateStorage.h> + +namespace Swift { +	class CertificateMemoryStorage : public CertificateStorage { +		public: +			CertificateMemoryStorage(); + +			virtual bool hasCertificate(Certificate::ref certificate) const; +			virtual void addCertificate(Certificate::ref certificate); + +		private: +			std::vector<Certificate::ref> certificates; +	}; + +} diff --git a/Swift/Controllers/CertificateStorage.cpp b/Swift/Controllers/Storages/CertificateStorage.cpp index 343fccd..ee942c0 100644 --- a/Swift/Controllers/CertificateStorage.cpp +++ b/Swift/Controllers/Storages/CertificateStorage.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/Controllers/CertificateStorage.h" +#include "Swift/Controllers/Storages/CertificateStorage.h"  namespace Swift { diff --git a/Swift/Controllers/CertificateStorage.h b/Swift/Controllers/Storages/CertificateStorage.h index f8c6fb5..f8c6fb5 100644 --- a/Swift/Controllers/CertificateStorage.h +++ b/Swift/Controllers/Storages/CertificateStorage.h diff --git a/Swift/Controllers/CertificateStorageFactory.cpp b/Swift/Controllers/Storages/CertificateStorageFactory.cpp index 613a8c3..ba0179a 100644 --- a/Swift/Controllers/CertificateStorageFactory.cpp +++ b/Swift/Controllers/Storages/CertificateStorageFactory.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <Swift/Controllers/CertificateStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateStorageFactory.h>  namespace Swift { diff --git a/Swift/Controllers/CertificateStorageFactory.h b/Swift/Controllers/Storages/CertificateStorageFactory.h index 5b85757..5b85757 100644 --- a/Swift/Controllers/CertificateStorageFactory.h +++ b/Swift/Controllers/Storages/CertificateStorageFactory.h diff --git a/Swift/Controllers/CertificateStorageTrustChecker.h b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h index f33287c..40838dd 100644 --- a/Swift/Controllers/CertificateStorageTrustChecker.h +++ b/Swift/Controllers/Storages/CertificateStorageTrustChecker.h @@ -7,7 +7,7 @@  #pragma once  #include <Swiften/TLS/CertificateTrustChecker.h> -#include <Swift/Controllers/CertificateStorage.h> +#include <Swift/Controllers/Storages/CertificateStorage.h>  namespace Swift {  	/** diff --git a/Swiften/Client/FileStorages.cpp b/Swift/Controllers/Storages/FileStorages.cpp index 3c76c46..6447099 100644 --- a/Swiften/Client/FileStorages.cpp +++ b/Swift/Controllers/Storages/FileStorages.cpp @@ -4,10 +4,11 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/Client/FileStorages.h" -#include "Swiften/VCards/VCardFileStorage.h" -#include "Swiften/Avatars/AvatarFileStorage.h" -#include "Swiften/Disco/CapsFileStorage.h" +#include "Swift/Controllers/Storages/FileStorages.h" +#include "Swift/Controllers/Storages/VCardFileStorage.h" +#include "Swift/Controllers/Storages/AvatarFileStorage.h" +#include "Swift/Controllers/Storages/CapsFileStorage.h" +#include "Swift/Controllers/Storages/RosterFileStorage.h"  namespace Swift { @@ -16,9 +17,11 @@ FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& ji  	vcardStorage = new VCardFileStorage(baseDir / profile / "vcards");  	capsStorage = new CapsFileStorage(baseDir / "caps");  	avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars"); +	rosterStorage = new RosterFileStorage(baseDir / profile / "roster.xml");  }  FileStorages::~FileStorages() { +	delete rosterStorage;  	delete avatarStorage;  	delete capsStorage;  	delete vcardStorage; @@ -36,4 +39,8 @@ AvatarStorage* FileStorages::getAvatarStorage() const {  	return avatarStorage;  } +RosterStorage* FileStorages::getRosterStorage() const { +	return rosterStorage; +} +  } diff --git a/Swiften/Client/FileStorages.h b/Swift/Controllers/Storages/FileStorages.h index 451105f..28df314 100644 --- a/Swiften/Client/FileStorages.h +++ b/Swift/Controllers/Storages/FileStorages.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include "Swiften/Client/Storages.h" @@ -14,6 +14,7 @@ namespace Swift {  	class VCardFileStorage;  	class AvatarFileStorage;  	class CapsFileStorage; +	class RosterFileStorage;  	class JID;  	/** @@ -41,10 +42,12 @@ namespace Swift {  			virtual VCardStorage* getVCardStorage() const;  			virtual AvatarStorage* getAvatarStorage() const;  			virtual CapsStorage* getCapsStorage() const; +			virtual RosterStorage* getRosterStorage() const;  		private:  			VCardFileStorage* vcardStorage;  			AvatarFileStorage* avatarStorage;  			CapsFileStorage* capsStorage; +			RosterFileStorage* rosterStorage;  	};  } diff --git a/Swift/Controllers/FileStoragesFactory.h b/Swift/Controllers/Storages/FileStoragesFactory.h index bd7cdfb..0676bc3 100644 --- a/Swift/Controllers/FileStoragesFactory.h +++ b/Swift/Controllers/Storages/FileStoragesFactory.h @@ -6,8 +6,8 @@  #pragma once -#include "Swift/Controllers/StoragesFactory.h" -#include "Swiften/Client/FileStorages.h" +#include "Swift/Controllers/Storages/StoragesFactory.h" +#include "Swift/Controllers/Storages/FileStorages.h"  namespace Swift {  	class FileStoragesFactory : public StoragesFactory { diff --git a/Swift/Controllers/MemoryStoragesFactory.h b/Swift/Controllers/Storages/MemoryStoragesFactory.h index 8408e10..0dea349 100644 --- a/Swift/Controllers/MemoryStoragesFactory.h +++ b/Swift/Controllers/Storages/MemoryStoragesFactory.h @@ -6,10 +6,12 @@  #pragma once -#include "Swift/Controllers/StoragesFactory.h" +#include "Swift/Controllers/Storages/StoragesFactory.h"  #include "Swiften/Client/MemoryStorages.h"  namespace Swift { +	class JID; +	  	class MemoryStoragesFactory : public StoragesFactory {  		public:  			MemoryStoragesFactory() {} diff --git a/Swift/Controllers/Storages/RosterFileStorage.cpp b/Swift/Controllers/Storages/RosterFileStorage.cpp new file mode 100644 index 0000000..73e582f --- /dev/null +++ b/Swift/Controllers/Storages/RosterFileStorage.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/Storages/RosterFileStorage.h> + +#include <Swiften/Entity/GenericPayloadPersister.h> +#include <Swiften/Serializer/PayloadSerializers/RosterSerializer.h> +#include <Swiften/Parser/PayloadParsers/RosterParser.h> + +using namespace Swift; + +typedef GenericPayloadPersister<RosterPayload, RosterParser, RosterSerializer> RosterPersister; + +RosterFileStorage::RosterFileStorage(const boost::filesystem::path& path) : path(path) { +} + +boost::shared_ptr<RosterPayload> RosterFileStorage::getRoster() const { +	return RosterPersister().loadPayloadGeneric(path); +} + +void RosterFileStorage::setRoster(boost::shared_ptr<RosterPayload> roster) { +	RosterPersister().savePayload(roster, path); +} diff --git a/Swift/Controllers/Storages/RosterFileStorage.h b/Swift/Controllers/Storages/RosterFileStorage.h new file mode 100644 index 0000000..cb00969 --- /dev/null +++ b/Swift/Controllers/Storages/RosterFileStorage.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/filesystem/path.hpp> + +#include <Swiften/Roster/RosterStorage.h> + +namespace Swift { +	class RosterFileStorage : public RosterStorage { +		public: +			RosterFileStorage(const boost::filesystem::path& path); + +			virtual boost::shared_ptr<RosterPayload> getRoster() const; +			virtual void setRoster(boost::shared_ptr<RosterPayload>); + +		private: +			boost::filesystem::path path; +	}; +} diff --git a/Swift/Controllers/StoragesFactory.h b/Swift/Controllers/Storages/StoragesFactory.h index 441a4e9..203f9c9 100644 --- a/Swift/Controllers/StoragesFactory.h +++ b/Swift/Controllers/Storages/StoragesFactory.h @@ -8,6 +8,7 @@  namespace Swift {  	class Storages; +	class JID;  	class StoragesFactory {  		public: diff --git a/Swiften/VCards/VCardFileStorage.cpp b/Swift/Controllers/Storages/VCardFileStorage.cpp index a246838..4933d0c 100644 --- a/Swiften/VCards/VCardFileStorage.cpp +++ b/Swift/Controllers/Storages/VCardFileStorage.cpp @@ -4,22 +4,26 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/VCards/VCardFileStorage.h" +#include "Swift/Controllers/Storages/VCardFileStorage.h"  #include <boost/filesystem/fstream.hpp> +#include <boost/filesystem.hpp> +#include <iostream> +#include <Swiften/Entity/GenericPayloadPersister.h>  #include <Swiften/Base/String.h>  #include <Swiften/StringCodecs/Hexify.h>  #include <Swiften/StringCodecs/SHA1.h>  #include <Swiften/Base/foreach.h>  #include "Swiften/JID/JID.h" -#include "Swiften/Base/ByteArray.h"  #include "Swiften/Elements/VCard.h"  #include "Swiften/Serializer/PayloadSerializers/VCardSerializer.h"  #include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h"  #include "Swiften/Parser/PayloadParsers/VCardParser.h" -namespace Swift { +using namespace Swift; + +typedef GenericPayloadPersister<VCard, VCardParser, VCardSerializer> VCardPersister;  VCardFileStorage::VCardFileStorage(boost::filesystem::path dir) : vcardsPath(dir) {  	cacheFile = vcardsPath / "phashes"; @@ -48,34 +52,11 @@ VCardFileStorage::VCardFileStorage(boost::filesystem::path dir) : vcardsPath(dir  }  boost::shared_ptr<VCard> VCardFileStorage::getVCard(const JID& jid) const { -	boost::filesystem::path vcardPath(getVCardPath(jid)); -	if (boost::filesystem::exists(vcardPath)) { -		ByteArray data; -		data.readFromFile(vcardPath.string()); - -		VCardParser parser; -		PayloadParserTester tester(&parser); -		tester.parse(data.toString()); -		return boost::dynamic_pointer_cast<VCard>(parser.getPayload()); -	} -	else { -		return boost::shared_ptr<VCard>(); -	} +	return VCardPersister().loadPayloadGeneric(getVCardPath(jid));  }  void VCardFileStorage::setVCard(const JID& jid, VCard::ref v) { -	boost::filesystem::path vcardPath(getVCardPath(jid)); -	if (!boost::filesystem::exists(vcardPath.parent_path())) { -		try { -			boost::filesystem::create_directories(vcardPath.parent_path()); -		} -		catch (const boost::filesystem::filesystem_error& e) { -			std::cerr << "ERROR: " << e.what() << std::endl; -		} -	} -	boost::filesystem::ofstream file(getVCardPath(jid)); -	file << VCardSerializer().serializePayload(v); -	file.close(); +	VCardPersister().savePayload(v, getVCardPath(jid));  	getAndUpdatePhotoHash(jid, v);  } @@ -124,6 +105,3 @@ void VCardFileStorage::savePhotoHashes() const {  		std::cerr << "Error writing vcards file" << std::endl;  	}  } - - -} diff --git a/Swiften/VCards/VCardFileStorage.h b/Swift/Controllers/Storages/VCardFileStorage.h index 26bf4b2..ba422f4 100644 --- a/Swiften/VCards/VCardFileStorage.h +++ b/Swift/Controllers/Storages/VCardFileStorage.h @@ -7,7 +7,7 @@  #pragma once  #include <boost/shared_ptr.hpp> -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <string>  #include <map> diff --git a/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h new file mode 100644 index 0000000..c3b4b49 --- /dev/null +++ b/Swift/Controllers/UIEvents/RequestAdHocUIEvent.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/MainWindow.h> + +#include <Swift/Controllers/UIEvents/UIEvent.h> + +namespace Swift { +	class RequestAdHocUIEvent : public UIEvent { +		public: +			RequestAdHocUIEvent(const DiscoItems::Item& command) : command_(command) {}; +			const DiscoItems::Item& getCommand() const {return command_;} +		private: +			DiscoItems::Item command_; +	}; +} diff --git a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h index dd2ff6c..2c7b105 100644 --- a/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h +++ b/Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h @@ -6,18 +6,25 @@  #pragma once -#include <boost/optional.hpp>  #include <boost/shared_ptr.hpp> -  #include <string> +  #include <Swift/Controllers/UIEvents/UIEvent.h> +#include <Swiften/JID/JID.h>  namespace Swift {  	class RequestJoinMUCUIEvent : public UIEvent {  		public:  			typedef boost::shared_ptr<RequestJoinMUCUIEvent> ref; -			RequestJoinMUCUIEvent() { +			RequestJoinMUCUIEvent(const JID& room = JID()) : room(room) {  			} + +			const JID& getRoom() const { +				return room; +			} + +		private: +			JID room;  	};  } diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h new file mode 100644 index 0000000..f7a5d39 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindow.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2010 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { +	class AdHocCommandWindow { +		public: +			virtual ~AdHocCommandWindow() {}; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h new file mode 100644 index 0000000..ae77180 --- /dev/null +++ b/Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +namespace Swift { +	class AdHocCommandWindowFactory { +		public: +			virtual ~AdHocCommandWindowFactory() {} +			/** +			 * The UI should deal with the lifetime of this window (i.e. DeleteOnClose), +			 * so the result isn't returned. +			 */ +			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) = 0; +	}; +} diff --git a/Swift/Controllers/UIInterfaces/ChatListWindow.h b/Swift/Controllers/UIInterfaces/ChatListWindow.h index a2a0874..f717684 100644 --- a/Swift/Controllers/UIInterfaces/ChatListWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatListWindow.h @@ -1,23 +1,35 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once +#include <list>  #include <boost/shared_ptr.hpp> - -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/MUC/MUCBookmark.h>  namespace Swift {  	class ChatListWindow {  		public: +			class Chat { +				public: +					Chat(const JID& jid, const std::string& chatName, const std::string& activity, bool isMUC, const std::string& nick = "") : jid(jid), chatName(chatName), activity(activity), isMUC(isMUC), nick(nick) {} +					/** Assume that nicks aren't important for equality */ +					bool operator==(const Chat& other) const {return jid == other.jid && isMUC == other.isMUC;}; +					JID jid; +					std::string chatName; +					std::string activity; +					bool isMUC; +					std::string nick; +			};  			virtual ~ChatListWindow();  			virtual void setBookmarksEnabled(bool enabled) = 0;  			virtual void addMUCBookmark(const MUCBookmark& bookmark) = 0;  			virtual void removeMUCBookmark(const MUCBookmark& bookmark) = 0; -			virtual void clear() = 0; +			virtual void setRecents(const std::list<Chat>& recents) = 0; +			virtual void clearBookmarks() = 0;  	};  } diff --git a/Swift/Controllers/UIInterfaces/ChatWindow.h b/Swift/Controllers/UIInterfaces/ChatWindow.h index c7bcf1e..aa4416b 100644 --- a/Swift/Controllers/UIInterfaces/ChatWindow.h +++ b/Swift/Controllers/UIInterfaces/ChatWindow.h @@ -40,6 +40,7 @@ namespace Swift {  			virtual void addSystemMessage(const std::string& message) = 0;  			virtual void addPresenceMessage(const std::string& message) = 0;  			virtual void addErrorMessage(const std::string& message) = 0; +			virtual void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) = 0;  			virtual void setContactChatState(ChatState::ChatStateType state) = 0;  			virtual void setName(const std::string& name) = 0; @@ -61,9 +62,11 @@ namespace Swift {  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest; +			boost::signal<void ()> onSendCorrectionMessageRequest;  			boost::signal<void ()> onUserTyping;  			boost::signal<void ()> onUserCancelsTyping; +			boost::signal<void (bool correction)> onSendMessageCorrection;  	};  }  #endif diff --git a/Swift/Controllers/UIInterfaces/MainWindow.h b/Swift/Controllers/UIInterfaces/MainWindow.h index 2fd463b..93584e7 100644 --- a/Swift/Controllers/UIInterfaces/MainWindow.h +++ b/Swift/Controllers/UIInterfaces/MainWindow.h @@ -9,6 +9,7 @@  #include <string>  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/StatusShow.h" +#include "Swiften/Elements/DiscoItems.h"  #include "Swiften/Base/boost_bsignals.h"  #include <boost/shared_ptr.hpp> @@ -33,6 +34,7 @@ namespace Swift {  			/** Must be able to cope with NULL to clear the roster */  			virtual void setRosterModel(Roster* roster) = 0;  			virtual void setConnecting() = 0; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) = 0;  			boost::signal<void (StatusShow::Type, const std::string&)> onChangeStatusRequest;  			boost::signal<void ()> onSignOutRequest; diff --git a/Swift/Controllers/UIInterfaces/UIFactory.h b/Swift/Controllers/UIInterfaces/UIFactory.h index 9b36ac5..57f55d0 100644 --- a/Swift/Controllers/UIInterfaces/UIFactory.h +++ b/Swift/Controllers/UIInterfaces/UIFactory.h @@ -17,6 +17,7 @@  #include <Swift/Controllers/UIInterfaces/XMLConsoleWidgetFactory.h>  #include <Swift/Controllers/UIInterfaces/ProfileWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/ContactEditWindowFactory.h> +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindowFactory.h>  namespace Swift {  	class UIFactory :  @@ -30,7 +31,8 @@ namespace Swift {  			public UserSearchWindowFactory,   			public JoinMUCWindowFactory,  			public ProfileWindowFactory, -			public ContactEditWindowFactory { +			public ContactEditWindowFactory, +			public AdHocCommandWindowFactory {  		public:  			virtual ~UIFactory() {}  	}; diff --git a/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp new file mode 100644 index 0000000..ee0ee9f --- /dev/null +++ b/Swift/Controllers/UnitTest/ChatMessageSummarizerTest.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swift/Controllers/ChatMessageSummarizer.h" + +using namespace Swift; +using namespace std; + +class ChatMessageSummarizerTest : public CppUnit::TestFixture { +	CPPUNIT_TEST_SUITE(ChatMessageSummarizerTest); +	CPPUNIT_TEST(testEmpty); +	CPPUNIT_TEST(testCurrentNone); +	CPPUNIT_TEST(testCurrentCount); +	CPPUNIT_TEST(testCurrentCountOthersNone); +	CPPUNIT_TEST(testCurrentCountOtherCount); +	CPPUNIT_TEST(testCurrentNoneOtherCount); +	CPPUNIT_TEST(testCurrentCountOthersCount); +	CPPUNIT_TEST(testCurrentNoneOthersCount); +	CPPUNIT_TEST(testCurrentCountSomeOthersCount); +	CPPUNIT_TEST_SUITE_END(); + +public: +	ChatMessageSummarizerTest() {}; + +	void setUp() { + +	} + +	void testEmpty() { +		string current(""); +		vector<UnreadPair> unreads; +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); +	} + +	void testCurrentNone() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bob", 0)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(current, summary.getSummary(current, unreads)); +	} + +	void testCurrentCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bob", 3)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOthersNone() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 3)); +		unreads.push_back(UnreadPair("Betty", 0)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOtherCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 3)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (3), Betty (7)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentNoneOtherCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 0)); +		unreads.push_back(UnreadPair("Bob", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob, Betty (7)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentNoneOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Bob", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Bob", 11)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +	void testCurrentCountSomeOthersCount() { +		string current("Bob"); +		vector<UnreadPair> unreads; +		unreads.push_back(UnreadPair("Bert", 2)); +		unreads.push_back(UnreadPair("Beverly", 0)); +		unreads.push_back(UnreadPair("Bob", 11)); +		unreads.push_back(UnreadPair("Beatrice", 0)); +		unreads.push_back(UnreadPair("Betty", 7)); +		ChatMessageSummarizer summary; +		CPPUNIT_ASSERT_EQUAL(string("Bob (11) and 2 others (9)"), summary.getSummary(current, unreads)); +	} + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChatMessageSummarizerTest); diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 53a90a7..82c5b52 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -34,12 +34,13 @@ namespace Swift {  			virtual void setRosterModel(Roster* /*roster*/) {};  			virtual void setTabComplete(TabComplete*) {};  			virtual void replaceLastMessage(const std::string&) {}; +			virtual void replaceMessage(const std::string&, const std::string&, const boost::posix_time::ptime&) {};  			void setAckState(const std::string& /*id*/, AckState /*state*/) {};  			virtual void flash() {};  			boost::signal<void ()> onClosed;  			boost::signal<void ()> onAllMessagesRead; -			boost::signal<void (const std::string&)> onSendMessageRequest; +			boost::signal<void (const std::string&, bool isCorrection)> onSendMessageRequest;  			std::string name_;  			std::string lastMessageBody_; diff --git a/Swift/Controllers/UnitTest/MockMainWindow.h b/Swift/Controllers/UnitTest/MockMainWindow.h index afa5c2a..f773062 100644 --- a/Swift/Controllers/UnitTest/MockMainWindow.h +++ b/Swift/Controllers/UnitTest/MockMainWindow.h @@ -20,6 +20,7 @@ namespace Swift {  			virtual void setMyAvatarPath(const std::string& /*path*/) {};  			virtual void setMyStatusText(const std::string& /*status*/) {};  			virtual void setMyStatusType(StatusShow::Type /*type*/) {}; +			virtual void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& /*commands*/) {};  			virtual void setConnecting() {};  			Roster* roster; diff --git a/Swift/Controllers/XMPPEvents/EventController.cpp b/Swift/Controllers/XMPPEvents/EventController.cpp index 7f8f216..98fd634 100644 --- a/Swift/Controllers/XMPPEvents/EventController.cpp +++ b/Swift/Controllers/XMPPEvents/EventController.cpp @@ -9,6 +9,7 @@  #include <boost/bind.hpp>  #include <algorithm> +#include <Swiften/Base/foreach.h>  #include "Swift/Controllers/XMPPEvents/MessageEvent.h"  #include "Swift/Controllers/XMPPEvents/ErrorEvent.h"  #include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" diff --git a/Swift/Controllers/XMPPURIController.cpp b/Swift/Controllers/XMPPURIController.cpp new file mode 100644 index 0000000..00759b8 --- /dev/null +++ b/Swift/Controllers/XMPPURIController.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/Controllers/XMPPURIController.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <SwifTools/URIHandler/URIHandler.h> +#include <SwifTools/URIHandler/XMPPURI.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> + +using namespace Swift; + +XMPPURIController::XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream) : uriHandler(uriHandler), uiEventStream(uiEventStream) { +	uriHandler->onURI.connect(boost::bind(&XMPPURIController::handleURI, this, _1)); +} + +XMPPURIController::~XMPPURIController() { +	uriHandler->onURI.disconnect(boost::bind(&XMPPURIController::handleURI, this, _1)); +} + +void XMPPURIController::handleURI(const std::string& s) { +	XMPPURI uri = XMPPURI::fromString(s); +	if (!uri.isNull()) { +		if (uri.getQueryType() == "join") { +			uiEventStream->send(boost::make_shared<RequestJoinMUCUIEvent>(uri.getPath())); +		} +		else { +			uiEventStream->send(boost::make_shared<RequestChatUIEvent>(uri.getPath())); +		} +	} +} diff --git a/Swift/Controllers/XMPPURIController.h b/Swift/Controllers/XMPPURIController.h new file mode 100644 index 0000000..54534d4 --- /dev/null +++ b/Swift/Controllers/XMPPURIController.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { +	class URIHandler; +	class JID; +	class UIEventStream; + +	class XMPPURIController { +		public: +			XMPPURIController(URIHandler* uriHandler, UIEventStream* uiEventStream); +			~XMPPURIController(); + +			boost::signal<void (const JID&)> onStartChat; +			boost::signal<void (const JID&)> onJoinMUC; + +		private: +			void handleURI(const std::string&); + +		private: +			URIHandler* uriHandler; +			UIEventStream* uiEventStream; +	}; +} diff --git a/Swift/QtUI/.gitignore b/Swift/QtUI/.gitignore index f539e86..53acb9f 100644 --- a/Swift/QtUI/.gitignore +++ b/Swift/QtUI/.gitignore @@ -1,3 +1,4 @@  Swift  BuildVersion.h  *.dmg +swift-open-uri diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp index 274a10a..b2bfe0a 100644 --- a/Swift/QtUI/ChatList/ChatListDelegate.cpp +++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,6 +11,7 @@  #include "Swift/QtUI/Roster/GroupItemDelegate.h"  #include "Swift/QtUI/ChatList/ChatListItem.h"  #include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include "Swift/QtUI/ChatList/ChatListRecentItem.h"  #include "Swift/QtUI/ChatList/ChatListGroupItem.h"  namespace Swift { @@ -27,7 +28,11 @@ QSize ChatListDelegate::sizeHint(const QStyleOptionViewItem& option, const QMode  	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());  	if (item && dynamic_cast<ChatListMUCItem*>(item)) {  		return mucSizeHint(option, index); -	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) { +	} +	else if (item && dynamic_cast<ChatListRecentItem*>(item)) { +		return recentSizeHint(option, index); +	} +	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {  		return groupDelegate_->sizeHint(option, index);  	}   	return QStyledItemDelegate::sizeHint(option, index); @@ -40,14 +45,23 @@ QSize ChatListDelegate::mucSizeHint(const QStyleOptionViewItem& /*option*/, cons  	return QSize(150, sizeByText);  } +QSize ChatListDelegate::recentSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { +	return mucSizeHint(option, index); +} +  void ChatListDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {  	ChatListItem* item = static_cast<ChatListItem*>(index.internalPointer());  	if (item && dynamic_cast<ChatListMUCItem*>(item)) {  		paintMUC(painter, option, dynamic_cast<ChatListMUCItem*>(item)); -	} else if (item && dynamic_cast<ChatListGroupItem*>(item)) { +	} +	else if (item && dynamic_cast<ChatListRecentItem*>(item)) { +		paintRecent(painter, option, dynamic_cast<ChatListRecentItem*>(item)); +	} +	else if (item && dynamic_cast<ChatListGroupItem*>(item)) {  		ChatListGroupItem* group = dynamic_cast<ChatListGroupItem*>(item);  		groupDelegate_->paint(painter, option, group->data(Qt::DisplayRole).toString(), group->rowCount(), option.state & QStyle::State_Open);  -	} else { +	} +	else {  		QStyledItemDelegate::paint(painter, option, index);  	}  } @@ -78,9 +92,40 @@ void ChatListDelegate::paintMUC(QPainter* painter, const QStyleOptionViewItem& o  	painter->setPen(QPen(QColor(160,160,160)));  	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); -	DelegateCommons::drawElidedText(painter, detailRegion, item->data(DetailTextRole).toString()); +	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListMUCItem::DetailTextRole).toString());  	painter->restore();  } +void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const { +	painter->save(); +	QRect fullRegion(option.rect); +	if ( option.state & QStyle::State_Selected ) { +		painter->fillRect(fullRegion, option.palette.highlight()); +		painter->setPen(option.palette.highlightedText().color()); +	} else { +		QColor nameColor = item->data(Qt::TextColorRole).value<QColor>(); +		painter->setPen(QPen(nameColor)); +	} + +	QFontMetrics nameMetrics(common_.nameFont); +	painter->setFont(common_.nameFont); +	int extraFontWidth = nameMetrics.width("H"); +	int leftOffset = common_.horizontalMargin * 2 + extraFontWidth / 2; +	QRect textRegion(fullRegion.adjusted(leftOffset, 0, 0, 0)); + +	int nameHeight = nameMetrics.height() + common_.verticalMargin; +	QRect nameRegion(textRegion.adjusted(0, common_.verticalMargin, 0, 0)); + +	DelegateCommons::drawElidedText(painter, nameRegion, item->data(Qt::DisplayRole).toString()); + +	painter->setFont(common_.detailFont); +	painter->setPen(QPen(QColor(160,160,160))); + +	QRect detailRegion(textRegion.adjusted(0, nameHeight, 0, 0)); +	DelegateCommons::drawElidedText(painter, detailRegion, item->data(ChatListRecentItem::DetailTextRole).toString()); + +	painter->restore(); +} +  } diff --git a/Swift/QtUI/ChatList/ChatListDelegate.h b/Swift/QtUI/ChatList/ChatListDelegate.h index f6c6c40..a898df4 100644 --- a/Swift/QtUI/ChatList/ChatListDelegate.h +++ b/Swift/QtUI/ChatList/ChatListDelegate.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -12,6 +12,7 @@  namespace Swift {  	class ChatListMUCItem; +	class ChatListRecentItem;  	class ChatListDelegate : public QStyledItemDelegate {  		public:  			ChatListDelegate(); @@ -20,7 +21,9 @@ namespace Swift {  			void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;  		private:  			void paintMUC(QPainter* painter, const QStyleOptionViewItem& option, ChatListMUCItem* item) const; +			void paintRecent(QPainter* painter, const QStyleOptionViewItem& option, ChatListRecentItem* item) const;  			QSize mucSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const; +			QSize recentSizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const;  			DelegateCommons common_;  			GroupItemDelegate* groupDelegate_; diff --git a/Swift/QtUI/ChatList/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h index cc4d4af..a1e479f 100644 --- a/Swift/QtUI/ChatList/ChatListGroupItem.h +++ b/Swift/QtUI/ChatList/ChatListGroupItem.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -13,8 +13,8 @@  namespace Swift {  	class ChatListGroupItem : public ChatListItem {  		public: -			ChatListGroupItem(const QString& name, ChatListGroupItem* parent) : ChatListItem(parent), name_(name) {}; -			void addItem(ChatListItem* item) {items_.push_back(item); qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}; +			ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {}; +			void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}};  			void remove(int index) {items_.removeAt(index);};  			int rowCount() {return items_.size();};  			ChatListItem* item(int i) {return items_[i];}; @@ -30,5 +30,6 @@ namespace Swift {  			QString name_;  			QList<ChatListItem*> items_; +			bool sorted_;  	};  } diff --git a/Swift/QtUI/ChatList/ChatListMUCItem.h b/Swift/QtUI/ChatList/ChatListMUCItem.h index 068f5d6..f26aa67 100644 --- a/Swift/QtUI/ChatList/ChatListMUCItem.h +++ b/Swift/QtUI/ChatList/ChatListMUCItem.h @@ -15,14 +15,14 @@  #include "Swift/QtUI/ChatList/ChatListItem.h"  namespace Swift { -	enum MUCItemRoles { -		DetailTextRole = Qt::UserRole/*, -		AvatarRole = Qt::UserRole + 1, -		PresenceIconRole = Qt::UserRole + 2, -		StatusShowTypeRole = Qt::UserRole + 3*/ -	};  	class ChatListMUCItem : public ChatListItem {  		public: +			enum MUCItemRoles { +				DetailTextRole = Qt::UserRole/*, +				AvatarRole = Qt::UserRole + 1, +				PresenceIconRole = Qt::UserRole + 2, +				StatusShowTypeRole = Qt::UserRole + 3*/ +			};  			ChatListMUCItem(const MUCBookmark& bookmark, ChatListGroupItem* parent);  			const MUCBookmark& getBookmark();  			QVariant data(int role) const; diff --git a/Swift/QtUI/ChatList/ChatListModel.cpp b/Swift/QtUI/ChatList/ChatListModel.cpp index ba7b766..681c1c2 100644 --- a/Swift/QtUI/ChatList/ChatListModel.cpp +++ b/Swift/QtUI/ChatList/ChatListModel.cpp @@ -1,22 +1,25 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swift/QtUI/ChatList/ChatListModel.h" +#include <Swift/QtUI/ChatList/ChatListModel.h> -#include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include <Swift/QtUI/ChatList/ChatListMUCItem.h> +#include <Swift/QtUI/ChatList/ChatListRecentItem.h>  namespace Swift {  ChatListModel::ChatListModel() { -	root_ = new ChatListGroupItem("", NULL); +	root_ = new ChatListGroupItem("", NULL, false);  	mucBookmarks_ = new ChatListGroupItem(tr("Bookmarked Rooms"), root_); +	recents_ = new ChatListGroupItem(tr("Recent Chats"), root_, false); +	root_->addItem(recents_);  	root_->addItem(mucBookmarks_);  } -void ChatListModel::clear() { +void ChatListModel::clearBookmarks() {  	emit layoutAboutToBeChanged();  	mucBookmarks_->clear();  	emit layoutChanged(); @@ -43,6 +46,15 @@ void ChatListModel::removeMUCBookmark(const Swift::MUCBookmark& bookmark) {  	}  } +void ChatListModel::setRecents(const std::list<ChatListWindow::Chat>& recents) { +	emit layoutAboutToBeChanged(); +	recents_->clear(); +	foreach (const ChatListWindow::Chat chat, recents) { +		recents_->addItem(new ChatListRecentItem(chat, recents_)); +	} +	emit layoutChanged(); +} +  int ChatListModel::columnCount(const QModelIndex& /*parent*/) const {  	return 1;  } diff --git a/Swift/QtUI/ChatList/ChatListModel.h b/Swift/QtUI/ChatList/ChatListModel.h index adde148..8e7828c 100644 --- a/Swift/QtUI/ChatList/ChatListModel.h +++ b/Swift/QtUI/ChatList/ChatListModel.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -11,9 +11,10 @@  #include <QAbstractItemModel>  #include <QList> -#include "Swiften/MUC/MUCBookmark.h" +#include <Swiften/MUC/MUCBookmark.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> -#include "Swift/QtUI/ChatList/ChatListGroupItem.h" +#include <Swift/QtUI/ChatList/ChatListGroupItem.h>  namespace Swift {  	class ChatListModel : public QAbstractItemModel { @@ -28,9 +29,11 @@ namespace Swift {  			QModelIndex parent(const QModelIndex& index) const;  			int rowCount(const QModelIndex& parent = QModelIndex()) const;  			ChatListItem* getItemForIndex(const QModelIndex& index) const; -			void clear(); +			void clearBookmarks(); +			void setRecents(const std::list<ChatListWindow::Chat>& recents);  		private:  			ChatListGroupItem* mucBookmarks_; +			ChatListGroupItem* recents_;  			ChatListGroupItem* root_;  	}; diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp new file mode 100644 index 0000000..8b6707c --- /dev/null +++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/ChatList/ChatListRecentItem.h> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { +ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { + +} + +const ChatListWindow::Chat& ChatListRecentItem::getChat() { +	return chat_; +} + +QVariant ChatListRecentItem::data(int role) const { +	switch (role) { +		case Qt::DisplayRole: return P2QSTRING(chat_.chatName); +		case DetailTextRole: return P2QSTRING(chat_.activity); +			/*case Qt::TextColorRole: return textColor_; +		case Qt::BackgroundColorRole: return backgroundColor_; +		case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); +		case StatusTextRole: return statusText_; +		case AvatarRole: return avatar_; +		case PresenceIconRole: return getPresenceIcon();*/ +		default: return QVariant(); +	} +} + +} diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.h b/Swift/QtUI/ChatList/ChatListRecentItem.h new file mode 100644 index 0000000..c2646cc --- /dev/null +++ b/Swift/QtUI/ChatList/ChatListRecentItem.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QList> + +#include <boost/shared_ptr.hpp> + +#include <Swiften/MUC/MUCBookmark.h> +#include <Swift/Controllers/UIInterfaces/ChatListWindow.h> + +#include <Swift/QtUI/ChatList/ChatListItem.h> + +namespace Swift { +	class ChatListRecentItem : public ChatListItem { +		public: +			enum RecentItemRoles { +				DetailTextRole = Qt::UserRole/*, +				AvatarRole = Qt::UserRole + 1, +				PresenceIconRole = Qt::UserRole + 2, +				StatusShowTypeRole = Qt::UserRole + 3*/ +			}; +			ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent); +			const ChatListWindow::Chat& getChat(); +			QVariant data(int role) const; +		private: +			ChatListWindow::Chat chat_; +	}; +} diff --git a/Swift/QtUI/ChatList/QtChatListWindow.cpp b/Swift/QtUI/ChatList/QtChatListWindow.cpp index b532cdb..d71563d 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.cpp +++ b/Swift/QtUI/ChatList/QtChatListWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -10,9 +10,11 @@  #include <QContextMenuEvent>  #include "Swift/QtUI/ChatList/ChatListMUCItem.h" +#include "Swift/QtUI/ChatList/ChatListRecentItem.h"  #include "Swift/QtUI/QtAddBookmarkWindow.h"  #include "Swift/QtUI/QtEditBookmarkWindow.h"  #include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" +#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"  #include "Swift/Controllers/UIEvents/AddMUCBookmarkUIEvent.h"  #include "Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h"  #include "Swift/Controllers/UIEvents/EditMUCBookmarkUIEvent.h" @@ -68,19 +70,30 @@ void QtChatListWindow::setupContextMenus() {  }  void QtChatListWindow::handleItemActivated(const QModelIndex& index) { -	if (!bookmarksEnabled_) { -		return; -	}  	ChatListItem* item = model_->getItemForIndex(index);  	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(item); -	if (mucItem) { +	if (bookmarksEnabled_ && mucItem) {  		boost::shared_ptr<UIEvent> event(new JoinMUCUIEvent(mucItem->getBookmark().getRoom(), mucItem->getBookmark().getNick()));  		eventStream_->send(event);  	} +	ChatListRecentItem* recentItem = dynamic_cast<ChatListRecentItem*>(item); +	if (recentItem) { +		boost::shared_ptr<UIEvent> event; +		if (recentItem->getChat().isMUC) { +			if (!bookmarksEnabled_) { +				return; +			} +			return; +		} +		else { +			event = boost::shared_ptr<UIEvent>(new RequestChatUIEvent(recentItem->getChat().jid)); +		} +		eventStream_->send(event); +	}  } -void QtChatListWindow::clear() { -	model_->clear(); +void QtChatListWindow::clearBookmarks() { +	model_->clearBookmarks();  }  void QtChatListWindow::addMUCBookmark(const MUCBookmark& bookmark) { @@ -91,6 +104,10 @@ void QtChatListWindow::removeMUCBookmark(const MUCBookmark& bookmark) {  	model_->removeMUCBookmark(bookmark);  } +void QtChatListWindow::setRecents(const std::list<ChatListWindow::Chat>& recents) { +	model_->setRecents(recents); +} +  void QtChatListWindow::handleRemoveBookmark() {  	ChatListMUCItem* mucItem = dynamic_cast<ChatListMUCItem*>(contextMenuItem_);  	if (!mucItem) return; diff --git a/Swift/QtUI/ChatList/QtChatListWindow.h b/Swift/QtUI/ChatList/QtChatListWindow.h index 3a3e95f..f5c12f6 100644 --- a/Swift/QtUI/ChatList/QtChatListWindow.h +++ b/Swift/QtUI/ChatList/QtChatListWindow.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -23,7 +23,8 @@ namespace Swift {  			void addMUCBookmark(const MUCBookmark& bookmark);  			void removeMUCBookmark(const MUCBookmark& bookmark);  			void setBookmarksEnabled(bool enabled); -			void clear(); +			void setRecents(const std::list<ChatListWindow::Chat>& recents); +			void clearBookmarks();  		private slots:  			void handleItemActivated(const QModelIndex&);  			void handleAddBookmark(); diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h index 3aa5fcc..7cbb3b3 100644 --- a/Swift/QtUI/ChatSnippet.h +++ b/Swift/QtUI/ChatSnippet.h @@ -17,7 +17,7 @@ namespace Swift {  		public:  			ChatSnippet(bool appendToPrevious);  			virtual ~ChatSnippet(); -			 +  			virtual const QString& getContent() const = 0;  			virtual QString getContinuationElementID() const { return ""; } @@ -26,7 +26,7 @@ namespace Swift {  			bool getAppendToPrevious() const {  				return appendToPrevious_;  			} -			 +  			static QString escape(const QString& original) {  				QString result(original);  				result.replace("%message%", "%message%"); @@ -37,11 +37,12 @@ namespace Swift {  				return result;  			} +			static QString timeToEscapedString(const QDateTime& time); +  		protected:  			void setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet> continuationFallback) {  				continuationFallback_ = continuationFallback;  			} -			static QString timeToEscapedString(const QDateTime& time);  		private:  			bool appendToPrevious_;  			boost::shared_ptr<ChatSnippet> continuationFallback_; diff --git a/Swift/QtUI/QtAdHocCommandWindow.cpp b/Swift/QtUI/QtAdHocCommandWindow.cpp new file mode 100644 index 0000000..a3bb077 --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtAdHocCommandWindow.h> + +#include <boost/bind.hpp> +#include <QBoxLayout> +#include <Swift/QtUI/QtFormWidget.h> +#include <Swiften/Elements/Command.h> + +namespace Swift { +QtAdHocCommandWindow::QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) : command_(command) { + +	formWidget_ = NULL; + +	setAttribute(Qt::WA_DeleteOnClose); +	command->onNextStageReceived.connect(boost::bind(&QtAdHocCommandWindow::handleNextStageReceived, this, _1)); +	command->onError.connect(boost::bind(&QtAdHocCommandWindow::handleError, this, _1)); +	command->start(); + +	QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); +	layout->setContentsMargins(0,0,0,0); +	layout->setSpacing(2); +	label_ = new QLabel(this); +	layout->addWidget(label_); +	QWidget* formContainer = new QWidget(this); +	layout->addWidget(formContainer); +	formLayout_ = new QBoxLayout(QBoxLayout::TopToBottom, formContainer); +	QWidget* buttonsWidget = new QWidget(this); +	layout->addWidget(buttonsWidget); + +	QBoxLayout* buttonsLayout = new QBoxLayout(QBoxLayout::LeftToRight, buttonsWidget); +	cancelButton_ = new QPushButton(tr("Cancel"), buttonsWidget); +	buttonsLayout->addWidget(cancelButton_); +	connect(cancelButton_, SIGNAL(clicked()), this, SLOT(handleCancelClicked())); +	backButton_ = new QPushButton(tr("Back"), buttonsWidget); +	buttonsLayout->addWidget(backButton_); +	connect(backButton_, SIGNAL(clicked()), this, SLOT(handlePrevClicked())); +	nextButton_ = new QPushButton(tr("Next"), buttonsWidget); +	buttonsLayout->addWidget(nextButton_); +	connect(nextButton_, SIGNAL(clicked()), this, SLOT(handleNextClicked())); +	completeButton_ = new QPushButton(tr("Complete"), buttonsWidget); +	buttonsLayout->addWidget(completeButton_); +	connect(completeButton_, SIGNAL(clicked()), this, SLOT(handleCompleteClicked())); +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	actions_[Command::Next] = nextButton_; +	actions_[Command::Prev] = backButton_; +	actions_[Command::Complete] = completeButton_; +	actions_[Command::Cancel] = cancelButton_; +	show(); +} + +QtAdHocCommandWindow::~QtAdHocCommandWindow() { + +} + +void QtAdHocCommandWindow::handleCancelClicked() { +	command_->cancel(); +} + +void QtAdHocCommandWindow::handlePrevClicked() { +	command_->goBack(); +} + +void QtAdHocCommandWindow::handleNextClicked() { +	command_->goNext(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleCompleteClicked() { +	command_->complete(formWidget_ ? formWidget_->getCompletedForm() : Form::ref()); +} + +void QtAdHocCommandWindow::handleNextStageReceived(Command::ref command) { +	if (command->getForm()) { +		setForm(command->getForm()); +	} else { +		setNoForm(); +	} +	QString notes; +	foreach (Command::Note note, command->getNotes()) { +		if (!notes.isEmpty()) { +			notes += "\n"; +			QString qNote(note.note.c_str()); +			switch (note.type) { +				case Command::Note::Error: notes += tr("Error: %1").arg(qNote); break; +				case Command::Note::Warn: notes += tr("Warning: %1").arg(qNote); break; +				case Command::Note::Info: notes += qNote; break; +			} +		} +	} +	label_->setText(notes); +	setAvailableActions(command); +} + +void QtAdHocCommandWindow::handleError(ErrorPayload::ref /*error*/) { +	nextButton_->setEnabled(false); +	backButton_->setEnabled(false); +	completeButton_->setEnabled(false); +	label_->setText(tr("Error executing command")); +} + +void QtAdHocCommandWindow::setForm(Form::ref form) { +	delete formWidget_; +	formWidget_ = new QtFormWidget(form, this); +	formLayout_->addWidget(formWidget_); +} + +void QtAdHocCommandWindow::setNoForm() { +	delete formWidget_; +} + +typedef std::pair<Command::Action, QPushButton*> ActionButton; + +void QtAdHocCommandWindow::setAvailableActions(Command::ref /*commandResult*/) { +	foreach (ActionButton pair, actions_) { +		OutgoingAdHocCommandSession::ActionState state = command_->getActionState(pair.first); +		if (state & OutgoingAdHocCommandSession::Present) { +			pair.second->show(); +		} +		else { +			pair.second->hide(); +		} +		if (state & OutgoingAdHocCommandSession::Enabled) { +			pair.second->setEnabled(true); +		} +		else { +			pair.second->setEnabled(false); +		} +	} +} + +} diff --git a/Swift/QtUI/QtAdHocCommandWindow.h b/Swift/QtUI/QtAdHocCommandWindow.h new file mode 100644 index 0000000..adeb3e6 --- /dev/null +++ b/Swift/QtUI/QtAdHocCommandWindow.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <QPushButton> +#include <QLabel> + +#include <Swift/Controllers/UIInterfaces/AdHocCommandWindow.h> +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +class QBoxLayout; + +namespace Swift { +	class QtFormWidget; +	class QtAdHocCommandWindow : public QWidget, public AdHocCommandWindow { +		Q_OBJECT +		public: +			QtAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command); +			virtual ~QtAdHocCommandWindow(); +		private: +			void handleNextStageReceived(Command::ref command); +			void handleError(ErrorPayload::ref error); +			void setForm(Form::ref); +			void setNoForm(); +			void setAvailableActions(Command::ref commandResult); +		private slots: +			void handleCancelClicked(); +			void handlePrevClicked(); +			void handleNextClicked(); +			void handleCompleteClicked(); +		private: +			boost::shared_ptr<OutgoingAdHocCommandSession> command_; +			QtFormWidget* formWidget_; +			QBoxLayout* formLayout_; +			Form::ref form_; +			QLabel* label_; +			QPushButton* backButton_; +			QPushButton* nextButton_; +			QPushButton* completeButton_; +			QPushButton* cancelButton_; +			std::map<Command::Action, QPushButton*> actions_; +	}; +} diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index 25c7ca2..249080b 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -7,6 +7,10 @@  #include "QtChatTabs.h"  #include <algorithm> +#include <vector> + +#include <Swift/Controllers/ChatMessageSummarizer.h> +#include <Swift/QtUI/QtSwiftUtil.h>  #include <QCloseEvent>  #include <QDesktopWidget> @@ -236,16 +240,18 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  	default : tabTextColor = QColor();  	}  	tabs_->tabBar()->setTabTextColor(index, tabTextColor); -	int unread = 0; + +	std::vector<std::pair<std::string, int> > unreads;  	for (int i = 0; i < tabs_->count(); i++) {  		QtTabbable* tab = qobject_cast<QtTabbable*>(tabs_->widget(i));  		if (tab) { -			unread += tab->getCount(); +			unreads.push_back(std::pair<std::string, int>(Q2PSTRING(tab->windowTitle()), tab->getCount()));  		}  	} -	QtTabbable* current = qobject_cast<QtTabbable*>(tabs_->currentWidget()); -	setWindowTitle(unread > 0 ? QString("(%1) %2").arg(unread).arg(current->windowTitle()) : current->windowTitle()); +	std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(tabs_->currentWidget())->windowTitle())); +	ChatMessageSummarizer summary; +	setWindowTitle(summary.getSummary(current, unreads).c_str());  }  void QtChatTabs::flash() { diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index 521b072..eab6d91 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -106,6 +106,19 @@ void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) {  	//qApp->processEvents();  } +void QtChatView::addLastSeenLine() { +	if (lineSeparator_.isNull()) { +		lineSeparator_ = newInsertPoint_.clone(); +		lineSeparator_.setInnerXml(QString("<hr/>")); +		newInsertPoint_.prependOutside(lineSeparator_); +	} +	else { +		QWebElement lineSeparatorC = lineSeparator_.clone(); +		lineSeparatorC.removeFromDocument(); +	} +	newInsertPoint_.prependOutside(lineSeparator_); +} +  void QtChatView::replaceLastMessage(const QString& newMessage) {  	assert(viewReady_);  	/* FIXME: must be queued? */ @@ -125,6 +138,25 @@ void QtChatView::replaceLastMessage(const QString& newMessage, const QString& no  	replace.setInnerXml(ChatSnippet::escape(note));  } +QString QtChatView::getLastSentMessage() { +	return lastElement_.toPlainText(); +} + +void QtChatView::replaceMessage(const QString& newMessage, const QString& id, const QDateTime& editTime) { +	rememberScrolledToBottom(); +	QWebElement message = document_.findFirst("#" + id); +	if (!message.isNull()) { +		QWebElement replaceContent = message.findFirst("span.swift_message"); +		assert(!replaceContent.isNull()); +		QString old = replaceContent.toOuterXml(); +		replaceContent.setInnerXml(ChatSnippet::escape(newMessage)); +		QWebElement replaceTime = message.findFirst("span.swift_time"); +		assert(!replaceTime.isNull()); +		old = replaceTime.toOuterXml(); +		replaceTime.setInnerXml(ChatSnippet::escape(tr("%1 edited").arg(ChatSnippet::timeToEscapedString(editTime)))); +	} +} +  void QtChatView::copySelectionToClipboard() {  	if (!webPage_->selectedText().isEmpty()) {  		webPage_->triggerAction(QWebPage::Copy); diff --git a/Swift/QtUI/QtChatView.h b/Swift/QtUI/QtChatView.h index 58b33df..32741f4 100644 --- a/Swift/QtUI/QtChatView.h +++ b/Swift/QtUI/QtChatView.h @@ -26,12 +26,14 @@ namespace Swift {  			Q_OBJECT  		public:  			QtChatView(QtChatTheme* theme, QWidget* parent); -  			void addMessage(boost::shared_ptr<ChatSnippet> snippet); +			void addLastSeenLine();  			void replaceLastMessage(const QString& newMessage);  			void replaceLastMessage(const QString& newMessage, const QString& note); +			void replaceMessage(const QString& newMessage, const QString& id, const QDateTime& time);  			void rememberScrolledToBottom();  			void setAckXML(const QString& id, const QString& xml); +			QString getLastSentMessage();  		signals:  			void gotFocus(); @@ -63,6 +65,7 @@ namespace Swift {  			QtChatTheme* theme_;  			QWebElement newInsertPoint_; +			QWebElement lineSeparator_;  			QWebElement lastElement_;  			QWebElement document_;  	}; diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 1a909fd..ab230c4 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -35,12 +35,13 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	inputEnabled_ = true;  	completer_ = NULL;  	theme_ = theme; +	isCorrection_ = false;  	updateTitleWithUnreadCount();  	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, this);  	layout->setContentsMargins(0,0,0,0);  	layout->setSpacing(2); -	 +  	QSplitter *logRosterSplitter = new QSplitter(this);  	logRosterSplitter->setAutoFillBackground(true); @@ -72,7 +73,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	input_ = new QtTextEdit(this);  	input_->setAcceptRichText(false);  	layout->addWidget(input_); -	 +  	inputClearing_ = false;  	contactIsTyping_ = false; @@ -99,6 +100,7 @@ void QtChatWindow::setTabComplete(TabComplete* completer) {  void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  	int key = event->key();  	Qt::KeyboardModifiers modifiers = event->modifiers(); +	QTextCursor cursor;  	if (key == Qt::Key_W && modifiers == Qt::ControlModifier) {  		close();  	} else if ( @@ -118,6 +120,17 @@ void QtChatWindow::handleKeyPressEvent(QKeyEvent* event) {  		emit requestActiveTab();  	} else if (key == Qt::Key_Tab) {  		tabComplete(); +	} else if ((key == Qt::Key_Up) && input_->toPlainText().isEmpty() && !(lastSentMessage_.isEmpty())) { +		cursor = input_->textCursor(); +		cursor.select(QTextCursor::Document); +		cursor.beginEditBlock(); +		cursor.insertText(QString(lastSentMessage_)); +		cursor.endEditBlock(); +		isCorrection_ = true; +	} else if (key == Qt::Key_Down && isCorrection_ && (cursor = input_->textCursor()).atBlockEnd()) { +		cursor.select(QTextCursor::Document); +		cursor.removeSelectedText(); +		isCorrection_ = false;  	} else {  		messageLog_->handleKeyPressEvent(event);  	} @@ -150,7 +163,7 @@ void QtChatWindow::tabComplete() {  }  void QtChatWindow::setRosterModel(Roster* roster) { -	treeWidget_->setRosterModel(roster);	 +	treeWidget_->setRosterModel(roster);  }  void QtChatWindow::setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) { @@ -204,10 +217,13 @@ void QtChatWindow::qAppFocusChanged(QWidget *old, QWidget *now) {  	Q_UNUSED(old);  	Q_UNUSED(now);  	if (isWidgetSelected()) { +		lastLineTracker_.setHasFocus(true);  		input_->setFocus();  		onAllMessagesRead();  	} -	 +	else { +		lastLineTracker_.setHasFocus(false); +	}  }  void QtChatWindow::setInputEnabled(bool enabled) { @@ -236,7 +252,7 @@ void QtChatWindow::setContactChatState(ChatState::ChatStateType state) {  QtTabbable::AlertType QtChatWindow::getWidgetAlertState() {  	if (contactIsTyping_) {  		return ImpendingActivity; -	}  +	}  	if (unreadCount_ > 0) {  		return WaitingActivity;  	} @@ -265,7 +281,6 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri  	if (isWidgetSelected()) {  		onAllMessagesRead();  	} -  	QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str());  	QString htmlString; @@ -281,6 +296,12 @@ std::string QtChatWindow::addMessage(const std::string &message, const std::stri  	htmlString += styleSpanStart + messageHTML + styleSpanEnd;  	bool appendToPrevious = !previousMessageWasSystem_ && !previousMessageWasPresence_ && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_ && previousSenderName_ == P2QSTRING(senderName))); +	if (lastLineTracker_.getShouldMoveLastLine()) { +		/* should this be queued? */ +		messageLog_->addLastSeenLine(); +		/* if the line is added we should break the snippet */ +		appendToPrevious = false; +	}  	QString qAvatarPath =  scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded();  	std::string id = id_.generateID();  	messageLog_->addMessage(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); @@ -343,6 +364,15 @@ void QtChatWindow::addSystemMessage(const std::string& message) {  	previousMessageWasPresence_ = false;  } +void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { +	if (!id.empty()) { +		QString messageHTML(Qt::escape(P2QSTRING(message))); +		messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); +		messageHTML.replace("\n","<br/>"); +		messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); +	} +} +  void QtChatWindow::addPresenceMessage(const std::string& message) {  	if (isWidgetSelected()) {  		onAllMessagesRead(); @@ -364,7 +394,9 @@ void QtChatWindow::returnPressed() {  		return;  	}  	messageLog_->scrollToBottom(); -	onSendMessageRequest(Q2PSTRING(input_->toPlainText())); +	lastSentMessage_ = QString(input_->toPlainText()); +	onSendMessageRequest(Q2PSTRING(input_->toPlainText()), isCorrection_); +	isCorrection_ = false;  	inputClearing_ = true;  	input_->clear();  	inputClearing_ = false; @@ -399,7 +431,7 @@ void QtChatWindow::resizeEvent(QResizeEvent*) {  }  void QtChatWindow::moveEvent(QMoveEvent*) { -	emit geometryChanged();	 +	emit geometryChanged();  }  void QtChatWindow::replaceLastMessage(const std::string& message) { diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 910019b..fd6b315 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -4,13 +4,14 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFT_QtChatWindow_H -#define SWIFT_QtChatWindow_H +#pragma once  #include "Swift/Controllers/UIInterfaces/ChatWindow.h"  #include "QtTabbable.h" +#include "SwifTools/LastLineTracker.h" +  #include "Swiften/Base/IDGenerator.h"  class QTextEdit; @@ -35,6 +36,7 @@ namespace Swift {  			void addSystemMessage(const std::string& message);  			void addPresenceMessage(const std::string& message);  			void addErrorMessage(const std::string& errorMessage); +			void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time);  			void show();  			void activate();  			void setUnreadMessageCount(int count); @@ -79,7 +81,9 @@ namespace Swift {  			int unreadCount_;  			bool contactIsTyping_; +			LastLineTracker lastLineTracker_;  			QString contact_; +			QString lastSentMessage_;  			QtChatView* messageLog_;  			QtChatTheme* theme_;  			QtTextEdit* input_; @@ -87,6 +91,7 @@ namespace Swift {  			QtTreeWidget* treeWidget_;  			TabComplete* completer_;  			std::vector<SecurityLabelsCatalog::Item> availableLabels_; +			bool isCorrection_;  			bool previousMessageWasSelf_;  			bool previousMessageWasSystem_;  			bool previousMessageWasPresence_; @@ -97,5 +102,3 @@ namespace Swift {  			IDGenerator id_;  	};  } - -#endif diff --git a/Swift/QtUI/QtDBUSURIHandler.cpp b/Swift/QtUI/QtDBUSURIHandler.cpp new file mode 100644 index 0000000..9b69ca6 --- /dev/null +++ b/Swift/QtUI/QtDBUSURIHandler.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtDBUSURIHandler.h" + +#include <QDBusAbstractAdaptor> +#include <QDBusConnection> + +#include "QtSwiftUtil.h" + +using namespace Swift; + +namespace { +	class DBUSAdaptor: public QDBusAbstractAdaptor { +			Q_OBJECT +			Q_CLASSINFO("D-Bus Interface", "im.swift.Swift.URIHandler"); +		public: +			DBUSAdaptor(QtDBUSURIHandler* uriHandler) : QDBusAbstractAdaptor(uriHandler), uriHandler(uriHandler) { +			} + +		public slots: +			void openURI(const QString& uri) { +				uriHandler->onURI(Q2PSTRING(uri)); +			} + +		private: +			QtDBUSURIHandler* uriHandler; +	}; +} + +QtDBUSURIHandler::QtDBUSURIHandler() { +	new DBUSAdaptor(this); +	QDBusConnection connection = QDBusConnection::sessionBus(); +	connection.registerService("im.swift.Swift.URIHandler"); +	connection.registerObject("/", this); +} + +#include "QtDBUSURIHandler.moc" diff --git a/Swift/QtUI/QtDBUSURIHandler.h b/Swift/QtUI/QtDBUSURIHandler.h new file mode 100644 index 0000000..be1872e --- /dev/null +++ b/Swift/QtUI/QtDBUSURIHandler.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QObject> +#include <SwifTools/URIHandler/URIHandler.h> + +namespace Swift { +	class QtDBUSURIHandler : public QObject, public URIHandler { +		public: +			QtDBUSURIHandler(); +	}; +} diff --git a/Swift/QtUI/QtFormWidget.cpp b/Swift/QtUI/QtFormWidget.cpp new file mode 100644 index 0000000..050ff27 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtFormWidget.h> + +#include <QGridLayout> +#include <QLabel> +#include <QListWidget> +#include <QLineEdit> +#include <QTextEdit> +#include <QCheckBox> +#include <QScrollArea> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/Base/foreach.h> + +namespace Swift { + +QtFormWidget::QtFormWidget(Form::ref form, QWidget* parent) : QWidget(parent), form_(form) { +	QGridLayout* thisLayout = new QGridLayout(this); +	int row = 0; +	if (!form->getTitle().empty()) { +		QLabel* instructions = new QLabel(("<b>" + form->getTitle() + "</b>").c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	if (!form->getInstructions().empty()) { +		QLabel* instructions = new QLabel(form->getInstructions().c_str(), this); +		thisLayout->addWidget(instructions, row++, 0, 1, 2); +	} +	QScrollArea* scrollArea = new QScrollArea(this); +	thisLayout->addWidget(scrollArea); +	QWidget* scroll = new QWidget(this); +	QGridLayout* layout = new QGridLayout(scroll); +	foreach (boost::shared_ptr<FormField> field, form->getFields()) { +		QWidget* widget = createWidget(field); +		if (widget) { +			layout->addWidget(new QLabel(field->getLabel().c_str(), this), row, 0); +			layout->addWidget(widget, row++, 1); +		} +	} +	scrollArea->setWidget(scroll); +	scrollArea->setWidgetResizable(true); +} + +QtFormWidget::~QtFormWidget() { + +} + +QListWidget* QtFormWidget::createList(FormField::ref field) { +	QListWidget* listWidget = new QListWidget(this); +	listWidget->setSortingEnabled(false); +	listWidget->setSelectionMode(boost::dynamic_pointer_cast<ListMultiFormField>(field) ? QAbstractItemView::MultiSelection : QAbstractItemView::SingleSelection); +	foreach (FormField::Option option, field->getOptions()) { +		listWidget->addItem(option.label.c_str()); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	for (int i = 0; i < listWidget->count(); i++) { +		QListWidgetItem* item = listWidget->item(i); +		bool selected = false; +		if (listSingleField) { +			selected = (item->text() == QString(listSingleField->getValue().c_str())); +		} +		else if (listMultiField) { +			std::string text = Q2PSTRING(item->text()); +			selected = (std::find(listMultiField->getValue().begin(), listMultiField->getValue().end(), text) != listMultiField->getValue().end()); +		} +		item->setSelected(selected); +	} +	return listWidget; +} + +QWidget* QtFormWidget::createWidget(FormField::ref field) { +	QWidget* widget = NULL; +	boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +	if (booleanField) { +		QCheckBox* checkWidget = new QCheckBox(this); +		checkWidget->setCheckState(booleanField->getValue() ? Qt::Checked : Qt::Unchecked); +		widget = checkWidget; +	} +	boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +	if (fixedField) { +		QString value = fixedField->getValue().c_str(); +		widget = new QLabel(value, this); +	} +	boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +	if (listSingleField) { +		widget = createList(field); +	} +	boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +	if (textMultiField) { +		QString value = textMultiField->getValue().c_str(); +		widget = new QTextEdit(value, this); +	} +	boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +	if (textPrivateField) { +		QString value = textPrivateField->getValue().c_str(); +		QLineEdit* lineWidget = new QLineEdit(value, this); +		lineWidget->setEchoMode(QLineEdit::Password); +		widget = lineWidget; +	} +	boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +	if (textSingleField) { +		QString value = textSingleField->getValue().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +	if (jidSingleField) { +		QString value = jidSingleField->getValue().toString().c_str(); +		widget = new QLineEdit(value, this); +	} +	boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +	if (jidMultiField) { +		QString text; +		bool prev = false; +		foreach (JID line, jidMultiField->getValue()) { +			if (prev) { +				text += "\n"; +			} +			prev = true; +			text += line.toString().c_str(); +		} +		widget = new QTextEdit(text, this); +	} +	boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +	if (listMultiField) { +		widget = createList(field); +	} +	boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +	if (hiddenField) { +	} +	fields_[field->getName()] = widget; +	return widget; +} + +Form::ref QtFormWidget::getCompletedForm() { +	Form::ref result(new Form(Form::SubmitType)); +	foreach (boost::shared_ptr<FormField> field, form_->getFields()) { +		boost::shared_ptr<FormField> resultField; +		boost::shared_ptr<BooleanFormField> booleanField = boost::dynamic_pointer_cast<BooleanFormField>(field); +		if (booleanField) { +			resultField = FormField::ref(BooleanFormField::create(qobject_cast<QCheckBox*>(fields_[field->getName()])->checkState() == Qt::Checked)); +		} +		boost::shared_ptr<FixedFormField> fixedField = boost::dynamic_pointer_cast<FixedFormField>(field); +		if (fixedField) { +			resultField = FormField::ref(FixedFormField::create(fixedField->getValue())); +		} +		boost::shared_ptr<ListSingleFormField> listSingleField = boost::dynamic_pointer_cast<ListSingleFormField>(field); +		if (listSingleField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			if (listWidget->selectedItems().size() > 0) { +				int i = listWidget->row(listWidget->selectedItems()[0]); +				resultField = FormField::ref(ListSingleFormField::create(field->getOptions()[i].value)); +			} +			else { +				resultField = FormField::ref(ListSingleFormField::create()); +			} +		} +		boost::shared_ptr<TextMultiFormField> textMultiField = boost::dynamic_pointer_cast<TextMultiFormField>(field); +		if (textMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextMultiFormField::create()); +			} +			else { +				resultField = FormField::ref(TextMultiFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextPrivateFormField> textPrivateField = boost::dynamic_pointer_cast<TextPrivateFormField>(field); +		if (textPrivateField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextPrivateFormField::create()); +			} +			else { +				resultField = FormField::ref(TextPrivateFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<TextSingleFormField> textSingleField = boost::dynamic_pointer_cast<TextSingleFormField>(field); +		if (textSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			if (string.isEmpty()) { +				resultField = FormField::ref(TextSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(TextSingleFormField::create(Q2PSTRING(string))); +			} +		} +		boost::shared_ptr<JIDSingleFormField> jidSingleField = boost::dynamic_pointer_cast<JIDSingleFormField>(field); +		if (jidSingleField) { +			QLineEdit* widget = qobject_cast<QLineEdit*>(fields_[field->getName()]); +			QString string = widget->text(); +			JID jid(Q2PSTRING(string)); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDSingleFormField::create()); +			} +			else { +				resultField = FormField::ref(JIDSingleFormField::create(jid)); +			} +		} +		boost::shared_ptr<JIDMultiFormField> jidMultiField = boost::dynamic_pointer_cast<JIDMultiFormField>(field); +		if (jidMultiField) { +			QTextEdit* widget = qobject_cast<QTextEdit*>(fields_[field->getName()]); +			QString string = widget->toPlainText(); +			if (string.isEmpty()) { +				resultField = FormField::ref(JIDMultiFormField::create()); +			} +			else { +				QStringList lines = string.split("\n"); +				std::vector<JID> value; +				foreach (QString line, lines) { +					value.push_back(JID(Q2PSTRING(line))); +				} +				resultField = FormField::ref(JIDMultiFormField::create(value)); +			} +		} +		boost::shared_ptr<ListMultiFormField> listMultiField = boost::dynamic_pointer_cast<ListMultiFormField>(field); +		if (listMultiField) { +			QListWidget* listWidget = qobject_cast<QListWidget*>(fields_[field->getName()]); +			std::vector<std::string> values; +			foreach (QListWidgetItem* item, listWidget->selectedItems()) { +				values.push_back(field->getOptions()[listWidget->row(item)].value); +			} +			resultField = FormField::ref(ListMultiFormField::create(values)); +		} +		boost::shared_ptr<HiddenFormField> hiddenField = boost::dynamic_pointer_cast<HiddenFormField>(field); +		if (hiddenField) { +			resultField = FormField::ref(HiddenFormField::create(hiddenField->getValue())); +		} +		resultField->setName(field->getName()); +		result->addField(resultField); +	} +	return result; +} + +} diff --git a/Swift/QtUI/QtFormWidget.h b/Swift/QtUI/QtFormWidget.h new file mode 100644 index 0000000..2fb7b98 --- /dev/null +++ b/Swift/QtUI/QtFormWidget.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QWidget> + +#include <map> +#include <Swiften/Elements/Form.h> + +class QListWidget; + +namespace Swift { + +class QtFormWidget : public QWidget { +	Q_OBJECT +	public: +		QtFormWidget(Form::ref form, QWidget* parent = NULL); +		virtual ~QtFormWidget(); +		Form::ref getCompletedForm(); +	private: +		QWidget* createWidget(FormField::ref field); +		QListWidget* createList(FormField::ref field); +		std::map<std::string, QWidget*> fields_; +		Form::ref form_; +}; + +} diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index d0ab61e..4302c10 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -56,9 +56,9 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow() {  	stack_ = new QStackedWidget(centralWidget);  	topLayout->addWidget(stack_);  	topLayout->setMargin(0); -	QWidget *wrapperWidget = new QWidget(this); -	wrapperWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); -	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, wrapperWidget); +	loginWidgetWrapper_ = new QWidget(this); +	loginWidgetWrapper_->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); +	QBoxLayout *layout = new QBoxLayout(QBoxLayout::TopToBottom, loginWidgetWrapper_);  	layout->addStretch(2);  	QLabel* logo = new QLabel(this); @@ -139,7 +139,7 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream) : QMainWindow() {  	layout->addWidget(loginAutomatically_);  	connect(loginButton_, SIGNAL(clicked()), SLOT(loginClicked())); -	stack_->addWidget(wrapperWidget); +	stack_->addWidget(loginWidgetWrapper_);  #ifdef SWIFTEN_PLATFORM_MACOSX  	menuBar_ = new QMenuBar(NULL);  #else @@ -284,11 +284,9 @@ void QtLoginWindow::handleUsernameTextChanged() {  }  void QtLoginWindow::loggedOut() { -	if (stack_->count() > 1) { -		QWidget* current = stack_->currentWidget(); -		stack_->setCurrentIndex(0); -		stack_->removeWidget(current); -	} +	stack_->removeWidget(stack_->currentWidget()); +	stack_->addWidget(loginWidgetWrapper_); +	stack_->setCurrentWidget(loginWidgetWrapper_);  	setInitialMenus();  	setIsLoggingIn(false);  } @@ -370,6 +368,7 @@ void QtLoginWindow::setInitialMenus() {  void QtLoginWindow::morphInto(MainWindow *mainWindow) {  	QtMainWindow *qtMainWindow = dynamic_cast<QtMainWindow*>(mainWindow);  	assert(qtMainWindow); +	stack_->removeWidget(loginWidgetWrapper_);  	stack_->addWidget(qtMainWindow);  	stack_->setCurrentWidget(qtMainWindow);  	setEnabled(true); diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index 3f3b5f8..b667a4b 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -1,11 +1,10 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFT_QtLoginWindow_H -#define SWIFT_QtLoginWindow_H +#pragma once  #include <QMainWindow>  #include <QPointer> @@ -65,6 +64,7 @@ namespace Swift {  		private:  			void setInitialMenus(); +			QWidget* loginWidgetWrapper_;  			QStringList usernames_;  			QStringList passwords_;  			QStringList certificateFiles_; @@ -87,5 +87,3 @@ namespace Swift {  			QPointer<QtAboutWidget> aboutDialog_;  	};  } - -#endif diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 6391961..0c959d6 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -21,20 +21,25 @@  #include <QAction>  #include <QTabWidget> -#include "QtSwiftUtil.h" -#include "QtTabWidget.h" -#include "Roster/QtTreeWidget.h" -#include "Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h" -#include "Swift/Controllers/UIEvents/JoinMUCUIEvent.h" -#include "Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h" +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtTabWidget.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Roster/QtTreeWidget.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/ToggleShowOfflineUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>  namespace Swift { +#define CURRENT_ROSTER_TAB "current_roster_tab" +  QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventStream) : QWidget(), MainWindow(false) {  	uiEventStream_ = uiEventStream; +	settings_ = settings;  	setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));  	QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);  	mainLayout->setContentsMargins(0,0,0,0); @@ -68,8 +73,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	chatListWindow_ = new QtChatListWindow(uiEventStream_); -	tabs_->addTab(eventWindow_, tr("&Notices"));  	tabs_->addTab(chatListWindow_, tr("C&hats")); +	tabs_->addTab(eventWindow_, tr("&Notices")); + +	tabs_->setCurrentIndex(settings_->getIntSetting(CURRENT_ROSTER_TAB, 0)); + +	connect(tabs_, SIGNAL(currentChanged(int)), this, SLOT(handleTabChanged(int)));  	this->setLayout(mainLayout); @@ -99,6 +108,8 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	chatUserAction_ = new QAction(tr("Start &Chat"), this);  	connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));  	actionsMenu->addAction(chatUserAction_); +	serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this); +	actionsMenu->addMenu(serverAdHocMenu_);  	actionsMenu->addSeparator();  	QAction* signOutAction = new QAction(tr("&Sign Out"), this);  	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction())); @@ -106,6 +117,12 @@ QtMainWindow::QtMainWindow(QtSettingsProvider* settings, UIEventStream* uiEventS  	connect(treeWidget_, SIGNAL(onSomethingSelectedChanged(bool)), editUserAction_, SLOT(setEnabled(bool))); +	setAvailableAdHocCommands(std::vector<DiscoItems::Item>()); +	QAction* adHocAction = new QAction(tr("Collecting commands..."), this); +	adHocAction->setEnabled(false); +	serverAdHocMenu_->addAction(adHocAction); +	serverAdHocCommandActions_.append(adHocAction); +  	lastOfflineState_ = false;  	uiEventStream_->onUIEvent.connect(boost::bind(&QtMainWindow::handleUIEvent, this, _1));  } @@ -114,6 +131,10 @@ QtMainWindow::~QtMainWindow() {  	uiEventStream_->onUIEvent.disconnect(boost::bind(&QtMainWindow::handleUIEvent, this, _1));  } +void QtMainWindow::handleTabChanged(int index) { +	settings_->storeInt(CURRENT_ROSTER_TAB, index); +} +  QtEventWindow* QtMainWindow::getEventWindow() {  	return eventWindow_;  } @@ -132,7 +153,7 @@ void QtMainWindow::handleEditProfileRequest() {  void QtMainWindow::handleEventCountUpdated(int count) {  	QColor eventTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default -	int eventIndex = 1; +	int eventIndex = 2;  	tabs_->tabBar()->setTabTextColor(eventIndex, eventTabColor);  	QString text = tr("&Notices");  	if (count > 0) { @@ -206,6 +227,33 @@ void QtMainWindow::setConnecting() {  	meView_->setConnecting();  } +void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) { +	QAction* action = qobject_cast<QAction*>(sender()); +	assert(action); +	DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)]; +	uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestAdHocUIEvent(command))); +} + +void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) { +	serverAdHocCommands_ = commands; +	foreach (QAction* action, serverAdHocCommandActions_) { +		delete action; +	} +	serverAdHocMenu_->clear(); +	serverAdHocCommandActions_.clear(); +	foreach (DiscoItems::Item command, commands) { +		QAction* action = new QAction(P2QSTRING(command.getName()), this); +		connect(action, SIGNAL(triggered(bool)), this, SLOT(handleAdHocActionTriggered(bool))); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +	if (serverAdHocCommandActions_.isEmpty()) { +		QAction* action = new QAction(tr("No Available Commands"), this); +		action->setEnabled(false); +		serverAdHocMenu_->addAction(action); +		serverAdHocCommandActions_.append(action); +	} +}  } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 3462bb0..5c29f6d 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2011 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -8,6 +8,7 @@  #include <QWidget>  #include <QMenu> +#include <QList>  #include "Swift/Controllers/UIInterfaces/MainWindow.h"  #include "Swift/QtUI/QtRosterHeader.h"  #include "Swift/QtUI/EventViewer/QtEventWindow.h" @@ -20,7 +21,7 @@ class QLineEdit;  class QPushButton;  class QToolBar;  class QAction; - +class QMenu;  class QTabWidget;  namespace Swift { @@ -46,6 +47,7 @@ namespace Swift {  			QtEventWindow* getEventWindow();  			QtChatListWindow* getChatListWindow();  			void setRosterModel(Roster* roster); +			void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands);  		private slots:  			void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage);  			void handleUIEvent(boost::shared_ptr<UIEvent> event); @@ -55,10 +57,13 @@ namespace Swift {  			void handleEditProfileAction();  			void handleAddUserActionTriggered(bool checked);  			void handleChatUserActionTriggered(bool checked); +			void handleAdHocActionTriggered(bool checked);  			void handleEventCountUpdated(int count);  			void handleEditProfileRequest(); +			void handleTabChanged(int index);  		private: +			QtSettingsProvider* settings_;  			std::vector<QMenu*> menus_;  			QtTreeWidget* treeWidget_;  			QtRosterHeader* meView_; @@ -66,6 +71,7 @@ namespace Swift {  			QAction* editUserAction_;  			QAction* chatUserAction_;  			QAction* showOfflineAction_; +			QMenu* serverAdHocMenu_;  			QtTabWidget* tabs_;  			QWidget* contactsTabWidget_;  			QWidget* eventsTabWidget_; @@ -73,5 +79,7 @@ namespace Swift {  			QtChatListWindow* chatListWindow_;  			UIEventStream* uiEventStream_;  			bool lastOfflineState_; +			std::vector<DiscoItems::Item> serverAdHocCommands_; +			QList<QAction*> serverAdHocCommandActions_;  	};  } diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index d4c306f..d7a1f78 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -18,13 +18,11 @@  #include "QtUIFactory.h"  #include "QtChatWindowFactory.h"  #include <Swiften/Base/Log.h> -#include <Swift/Controllers/CertificateFileStorageFactory.h> +#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h> +#include "Swift/Controllers/Storages/FileStoragesFactory.h"  #include "SwifTools/Application/PlatformApplicationPathProvider.h" -#include "Swiften/Avatars/AvatarFileStorage.h" -#include "Swiften/Disco/CapsFileStorage.h"  #include <string>  #include "Swiften/Base/Platform.h" -#include "Swift/Controllers/FileStoragesFactory.h"  #include "Swiften/Elements/Presence.h"  #include "Swiften/Client/Client.h"  #include "Swift/Controllers/MainController.h" @@ -32,20 +30,30 @@  #include "Swift/Controllers/BuildVersion.h"  #include "SwifTools/AutoUpdater/AutoUpdater.h"  #include "SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h" +  #if defined(SWIFTEN_PLATFORM_WINDOWS)  #include "WindowsNotifier.h" -#endif -#if defined(HAVE_GROWL) +#elif defined(HAVE_GROWL)  #include "SwifTools/Notifier/GrowlNotifier.h"  #elif defined(SWIFTEN_PLATFORM_LINUX)  #include "FreeDesktopNotifier.h"  #else  #include "SwifTools/Notifier/NullNotifier.h"  #endif +  #if defined(SWIFTEN_PLATFORM_MACOSX)  #include "SwifTools/Dock/MacOSXDock.h" -#endif +#else  #include "SwifTools/Dock/NullDock.h" +#endif + +#if defined(SWIFTEN_PLATFORM_MACOSX) +#include "QtURIHandler.h" +#elif defined(SWIFTEN_PLATFORM_WIN32) +#include <SwifTools/URIHandler/NullURIHandler.h> +#else +#include "QtDBUSURIHandler.h" +#endif  namespace Swift{ @@ -123,6 +131,14 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  	dock_ = new NullDock();  #endif +#if defined(SWIFTEN_PLATFORM_MACOSX) +	uriHandler_ = new QtURIHandler(); +#elif defined(SWIFTEN_PLATFORM_WIN32) +	uriHandler_ = new NullURIHandler(); +#else +	uriHandler_ = new QtDBUSURIHandler(); +#endif +  	if (splitter_) {  		splitter_->show();  	} @@ -145,6 +161,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  				certificateStorageFactory_,  				dock_,  				notifier_, +				uriHandler_,  				options.count("latency-debug") > 0);  		mainControllers_.push_back(mainController);  	} @@ -172,6 +189,7 @@ QtSwift::~QtSwift() {  	}  	delete tabs_;  	delete splitter_; +	delete uriHandler_;  	delete dock_;  	delete soundPlayer_;  	delete chatWindowFactory_; diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 978fa14..4bf5c97 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -44,6 +44,7 @@ namespace Swift {  	class QtMUCSearchWindowFactory;  	class QtUserSearchWindowFactory;  	class EventLoop; +	class URIHandler;  	class QtSwift : public QObject {  		Q_OBJECT @@ -63,6 +64,7 @@ namespace Swift {  			QSplitter* splitter_;  			QtSoundPlayer* soundPlayer_;  			Dock* dock_; +			URIHandler* uriHandler_;  			QtChatTabs* tabs_;  			ApplicationPathProvider* applicationPathProvider_;  			StoragesFactory* storagesFactory_; diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp index 3668220..f7ac1ef 100644 --- a/Swift/QtUI/QtTextEdit.cpp +++ b/Swift/QtUI/QtTextEdit.cpp @@ -4,7 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "QtTextEdit.h" +#include <Swift/QtUI/QtTextEdit.h>  #include <QFontMetrics>  #include <QKeyEvent> @@ -22,19 +22,21 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) {  	if ((key == Qt::Key_Enter || key == Qt::Key_Return)  		&& (modifiers == Qt::NoModifier || modifiers == Qt::KeypadModifier)) {  		emit returnPressed(); -	} else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier) +	} +	else if (((key == Qt::Key_PageUp || key == Qt::Key_PageDown) && modifiers == Qt::ShiftModifier)  			   || (key == Qt::Key_C && modifiers == Qt::ControlModifier && textCursor().selectedText().isEmpty())  			   || (key == Qt::Key_W && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_PageUp && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_PageDown && modifiers == Qt::ControlModifier) -//			   || (key == Qt::Key_Left && modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) -//			   || (key == Qt::Key_Right && modifiers == (Qt::ControlModifier | Qt::ShiftModifier))  			   || (key == Qt::Key_Tab && modifiers == Qt::ControlModifier)  			   || (key == Qt::Key_A && modifiers == Qt::AltModifier)  			   || (key == Qt::Key_Tab) +			   || (key == Qt::Key_Up) +			   || (key == Qt::Key_Down)  	) {  		emit unhandledKeyPressEvent(event); -	} else { +	} +	else {  		QTextEdit::keyPressEvent(event);  	}  } diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index 35fbfcd..5c1f78d 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -23,6 +23,7 @@  #include "UserSearch/QtUserSearchWindow.h"  #include "QtProfileWindow.h"  #include "QtContactEditWindow.h" +#include "QtAdHocCommandWindow.h"  namespace Swift { @@ -99,5 +100,8 @@ ContactEditWindow* QtUIFactory::createContactEditWindow() {  	return new QtContactEditWindow();  } +void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) { +	new QtAdHocCommandWindow(command); +}  } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index ddaaf6e..9ef228a 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -37,6 +37,7 @@ namespace Swift {  			virtual JoinMUCWindow* createJoinMUCWindow();  			virtual ProfileWindow* createProfileWindow();  			virtual ContactEditWindow* createContactEditWindow(); +			virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command);  		private slots:  			void handleLoginWindowGeometryChanged(); diff --git a/Swift/QtUI/QtURIHandler.cpp b/Swift/QtUI/QtURIHandler.cpp new file mode 100644 index 0000000..43f3ed1 --- /dev/null +++ b/Swift/QtUI/QtURIHandler.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "QtURIHandler.h" + +#include <QCoreApplication> +#include <QFileOpenEvent> +#include <QUrl> + +#include "QtSwiftUtil.h" +#ifdef Q_WS_MAC +#include <SwifTools/URIHandler/MacOSXURIHandlerHelpers.h> +#endif + +using namespace Swift; + +QtURIHandler::QtURIHandler() { +	qApp->installEventFilter(this); +#ifdef Q_WS_MAC +	registerAppAsDefaultXMPPURIHandler(); +#endif +} + +bool QtURIHandler::eventFilter(QObject*, QEvent* event) { +	if (event->type() == QEvent::FileOpen) { +		QFileOpenEvent* fileOpenEvent = static_cast<QFileOpenEvent*>(event); +		if (fileOpenEvent->url().scheme() == "xmpp") { +			onURI(Q2PSTRING(fileOpenEvent->url().toString())); +			return true; +		} +	} +	return false; +} diff --git a/Swift/QtUI/QtURIHandler.h b/Swift/QtUI/QtURIHandler.h new file mode 100644 index 0000000..a02114a --- /dev/null +++ b/Swift/QtUI/QtURIHandler.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QObject> +#include <SwifTools/URIHandler/URIHandler.h> + +class QUrl; + +namespace Swift { +	class QtURIHandler : public QObject, public URIHandler { +		public: +			QtURIHandler(); + +		private: +			bool eventFilter(QObject* obj, QEvent* event); +	}; +} diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 494731c..ef4f744 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -77,6 +77,7 @@ sources = [      "QtStatusWidget.cpp",  		"QtScaledAvatarCache.cpp",      "QtSwift.cpp", +    "QtURIHandler.cpp",      "QtChatView.cpp",      "QtChatTheme.cpp",      "QtChatTabs.cpp", @@ -87,6 +88,7 @@ sources = [      "QtTabWidget.cpp",      "QtTextEdit.cpp",      "QtXMLConsoleWidget.cpp", +    "QtAdHocCommandWindow.cpp",      "QtUtilities.cpp",      "QtBookmarkDetailWindow.cpp",      "QtAddBookmarkWindow.cpp", @@ -97,6 +99,7 @@ sources = [      "MessageSnippet.cpp",      "SystemMessageSnippet.cpp",      "QtElidingLabel.cpp", +    "QtFormWidget.cpp",      "QtLineEdit.cpp",      "QtJoinMUCWindow.cpp",      "Roster/RosterModel.cpp", @@ -114,6 +117,7 @@ sources = [      "ChatList/ChatListModel.cpp",      "ChatList/ChatListDelegate.cpp",      "ChatList/ChatListMUCItem.cpp", +    "ChatList/ChatListRecentItem.cpp",      "MUCSearch/QtMUCSearchWindow.cpp",      "MUCSearch/MUCSearchModel.cpp",      "MUCSearch/MUCSearchRoomItem.cpp", @@ -136,20 +140,31 @@ sources = [  myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift")  if env["PLATFORM"] == "win32" : -  myenv.RES("../resources/Windows/Swift.rc") +  res = myenv.RES("../resources/Windows/Swift.rc") +  # For some reason, SCons isn't picking up the dependency correctly +	# Adding it explicitly until i figure out why +  myenv.Depends(res, "../Controllers/BuildVersion.h")    sources += [  			"WindowsNotifier.cpp",  			"../resources/Windows/Swift.res"  		]  if env["PLATFORM"] == "posix" : -	sources += ["FreeDesktopNotifier.cpp"] +	sources += [ +			"FreeDesktopNotifier.cpp", +			"QtDBUSURIHandler.cpp", +	]  if env["PLATFORM"] == "darwin" or env["PLATFORM"] == "win32" :    swiftProgram = myenv.Program("Swift", sources)  else :    swiftProgram = myenv.Program("swift", sources) +if env["PLATFORM"] != "darwin" and env["PLATFORM"] != "win32" : +	openURIProgram = myenv.Program("swift-open-uri", "swift-open-uri.cpp") +else : +	openURIProgram = [] +  myenv.Uic4("MUCSearch/QtMUCSearchWindow.ui")  myenv.Uic4("UserSearch/QtUserSearchWizard.ui")  myenv.Uic4("UserSearch/QtUserSearchFirstPage.ui") @@ -215,12 +230,12 @@ if env["PLATFORM"] == "darwin" :    if env["HAVE_GROWL"] :      frameworks.append(env["GROWL_FRAMEWORK"])    commonResources[""] = commonResources.get("", []) + ["../resources/MacOSX/Swift.icns"] -  app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks) +  app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = commonResources, frameworks = frameworks, handlesXMPPURIs = True)    if env["DIST"] :      myenv.Command(["Swift-${SWIFT_VERSION}.dmg"], [app], ["Swift/Packaging/MacOSX/package.sh " + app.path + " Swift/Packaging/MacOSX/Swift.dmg.gz $TARGET $QTDIR"])  if env.get("SWIFT_INSTALLDIR", "") : -  env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram) +  env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "bin"), swiftProgram + openURIProgram)    env.InstallAs(os.path.join(env["SWIFT_INSTALLDIR"], "share", "pixmaps", "swift.xpm"), "../resources/logo/logo-icon-32.xpm")    icons_path = os.path.join(env["SWIFT_INSTALLDIR"], "share", "icons", "hicolor")    env.InstallAs(os.path.join(icons_path, "32x32", "apps", "swift.xpm"), "../resources/logo/logo-icon-32.xpm") diff --git a/Swift/QtUI/swift-open-uri.cpp b/Swift/QtUI/swift-open-uri.cpp new file mode 100644 index 0000000..2d5ef19 --- /dev/null +++ b/Swift/QtUI/swift-open-uri.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <QCoreApplication> +#include <QDBusConnection> +#include <QDBusMessage> +#include <iostream> + +int main(int argc, char* argv[]) { +	QCoreApplication app(argc, argv); + +	QDBusConnection bus = QDBusConnection::sessionBus(); +	if (!bus.isConnected()) { +		return -1; +	} +	if (argc != 2) { +		std::cerr << "Usage: " << argv[0] << " uri" << std::endl; +		return -1; +	} + +	QDBusMessage msg = QDBusMessage::createMethodCall("im.swift.Swift.URIHandler", "/", "im.swift.Swift.URIHandler", "openURI"); +	msg << argv[1]; + +	bus.call(msg); + +	return 0; +} diff --git a/Swift/Translations/swift_nl.ts b/Swift/Translations/swift_nl.ts index 0683260..7e8bfbd 100644 --- a/Swift/Translations/swift_nl.ts +++ b/Swift/Translations/swift_nl.ts @@ -416,6 +416,22 @@          <source>There was an error publishing your profile data</source>          <translation>Er is een fout opgetreden bij het publiceren van uw profiel</translation>      </message> +    <message> +        <source>%1% (%2%)</source> +        <translation>%1% (%2%)</translation> +    </message> +    <message> +        <source>%1% and %2% others (%3%)</source> +        <translation>%1% en %2% anderen (%3%)</translation> +    </message> +    <message> +        <source>%1%, %2% (%3%)</source> +        <translation>%1%, %2% (%3%)</translation> +    </message> +    <message> +        <source>User address invalid. User address should be of the form 'alice@wonderland.lit'</source> +        <translation>Gebruikersadres ongeldig. Gebruikersadres moet van de vorm 'alice@wonderland.lit' zijn</translation> +    </message>  </context>  <context>      <name>CloseButton</name> @@ -835,6 +851,10 @@          <source>Bookmarked Rooms</source>          <translation>Bladwijzers</translation>      </message> +    <message> +        <source>Recent Chats</source> +        <translation>Recente conversaties</translation> +    </message>  </context>  <context>      <name>Swift::QtAboutWidget</name> @@ -866,6 +886,37 @@      </message>  </context>  <context> +    <name>Swift::QtAdHocCommandWindow</name> +    <message> +        <source>Cancel</source> +        <translation>Annuleren</translation> +    </message> +    <message> +        <source>Back</source> +        <translation>Terug</translation> +    </message> +    <message> +        <source>Next</source> +        <translation>Volgende</translation> +    </message> +    <message> +        <source>Complete</source> +        <translation>Voltooien</translation> +    </message> +    <message> +        <source>Error: %1</source> +        <translation>Fout: %1</translation> +    </message> +    <message> +        <source>Warning: %1</source> +        <translation>Waarschuwing: %1</translation> +    </message> +    <message> +        <source>Error executing command</source> +        <translation>Fout bij het uitvoeren van de opdracht</translation> +    </message> +</context> +<context>      <name>Swift::QtAvatarWidget</name>      <message>          <source>No picture</source> @@ -1161,6 +1212,18 @@ afbeelding</translation>          <source>Enter &Room</source>          <translation>&Kamer betreden</translation>      </message> +    <message> +        <source>Run Server Command</source> +        <translation>Voer opdracht op server uit</translation> +    </message> +    <message> +        <source>Collecting commands...</source> +        <translation>Opdrachten aan het verzamelen...</translation> +    </message> +    <message> +        <source>No Available Commands</source> +        <translation>Geen beschikbare opdrachten</translation> +    </message>  </context>  <context>      <name>Swift::QtNameWidget</name> diff --git a/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp b/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp new file mode 100644 index 0000000..da7f042 --- /dev/null +++ b/Swiften/AdHoc/OutgoingAdHocCommandSession.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/AdHoc/OutgoingAdHocCommandSession.h> + +#include <boost/bind.hpp> + +#include <Swiften/Queries/GenericRequest.h> + +namespace Swift { +OutgoingAdHocCommandSession::OutgoingAdHocCommandSession(const DiscoItems::Item& command, AdHocCommandWindowFactory* /*factory*/, IQRouter* iqRouter) : command_(command), iqRouter_(iqRouter), isMultiStage_(false) { + +} + +void OutgoingAdHocCommandSession::handleResponse(boost::shared_ptr<Command> payload, ErrorPayload::ref error) { +	if (error) { +		onError(error); +	} else { +		const std::vector<Command::Action> actions = payload->getAvailableActions(); +		actionStates_.clear(); +		if (payload->getStatus() == Command::Executing ) { +			actionStates_[Command::Cancel] = EnabledAndPresent; +			actionStates_[Command::Complete] = Present; +			if (std::find(actions.begin(), actions.end(), Command::Complete) != actions.end()) { +				actionStates_[Command::Complete] = EnabledAndPresent; +			} + +			if (getIsMultiStage()) { +				actionStates_[Command::Next] = Present; +				actionStates_[Command::Prev] = Present; +			} + +		if (std::find(actions.begin(), actions.end(), Command::Next) != actions.end()) { +				actionStates_[Command::Next] = EnabledAndPresent; +			} +		if (std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()) { +				actionStates_[Command::Prev] = EnabledAndPresent; +			} +		} + +		sessionID_ = payload->getSessionID(); +		if (std::find(actions.begin(), actions.end(), Command::Next) != actions.end() +				|| std::find(actions.begin(), actions.end(), Command::Prev) != actions.end()) { +			isMultiStage_ = true; +		} +		onNextStageReceived(payload); +	} +} + +bool OutgoingAdHocCommandSession::getIsMultiStage() { +	return isMultiStage_; +} + +void OutgoingAdHocCommandSession::start() { +	boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode())); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::cancel() { +	if (!sessionID_.empty()) { +		boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode(), sessionID_, Command::Cancel)); +		boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +		commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +		commandRequest->send(); +	} +} + +void OutgoingAdHocCommandSession::goBack() { +	boost::shared_ptr<Payload> commandPayload(new Command(command_.getNode(), sessionID_, Command::Prev)); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::complete(Form::ref form) { +	Command* command = new Command(command_.getNode(), sessionID_, Command::Complete); +	boost::shared_ptr<Payload> commandPayload(command); +	command->setForm(form); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +void OutgoingAdHocCommandSession::goNext(Form::ref form) { +	Command* command = new Command(command_.getNode(), sessionID_, Command::Next); +	boost::shared_ptr<Payload> commandPayload(command); +	command->setForm(form); +	boost::shared_ptr<GenericRequest<Command> > commandRequest(new GenericRequest<Command>(IQ::Set, command_.getJID(), commandPayload, iqRouter_)); +	commandRequest->onResponse.connect(boost::bind(&OutgoingAdHocCommandSession::handleResponse, this, _1, _2)); +	commandRequest->send(); +} + +OutgoingAdHocCommandSession::ActionState OutgoingAdHocCommandSession::getActionState(Command::Action action) { +	return actionStates_[action]; +} + +} diff --git a/Swiften/AdHoc/OutgoingAdHocCommandSession.h b/Swiften/AdHoc/OutgoingAdHocCommandSession.h new file mode 100644 index 0000000..f4b5242 --- /dev/null +++ b/Swiften/AdHoc/OutgoingAdHocCommandSession.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010-2011 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/boost_bsignals.h> +#include <boost/shared_ptr.hpp> +#include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Elements/Command.h> +#include <Swiften/Elements/ErrorPayload.h> + +namespace Swift { +	class IQRouter; +	class MainWindow; +	class UIEventStream; +	class AdHocCommandWindowFactory; +	class OutgoingAdHocCommandSession { +		public: + +			/** +			 * Availability of action. +			 */ +			enum ActionState { +				Absent /** Action isn't applicable to this command. */ = 0, +				Present /** Action is applicable to this command */= 1, +				Enabled /** Action is applicable and currently available */ = 2, +				EnabledAndPresent = 3}; + +			OutgoingAdHocCommandSession(const DiscoItems::Item& command, AdHocCommandWindowFactory* factory, IQRouter* iqRouter); +			/** +			 * Send initial request to the target. +			 */ +			void start(); +			/** +			 * Cancel command session with the target. +			 */ +			void cancel(); +			/** +			 * Return to the previous stage. +			 */ +			void goBack(); +			/** +			 * Send the form to complete the command. +			 * \param form Form for submission - if missing the command will be submitted with no form. +			 */ +			void complete(Form::ref form); +			/** +			 * Send the form to advance to the next stage of the command. +			 * \param form Form for submission - if missing the command will be submitted with no form. +			 */ +			void goNext(Form::ref form); + +			/** +			 * Is the form multi-stage? +			 */ +			bool getIsMultiStage(); + +			/** +			 * Emitted when the form for the next stage is available. +			 */ +			boost::signal<void (Command::ref)> onNextStageReceived; + +			/** +			 * Emitted on error. +			 */ +			boost::signal<void (ErrorPayload::ref)> onError; + +			/** +			 * Get the state of a given action. +			 * This is useful for a UI to determine which buttons should be visible, +			 * and which enabled. +			 * Use for Next, Prev, Cancel and Complete only. +			 * If no actions are available, the command has completed. +			 */ +			ActionState getActionState(Command::Action action); +		private: +			void handleResponse(boost::shared_ptr<Command> payload, ErrorPayload::ref error); +		private: +			DiscoItems::Item command_; +			IQRouter* iqRouter_; +			bool isMultiStage_; +			std::string sessionID_; +			std::map<Command::Action, ActionState> actionStates_; +	}; +} diff --git a/Swiften/AdHoc/SConscript b/Swiften/AdHoc/SConscript new file mode 100644 index 0000000..69c9083 --- /dev/null +++ b/Swiften/AdHoc/SConscript @@ -0,0 +1,6 @@ +Import("swiften_env") + +objects = swiften_env.SwiftenObject([ +			"OutgoingAdHocCommandSession.cpp", +		]) +swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h index e1af451..3461973 100644 --- a/Swiften/Avatars/AvatarManager.h +++ b/Swiften/Avatars/AvatarManager.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <Swiften/Base/boost_bsignals.h>  #include <Swiften/Base/ByteArray.h> diff --git a/Swiften/Avatars/AvatarProvider.h b/Swiften/Avatars/AvatarProvider.h index 0f66904..d2d408e 100644 --- a/Swiften/Avatars/AvatarProvider.h +++ b/Swiften/Avatars/AvatarProvider.h @@ -6,9 +6,10 @@  #pragma once -#include "Swiften/Base/boost_bsignals.h"  #include <string> +#include <Swiften/Base/boost_bsignals.h> +  namespace Swift {  	class JID; diff --git a/Swiften/Avatars/AvatarStorage.h b/Swiften/Avatars/AvatarStorage.h index 48fc885..d1aff39 100644 --- a/Swiften/Avatars/AvatarStorage.h +++ b/Swiften/Avatars/AvatarStorage.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <string>  namespace Swift { diff --git a/Swiften/Avatars/SConscript b/Swiften/Avatars/SConscript index 46ae897..9c219a4 100644 --- a/Swiften/Avatars/SConscript +++ b/Swiften/Avatars/SConscript @@ -1,7 +1,6 @@  Import("swiften_env")  objects = swiften_env.SwiftenObject([ -			"AvatarFileStorage.cpp",  			"VCardUpdateAvatarManager.cpp",  			"VCardAvatarManager.cpp",  			"OfflineAvatarManager.cpp", diff --git a/Swiften/Avatars/VCardAvatarManager.h b/Swiften/Avatars/VCardAvatarManager.h index 3f99dad..6c02ace 100644 --- a/Swiften/Avatars/VCardAvatarManager.h +++ b/Swiften/Avatars/VCardAvatarManager.h @@ -6,11 +6,8 @@  #pragma once -#include <map> -  #include "Swiften/Avatars/AvatarProvider.h"  #include "Swiften/JID/JID.h" -#include "Swiften/Elements/VCard.h"  namespace Swift {  	class MUCRegistry; diff --git a/Swiften/Base/ByteArray.cpp b/Swiften/Base/ByteArray.cpp index 928e145..323c866 100644 --- a/Swiften/Base/ByteArray.cpp +++ b/Swiften/Base/ByteArray.cpp @@ -9,6 +9,10 @@  #include <fstream>  std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s) { +	return operator<<(os, s.getDataVector()); +} + +std::ostream& operator<<(std::ostream& os, const std::vector<unsigned char>& s) {  	std::ios::fmtflags oldFlags = os.flags();   	os << std::hex;  	for (Swift::ByteArray::const_iterator i = s.begin(); i != s.end(); ++i) { @@ -37,4 +41,45 @@ void ByteArray::readFromFile(const std::string& file) {  	input.close();  } +std::vector<unsigned char> ByteArray::create(const std::string& s) { +	return std::vector<unsigned char>(s.begin(), s.end()); +} + +std::vector<unsigned char> ByteArray::create(const char* c) { +	std::vector<unsigned char> data; +	while (*c) { +		data.push_back(static_cast<unsigned char>(*c)); +		++c; +	} +	return data; +} + +std::vector<unsigned char> ByteArray::create(const char* c, size_t n) { +	std::vector<unsigned char> data; +	if (n > 0) { +		data.resize(n); +		memcpy(&data[0], c, n); +	} +	return data; +} + +std::vector<unsigned char> ByteArray::create(const unsigned char* c, size_t n) { +	std::vector<unsigned char> data; +	if (n > 0) { +		data.resize(n); +		memcpy(&data[0], c, n); +	} +	return data; +} + +std::string ByteArray::toString() const { +	size_t i; +	for (i = data_.size(); i > 0; --i) { +		if (data_[i - 1] != 0) { +			break; +		} +	} +	return i > 0 ? std::string(reinterpret_cast<const char*>(getData()), i) : ""; +} +  } diff --git a/Swiften/Base/ByteArray.h b/Swiften/Base/ByteArray.h index ad2c1e5..ebc22d8 100644 --- a/Swiften/Base/ByteArray.h +++ b/Swiften/Base/ByteArray.h @@ -6,11 +6,9 @@  #pragma once -#include <cstring>  #include <vector> -#include <iostream> -  #include <string> +#include <cstring> // for memcpy  namespace Swift {  	class ByteArray @@ -24,7 +22,7 @@ namespace Swift {  			ByteArray(const char* c) {  				while (*c) { -					data_.push_back(*c); +					data_.push_back(static_cast<unsigned char>(*c));  					++c;  				}  			} @@ -75,7 +73,7 @@ namespace Swift {  			}  			void resize(size_t size, char c) { -				return data_.resize(size, c); +				return data_.resize(size, static_cast<unsigned char>(c));  			}  			friend ByteArray operator+(const ByteArray& a, const ByteArray&b) { @@ -87,7 +85,7 @@ namespace Swift {  			friend ByteArray operator+(const ByteArray& a, char b) {  				ByteArray x;  				x.resize(1); -				x[0] = b; +				x[0] = static_cast<unsigned char>(b);  				return a + x;  			} @@ -97,7 +95,7 @@ namespace Swift {  			}  			ByteArray& operator+=(char c) { -				data_.push_back(c); +				data_.push_back(static_cast<unsigned char>(c));  				return *this;  			} @@ -122,9 +120,7 @@ namespace Swift {  				return data_.end();   			} -			std::string toString() const { -				return std::string(reinterpret_cast<const char*>(getData()), getSize()); -			} +			std::string toString() const;  			void readFromFile(const std::string& file); @@ -132,9 +128,27 @@ namespace Swift {  				data_.clear();  			} +			const std::vector<unsigned char>& getDataVector() const { +				return data_; +			} + +			static std::vector<unsigned char> create(const std::string& s); +			static std::vector<unsigned char> create(const char* c); +			static std::vector<unsigned char> create(const unsigned char* c, size_t n); +			static std::vector<unsigned char> create(const char* c, size_t n); + +			static const unsigned char* data(const std::vector<unsigned char>& v) { +				return v.empty() ? NULL : &v[0]; +			} + +			static const char* charData(const std::vector<unsigned char>& v) { +				return v.empty() ? NULL : reinterpret_cast<const char*>(&v[0]); +			} +  		private:  			std::vector<unsigned char> data_;  	};  }  std::ostream& operator<<(std::ostream& os, const Swift::ByteArray& s); +std::ostream& operator<<(std::ostream& os, const std::vector<unsigned char>& s); diff --git a/Swiften/Base/IDGenerator.h b/Swiften/Base/IDGenerator.h index 4b6289b..d536dcc 100644 --- a/Swiften/Base/IDGenerator.h +++ b/Swiften/Base/IDGenerator.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_IDGenerator_H -#define SWIFTEN_IDGenerator_H +#pragma once  #include <string> @@ -20,5 +19,3 @@ namespace Swift {  			std::string currentID_;  	};  } - -#endif diff --git a/Swiften/Base/Paths.cpp b/Swiften/Base/Paths.cpp index 43eee57..d901ff9 100644 --- a/Swiften/Base/Paths.cpp +++ b/Swiften/Base/Paths.cpp @@ -25,7 +25,7 @@ boost::filesystem::path Paths::getExecutablePath() {  	uint32_t size = 4096;  	path.resize(size);  	if (_NSGetExecutablePath(reinterpret_cast<char*>(path.getData()), &size) == 0) { -		return boost::filesystem::path(path.toString().c_str()).parent_path(); +		return boost::filesystem::path(std::string(reinterpret_cast<const char*>(path.getData()), path.getSize()).c_str()).parent_path();  	}  #elif defined(SWIFTEN_PLATFORM_LINUX)  	ByteArray path; @@ -33,13 +33,13 @@ boost::filesystem::path Paths::getExecutablePath() {  	size_t size = readlink("/proc/self/exe", reinterpret_cast<char*>(path.getData()), path.getSize());  	if (size > 0) {  		path.resize(size); -		return boost::filesystem::path(path.toString().c_str()).parent_path(); +		return boost::filesystem::path(std::string(reinterpret_cast<const char*>(path.getData()), path.getSize()).c_str()).parent_path();  	}  #elif defined(SWIFTEN_PLATFORM_WINDOWS)  	ByteArray data;  	data.resize(2048);  	GetModuleFileName(NULL, reinterpret_cast<char*>(data.getData()), data.getSize()); -	return boost::filesystem::path(data.toString().c_str()).parent_path(); +	return boost::filesystem::path(std::string(reinterpret_cast<const char*>(data.getData()), data.getSize()).c_str()).parent_path();  #endif  	return boost::filesystem::path();  } diff --git a/Swiften/Base/Paths.h b/Swiften/Base/Paths.h index 06c6aeb..8ac4640 100644 --- a/Swiften/Base/Paths.h +++ b/Swiften/Base/Paths.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  namespace Swift {  	class Paths { diff --git a/Swiften/Base/Platform.h b/Swiften/Base/Platform.h index 32e2671..395747c 100644 --- a/Swiften/Base/Platform.h +++ b/Swiften/Base/Platform.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_Platform_H -#define SWIFTEN_Platform_H +#pragma once  // Base platforms  #if defined(linux) || defined(__linux) || defined(__linux__) @@ -26,6 +25,10 @@  #define SWIFTEN_PLATFORM_BEOS  #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)  #define SWIFTEN_PLATFORM_MACOSX +#include <TargetConditionals.h> +#  if defined(TARGET_OS_IPHONE) +#  define SWIFTEN_PLATFORM_IPHONE +#  endif  #elif defined(__IBMCPP__) || defined(_AIX)  #define SWIFTEN_PLATFORM_AIX  #elif defined(__amigaos__) @@ -46,5 +49,3 @@  #elif defined(BOOST_BIG_ENDIAN)  #define SWIFTEN_BIG_ENDIAN  #endif - -#endif diff --git a/Swiften/Base/String.h b/Swiften/Base/String.h index 192d53b..0de6cac 100644 --- a/Swiften/Base/String.h +++ b/Swiften/Base/String.h @@ -6,7 +6,6 @@  #pragma once -#include <map>  #include <string>  #include <vector> diff --git a/Swiften/Base/UnitTest/ByteArrayTest.cpp b/Swiften/Base/UnitTest/ByteArrayTest.cpp index cb10dd4..069e68e 100644 --- a/Swiften/Base/UnitTest/ByteArrayTest.cpp +++ b/Swiften/Base/UnitTest/ByteArrayTest.cpp @@ -8,12 +8,17 @@  #include <cppunit/extensions/TestFactoryRegistry.h>  #include "Swiften/Base/ByteArray.h" +#include <boost/lexical_cast.hpp>  using namespace Swift;  class ByteArrayTest : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(ByteArrayTest);  		CPPUNIT_TEST(testGetData_NoData); +		CPPUNIT_TEST(testToString); +		CPPUNIT_TEST(testToString_NullTerminated); +		CPPUNIT_TEST(testToString_TwoNullTerminated); +		CPPUNIT_TEST(testToString_AllNull);  		CPPUNIT_TEST_SUITE_END();  	public: @@ -22,6 +27,30 @@ class ByteArrayTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(reinterpret_cast<const char*>(NULL), reinterpret_cast<const char*>(testling.getData()));  		} + +		void testToString() { +			ByteArray testling(ByteArray::create("abcde")); + +			CPPUNIT_ASSERT_EQUAL(std::string("abcde"), testling.toString()); +		} + +		void testToString_NullTerminated() { +			ByteArray testling(ByteArray::create("abcde\0", 6)); + +			CPPUNIT_ASSERT_EQUAL(std::string("abcde"), testling.toString()); +		} + +		void testToString_TwoNullTerminated() { +			ByteArray testling(ByteArray::create("abcde\0\0", 7)); + +			CPPUNIT_ASSERT_EQUAL(std::string("abcde"), testling.toString()); +		} + +		void testToString_AllNull() { +			ByteArray testling(ByteArray::create("\0\0", 2)); + +			CPPUNIT_ASSERT_EQUAL(std::string(""), testling.toString()); +		}  };  CPPUNIT_TEST_SUITE_REGISTRATION(ByteArrayTest); diff --git a/Swiften/Base/foreach.h b/Swiften/Base/foreach.h index 05366d6..87f6147 100644 --- a/Swiften/Base/foreach.h +++ b/Swiften/Base/foreach.h @@ -4,12 +4,9 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_FOREACH_H -#define SWIFTEN_FOREACH_H +#pragma once  #include <boost/foreach.hpp>  #undef foreach  #define foreach BOOST_FOREACH - -#endif diff --git a/Swiften/Base/format.h b/Swiften/Base/format.h index 4591827..0e49eaa 100644 --- a/Swiften/Base/format.h +++ b/Swiften/Base/format.h @@ -7,6 +7,7 @@  #pragma once  #include <boost/format.hpp> +#include <iostream>  namespace Swift {  	inline boost::format format(const std::string& s) { diff --git a/Swiften/Base/sleep.h b/Swiften/Base/sleep.h index c2bc601..a95e907 100644 --- a/Swiften/Base/sleep.h +++ b/Swiften/Base/sleep.h @@ -4,11 +4,8 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_sleep_H -#define SWIFTEN_sleep_H +#pragma once  namespace Swift {  	void sleep(unsigned int msecs);  } - -#endif diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp index 5d7961b..aa16892 100644 --- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp +++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp @@ -8,6 +8,7 @@  #include <cppunit/extensions/TestFactoryRegistry.h>  #include <boost/bind.hpp> +#include <Swiften/Base/foreach.h>  #include "Swiften/Chat/ChatStateNotifier.h"  #include "Swiften/Client/DummyStanzaChannel.h"  #include "Swiften/Disco/DummyEntityCapsProvider.h" diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp index 7918c46..5a3450c 100644 --- a/Swiften/Client/Client.cpp +++ b/Swiften/Client/Client.cpp @@ -25,6 +25,7 @@  #include "Swiften/Presence/SubscriptionManager.h"  #include "Swiften/TLS/BlindCertificateTrustChecker.h"  #include <Swiften/Client/NickManagerImpl.h> +#include <Swiften/Client/ClientSession.h>  namespace Swift { @@ -35,7 +36,7 @@ Client::Client(const JID& jid, const std::string& password, NetworkFactories* ne  	softwareVersionResponder->start();  	roster = new XMPPRosterImpl(); -	rosterController = new XMPPRosterController(getIQRouter(), roster); +	rosterController = new XMPPRosterController(getIQRouter(), roster, getStorages()->getRosterStorage());  	subscriptionManager = new SubscriptionManager(getStanzaChannel()); @@ -98,6 +99,11 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers  }  void Client::requestRoster() { +	// FIXME: We should set this once when the session is finished, but there +	// is currently no callback for this +	if (getSession()) { +		rosterController->setUseVersioning(getSession()->getRosterVersioningSupported()); +	}  	rosterController->requestRoster();  } diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h index 083b8a0..05c1e6e 100644 --- a/Swiften/Client/Client.h +++ b/Swiften/Client/Client.h @@ -6,7 +6,7 @@  #pragma once -#include "Swiften/Client/CoreClient.h" +#include <Swiften/Client/CoreClient.h>  namespace Swift {  	class SoftwareVersionResponder; @@ -85,12 +85,12 @@ namespace Swift {  			/**  			 * Returns the last received presence for the given (full) JID.  			 */ -			Presence::ref getLastPresence(const JID& jid) const; +			boost::shared_ptr<Presence> getLastPresence(const JID& jid) const;  			/**  			 * Returns the presence with the highest priority received for the given JID.  			 */ -			Presence::ref getHighestPriorityPresence(const JID& bareJID) const; +			boost::shared_ptr<Presence> getHighestPriorityPresence(const JID& bareJID) const;  			PresenceOracle* getPresenceOracle() const {  				return presenceOracle; @@ -142,7 +142,7 @@ namespace Swift {  			/**  			 * This signal is emitted when a JID changes presence.  			 */ -			boost::signal<void (Presence::ref)> onPresenceChange; +			boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChange;  		private:  			Storages* getStorages() const; diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h new file mode 100644 index 0000000..1155b46 --- /dev/null +++ b/Swiften/Client/ClientOptions.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +struct ClientOptions { +	enum UseTLS { +		NeverUseTLS, +		UseTLSWhenAvailable +	}; + +	ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), useStreamResumption(false) { +	} + +	/** +	 * Whether ZLib stream compression should be used when available. +	 * +	 * Default: true +	 */ +	bool useStreamCompression; + +	/** +	 * Sets whether TLS encryption should be used. +	 * +	 * Default: UseTLSWhenAvailable +	 */ +	UseTLS useTLS; + +	/** +	 * Use XEP-196 stream resumption when available. +	 * +	 * Default: false +	 */ +	bool useStreamResumption; +}; + diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index e1c1d8e..cadc9a4 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -54,6 +54,7 @@ ClientSession::ClientSession(  			needSessionStart(false),  			needResourceBind(false),  			needAcking(false), +			rosterVersioningSupported(false),  			authenticator(NULL),  			certificateTrustChecker(NULL) {  } @@ -223,6 +224,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {  		}  		else {  			// Start the session +			rosterVersioningSupported = streamFeatures->hasRosterVersioning();  			stream->setWhitespacePingEnabled(true);  			needSessionStart = streamFeatures->hasSession();  			needResourceBind = streamFeatures->hasResourceBind(); diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index 25ee694..4447f57 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -89,6 +89,10 @@ namespace Swift {  				return stanzaAckRequester_;  			} +			bool getRosterVersioningSupported() const { +				return rosterVersioningSupported; +			} +  			const JID& getLocalJID() const {  				return localJID;  			} @@ -153,6 +157,7 @@ namespace Swift {  			bool needSessionStart;  			bool needResourceBind;  			bool needAcking; +			bool rosterVersioningSupported;  			ClientAuthenticator* authenticator;  			boost::shared_ptr<StanzaAckRequester> stanzaAckRequester_;  			boost::shared_ptr<StanzaAckResponder> stanzaAckResponder_; diff --git a/Swiften/Client/ClientSessionStanzaChannel.cpp b/Swiften/Client/ClientSessionStanzaChannel.cpp index 6b32b3d..85d9dec 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.cpp +++ b/Swiften/Client/ClientSessionStanzaChannel.cpp @@ -7,6 +7,7 @@  #include "Swiften/Client/ClientSessionStanzaChannel.h"  #include <boost/bind.hpp> +#include <iostream>  namespace Swift { diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp new file mode 100644 index 0000000..a26ce66 --- /dev/null +++ b/Swiften/Client/ClientXMLTracer.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/ClientXMLTracer.h> + +#include <iostream> +#include <boost/bind.hpp> + +namespace Swift { + +ClientXMLTracer::ClientXMLTracer(CoreClient* client) { +	client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1)); +	client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1)); +} + +void ClientXMLTracer::printData(char direction, const std::string& data) { +	printLine(direction); +	std::cerr << data << std::endl; +} + +void ClientXMLTracer::printLine(char c) { +	for (unsigned int i = 0; i < 80; ++i) { +		std::cerr << c; +	} +	std::cerr << std::endl; +} + +} diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h index bca2a54..617c53f 100644 --- a/Swiften/Client/ClientXMLTracer.h +++ b/Swiften/Client/ClientXMLTracer.h @@ -6,29 +6,15 @@  #pragma once -#include <boost/bind.hpp> -  #include <Swiften/Client/CoreClient.h>  namespace Swift {  	class ClientXMLTracer {  		public: -			ClientXMLTracer(CoreClient* client) { -				client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1)); -				client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1)); -			} +			ClientXMLTracer(CoreClient* client);  		private: -			static void printData(char direction, const std::string& data) { -				printLine(direction); -				std::cerr << data << std::endl; -			} - -			static void printLine(char c) { -				for (unsigned int i = 0; i < 80; ++i) { -					std::cerr << c; -				} -				std::cerr << std::endl; -			} +			static void printData(char direction, const std::string& data); +			static void printLine(char c);  	};  } diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index f0c5333..de40517 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -7,11 +7,12 @@  #include "Swiften/Client/CoreClient.h"  #include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp>  #include "Swiften/Client/ClientSession.h"  #include "Swiften/TLS/PlatformTLSFactories.h"  #include "Swiften/TLS/CertificateVerificationError.h" -#include "Swiften/Network/Connector.h" +#include <Swiften/Network/ChainedConnector.h>  #include "Swiften/Network/NetworkFactories.h"  #include "Swiften/TLS/PKCS12Certificate.h"  #include "Swiften/Session/BasicSessionStream.h" @@ -19,10 +20,14 @@  #include "Swiften/Base/IDGenerator.h"  #include "Swiften/Client/ClientSessionStanzaChannel.h"  #include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include "Swiften/Network/PlatformProxyProvider.h" +#include "Swiften/Network/SOCKS5ProxiedConnectionFactory.h" +#include "Swiften/Network/HTTPConnectProxiedConnectionFactory.h"  namespace Swift { -CoreClient::CoreClient(const JID& jid, const std::string& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), useStreamCompression(true), useTLS(UseTLSWhenAvailable), disconnectRequested_(false), certificateTrustChecker(NULL) { +CoreClient::CoreClient(const JID& jid, const std::string& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) {  	stanzaChannel_ = new ClientSessionStanzaChannel();  	stanzaChannel_->onMessageReceived.connect(boost::bind(&CoreClient::handleMessageReceived, this, _1));  	stanzaChannel_->onPresenceReceived.connect(boost::bind(&CoreClient::handlePresenceReceived, this, _1)); @@ -47,8 +52,9 @@ CoreClient::~CoreClient() {  	delete stanzaChannel_;  } -void CoreClient::connect() { +void CoreClient::connect(const ClientOptions& o) {  	SWIFT_LOG(debug) << "Connecting" << std::endl; +	options = o;  	connect(jid_.getDomain());  } @@ -56,7 +62,19 @@ void CoreClient::connect(const std::string& host) {  	SWIFT_LOG(debug) << "Connecting to host " << host << std::endl;  	disconnectRequested_ = false;  	assert(!connector_); -	connector_ = Connector::create(host, networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory()); + +	assert(proxyConnectionFactories.empty()); +	PlatformProxyProvider proxyProvider; +	if(proxyProvider.getSOCKS5Proxy().isValid()) { +		proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getSOCKS5Proxy())); +	} +	if(proxyProvider.getHTTPConnectProxy().isValid()) { +		proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getHTTPConnectProxy())); +	} +	std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories); +	connectionFactories.push_back(networkFactories->getConnectionFactory()); + +	connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory());  	connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));  	connector_->setTimeoutMilliseconds(60*1000);  	connector_->start(); @@ -65,6 +83,10 @@ void CoreClient::connect(const std::string& host) {  void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) {  	connector_->onConnectFinished.disconnect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));  	connector_.reset(); +	foreach(ConnectionFactory* f, proxyConnectionFactories) { +		delete f; +	} +  	if (!connection) {  		onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError));  	} @@ -82,12 +104,12 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio  		session_ = ClientSession::create(jid_, sessionStream_);  		session_->setCertificateTrustChecker(certificateTrustChecker); -		session_->setUseStreamCompression(useStreamCompression); -		switch(useTLS) { -			case UseTLSWhenAvailable: +		session_->setUseStreamCompression(options.useStreamCompression); +		switch(options.useTLS) { +			case ClientOptions::UseTLSWhenAvailable:  				session_->setUseTLS(ClientSession::UseTLSWhenAvailable);  				break; -			case NeverUseTLS: +			case ClientOptions::NeverUseTLS:  				session_->setUseTLS(ClientSession::NeverUseTLS);  				break;  		} @@ -275,13 +297,25 @@ void CoreClient::handleStanzaAcked(Stanza::ref stanza) {  	onStanzaAcked(stanza);  } -void CoreClient::setUseStreamCompression(bool b) { -	useStreamCompression = b; +bool CoreClient::isAvailable() const { +	return stanzaChannel_->isAvailable();  } -void CoreClient::setUseTLS(UseTLS b) { -	useTLS = b; +bool CoreClient::getStreamManagementEnabled() const { +	return stanzaChannel_->getStreamManagementEnabled();  } +StanzaChannel* CoreClient::getStanzaChannel() const { +	return stanzaChannel_; +} + +const JID& CoreClient::getJID() const { +	if (session_) { +		return session_->getLocalJID(); +	} +	else { +		return jid_; +	} +}  } diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h index eb9c42c..7c46fe7 100644 --- a/Swiften/Client/CoreClient.h +++ b/Swiften/Client/CoreClient.h @@ -6,35 +6,33 @@  #pragma once -#include "Swiften/Base/boost_bsignals.h" +#include <string>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/boost_bsignals.h> -#include "Swiften/Network/PlatformDomainNameResolver.h" -#include "Swiften/Network/Connector.h" -#include "Swiften/Base/Error.h" -#include "Swiften/Client/ClientSession.h" -#include "Swiften/Client/ClientError.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/JID/JID.h" -#include <string> -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" -#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"  #include <Swiften/Entity/Entity.h> - -#include "Swiften/Client/ClientSessionStanzaChannel.h" +#include <Swiften/JID/JID.h> +#include <Swiften/Client/ClientError.h> +#include <Swiften/Client/ClientOptions.h>  namespace Swift { +	class ChainedConnector; +	class Message; +	class Presence; +	class Error;  	class IQRouter;  	class TLSContextFactory;  	class ConnectionFactory; +	class Connection;  	class TimerFactory;  	class ClientSession; +	class StanzaChannel; +	class Stanza;  	class BasicSessionStream;  	class PlatformTLSFactories;  	class CertificateTrustChecker;  	class NetworkFactories; +	class ClientSessionStanzaChannel;  	/**   	 * The central class for communicating with an XMPP server. @@ -48,11 +46,6 @@ namespace Swift {  	 */  	class CoreClient : public Entity {  		public:  -			enum UseTLS { -				NeverUseTLS, -				UseTLSWhenAvailable -			}; -  			/**  			 * Constructs a client for the given JID with the given password.  			 * The given eventLoop will be used to post events to. @@ -68,7 +61,7 @@ namespace Swift {  			 * After the connection is established, the client will set   			 * initialize the stream and authenticate.  			 */ -			void connect(); +			void connect(const ClientOptions& = ClientOptions());  			/**  			 * Disconnects the client from the server. @@ -80,12 +73,12 @@ namespace Swift {  			/**  			 * Sends a message.  			 */ -			void sendMessage(Message::ref); +			void sendMessage(boost::shared_ptr<Message>);  			/**  			 * Sends a presence stanza.  			 */ -			void sendPresence(Presence::ref); +			void sendPresence(boost::shared_ptr<Presence>);  			/**  			 * Sends raw, unchecked data. @@ -103,9 +96,7 @@ namespace Swift {  			 * Checks whether the client is connected to the server,  			 * and stanzas can be sent.  			 */ -			bool isAvailable() const { -				return stanzaChannel_->isAvailable(); -			} +			bool isAvailable() const;  			/**  			 * Checks whether the client is active. @@ -118,14 +109,7 @@ namespace Swift {  			 * Returns the JID of the client.   			 * After the session was initialized, this returns the bound JID.  			 */ -			const JID& getJID() const { -				if (session_) { -					return session_->getLocalJID(); -				} -				else { -					return jid_; -				} -			} +			const JID& getJID() const;  			/**  			 * Checks whether stream management is enabled. @@ -135,13 +119,9 @@ namespace Swift {  			 *  			 * \see onStanzaAcked  			 */ -			bool getStreamManagementEnabled() const { -				return stanzaChannel_->getStreamManagementEnabled(); -			} +			bool getStreamManagementEnabled() const; -			StanzaChannel* getStanzaChannel() const { -				return stanzaChannel_; -			} +			StanzaChannel* getStanzaChannel() const;  			/**  			 * Sets the certificate trust checker. @@ -153,16 +133,6 @@ namespace Swift {  			 */  			void setCertificateTrustChecker(CertificateTrustChecker*); -			/** -			 * Sets whether ZLib stream compression should be used when available. -			 */ -			void setUseStreamCompression(bool b); - -			/** -			 * Sets whether TLS encryption should be used. -			 */ -			void setUseTLS(UseTLS useTLS); -  		public:  			/**  			 * Emitted when the client was disconnected from the network. @@ -197,12 +167,12 @@ namespace Swift {  			/**  			 * Emitted when a message is received.  			 */ -			boost::signal<void (Message::ref)> onMessageReceived; +			boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived;  			/**  			 * Emitted when a presence stanza is received.  			 */ -			boost::signal<void (Presence::ref) > onPresenceReceived; +			boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived;  			/**  			 * Emitted when the server acknowledges receipt of a @@ -210,7 +180,12 @@ namespace Swift {  			 *  			 * \see getStreamManagementEnabled()  			 */ -			boost::signal<void (Stanza::ref)> onStanzaAcked; +			boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked; + +		protected: +			boost::shared_ptr<ClientSession> getSession() const { +				return session_; +			}  		private:  			void handleConnectorFinished(boost::shared_ptr<Connection>); @@ -219,19 +194,19 @@ namespace Swift {  			void handleNeedCredentials();  			void handleDataRead(const std::string&);  			void handleDataWritten(const std::string&); -			void handlePresenceReceived(Presence::ref); -			void handleMessageReceived(Message::ref); -			void handleStanzaAcked(Stanza::ref); +			void handlePresenceReceived(boost::shared_ptr<Presence>); +			void handleMessageReceived(boost::shared_ptr<Message>); +			void handleStanzaAcked(boost::shared_ptr<Stanza>);  		private:  			JID jid_;  			std::string password_;  			NetworkFactories* networkFactories; -			bool useStreamCompression; -			UseTLS useTLS;  			ClientSessionStanzaChannel* stanzaChannel_;  			IQRouter* iqRouter_; -			Connector::ref connector_; +			ClientOptions options; +			boost::shared_ptr<ChainedConnector> connector_; +			std::vector<ConnectionFactory*> proxyConnectionFactories;  			PlatformTLSFactories* tlsFactories;  			boost::shared_ptr<Connection> connection_;  			boost::shared_ptr<BasicSessionStream> sessionStream_; diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h index b9f05c3..306e2b4 100644 --- a/Swiften/Client/DummyStanzaChannel.h +++ b/Swiften/Client/DummyStanzaChannel.h @@ -56,6 +56,22 @@ namespace Swift {  				return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload<T>();  			} +			bool isResultAtIndex(size_t index, const std::string& id) { +				if (index >= sentStanzas.size()) { +					return false; +				} +				boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]); +				return iqStanza && iqStanza->getType() == IQ::Result && iqStanza->getID() == id; +			} + +			bool isErrorAtIndex(size_t index, const std::string& id) { +				if (index >= sentStanzas.size()) { +					return false; +				} +				boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]); +				return iqStanza && iqStanza->getType() == IQ::Error && iqStanza->getID() == id; +			} +  			template<typename T> boost::shared_ptr<T> getStanzaAtIndex(size_t index) {  				if (sentStanzas.size() <= index) {  					return boost::shared_ptr<T>(); diff --git a/Swiften/Client/MemoryStorages.cpp b/Swiften/Client/MemoryStorages.cpp index 5f6371b..6941add 100644 --- a/Swiften/Client/MemoryStorages.cpp +++ b/Swiften/Client/MemoryStorages.cpp @@ -8,6 +8,7 @@  #include "Swiften/VCards/VCardMemoryStorage.h"  #include "Swiften/Avatars/AvatarMemoryStorage.h"  #include "Swiften/Disco/CapsMemoryStorage.h" +#include "Swiften/Roster/RosterMemoryStorage.h"  namespace Swift { @@ -15,9 +16,11 @@ MemoryStorages::MemoryStorages() {  	vcardStorage = new VCardMemoryStorage();  	capsStorage = new CapsMemoryStorage();  	avatarStorage = new AvatarMemoryStorage(); +	rosterStorage = new RosterMemoryStorage();  }  MemoryStorages::~MemoryStorages() { +	delete rosterStorage;  	delete avatarStorage;  	delete capsStorage;  	delete vcardStorage; @@ -35,4 +38,9 @@ AvatarStorage* MemoryStorages::getAvatarStorage() const {  	return avatarStorage;  } +RosterStorage* MemoryStorages::getRosterStorage() const { +	return rosterStorage; +} + +  } diff --git a/Swiften/Client/MemoryStorages.h b/Swiften/Client/MemoryStorages.h index 67025cd..1e1a596 100644 --- a/Swiften/Client/MemoryStorages.h +++ b/Swiften/Client/MemoryStorages.h @@ -23,10 +23,12 @@ namespace Swift {  			virtual VCardStorage* getVCardStorage() const;  			virtual AvatarStorage* getAvatarStorage() const;  			virtual CapsStorage* getCapsStorage() const; +			virtual RosterStorage* getRosterStorage() const;  		private:  			VCardMemoryStorage* vcardStorage;  			AvatarStorage* avatarStorage;  			CapsStorage* capsStorage; +			RosterStorage* rosterStorage;  	};  } diff --git a/Swiften/Client/NickResolver.h b/Swiften/Client/NickResolver.h index 881362a..bf373fa 100644 --- a/Swiften/Client/NickResolver.h +++ b/Swiften/Client/NickResolver.h @@ -5,9 +5,9 @@   */  #include <map> -#include <boost/signals.hpp>  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/boost_bsignals.h>  #include <string>  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/VCard.h" diff --git a/Swiften/Client/Storages.cpp b/Swiften/Client/Storages.cpp new file mode 100644 index 0000000..3c2dbc5 --- /dev/null +++ b/Swiften/Client/Storages.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/Storages.h> + +using namespace Swift; + +Storages::~Storages() { +} diff --git a/Swiften/Client/Storages.h b/Swiften/Client/Storages.h index e62f0a9..1c5bbe9 100644 --- a/Swiften/Client/Storages.h +++ b/Swiften/Client/Storages.h @@ -10,6 +10,7 @@ namespace Swift {  	class VCardStorage;  	class AvatarStorage;  	class CapsStorage; +	class RosterStorage;  	/**  	 * An interface to hold storage classes for different @@ -17,10 +18,11 @@ namespace Swift {  	 */  	class Storages {  		public: -			virtual ~Storages() {} +			virtual ~Storages();  			virtual VCardStorage* getVCardStorage() const = 0;  			virtual AvatarStorage* getAvatarStorage() const = 0;  			virtual CapsStorage* getCapsStorage() const = 0; +			virtual RosterStorage* getRosterStorage() const = 0;  	};  } diff --git a/Swiften/Component/ComponentSessionStanzaChannel.cpp b/Swiften/Component/ComponentSessionStanzaChannel.cpp index b9fecb2..b342714 100644 --- a/Swiften/Component/ComponentSessionStanzaChannel.cpp +++ b/Swiften/Component/ComponentSessionStanzaChannel.cpp @@ -7,6 +7,7 @@  #include "Swiften/Component/ComponentSessionStanzaChannel.h"  #include <boost/bind.hpp> +#include <iostream>  namespace Swift { diff --git a/Swiften/Component/ComponentXMLTracer.cpp b/Swiften/Component/ComponentXMLTracer.cpp new file mode 100644 index 0000000..b952c29 --- /dev/null +++ b/Swiften/Component/ComponentXMLTracer.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Component/ComponentXMLTracer.h> + +#include <iostream> +#include <boost/bind.hpp> + +namespace Swift { + +ComponentXMLTracer::ComponentXMLTracer(CoreComponent* client) { +	client->onDataRead.connect(boost::bind(&ComponentXMLTracer::printData, '<', _1)); +	client->onDataWritten.connect(boost::bind(&ComponentXMLTracer::printData, '>', _1)); +} + +void ComponentXMLTracer::printData(char direction, const std::string& data) { +	printLine(direction); +	std::cerr << data << std::endl; +} + +void ComponentXMLTracer::printLine(char c) { +	for (unsigned int i = 0; i < 80; ++i) { +		std::cerr << c; +	} +	std::cerr << std::endl; +} + +} diff --git a/Swiften/Component/ComponentXMLTracer.h b/Swiften/Component/ComponentXMLTracer.h index 70a617b..d7e2b46 100644 --- a/Swiften/Component/ComponentXMLTracer.h +++ b/Swiften/Component/ComponentXMLTracer.h @@ -6,29 +6,15 @@  #pragma once -#include <boost/bind.hpp> -  #include "Swiften/Component/Component.h"  namespace Swift {  	class ComponentXMLTracer {  		public: -			ComponentXMLTracer(Component* component) { -				component->onDataRead.connect(boost::bind(&ComponentXMLTracer::printData, '<', _1)); -				component->onDataWritten.connect(boost::bind(&ComponentXMLTracer::printData, '>', _1)); -			} +			ComponentXMLTracer(CoreComponent* component);  		private: -			static void printData(char direction, const std::string& data) { -				printLine(direction); -				std::cerr << data << std::endl; -			} - -			static void printLine(char c) { -				for (unsigned int i = 0; i < 80; ++i) { -					std::cerr << c; -				} -				std::cerr << std::endl; -			} +			static void printData(char direction, const std::string& data); +			static void printLine(char c);  	};  } diff --git a/Swiften/Component/CoreComponent.cpp b/Swiften/Component/CoreComponent.cpp index e79d735..f995ab0 100644 --- a/Swiften/Component/CoreComponent.cpp +++ b/Swiften/Component/CoreComponent.cpp @@ -7,6 +7,7 @@  #include "Swiften/Component/CoreComponent.h"  #include <boost/bind.hpp> +#include <iostream>  #include "Swiften/Component/ComponentSession.h"  #include "Swiften/Network/Connector.h" diff --git a/Swiften/Component/SConscript b/Swiften/Component/SConscript index 0a9f250..ef5700c 100644 --- a/Swiften/Component/SConscript +++ b/Swiften/Component/SConscript @@ -7,6 +7,7 @@ sources = [  		"ComponentSessionStanzaChannel.cpp",  		"CoreComponent.cpp",  		"Component.cpp", +		"ComponentXMLTracer.cpp",  	]  swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) diff --git a/Swiften/Compress/ZLibCompressor.cpp b/Swiften/Compress/ZLibCompressor.cpp index 7e3116e..b740c68 100644 --- a/Swiften/Compress/ZLibCompressor.cpp +++ b/Swiften/Compress/ZLibCompressor.cpp @@ -6,6 +6,8 @@  #include "Swiften/Compress/ZLibCompressor.h" +#include <cassert> +  #pragma GCC diagnostic ignored "-Wold-style-cast"  namespace Swift { diff --git a/Swiften/Compress/ZLibCompressor.h b/Swiften/Compress/ZLibCompressor.h index 7fe5387..e0b4759 100644 --- a/Swiften/Compress/ZLibCompressor.h +++ b/Swiften/Compress/ZLibCompressor.h @@ -6,8 +6,6 @@  #pragma once -#include <cassert> -  #include "Swiften/Compress/ZLibCodecompressor.h"  #include "Swiften/Base/ByteArray.h" diff --git a/Swiften/Compress/ZLibDecompressor.cpp b/Swiften/Compress/ZLibDecompressor.cpp index af7349b..78e9846 100644 --- a/Swiften/Compress/ZLibDecompressor.cpp +++ b/Swiften/Compress/ZLibDecompressor.cpp @@ -6,6 +6,8 @@  #include "Swiften/Compress/ZLibDecompressor.h" +#include <cassert> +  #pragma GCC diagnostic ignored "-Wold-style-cast"  namespace Swift { diff --git a/Swiften/Compress/ZLibDecompressor.h b/Swiften/Compress/ZLibDecompressor.h index ec08a4f..917e1b7 100644 --- a/Swiften/Compress/ZLibDecompressor.h +++ b/Swiften/Compress/ZLibDecompressor.h @@ -6,8 +6,6 @@  #pragma once -#include <cassert> -  #include "Swiften/Compress/ZLibCodecompressor.h"  #include "Swiften/Base/ByteArray.h" diff --git a/Swiften/Config/swiften-config.cpp b/Swiften/Config/swiften-config.cpp index b3875cb..0c46cf0 100644 --- a/Swiften/Config/swiften-config.cpp +++ b/Swiften/Config/swiften-config.cpp @@ -11,6 +11,7 @@  #include <boost/program_options/variables_map.hpp>  #include <boost/program_options.hpp>  #include <boost/version.hpp> +#include <boost/filesystem.hpp>  #include <string>  #include <Swiften/Base/Platform.h> diff --git a/Swiften/Disco/CapsFileStorage.cpp b/Swiften/Disco/CapsFileStorage.cpp deleted file mode 100644 index 1e53854..0000000 --- a/Swiften/Disco/CapsFileStorage.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "Swiften/Disco/CapsFileStorage.h" - -#include <iostream> -#include <boost/filesystem/fstream.hpp> - -#include "Swiften/Base/ByteArray.h" -#include "Swiften/Serializer/PayloadSerializers/DiscoInfoSerializer.h" -#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h" -#include "Swiften/Parser/PayloadParsers/DiscoInfoParser.h" -#include "Swiften/StringCodecs/Hexify.h" -#include "Swiften/StringCodecs/Base64.h" - -namespace Swift { - -CapsFileStorage::CapsFileStorage(const boost::filesystem::path& path) : path(path) { -} - -DiscoInfo::ref CapsFileStorage::getDiscoInfo(const std::string& hash) const { -	boost::filesystem::path capsPath(getCapsPath(hash)); -	if (boost::filesystem::exists(capsPath)) { -		ByteArray data; -		data.readFromFile(capsPath.string()); - -		DiscoInfoParser parser; -		PayloadParserTester tester(&parser); -		tester.parse(data.toString()); -		return boost::dynamic_pointer_cast<DiscoInfo>(parser.getPayload()); -	} -	else { -		return DiscoInfo::ref(); -	} -} - -void CapsFileStorage::setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { -	boost::filesystem::path capsPath(getCapsPath(hash)); -	if (!boost::filesystem::exists(capsPath.parent_path())) { -		try { -			boost::filesystem::create_directories(capsPath.parent_path()); -		} -		catch (const boost::filesystem::filesystem_error& e) { -			std::cerr << "ERROR: " << e.what() << std::endl; -		} -	} -	DiscoInfo::ref bareDiscoInfo(new DiscoInfo(*discoInfo.get())); -	bareDiscoInfo->setNode(""); -	boost::filesystem::ofstream file(capsPath); -	file << DiscoInfoSerializer().serializePayload(bareDiscoInfo); -	file.close(); -} - -boost::filesystem::path CapsFileStorage::getCapsPath(const std::string& hash) const { -	return path / (Hexify::hexify(Base64::decode(hash)) + ".xml"); -} - -} diff --git a/Swiften/Disco/CapsManager.cpp b/Swiften/Disco/CapsManager.cpp index 63166e6..6eb7c17 100644 --- a/Swiften/Disco/CapsManager.cpp +++ b/Swiften/Disco/CapsManager.cpp @@ -7,6 +7,7 @@  #include "Swiften/Disco/CapsManager.h"  #include <boost/bind.hpp> +#include <iostream>  #include "Swiften/Client/StanzaChannel.h"  #include "Swiften/Disco/CapsStorage.h" diff --git a/Swiften/Disco/DummyEntityCapsProvider.cpp b/Swiften/Disco/DummyEntityCapsProvider.cpp new file mode 100644 index 0000000..a906652 --- /dev/null +++ b/Swiften/Disco/DummyEntityCapsProvider.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Disco/DummyEntityCapsProvider.h> + +#include <iostream> + +namespace Swift { + +DiscoInfo::ref DummyEntityCapsProvider::getCaps(const JID& jid) const { +	std::map<JID, DiscoInfo::ref>::const_iterator i = caps.find(jid); +	if (i != caps.end()) { +		return i->second; +	} +	return DiscoInfo::ref(); +} + +} diff --git a/Swiften/Disco/DummyEntityCapsProvider.h b/Swiften/Disco/DummyEntityCapsProvider.h index 68cef2f..1bd4bb9 100644 --- a/Swiften/Disco/DummyEntityCapsProvider.h +++ b/Swiften/Disco/DummyEntityCapsProvider.h @@ -7,7 +7,7 @@  #pragma once  #include <map> -#include <iostream> +  #include "Swiften/Disco/EntityCapsProvider.h"  namespace Swift { @@ -16,13 +16,7 @@ namespace Swift {  			DummyEntityCapsProvider() {  			} -			DiscoInfo::ref getCaps(const JID& jid) const { -				std::map<JID, DiscoInfo::ref>::const_iterator i = caps.find(jid); -				if (i != caps.end()) { -					return i->second; -				} -				return DiscoInfo::ref(); -			} +			DiscoInfo::ref getCaps(const JID& jid) const;  			std::map<JID, DiscoInfo::ref> caps;  	}; diff --git a/Swiften/Disco/GetDiscoItemsRequest.h b/Swiften/Disco/GetDiscoItemsRequest.h index 0a94402..46735ef 100644 --- a/Swiften/Disco/GetDiscoItemsRequest.h +++ b/Swiften/Disco/GetDiscoItemsRequest.h @@ -18,9 +18,18 @@ namespace Swift {  				return ref(new GetDiscoItemsRequest(jid, router));  			} +			static ref create(const JID& jid, const std::string& node, IQRouter* router) { +				return ref(new GetDiscoItemsRequest(jid, node, router)); +			} +  		private:  			GetDiscoItemsRequest(const JID& jid, IQRouter* router) :  					GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) {  			} + +			GetDiscoItemsRequest(const JID& jid, const std::string& node, IQRouter* router) : +				GenericRequest<DiscoItems>(IQ::Get, jid, boost::shared_ptr<DiscoItems>(new DiscoItems()), router) { +				getPayloadGeneric()->setNode(node); +			}  	};  } diff --git a/Swiften/Disco/SConscript b/Swiften/Disco/SConscript index 9982192..434018a 100644 --- a/Swiften/Disco/SConscript +++ b/Swiften/Disco/SConscript @@ -5,8 +5,8 @@ objects = swiften_env.SwiftenObject([  			"CapsManager.cpp",  			"EntityCapsManager.cpp",  			"EntityCapsProvider.cpp", +			"DummyEntityCapsProvider.cpp",  			"CapsStorage.cpp", -			"CapsFileStorage.cpp",  			"ClientDiscoManager.cpp",  			"DiscoInfoResponder.cpp",  			"JIDDiscoInfoResponder.cpp", diff --git a/Swiften/Elements/Body.h b/Swiften/Elements/Body.h index 2887390..a2497f7 100644 --- a/Swiften/Elements/Body.h +++ b/Swiften/Elements/Body.h @@ -6,14 +6,13 @@  #pragma once -#include "Swiften/Elements/Payload.h"  #include <string> +#include <Swiften/Elements/Payload.h> +  namespace Swift {  	class Body : public Payload {  		public: -			typedef boost::shared_ptr<Body> ref; -  			Body(const std::string& text = "") : text_(text) {  			} diff --git a/Swiften/Elements/Bytestreams.h b/Swiften/Elements/Bytestreams.h index b493375..211396b 100644 --- a/Swiften/Elements/Bytestreams.h +++ b/Swiften/Elements/Bytestreams.h @@ -9,9 +9,9 @@  #include <vector>  #include <boost/optional.hpp>  #include <boost/shared_ptr.hpp> +#include <string>  #include "Swiften/JID/JID.h" -#include <string>  #include "Swiften/Elements/Payload.h"  namespace Swift { diff --git a/Swiften/Elements/CapsInfo.h b/Swiften/Elements/CapsInfo.h index ccad278..f1e2c37 100644 --- a/Swiften/Elements/CapsInfo.h +++ b/Swiften/Elements/CapsInfo.h @@ -7,8 +7,8 @@  #pragma once  #include <boost/shared_ptr.hpp> -  #include <string> +  #include "Swiften/Elements/Payload.h"  namespace Swift { diff --git a/Swiften/Elements/ChatState.h b/Swiften/Elements/ChatState.h index 2896877..ddeed79 100644 --- a/Swiften/Elements/ChatState.h +++ b/Swiften/Elements/ChatState.h @@ -7,6 +7,7 @@  #pragma once  #include <string> +  #include "Swiften/Elements/Payload.h"  namespace Swift { diff --git a/Swiften/Elements/Command.h b/Swiften/Elements/Command.h index f4059a8..4a9c2a3 100644 --- a/Swiften/Elements/Command.h +++ b/Swiften/Elements/Command.h @@ -7,8 +7,8 @@  #pragma once  #include <boost/shared_ptr.hpp> -  #include <string> +  #include "Swiften/Elements/Payload.h"  #include "Swiften/Elements/Form.h" diff --git a/Swiften/Elements/ComponentHandshake.h b/Swiften/Elements/ComponentHandshake.h index 6047eab..3fe0457 100644 --- a/Swiften/Elements/ComponentHandshake.h +++ b/Swiften/Elements/ComponentHandshake.h @@ -7,9 +7,9 @@  #pragma once  #include <boost/shared_ptr.hpp> +#include <string>  #include "Swiften/Elements/Element.h" -#include <string>  namespace Swift {  	class ComponentHandshake : public Element { diff --git a/Swiften/Elements/Delay.h b/Swiften/Elements/Delay.h index 3213037..85d167b 100644 --- a/Swiften/Elements/Delay.h +++ b/Swiften/Elements/Delay.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp>  #include <boost/optional.hpp>  #include "Swiften/Elements/Payload.h" diff --git a/Swiften/Elements/DiscoInfo.cpp b/Swiften/Elements/DiscoInfo.cpp index f0e728e..5b2bb04 100644 --- a/Swiften/Elements/DiscoInfo.cpp +++ b/Swiften/Elements/DiscoInfo.cpp @@ -6,12 +6,15 @@  #include "Swiften/Elements/DiscoInfo.h" +#include <algorithm> +  namespace Swift {  const std::string DiscoInfo::ChatStatesFeature = std::string("http://jabber.org/protocol/chatstates");  const std::string DiscoInfo::SecurityLabelsFeature = std::string("urn:xmpp:sec-label:0");  const std::string DiscoInfo::SecurityLabelsCatalogFeature = std::string("urn:xmpp:sec-label:catalog:2");  const std::string DiscoInfo::JabberSearchFeature = std::string("jabber:iq:search"); +const std::string DiscoInfo::CommandsFeature = std::string("http://jabber.org/protocol/commands");  bool DiscoInfo::Identity::operator<(const Identity& other) const { @@ -33,4 +36,8 @@ bool DiscoInfo::Identity::operator<(const Identity& other) const {  	}  } +bool DiscoInfo::hasFeature(const std::string& feature) const { +	return std::find(features_.begin(), features_.end(), feature) != features_.end(); +} +  } diff --git a/Swiften/Elements/DiscoInfo.h b/Swiften/Elements/DiscoInfo.h index d5bf64a..cd650b9 100644 --- a/Swiften/Elements/DiscoInfo.h +++ b/Swiften/Elements/DiscoInfo.h @@ -7,11 +7,9 @@  #pragma once  #include <vector> -#include <algorithm> - -#include "Swiften/Elements/Payload.h"  #include <string> +#include "Swiften/Elements/Payload.h"  #include "Swiften/Elements/Form.h"  namespace Swift { @@ -23,6 +21,7 @@ namespace Swift {  			static const std::string SecurityLabelsFeature;  			static const std::string SecurityLabelsCatalogFeature;  			static const std::string JabberSearchFeature; +			static const std::string CommandsFeature;  			class Identity {  				public: @@ -82,9 +81,7 @@ namespace Swift {  				features_.push_back(feature);  			} -			bool hasFeature(const std::string& feature) const { -				return std::find(features_.begin(), features_.end(), feature) != features_.end(); -			} +			bool hasFeature(const std::string& feature) const;  			void addExtension(Form::ref form) {  				extensions_.push_back(form); diff --git a/Swiften/Elements/DiscoItems.h b/Swiften/Elements/DiscoItems.h index cc5a583..1b7063b 100644 --- a/Swiften/Elements/DiscoItems.h +++ b/Swiften/Elements/DiscoItems.h @@ -7,10 +7,9 @@  #pragma once  #include <vector> -#include <algorithm> +#include <string>  #include "Swiften/Elements/Payload.h" -#include <string>  #include "Swiften/JID/JID.h"  namespace Swift { diff --git a/Swiften/Elements/Element.h b/Swiften/Elements/Element.h index aded528..1e6a9d0 100644 --- a/Swiften/Elements/Element.h +++ b/Swiften/Elements/Element.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_ELEMENT_H -#define SWIFTEN_ELEMENT_H +#pragma once  namespace Swift {  	class Element { @@ -13,5 +12,3 @@ namespace Swift {  			virtual ~Element();  	};  } - -#endif diff --git a/Swiften/Elements/ErrorPayload.h b/Swiften/Elements/ErrorPayload.h index 12ad574..cece81f 100644 --- a/Swiften/Elements/ErrorPayload.h +++ b/Swiften/Elements/ErrorPayload.h @@ -7,9 +7,9 @@  #pragma once  #include <boost/shared_ptr.hpp> +#include <string>  #include "Swiften/Elements/Payload.h" -#include <string>  namespace Swift {  	class ErrorPayload : public Payload { diff --git a/Swiften/Elements/Form.h b/Swiften/Elements/Form.h index 1c50f0c..4e6e9f1 100644 --- a/Swiften/Elements/Form.h +++ b/Swiften/Elements/Form.h @@ -7,11 +7,10 @@  #pragma once  #include <vector> +#include <string>  #include "Swiften/Elements/Payload.h"  #include "Swiften/Elements/FormField.h" -#include <string> -  #include "Swiften/JID/JID.h"  namespace Swift { diff --git a/Swiften/Elements/FormField.h b/Swiften/Elements/FormField.h index f455303..2438bb3 100644 --- a/Swiften/Elements/FormField.h +++ b/Swiften/Elements/FormField.h @@ -11,8 +11,8 @@  #include <vector>  #include <boost/shared_ptr.hpp> -  #include <string> +  #include "Swiften/JID/JID.h"  namespace Swift { @@ -111,5 +111,4 @@ namespace Swift {  	SWIFTEN_DECLARE_FORM_FIELD(JIDSingle, JID);  	SWIFTEN_DECLARE_FORM_FIELD(JIDMulti, std::vector<JID>);  	SWIFTEN_DECLARE_FORM_FIELD(ListMulti, std::vector<std::string>); -	SWIFTEN_DECLARE_FORM_FIELD(Untyped, std::vector<std::string>);  } diff --git a/Swiften/Elements/IBB.h b/Swiften/Elements/IBB.h index 55f2c4f..8138e83 100644 --- a/Swiften/Elements/IBB.h +++ b/Swiften/Elements/IBB.h @@ -6,11 +6,11 @@  #pragma once +#include <string> +#include <vector>  #include <boost/shared_ptr.hpp> -#include <string> -#include "Swiften/Base/ByteArray.h" -#include "Swiften/Elements/Payload.h" +#include <Swiften/Elements/Payload.h>  namespace Swift {  	class IBB : public Payload { @@ -36,7 +36,7 @@ namespace Swift {  				return result;  			} -			static IBB::ref createIBBData(const std::string& streamID, int sequenceNumber, const ByteArray& data) { +			static IBB::ref createIBBData(const std::string& streamID, int sequenceNumber, const std::vector<unsigned char>& data) {  				IBB::ref result(new IBB(Data, streamID));  				result->setSequenceNumber(sequenceNumber);  				result->setData(data); @@ -71,11 +71,11 @@ namespace Swift {  				return streamID;  			} -			const ByteArray& getData() const { +			const std::vector<unsigned char>& getData() const {  				return data;  			} -			void setData(const ByteArray& data) { +			void setData(const std::vector<unsigned char>& data) {  				this->data = data;  			} @@ -98,7 +98,7 @@ namespace Swift {  		private:  			Action action;  			std::string streamID; -			ByteArray data; +			std::vector<unsigned char> data;  			StanzaType stanzaType;  			int blockSize;  			int sequenceNumber; diff --git a/Swiften/Elements/InBandRegistrationPayload.h b/Swiften/Elements/InBandRegistrationPayload.h index e4e1e6f..6e0f741 100644 --- a/Swiften/Elements/InBandRegistrationPayload.h +++ b/Swiften/Elements/InBandRegistrationPayload.h @@ -8,10 +8,10 @@  #include <boost/shared_ptr.hpp>  #include <boost/optional.hpp> +#include <string>  #include "Swiften/Elements/Payload.h"  #include "Swiften/Elements/Form.h" -#include <string>  namespace Swift {  	class InBandRegistrationPayload : public Payload { diff --git a/Swiften/Elements/JingleContent.h b/Swiften/Elements/JingleContentPayload.h index 4ae908b..c44a806 100644 --- a/Swiften/Elements/JingleContent.h +++ b/Swiften/Elements/JingleContentPayload.h @@ -8,18 +8,17 @@  #include <vector>  #include <boost/optional.hpp> -  #include <string> +  #include <Swiften/JID/JID.h>  #include <Swiften/Elements/Payload.h>  #include <Swiften/Elements/JingleDescription.h> -#include <Swiften/Elements/JingleTransport.h> -#include <Swiften/Base/foreach.h> +#include <Swiften/Elements/JingleTransportPayload.h>  namespace Swift { -	class JingleContent : public Payload { +	class JingleContentPayload : public Payload {  		public: -			typedef boost::shared_ptr<JingleContent> ref; +			typedef boost::shared_ptr<JingleContentPayload> ref;  			enum Creator {  				InitiatorCreator, @@ -33,10 +32,18 @@ namespace Swift {  				BothSenders,  			};*/ +			Creator getCreator() const { +				return creator; +			} +  			void setCreator(Creator creator) {  				this->creator = creator;  			} +			const std::string& getName() const { +				return name; +			} +  			void setName(const std::string& name) {  				this->name = name;  			} @@ -49,18 +56,18 @@ namespace Swift {  				descriptions.push_back(description);  			} -			const std::vector<JingleTransport::ref>& getTransports() const { +			const std::vector<boost::shared_ptr<JingleTransportPayload> >& getTransports() const {  				return transports;  			} -			void addTransport(JingleTransport::ref transport) { +			void addTransport(boost::shared_ptr<JingleTransportPayload>  transport) {  				transports.push_back(transport);  			}  			template<typename T>  			boost::shared_ptr<T> getDescription() const { -				foreach (JingleDescription::ref i, descriptions) { -					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(i)); +				for (size_t i = 0; i < descriptions.size(); ++i) { +					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(descriptions[i]));  					if (result) {  						return result;  					} @@ -70,8 +77,8 @@ namespace Swift {  			template<typename T>  			boost::shared_ptr<T> getTransport() const { -				foreach (JingleTransport::ref i, transports) { -					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(i)); +				for (size_t i = 0; i < transports.size(); ++i) { +					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(transports[i]));  					if (result) {  						return result;  					} @@ -84,6 +91,6 @@ namespace Swift {  			std::string name;  			//Senders senders;  			std::vector<JingleDescription::ref> descriptions; -			std::vector<JingleTransport::ref> transports; +			std::vector<boost::shared_ptr<JingleTransportPayload> > transports;  	};  } diff --git a/Swiften/Elements/JingleIBBTransport.h b/Swiften/Elements/JingleIBBTransportPayload.h index faa5af3..67aab09 100644 --- a/Swiften/Elements/JingleIBBTransport.h +++ b/Swiften/Elements/JingleIBBTransportPayload.h @@ -6,12 +6,16 @@  #pragma once +#include <boost/shared_ptr.hpp>  #include <string> -#include <Swiften/Elements/JingleTransport.h> + +#include <Swiften/Elements/JingleTransportPayload.h>  namespace Swift { -	class JingleIBBTransport : public JingleTransport { +	class JingleIBBTransportPayload : public JingleTransportPayload {  		public: +			typedef boost::shared_ptr<JingleIBBTransportPayload> ref; +  			enum StanzaType {  				IQStanza,  				MessageStanza, diff --git a/Swiften/Elements/JinglePayload.h b/Swiften/Elements/JinglePayload.h index 59d3c99..be02543 100644 --- a/Swiften/Elements/JinglePayload.h +++ b/Swiften/Elements/JinglePayload.h @@ -12,7 +12,7 @@  #include <string>  #include <Swiften/JID/JID.h>  #include <Swiften/Elements/Payload.h> -#include <Swiften/Elements/JingleContent.h> +#include <Swiften/Elements/JingleContentPayload.h>  namespace Swift { @@ -98,11 +98,11 @@ namespace Swift {  				return sessionID;  			} -			void addContent(JingleContent::ref content) { +			void addContent(JingleContentPayload::ref content) {  				this->contents.push_back(content);  			} -			const std::vector<JingleContent::ref> getContents() const { +			const std::vector<JingleContentPayload::ref> getContents() const {  				return contents;  			} @@ -119,7 +119,7 @@ namespace Swift {  			JID initiator;  			JID responder;  			std::string sessionID; -			std::vector<JingleContent::ref> contents; +			std::vector<JingleContentPayload::ref> contents;  			boost::optional<Reason> reason;  	};  } diff --git a/Swiften/Elements/JingleS5BTransport.h b/Swiften/Elements/JingleS5BTransportPayload.h index 4522417..7b3089f 100644 --- a/Swiften/Elements/JingleS5BTransport.h +++ b/Swiften/Elements/JingleS5BTransportPayload.h @@ -6,11 +6,13 @@  #pragma once -#include <Swiften/Elements/JingleTransport.h> +#include <Swiften/Elements/JingleTransportPayload.h>  #include <Swiften/Elements/Bytestreams.h> +// FIXME: Remove Bytestreams, and replace by our own candidate +  namespace Swift { -	class JingleS5BTransport : public JingleTransport { +	class JingleS5BTransportPayload : public JingleTransportPayload {  		public:  			const Bytestreams& getInfo() const {  				return info; diff --git a/Swiften/Elements/JingleTransportPayload.h b/Swiften/Elements/JingleTransportPayload.h new file mode 100644 index 0000000..7a9ea29 --- /dev/null +++ b/Swiften/Elements/JingleTransportPayload.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011 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/Elements/Payload.h> + +namespace Swift { +	class JingleTransportPayload : public Payload { +		public: +			typedef boost::shared_ptr<JingleTransportPayload> ref; +	}; +} diff --git a/Swiften/Elements/MUCPayload.h b/Swiften/Elements/MUCPayload.h index c372360..eb3baeb 100644 --- a/Swiften/Elements/MUCPayload.h +++ b/Swiften/Elements/MUCPayload.h @@ -7,7 +7,7 @@  #pragma once  #include <boost/optional.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp>  #include "Swiften/JID/JID.h"  #include <string> diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h index a553eb3..3623e73 100644 --- a/Swiften/Elements/Message.h +++ b/Swiften/Elements/Message.h @@ -14,6 +14,7 @@  #include "Swiften/Elements/Subject.h"  #include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/Elements/Stanza.h" +#include "Swiften/Elements/Replace.h"  namespace Swift {  	class Message : public Stanza { diff --git a/Swiften/Elements/Payload.h b/Swiften/Elements/Payload.h index c87b899..8b6d44a 100644 --- a/Swiften/Elements/Payload.h +++ b/Swiften/Elements/Payload.h @@ -6,13 +6,9 @@  #pragma once -#include <boost/shared_ptr.hpp> -  namespace Swift {  	class Payload {  		public: -			typedef boost::shared_ptr<Payload> ref; -  			virtual ~Payload();  	};  } diff --git a/Swiften/Elements/Presence.cpp b/Swiften/Elements/Presence.cpp new file mode 100644 index 0000000..6cde567 --- /dev/null +++ b/Swiften/Elements/Presence.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/Presence.h> + +#include <Swiften/Elements/Priority.h> +#include <Swiften/Elements/Status.h> + +namespace Swift { + +Presence::Presence() : type_(Available) /*, showType_(Online)*/ { +} + +Presence::Presence(const std::string& status) : type_(Available) { +	setStatus(status); +} + +Presence::~Presence() { +} + +int Presence::getPriority() const { +	boost::shared_ptr<Priority> priority(getPayload<Priority>()); +	return (priority ? priority->getPriority() : 0); +} + +void Presence::setPriority(int priority) { +	updatePayload(boost::shared_ptr<Priority>(new Priority(priority))); +} + +std::string Presence::getStatus() const {  +	boost::shared_ptr<Status> status(getPayload<Status>()); +	if (status) { +		return status->getText(); +	} +	return ""; +} + +void Presence::setStatus(const std::string& status) {  +	updatePayload(boost::shared_ptr<Status>(new Status(status))); +} + + +} diff --git a/Swiften/Elements/Presence.h b/Swiften/Elements/Presence.h index 7f957ba..5ae482b 100644 --- a/Swiften/Elements/Presence.h +++ b/Swiften/Elements/Presence.h @@ -6,11 +6,8 @@  #pragma once - -#include "Swiften/Elements/Stanza.h" -#include "Swiften/Elements/Status.h" -#include "Swiften/Elements/StatusShow.h" -#include "Swiften/Elements/Priority.h" +#include <Swiften/Elements/Stanza.h> +#include <Swiften/Elements/StatusShow.h>  namespace Swift {  	class Presence : public Stanza { @@ -19,10 +16,9 @@ namespace Swift {  			enum Type { Available, Error, Probe, Subscribe, Subscribed, Unavailable, Unsubscribe, Unsubscribed }; -			Presence() : type_(Available) /*, showType_(Online)*/ {} -			Presence(const std::string& status) : type_(Available) { -				setStatus(status); -			} +			Presence(); +			Presence(const std::string& status); +			virtual ~Presence();  			static ref create() {  				return ref(new Presence()); @@ -51,26 +47,11 @@ namespace Swift {  				updatePayload(boost::shared_ptr<StatusShow>(new StatusShow(show)));  			} -			std::string getStatus() const {  -				boost::shared_ptr<Status> status(getPayload<Status>()); -				if (status) { -					return status->getText(); -				} -				return ""; -			} +			std::string getStatus() const; +			void setStatus(const std::string& status); -			void setStatus(const std::string& status) {  -				updatePayload(boost::shared_ptr<Status>(new Status(status))); -			} - -			int getPriority() const { -				boost::shared_ptr<Priority> priority(getPayload<Priority>()); -				return (priority ? priority->getPriority() : 0); -			} - -			void setPriority(int priority) { -				updatePayload(boost::shared_ptr<Priority>(new Priority(priority))); -			} +			int getPriority() const; +			void setPriority(int priority);  			boost::shared_ptr<Presence> clone() const {  				return boost::shared_ptr<Presence>(new Presence(*this)); diff --git a/Swiften/Elements/Priority.h b/Swiften/Elements/Priority.h index 12181d4..2c0cb9b 100644 --- a/Swiften/Elements/Priority.h +++ b/Swiften/Elements/Priority.h @@ -6,13 +6,11 @@  #pragma once -#include "Swiften/Elements/Payload.h" +#include <Swiften/Elements/Payload.h>  namespace Swift {  	class Priority : public Payload {  		public: -			typedef boost::shared_ptr<Priority> ref; -  			Priority(int priority = 0) : priority_(priority) {  			} diff --git a/Swiften/Elements/Replace.h b/Swiften/Elements/Replace.h new file mode 100644 index 0000000..dc8ff59 --- /dev/null +++ b/Swiften/Elements/Replace.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Elements/Payload.h" + +namespace Swift { +	class Replace : public Payload { +		public: +			typedef boost::shared_ptr<Replace> ref; +			Replace(std::string id = "") : replaceID_(id) {}; +			std::string getId() { +				return replaceID_; +			} +			void setId(std::string id) { +				replaceID_ = id; +			} +		private: +			std::string replaceID_; +	}; +} diff --git a/Swiften/Elements/RosterItemExchangePayload.cpp b/Swiften/Elements/RosterItemExchangePayload.cpp new file mode 100644 index 0000000..f4f3a57 --- /dev/null +++ b/Swiften/Elements/RosterItemExchangePayload.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Elements/RosterItemExchangePayload.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + +RosterItemExchangePayload::Item::Item() { +} + +RosterItemExchangePayload::RosterItemExchangePayload() { +} + +} diff --git a/Swiften/Elements/RosterItemExchangePayload.h b/Swiften/Elements/RosterItemExchangePayload.h new file mode 100644 index 0000000..d9e2912 --- /dev/null +++ b/Swiften/Elements/RosterItemExchangePayload.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <vector> +#include <string> +#include <boost/shared_ptr.hpp> + +#include "Swiften/Elements/Payload.h" +#include "Swiften/JID/JID.h" + + +namespace Swift { +	class RosterItemExchangePayload : public Payload { +		public: +			typedef boost::shared_ptr<RosterItemExchangePayload> ref; + +			class Item { +				public: +					enum Action { Add, Modify, Delete }; + +					Item(); + +					Action getAction() const { +						return action; +					} + +					void setAction(Action action) { +						this->action = action; +					} + +					const JID& getJID() const { +						return jid; +					} + +					void setJID(const JID& jid) { +						this->jid = jid; +					} + +					const std::string& getName() const { +						return name; +					} + +					void setName(const std::string& name) { +						this->name = name; +					} + +					const std::vector<std::string>& getGroups() const { +						return groups; +					} + +					void addGroup(const std::string& group) { +						groups.push_back(group); +					} + +				private: +					Action action; +					JID jid; +					std::string name; +					std::vector<std::string> groups; +			}; + +			typedef std::vector<RosterItemExchangePayload::Item> RosterItemExchangePayloadItems; + +		public: +			RosterItemExchangePayload(); + +			void addItem(const RosterItemExchangePayload::Item& item) { +				items_.push_back(item); +			} + +			const RosterItemExchangePayloadItems& getItems() const { +				return items_; +			} + +		private: +			RosterItemExchangePayloadItems items_; +	}; +} diff --git a/Swiften/Elements/RosterItemPayload.h b/Swiften/Elements/RosterItemPayload.h index b8a1b10..915ae31 100644 --- a/Swiften/Elements/RosterItemPayload.h +++ b/Swiften/Elements/RosterItemPayload.h @@ -4,22 +4,20 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_RosterItemPayloadPayload_H -#define SWIFTEN_RosterItemPayloadPayload_H +#pragma once  #include <vector> - -#include "Swiften/JID/JID.h"  #include <string> +#include <Swiften/JID/JID.h> +  namespace Swift { -	class RosterItemPayload -	{ +	class RosterItemPayload {  		public:  			enum Subscription { None, To, From, Both, Remove };  			RosterItemPayload() : subscription_(None), ask_(false) {} -			RosterItemPayload(const JID& jid, const std::string& name, Subscription subscription) : jid_(jid), name_(name), subscription_(subscription), ask_(false) { } +			RosterItemPayload(const JID& jid, const std::string& name, Subscription subscription, const std::vector<std::string>& groups = std::vector<std::string>()) : jid_(jid), name_(name), subscription_(subscription), groups_(groups), ask_(false) { }  			void setJID(const JID& jid) { jid_ = jid; }  			const JID& getJID() const { return jid_; } @@ -51,5 +49,3 @@ namespace Swift {  			std::string unknownContent_;  	};  } - -#endif diff --git a/Swiften/Elements/RosterPayload.h b/Swiften/Elements/RosterPayload.h index b46b384..3102f0e 100644 --- a/Swiften/Elements/RosterPayload.h +++ b/Swiften/Elements/RosterPayload.h @@ -33,7 +33,16 @@ namespace Swift {  				return items_;  			} +			const boost::optional<std::string>& getVersion() const { +				return version_; +			} + +			void setVersion(const std::string& version) { +				version_ = version; +			} +  		private:  			RosterItemPayloads items_; +			boost::optional<std::string> version_;  	};  } diff --git a/Swiften/Elements/SecurityLabelsCatalog.h b/Swiften/Elements/SecurityLabelsCatalog.h index 10ef459..cd84b0b 100644 --- a/Swiften/Elements/SecurityLabelsCatalog.h +++ b/Swiften/Elements/SecurityLabelsCatalog.h @@ -4,13 +4,13 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_SecurityLabelsCatalog_H -#define SWIFTEN_SecurityLabelsCatalog_H +#pragma once  #include <vector> +#include <string> +#include <boost/shared_ptr.hpp>  #include "Swiften/JID/JID.h" -#include <string>  #include "Swiften/Elements/Payload.h"  #include "Swiften/Elements/SecurityLabel.h" @@ -85,5 +85,3 @@ namespace Swift {  			std::vector<Item> items_;  	};  } - -#endif diff --git a/Swiften/Elements/SoftwareVersion.h b/Swiften/Elements/SoftwareVersion.h index 5863b38..887c6e9 100644 --- a/Swiften/Elements/SoftwareVersion.h +++ b/Swiften/Elements/SoftwareVersion.h @@ -6,8 +6,10 @@  #pragma once -#include "Swiften/Elements/Payload.h"  #include <string> +#include <boost/shared_ptr.hpp> + +#include "Swiften/Elements/Payload.h"  namespace Swift {  	class SoftwareVersion : public Payload { diff --git a/Swiften/Elements/Stanza.cpp b/Swiften/Elements/Stanza.cpp index d15d778..607dfd1 100644 --- a/Swiften/Elements/Stanza.cpp +++ b/Swiften/Elements/Stanza.cpp @@ -9,8 +9,13 @@  #include <typeinfo> +#include <Swiften/Base/foreach.h> +  namespace Swift { +Stanza::Stanza() { +} +	  Stanza::~Stanza() {  	payloads_.clear();  } diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h index 9b934e4..9e082cc 100644 --- a/Swiften/Elements/Stanza.h +++ b/Swiften/Elements/Stanza.h @@ -7,27 +7,28 @@  #pragma once  #include <vector> +#include <string>  #include <boost/shared_ptr.hpp> -#include <boost/optional.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/optional/optional_fwd.hpp> +#include <boost/date_time/posix_time/ptime.hpp> -#include "Swiften/Elements/Element.h" -#include "Swiften/Elements/Payload.h" -#include <string> -#include "Swiften/Base/foreach.h" -#include "Swiften/JID/JID.h" +#include <Swiften/Elements/Element.h> +#include <Swiften/JID/JID.h>  namespace Swift { +	class Payload; +  	class Stanza : public Element {  		public:  			typedef boost::shared_ptr<Stanza> ref; +			Stanza();  			virtual ~Stanza();  			template<typename T>   			boost::shared_ptr<T> getPayload() const { -				foreach (const boost::shared_ptr<Payload>& i, payloads_) { -					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(i)); +				for (size_t i = 0; i < payloads_.size(); ++i) { +					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(payloads_[i]));  					if (result) {  						return result;  					} @@ -38,8 +39,8 @@ namespace Swift {  			template<typename T>   			std::vector< boost::shared_ptr<T> > getPayloads() const {  				std::vector< boost::shared_ptr<T> > results; -				foreach (const boost::shared_ptr<Payload>& i, payloads_) { -					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(i)); +				for (size_t i = 0; i < payloads_.size(); ++i) { +					boost::shared_ptr<T> result(boost::dynamic_pointer_cast<T>(payloads_[i]));  					if (result) {  						results.push_back(result);  					} @@ -78,8 +79,6 @@ namespace Swift {  			std::string id_;  			JID from_;  			JID to_; - -			typedef std::vector< boost::shared_ptr<Payload> > Payloads; -			Payloads payloads_; +			std::vector< boost::shared_ptr<Payload> > payloads_;  	};  } diff --git a/Swiften/Elements/Status.h b/Swiften/Elements/Status.h index 3ef6401..bb9b6e9 100644 --- a/Swiften/Elements/Status.h +++ b/Swiften/Elements/Status.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_Status_H -#define SWIFTEN_Status_H +#pragma once  #include "Swiften/Elements/Payload.h"  #include <string> @@ -28,5 +27,3 @@ namespace Swift {  			std::string text_;  	};  } - -#endif diff --git a/Swiften/Elements/StatusShow.cpp b/Swiften/Elements/StatusShow.cpp new file mode 100644 index 0000000..656e5c4 --- /dev/null +++ b/Swiften/Elements/StatusShow.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/StatusShow.h> + +using namespace Swift; + +StatusShow::StatusShow(const Type& type) : type_(type) { +} diff --git a/Swiften/Elements/StatusShow.h b/Swiften/Elements/StatusShow.h index a158239..cd3477e 100644 --- a/Swiften/Elements/StatusShow.h +++ b/Swiften/Elements/StatusShow.h @@ -4,19 +4,16 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_StatusShow_H -#define SWIFTEN_StatusShow_H +#pragma once -#include "Swiften/Elements/Payload.h" -#include <string> +#include <Swiften/Elements/Payload.h>  namespace Swift {  	class StatusShow : public Payload {  		public:  			enum Type { Online, Away, FFC, XA, DND, None }; -			StatusShow(const Type& type = Online) : type_(type) { -			} +			StatusShow(const Type& type = Online);  			void setType(const Type& type) {  				type_ = type; @@ -32,19 +29,17 @@ namespace Swift {  			 */  			static int typeToAvailabilityOrdering(Type type) {  				switch (type) { -				case Online: return 4; -				case FFC: return 5; -				case Away: return 2; -				case XA: return 1; -				case DND: return 3; -				case None: return 0; +					case Online: return 4; +					case FFC: return 5; +					case Away: return 2; +					case XA: return 1; +					case DND: return 3; +					case None: return 0;  				} -				return -1; +				return 0;  			}  		private:  			Type type_;  	};  } - -#endif diff --git a/Swiften/Elements/StreamFeatures.cpp b/Swiften/Elements/StreamFeatures.cpp new file mode 100644 index 0000000..c6f6c04 --- /dev/null +++ b/Swiften/Elements/StreamFeatures.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/StreamFeatures.h> + +#include <algorithm> + +namespace Swift { + +bool StreamFeatures::hasCompressionMethod(const std::string& mechanism) const { +	return std::find(compressionMethods_.begin(), compressionMethods_.end(), mechanism) != compressionMethods_.end(); +} + +bool StreamFeatures::hasAuthenticationMechanism(const std::string& mechanism) const { +	return std::find(authenticationMechanisms_.begin(), authenticationMechanisms_.end(), mechanism) != authenticationMechanisms_.end(); +} + +} diff --git a/Swiften/Elements/StreamFeatures.h b/Swiften/Elements/StreamFeatures.h index fbc0bb8..5dc5a26 100644 --- a/Swiften/Elements/StreamFeatures.h +++ b/Swiften/Elements/StreamFeatures.h @@ -7,9 +7,9 @@  #pragma once  #include <vector> -#include <algorithm> -  #include <string> +#include <boost/shared_ptr.hpp> +  #include "Swiften/Elements/Element.h"  namespace Swift { @@ -17,7 +17,7 @@ namespace Swift {  		public:  			typedef boost::shared_ptr<StreamFeatures> ref; -			StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false), hasStreamManagement_(false) {} +			StreamFeatures() : hasStartTLS_(false), hasResourceBind_(false), hasSession_(false), hasStreamManagement_(false), hasRosterVersioning_(false) {}  			void setHasStartTLS() {  				hasStartTLS_ = true; @@ -51,9 +51,7 @@ namespace Swift {  				compressionMethods_.push_back(mechanism);  			} -			bool hasCompressionMethod(const std::string& mechanism) const { -				return std::find(compressionMethods_.begin(), compressionMethods_.end(), mechanism) != compressionMethods_.end(); -			} +			bool hasCompressionMethod(const std::string& mechanism) const;  			const std::vector<std::string>& getAuthenticationMechanisms() const {  				return authenticationMechanisms_; @@ -63,9 +61,7 @@ namespace Swift {  				authenticationMechanisms_.push_back(mechanism);  			} -			bool hasAuthenticationMechanism(const std::string& mechanism) const { -				return std::find(authenticationMechanisms_.begin(), authenticationMechanisms_.end(), mechanism) != authenticationMechanisms_.end(); -			} +			bool hasAuthenticationMechanism(const std::string& mechanism) const;  			bool hasAuthenticationMechanisms() const {  				return !authenticationMechanisms_.empty(); @@ -79,6 +75,14 @@ namespace Swift {  				hasStreamManagement_ = true;  			} +			bool hasRosterVersioning() const { +				return hasRosterVersioning_; +			} + +			void setHasRosterVersioning() { +				hasRosterVersioning_ = true; +			} +  		private:  			bool hasStartTLS_;  			std::vector<std::string> compressionMethods_; @@ -86,5 +90,6 @@ namespace Swift {  			bool hasResourceBind_;  			bool hasSession_;  			bool hasStreamManagement_; +			bool hasRosterVersioning_;  	};  } diff --git a/Swiften/Elements/StreamManagementEnabled.cpp b/Swiften/Elements/StreamManagementEnabled.cpp new file mode 100644 index 0000000..bab7516 --- /dev/null +++ b/Swiften/Elements/StreamManagementEnabled.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/StreamManagementEnabled.h> + +using namespace Swift; + +StreamManagementEnabled::StreamManagementEnabled() { +} + +StreamManagementEnabled::~StreamManagementEnabled() { +} diff --git a/Swiften/Elements/StreamManagementEnabled.h b/Swiften/Elements/StreamManagementEnabled.h index 0c72b84..02e77f3 100644 --- a/Swiften/Elements/StreamManagementEnabled.h +++ b/Swiften/Elements/StreamManagementEnabled.h @@ -1,17 +1,39 @@  /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2011 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once -#include "Swiften/Elements/Element.h" +#include <string> +#include <Swiften/Elements/Element.h>  namespace Swift {  	class StreamManagementEnabled : public Element {  		public: -			StreamManagementEnabled() {} +			StreamManagementEnabled(); +			~StreamManagementEnabled(); + +			void setResumeSupported() { +				resumeSupported = true; +			} + +			bool getResumeSupported() const { +				return resumeSupported; +			} + +			void setResumeID(const std::string& id) { +				resumeID = id; +			} + +			const std::string& getResumeID() const { +				return resumeID; +			} + +		private: +			bool resumeSupported; +			std::string resumeID;  	};  } diff --git a/Swiften/Elements/StreamResume.cpp b/Swiften/Elements/StreamResume.cpp new file mode 100644 index 0000000..d55ef78 --- /dev/null +++ b/Swiften/Elements/StreamResume.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/StreamResume.h> + +using namespace Swift; + +StreamResume::StreamResume() { +} + +StreamResume::~StreamResume() { +} diff --git a/Swiften/Elements/StreamResume.h b/Swiften/Elements/StreamResume.h new file mode 100644 index 0000000..652182a --- /dev/null +++ b/Swiften/Elements/StreamResume.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <boost/optional.hpp> + +#include <Swiften/Elements/Element.h> + +namespace Swift { +	class StreamResume : public Element { +		public: +			StreamResume(); +			~StreamResume(); + +			void setResumeID(const std::string& id) { +				resumeID = id; +			} + +			const std::string& getResumeID() const { +				return resumeID; +			} + +			const boost::optional<int> getHandledStanzasCount() const { +				return handledStanzasCount; +			} + +			void setHandledStanzasCount(int i) { +				handledStanzasCount = i; +			} + +		private: +			std::string resumeID; +			boost::optional<int> handledStanzasCount; +	}; +} diff --git a/Swiften/Elements/StreamResumed.cpp b/Swiften/Elements/StreamResumed.cpp new file mode 100644 index 0000000..552e654 --- /dev/null +++ b/Swiften/Elements/StreamResumed.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Elements/StreamResumed.h> + +using namespace Swift; + +StreamResumed::StreamResumed() { +} + +StreamResumed::~StreamResumed() { +} diff --git a/Swiften/Elements/StreamResumed.h b/Swiften/Elements/StreamResumed.h new file mode 100644 index 0000000..cc42895 --- /dev/null +++ b/Swiften/Elements/StreamResumed.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <boost/optional.hpp> + +#include <Swiften/Elements/Element.h> + +namespace Swift { +	class StreamResumed : public Element { +		public: +			StreamResumed(); +			~StreamResumed(); + +			void setResumeID(const std::string& id) { +				resumeID = id; +			} + +			const std::string& getResumeID() const { +				return resumeID; +			} + +			const boost::optional<int> getHandledStanzasCount() const { +				return handledStanzasCount; +			} + +			void setHandledStanzasCount(int i) { +				handledStanzasCount = i; +			} + +		private: +			std::string resumeID; +			boost::optional<int> handledStanzasCount; +	}; +} diff --git a/Swiften/Elements/UnitTest/StanzaTest.cpp b/Swiften/Elements/UnitTest/StanzaTest.cpp index 4020f8b..4669f16 100644 --- a/Swiften/Elements/UnitTest/StanzaTest.cpp +++ b/Swiften/Elements/UnitTest/StanzaTest.cpp @@ -7,6 +7,7 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h>  #include <boost/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp>  #include "Swiften/Elements/Stanza.h"  #include "Swiften/Elements/Payload.h" diff --git a/Swiften/Entity/Entity.cpp b/Swiften/Entity/Entity.cpp index da2ecaf..dea47b0 100644 --- a/Swiften/Entity/Entity.cpp +++ b/Swiften/Entity/Entity.cpp @@ -6,26 +6,45 @@  #include "Swiften/Entity/Entity.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" + +  namespace Swift { +Entity::Entity() { +	payloadParserFactories = new FullPayloadParserFactoryCollection(); +	payloadSerializers = new FullPayloadSerializerCollection(); +} +  Entity::~Entity() { +	delete payloadSerializers; +	delete payloadParserFactories;  }  void Entity::addPayloadParserFactory(PayloadParserFactory* payloadParserFactory) { -	payloadParserFactories.addFactory(payloadParserFactory); +	payloadParserFactories->addFactory(payloadParserFactory);  }  void Entity::removePayloadParserFactory(PayloadParserFactory* payloadParserFactory) { -	payloadParserFactories.removeFactory(payloadParserFactory); +	payloadParserFactories->removeFactory(payloadParserFactory);  }  void Entity::addPayloadSerializer(PayloadSerializer* payloadSerializer) { -	payloadSerializers.addSerializer(payloadSerializer); +	payloadSerializers->addSerializer(payloadSerializer);  }  void Entity::removePayloadSerializer(PayloadSerializer* payloadSerializer) { -	payloadSerializers.removeSerializer(payloadSerializer); +	payloadSerializers->removeSerializer(payloadSerializer); +} + +PayloadParserFactoryCollection* Entity::getPayloadParserFactories() { +	return payloadParserFactories; +} + +PayloadSerializerCollection* Entity::getPayloadSerializers() { +	return payloadSerializers;  }  } diff --git a/Swiften/Entity/Entity.h b/Swiften/Entity/Entity.h index 20d02ba..65480d0 100644 --- a/Swiften/Entity/Entity.h +++ b/Swiften/Entity/Entity.h @@ -6,21 +6,20 @@  #pragma once -#include <Swiften/Base/boost_bsignals.h> -#include <boost/shared_ptr.hpp> - -#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" -#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" -  namespace Swift {  	class PayloadParserFactory;  	class PayloadSerializer; +	class FullPayloadParserFactoryCollection; +	class FullPayloadSerializerCollection; +	class PayloadParserFactoryCollection; +	class PayloadSerializerCollection;  	/**   	 * The base class for XMPP entities (Clients, Components).  	 */  	class Entity  {  		public:  +			Entity();  			virtual ~Entity();  			void addPayloadParserFactory(PayloadParserFactory* payloadParserFactory); @@ -30,16 +29,11 @@ namespace Swift {  			void removePayloadSerializer(PayloadSerializer* payloadSerializer);  		protected: -			PayloadParserFactoryCollection* getPayloadParserFactories() { -				return &payloadParserFactories; -			} - -			PayloadSerializerCollection* getPayloadSerializers() { -				return &payloadSerializers; -			} +			PayloadParserFactoryCollection* getPayloadParserFactories(); +			PayloadSerializerCollection* getPayloadSerializers();  		private: -			FullPayloadParserFactoryCollection payloadParserFactories; -			FullPayloadSerializerCollection payloadSerializers; +			FullPayloadParserFactoryCollection* payloadParserFactories; +			FullPayloadSerializerCollection* payloadSerializers;  	};  } diff --git a/Swiften/Entity/GenericPayloadPersister.h b/Swiften/Entity/GenericPayloadPersister.h new file mode 100644 index 0000000..63553de --- /dev/null +++ b/Swiften/Entity/GenericPayloadPersister.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Entity/PayloadPersister.h> +#include <Swiften/Parser/GenericPayloadParserFactory.h> + +namespace Swift { +	template<typename PAYLOAD, typename PARSER, typename SERIALIZER> +	class GenericPayloadPersister : public PayloadPersister { +		public: +			GenericPayloadPersister() { +			} + +		public: +			boost::shared_ptr<PAYLOAD> loadPayloadGeneric(const boost::filesystem::path& path) { +				return boost::dynamic_pointer_cast<PAYLOAD>(loadPayload(path)); +			} + +		protected: +			virtual const PayloadSerializer* getSerializer() const { +				return &serializer; +			} + +			virtual PayloadParser* createParser() const { +				return new PARSER(); +			} + +		private: +			SERIALIZER serializer; +	}; +} diff --git a/Swiften/Entity/PayloadPersister.cpp b/Swiften/Entity/PayloadPersister.cpp new file mode 100644 index 0000000..f7278cc --- /dev/null +++ b/Swiften/Entity/PayloadPersister.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Entity/PayloadPersister.h> + +#include <boost/filesystem/fstream.hpp> +#include <boost/filesystem.hpp> +#include <iostream> + +#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadParserTester.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Parser/PayloadParser.h> +#include <Swiften/Parser/PayloadParserFactory.h> +#include <Swiften/Serializer/PayloadSerializer.h> + +using namespace Swift; + +PayloadPersister::PayloadPersister() { +} + +PayloadPersister::~PayloadPersister() { +} + +void PayloadPersister::savePayload(boost::shared_ptr<Payload> payload, const boost::filesystem::path& path) { +	if (!boost::filesystem::exists(path.parent_path())) { +		try { +			boost::filesystem::create_directories(path.parent_path()); +		} +		catch (const boost::filesystem::filesystem_error& e) { +			std::cerr << "ERROR: " << e.what() << std::endl; +		} +	} +	boost::filesystem::ofstream file(path); +	file << getSerializer()->serialize(payload); +	file.close(); +} + +boost::shared_ptr<Payload> PayloadPersister::loadPayload(const boost::filesystem::path& path) { +	if (boost::filesystem::exists(path)) { +		ByteArray data; +		data.readFromFile(path.string()); +		std::auto_ptr<PayloadParser> parser(createParser()); +		PayloadParserTester tester(parser.get()); +		tester.parse(data.toString()); +		return parser->getPayload(); +	} +	else { +		return boost::shared_ptr<Payload>(); +	} +} diff --git a/Swiften/Entity/PayloadPersister.h b/Swiften/Entity/PayloadPersister.h new file mode 100644 index 0000000..ea7c74c --- /dev/null +++ b/Swiften/Entity/PayloadPersister.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 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 <boost/filesystem/path.hpp> + +namespace Swift { +	class Payload; +	class PayloadSerializer; +	class PayloadParser; + +	class PayloadPersister { +		public:  +			PayloadPersister(); +			virtual ~PayloadPersister(); + +			void savePayload(boost::shared_ptr<Payload>, const boost::filesystem::path&); +			boost::shared_ptr<Payload> loadPayload(const boost::filesystem::path&); + +		protected: + +			virtual const PayloadSerializer* getSerializer() const = 0; +			virtual PayloadParser* createParser() const = 0; +	}; +} diff --git a/Swiften/EventLoop/DummyEventLoop.cpp b/Swiften/EventLoop/DummyEventLoop.cpp new file mode 100644 index 0000000..3741eec --- /dev/null +++ b/Swiften/EventLoop/DummyEventLoop.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/EventLoop/DummyEventLoop.h> + +#include <iostream> + +namespace Swift { + +DummyEventLoop::DummyEventLoop() { +} + +DummyEventLoop::~DummyEventLoop() { +	if (!events_.empty()) { +		std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; +	} +	events_.clear(); +} + + +} diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h index b7ef516..68f9b85 100644 --- a/Swiften/EventLoop/DummyEventLoop.h +++ b/Swiften/EventLoop/DummyEventLoop.h @@ -7,24 +7,14 @@  #pragma once  #include <deque> -#include <iostream> -#include <boost/function.hpp>  #include "Swiften/EventLoop/EventLoop.h" -#include "Swiften/Base/foreach.h"  namespace Swift {  	class DummyEventLoop : public EventLoop {  		public: -			DummyEventLoop() { -			} - -			~DummyEventLoop() { -				if (!events_.empty()) { -					std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; -				} -				events_.clear(); -			} +			DummyEventLoop(); +			~DummyEventLoop();  			void processEvents() {  				while (!events_.empty()) { diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp index 56bb6ac..510ed63 100644 --- a/Swiften/EventLoop/EventLoop.cpp +++ b/Swiften/EventLoop/EventLoop.cpp @@ -9,6 +9,7 @@  #include <algorithm>  #include <boost/bind.hpp>  #include <iostream> +#include <cassert>  #include <Swiften/Base/Log.h> @@ -17,6 +18,7 @@ namespace Swift {  inline void invokeCallback(const Event& event) {  	try { +		assert(!event.callback.empty());  		event.callback();  	}  	catch (const std::exception& e) { diff --git a/Swiften/EventLoop/SConscript b/Swiften/EventLoop/SConscript index 21ae8b9..e448f43 100644 --- a/Swiften/EventLoop/SConscript +++ b/Swiften/EventLoop/SConscript @@ -5,6 +5,7 @@ sources = [  		"EventOwner.cpp",  		"Event.cpp",  		"SimpleEventLoop.cpp", +		"DummyEventLoop.cpp",  	]  objects = swiften_env.SwiftenObject(sources) diff --git a/Swiften/Examples/BenchTool/BenchTool.cpp b/Swiften/Examples/BenchTool/BenchTool.cpp index 9e54ed9..1fb6601 100644 --- a/Swiften/Examples/BenchTool/BenchTool.cpp +++ b/Swiften/Examples/BenchTool/BenchTool.cpp @@ -6,6 +6,7 @@  #include <boost/bind.hpp>  #include <boost/thread.hpp> +#include <iostream>  #include "Swiften/Client/Client.h"  #include "Swiften/Network/TimerFactory.h" diff --git a/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp b/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp index fda203a..3b66d96 100644 --- a/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp +++ b/Swiften/Examples/ConnectivityTest/ConnectivityTest.cpp @@ -6,6 +6,7 @@  #include <boost/bind.hpp>  #include <boost/thread.hpp> +#include <iostream>  #include "Swiften/Client/Client.h"  #include "Swiften/Network/Timer.h" diff --git a/Swiften/Examples/SendFile/ReceiveFile.cpp b/Swiften/Examples/SendFile/ReceiveFile.cpp index b46d790..effa1b7 100644 --- a/Swiften/Examples/SendFile/ReceiveFile.cpp +++ b/Swiften/Examples/SendFile/ReceiveFile.cpp @@ -7,7 +7,10 @@  #include <boost/bind.hpp>  #include <boost/filesystem.hpp>  #include <boost/smart_ptr/make_shared.hpp> +#include <iostream> +#include <Swiften/Elements/Presence.h> +#include <Swiften/Base/foreach.h>  #include <Swiften/Client/Client.h>  #include <Swiften/Network/BoostNetworkFactories.h>  #include <Swiften/EventLoop/SimpleEventLoop.h> diff --git a/Swiften/Examples/SendFile/SendFile.cpp b/Swiften/Examples/SendFile/SendFile.cpp index 5ec00a9..d8300be 100644 --- a/Swiften/Examples/SendFile/SendFile.cpp +++ b/Swiften/Examples/SendFile/SendFile.cpp @@ -6,15 +6,17 @@  #include <boost/bind.hpp>  #include <boost/filesystem.hpp> +#include <iostream>  #include "Swiften/Client/Client.h" +#include <Swiften/Elements/Presence.h>  #include "Swiften/Network/BoostTimer.h"  #include "Swiften/Network/TimerFactory.h"  #include "Swiften/Network/BoostNetworkFactories.h"  #include "Swiften/EventLoop/EventLoop.h"  #include "Swiften/Client/ClientXMLTracer.h"  #include "Swiften/EventLoop/SimpleEventLoop.h" -#include "Swiften/FileTransfer/OutgoingFileTransfer.h" +#include "Swiften/FileTransfer/OutgoingSIFileTransfer.h"  #include "Swiften/FileTransfer/FileReadBytestream.h"  #include "Swiften/FileTransfer/SOCKS5BytestreamServer.h"  #include "Swiften/Network/BoostConnectionServer.h" @@ -64,7 +66,7 @@ class FileSender {  	private:  		void handleConnected() {  			client->sendPresence(Presence::create()); -			transfer = new OutgoingFileTransfer("myid",	client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::shared_ptr<FileReadBytestream>(new FileReadBytestream(file)), client->getIQRouter(), socksBytestreamServer); +			transfer = new OutgoingSIFileTransfer("myid",	client->getJID(), recipient, file.filename(), boost::filesystem::file_size(file), "A file", boost::shared_ptr<FileReadBytestream>(new FileReadBytestream(file)), client->getIQRouter(), socksBytestreamServer);  			transfer->onFinished.connect(boost::bind(&FileSender::handleFileTransferFinished, this, _1));  			transfer->start();  		} @@ -99,7 +101,7 @@ class FileSender {  		boost::filesystem::path file;  		Client* client;  		ClientXMLTracer* tracer; -		OutgoingFileTransfer* transfer; +		OutgoingSIFileTransfer* transfer;  }; diff --git a/Swiften/Examples/SendMessage/SendMessage.cpp b/Swiften/Examples/SendMessage/SendMessage.cpp index d7f7333..ad75318 100644 --- a/Swiften/Examples/SendMessage/SendMessage.cpp +++ b/Swiften/Examples/SendMessage/SendMessage.cpp @@ -6,8 +6,10 @@  #include <boost/bind.hpp>  #include <boost/thread.hpp> +#include <iostream>  #include "Swiften/Client/Client.h" +#include "Swiften/Elements/Message.h"  #include "Swiften/Network/BoostNetworkFactories.h"  #include "Swiften/Network/TimerFactory.h"  #include "Swiften/EventLoop/EventLoop.h" diff --git a/Swiften/FileTransfer/ByteArrayReadBytestream.h b/Swiften/FileTransfer/ByteArrayReadBytestream.h index d459658..4704db6 100644 --- a/Swiften/FileTransfer/ByteArrayReadBytestream.h +++ b/Swiften/FileTransfer/ByteArrayReadBytestream.h @@ -6,31 +6,32 @@  #pragma once -#include "Swiften/FileTransfer/ReadBytestream.h" -#include "Swiften/Base/ByteArray.h" +#include <vector> + +#include <Swiften/FileTransfer/ReadBytestream.h>  namespace Swift {  	class ByteArrayReadBytestream : public ReadBytestream {  		public: -			ByteArrayReadBytestream(const ByteArray& data) : data(data), position(0) { +			ByteArrayReadBytestream(const std::vector<unsigned char>& data) : data(data), position(0) {  			} -			virtual ByteArray read(size_t size) { +			virtual std::vector<unsigned char> read(size_t size) {  				size_t readSize = size; -				if (position + readSize > data.getSize()) { -					readSize = data.getSize() - position; +				if (position + readSize > data.size()) { +					readSize = data.size() - position;  				} -				ByteArray result(data.getData() + position, readSize); +				std::vector<unsigned char> result(data.begin() + position, data.begin() + position + readSize);  				position += readSize;  				return result;  			}  			virtual bool isFinished() const { -				return position >= data.getSize(); +				return position >= data.size();  			}  		private: -			ByteArray data; +			std::vector<unsigned char> data;  			size_t position;  	};  } diff --git a/Swiften/FileTransfer/ByteArrayWriteBytestream.h b/Swiften/FileTransfer/ByteArrayWriteBytestream.h new file mode 100644 index 0000000..6c360e6 --- /dev/null +++ b/Swiften/FileTransfer/ByteArrayWriteBytestream.h @@ -0,0 +1,28 @@ +/* + * 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/FileTransfer/WriteBytestream.h> + +namespace Swift { +	class ByteArrayWriteBytestream : public WriteBytestream { +		public: +			ByteArrayWriteBytestream() { +			} + +			virtual void write(const std::vector<unsigned char>& bytes) { +				data.insert(data.end(), bytes.begin(), bytes.end()); +			} + +			const std::vector<unsigned char>& getData() const { +				return data; +			} + +		private: +			std::vector<unsigned char> data; +	}; +} diff --git a/Swiften/FileTransfer/FileReadBytestream.cpp b/Swiften/FileTransfer/FileReadBytestream.cpp index c08747b..e997366 100644 --- a/Swiften/FileTransfer/FileReadBytestream.cpp +++ b/Swiften/FileTransfer/FileReadBytestream.cpp @@ -21,14 +21,14 @@ FileReadBytestream::~FileReadBytestream() {  	}  } -ByteArray FileReadBytestream::read(size_t size)  { +std::vector<unsigned char> FileReadBytestream::read(size_t size)  {  	if (!stream) {  		stream = new boost::filesystem::ifstream(file, std::ios_base::in|std::ios_base::binary);  	} -	ByteArray result; +	std::vector<unsigned char> result;  	result.resize(size);  	assert(stream->good()); -	stream->read(reinterpret_cast<char*>(result.getData()), size); +	stream->read(reinterpret_cast<char*>(&result[0]), size);  	result.resize(stream->gcount());  	return result;  } diff --git a/Swiften/FileTransfer/FileReadBytestream.h b/Swiften/FileTransfer/FileReadBytestream.h index 055e194..f136a68 100644 --- a/Swiften/FileTransfer/FileReadBytestream.h +++ b/Swiften/FileTransfer/FileReadBytestream.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <boost/filesystem/fstream.hpp>  #include "Swiften/FileTransfer/ReadBytestream.h" @@ -17,7 +17,7 @@ namespace Swift {  			FileReadBytestream(const boost::filesystem::path& file);  			~FileReadBytestream(); -			virtual ByteArray read(size_t size) ; +			virtual std::vector<unsigned char> read(size_t size);  			virtual bool isFinished() const;  		private: diff --git a/Swiften/FileTransfer/FileWriteBytestream.cpp b/Swiften/FileTransfer/FileWriteBytestream.cpp index 4d29bd1..803a10b 100644 --- a/Swiften/FileTransfer/FileWriteBytestream.cpp +++ b/Swiften/FileTransfer/FileWriteBytestream.cpp @@ -21,12 +21,12 @@ FileWriteBytestream::~FileWriteBytestream() {  	}  } -void FileWriteBytestream::write(const ByteArray& data) { +void FileWriteBytestream::write(const std::vector<unsigned char>& data) {  	if (!stream) {  		stream = new boost::filesystem::ofstream(file, std::ios_base::out|std::ios_base::binary);  	}  	assert(stream->good()); -	stream->write(reinterpret_cast<const char*>(data.getData()), data.getSize()); +	stream->write(reinterpret_cast<const char*>(&data[0]), data.size());  }  } diff --git a/Swiften/FileTransfer/FileWriteBytestream.h b/Swiften/FileTransfer/FileWriteBytestream.h index c6f7b39..8cfa718 100644 --- a/Swiften/FileTransfer/FileWriteBytestream.h +++ b/Swiften/FileTransfer/FileWriteBytestream.h @@ -6,10 +6,10 @@  #pragma once -#include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp>  #include <boost/filesystem/fstream.hpp> -#include "Swiften/FileTransfer/WriteBytestream.h" +#include <Swiften/FileTransfer/WriteBytestream.h>  namespace Swift {  	class FileWriteBytestream : public WriteBytestream { @@ -17,7 +17,7 @@ namespace Swift {  			FileWriteBytestream(const boost::filesystem::path& file);  			~FileWriteBytestream(); -			virtual void write(const ByteArray&); +			virtual void write(const std::vector<unsigned char>&);  		private:  			boost::filesystem::path file; diff --git a/Swiften/FileTransfer/IBBReceiveSession.cpp b/Swiften/FileTransfer/IBBReceiveSession.cpp index 5c90757..566dcca 100644 --- a/Swiften/FileTransfer/IBBReceiveSession.cpp +++ b/Swiften/FileTransfer/IBBReceiveSession.cpp @@ -4,31 +4,96 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/FileTransfer/IBBReceiveSession.h" +#include <Swiften/FileTransfer/IBBReceiveSession.h>  #include <boost/bind.hpp> -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/FileTransfer/IBBRequest.h" -#include "Swiften/FileTransfer/BytestreamException.h" +#include <Swiften/Base/Log.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/FileTransfer/IBBRequest.h> +#include <Swiften/FileTransfer/BytestreamException.h> +#include <Swiften/Queries/SetResponder.h>  namespace Swift { -IBBReceiveSession::IBBReceiveSession(const std::string& id, const JID& from, size_t size, WriteBytestream::ref bytestream, IQRouter* router) : SetResponder<IBB>(router), id(id), from(from), size(size), bytestream(bytestream), router(router), sequenceNumber(0), active(false), receivedSize(0) { +class IBBReceiveSession::IBBResponder : public SetResponder<IBB> { +	public: +		IBBResponder(IBBReceiveSession* session, IQRouter* router) : SetResponder<IBB>(router), session(session), sequenceNumber(0), receivedSize(0) { +		} + +		virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, IBB::ref ibb) { +			if (from == session->from && ibb->getStreamID() == session->id) { +				if (ibb->getAction() == IBB::Data) { +					if (sequenceNumber == ibb->getSequenceNumber()) { +						session->onDataReceived(ibb->getData()); +						receivedSize += ibb->getData().size(); +						sequenceNumber++; +						sendResponse(from, id, IBB::ref()); +						if (receivedSize >= session->size) { +							if (receivedSize > session->size) { +								std::cerr << "Warning: Received more data than expected" << std::endl; +							} +							session->finish(boost::optional<FileTransferError>()); +						} +					} +					else { +						SWIFT_LOG(warning) << "Received data out of order" << std::endl; +						sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel); +						session->finish(FileTransferError(FileTransferError::ClosedError)); +					} +				} +				else if (ibb->getAction() == IBB::Open) { +					sendResponse(from, id, IBB::ref()); +				} +				else if (ibb->getAction() == IBB::Close) { +					sendResponse(from, id, IBB::ref()); +					session->finish(FileTransferError(FileTransferError::ClosedError)); +				} +				return true; +			} +			return false; +		} + +	private: +		IBBReceiveSession* session; +		int sequenceNumber; +		size_t receivedSize; +}; + + +IBBReceiveSession::IBBReceiveSession( +		const std::string& id,  +		const JID& from,  +		size_t size,  +		IQRouter* router) :  +			id(id),  +			from(from),  +			size(size),  +			router(router),  +			active(false) { +	responder = new IBBResponder(this, router);  }  IBBReceiveSession::~IBBReceiveSession() { +	if (active) { +		SWIFT_LOG(warning) << "Session still active" << std::endl; +	} +	delete responder;  }  void IBBReceiveSession::start() {  	active = true; +	responder->start();  }  void IBBReceiveSession::stop() { -	if (active && router->isAvailable()) { -		IBBRequest::create(from, IBB::createIBBClose(id), router)->send(); +	responder->stop(); +	if (active) { +		if (router->isAvailable()) { +			IBBRequest::create(from, IBB::createIBBClose(id), router)->send(); +		} +		finish(boost::optional<FileTransferError>());  	} -	finish(boost::optional<FileTransferError>());  }  void IBBReceiveSession::finish(boost::optional<FileTransferError> error) { @@ -36,34 +101,4 @@ void IBBReceiveSession::finish(boost::optional<FileTransferError> error) {  	onFinished(error);  } -bool IBBReceiveSession::handleSetRequest(const JID& from, const JID&, const std::string& id, IBB::ref ibb) { -	if (from == this->from && ibb->getStreamID() == id) { -		if (ibb->getAction() == IBB::Data) { -			if (sequenceNumber == ibb->getSequenceNumber()) { -				bytestream->write(ibb->getData()); -				receivedSize += ibb->getData().getSize(); -				if (receivedSize >= size) { -					if (receivedSize > size) { -						std::cerr << "Warning: Received more data than expected" << std::endl; -					} -					finish(boost::optional<FileTransferError>()); -				} -			} -			else { -				sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel); -				finish(FileTransferError(FileTransferError::ClosedError)); -			} -		} -		else if (ibb->getAction() == IBB::Open) { -			sendResponse(from, id, IBB::ref()); -		} -		else if (ibb->getAction() == IBB::Close) { -			sendResponse(from, id, IBB::ref()); -			finish(FileTransferError(FileTransferError::ClosedError)); -		} -		return true; -	} -	return false; -} -  } diff --git a/Swiften/FileTransfer/IBBReceiveSession.h b/Swiften/FileTransfer/IBBReceiveSession.h index 6d936de..d512025 100644 --- a/Swiften/FileTransfer/IBBReceiveSession.h +++ b/Swiften/FileTransfer/IBBReceiveSession.h @@ -7,27 +7,30 @@  #pragma once  #include <boost/shared_ptr.hpp> -#include <boost/optional.hpp> +#include <boost/optional/optional_fwd.hpp>  #include "Swiften/Base/boost_bsignals.h"  #include "Swiften/FileTransfer/WriteBytestream.h"  #include "Swiften/JID/JID.h"  #include "Swiften/Elements/IBB.h" -#include "Swiften/Elements/ErrorPayload.h"  #include "Swiften/FileTransfer/FileTransferError.h" -#include "Swiften/Queries/SetResponder.h"  namespace Swift {  	class IQRouter; -	class IBBReceiveSession : public SetResponder<IBB> { +	class IBBReceiveSession {  		public: -			IBBReceiveSession(const std::string& id, const JID& from, size_t size, WriteBytestream::ref bytestream, IQRouter* router); +			IBBReceiveSession( +					const std::string& id,  +					const JID& from,  +					size_t size,  +					IQRouter* router);  			~IBBReceiveSession();  			void start();  			void stop(); +			boost::signal<void (const std::vector<unsigned char>&)> onDataReceived;  			boost::signal<void (boost::optional<FileTransferError>)> onFinished;  		private: @@ -35,13 +38,14 @@ namespace Swift {  			void finish(boost::optional<FileTransferError>);  		private: +			class IBBResponder; +			friend class IBBResponder; +  			std::string id;  			JID from;  			size_t size; -			WriteBytestream::ref bytestream;  			IQRouter* router; -			int sequenceNumber; +			IBBResponder* responder;  			bool active; -			size_t receivedSize;  	};  } diff --git a/Swiften/FileTransfer/IBBSendSession.cpp b/Swiften/FileTransfer/IBBSendSession.cpp index 0fb47d3..c31fe4a 100644 --- a/Swiften/FileTransfer/IBBSendSession.cpp +++ b/Swiften/FileTransfer/IBBSendSession.cpp @@ -38,13 +38,13 @@ void IBBSendSession::handleIBBResponse(IBB::ref, ErrorPayload::ref error) {  	if (!error) {  		if (!bytestream->isFinished()) {  			try { -				ByteArray data = bytestream->read(blockSize); +				std::vector<unsigned char> data = bytestream->read(blockSize);  				IBBRequest::ref request = IBBRequest::create(to, IBB::createIBBData(id, sequenceNumber, data), router);  				sequenceNumber++;  				request->onResponse.connect(boost::bind(&IBBSendSession::handleIBBResponse, this, _1, _2));  				request->send();  			} -			catch (const BytestreamException& e) { +			catch (const BytestreamException&) {  				finish(FileTransferError(FileTransferError::ReadError));  			}  		} diff --git a/Swiften/FileTransfer/IncomingFileTransfer.cpp b/Swiften/FileTransfer/IncomingFileTransfer.cpp index 238ccce..7c97e4d 100644 --- a/Swiften/FileTransfer/IncomingFileTransfer.cpp +++ b/Swiften/FileTransfer/IncomingFileTransfer.cpp @@ -4,20 +4,11 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/FileTransfer/IncomingFileTransfer.h" +#include <Swiften/FileTransfer/IncomingFileTransfer.h>  namespace Swift {  IncomingFileTransfer::~IncomingFileTransfer() { - -} - -/*void IncomingFileTransfer::accept(WriteBytestream::ref) { -  } -void IncomingFileTransfer::stop() { - -}*/ -  } diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.cpp b/Swiften/FileTransfer/IncomingFileTransferManager.cpp index 5535840..79d2391 100644 --- a/Swiften/FileTransfer/IncomingFileTransferManager.cpp +++ b/Swiften/FileTransfer/IncomingFileTransferManager.cpp @@ -10,8 +10,10 @@  #include <Swiften/Elements/JingleDescription.h>  #include <Swiften/Elements/JingleFileTransferDescription.h> -#include <Swiften/Elements/JingleIBBTransport.h> +#include <Swiften/Elements/JingleIBBTransportPayload.h> +#include <Swiften/Elements/JingleS5BTransportPayload.h>  #include <Swiften/Jingle/JingleSessionManager.h> +#include <Swiften/Jingle/Jingle.h>  #include <Swiften/FileTransfer/IncomingJingleFileTransfer.h>  namespace Swift { @@ -24,12 +26,12 @@ IncomingFileTransferManager::~IncomingFileTransferManager() {  	jingleSessionManager->removeIncomingSessionHandler(this);  } -bool IncomingFileTransferManager::handleIncomingJingleSession(IncomingJingleSession::ref session) { -	JingleContent::ref content = session->getContentWithDescription<JingleFileTransferDescription>(); -	if (content) { -		// Check for supported transports -		if (content->getTransport<JingleIBBTransport>()) { -			IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session); +bool IncomingFileTransferManager::handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents) { +	if (JingleContentPayload::ref content = Jingle::getContentWithDescription<JingleFileTransferDescription>(contents)) { +		if (content->getTransport<JingleIBBTransportPayload>() || content->getTransport<JingleS5BTransportPayload>()) { +			RemoteJingleTransportCandidateSelectorFactory* a; +			LocalJingleTransportCandidateGeneratorFactory* b; +			IncomingJingleFileTransfer::ref transfer = boost::make_shared<IncomingJingleFileTransfer>(session, content, a, b, router);  			onIncomingFileTransfer(transfer);  		}  		else { diff --git a/Swiften/FileTransfer/IncomingFileTransferManager.h b/Swiften/FileTransfer/IncomingFileTransferManager.h index a54b5cd..428a838 100644 --- a/Swiften/FileTransfer/IncomingFileTransferManager.h +++ b/Swiften/FileTransfer/IncomingFileTransferManager.h @@ -15,6 +15,8 @@  namespace Swift {  	class IQRouter;  	class JingleSessionManager; +	class RemoteJingleTransportCandidateSelectorFactory; +	class LocalJingleTransportCandidateGeneratorFactory;  	class IncomingFileTransferManager : public IncomingJingleSessionHandler {  		public: @@ -24,7 +26,7 @@ namespace Swift {  			boost::signal<void (IncomingFileTransfer::ref)> onIncomingFileTransfer;  		private: -			bool handleIncomingJingleSession(IncomingJingleSession::ref session); +			bool handleIncomingJingleSession(JingleSession::ref session, const std::vector<JingleContentPayload::ref>& contents);  		private:  			JingleSessionManager* jingleSessionManager; diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp index cb2f65c..904b53e 100644 --- a/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp +++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.cpp @@ -6,14 +6,164 @@  #include <Swiften/FileTransfer/IncomingJingleFileTransfer.h> +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h> +#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h> +#include <Swiften/Elements/JingleIBBTransportPayload.h> +#include <Swiften/Elements/JingleS5BTransportPayload.h> +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h> +  namespace Swift { -IncomingJingleFileTransfer::IncomingJingleFileTransfer(IncomingJingleSession::ref session) : session(session) { +IncomingJingleFileTransfer::IncomingJingleFileTransfer( +		JingleSession::ref session, +		JingleContentPayload::ref content, +		RemoteJingleTransportCandidateSelectorFactory* candidateSelectorFactory, +		LocalJingleTransportCandidateGeneratorFactory* candidateGeneratorFactory, +		IQRouter* router) : +			session(session), +			router(router), +			initialContent(content), +			contentID(content->getName(), content->getCreator()), +			state(Initial), +			remoteTransportCandidateSelectFinished(false), +			localTransportCandidateSelectFinished(false) { +	 +	candidateSelector = candidateSelectorFactory->createCandidateSelector(); +	candidateSelector->onRemoteTransportCandidateSelectFinished.connect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1)); + +	candidateGenerator = candidateGeneratorFactory->createCandidateGenerator(); +	candidateGenerator->onLocalTransportCandidatesGenerated.connect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1)); + +	session->onTransportInfoReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2)); +	session->onTransportReplaceReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2)); +	session->onSessionTerminateReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this)); + +	description = initialContent->getDescription<JingleFileTransferDescription>(); +	assert(description); +} + +IncomingJingleFileTransfer::~IncomingJingleFileTransfer() { +	session->onSessionTerminateReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleSessionTerminateReceived, this)); +	session->onTransportReplaceReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportReplaceReceived, this, _1, _2)); +	session->onTransportInfoReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportInfoReceived, this, _1, _2)); + +	candidateGenerator->onLocalTransportCandidatesGenerated.disconnect(boost::bind(&IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated, this, _1)); +	delete candidateGenerator; +	candidateSelector->onRemoteTransportCandidateSelectFinished.disconnect(boost::bind(&IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished, this, _1)); +	delete candidateSelector;  }  void IncomingJingleFileTransfer::accept(WriteBytestream::ref stream) { +	assert(!stream);  	this->stream = stream; + +	if (JingleIBBTransportPayload::ref ibbTransport = initialContent->getTransport<JingleIBBTransportPayload>()) { +		setActiveTransport(createIBBTransport(ibbTransport)); +		session->accept(); +	} +	else if (JingleS5BTransportPayload::ref s5bTransport = initialContent->getTransport<JingleS5BTransportPayload>()) { +		state = CreatingInitialTransports; +		candidateSelector->addRemoteTransportCandidates(s5bTransport); +		candidateGenerator->generateLocalTransportCandidates(); +	} +	else { +		assert(false); +	} +} + + +void IncomingJingleFileTransfer::handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates) { +	if (state == CreatingInitialTransports) { +		if (!candidates) { +			localTransportCandidateSelectFinished = true; +		} +		session->accept(candidates); +		state = NegotiatingTransport; +		candidateSelector->selectCandidate(); +	} +} + + +void IncomingJingleFileTransfer::handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref transport) { +	remoteTransportCandidateSelectFinished = true; +	selectedRemoteTransportCandidate = transport; +	session->sendTransportInfo(contentID, transport); +	checkCandidateSelected(); +} + +void IncomingJingleFileTransfer::checkCandidateSelected() { +	if (localTransportCandidateSelectFinished && remoteTransportCandidateSelectFinished) { +		if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate) && candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) { +			if (candidateGenerator->getPriority(selectedLocalTransportCandidate) > candidateSelector->getPriority(selectedRemoteTransportCandidate)) { +				setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate)); +			} +			else { +				setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate)); +			} +		} +		else if (candidateSelector->isActualCandidate(selectedRemoteTransportCandidate)) { +			setActiveTransport(candidateSelector->selectTransport(selectedRemoteTransportCandidate)); +		} +		else if (candidateGenerator->isActualCandidate(selectedLocalTransportCandidate)) { +			setActiveTransport(candidateGenerator->selectTransport(selectedLocalTransportCandidate)); +		} +		else { +			state = WaitingForFallbackOrTerminate; +		} +	} +} + +void IncomingJingleFileTransfer::setActiveTransport(JingleTransport::ref transport) { +	state = Transferring; +	activeTransport = transport; +	activeTransport->onDataReceived.connect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1)); +	activeTransport->start(); +} + +void IncomingJingleFileTransfer::handleSessionTerminateReceived() { +	// TODO +	state = Terminated; +} + +void IncomingJingleFileTransfer::handleTransportDataReceived(const std::vector<unsigned char>& data) { +	stream->write(data); +} + + +void IncomingJingleFileTransfer::handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref transport) { +	localTransportCandidateSelectFinished = true; +	selectedLocalTransportCandidate = transport; +	if (candidateGenerator->isActualCandidate(transport)) { +		candidateSelector->setMinimumPriority(candidateGenerator->getPriority(transport)); +	} +	checkCandidateSelected(); +} + +void IncomingJingleFileTransfer::handleTransportReplaceReceived(const JingleContentID& content, JingleTransportPayload::ref transport) { +	if (JingleIBBTransportPayload::ref ibbTransport = boost::dynamic_pointer_cast<JingleIBBTransportPayload>(transport)) { +		setActiveTransport(createIBBTransport(ibbTransport)); +		session->acceptTransport(content, transport); +	} +	else { +		session->rejectTransport(content, transport); +	} +} + +void IncomingJingleFileTransfer::stopActiveTransport() { +	if (activeTransport) { +		activeTransport->stop(); +		activeTransport->onDataReceived.disconnect(boost::bind(&IncomingJingleFileTransfer::handleTransportDataReceived, this, _1)); +	} +} + +JingleIncomingIBBTransport::ref IncomingJingleFileTransfer::createIBBTransport(JingleIBBTransportPayload::ref ibbTransport) { +	return boost::make_shared<JingleIncomingIBBTransport>(session->getInitiator(), ibbTransport->getSessionID(), description->getOffer()->size, router);  }  } diff --git a/Swiften/FileTransfer/IncomingJingleFileTransfer.h b/Swiften/FileTransfer/IncomingJingleFileTransfer.h index d69449e..164d868 100644 --- a/Swiften/FileTransfer/IncomingJingleFileTransfer.h +++ b/Swiften/FileTransfer/IncomingJingleFileTransfer.h @@ -8,20 +8,71 @@  #include <boost/shared_ptr.hpp> -#include <Swiften/Jingle/IncomingJingleSession.h> +#include <Swiften/Jingle/JingleSession.h> +#include <Swiften/Jingle/JingleContentID.h>  #include <Swiften/FileTransfer/IncomingFileTransfer.h> +#include <Swiften/FileTransfer/JingleTransport.h> +#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h> +#include <Swiften/Elements/JingleContentPayload.h> +#include <Swiften/Elements/JingleFileTransferDescription.h> +#include <Swiften/Elements/JingleIBBTransportPayload.h>  namespace Swift { +	class IQRouter; +	class RemoteJingleTransportCandidateSelectorFactory; +	class LocalJingleTransportCandidateGeneratorFactory; +	class RemoteJingleTransportCandidateSelector; +	class LocalJingleTransportCandidateGenerator; +  	class IncomingJingleFileTransfer : public IncomingFileTransfer {  		public:  			typedef boost::shared_ptr<IncomingJingleFileTransfer> ref; +			enum State { +				Initial, +				CreatingInitialTransports, +				NegotiatingTransport, +				Transferring, +				WaitingForFallbackOrTerminate, +				Terminated +			}; -			IncomingJingleFileTransfer(IncomingJingleSession::ref session); +			IncomingJingleFileTransfer( +					JingleSession::ref, +					JingleContentPayload::ref content, +					RemoteJingleTransportCandidateSelectorFactory*, +					LocalJingleTransportCandidateGeneratorFactory*, +					IQRouter* router); +			~IncomingJingleFileTransfer();  			virtual void accept(WriteBytestream::ref);  		private: -			IncomingJingleSession::ref session; +			void handleSessionTerminateReceived(); +			void handleTransportReplaceReceived(const JingleContentID&, JingleTransportPayload::ref); +			void handleTransportInfoReceived(const JingleContentID&, JingleTransportPayload::ref); +			void handleLocalTransportCandidatesGenerated(JingleTransportPayload::ref candidates); +			void handleRemoteTransportCandidateSelectFinished(JingleTransportPayload::ref candidate); +			void setActiveTransport(JingleTransport::ref transport); +			void handleTransportDataReceived(const std::vector<unsigned char>& data); +			void stopActiveTransport(); +			void checkCandidateSelected(); +			JingleIncomingIBBTransport::ref createIBBTransport(JingleIBBTransportPayload::ref ibbTransport); + +		private: +			JingleSession::ref session; +			IQRouter* router; +			JingleContentPayload::ref initialContent; +			JingleContentID contentID; +			State state; +			JingleFileTransferDescription::ref description;  			WriteBytestream::ref stream; +			RemoteJingleTransportCandidateSelector* candidateSelector; +			LocalJingleTransportCandidateGenerator* candidateGenerator; +			bool remoteTransportCandidateSelectFinished; +			JingleTransportPayload::ref selectedRemoteTransportCandidate; +			bool localTransportCandidateSelectFinished; +			JingleTransportPayload::ref selectedLocalTransportCandidate; + +			JingleTransport::ref activeTransport;  	};  } diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp new file mode 100644 index 0000000..0ca899f --- /dev/null +++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/JingleIncomingIBBTransport.h> + +namespace Swift { + +JingleIncomingIBBTransport::JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router) : ibbSession(from, id, size, router) { +	ibbSession.onDataReceived.connect(boost::ref(onDataReceived)); +} + +void JingleIncomingIBBTransport::start() { +	ibbSession.start(); +} + +void JingleIncomingIBBTransport::stop() { +	ibbSession.stop(); +} + +} diff --git a/Swiften/FileTransfer/JingleIncomingIBBTransport.h b/Swiften/FileTransfer/JingleIncomingIBBTransport.h new file mode 100644 index 0000000..e2fa485 --- /dev/null +++ b/Swiften/FileTransfer/JingleIncomingIBBTransport.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 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/JingleTransport.h> +#include <Swiften/FileTransfer/IBBReceiveSession.h> + +namespace Swift { +	class JingleIncomingIBBTransport : public JingleTransport { +		public: +			typedef boost::shared_ptr<JingleIncomingIBBTransport> ref; + +			JingleIncomingIBBTransport(const JID& from, const std::string& id, size_t size, IQRouter* router); + +			virtual void start(); +			virtual void stop(); + +		private: +			IBBReceiveSession ibbSession; +	}; +} diff --git a/Swiften/FileTransfer/JingleTransport.cpp b/Swiften/FileTransfer/JingleTransport.cpp new file mode 100644 index 0000000..c507922 --- /dev/null +++ b/Swiften/FileTransfer/JingleTransport.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/JingleTransport.h> + +namespace Swift { + +JingleTransport::~JingleTransport() { + +} + +} diff --git a/Swiften/FileTransfer/JingleTransport.h b/Swiften/FileTransfer/JingleTransport.h new file mode 100644 index 0000000..1d163d0 --- /dev/null +++ b/Swiften/FileTransfer/JingleTransport.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 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> + +namespace Swift { +	class JingleTransport { +		public: +			typedef boost::shared_ptr<JingleTransport> ref; + +			virtual ~JingleTransport(); + +			virtual void start() = 0; +			virtual void stop() = 0; + +			boost::signal<void (const std::vector<unsigned char>&)> onDataReceived; +	}; +} diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp new file mode 100644 index 0000000..852902b --- /dev/null +++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h> + +namespace Swift { + +LocalJingleTransportCandidateGenerator::~LocalJingleTransportCandidateGenerator() { +} + +} diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h new file mode 100644 index 0000000..c111005 --- /dev/null +++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/boost_bsignals.h> + +#include <Swiften/Elements/JingleTransportPayload.h> +#include <Swiften/FileTransfer/JingleTransport.h> + +namespace Swift { +	class LocalJingleTransportCandidateGenerator { +		public: +			virtual ~LocalJingleTransportCandidateGenerator(); + +			virtual void generateLocalTransportCandidates() = 0; + +			virtual bool isActualCandidate(JingleTransportPayload::ref) = 0; +			virtual int getPriority(JingleTransportPayload::ref) = 0; +			virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0; + +			boost::signal<void (JingleTransportPayload::ref)> onLocalTransportCandidatesGenerated; +	}; +} diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp new file mode 100644 index 0000000..a1e3874 --- /dev/null +++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h> + +namespace Swift { + +LocalJingleTransportCandidateGeneratorFactory::~LocalJingleTransportCandidateGeneratorFactory() { +} + +} diff --git a/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h new file mode 100644 index 0000000..c969fc7 --- /dev/null +++ b/Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { +	class LocalJingleTransportCandidateGenerator; + +	class LocalJingleTransportCandidateGeneratorFactory { +		public: +			virtual ~LocalJingleTransportCandidateGeneratorFactory(); + +			virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() = 0; +	}; +} diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.cpp b/Swiften/FileTransfer/OutgoingFileTransfer.cpp index 32f7e17..94d4348 100644 --- a/Swiften/FileTransfer/OutgoingFileTransfer.cpp +++ b/Swiften/FileTransfer/OutgoingFileTransfer.cpp @@ -4,75 +4,11 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/FileTransfer/OutgoingFileTransfer.h" - -#include <boost/bind.hpp> - -#include "Swiften/FileTransfer/StreamInitiationRequest.h" -#include "Swiften/FileTransfer/BytestreamsRequest.h" -#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h" -#include "Swiften/FileTransfer/IBBSendSession.h" +#include <Swiften/FileTransfer/OutgoingFileTransfer.h>  namespace Swift { -OutgoingFileTransfer::OutgoingFileTransfer(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) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) { -} - -void OutgoingFileTransfer::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(to, streamInitiation, iqRouter); -	request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2)); -	request->send(); -} - -void OutgoingFileTransfer::stop() { -} - -void OutgoingFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) { -	if (error) { -		finish(FileTransferError()); -	} -	else { -		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") { -			socksServer->addBytestream(id, from, to, bytestream);  -			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(to, bytestreams, iqRouter); -			request->onResponse.connect(boost::bind(&OutgoingFileTransfer::handleBytestreamsRequestResponse, this, _1, _2)); -			request->send(); -		} -		else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") { -			ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, to, bytestream, iqRouter)); -			ibbSession->onFinished.connect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1)); -			ibbSession->start(); -		} -	} -} - -void OutgoingFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) { -	if (error) { -		finish(FileTransferError()); -	} -	//socksServer->onTransferFinished.connect(); -} - -void OutgoingFileTransfer::finish(boost::optional<FileTransferError> error) { -	if (ibbSession) { -		ibbSession->onFinished.disconnect(boost::bind(&OutgoingFileTransfer::handleIBBSessionFinished, this, _1)); -		ibbSession.reset(); -	} -	socksServer->removeBytestream(id, from, to);  -	onFinished(error); -} - -void OutgoingFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) { -	finish(error); +OutgoingFileTransfer::~OutgoingFileTransfer() {  }  } diff --git a/Swiften/FileTransfer/OutgoingFileTransfer.h b/Swiften/FileTransfer/OutgoingFileTransfer.h index a694c13..a8c1e81 100644 --- a/Swiften/FileTransfer/OutgoingFileTransfer.h +++ b/Swiften/FileTransfer/OutgoingFileTransfer.h @@ -6,47 +6,12 @@  #pragma once -#include <boost/shared_ptr.hpp> - -#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" -  namespace Swift { -	class IQRouter; -	class SOCKS5BytestreamServer; -  	class OutgoingFileTransfer {  		public: -			OutgoingFileTransfer(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); - -			void start(); -			void stop(); - -			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); +			virtual ~OutgoingFileTransfer(); -		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; +			virtual void start() = 0; +			virtual void stop() = 0;  	};  } diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp new file mode 100644 index 0000000..2ed3a9d --- /dev/null +++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.cpp @@ -0,0 +1,78 @@ +/* + * 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/OutgoingSIFileTransfer.h" + +#include <boost/bind.hpp> + +#include "Swiften/FileTransfer/StreamInitiationRequest.h" +#include "Swiften/FileTransfer/BytestreamsRequest.h" +#include "Swiften/FileTransfer/SOCKS5BytestreamServer.h" +#include "Swiften/FileTransfer/IBBSendSession.h" + +namespace Swift { + +OutgoingSIFileTransfer::OutgoingSIFileTransfer(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) : id(id), from(from), to(to), name(name), size(size), description(description), bytestream(bytestream), iqRouter(iqRouter), socksServer(socksServer) { +} + +void OutgoingSIFileTransfer::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(to, streamInitiation, iqRouter); +	request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleStreamInitiationRequestResponse, this, _1, _2)); +	request->send(); +} + +void OutgoingSIFileTransfer::stop() { +} + +void OutgoingSIFileTransfer::handleStreamInitiationRequestResponse(StreamInitiation::ref response, ErrorPayload::ref error) { +	if (error) { +		finish(FileTransferError()); +	} +	else { +		if (response->getRequestedMethod() == "http://jabber.org/protocol/bytestreams") { +			socksServer->addBytestream(id, from, to, bytestream);  +			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(to, bytestreams, iqRouter); +			request->onResponse.connect(boost::bind(&OutgoingSIFileTransfer::handleBytestreamsRequestResponse, this, _1, _2)); +			request->send(); +		} +		else if (response->getRequestedMethod() == "http://jabber.org/protocol/ibb") { +			ibbSession = boost::shared_ptr<IBBSendSession>(new IBBSendSession(id, to, bytestream, iqRouter)); +			ibbSession->onFinished.connect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); +			ibbSession->start(); +		} +	} +} + +void OutgoingSIFileTransfer::handleBytestreamsRequestResponse(Bytestreams::ref, ErrorPayload::ref error) { +	if (error) { +		finish(FileTransferError()); +	} +	//socksServer->onTransferFinished.connect(); +} + +void OutgoingSIFileTransfer::finish(boost::optional<FileTransferError> error) { +	if (ibbSession) { +		ibbSession->onFinished.disconnect(boost::bind(&OutgoingSIFileTransfer::handleIBBSessionFinished, this, _1)); +		ibbSession.reset(); +	} +	socksServer->removeBytestream(id, from, to);  +	onFinished(error); +} + +void OutgoingSIFileTransfer::handleIBBSessionFinished(boost::optional<FileTransferError> error) { +	finish(error); +} + +} diff --git a/Swiften/FileTransfer/OutgoingSIFileTransfer.h b/Swiften/FileTransfer/OutgoingSIFileTransfer.h new file mode 100644 index 0000000..cdf988f --- /dev/null +++ b/Swiften/FileTransfer/OutgoingSIFileTransfer.h @@ -0,0 +1,53 @@ +/* + * 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" + +namespace Swift { +	class IQRouter; +	class SOCKS5BytestreamServer; + +	class OutgoingSIFileTransfer : public OutgoingFileTransfer { +		public: +			OutgoingSIFileTransfer(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); + +			virtual void start(); +			virtual void stop(); + +			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; +	}; +} diff --git a/Swiften/FileTransfer/ReadBytestream.h b/Swiften/FileTransfer/ReadBytestream.h index 4da2bc2..2601192 100644 --- a/Swiften/FileTransfer/ReadBytestream.h +++ b/Swiften/FileTransfer/ReadBytestream.h @@ -6,13 +6,14 @@  #pragma once -#include "Swiften/Base/ByteArray.h" +#include <vector> +#include <cstring>  namespace Swift {  	class ReadBytestream {  		public:  			virtual ~ReadBytestream(); -			virtual ByteArray read(size_t size) = 0; +			virtual std::vector<unsigned char> read(size_t size) = 0;  			virtual bool isFinished() const = 0;  	};  } diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp new file mode 100644 index 0000000..338f221 --- /dev/null +++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h> + +namespace Swift { + +RemoteJingleTransportCandidateSelector::~RemoteJingleTransportCandidateSelector() { +} + +} diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h new file mode 100644 index 0000000..b12b06b --- /dev/null +++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/boost_bsignals.h> + +#include <Swiften/Elements/JingleTransportPayload.h> +#include <Swiften/FileTransfer/JingleTransport.h> + +namespace Swift { +	class RemoteJingleTransportCandidateSelector { +		public: +			virtual ~RemoteJingleTransportCandidateSelector(); + +			virtual void addRemoteTransportCandidates(JingleTransportPayload::ref) = 0; +			virtual void selectCandidate() = 0; +			virtual void setMinimumPriority(int) = 0; + +			virtual bool isActualCandidate(JingleTransportPayload::ref) = 0; +			virtual int getPriority(JingleTransportPayload::ref) = 0; +			virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) = 0; + +			boost::signal<void (JingleTransportPayload::ref)> onRemoteTransportCandidateSelectFinished; +	}; +} diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp new file mode 100644 index 0000000..36b7cba --- /dev/null +++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h> + +namespace Swift { + +RemoteJingleTransportCandidateSelectorFactory::~RemoteJingleTransportCandidateSelectorFactory() { +} + +} diff --git a/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h new file mode 100644 index 0000000..caa3097 --- /dev/null +++ b/Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { +	class RemoteJingleTransportCandidateSelector; + +	class RemoteJingleTransportCandidateSelectorFactory { +		public: +			virtual ~RemoteJingleTransportCandidateSelectorFactory(); + +			virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() = 0; +	}; +} diff --git a/Swiften/FileTransfer/SConscript b/Swiften/FileTransfer/SConscript index ea9e7bb..24fc9e8 100644 --- a/Swiften/FileTransfer/SConscript +++ b/Swiften/FileTransfer/SConscript @@ -1,10 +1,17 @@ -Import("swiften_env") +Import("swiften_env", "env")  sources = [  		"OutgoingFileTransfer.cpp", +		"OutgoingSIFileTransfer.cpp",  		"IncomingFileTransfer.cpp",  		"IncomingJingleFileTransfer.cpp", -		"IncomingFileTransferManager.cpp",	 +		"IncomingFileTransferManager.cpp", +		"RemoteJingleTransportCandidateSelector.cpp", +		"RemoteJingleTransportCandidateSelectorFactory.cpp", +		"LocalJingleTransportCandidateGenerator.cpp", +		"LocalJingleTransportCandidateGeneratorFactory.cpp", +		"JingleTransport.cpp", +		"JingleIncomingIBBTransport.cpp",  		"ReadBytestream.cpp",  		"WriteBytestream.cpp",  		"FileReadBytestream.cpp", @@ -17,3 +24,9 @@ sources = [  	]  swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) + +env.Append(UNITTEST_SOURCES = [ +			File("UnitTest/SOCKS5BytestreamServerSessionTest.cpp"), +			File("UnitTest/IBBSendSessionTest.cpp"), +			File("UnitTest/IBBReceiveSessionTest.cpp"), +	]) diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp index 9951f7a..268ba4c 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp +++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp @@ -7,6 +7,7 @@  #include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h"  #include <boost/bind.hpp> +#include <iostream>  #include "Swiften/Base/ByteArray.h"  #include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h" @@ -98,7 +99,7 @@ void SOCKS5BytestreamServerSession::sendData() {  		try {  			connection->write(bytestream->read(chunkSize));  		} -		catch (const BytestreamException& e) { +		catch (const BytestreamException&) {  			finish(true);  		}  	} diff --git a/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp new file mode 100644 index 0000000..590443f --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <vector> +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/ByteArray.h> +#include "Swiften/FileTransfer/IBBReceiveSession.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Client/DummyStanzaChannel.h" + +using namespace Swift; + +class IBBReceiveSessionTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(IBBReceiveSessionTest); +		CPPUNIT_TEST(testOpen); +		CPPUNIT_TEST(testReceiveData); +		CPPUNIT_TEST(testReceiveMultipleData); +		CPPUNIT_TEST(testReceiveDataForOtherSession); +		CPPUNIT_TEST(testReceiveDataOutOfOrder); +		CPPUNIT_TEST(testReceiveLastData); +		CPPUNIT_TEST(testReceiveClose); +		CPPUNIT_TEST(testStopWhileActive); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			stanzaChannel = new DummyStanzaChannel(); +			iqRouter = new IQRouter(stanzaChannel); +			finished = false; +		} + +		void tearDown() { +			delete iqRouter; +			delete stanzaChannel; +		} + +		void testOpen() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(0, "id-open")); +			CPPUNIT_ASSERT(!finished); + +			testling->stop(); +		} + +		void testReceiveData() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, ByteArray::create("abc")), "foo@bar.com/baz", "id-a")); + +			CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(1, "id-a")); +			CPPUNIT_ASSERT(ByteArray::create("abc") == receivedData); +			CPPUNIT_ASSERT(!finished); + +			testling->stop(); +		} + +		void testReceiveMultipleData() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, ByteArray::create("abc")), "foo@bar.com/baz", "id-a")); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, ByteArray::create("def")), "foo@bar.com/baz", "id-b")); + +			CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); +			CPPUNIT_ASSERT(ByteArray::create("abcdef") == receivedData); +			CPPUNIT_ASSERT(!finished); + +			testling->stop(); +		} + +		void testReceiveDataForOtherSession() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("othersession", 0, ByteArray::create("abc")), "foo@bar.com/baz", "id-a")); + +			CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(1, "id-a")); + +			testling->stop(); +		} + +		void testReceiveDataOutOfOrder() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, ByteArray::create("abc")), "foo@bar.com/baz", "id-a")); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, ByteArray::create("def")), "foo@bar.com/baz", "id-b")); + +			CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(2, "id-b")); +			CPPUNIT_ASSERT(finished); +			CPPUNIT_ASSERT(error); + +			testling->stop(); +		} + +		void testReceiveLastData() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession", 6)); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, ByteArray::create("abc")), "foo@bar.com/baz", "id-a")); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, ByteArray::create("def")), "foo@bar.com/baz", "id-b")); + +			CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); +			CPPUNIT_ASSERT(ByteArray::create("abcdef") == receivedData); +			CPPUNIT_ASSERT(finished); +			CPPUNIT_ASSERT(!error); + +			testling->stop(); +		} + +		void testReceiveClose() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBClose("mysession"), "foo@bar.com/baz", "id-close")); + +			CPPUNIT_ASSERT(finished); +			CPPUNIT_ASSERT(error); + +			testling->stop(); +		} + +		void testStopWhileActive() { +			std::auto_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); +			testling->start(); +			stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + +			testling->stop(); + +			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set)); +			IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>(); +			CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction()); +			CPPUNIT_ASSERT_EQUAL(std::string("mysession"), ibb->getStreamID()); +			CPPUNIT_ASSERT(finished); +			CPPUNIT_ASSERT(!error); +		} + +	private: +		IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { +			IQ::ref request = IQ::createRequest(IQ::Set, JID("baz@fum.com/dum"), id, ibb); +			request->setFrom(from); +			return request; +		} + +		IBBReceiveSession* createSession(const std::string& from, const std::string& id, size_t size = 0x1000) { +			IBBReceiveSession* session = new IBBReceiveSession(id, JID(from), size, iqRouter); +			session->onDataReceived.connect(boost::bind(&IBBReceiveSessionTest::handleDataReceived, this, _1)); +			session->onFinished.connect(boost::bind(&IBBReceiveSessionTest::handleFinished, this, _1)); +			return session; +		} + + +		void handleFinished(boost::optional<FileTransferError> error) { +			finished = true; +			this->error = error; +		} + +		void handleDataReceived(const std::vector<unsigned char>& data) { +			receivedData.insert(receivedData.end(), data.begin(), data.end()); +		} + +	private: +		DummyStanzaChannel* stanzaChannel; +		IQRouter* iqRouter; +		bool finished; +		boost::optional<FileTransferError> error; +		std::vector<unsigned char> receivedData; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(IBBReceiveSessionTest); diff --git a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp index 0cd273a..32df34b 100644 --- a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp +++ b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp @@ -4,13 +4,12 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/Base/ByteArray.h" -  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h>  #include <vector>  #include <boost/bind.hpp> +#include "Swiften/Base/ByteArray.h"  #include "Swiften/FileTransfer/IBBSendSession.h"  #include "Swiften/FileTransfer/ByteArrayReadBytestream.h"  #include "Swiften/Queries/IQRouter.h" @@ -33,7 +32,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {  		void setUp() {  			stanzaChannel = new DummyStanzaChannel();  			iqRouter = new IQRouter(stanzaChannel); -			bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg"))); +			bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray::create("abcdefg")));  		}  		void tearDown() { @@ -66,7 +65,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set));  			IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>();  			CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); -			CPPUNIT_ASSERT_EQUAL(ByteArray("abc"), ibb->getData()); +			CPPUNIT_ASSERT(ByteArray::create("abc") == ibb->getData());  			CPPUNIT_ASSERT_EQUAL(0, ibb->getSequenceNumber());  			CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID());  		} @@ -82,7 +81,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(2, JID("foo@bar.com/baz"), IQ::Set));  			IBB::ref ibb = stanzaChannel->sentStanzas[2]->getPayload<IBB>();  			CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); -			CPPUNIT_ASSERT_EQUAL(ByteArray("def"), ibb->getData()); +			CPPUNIT_ASSERT(ByteArray::create("def") == ibb->getData());  			CPPUNIT_ASSERT_EQUAL(1, ibb->getSequenceNumber());  			CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID());  		} diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp index c6d246d..1c1a246 100644 --- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp +++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp @@ -35,7 +35,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			eventLoop = new DummyEventLoop();  			connection = boost::shared_ptr<DummyConnection>(new DummyConnection(eventLoop));  			connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1)); -			stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg"))); +			stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray::create("abcdefg")));  		}  		void tearDown() { @@ -47,20 +47,20 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());  			StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); -			receive(ByteArray("\x05\x02\x01\x02")); +			receive(ByteArray::create("\x05\x02\x01\x02")); -			CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData); +			CPPUNIT_ASSERT(ByteArray::create("\x05\x00", 2) == receivedData);  		}  		void testAuthenticate_Chunked() {  			std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession());  			StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); -			receive(ByteArray("\x05\x02\x01")); +			receive(ByteArray::create("\x05\x02\x01")); -			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.getSize())); -			receive(ByteArray("\x01")); -			CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData); +			CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.size())); +			receive(ByteArray::create("\x01")); +			CPPUNIT_ASSERT(ByteArray::create("\x05\x00", 2) == receivedData);  		}  		void testRequest() { @@ -70,8 +70,8 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			authenticate();  			ByteArray hostname("abcdef"); -			receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2)); -			CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), ByteArray(receivedData.getData(), 13)); +			receive(ByteArray(ByteArray::create("\x05\x01\x00\x03", 4)) + hostname.getSize() + hostname + ByteArray::create("\x00\x00", 2)); +			CPPUNIT_ASSERT(ByteArray::create("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == ByteArray::create(&receivedData[0], 13));  		}  		void testRequest_UnknownBytestream() { @@ -80,8 +80,8 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			authenticate();  			ByteArray hostname("abcdef"); -			receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2)); -			CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), receivedData); +			receive(ByteArray(ByteArray::create("\x05\x01\x00\x03", 4)) + hostname.getSize() + hostname + ByteArray::create("\x00\x00", 2)); +			CPPUNIT_ASSERT(ByteArray::create("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == receivedData);  		}  		void testReceiveData() { @@ -93,7 +93,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			eventLoop->processEvents();  			skipHeader("abcdef"); -			CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData); +			CPPUNIT_ASSERT(ByteArray::create("abcdefg") == receivedData);  			CPPUNIT_ASSERT_EQUAL(2, receivedDataChunks);  		} @@ -107,7 +107,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  			eventLoop->processEvents();  			skipHeader("abcdef"); -			CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData); +			CPPUNIT_ASSERT(ByteArray::create("abcdefg") == receivedData);  			CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks);  		} @@ -118,23 +118,23 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  		}  		void authenticate() { -			receive(ByteArray("\x05\x02\x01\x02")); +			receive(ByteArray::create("\x05\x02\x01\x02"));  			receivedData.clear();  			receivedDataChunks = 0;  		}  		void request(const std::string& hostname) { -			receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.size() + hostname + ByteArray("\x00\x00", 2)); +			receive(ByteArray(ByteArray::create("\x05\x01\x00\x03", 4)) + hostname.size() + hostname + ByteArray::create("\x00\x00", 2));  		}  		void skipHeader(const std::string& hostname) {  			int headerSize = 7 + hostname.size(); -			receivedData = ByteArray(receivedData.getData() + headerSize, receivedData.getSize() - headerSize); +			receivedData = ByteArray::create(&receivedData[headerSize], receivedData.size() - headerSize);  		}  		void handleDataWritten(const ByteArray& data) { -			receivedData += data; +			receivedData.insert(receivedData.end(), data.begin(), data.end());  			receivedDataChunks++;  		} @@ -148,7 +148,7 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture {  		DummyEventLoop* eventLoop;  		SOCKS5BytestreamRegistry bytestreams;  		boost::shared_ptr<DummyConnection> connection; -		ByteArray receivedData; +		std::vector<unsigned char> receivedData;  		int receivedDataChunks;  		boost::shared_ptr<ByteArrayReadBytestream> stream1;  }; diff --git a/Swiften/FileTransfer/WriteBytestream.h b/Swiften/FileTransfer/WriteBytestream.h index 1dc791c..c27aeff 100644 --- a/Swiften/FileTransfer/WriteBytestream.h +++ b/Swiften/FileTransfer/WriteBytestream.h @@ -7,8 +7,7 @@  #pragma once  #include <boost/shared_ptr.hpp> - -#include "Swiften/Base/ByteArray.h" +#include <vector>  namespace Swift {  	class WriteBytestream { @@ -17,6 +16,6 @@ namespace Swift {  			virtual ~WriteBytestream(); -			virtual void write(const ByteArray&) = 0; +			virtual void write(const std::vector<unsigned char>&) = 0;  	};  } diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp index e4611b3..00adf34 100644 --- a/Swiften/JID/JID.cpp +++ b/Swiften/JID/JID.cpp @@ -7,12 +7,17 @@  #define SWIFTEN_CACHE_JID_PREP  #include <vector> +#include <list>  #include <iostream>  #include <string>  #ifdef SWIFTEN_CACHE_JID_PREP  #include <boost/unordered_map.hpp>  #endif +#include <boost/assign/list_of.hpp> +#include <boost/algorithm/string/find_format.hpp> +#include <boost/algorithm/string/finder.hpp> +#include <sstream>  #include <stringprep.h>  #include <Swiften/Base/String.h> @@ -27,6 +32,75 @@ static PrepCache domainPrepCache;  static PrepCache resourcePrepCache;  #endif +static const std::list<char> escapedChars = boost::assign::list_of(' ')('"')('&')('\'')('/')('<')('>')('@')(':'); + +bool getEscapeSequenceValue(const std::string& sequence, unsigned char& value) { +	std::stringstream s; +	unsigned int v; +	s << std::hex << sequence; +	s >> v; +	value = static_cast<unsigned char>(v); +	return (!s.fail() && !s.bad() && (value == 0x5C || std::find(escapedChars.begin(), escapedChars.end(), value) != escapedChars.end())); +} + +struct UnescapedCharacterFinder { +	template<typename Iterator>	boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { +		for (; begin != end; ++begin) { +			if (std::find(escapedChars.begin(), escapedChars.end(), *begin) != escapedChars.end()) { +				return boost::iterator_range<Iterator>(begin, begin + 1); +			} +			else if (*begin == '\\') { +				// Check if we have an escaped dissalowed character sequence +				Iterator innerBegin = begin + 1; +				if (innerBegin != end && innerBegin + 1 != end) { +					Iterator innerEnd = innerBegin + 2; +					unsigned char value; +					if (getEscapeSequenceValue(std::string(innerBegin, innerEnd), value)) { +						return boost::iterator_range<Iterator>(begin, begin + 1); +					} +				} +			} +		} +		return boost::iterator_range<Iterator>(end, end); +	} +}; + +struct UnescapedCharacterFormatter { +	template<typename FindResult>	std::string operator()(const FindResult& match) const { +		std::ostringstream s; +		s << '\\' << std::hex << static_cast<int>(*match.begin()); +		return s.str(); +	} +}; + +struct EscapedCharacterFinder { +	template<typename Iterator>	boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { +		for (; begin != end; ++begin) { +			if (*begin == '\\') { +				Iterator innerEnd = begin + 1; +				for (size_t i = 0; i < 2 && innerEnd != end; ++i, ++innerEnd) { +				} +				unsigned char value; +				if (getEscapeSequenceValue(std::string(begin + 1, innerEnd), value)) { +					return boost::iterator_range<Iterator>(begin, innerEnd); +				} +			} +		} +		return boost::iterator_range<Iterator>(end, end); +	} +}; + +struct EscapedCharacterFormatter { +	template<typename FindResult>	std::string operator()(const FindResult& match) const { +		unsigned char value; +		if (getEscapeSequenceValue(std::string(match.begin() + 1, match.end()), value)) { +			return std::string(reinterpret_cast<const char*>(&value), 1); +		} +		return boost::copy_range<std::string>(match); +	} +}; + +  namespace Swift {  JID::JID(const char* jid) { @@ -126,5 +200,13 @@ int JID::compare(const Swift::JID& o, CompareType compareType) const {  	return 0;  } +std::string JID::getEscapedNode(const std::string& node) { +	return boost::find_format_all_copy(node, UnescapedCharacterFinder(), UnescapedCharacterFormatter()); +} + +std::string JID::getUnescapedNode() const { +	return boost::find_format_all_copy(node_, EscapedCharacterFinder(), EscapedCharacterFormatter()); +} +  } // namespace Swift diff --git a/Swiften/JID/JID.h b/Swiften/JID/JID.h index 63e063d..98b42da 100644 --- a/Swiften/JID/JID.h +++ b/Swiften/JID/JID.h @@ -7,7 +7,7 @@  #pragma once  #include <string> -#include <ostream> +#include <iosfwd>  namespace Swift {  	class JID { @@ -38,6 +38,18 @@ namespace Swift {  				return !hasResource_;  			} +			/** +			 * Returns the given node, escaped according to XEP-0106. +			 * The resulting node is a valid node for a JID, whereas the input value can contain characters +			 * that are not allowed. +			 */ +			static std::string getEscapedNode(const std::string& node); + +			/** +			 * Returns the node of the current JID, unescaped according to XEP-0106. +			 */ +			std::string getUnescapedNode() const; +  			JID toBare() const {  				JID result(*this);  				result.hasResource_ = false; diff --git a/Swiften/JID/UnitTest/JIDTest.cpp b/Swiften/JID/UnitTest/JIDTest.cpp index 0f22e15..5e79db2 100644 --- a/Swiften/JID/UnitTest/JIDTest.cpp +++ b/Swiften/JID/UnitTest/JIDTest.cpp @@ -51,6 +51,10 @@ class JIDTest : public CppUnit::TestFixture  		CPPUNIT_TEST(testSmallerThan_Larger);  		CPPUNIT_TEST(testHasResource);  		CPPUNIT_TEST(testHasResource_NoResource); +		CPPUNIT_TEST(testGetEscapedNode); +		CPPUNIT_TEST(testGetEscapedNode_XEP106Examples); +		CPPUNIT_TEST(testGetUnescapedNode); +		CPPUNIT_TEST(testGetUnescapedNode_XEP106Examples);  		CPPUNIT_TEST_SUITE_END();  	public: @@ -311,6 +315,57 @@ class JIDTest : public CppUnit::TestFixture  			CPPUNIT_ASSERT(testling.isBare());  		} + +		void testGetEscapedNode() { +			std::string escaped = JID::getEscapedNode("alice@wonderland.lit"); +			CPPUNIT_ASSERT_EQUAL(std::string("alice\\40wonderland.lit"), escaped); + +			escaped = JID::getEscapedNode("\\& \" ' / <\\\\> @ :\\3a\\40"); +			CPPUNIT_ASSERT_EQUAL(std::string("\\\\26\\20\\22\\20\\27\\20\\2f\\20\\3c\\\\\\3e\\20\\40\\20\\3a\\5c3a\\5c40"), escaped); +		} + +		void testGetEscapedNode_XEP106Examples() { +			CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID::getEscapedNode("\\2plus\\2is\\4")); +			CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID::getEscapedNode("foo\\bar")); +			CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID::getEscapedNode("foob\\41r")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("space cadet"), std::string("space\\20cadet")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("call me \"ishmael\""), std::string("call\\20me\\20\\22ishmael\\22")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("at&t guy"), std::string("at\\26t\\20guy")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("d'artagnan"), std::string("d\\27artagnan")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("/.fanboy"), std::string("\\2f.fanboy")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("::foo::"), std::string("\\3a\\3afoo\\3a\\3a")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("<foo>"), std::string("\\3cfoo\\3e")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("user@host"), std::string("user\\40host")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\net"), std::string("c\\3a\\net")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\\\net"), std::string("c\\3a\\\\net")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\cool stuff"), std::string("c\\3a\\cool\\20stuff")); +			CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\5commas"), std::string("c\\3a\\5c5commas")); +		} + +		void testGetUnescapedNode() { +			std::string input = "\\& \" ' / <\\\\> @ : \\5c\\40"; +			JID testling(JID::getEscapedNode(input) + "@y"); +			CPPUNIT_ASSERT(testling.isValid()); +			CPPUNIT_ASSERT_EQUAL(input, testling.getUnescapedNode()); +		} + +		void testGetUnescapedNode_XEP106Examples() { +			CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID("\\2plus\\2is\\4@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID("foo\\bar@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID("foob\\41r@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("space cadet"), JID("space\\20cadet@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("call me \"ishmael\""), JID("call\\20me\\20\\22ishmael\\22@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("at&t guy"), JID("at\\26t\\20guy@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("d'artagnan"), JID("d\\27artagnan@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("/.fanboy"), JID("\\2f.fanboy@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("::foo::"), JID("\\3a\\3afoo\\3a\\3a@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("<foo>"), JID("\\3cfoo\\3e@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("user@host"), JID("user\\40host@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("c:\\net"), JID("c\\3a\\net@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("c:\\\\net"), JID("c\\3a\\\\net@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("c:\\cool stuff"), JID("c\\3a\\cool\\20stuff@example.com").getUnescapedNode()); +			CPPUNIT_ASSERT_EQUAL(std::string("c:\\5commas"), JID("c\\3a\\5c5commas@example.com").getUnescapedNode()); +		}  };  CPPUNIT_TEST_SUITE_REGISTRATION(JIDTest); diff --git a/Swiften/Jingle/IncomingJingleSession.cpp b/Swiften/Jingle/IncomingJingleSession.cpp deleted file mode 100644 index b18d9d3..0000000 --- a/Swiften/Jingle/IncomingJingleSession.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include <Swiften/Jingle/IncomingJingleSession.h> - -namespace Swift { - -IncomingJingleSession::IncomingJingleSession(const std::string& id, const std::vector<JingleContent::ref>& contents) : JingleSession(id, contents) { - -} - -} diff --git a/Swiften/Jingle/IncomingJingleSession.h b/Swiften/Jingle/IncomingJingleSession.h deleted file mode 100644 index 64816f6..0000000 --- a/Swiften/Jingle/IncomingJingleSession.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2011 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/Jingle/JingleSession.h> - -namespace Swift { -	class IncomingJingleSession : public JingleSession { -		public: -			IncomingJingleSession(const std::string& id, const std::vector<JingleContent::ref>& contents); - -			typedef boost::shared_ptr<IncomingJingleSession> ref; -	}; -} diff --git a/Swiften/Jingle/IncomingJingleSessionHandler.h b/Swiften/Jingle/IncomingJingleSessionHandler.h index 5bf9237..4d22a4e 100644 --- a/Swiften/Jingle/IncomingJingleSessionHandler.h +++ b/Swiften/Jingle/IncomingJingleSessionHandler.h @@ -6,13 +6,13 @@  #pragma once -#include <Swiften/Jingle/IncomingJingleSession.h> +#include <Swiften/Jingle/JingleSession.h>  namespace Swift {  	class IncomingJingleSessionHandler {  		public:  			virtual ~IncomingJingleSessionHandler(); -			virtual bool handleIncomingJingleSession(IncomingJingleSession::ref) = 0; +			virtual bool handleIncomingJingleSession(JingleSession::ref, const std::vector<JingleContentPayload::ref>& contents) = 0;  	};  } diff --git a/Swiften/Jingle/Jingle.h b/Swiften/Jingle/Jingle.h new file mode 100644 index 0000000..ba4dfe3 --- /dev/null +++ b/Swiften/Jingle/Jingle.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <vector> + +#include <Swiften/Elements/JingleContentPayload.h> + +namespace Swift { +	namespace Jingle { +		template<typename T> +		JingleContentPayload::ref getContentWithDescription(const std::vector<JingleContentPayload::ref>& contents) { +			for (size_t i = 0; i < contents.size(); ++i) { +				if (contents[i]->getDescription<T>()) { +					return contents[i]; +				} +			} +			return JingleContentPayload::ref(); +		} +	} +} diff --git a/Swiften/Jingle/JingleContentID.h b/Swiften/Jingle/JingleContentID.h new file mode 100644 index 0000000..8d75581 --- /dev/null +++ b/Swiften/Jingle/JingleContentID.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> + +#include <Swiften/Elements/JingleContentPayload.h> + +namespace Swift { +	class JingleContentID { +		public: +			JingleContentID(const std::string& name, JingleContentPayload::Creator creator) : name(name), creator(creator) { +			} + +		private: +			std::string name; +			JingleContentPayload::Creator creator; +	}; +} diff --git a/Swiften/Jingle/JingleResponder.cpp b/Swiften/Jingle/JingleResponder.cpp index 2397e63..198f9a2 100644 --- a/Swiften/Jingle/JingleResponder.cpp +++ b/Swiften/Jingle/JingleResponder.cpp @@ -9,7 +9,7 @@  #include <boost/smart_ptr/make_shared.hpp>  #include <Swiften/Jingle/JingleSessionManager.h> -#include <Swiften/Jingle/IncomingJingleSession.h> +#include <Swiften/Jingle/JingleSessionImpl.h>  namespace Swift { @@ -24,12 +24,12 @@ bool JingleResponder::handleSetRequest(const JID& from, const JID&, const std::s  		}  		else {  			sendResponse(from, id, boost::shared_ptr<JinglePayload>()); -			IncomingJingleSession::ref session = boost::make_shared<IncomingJingleSession>(id, payload->getContents()); -			sessionManager->handleIncomingSession(from, session); +			JingleSessionImpl::ref session = boost::make_shared<JingleSessionImpl>(payload->getInitiator(), payload->getSessionID()); +			sessionManager->handleIncomingSession(from, session, payload->getContents());  		}  	}  	else { -		JingleSession::ref session = sessionManager->getSession(from, payload->getSessionID()); +		JingleSessionImpl::ref session = sessionManager->getSession(from, payload->getSessionID());  		if (session) {  			session->handleIncomingAction(payload);  			sendResponse(from, id, boost::shared_ptr<JinglePayload>()); diff --git a/Swiften/Jingle/JingleSession.cpp b/Swiften/Jingle/JingleSession.cpp index d255abd..1366191 100644 --- a/Swiften/Jingle/JingleSession.cpp +++ b/Swiften/Jingle/JingleSession.cpp @@ -10,21 +10,11 @@  namespace Swift { -JingleSession::JingleSession(const std::string& id, const std::vector<JingleContent::ref>& contents) : id(id), contents(contents) { +JingleSession::JingleSession(const JID& initiator, const std::string& id) : initiator(initiator), id(id) {  }  JingleSession::~JingleSession() {  } -void JingleSession::handleIncomingAction(JinglePayload::ref) { -} - -void JingleSession::terminate(JinglePayload::Reason::Type reason) { -	JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionTerminate, id); -	payload->setReason(JinglePayload::Reason(reason)); -	//onAction(payload) -} - -  } diff --git a/Swiften/Jingle/JingleSession.h b/Swiften/Jingle/JingleSession.h index c00492d..fa7da7e 100644 --- a/Swiften/Jingle/JingleSession.h +++ b/Swiften/Jingle/JingleSession.h @@ -7,47 +7,43 @@  #pragma once  #include <boost/shared_ptr.hpp> +#include <string>  #include <Swiften/Base/boost_bsignals.h> -#include <string> +#include <Swiften/JID/JID.h>  #include <Swiften/Elements/JinglePayload.h> -#include <Swiften/Elements/JingleContent.h> -#include <Swiften/Base/foreach.h>  namespace Swift { +	class JingleContentID; +  	class JingleSession { -			friend class JingleResponder;  		public:  			typedef boost::shared_ptr<JingleSession> ref; -			JingleSession(const std::string& id, const std::vector<JingleContent::ref>& contents); +			JingleSession(const JID& initiator, const std::string& id);  			virtual ~JingleSession(); -			std::string getID() const { -				return id; +			const JID& getInitiator() const { +				return initiator;  			} -			template<typename T> -			JingleContent::ref getContentWithDescription() const { -				foreach (JingleContent::ref content, contents) { -					if (content->getDescription<T>()) { -						return content; -					} -				} -				return JingleContent::ref(); -			} - -			const std::vector<JingleContent::ref> getContents() const { -				return contents; +			std::string getID() const { +				return id;  			} -			void terminate(JinglePayload::Reason::Type reason); +			virtual void terminate(JinglePayload::Reason::Type reason) = 0; +			virtual void accept(JingleTransportPayload::ref = JingleTransportPayload::ref()) = 0; +			virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) = 0; +			virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref) = 0; +			virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref) = 0; -		private: -			void handleIncomingAction(JinglePayload::ref); +		public: +			boost::signal<void ()> onSessionTerminateReceived; +			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportInfoReceived; +			boost::signal<void (const JingleContentID&, JingleTransportPayload::ref)> onTransportReplaceReceived;  		private: +			JID initiator;  			std::string id; -			std::vector<JingleContent::ref> contents;  	};  } diff --git a/Swiften/Jingle/JingleSessionImpl.cpp b/Swiften/Jingle/JingleSessionImpl.cpp new file mode 100644 index 0000000..cbb2b42 --- /dev/null +++ b/Swiften/Jingle/JingleSessionImpl.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Jingle/JingleSessionImpl.h> + +#include <boost/smart_ptr/make_shared.hpp> + +namespace Swift { + +JingleSessionImpl::JingleSessionImpl(const JID& initiator, const std::string& id) : JingleSession(initiator, id) { +} + +void JingleSessionImpl::handleIncomingAction(JinglePayload::ref) { +} + +void JingleSessionImpl::terminate(JinglePayload::Reason::Type reason) { +	JinglePayload::ref payload = boost::make_shared<JinglePayload>(JinglePayload::SessionTerminate, getID()); +	payload->setReason(JinglePayload::Reason(reason)); +	//onAction(payload) +} + +void JingleSessionImpl::acceptTransport(const JingleContentID&, JingleTransportPayload::ref) { + +} + +void JingleSessionImpl::rejectTransport(const JingleContentID&, JingleTransportPayload::ref) { + +} + +void JingleSessionImpl::accept(JingleTransportPayload::ref) { +} + +void JingleSessionImpl::sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref) { + +} + + + +} diff --git a/Swiften/Jingle/JingleSessionImpl.h b/Swiften/Jingle/JingleSessionImpl.h new file mode 100644 index 0000000..a254ead --- /dev/null +++ b/Swiften/Jingle/JingleSessionImpl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 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/Jingle/JingleSession.h> + +namespace Swift { +	class JingleSessionImpl : public JingleSession { +			friend class JingleResponder; +		public: +			typedef boost::shared_ptr<JingleSessionImpl> ref; + +			JingleSessionImpl(const JID& initiator, const std::string& id); + +			virtual void terminate(JinglePayload::Reason::Type reason); +			virtual void accept(JingleTransportPayload::ref); +			virtual void sendTransportInfo(const JingleContentID&, JingleTransportPayload::ref); +			virtual void acceptTransport(const JingleContentID&, JingleTransportPayload::ref); +			virtual void rejectTransport(const JingleContentID&, JingleTransportPayload::ref); + +		private: +			void handleIncomingAction(JinglePayload::ref); +	}; +} diff --git a/Swiften/Jingle/JingleSessionManager.cpp b/Swiften/Jingle/JingleSessionManager.cpp index e60449b..58e90c8 100644 --- a/Swiften/Jingle/JingleSessionManager.cpp +++ b/Swiften/Jingle/JingleSessionManager.cpp @@ -7,6 +7,7 @@  #include <Swiften/Jingle/JingleSessionManager.h>  #include <Swiften/Jingle/JingleResponder.h>  #include <Swiften/Jingle/IncomingJingleSessionHandler.h> +#include <Swiften/Base/foreach.h>  namespace Swift { @@ -18,9 +19,9 @@ JingleSessionManager::~JingleSessionManager() {  	delete responder;  } -JingleSession::ref JingleSessionManager::getSession(const JID& jid, const std::string& id) const { +JingleSessionImpl::ref JingleSessionManager::getSession(const JID& jid, const std::string& id) const {  	SessionMap::const_iterator i = incomingSessions.find(JIDSession(jid, id)); -	return i != incomingSessions.end() ? i->second : JingleSession::ref(); +	return i != incomingSessions.end() ? i->second : JingleSessionImpl::ref();  }  void JingleSessionManager::addIncomingSessionHandler(IncomingJingleSessionHandler* handler) { @@ -31,10 +32,10 @@ void JingleSessionManager::removeIncomingSessionHandler(IncomingJingleSessionHan  	incomingSessionHandlers.erase(std::remove(incomingSessionHandlers.begin(), incomingSessionHandlers.end(), handler), incomingSessionHandlers.end());  } -void JingleSessionManager::handleIncomingSession(const JID& from, IncomingJingleSession::ref session) { +void JingleSessionManager::handleIncomingSession(const JID& from, JingleSessionImpl::ref session, const std::vector<JingleContentPayload::ref>& contents) {  	incomingSessions.insert(std::make_pair(JIDSession(from, session->getID()), session));  	foreach (IncomingJingleSessionHandler* handler, incomingSessionHandlers) { -		if (handler->handleIncomingJingleSession(session)) { +		if (handler->handleIncomingJingleSession(session, contents)) {  			return;  		}  	} diff --git a/Swiften/Jingle/JingleSessionManager.h b/Swiften/Jingle/JingleSessionManager.h index 3e99656..3b23fb0 100644 --- a/Swiften/Jingle/JingleSessionManager.h +++ b/Swiften/Jingle/JingleSessionManager.h @@ -10,7 +10,7 @@  #include <map>  #include <Swiften/Base/boost_bsignals.h> -#include <Swiften/Jingle/IncomingJingleSession.h> +#include <Swiften/Jingle/JingleSessionImpl.h>  namespace Swift {  	class IQRouter; @@ -23,13 +23,13 @@ namespace Swift {  			JingleSessionManager(IQRouter* router);  			~JingleSessionManager(); -			JingleSession::ref getSession(const JID& jid, const std::string& id) const; +			JingleSessionImpl::ref getSession(const JID& jid, const std::string& id) const;  			void addIncomingSessionHandler(IncomingJingleSessionHandler* handler);  			void removeIncomingSessionHandler(IncomingJingleSessionHandler* handler);  		protected: -			void handleIncomingSession(const JID& from, IncomingJingleSession::ref); +			void handleIncomingSession(const JID& from, JingleSessionImpl::ref, const std::vector<JingleContentPayload::ref>& contents);  		private:  			IQRouter* router; @@ -43,7 +43,7 @@ namespace Swift {  				JID jid;  				std::string session;  			}; -			typedef std::map<JIDSession, JingleSession::ref> SessionMap; +			typedef std::map<JIDSession, JingleSessionImpl::ref> SessionMap;  			SessionMap incomingSessions;  	};  } diff --git a/Swiften/Jingle/SConscript b/Swiften/Jingle/SConscript index a8890b7..6b3cfd3 100644 --- a/Swiften/Jingle/SConscript +++ b/Swiften/Jingle/SConscript @@ -1,11 +1,11 @@  Import("swiften_env")  sources = [ -		"IncomingJingleSession.cpp", +		"JingleSession.cpp", +		"JingleSessionImpl.cpp",	  		"IncomingJingleSessionHandler.cpp",  		"JingleSessionManager.cpp",	  		"JingleResponder.cpp", -		"JingleSession.cpp",  	]  swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.SwiftenObject(sources)) diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.cpp new file mode 100644 index 0000000..e31bf87 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h> + +#include <boost/bind.hpp> +#include <iostream> + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h> + +namespace Swift { + +void AvahiBrowseQuery::startBrowsing() { +	std::cout << "Start browsing" << std::endl; +	assert(!browser); +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, static_cast<AvahiLookupFlags>(0), &handleServiceDiscoveredStatic, this); +	if (!browser) { +		std::cout << "Error" << std::endl; +		eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); +	} +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiBrowseQuery::stopBrowsing() { +	std::cout << "Stop browsing" << std::endl; +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	avahi_service_browser_free(browser); +	browser = NULL; +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiBrowseQuery::handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) { +	switch (event) { +		case AVAHI_BROWSER_FAILURE:	 +			std::cout << "Service browse error" << std::endl; +			eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); +			break; +		case AVAHI_BROWSER_NEW: { +			DNSSDServiceID service(name, domain, type, interfaceIndex); +			std::cout << "Service discovered " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; +			eventLoop->postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); +			break; +		} +		case AVAHI_BROWSER_REMOVE: { +			std::cout << "Service went away " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; +			DNSSDServiceID service(name, domain, type, interfaceIndex); +			eventLoop->postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); +			break; +		} +		case AVAHI_BROWSER_ALL_FOR_NOW: +		case AVAHI_BROWSER_CACHE_EXHAUSTED: +			break; +	} +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h index 163a5f6..7641712 100644 --- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiBrowseQuery.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/bind.hpp> +#include <avahi-client/lookup.h>  #include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"  #include "Swiften/LinkLocal/DNSSD/DNSSDBrowseQuery.h" @@ -20,54 +20,15 @@ namespace Swift {  			AvahiBrowseQuery(boost::shared_ptr<AvahiQuerier> q, EventLoop* eventLoop) : AvahiQuery(q, eventLoop), browser(NULL) {  			} -			void startBrowsing() { -				std::cout << "Start browsing" << std::endl; -				assert(!browser); -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				browser = avahi_service_browser_new(querier->getClient(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_presence._tcp", NULL, static_cast<AvahiLookupFlags>(0), &handleServiceDiscoveredStatic, this); -				if (!browser) { -					std::cout << "Error" << std::endl; -					eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); -				} -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} - -			void stopBrowsing() { -				std::cout << "Stop browsing" << std::endl; -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				avahi_service_browser_free(browser); -				browser = NULL; -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} +			void startBrowsing(); +			void stopBrowsing();  		private:  			static void handleServiceDiscoveredStatic(AvahiServiceBrowser *b, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* context) {  				static_cast<AvahiBrowseQuery*>(context)->handleServiceDiscovered(b, interfaceIndex, protocol, event, name, type, domain, flags);  			} -			void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags) { -				switch (event) { -					case AVAHI_BROWSER_FAILURE:	 -						std::cout << "Service browse error" << std::endl; -						eventLoop->postEvent(boost::bind(boost::ref(onError)), shared_from_this()); -						break; -					case AVAHI_BROWSER_NEW: { -						DNSSDServiceID service(name, domain, type, interfaceIndex); -						std::cout << "Service discovered " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; -						eventLoop->postEvent(boost::bind(boost::ref(onServiceAdded), service), shared_from_this()); -						break; -					} -					case AVAHI_BROWSER_REMOVE: { -						std::cout << "Service went away " << name << " " << domain << " " << type << " " << interfaceIndex << std::endl; -						DNSSDServiceID service(name, domain, type, interfaceIndex); -						eventLoop->postEvent(boost::bind(boost::ref(onServiceRemoved), service), shared_from_this()); -						break; -					} -					case AVAHI_BROWSER_ALL_FOR_NOW: -					case AVAHI_BROWSER_CACHE_EXHAUSTED: -						break; -				} -			} +			void handleServiceDiscovered(AvahiServiceBrowser *, AvahiIfIndex interfaceIndex, AvahiProtocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags);  		private:  			AvahiServiceBrowser* browser; diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.cpp new file mode 100644 index 0000000..7975e7b --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h> + +#include <iostream> +#include <boost/bind.hpp> + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h> + +namespace Swift { + +void AvahiRegisterQuery::registerService() { +	std::cout << "Registering service " << name << ":" << port << std::endl; +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	if (!group) { +		std::cout << "Creating entry group" << std::endl; +		group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this); +		if (!group) { +			std::cout << "Error ceating entry group" << std::endl; +			eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); +		} +	} + +	doRegisterService(); +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiRegisterQuery::unregisterService() { +	if (group) { +		avahi_entry_group_free(group); +		group = NULL; +	} +} + +void AvahiRegisterQuery::updateServiceInfo(const ByteArray& txtRecord) { +	this->txtRecord = txtRecord; +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	assert(group); +	avahi_entry_group_reset(group); +	doRegisterService(); +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiRegisterQuery::doRegisterService() { +	AvahiStringList* txtList; +	avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList); + +	int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), name.c_str(), "_presence._tcp", NULL, NULL, port, txtList); +	if (result < 0) { +		std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; +		eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); +	} +	result = avahi_entry_group_commit(group); +	if (result < 0) { +		std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; +	} +} + +void AvahiRegisterQuery::handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) { +	std::cout << "ENtry group callback: " << state << std::endl; +	switch (state) { +		case AVAHI_ENTRY_GROUP_ESTABLISHED : +			// Domain is a hack! +			eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this()); +			std::cout << "Entry group established" << std::endl; +			break; +	case AVAHI_ENTRY_GROUP_COLLISION : { +			std::cout << "Entry group collision" << std::endl; +			/*char *n; +			n = avahi_alternative_service_name(name); +			avahi_free(name); +			name = n;*/ +			break; +	} + +	case AVAHI_ENTRY_GROUP_FAILURE : +			std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl; +			break; + +	case AVAHI_ENTRY_GROUP_UNCOMMITED: +	case AVAHI_ENTRY_GROUP_REGISTERING: +			; + +	/* +	DNSServiceErrorType result = DNSServiceRegister( +			&sdRef, 0, 0, name.c_str(), "_presence._tcp", NULL, NULL, port,  +			txtRecord.getSize(), txtRecord.getData(),  +			&AvahiRegisterQuery::handleServiceRegisteredStatic, this); +	if (result != kDNSServiceErr_NoError) { +		sdRef = NULL; +	}*/ +	//eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); +	} +} + + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h index 07966af..3303f1b 100644 --- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiRegisterQuery.h @@ -21,94 +21,18 @@ namespace Swift {  			AvahiRegisterQuery(const std::string& name, int port, const ByteArray& txtRecord, boost::shared_ptr<AvahiQuerier> querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), name(name), port(port), txtRecord(txtRecord), group(0) {  			} -			void registerService() { -				std::cout << "Registering service " << name << ":" << port << std::endl; -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				if (!group) { -					std::cout << "Creating entry group" << std::endl; -					group = avahi_entry_group_new(querier->getClient(), handleEntryGroupChange, this); -					if (!group) { -						std::cout << "Error ceating entry group" << std::endl; -						eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); -					} -				} - -				doRegisterService(); -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} - -			void unregisterService() { -				if (group) { -					avahi_entry_group_free(group); -					group = NULL; -				} -			} - -			void updateServiceInfo(const ByteArray& txtRecord) { -				this->txtRecord = txtRecord; -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				assert(group); -				avahi_entry_group_reset(group); -				doRegisterService(); -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} +			void registerService(); +			void unregisterService(); +			void updateServiceInfo(const ByteArray& txtRecord);  		private: -			void doRegisterService() { -				AvahiStringList* txtList; -				avahi_string_list_parse(txtRecord.getData(), txtRecord.getSize(), &txtList); - -				int result = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast<AvahiPublishFlags>(0), name.c_str(), "_presence._tcp", NULL, NULL, port, txtList); -				if (result < 0) { -					std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; -					eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); -				} -				result = avahi_entry_group_commit(group); -				if (result < 0) { -					std::cout << "Error registering service: " << avahi_strerror(result) << std::endl; -				} -			} +			void doRegisterService();  			static void handleEntryGroupChange(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {  				static_cast<AvahiRegisterQuery*>(userdata)->handleEntryGroupChange(g, state);  			} -			void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state) { -				std::cout << "ENtry group callback: " << state << std::endl; -				switch (state) { -					case AVAHI_ENTRY_GROUP_ESTABLISHED : -						// Domain is a hack! -						eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>(DNSSDServiceID(name, "local", "_presence._tcp", 0))), shared_from_this()); -						std::cout << "Entry group established" << std::endl; -						break; -				case AVAHI_ENTRY_GROUP_COLLISION : { -						std::cout << "Entry group collision" << std::endl; -						/*char *n; -						n = avahi_alternative_service_name(name); -						avahi_free(name); -						name = n;*/ -						break; -				} - -				case AVAHI_ENTRY_GROUP_FAILURE : -						std::cout << "Entry group failure " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << std::endl; -						break; - -				case AVAHI_ENTRY_GROUP_UNCOMMITED: -				case AVAHI_ENTRY_GROUP_REGISTERING: -						; - -				/* -				DNSServiceErrorType result = DNSServiceRegister( -						&sdRef, 0, 0, name.c_str(), "_presence._tcp", NULL, NULL, port,  -						txtRecord.getSize(), txtRecord.getData(),  -						&AvahiRegisterQuery::handleServiceRegisteredStatic, this); -				if (result != kDNSServiceErr_NoError) { -					sdRef = NULL; -				}*/ -				//eventLoop->postEvent(boost::bind(boost::ref(onRegisterFinished), boost::optional<DNSSDServiceID>()), shared_from_this()); -			} -		} +			void handleEntryGroupChange(AvahiEntryGroup* g, AvahiEntryGroupState state);  /*  			static void handleServiceRegisteredStatic(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, void *context) { diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.cpp new file mode 100644 index 0000000..d9a1c5c --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h> + +#include <iostream> +#include <boost/bind.hpp> + +namespace Swift { + +AvahiResolveHostnameQuery::AvahiResolveHostnameQuery(const std::string& hostname, int, boost::shared_ptr<AvahiQuerier> querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), hostname(hostname) { +	std::cout << "Resolving hostname " << hostname << std::endl; +} + +void AvahiResolveHostnameQuery::run() { +		eventLoop->postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this()); +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h index 00712f1..acc1897 100644 --- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveHostnameQuery.h @@ -19,13 +19,9 @@ namespace Swift {  	class AvahiResolveHostnameQuery : public DNSSDResolveHostnameQuery, public AvahiQuery {  		public:  -			AvahiResolveHostnameQuery(const std::string& hostname, int, boost::shared_ptr<AvahiQuerier> querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), hostname(hostname) { -				std::cout << "Resolving hostname " << hostname << std::endl; -			} +			AvahiResolveHostnameQuery(const std::string& hostname, int, boost::shared_ptr<AvahiQuerier> querier, EventLoop* eventLoop); -			void run() { -					eventLoop->postEvent(boost::bind(boost::ref(onHostnameResolved), boost::optional<HostAddress>(HostAddress(hostname))), shared_from_this()); -			} +			void run();  			void finish() {  			} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.cpp b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.cpp new file mode 100644 index 0000000..24fe067 --- /dev/null +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h> + +#include <boost/bind.hpp> +#include <iostream> + +#include <Swiften/LinkLocal/DNSSD/Avahi/AvahiQuerier.h> + +namespace Swift { + +void AvahiResolveServiceQuery::start() { +	std::cout << "Start resolving " << service.getName() << " " << service.getType() << " " << service.getDomain() << std::endl; +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	assert(!resolver); +	resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().c_str(), service.getType().c_str(), service.getDomain().c_str(), AVAHI_PROTO_UNSPEC, static_cast<AvahiLookupFlags>(0), handleServiceResolvedStatic, this); +	if (!resolver) { +		std::cout << "Error starting resolver" << std::endl; +		eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); +	} +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiResolveServiceQuery::stop() { +	std::cout << "Stop resolving" << std::endl; +	avahi_threaded_poll_lock(querier->getThreadedPoll()); +	avahi_service_resolver_free(resolver); +	resolver = NULL; +	avahi_threaded_poll_unlock(querier->getThreadedPoll()); +} + +void AvahiResolveServiceQuery::handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) { +	std::cout << "Resolve finished" << std::endl; +	switch(event) { +		case AVAHI_RESOLVER_FAILURE: +			std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl; +			eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); +			break; +		case AVAHI_RESOLVER_FOUND: { +			std::cout << "Success" << std::endl; +			char a[AVAHI_ADDRESS_STR_MAX]; +			avahi_address_snprint(a, sizeof(a), address); + +			ByteArray txtRecord; +			txtRecord.resize(1024); +			avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize()); + +			// FIXME: Probably not accurate +			std::string fullname = std::string(name) + "." + std::string(type) + "." + std::string(domain) + "."; +			std::cout << "Result: " << fullname << "->" << std::string(a) << ":" << port << std::endl; +			eventLoop->postEvent( +					boost::bind( +						boost::ref(onServiceResolved),  +						Result(fullname, std::string(a), port, txtRecord)), +					shared_from_this()); +			break; +		} +	} +} + +} diff --git a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h index e9c4db1..be48409 100644 --- a/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h +++ b/Swiften/LinkLocal/DNSSD/Avahi/AvahiResolveServiceQuery.h @@ -6,6 +6,8 @@  #pragma once +#include <avahi-client/lookup.h> +  #include "Swiften/LinkLocal/DNSSD/Avahi/AvahiQuery.h"  #include "Swiften/LinkLocal/DNSSD/DNSSDResolveServiceQuery.h"  #include "Swiften/LinkLocal/LinkLocalServiceInfo.h" @@ -20,59 +22,15 @@ namespace Swift {  			AvahiResolveServiceQuery(const DNSSDServiceID& service, boost::shared_ptr<AvahiQuerier> querier, EventLoop* eventLoop) : AvahiQuery(querier, eventLoop), service(service), resolver(NULL) {  			} -			void start() { -				std::cout << "Start resolving " << service.getName() << " " << service.getType() << " " << service.getDomain() << std::endl; -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				assert(!resolver); -				resolver = avahi_service_resolver_new(querier->getClient(), service.getNetworkInterfaceID(), AVAHI_PROTO_UNSPEC, service.getName().c_str(), service.getType().c_str(), service.getDomain().c_str(), AVAHI_PROTO_UNSPEC, static_cast<AvahiLookupFlags>(0), handleServiceResolvedStatic, this); -				if (!resolver) { -					std::cout << "Error starting resolver" << std::endl; -					eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); -				} -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} - -			void stop() { -				std::cout << "Stop resolving" << std::endl; -				avahi_threaded_poll_lock(querier->getThreadedPoll()); -				avahi_service_resolver_free(resolver); -				resolver = NULL; -				avahi_threaded_poll_unlock(querier->getThreadedPoll()); -			} +			void start(); +			void stop();  		private:  			static void handleServiceResolvedStatic(AvahiServiceResolver* resolver, AvahiIfIndex interfaceIndex, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* context) {  				static_cast<AvahiResolveServiceQuery*>(context)->handleServiceResolved(resolver, interfaceIndex, protocol, event, name, type, domain, host_name, address, port, txt, flags);  			} -			void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags) { -				std::cout << "Resolve finished" << std::endl; -				switch(event) { -					case AVAHI_RESOLVER_FAILURE: -						std::cout << "Resolve error " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(resolver))) << std::endl; -						eventLoop->postEvent(boost::bind(boost::ref(onServiceResolved), boost::optional<Result>()), shared_from_this()); -						break; -					case AVAHI_RESOLVER_FOUND: { -						std::cout << "Success" << std::endl; -						char a[AVAHI_ADDRESS_STR_MAX]; -						avahi_address_snprint(a, sizeof(a), address); - -						ByteArray txtRecord; -						txtRecord.resize(1024); -						avahi_string_list_serialize(txt, txtRecord.getData(), txtRecord.getSize()); - -						// FIXME: Probably not accurate -						std::string fullname = std::string(name) + "." + std::string(type) + "." + std::string(domain) + "."; -						std::cout << "Result: " << fullname << "->" << std::string(a) << ":" << port << std::endl; -						eventLoop->postEvent( -								boost::bind( -									boost::ref(onServiceResolved),  -									Result(fullname, std::string(a), port, txtRecord)), -								shared_from_this()); -						break; -					} -				} -			} +			void handleServiceResolved(AvahiServiceResolver* resolver, AvahiIfIndex, AvahiProtocol, AvahiResolverEvent event, const char *name, const char * type, const char* domain, const char * /*host_name*/, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags);  		private:  			DNSSDServiceID service; diff --git a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h index edd3056..c342247 100644 --- a/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h +++ b/Swiften/LinkLocal/DNSSD/Bonjour/BonjourQuerier.h @@ -9,7 +9,7 @@  #include <boost/shared_ptr.hpp>  #include <boost/enable_shared_from_this.hpp>  #include <list> -#include <boost/thread.hpp> +#include <boost/thread/thread.hpp>  #include <boost/thread/mutex.hpp>  #include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp index d7d0228..b13b0c4 100644 --- a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.cpp @@ -7,7 +7,9 @@  #include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h"  #include <boost/bind.hpp> +#include <iostream> +#include <Swiften/Base/foreach.h>  #include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDBrowseQuery.h"  #include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDRegisterQuery.h"  #include "Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDResolveServiceQuery.h" diff --git a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h index b2871c9..9aef6a5 100644 --- a/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h +++ b/Swiften/LinkLocal/DNSSD/Fake/FakeDNSSDQuerier.h @@ -11,7 +11,6 @@  #include <list>  #include <set> -#include "Swiften/Base/foreach.h"  #include <string>  #include "Swiften/EventLoop/EventOwner.h"  #include "Swiften/LinkLocal/DNSSD/DNSSDQuerier.h" @@ -63,8 +62,8 @@ namespace Swift {  			template<typename T>  			std::vector< boost::shared_ptr<T> > getAllQueriesEverRun() const {  				std::vector< boost::shared_ptr<T> > result; -				foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, allQueriesEverRun) { -					if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) { +				for (QueryList::const_iterator i = allQueriesEverRun.begin(); i != allQueriesEverRun.end(); ++i) { +					if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(*i)) {  						result.push_back(resultQuery);  					}  				} @@ -75,8 +74,8 @@ namespace Swift {  			template<typename T>  			std::vector< boost::shared_ptr<T> > getQueries() const {  				std::vector< boost::shared_ptr<T> > result; -				foreach(const boost::shared_ptr<FakeDNSSDQuery>& query, runningQueries) { -					if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(query)) { +				for (QueryList::const_iterator i = runningQueries.begin(); i != runningQueries.end(); ++i) { +					if (boost::shared_ptr<T> resultQuery = boost::dynamic_pointer_cast<T>(*i)) {  						result.push_back(resultQuery);  					}  				} @@ -86,8 +85,9 @@ namespace Swift {  		private:  			std::string domain;  			EventLoop* eventLoop; -			std::list< boost::shared_ptr<FakeDNSSDQuery> > runningQueries; -			std::list< boost::shared_ptr<FakeDNSSDQuery> > allQueriesEverRun; +			typedef std::list< boost::shared_ptr<FakeDNSSDQuery> > QueryList; +			QueryList runningQueries; +			QueryList allQueriesEverRun;  			std::set<DNSSDServiceID> services;  			typedef std::map<DNSSDServiceID,DNSSDResolveServiceQuery::Result> ServiceInfoMap;  			ServiceInfoMap serviceInfo; diff --git a/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp index 65542d2..d13032d 100644 --- a/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp +++ b/Swiften/LinkLocal/OutgoingLinkLocalSession.cpp @@ -8,6 +8,7 @@  #include <boost/bind.hpp> +#include <Swiften/Base/foreach.h>  #include "Swiften/StreamStack/XMPPLayer.h"  #include "Swiften/Elements/ProtocolHeader.h"  #include "Swiften/Elements/StreamFeatures.h" diff --git a/Swiften/LinkLocal/SConscript b/Swiften/LinkLocal/SConscript index 6edf993..29ea692 100644 --- a/Swiften/LinkLocal/SConscript +++ b/Swiften/LinkLocal/SConscript @@ -31,7 +31,11 @@ elif myenv.get("HAVE_AVAHI", 0) :  	myenv.Append(CPPDEFINES = ["HAVE_AVAHI"])  	sources += [  			"DNSSD/Avahi/AvahiQuerier.cpp", -			"DNSSD/Avahi/AvahiQuery.cpp" +			"DNSSD/Avahi/AvahiQuery.cpp", +			"DNSSD/Avahi/AvahiResolveHostnameQuery.cpp", +			"DNSSD/Avahi/AvahiResolveServiceQuery.cpp", +			"DNSSD/Avahi/AvahiRegisterQuery.cpp", +			"DNSSD/Avahi/AvahiBrowseQuery.cpp",  		]  objects = myenv.SwiftenObject(sources) diff --git a/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp index 98deed1..a2e8280 100644 --- a/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp +++ b/Swiften/LinkLocal/UnitTest/LinkLocalConnectorTest.cpp @@ -7,6 +7,8 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/bind.hpp> +  #include "Swiften/LinkLocal/LinkLocalConnector.h"  #include "Swiften/LinkLocal/LinkLocalService.h"  #include "Swiften/LinkLocal/DNSSD/DNSSDServiceID.h" diff --git a/Swiften/MUC/MUC.cpp b/Swiften/MUC/MUC.cpp index 68a5a86..553e49d 100644 --- a/Swiften/MUC/MUC.cpp +++ b/Swiften/MUC/MUC.cpp @@ -10,6 +10,7 @@  #include <boost/shared_ptr.hpp>  #include <boost/smart_ptr/make_shared.hpp> +#include <Swiften/Base/foreach.h>  #include "Swiften/Presence/DirectedPresenceSender.h"  #include "Swiften/Client/StanzaChannel.h"  #include "Swiften/Queries/IQRouter.h" diff --git a/Swiften/MUC/MUCBookmarkManager.cpp b/Swiften/MUC/MUCBookmarkManager.cpp index d0855cd..65e15e3 100644 --- a/Swiften/MUC/MUCBookmarkManager.cpp +++ b/Swiften/MUC/MUCBookmarkManager.cpp @@ -9,6 +9,7 @@  #include <boost/bind.hpp>  #include <iostream> +#include <Swiften/Base/foreach.h>  #include "Swiften/Queries/IQRouter.h"  #include "Swiften/Queries/Requests/GetPrivateStorageRequest.h"  #include "Swiften/Queries/Requests/SetPrivateStorageRequest.h" diff --git a/Swiften/Network/BoostConnection.cpp b/Swiften/Network/BoostConnection.cpp index f7ff8c4..c0faad9 100644 --- a/Swiften/Network/BoostConnection.cpp +++ b/Swiften/Network/BoostConnection.cpp @@ -9,6 +9,8 @@  #include <iostream>  #include <boost/bind.hpp>  #include <boost/thread.hpp> +#include <boost/asio/placeholders.hpp> +#include <boost/asio/write.hpp>  #include <Swiften/Base/Log.h>  #include "Swiften/EventLoop/EventLoop.h" diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h index 506eedf..16e587d 100644 --- a/Swiften/Network/BoostConnection.h +++ b/Swiften/Network/BoostConnection.h @@ -6,7 +6,8 @@  #pragma once -#include <boost/asio.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/asio/ip/tcp.hpp>  #include <boost/enable_shared_from_this.hpp>  #include <boost/thread/mutex.hpp> diff --git a/Swiften/Network/BoostConnectionFactory.h b/Swiften/Network/BoostConnectionFactory.h index ea9d656..bf1bc6c 100644 --- a/Swiften/Network/BoostConnectionFactory.h +++ b/Swiften/Network/BoostConnectionFactory.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/asio.hpp> +#include <boost/asio/io_service.hpp>  #include "Swiften/Network/ConnectionFactory.h"  #include "Swiften/Network/BoostConnection.h" diff --git a/Swiften/Network/BoostConnectionServer.cpp b/Swiften/Network/BoostConnectionServer.cpp index 4c6403c..e5fb8c0 100644 --- a/Swiften/Network/BoostConnectionServer.cpp +++ b/Swiften/Network/BoostConnectionServer.cpp @@ -8,6 +8,7 @@  #include <boost/bind.hpp>  #include <boost/system/system_error.hpp> +#include <boost/asio/placeholders.hpp>  #include "Swiften/EventLoop/EventLoop.h" diff --git a/Swiften/Network/BoostConnectionServer.h b/Swiften/Network/BoostConnectionServer.h index a45e598..3424720 100644 --- a/Swiften/Network/BoostConnectionServer.h +++ b/Swiften/Network/BoostConnectionServer.h @@ -7,8 +7,9 @@  #pragma once  #include <boost/shared_ptr.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/asio/ip/tcp.hpp>  #include <boost/enable_shared_from_this.hpp> -#include <boost/asio.hpp>  #include "Swiften/Base/boost_bsignals.h"  #include "Swiften/Network/BoostConnection.h" diff --git a/Swiften/Network/BoostIOServiceThread.h b/Swiften/Network/BoostIOServiceThread.h index 1f72049..ea04b02 100644 --- a/Swiften/Network/BoostIOServiceThread.h +++ b/Swiften/Network/BoostIOServiceThread.h @@ -6,8 +6,8 @@  #pragma once -#include <boost/asio.hpp> -#include <boost/thread.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/thread/thread.hpp>  #include <boost/shared_ptr.hpp>  namespace Swift { diff --git a/Swiften/Network/BoostTimer.cpp b/Swiften/Network/BoostTimer.cpp index 12d06c1..27e4b34 100644 --- a/Swiften/Network/BoostTimer.cpp +++ b/Swiften/Network/BoostTimer.cpp @@ -8,6 +8,7 @@  #include <boost/date_time/posix_time/posix_time.hpp>  #include <boost/asio.hpp> +#include <boost/bind.hpp>  #include "Swiften/EventLoop/EventLoop.h" diff --git a/Swiften/Network/BoostTimer.h b/Swiften/Network/BoostTimer.h index 1139dcf..614698d 100644 --- a/Swiften/Network/BoostTimer.h +++ b/Swiften/Network/BoostTimer.h @@ -6,8 +6,8 @@  #pragma once -#include <boost/asio.hpp> -#include <boost/thread.hpp> +#include <boost/asio/io_service.hpp> +#include <boost/asio/deadline_timer.hpp>  #include <boost/enable_shared_from_this.hpp>  #include "Swiften/EventLoop/EventOwner.h" diff --git a/Swiften/Network/BoostTimerFactory.h b/Swiften/Network/BoostTimerFactory.h index c0e9ef7..789ba24 100644 --- a/Swiften/Network/BoostTimerFactory.h +++ b/Swiften/Network/BoostTimerFactory.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/asio.hpp> +#include <boost/asio/io_service.hpp>  #include "Swiften/Network/TimerFactory.h"  #include "Swiften/Network/BoostTimer.h" diff --git a/Swiften/Network/CAresDomainNameResolver.h b/Swiften/Network/CAresDomainNameResolver.h index a630b61..f0973b9 100644 --- a/Swiften/Network/CAresDomainNameResolver.h +++ b/Swiften/Network/CAresDomainNameResolver.h @@ -7,7 +7,7 @@  #pragma once  #include <ares.h> -#include <boost/thread.hpp> +#include <boost/thread/thread.hpp>  #include <boost/thread/mutex.hpp>  #include <list> diff --git a/Swiften/Network/ChainedConnector.cpp b/Swiften/Network/ChainedConnector.cpp new file mode 100644 index 0000000..1a38e53 --- /dev/null +++ b/Swiften/Network/ChainedConnector.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Network/ChainedConnector.h> + +#include <boost/bind.hpp> +#include <typeinfo> + +#include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Network/Connector.h> +#include <Swiften/Network/ConnectionFactory.h> + +using namespace Swift; + +ChainedConnector::ChainedConnector( +		const std::string& hostname,  +		DomainNameResolver* resolver,  +		const std::vector<ConnectionFactory*>& connectionFactories,  +		TimerFactory* timerFactory) :  +			hostname(hostname),  +			resolver(resolver),  +			connectionFactories(connectionFactories),  +			timerFactory(timerFactory),  +			timeoutMilliseconds(0) { +} + +void ChainedConnector::setTimeoutMilliseconds(int milliseconds) { +	timeoutMilliseconds = milliseconds; +} + +void ChainedConnector::start() { +	SWIFT_LOG(debug) << "Starting queued connector for " << hostname << std::endl; + +	connectionFactoryQueue = std::deque<ConnectionFactory*>(connectionFactories.begin(), connectionFactories.end()); +	tryNextConnectionFactory(); +} + +void ChainedConnector::stop() { +	if (currentConnector) { +		currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); +		currentConnector->stop(); +		currentConnector.reset(); +	} +	finish(boost::shared_ptr<Connection>()); +} + +void ChainedConnector::tryNextConnectionFactory() { +	assert(!currentConnector); +	if (connectionFactoryQueue.empty()) { +		SWIFT_LOG(debug) << "No more connection factories" << std::endl; +		finish(boost::shared_ptr<Connection>()); +	} +	else { +		ConnectionFactory* connectionFactory = connectionFactoryQueue.front(); +		SWIFT_LOG(debug) << "Trying next connection factory: " << typeid(*connectionFactory).name() << std::endl; +		connectionFactoryQueue.pop_front(); +		currentConnector = Connector::create(hostname, resolver, connectionFactory, timerFactory); +		currentConnector->setTimeoutMilliseconds(timeoutMilliseconds); +		currentConnector->onConnectFinished.connect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); +		currentConnector->start(); +	} +} + +void ChainedConnector::handleConnectorFinished(boost::shared_ptr<Connection> connection) { +	SWIFT_LOG(debug) << "Connector finished" << std::endl; +	currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); +	currentConnector.reset(); +	if (connection) { +		finish(connection); +	} +	else { +		tryNextConnectionFactory(); +	} +} + +void ChainedConnector::finish(boost::shared_ptr<Connection> connection) { +	onConnectFinished(connection); +} diff --git a/Swiften/Network/ChainedConnector.h b/Swiften/Network/ChainedConnector.h new file mode 100644 index 0000000..15b17f3 --- /dev/null +++ b/Swiften/Network/ChainedConnector.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <string> +#include <vector> +#include <deque> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { +	class Connection; +	class Connector; +	class ConnectionFactory; +	class TimerFactory; +	class DomainNameResolver; + +	class ChainedConnector { +		public: +			ChainedConnector(const std::string& hostname, DomainNameResolver*, const std::vector<ConnectionFactory*>&, TimerFactory*); + +			void setTimeoutMilliseconds(int milliseconds); +			void start(); +			void stop(); + +			boost::signal<void (boost::shared_ptr<Connection>)> onConnectFinished; + +		private: +			void finish(boost::shared_ptr<Connection> connection); +			void tryNextConnectionFactory(); +			void handleConnectorFinished(boost::shared_ptr<Connection>); + +		private: +			std::string hostname; +			DomainNameResolver* resolver; +			std::vector<ConnectionFactory*> connectionFactories; +			TimerFactory* timerFactory; +			int timeoutMilliseconds; +			std::deque<ConnectionFactory*> connectionFactoryQueue; +			boost::shared_ptr<Connector> currentConnector; +	}; +}; diff --git a/Swiften/Network/Connection.cpp b/Swiften/Network/Connection.cpp new file mode 100644 index 0000000..9bb29e1 --- /dev/null +++ b/Swiften/Network/Connection.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Network/Connection.h> + +using namespace Swift; + +Connection::Connection() { +} + +Connection::~Connection() { +} diff --git a/Swiften/Network/Connection.h b/Swiften/Network/Connection.h index 529dd82..74d25aa 100644 --- a/Swiften/Network/Connection.h +++ b/Swiften/Network/Connection.h @@ -7,13 +7,13 @@  #pragma once  #include <boost/shared_ptr.hpp> +#include <Swiften/Base/boost_bsignals.h> -#include "Swiften/Base/boost_bsignals.h" -#include "Swiften/Base/ByteArray.h" -#include <string> -#include "Swiften/Network/HostAddressPort.h" +#include <Swiften/Base/ByteArray.h>  namespace Swift { +	class HostAddressPort; +  	class Connection {  		public:  			typedef boost::shared_ptr<Connection> ref; @@ -23,8 +23,8 @@ namespace Swift {  				WriteError  			}; -			Connection() {} -			virtual ~Connection() {} +			Connection(); +			virtual ~Connection();  			virtual void listen() = 0;  			virtual void connect(const HostAddressPort& address) = 0; diff --git a/Swiften/Network/DummyConnection.cpp b/Swiften/Network/DummyConnection.cpp new file mode 100644 index 0000000..ffc6dc2 --- /dev/null +++ b/Swiften/Network/DummyConnection.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Network/DummyConnection.h> + +#include <boost/bind.hpp> +#include <cassert> + +namespace Swift { + +DummyConnection::DummyConnection(EventLoop* eventLoop) : eventLoop(eventLoop) { +} + +void DummyConnection::receive(const ByteArray& data) { +	eventLoop->postEvent(boost::bind(boost::ref(onDataRead), ByteArray(data)), shared_from_this()); +} + +void DummyConnection::listen() { +	assert(false); +} + +void DummyConnection::connect(const HostAddressPort&) { +	assert(false); +} + + +} diff --git a/Swiften/Network/DummyConnection.h b/Swiften/Network/DummyConnection.h index 6b426b1..e8cc48b 100644 --- a/Swiften/Network/DummyConnection.h +++ b/Swiften/Network/DummyConnection.h @@ -6,26 +6,20 @@  #pragma once -#include <cassert> -#include <boost/bind.hpp>  #include <boost/enable_shared_from_this.hpp>  #include "Swiften/Network/Connection.h" +#include "Swiften/Network/HostAddressPort.h"  #include "Swiften/EventLoop/EventLoop.h"  #include "Swiften/EventLoop/EventOwner.h"  namespace Swift {  	class DummyConnection : public Connection, public EventOwner,	public boost::enable_shared_from_this<DummyConnection> {  		public: -			DummyConnection(EventLoop* eventLoop) : eventLoop(eventLoop) {} +			DummyConnection(EventLoop* eventLoop); -			void listen() { -				assert(false); -			} - -			void connect(const HostAddressPort&) { -				assert(false); -			} +			void listen(); +			void connect(const HostAddressPort&);  			void disconnect() {  				//assert(false); @@ -36,9 +30,7 @@ namespace Swift {  				onDataSent(data);  			} -			void receive(const ByteArray& data) { -				eventLoop->postEvent(boost::bind(boost::ref(onDataRead), ByteArray(data)), shared_from_this()); -			} +			void receive(const ByteArray& data);  			HostAddressPort getLocalAddress() const {  				return localAddress; diff --git a/Swiften/Network/EnvironmentProxyProvider.cpp b/Swiften/Network/EnvironmentProxyProvider.cpp new file mode 100644 index 0000000..b3bd0f6 --- /dev/null +++ b/Swiften/Network/EnvironmentProxyProvider.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> + +#include "Swiften/Base/Log.h" +#include "Swiften/Network/EnvironmentProxyProvider.h" + +namespace Swift { + +EnvironmentProxyProvider::EnvironmentProxyProvider() { +	socksProxy = getFromEnv("all_proxy", "socks"); +	httpProxy = getFromEnv("http_proxy", "http"); +	SWIFT_LOG(debug) << "Environment: SOCKS5 => " << socksProxy.toString() << "; HTTP Connect => " << httpProxy.toString() << std::endl; +} + +HostAddressPort EnvironmentProxyProvider::getHTTPConnectProxy() const { +	return httpProxy; +} + +HostAddressPort EnvironmentProxyProvider::getSOCKS5Proxy() const { +	return socksProxy; +} + +HostAddressPort EnvironmentProxyProvider::getFromEnv(const char* envVarName, std::string proxyProtocol) { +	char* envVar = NULL; +	std::string address; +	int port = 0; + +	envVar = getenv(envVarName); + +	proxyProtocol += "://"; +	address = envVar != NULL ? envVar : "0.0.0.0"; +	if(envVar != NULL && address.compare(0, proxyProtocol.length(), proxyProtocol) == 0) { +		address = address.substr(proxyProtocol.length(), address.length()); +		port = atoi(address.substr(address.find(':') + 1, address.length()).c_str()); +		address = address.substr(0, address.find(':')); +	} + +	return HostAddressPort(HostAddress(address), port); +} + +} diff --git a/Swiften/Network/EnvironmentProxyProvider.h b/Swiften/Network/EnvironmentProxyProvider.h new file mode 100644 index 0000000..1743269 --- /dev/null +++ b/Swiften/Network/EnvironmentProxyProvider.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Network/ProxyProvider.h" + +namespace Swift { +	class EnvironmentProxyProvider : public ProxyProvider { +		public: +			EnvironmentProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +		private: +			HostAddressPort getFromEnv(const char* envVarName, std::string proxyProtocol); +			HostAddressPort socksProxy; +			HostAddressPort httpProxy; +	}; +} + + diff --git a/Swiften/Network/FakeConnection.cpp b/Swiften/Network/FakeConnection.cpp new file mode 100644 index 0000000..be5555c --- /dev/null +++ b/Swiften/Network/FakeConnection.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Network/FakeConnection.h> + +#include <boost/bind.hpp> + +namespace Swift { + +FakeConnection::FakeConnection(EventLoop* eventLoop) : eventLoop(eventLoop), state(Initial), delayConnect(false) { +} + +FakeConnection::~FakeConnection() { +} + +void FakeConnection::listen() { +	assert(false); +} + +void FakeConnection::setError(const Error& e) { +	error = boost::optional<Error>(e); +	state = DisconnectedWithError; +	if (connectedTo) { +		eventLoop->postEvent( +				boost::bind(boost::ref(onDisconnected), error), +				shared_from_this()); +	} +} + +void FakeConnection::connect(const HostAddressPort& address) { +	if (delayConnect) { +		state = Connecting; +	} +	else { +		if (!error) { +			connectedTo = address; +			state = Connected; +		} +		else { +			state = DisconnectedWithError; +		} +		eventLoop->postEvent( +				boost::bind(boost::ref(onConnectFinished), error), +				shared_from_this()); +	} +} + +void FakeConnection::disconnect() { +	if (!error) { +		state = Disconnected; +	} +	else { +		state = DisconnectedWithError; +	} +	connectedTo.reset(); +	eventLoop->postEvent( +			boost::bind(boost::ref(onDisconnected), error), +			shared_from_this()); +} + +} diff --git a/Swiften/Network/FakeConnection.h b/Swiften/Network/FakeConnection.h index 4e2e960..693dabf 100644 --- a/Swiften/Network/FakeConnection.h +++ b/Swiften/Network/FakeConnection.h @@ -7,7 +7,6 @@  #pragma once  #include <boost/optional.hpp> -#include <boost/bind.hpp>  #include <boost/enable_shared_from_this.hpp>  #include <vector> @@ -30,56 +29,17 @@ namespace Swift {  				DisconnectedWithError  			}; -			FakeConnection(EventLoop* eventLoop) : eventLoop(eventLoop), state(Initial), delayConnect(false) {} - -			virtual void listen() { -				assert(false); -			} +			FakeConnection(EventLoop* eventLoop); +			~FakeConnection(); +			virtual void listen();  			virtual HostAddressPort getLocalAddress() const {  				return HostAddressPort();  			} -			void setError(const Error& e) { -				error = boost::optional<Error>(e); -				state = DisconnectedWithError; -				if (connectedTo) { -					eventLoop->postEvent( -							boost::bind(boost::ref(onDisconnected), error), -							shared_from_this()); -				} -			} - -			virtual void connect(const HostAddressPort& address) { -				if (delayConnect) { -					state = Connecting; -				} -				else { -					if (!error) { -						connectedTo = address; -						state = Connected; -					} -					else { -						state = DisconnectedWithError; -					} -					eventLoop->postEvent( -							boost::bind(boost::ref(onConnectFinished), error), -							shared_from_this()); -				} -			} - -			virtual void disconnect() { -				if (!error) { -					state = Disconnected; -				} -				else { -					state = DisconnectedWithError; -				} -				connectedTo.reset(); -				eventLoop->postEvent( -						boost::bind(boost::ref(onDisconnected), error), -						shared_from_this()); -			} +			void setError(const Error& e); +			virtual void connect(const HostAddressPort& address); +			virtual void disconnect();  			virtual void write(const ByteArray& data) {  				dataWritten.push_back(data); diff --git a/Swiften/Network/GConfProxyProvider.cpp b/Swiften/Network/GConfProxyProvider.cpp new file mode 100644 index 0000000..687bf77 --- /dev/null +++ b/Swiften/Network/GConfProxyProvider.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> + +#include <gconf/gconf-client.h> + +#include "Swiften/Base/Log.h" +#include "Swiften/Network/GConfProxyProvider.h" + +namespace Swift { + +GConfProxyProvider::GConfProxyProvider() { +	socksProxy = getFromGConf("/system/proxy/socks_host", "/system/proxy/socks_port"); +	httpProxy = getFromGConf("/system/http_proxy/host", "/system/http_proxy/port"); +	SWIFT_LOG(debug) << "GConf: SOCKS5 => " << socksProxy.toString() << "; HTTP Connect => " << httpProxy.toString() << std::endl; +} + +HostAddressPort GConfProxyProvider::getHTTPConnectProxy() const { +	return httpProxy; +} + +HostAddressPort GConfProxyProvider::getSOCKS5Proxy() const { +	return socksProxy; +} + +HostAddressPort GConfProxyProvider::getFromGConf(const char* gcHost, const char* gcPort) { +	std::string address; +	int port = 0; +	gchar* str; + +	GConfClient* client = gconf_client_get_default(); + +	str = gconf_client_get_string(client, gcHost, NULL); +	port = static_cast<int> (gconf_client_get_int(client, gcPort, NULL)); + +	if(str) { +		address = static_cast<char*> (str); +		g_free(str); +	} + +	g_object_unref(client); +	return HostAddressPort(HostAddress(address), port); +} + +} diff --git a/Swiften/Network/GConfProxyProvider.h b/Swiften/Network/GConfProxyProvider.h new file mode 100644 index 0000000..15586ad --- /dev/null +++ b/Swiften/Network/GConfProxyProvider.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Network/ProxyProvider.h" + +namespace Swift { +	class GConfProxyProvider : public ProxyProvider { +		public: +			GConfProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +		private: +			HostAddressPort getFromGConf(const char* gcHost, const char* gcPort); +			HostAddressPort socksProxy; +			HostAddressPort httpProxy; +	}; +} + + diff --git a/Swiften/Network/HTTPConnectProxiedConnection.cpp b/Swiften/Network/HTTPConnectProxiedConnection.cpp new file mode 100644 index 0000000..51130e5 --- /dev/null +++ b/Swiften/Network/HTTPConnectProxiedConnection.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Network/HTTPConnectProxiedConnection.h" + +#include <iostream> +#include <boost/bind.hpp> +#include <boost/thread.hpp> +#include <boost/lexical_cast.hpp> + +#include "Swiften/Base/Log.h" +#include "Swiften/Base/String.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Network/HostAddressPort.h" +#include <Swiften/Network/ConnectionFactory.h> + +using namespace Swift; + +HTTPConnectProxiedConnection::HTTPConnectProxiedConnection(ConnectionFactory* connectionFactory, HostAddressPort proxy) : connectionFactory_(connectionFactory), proxy_(proxy), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)) { +	connected_ = false; +} + +HTTPConnectProxiedConnection::~HTTPConnectProxiedConnection() { +	if (connection_) { +		connection_->onDataRead.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleDataRead, shared_from_this(), _1)); +		connection_->onDisconnected.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleDisconnected, shared_from_this(), _1)); +	} + +	if (connected_) { +		std::cerr << "Warning: Connection was still established." << std::endl; +	} +} + +void HTTPConnectProxiedConnection::connect(const HostAddressPort& server) { +	server_ = server; +	connection_ = connectionFactory_->createConnection(); +	connection_->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1)); +	connection_->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnection::handleDataRead, shared_from_this(), _1)); +	connection_->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnection::handleDisconnected, shared_from_this(), _1)); +	connection_->connect(proxy_); +} + +void HTTPConnectProxiedConnection::listen() { +	assert(false); +	connection_->listen(); +} + +void HTTPConnectProxiedConnection::disconnect() { +	connected_ = false; +	connection_->disconnect(); +} + +void HTTPConnectProxiedConnection::handleDisconnected(const boost::optional<Error>& error) { +	onDisconnected(error); +} + +void HTTPConnectProxiedConnection::write(const ByteArray& data) { +	connection_->write(data); +} + +void HTTPConnectProxiedConnection::handleConnectionConnectFinished(bool error) { +	connection_->onConnectFinished.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1)); +	if (!error) { +		std::stringstream connect; +		connect << "CONNECT " << server_.getAddress().toString() << ":" << server_.getPort() << " HTTP/1.1\r\n\r\n"; +		connection_->write(ByteArray(connect.str())); +	} +	else { +		onConnectFinished(true); +	} +} + +void HTTPConnectProxiedConnection::handleDataRead(const ByteArray& data) { +	if (!connected_) { +		SWIFT_LOG(debug) << data.toString() << std::endl; +		std::vector<std::string> tmp = String::split(data.toString(), ' '); +		if(tmp.size() > 1) { +			int status = boost::lexical_cast<int> (tmp[1].c_str());  +			SWIFT_LOG(debug) << "Proxy Status: " << status << std::endl; +			if (status / 100 == 2) { // all 2XX states are OK +				connected_ = true; +				onConnectFinished(false); +				return; +			} +			SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << data.toString() << std::endl; +		} +		disconnect(); +		onConnectFinished(true); +	} +	else { +		onDataRead(data); +	} +} + +HostAddressPort HTTPConnectProxiedConnection::getLocalAddress() const { +	return connection_->getLocalAddress(); +} diff --git a/Swiften/Network/HTTPConnectProxiedConnection.h b/Swiften/Network/HTTPConnectProxiedConnection.h new file mode 100644 index 0000000..930f5e1 --- /dev/null +++ b/Swiften/Network/HTTPConnectProxiedConnection.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/Network/Connection.h" +#include "Swiften/Network/HostAddressPort.h" + +namespace boost { +	class thread; +	namespace system { +		class error_code; +	} +} + +namespace Swift { +	class ConnectionFactory; + +	class HTTPConnectProxiedConnection : public Connection, public boost::enable_shared_from_this<HTTPConnectProxiedConnection> { +		public: +			typedef boost::shared_ptr<HTTPConnectProxiedConnection> ref; + +			~HTTPConnectProxiedConnection(); + +			static ref create(ConnectionFactory* connectionFactory, HostAddressPort proxy) { +				return ref(new HTTPConnectProxiedConnection(connectionFactory, proxy)); +			} + +			virtual void listen(); +			virtual void connect(const HostAddressPort& address); +			virtual void disconnect(); +			virtual void write(const ByteArray& data); + +			virtual HostAddressPort getLocalAddress() const; +		private: +			HTTPConnectProxiedConnection(ConnectionFactory* connectionFactory, HostAddressPort proxy); + +			void handleConnectionConnectFinished(bool error); +			void handleDataRead(const ByteArray& data); +			void handleDisconnected(const boost::optional<Error>& error); + +		private: +			bool connected_; +			ConnectionFactory* connectionFactory_;	 +			HostAddressPort proxy_; +			HostAddressPort server_; +			boost::shared_ptr<Connection> connection_; +	}; +} diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp b/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp new file mode 100644 index 0000000..050b0c0 --- /dev/null +++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Network/HTTPConnectProxiedConnectionFactory.h" + +#include "Swiften/Network/HTTPConnectProxiedConnection.h" + +namespace Swift { + +HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy) : connectionFactory_(connectionFactory), proxy_(proxy) { +} + +boost::shared_ptr<Connection> HTTPConnectProxiedConnectionFactory::createConnection() { +	return HTTPConnectProxiedConnection::create(connectionFactory_, proxy_); +} + +} diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.h b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h new file mode 100644 index 0000000..2b0c8d5 --- /dev/null +++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Network/ConnectionFactory.h" +#include "Swiften/Network/HostAddressPort.h" + +namespace Swift { +	class HTTPConnectProxiedConnectionFactory : public ConnectionFactory { +		public: +			HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy); + +			virtual boost::shared_ptr<Connection> createConnection(); + +		private: +			ConnectionFactory* connectionFactory_; +			HostAddressPort proxy_; +	}; +} diff --git a/Swiften/Network/HostAddress.cpp b/Swiften/Network/HostAddress.cpp index 7acd407..f734329 100644 --- a/Swiften/Network/HostAddress.cpp +++ b/Swiften/Network/HostAddress.cpp @@ -24,7 +24,7 @@ HostAddress::HostAddress(const std::string& address) {  	try {  		address_ = boost::asio::ip::address::from_string(address);  	} -	catch (const std::exception& t) { +	catch (const std::exception&) {  	}  } @@ -57,4 +57,8 @@ bool HostAddress::isValid() const {  	return !(address_.is_v4() && address_.to_v4().to_ulong() == 0);  } +boost::asio::ip::address HostAddress::getRawAddress() const { +	return address_; +} +  } diff --git a/Swiften/Network/HostAddress.h b/Swiften/Network/HostAddress.h index 34ccd24..0b3bdda 100644 --- a/Swiften/Network/HostAddress.h +++ b/Swiften/Network/HostAddress.h @@ -3,16 +3,12 @@   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -  #pragma once  #include <string> -#include <vector> -#include <boost/asio.hpp> +#include <boost/asio/ip/address.hpp>  namespace Swift { -	 -  	class HostAddress {  		public:  			HostAddress(); @@ -21,6 +17,7 @@ namespace Swift {  			HostAddress(const boost::asio::ip::address& address);  			std::string toString() const; +			boost::asio::ip::address getRawAddress() const;  			bool operator==(const HostAddress& o) const {  				return address_ == o.address_; diff --git a/Swiften/Network/HostAddressPort.cpp b/Swiften/Network/HostAddressPort.cpp new file mode 100644 index 0000000..e2e6012 --- /dev/null +++ b/Swiften/Network/HostAddressPort.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Network/HostAddressPort.h> + +#include <boost/lexical_cast.hpp> + +using namespace Swift; + +HostAddressPort::HostAddressPort(const HostAddress& address, int port) : address_(address), port_(port) { +} + +HostAddressPort::HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint) { +	address_ = HostAddress(endpoint.address()); +	port_ = endpoint.port(); +} + +std::string HostAddressPort::toString() const { +	return getAddress().toString() + ":" + boost::lexical_cast<std::string>(getPort()); +} diff --git a/Swiften/Network/HostAddressPort.h b/Swiften/Network/HostAddressPort.h index 6883380..23a81b8 100644 --- a/Swiften/Network/HostAddressPort.h +++ b/Swiften/Network/HostAddressPort.h @@ -6,21 +6,15 @@  #pragma once -#include <boost/asio.hpp> +#include <boost/asio/ip/tcp.hpp>  #include "Swiften/Network/HostAddress.h"  namespace Swift {  	class HostAddressPort {  		public: -			HostAddressPort(const HostAddress& address = HostAddress(), int port = -1) : address_(address), port_(port) { -			} - -			HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint) { -				address_ = HostAddress(endpoint.address()); -				port_ = endpoint.port(); -			} - +			HostAddressPort(const HostAddress& address = HostAddress(), int port = -1); +			HostAddressPort(const boost::asio::ip::tcp::endpoint& endpoint);  			const HostAddress& getAddress() const {  				return address_; @@ -37,6 +31,8 @@ namespace Swift {  			bool isValid() const {  				return address_.isValid() && port_ > 0;  			} +			 +			std::string toString() const;  		private:  			HostAddress address_; diff --git a/Swiften/Network/MacOSXProxyProvider.cpp b/Swiften/Network/MacOSXProxyProvider.cpp new file mode 100644 index 0000000..dd44eff --- /dev/null +++ b/Swiften/Network/MacOSXProxyProvider.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swiften/Base/Platform.h> +#include <Swiften/Network/MacOSXProxyProvider.h> + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <utility> + +#ifndef SWIFTEN_PLATFORM_IPHONE +#include <SystemConfiguration/SystemConfiguration.h> +#endif + +using namespace Swift; + +#ifndef SWIFTEN_PLATFORM_IPHONE +static HostAddressPort getFromDictionary(CFDictionaryRef dict, CFStringRef enabledKey, CFStringRef hostKey, CFStringRef portKey) { +	CFNumberRef numberValue = NULL; +	HostAddressPort ret = HostAddressPort(HostAddress(), 0); + +	if(CFDictionaryGetValueIfPresent(dict, reinterpret_cast<const void*> (enabledKey), reinterpret_cast<const void**> (&numberValue)) == true) { +		const int i = 0; +		CFNumberRef zero = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i); +		CFComparisonResult result = CFNumberCompare(numberValue, zero, NULL); +		CFRelease(numberValue); + +		if(result != kCFCompareEqualTo) { +			int port = 0; +			std::string host = ""; + +			try { +				CFNumberRef numberValue = reinterpret_cast<CFNumberRef> (CFDictionaryGetValue(dict, portKey)); +				if(numberValue != NULL) { +					CFNumberGetValue(numberValue, kCFNumberIntType, &port); +					CFRelease(numberValue); +				} + +				CFStringRef stringValue = reinterpret_cast<CFStringRef> (CFDictionaryGetValue(dict, hostKey)); +				if(stringValue != NULL) { +					std::vector<char> buffer; + 					// length must be +1 for the ending zero; and the Docu of CFStringGetCString tells it like +					// if the string is toby the length must be at least 5. +					CFIndex length = CFStringGetLength(stringValue) + 1; +					buffer.resize(length); +					if(CFStringGetCString(stringValue, &buffer[0], length, kCFStringEncodingMacRoman)) { +						for(std::vector<char>::iterator iter = buffer.begin(); iter != buffer.end(); iter++) { +							host += *iter; +						} +					} +					CFRelease(stringValue); +				} +			} +			catch(...) { +				std::cerr << "Exception caught ... " << std::endl; +			} +			 +			if(host != "" && port != 0) { +				ret = HostAddressPort(HostAddress(host), port); +			} +		} +	} +	return ret; +} +#endif +namespace Swift { + +MacOSXProxyProvider::MacOSXProxyProvider() { +} + +HostAddressPort MacOSXProxyProvider::getHTTPConnectProxy() const { +	HostAddressPort result; +#ifndef SWIFTEN_PLATFORM_IPHONE +	CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL); +	if(proxies != NULL) { +		result = getFromDictionary(proxies, kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); +	} +#endif +	return result; +} + +HostAddressPort MacOSXProxyProvider::getSOCKS5Proxy() const { +	HostAddressPort result; +#ifndef SWIFTEN_PLATFORM_IPHONE +	CFDictionaryRef proxies = SCDynamicStoreCopyProxies(NULL); +	if(proxies != NULL) { +		result = getFromDictionary(proxies, kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort); +	} +#endif +	return result; +} + +} diff --git a/Swiften/Network/MacOSXProxyProvider.h b/Swiften/Network/MacOSXProxyProvider.h new file mode 100644 index 0000000..5e7ff8a --- /dev/null +++ b/Swiften/Network/MacOSXProxyProvider.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once +#include "Swiften/Network/ProxyProvider.h" +#include <CoreFoundation/CoreFoundation.h> + +namespace Swift { +	class MacOSXProxyProvider : public ProxyProvider { +		public: +			MacOSXProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +	}; +} diff --git a/Swiften/Network/NullProxyProvider.cpp b/Swiften/Network/NullProxyProvider.cpp new file mode 100644 index 0000000..3b9d94d --- /dev/null +++ b/Swiften/Network/NullProxyProvider.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swiften/Network/NullProxyProvider.h> + +using namespace Swift; + +NullProxyProvider::NullProxyProvider() { +} + +HostAddressPort NullProxyProvider::getHTTPConnectProxy() const { +	return HostAddressPort(); +} + +HostAddressPort NullProxyProvider::getSOCKS5Proxy() const { +	return HostAddressPort(); +} diff --git a/Swiften/Network/NullProxyProvider.h b/Swiften/Network/NullProxyProvider.h new file mode 100644 index 0000000..544bea2 --- /dev/null +++ b/Swiften/Network/NullProxyProvider.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Network/ProxyProvider.h> + +namespace Swift { +	class NullProxyProvider : public ProxyProvider { +		public: +			NullProxyProvider(); + +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +	}; +} diff --git a/Swiften/Network/PlatformDomainNameAddressQuery.cpp b/Swiften/Network/PlatformDomainNameAddressQuery.cpp index 1832255..ec7e663 100644 --- a/Swiften/Network/PlatformDomainNameAddressQuery.cpp +++ b/Swiften/Network/PlatformDomainNameAddressQuery.cpp @@ -6,6 +6,8 @@  #include <Swiften/Network/PlatformDomainNameAddressQuery.h> +#include <boost/asio/ip/tcp.hpp> +  #include <Swiften/Network/PlatformDomainNameResolver.h>  #include <Swiften/EventLoop/EventLoop.h> diff --git a/Swiften/Network/PlatformDomainNameAddressQuery.h b/Swiften/Network/PlatformDomainNameAddressQuery.h index c2854ac..e1dc05f 100644 --- a/Swiften/Network/PlatformDomainNameAddressQuery.h +++ b/Swiften/Network/PlatformDomainNameAddressQuery.h @@ -6,7 +6,7 @@  #pragma once -#include <boost/asio.hpp> +#include <boost/asio/io_service.hpp>  #include <boost/enable_shared_from_this.hpp>  #include <Swiften/Network/DomainNameAddressQuery.h> diff --git a/Swiften/Network/PlatformDomainNameResolver.h b/Swiften/Network/PlatformDomainNameResolver.h index e681331..295ecc5 100644 --- a/Swiften/Network/PlatformDomainNameResolver.h +++ b/Swiften/Network/PlatformDomainNameResolver.h @@ -7,7 +7,7 @@  #pragma once  #include <deque> -#include <boost/thread.hpp> +#include <boost/thread/thread.hpp>  #include <boost/thread/mutex.hpp>  #include <boost/thread/condition_variable.hpp> diff --git a/Swiften/Network/PlatformProxyProvider.h b/Swiften/Network/PlatformProxyProvider.h new file mode 100644 index 0000000..13b15d2 --- /dev/null +++ b/Swiften/Network/PlatformProxyProvider.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Base/Platform.h" + +#if defined(SWIFTEN_PLATFORM_MACOSX) +#include "Swiften/Network/MacOSXProxyProvider.h" +namespace Swift { +	typedef MacOSXProxyProvider PlatformProxyProvider; +} +#elif defined(SWIFTEN_PLATFORM_WIN32) +#include "Swiften/Network/WindowsProxyProvider.h" +namespace Swift { +	typedef WindowsProxyProvider PlatformProxyProvider; +} +#else +#include "Swiften/Network/UnixProxyProvider.h" +namespace Swift { +	typedef UnixProxyProvider PlatformProxyProvider; +} +#endif diff --git a/Swiften/Network/ProxyProvider.cpp b/Swiften/Network/ProxyProvider.cpp new file mode 100644 index 0000000..fe235b1 --- /dev/null +++ b/Swiften/Network/ProxyProvider.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "ProxyProvider.h" + +namespace Swift { + +ProxyProvider::ProxyProvider() +{ +} + +ProxyProvider::~ProxyProvider() +{ +} + +} diff --git a/Swiften/Network/ProxyProvider.h b/Swiften/Network/ProxyProvider.h new file mode 100644 index 0000000..05bb5a7 --- /dev/null +++ b/Swiften/Network/ProxyProvider.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once +#include <map> + +#include "Swiften/Base/Log.h" +#include "Swiften/Network/HostAddressPort.h" +#include "Swiften/Base/String.h" + +namespace Swift { +	class ProxyProvider { +		public: +			ProxyProvider(); +			virtual ~ProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const = 0; +			virtual HostAddressPort getSOCKS5Proxy() const = 0; +	}; +} + diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript index fa186fa..183d09c 100644 --- a/Swiften/Network/SConscript +++ b/Swiften/Network/SConscript @@ -6,13 +6,21 @@ if myenv.get("HAVE_CARES", False) :     myenv.MergeFlags(myenv.get("CARES_FLAGS", {}))  sourceList = [ +			"HTTPConnectProxiedConnection.cpp", +			"HTTPConnectProxiedConnectionFactory.cpp", +			"SOCKS5ProxiedConnection.cpp", +			"SOCKS5ProxiedConnectionFactory.cpp",  			"BoostConnection.cpp",  			"BoostConnectionFactory.cpp",  			"BoostConnectionServer.cpp",  			"BoostIOServiceThread.cpp",  			"ConnectionFactory.cpp",  			"ConnectionServer.cpp", +			"DummyConnection.cpp", +			"FakeConnection.cpp", + 			"ChainedConnector.cpp",   			"Connector.cpp", + 			"Connection.cpp",  			"TimerFactory.cpp",  			"DummyTimerFactory.cpp",  			"BoostTimerFactory.cpp", @@ -24,13 +32,30 @@ sourceList = [  			"PlatformDomainNameAddressQuery.cpp",  			"StaticDomainNameResolver.cpp",  			"HostAddress.cpp", +			"HostAddressPort.cpp",  			"NetworkFactories.cpp",  			"BoostNetworkFactories.cpp",  			"Timer.cpp", -			"BoostTimer.cpp"] +			"BoostTimer.cpp", +			"ProxyProvider.cpp", +			"NullProxyProvider.cpp" +	] +  if myenv.get("HAVE_CARES", False) :     sourceList.append("CAresDomainNameResolver.cpp") +if myenv["PLATFORM"] == "darwin" : +	myenv.Append(FRAMEWORKS = ["CoreServices", "SystemConfiguration"]) +	sourceList += [ "MacOSXProxyProvider.cpp" ] +elif myenv["PLATFORM"] == "win32" : +	sourceList += [ "WindowsProxyProvider.cpp" ] +else : +	sourceList += [ "UnixProxyProvider.cpp" ] +	sourceList += [ "EnvironmentProxyProvider.cpp" ] +	if myenv.get("HAVE_GCONF", 0) : +		myenv.Append(CPPDEFINES = "HAVE_GCONF") +		myenv.MergeFlags(myenv["GCONF_FLAGS"]) +		sourceList += [ "GConfProxyProvider.cpp" ]  objects = myenv.SwiftenObject(sourceList)  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/Network/SOCKS5ProxiedConnection.cpp b/Swiften/Network/SOCKS5ProxiedConnection.cpp new file mode 100644 index 0000000..0232ede --- /dev/null +++ b/Swiften/Network/SOCKS5ProxiedConnection.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Network/SOCKS5ProxiedConnection.h" + +#include <iostream> +#include <boost/bind.hpp> +#include <boost/thread.hpp> + +#include <Swiften/Network/ConnectionFactory.h> +#include "Swiften/Base/Log.h" +#include "Swiften/Base/String.h" +#include "Swiften/Base/ByteArray.h" +#include "Swiften/Network/HostAddressPort.h" + +using namespace Swift; + +SOCKS5ProxiedConnection::SOCKS5ProxiedConnection(ConnectionFactory* connectionFactory, const HostAddressPort& proxy) : connectionFactory_(connectionFactory), proxy_(proxy), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)) { +	connected_ = false; +} + +SOCKS5ProxiedConnection::~SOCKS5ProxiedConnection() { +	if (connection_) { +		connection_->onDataRead.disconnect(boost::bind(&SOCKS5ProxiedConnection::handleDataRead, shared_from_this(), _1)); +		connection_->onDisconnected.disconnect(boost::bind(&SOCKS5ProxiedConnection::handleDisconnected, shared_from_this(), _1)); +	} + +	if (connected_) { +		std::cerr << "Warning: Connection was still established." << std::endl; +	} +} + +void SOCKS5ProxiedConnection::connect(const HostAddressPort& server) { +	server_ = server; +	connection_ = connectionFactory_->createConnection(); +	connection_->onConnectFinished.connect(boost::bind(&SOCKS5ProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1)); +	connection_->onDataRead.connect(boost::bind(&SOCKS5ProxiedConnection::handleDataRead, shared_from_this(), _1)); +	connection_->onDisconnected.connect(boost::bind(&SOCKS5ProxiedConnection::handleDisconnected, shared_from_this(), _1)); +	SWIFT_LOG(debug) << "Trying to connect via proxy " << proxy_.getAddress().toString() << ":" << proxy_.getPort() << std::endl; +	SWIFT_LOG(debug) << "to server " << server.getAddress().toString() << ":" << server.getPort() << std::endl; +	connection_->connect(proxy_); +} + +void SOCKS5ProxiedConnection::listen() { +	assert(false); +	connection_->listen(); +} + +void SOCKS5ProxiedConnection::disconnect() { +	connected_ = false; +	if (connection_) { +		connection_->disconnect(); +	} +} + +void SOCKS5ProxiedConnection::handleDisconnected(const boost::optional<Error>& error) { +	onDisconnected(error); +} + +void SOCKS5ProxiedConnection::write(const ByteArray& data) { +	if (connection_) { +		connection_->write(data); +	} +} + +void SOCKS5ProxiedConnection::handleConnectionConnectFinished(bool error) { +	connection_->onConnectFinished.disconnect(boost::bind(&SOCKS5ProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1)); +	if (!error) { +		SWIFT_LOG(debug) << "Connection to proxy established, now connect to the server via it." << std::endl; +		 +		proxyState_ = ProxyAuthenticating; +		ByteArray socksConnect; +		socksConnect += 0x05; // VER = SOCKS5 = 0x05 +		socksConnect += 0x01; // Number of authentication methods after this byte. +		socksConnect += 0x00; // 0x00 == no authentication +		// buffer.push_back(0x01); // 0x01 == GSSAPI  +		// buffer.push_back(0x02); // 0x02 ==  Username/Password +		// rest see RFC 1928 (http://tools.ietf.org/html/rfc1928) +		connection_->write(socksConnect); +	} +	else { +		onConnectFinished(true); +	} +} + +void SOCKS5ProxiedConnection::handleDataRead(const ByteArray& data) { +	ByteArray socksConnect; +	boost::asio::ip::address rawAddress = server_.getAddress().getRawAddress(); +	assert(rawAddress.is_v4() || rawAddress.is_v6()); +	if (!connected_) { +		if (proxyState_ == ProxyAuthenticating) { +			SWIFT_LOG(debug) << "ProxyAuthenticating response received, reply with the connect BYTEs" << std::endl; +			unsigned char choosenMethod = static_cast<unsigned char> (data[1]); +			if (data[0] == 0x05 && choosenMethod != 0xFF) { +				switch(choosenMethod) { // use the correct Method +					case 0x00: +						try { +							proxyState_ = ProxyConnecting; +							socksConnect += 0x05; // VER = SOCKS5 = 0x05 +							socksConnect += 0x01; // Construct a TCP connection. (CMD) +							socksConnect += 0x00; // reserved. +							socksConnect += rawAddress.is_v4() ? 0x01 : 0x04; // IPv4 == 0x01, Hostname == 0x02, IPv6 == 0x04. (ATYP) +							size_t size = rawAddress.is_v4() ? rawAddress.to_v4().to_bytes().size() : rawAddress.to_v6().to_bytes().size(); +							for (size_t s = 0; s < size; s++) { +								unsigned char uc; +								if(rawAddress.is_v4()) { +									uc = rawAddress.to_v4().to_bytes()[s]; // the address. +								} +								else { +									uc = rawAddress.to_v6().to_bytes()[s]; // the address. +								} +								socksConnect += static_cast<char> (uc); +						 +							} +							socksConnect += static_cast<unsigned char> ((server_.getPort() >> 8) & 0xFF); // highbyte of the port. +							socksConnect += static_cast<unsigned char> (server_.getPort() & 0xFF); // lowbyte of the port. +							connection_->write(socksConnect); +							return; +						} +						catch(...) { +							std::cerr << "exception caught" << std::endl; +						} +						connection_->write(socksConnect); +						break; +					default: +						onConnectFinished(true); +						break; +				} +				return; +			} +		} +		else if (proxyState_ == ProxyConnecting) { +			SWIFT_LOG(debug) << "Connect response received, check if successfully." << std::endl; +			SWIFT_LOG(debug) << "Errorbyte: 0x" << std::hex << static_cast<int> (data[1]) << std::dec << std::endl; +			/* + +			data.at(1) can be one of the following: +			0x00 	succeeded +			0x01 	general SOCKS server failure +			0x02 	connection not allowed by ruleset +			0x03 	Network unreachable +			0x04 	Host unreachable +			0x05 	Connection refused +			0x06 	TTL expired +			0x07 	Command not supported (CMD) +			0x08 	Address type not supported (ATYP) +			0x09 bis 0xFF 	unassigned +			*/ +			if (data[0] == 0x05 && data[1] == 0x0) { +				SWIFT_LOG(debug) << "Successfully connected the server via the proxy." << std::endl; +				connected_ = true; +				onConnectFinished(false); +				return; +			} +			else { +				std::cerr << "SOCKS Proxy returned an error: " << std::hex << data[1] << std::endl; +			} +			return; +		} +	} +	else { +		onDataRead(data); +		return; +	} +	disconnect(); +	onConnectFinished(true); +} + +HostAddressPort SOCKS5ProxiedConnection::getLocalAddress() const { +	return connection_->getLocalAddress(); +} diff --git a/Swiften/Network/SOCKS5ProxiedConnection.h b/Swiften/Network/SOCKS5ProxiedConnection.h new file mode 100644 index 0000000..779bfa1 --- /dev/null +++ b/Swiften/Network/SOCKS5ProxiedConnection.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/Network/Connection.h" +#include "Swiften/Network/HostAddressPort.h" + +namespace boost { +	class thread; +	namespace system { +		class error_code; +	} +} + +namespace Swift { +	class ConnectionFactory; + +	class SOCKS5ProxiedConnection : public Connection, public boost::enable_shared_from_this<SOCKS5ProxiedConnection> { +		public: +			typedef boost::shared_ptr<SOCKS5ProxiedConnection> ref; + +			~SOCKS5ProxiedConnection(); + +			static ref create(ConnectionFactory* connectionFactory, const HostAddressPort& proxy) { +				return ref(new SOCKS5ProxiedConnection(connectionFactory, proxy)); +			} + +			virtual void listen(); +			virtual void connect(const HostAddressPort& address); +			virtual void disconnect(); +			virtual void write(const ByteArray& data); + +			virtual HostAddressPort getLocalAddress() const; + +		private: +			SOCKS5ProxiedConnection(ConnectionFactory* connectionFactory, const HostAddressPort& proxy); + +			void handleConnectionConnectFinished(bool error); +			void handleDataRead(const ByteArray& data); +			void handleDisconnected(const boost::optional<Error>& error); + +		private: +			enum { +				ProxyAuthenticating = 0, +				ProxyConnecting, +			} proxyState_; +			bool connected_; +			ConnectionFactory* connectionFactory_;	 +			HostAddressPort proxy_; +			HostAddressPort server_; +			boost::shared_ptr<Connection> connection_; +	}; +} diff --git a/Swiften/Network/SOCKS5ProxiedConnectionFactory.cpp b/Swiften/Network/SOCKS5ProxiedConnectionFactory.cpp new file mode 100644 index 0000000..ab75644 --- /dev/null +++ b/Swiften/Network/SOCKS5ProxiedConnectionFactory.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Network/SOCKS5ProxiedConnectionFactory.h" + +#include "Swiften/Network/SOCKS5ProxiedConnection.h" + +namespace Swift { + +SOCKS5ProxiedConnectionFactory::SOCKS5ProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy) : connectionFactory_(connectionFactory), proxy_(proxy) { +} + +boost::shared_ptr<Connection> SOCKS5ProxiedConnectionFactory::createConnection() { +	return SOCKS5ProxiedConnection::create(connectionFactory_, proxy_); +} + +} diff --git a/Swiften/Network/SOCKS5ProxiedConnectionFactory.h b/Swiften/Network/SOCKS5ProxiedConnectionFactory.h new file mode 100644 index 0000000..8f310c5 --- /dev/null +++ b/Swiften/Network/SOCKS5ProxiedConnectionFactory.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Network/ConnectionFactory.h" +#include "Swiften/Network/HostAddressPort.h" + +namespace Swift { +	class SOCKS5ProxiedConnectionFactory : public ConnectionFactory { +		public: +			SOCKS5ProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy); + +			virtual boost::shared_ptr<Connection> createConnection(); + +		private: +			ConnectionFactory* connectionFactory_; +			HostAddressPort proxy_; +	}; +} diff --git a/Swiften/Network/UnitTest/ChainedConnectorTest.cpp b/Swiften/Network/UnitTest/ChainedConnectorTest.cpp new file mode 100644 index 0000000..9c4c99d --- /dev/null +++ b/Swiften/Network/UnitTest/ChainedConnectorTest.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Network/ChainedConnector.h> +#include <Swiften/Network/Connection.h> +#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/HostAddressPort.h> +#include <Swiften/Network/StaticDomainNameResolver.h> +#include <Swiften/Network/DummyTimerFactory.h> +#include <Swiften/EventLoop/DummyEventLoop.h> + +using namespace Swift; + +class ChainedConnectorTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(ChainedConnectorTest); +		CPPUNIT_TEST(testConnect_FirstConnectorSucceeds); +		CPPUNIT_TEST(testConnect_SecondConnectorSucceeds); +		CPPUNIT_TEST(testConnect_NoConnectorSucceeds); +		CPPUNIT_TEST(testStop); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			host = HostAddressPort(HostAddress("1.1.1.1"), 1234); +			eventLoop = new DummyEventLoop(); +			resolver = new StaticDomainNameResolver(eventLoop); +			resolver->addXMPPClientService("foo.com", host); +			connectionFactory1 = new MockConnectionFactory(eventLoop, 1); +			connectionFactory2 = new MockConnectionFactory(eventLoop, 2); +			timerFactory = new DummyTimerFactory(); +		} + +		void tearDown() { +			delete timerFactory; +			delete connectionFactory2; +			delete connectionFactory1; +			delete resolver; +			delete eventLoop; +		} + +		void testConnect_FirstConnectorSucceeds() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = true; +			connectionFactory2->connects = false; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(connections[0]); +			CPPUNIT_ASSERT_EQUAL(1, boost::dynamic_pointer_cast<MockConnection>(connections[0])->id); +		} + +		void testConnect_SecondConnectorSucceeds() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = false; +			connectionFactory2->connects = true; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(connections[0]); +			CPPUNIT_ASSERT_EQUAL(2, boost::dynamic_pointer_cast<MockConnection>(connections[0])->id); +		} + +		void testConnect_NoConnectorSucceeds() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = false; +			connectionFactory2->connects = false; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(!connections[0]); +		} + +		void testStop() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = true; +			connectionFactory2->connects = false; + +			testling->start(); +			testling->stop(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(!connections[0]); +		} + +	private: +		boost::shared_ptr<ChainedConnector> createConnector() { +			std::vector<ConnectionFactory*> factories; +			factories.push_back(connectionFactory1); +			factories.push_back(connectionFactory2); +			boost::shared_ptr<ChainedConnector> connector = boost::make_shared<ChainedConnector>("foo.com", resolver, factories, timerFactory); +			connector->onConnectFinished.connect(boost::bind(&ChainedConnectorTest::handleConnectorFinished, this, _1)); +			return connector; +		} + +		void handleConnectorFinished(boost::shared_ptr<Connection> connection) { +			boost::shared_ptr<MockConnection> c(boost::dynamic_pointer_cast<MockConnection>(connection)); +			if (connection) { +				assert(c); +			} +			connections.push_back(c); +		} + +		struct MockConnection : public Connection { +			public: +				MockConnection(bool connects, int id, EventLoop* eventLoop) : connects(connects), id(id), eventLoop(eventLoop) { +				} + +				void listen() { assert(false); } +				void connect(const HostAddressPort&) { +					eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), !connects)); +				} + +				HostAddressPort getLocalAddress() const { return HostAddressPort(); } +				void disconnect() { assert(false); } +				void write(const ByteArray&) { assert(false); } + +				bool connects; +				int id; +				EventLoop* eventLoop; +		}; + +		struct MockConnectionFactory : public ConnectionFactory { +			MockConnectionFactory(EventLoop* eventLoop, int id) : eventLoop(eventLoop), connects(true), id(id) { +			} + +			boost::shared_ptr<Connection> createConnection() { +				return boost::make_shared<MockConnection>(connects, id, eventLoop); +			} + +			EventLoop* eventLoop; +			bool connects; +			int id; +		}; + +	private: +		HostAddressPort host; +		DummyEventLoop* eventLoop; +		StaticDomainNameResolver* resolver; +		MockConnectionFactory* connectionFactory1; +		MockConnectionFactory* connectionFactory2; +		DummyTimerFactory* timerFactory; +		std::vector< boost::shared_ptr<MockConnection> > connections; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChainedConnectorTest); diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp new file mode 100644 index 0000000..6c4c89c --- /dev/null +++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/optional.hpp> +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Network/Connection.h> +#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/HTTPConnectProxiedConnection.h> +#include <Swiften/Network/HostAddressPort.h> +#include <Swiften/EventLoop/DummyEventLoop.h> + +using namespace Swift; + +class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(HTTPConnectProxiedConnectionTest); +		CPPUNIT_TEST(testConnect_CreatesConnectionToProxy); +		CPPUNIT_TEST(testConnect_SendsConnectRequest); +		CPPUNIT_TEST(testConnect_ReceiveConnectResponse); +		CPPUNIT_TEST(testConnect_ReceiveMalformedConnectResponse); +		CPPUNIT_TEST(testConnect_ReceiveErrorConnectResponse); +		CPPUNIT_TEST(testConnect_ReceiveDataAfterConnect); +		CPPUNIT_TEST(testWrite_AfterConnect); +		CPPUNIT_TEST(testDisconnect_AfterConnectRequest); +		CPPUNIT_TEST(testDisconnect_AfterConnect); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void setUp() { +			proxyHost = HostAddressPort(HostAddress("1.1.1.1"), 1234); +			host = HostAddressPort(HostAddress("2.2.2.2"), 2345); +			eventLoop = new DummyEventLoop(); +			connectionFactory = new MockConnectionFactory(eventLoop); +			connectFinished = false; +			disconnected = false; +		} + +		void tearDown() { +			delete connectionFactory; +			delete eventLoop; +		} + +		void testConnect_CreatesConnectionToProxy() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); + +			testling->connect(host); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connectionFactory->connections.size())); +			CPPUNIT_ASSERT(connectionFactory->connections[0]->hostAddressPort); +			CPPUNIT_ASSERT(proxyHost == *connectionFactory->connections[0]->hostAddressPort); +			CPPUNIT_ASSERT(!connectFinished); +		} + +		void testConnect_SendsConnectRequest() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); + +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(ByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n\r\n"), connectionFactory->connections[0]->dataWritten); +		} + +		void testConnect_ReceiveConnectResponse() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); + +			connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT(connectFinished); +			CPPUNIT_ASSERT(!connectFinishedWithError); +			CPPUNIT_ASSERT(dataRead.isEmpty()); +		} + +		void testConnect_ReceiveMalformedConnectResponse() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); + +			connectionFactory->connections[0]->onDataRead(ByteArray("FLOOP")); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT(connectFinished); +			CPPUNIT_ASSERT(connectFinishedWithError); +			CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); +		} + +		void testConnect_ReceiveErrorConnectResponse() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); + +			connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 401 Unauthorized\r\n\r\n")); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT(connectFinished); +			CPPUNIT_ASSERT(connectFinishedWithError); +			CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); +		} + +		void testConnect_ReceiveDataAfterConnect() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); +			connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); +			eventLoop->processEvents(); + +			connectionFactory->connections[0]->onDataRead(ByteArray("abcdef")); + +			CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), dataRead); +		} + +		void testWrite_AfterConnect() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); +			connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); +			eventLoop->processEvents(); +			connectionFactory->connections[0]->dataWritten.clear(); + +			testling->write(ByteArray("abcdef")); + +			CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); +		} + +		void testDisconnect_AfterConnectRequest() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); + +			testling->disconnect(); + +			CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); +			CPPUNIT_ASSERT(disconnected); +			CPPUNIT_ASSERT(!disconnectedError); +		} + +		void testDisconnect_AfterConnect() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); +			testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); +			eventLoop->processEvents(); +			connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); +			eventLoop->processEvents(); + +			testling->disconnect(); + +			CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); +			CPPUNIT_ASSERT(disconnected); +			CPPUNIT_ASSERT(!disconnectedError); +		} + +	private: +		HTTPConnectProxiedConnection::ref createTestling() { +			boost::shared_ptr<HTTPConnectProxiedConnection> c = HTTPConnectProxiedConnection::create(connectionFactory, proxyHost); +			c->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleConnectFinished, this, _1)); +			c->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDisconnected, this, _1)); +			c->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDataRead, this, _1)); +			return c; +		} + +		void handleConnectFinished(bool error) { +			connectFinished = true; +			connectFinishedWithError = error; +		} + +		void handleDisconnected(const boost::optional<Connection::Error>& e) { +			disconnected = true; +			disconnectedError = e; +		} + +		void handleDataRead(const ByteArray& d) { +			dataRead += d; +		} + +		struct MockConnection : public Connection { +			public: +				MockConnection(const std::vector<HostAddressPort>& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) { +				} + +				void listen() { assert(false); } + +				void connect(const HostAddressPort& address) { +					hostAddressPort = address; +					bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); +					eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); +				} + +				HostAddressPort getLocalAddress() const { return HostAddressPort(); } + +				void disconnect() {  +					disconnected = true; +					onDisconnected(boost::optional<Connection::Error>()); +				} +				 +				void write(const ByteArray& d) {  +					dataWritten += d; +				} + +				EventLoop* eventLoop; +				boost::optional<HostAddressPort> hostAddressPort; +				std::vector<HostAddressPort> failingPorts; +				ByteArray dataWritten; +				bool disconnected; +		}; + +		struct MockConnectionFactory : public ConnectionFactory { +			MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { +			} + +			boost::shared_ptr<Connection> createConnection() { +				boost::shared_ptr<MockConnection> connection = boost::make_shared<MockConnection>(failingPorts, eventLoop); +				connections.push_back(connection); +				return connection; +			} + +			EventLoop* eventLoop; +			std::vector< boost::shared_ptr<MockConnection> > connections; +			std::vector<HostAddressPort> failingPorts; +		}; + +	private: +		HostAddressPort proxyHost; +		HostAddressPort host; +		DummyEventLoop* eventLoop; +		MockConnectionFactory* connectionFactory; +		std::vector< boost::shared_ptr<MockConnection> > connections; +		bool connectFinished; +		bool connectFinishedWithError; +		bool disconnected; +		boost::optional<Connection::Error> disconnectedError; +		ByteArray dataRead; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(HTTPConnectProxiedConnectionTest); diff --git a/Swiften/Network/UnixProxyProvider.cpp b/Swiften/Network/UnixProxyProvider.cpp new file mode 100644 index 0000000..e21b310 --- /dev/null +++ b/Swiften/Network/UnixProxyProvider.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <iostream> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Network/UnixProxyProvider.h" + +namespace Swift { + +UnixProxyProvider::UnixProxyProvider() : +#if defined(HAVE_GCONF) +	gconfProxyProvider(), +#endif +	environmentProxyProvider() +{ +} + +HostAddressPort UnixProxyProvider::getSOCKS5Proxy() const { +	HostAddressPort proxy; +#if defined(HAVE_GCONF) +	proxy = gconfProxyProvider.getSOCKS5Proxy(); +	if(proxy.isValid()) { +		return proxy; +	} +#endif +	proxy = environmentProxyProvider.getSOCKS5Proxy(); +	if(proxy.isValid()) { +		return proxy; +	} +	return HostAddressPort(HostAddress(), 0); +} + +HostAddressPort UnixProxyProvider::getHTTPConnectProxy() const { +	HostAddressPort proxy; +#if defined(HAVE_GCONF) +	proxy = gconfProxyProvider.getHTTPConnectProxy(); +	if(proxy.isValid()) { +		return proxy; +	} +#endif +	proxy = environmentProxyProvider.getHTTPConnectProxy(); +	if(proxy.isValid()) { +		return proxy; +	} +	return HostAddressPort(HostAddress(), 0); +} + + +} diff --git a/Swiften/Network/UnixProxyProvider.h b/Swiften/Network/UnixProxyProvider.h new file mode 100644 index 0000000..509cf4b --- /dev/null +++ b/Swiften/Network/UnixProxyProvider.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once +#if defined(HAVE_GCONF) +#  include "Swiften/Network/GConfProxyProvider.h" +#endif +#include "Swiften/Network/EnvironmentProxyProvider.h" + +namespace Swift { +	class UnixProxyProvider : public ProxyProvider { +		public: +			UnixProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +		private: +#if defined(HAVE_GCONF) +			GConfProxyProvider gconfProxyProvider; +#endif +			EnvironmentProxyProvider environmentProxyProvider; +	}; +} diff --git a/Swiften/Network/WindowsProxyProvider.cpp b/Swiften/Network/WindowsProxyProvider.cpp new file mode 100644 index 0000000..5f6f5d2 --- /dev/null +++ b/Swiften/Network/WindowsProxyProvider.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <iostream> +#include <boost/lexical_cast.hpp> + +#include "Swiften/Base/log.h" +#include "Swiften/Base/foreach.h" +#include "Swiften/Network/WindowsProxyProvider.h" +#include "Swiften/Base/ByteArray.h" + +#include <windows.h> + +namespace Swift { + +WindowsProxyProvider::WindowsProxyProvider() +: ProxyProvider() +{ +	HKEY hKey = (HKEY)INVALID_HANDLE_VALUE; +	long result; +		 +	result = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_READ, &hKey); +	if (result == ERROR_SUCCESS && hKey != INVALID_HANDLE_VALUE && proxyEnabled(hKey)) { +		DWORD dataType = REG_SZ; +		DWORD dataSize = 0; +		ByteArray dataBuffer; + +		result = RegQueryValueEx(hKey, "ProxyServer", NULL, &dataType, NULL, &dataSize); +		if(result != ERROR_SUCCESS) { +			return; +		} +		dataBuffer.resize(dataSize); +		result = RegQueryValueEx(hKey, "ProxyServer", NULL, &dataType, reinterpret_cast<BYTE*>(dataBuffer.getData()), &dataSize); +		if(result == ERROR_SUCCESS) { +			std::vector<std::string> proxies = String::split(dataBuffer.toString(), ';'); +			std::pair<std::string, std::string> protocolAndProxy; +			foreach(std::string proxy, proxies) { +				if(proxy.find('=') != std::string::npos) { +					protocolAndProxy = String::getSplittedAtFirst(proxy, '='); +					SWIFT_LOG(debug) << "Found proxy: " << protocolAndProxy.first << " => " << protocolAndProxy.second << std::endl; +					if(protocolAndProxy.first.compare("socks") == 0) { +						socksProxy = getAsHostAddressPort(protocolAndProxy.second); +					} +					else if (protocolAndProxy.first.compare("http") == 0) { +						httpProxy = getAsHostAddressPort(protocolAndProxy.second); +					} +				} +			} +		} +	} +} + +HostAddressPort WindowsProxyProvider::getHTTPConnectProxy() const { +	return httpProxy; +} + +HostAddressPort WindowsProxyProvider::getSOCKS5Proxy() const { +	return socksProxy; +} + +HostAddressPort WindowsProxyProvider::getAsHostAddressPort(std::string proxy) { +	HostAddressPort ret(HostAddress(), 0); + +	try { +		std::pair<std::string, std::string> tmp; +		int port = 0; +		tmp = String::getSplittedAtFirst(proxy, ':'); +		// .c_str() is needed as tmp.second can include a \0 char which will end in an exception of the lexical cast. +		// with .c_str() the \0 will not be part of the string which is to be casted +		port = boost::lexical_cast<int> (tmp.second.c_str()); +		ret = HostAddressPort(HostAddress(tmp.first), port); +	} +	catch(...) { +			std::cerr << "Exception occured while parsing windows proxy \"getHostAddressPort\"." << std::endl; +	} + +	return ret; +} + + +bool WindowsProxyProvider::proxyEnabled(HKEY hKey) const { +	bool ret = false; +	long result; +	DWORD dataType = REG_DWORD; +	DWORD dataSize = 0; +	DWORD data = 0; +	ByteArray dataBuffer; + +	if(hKey == INVALID_HANDLE_VALUE) +		return ret; + +	result = RegQueryValueEx(hKey, "ProxyEnable", NULL, &dataType, NULL, &dataSize); +	if(result != ERROR_SUCCESS) +		return ret; + +	dataBuffer.resize(dataSize); +	result = RegQueryValueEx(hKey, "ProxyEnable", NULL, &dataType, reinterpret_cast<BYTE*>(dataBuffer.getData()), &dataSize); +	if(result != ERROR_SUCCESS) +		return ret; + +	for(size_t t = 0; t < dataBuffer.getSize(); t++) { +		data += static_cast<int> (dataBuffer[t]) * pow(256, static_cast<double>(t)); +	} +	return (data == 1); +} + +} diff --git a/Swiften/Network/WindowsProxyProvider.h b/Swiften/Network/WindowsProxyProvider.h new file mode 100644 index 0000000..d8d08f0 --- /dev/null +++ b/Swiften/Network/WindowsProxyProvider.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once +#include "Swiften/Network/ProxyProvider.h" + +namespace Swift { +	class WindowsProxyProvider : public ProxyProvider { +		public: +			WindowsProxyProvider(); +			virtual HostAddressPort getHTTPConnectProxy() const; +			virtual HostAddressPort getSOCKS5Proxy() const; +		private: +			HostAddressPort getAsHostAddressPort(std::string proxy); +			bool proxyEnabled(HKEY hKey) const; +			HostAddressPort socksProxy; +			HostAddressPort httpProxy; +	}; +} diff --git a/Swiften/Parser/Attribute.h b/Swiften/Parser/Attribute.h new file mode 100644 index 0000000..f1f9a83 --- /dev/null +++ b/Swiften/Parser/Attribute.h @@ -0,0 +1,33 @@ +/* + * 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 <string> + +namespace Swift { +	class Attribute { +		public: +			Attribute(const std::string& name, const std::string& ns) : name(name), ns(ns) { +			} + +			const std::string& getName() const { +				return name; +			} + +			const std::string& getNamespace() const { +				return ns; +			} + +			bool operator==(const Attribute& o) const { +				return o.name == name && o.ns == ns; +			} + +		private: +			std::string name; +			std::string ns; +	}; +} diff --git a/Swiften/Parser/AttributeMap.cpp b/Swiften/Parser/AttributeMap.cpp new file mode 100644 index 0000000..1aeaf99 --- /dev/null +++ b/Swiften/Parser/AttributeMap.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/AttributeMap.h> + +#include <algorithm> +#include <boost/optional.hpp> + +using namespace Swift; + +namespace { +	struct AttributeIs { +		AttributeIs(const Attribute& attribute) : attribute(attribute) { +		} + +		bool operator()(const AttributeMap::Entry& o) const { +			return o.getAttribute() == attribute; +		} + +		Attribute attribute; +	}; +} + +AttributeMap::AttributeMap() { +} + +std::string AttributeMap::getAttribute(const std::string& attribute, const std::string& ns) const { +	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, ns))); +	if (i == attributes.end()) { +		return ""; +	} +	else { +		return i->getValue(); +	} +} + +bool AttributeMap::getBoolAttribute(const std::string& attribute, bool defaultValue) const { +	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, ""))); +	if (i == attributes.end()) { +		return defaultValue; +	} +	else { +		return i->getValue() == "true" || i->getValue() == "1"; +	} +} + +boost::optional<std::string> AttributeMap::getAttributeValue(const std::string& attribute) const { +	AttributeValueMap::const_iterator i = std::find_if(attributes.begin(), attributes.end(), AttributeIs(Attribute(attribute, ""))); +	if (i == attributes.end()) { +		return boost::optional<std::string>(); +	} +	else { +		return i->getValue(); +	} +} + +void AttributeMap::addAttribute(const std::string& name, const std::string& ns, const std::string& value) { +	attributes.push_back(Entry(Attribute(name, ns), value)); +} diff --git a/Swiften/Parser/AttributeMap.h b/Swiften/Parser/AttributeMap.h index c8b287b..31df606 100644 --- a/Swiften/Parser/AttributeMap.h +++ b/Swiften/Parser/AttributeMap.h @@ -4,38 +4,50 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef ATTRIBUTEMAP_H -#define ATTRIBUTEMAP_H +#pragma once +#include <vector> +#include <string>  #include <map> +#include <boost/optional/optional_fwd.hpp> -#include <string> +#include <Swiften/Parser/Attribute.h>  namespace Swift { -	class AttributeMap : public std::map<std::string,std::string> { +	class AttributeMap {  		public: -			AttributeMap() {} - -			std::string getAttribute(const std::string& attribute) const { -				AttributeMap::const_iterator i = find(attribute); -				if (i == end()) { -					return ""; -				} -				else { -					return i->second; -				} -			} +			class Entry { +				public: +					Entry(const Attribute& attribute, const std::string& value) : attribute(attribute), value(value) { +					} + +					const Attribute& getAttribute() const { +						return attribute; +					} + +					const std::string& getValue() const { +						return value; +					} + +				private: +					Attribute attribute; +					std::string value; +			}; -			bool getBoolAttribute(const std::string& attribute, bool defaultValue = false) const { -				AttributeMap::const_iterator i = find(attribute); -				if (i == end()) { -					return defaultValue; -				} -				else { -					return i->second == "true" || i->second == "1"; -				} +			AttributeMap(); + +			std::string getAttribute(const std::string& attribute, const std::string& ns = "") const; +			bool getBoolAttribute(const std::string& attribute, bool defaultValue = false) const; +			boost::optional<std::string> getAttributeValue(const std::string&) const; + +			void addAttribute(const std::string& name, const std::string& ns, const std::string& value); + +			const std::vector<Entry>& getEntries() const { +				return attributes;  			} + +		private: +			typedef std::vector<Entry> AttributeValueMap; +			AttributeValueMap attributes;  	};  } - -#endif diff --git a/Swiften/Parser/ExpatParser.cpp b/Swiften/Parser/ExpatParser.cpp index 88be752..448b199 100644 --- a/Swiften/Parser/ExpatParser.cpp +++ b/Swiften/Parser/ExpatParser.cpp @@ -30,7 +30,7 @@ static void handleStartElement(void* client, const XML_Char* name, const XML_Cha  			nsAttributePair.second = nsAttributePair.first;  			nsAttributePair.first = "";  		} -		attributeValues[nsAttributePair.second] = std::string(*(currentAttribute+1)); +		attributeValues.addAttribute(nsAttributePair.second, nsAttributePair.first, std::string(*(currentAttribute+1)));  		currentAttribute += 2;  	} diff --git a/Swiften/Parser/IQParser.cpp b/Swiften/Parser/IQParser.cpp index e0883f2..62d775f 100644 --- a/Swiften/Parser/IQParser.cpp +++ b/Swiften/Parser/IQParser.cpp @@ -5,8 +5,9 @@   */  #include <iostream> +#include <boost/optional.hpp> -#include "Swiften/Parser/IQParser.h" +#include <Swiften/Parser/IQParser.h>  namespace Swift { @@ -15,22 +16,22 @@ IQParser::IQParser(PayloadParserFactoryCollection* factories) :  }  void IQParser::handleStanzaAttributes(const AttributeMap& attributes) { -	AttributeMap::const_iterator type = attributes.find("type"); -	if (type != attributes.end()) { -		if (type->second == "set") { +	boost::optional<std::string> type = attributes.getAttributeValue("type"); +	if (type) { +		if (*type == "set") {  			getStanzaGeneric()->setType(IQ::Set);  		} -		else if (type->second == "get") { +		else if (*type == "get") {  			getStanzaGeneric()->setType(IQ::Get);  		} -		else if (type->second == "result") { +		else if (*type == "result") {  			getStanzaGeneric()->setType(IQ::Result);  		} -		else if (type->second == "error") { +		else if (*type == "error") {  			getStanzaGeneric()->setType(IQ::Error);  		}  		else { -			std::cerr << "Unknown IQ type: " << type->second << std::endl; +			std::cerr << "Unknown IQ type: " << *type << std::endl;  			getStanzaGeneric()->setType(IQ::Get);  		}  	} diff --git a/Swiften/Parser/IQParser.h b/Swiften/Parser/IQParser.h index e104dc4..35ab132 100644 --- a/Swiften/Parser/IQParser.h +++ b/Swiften/Parser/IQParser.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_IQParser_H -#define SWIFTEN_IQParser_H +#pragma once  #include "Swiften/Parser/GenericStanzaParser.h"  #include "Swiften/Elements/IQ.h" @@ -19,5 +18,3 @@ namespace Swift {  			virtual void handleStanzaAttributes(const AttributeMap&);  	};  } - -#endif diff --git a/Swiften/Parser/LibXMLParser.cpp b/Swiften/Parser/LibXMLParser.cpp index 34db4ca..0b15848 100644 --- a/Swiften/Parser/LibXMLParser.cpp +++ b/Swiften/Parser/LibXMLParser.cpp @@ -15,10 +15,18 @@  namespace Swift { -static void handleStartElement(void *client, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int, const xmlChar ** attributes) { +static void handleStartElement(void *client, const xmlChar* name, const xmlChar*, const xmlChar* xmlns, int, const xmlChar**, int nbAttributes, int nbDefaulted, const xmlChar ** attributes) {  	AttributeMap attributeValues; +	if (nbDefaulted != 0) { +		// Just because i don't understand what this means yet :-) +		std::cerr << "Unexpected nbDefaulted on XML element" << std::endl; +	}  	for (int i = 0; i < nbAttributes*5; i += 5) { -		attributeValues[std::string(reinterpret_cast<const char*>(attributes[i]))] = std::string(reinterpret_cast<const char*>(attributes[i+3]), attributes[i+4]-attributes[i+3]); +		std::string attributeNS = ""; +		if (attributes[i+2]) { +			attributeNS = std::string(reinterpret_cast<const char*>(attributes[i+2])); +		} +		attributeValues.addAttribute(std::string(reinterpret_cast<const char*>(attributes[i])), attributeNS, std::string(reinterpret_cast<const char*>(attributes[i+3]), attributes[i+4]-attributes[i+3]));  	}  	static_cast<XMLParserClient*>(client)->handleStartElement(reinterpret_cast<const char*>(name), (xmlns ? reinterpret_cast<const char*>(xmlns) : std::string()), attributeValues);  } diff --git a/Swiften/Parser/MessageParser.cpp b/Swiften/Parser/MessageParser.cpp index 5f4d59c..7f5e6d4 100644 --- a/Swiften/Parser/MessageParser.cpp +++ b/Swiften/Parser/MessageParser.cpp @@ -4,9 +4,9 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include <iostream> +#include <boost/optional.hpp> -#include "Swiften/Parser/MessageParser.h" +#include <Swiften/Parser/MessageParser.h>  namespace Swift { @@ -16,18 +16,18 @@ MessageParser::MessageParser(PayloadParserFactoryCollection* factories) :  }  void MessageParser::handleStanzaAttributes(const AttributeMap& attributes) { -	AttributeMap::const_iterator type = attributes.find("type"); -	if (type != attributes.end()) { -		if (type->second == "chat") { +	boost::optional<std::string> type = attributes.getAttributeValue("type"); +	if (type) { +		if (*type == "chat") {  			getStanzaGeneric()->setType(Message::Chat);  		} -		else if (type->second == "error") { +		else if (*type == "error") {  			getStanzaGeneric()->setType(Message::Error);  		} -		else if (type->second == "groupchat") { +		else if (*type == "groupchat") {  			getStanzaGeneric()->setType(Message::Groupchat);  		} -		else if (type->second == "headline") { +		else if (*type == "headline") {  			getStanzaGeneric()->setType(Message::Headline);  		}  		else { diff --git a/Swiften/Parser/PayloadParser.h b/Swiften/Parser/PayloadParser.h index 423a2bb..3dc04df 100644 --- a/Swiften/Parser/PayloadParser.h +++ b/Swiften/Parser/PayloadParser.h @@ -44,6 +44,6 @@ namespace Swift {  			/**  			 * Retrieve a pointer to the payload.  			 */ -			virtual Payload::ref getPayload() const = 0; +			virtual boost::shared_ptr<Payload> getPayload() const = 0;  	};  } diff --git a/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp b/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp index 35db9ec..3de11ac 100644 --- a/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp +++ b/Swiften/Parser/PayloadParsers/BytestreamsParser.cpp @@ -27,7 +27,7 @@ void BytestreamsParser::handleStartElement(const std::string& element, const std  			try {  				getPayloadInternal()->addStreamHost(Bytestreams::StreamHost(attributes.getAttribute("host"), JID(attributes.getAttribute("jid")), boost::lexical_cast<int>(attributes.getAttribute("port"))));  			} -			catch (boost::bad_lexical_cast& e) { +			catch (boost::bad_lexical_cast&) {  			}  		}  		else if (element == "streamhost-used") { diff --git a/Swiften/Parser/PayloadParsers/DelayParser.cpp b/Swiften/Parser/PayloadParsers/DelayParser.cpp index 3425b84..0ab2d7b 100644 --- a/Swiften/Parser/PayloadParsers/DelayParser.cpp +++ b/Swiften/Parser/PayloadParsers/DelayParser.cpp @@ -9,6 +9,7 @@  #include <locale>  #include <boost/date_time/time_facet.hpp> +#include <boost/date_time/posix_time/posix_time.hpp>  namespace Swift { diff --git a/Swiften/Parser/PayloadParsers/DelayParserFactory.cpp b/Swiften/Parser/PayloadParsers/DelayParserFactory.cpp index 19d0530..48841d2 100644 --- a/Swiften/Parser/PayloadParsers/DelayParserFactory.cpp +++ b/Swiften/Parser/PayloadParsers/DelayParserFactory.cpp @@ -6,6 +6,7 @@  #include <Swiften/Parser/PayloadParsers/DelayParserFactory.h> +#include <boost/date_time/posix_time/posix_time.hpp>  #include <boost/date_time/time_facet.hpp>  namespace Swift { diff --git a/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp index e1fcb20..2e9e87a 100644 --- a/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp +++ b/Swiften/Parser/PayloadParsers/DiscoInfoParser.cpp @@ -15,7 +15,7 @@ DiscoInfoParser::DiscoInfoParser() : level_(TopLevel), formParser_(NULL) {  void DiscoInfoParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) {  	if (level_ == PayloadLevel) {  		if (element == "identity") { -			getPayloadInternal()->addIdentity(DiscoInfo::Identity(attributes.getAttribute("name"), attributes.getAttribute("category"), attributes.getAttribute("type"), attributes.getAttribute("lang"))); +			getPayloadInternal()->addIdentity(DiscoInfo::Identity(attributes.getAttribute("name"), attributes.getAttribute("category"), attributes.getAttribute("type"), attributes.getAttribute("lang", "http://www.w3.org/XML/1998/namespace")));  		}  		else if (element == "feature") {  			getPayloadInternal()->addFeature(attributes.getAttribute("var")); diff --git a/Swiften/Parser/PayloadParsers/FormParser.cpp b/Swiften/Parser/PayloadParsers/FormParser.cpp index f8e02a4..72449b8 100644 --- a/Swiften/Parser/PayloadParsers/FormParser.cpp +++ b/Swiften/Parser/PayloadParsers/FormParser.cpp @@ -63,12 +63,9 @@ void FormParser::handleStartElement(const std::string& element, const std::strin  			else if (type == "text-private") {  				currentFieldParseHelper_ = TextPrivateFormFieldParseHelper::create();  			} -			else if (type == "text-single") { +			else /*if (type == "text-single") || undefined */ {  				currentFieldParseHelper_ = TextSingleFormFieldParseHelper::create();  			} -			else { -				currentFieldParseHelper_ = UntypedFormFieldParseHelper::create(); -			}  			if (currentFieldParseHelper_) {  				currentFieldParseHelper_->getField()->setName(attributes.getAttribute("var"));  				currentFieldParseHelper_->getField()->setLabel(attributes.getAttribute("label")); diff --git a/Swiften/Parser/PayloadParsers/FormParser.h b/Swiften/Parser/PayloadParsers/FormParser.h index 90a3550..e6e6ec0 100644 --- a/Swiften/Parser/PayloadParsers/FormParser.h +++ b/Swiften/Parser/PayloadParsers/FormParser.h @@ -96,7 +96,6 @@ namespace Swift {  			SWIFTEN_DECLARE_FORM_FIELD_PARSE_HELPER(JIDSingle, JID);  			SWIFTEN_DECLARE_FORM_FIELD_PARSE_HELPER(JIDMulti, JIDList);  			SWIFTEN_DECLARE_FORM_FIELD_PARSE_HELPER(ListMulti, StringList); -			SWIFTEN_DECLARE_FORM_FIELD_PARSE_HELPER(Untyped, StringList);  			enum Level {   				TopLevel = 0,  diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp index e20c06d..55deafc 100644 --- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp +++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp @@ -17,6 +17,7 @@  #include "Swiften/Parser/PayloadParsers/StartSessionParser.h"  #include "Swiften/Parser/PayloadParsers/StatusParser.h"  #include "Swiften/Parser/PayloadParsers/StatusShowParser.h" +#include "Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h"  #include "Swiften/Parser/PayloadParsers/RosterParser.h"  #include "Swiften/Parser/PayloadParsers/SoftwareVersionParser.h"  #include "Swiften/Parser/PayloadParsers/StorageParser.h" @@ -39,6 +40,7 @@  #include "Swiften/Parser/PayloadParsers/DelayParserFactory.h"  #include "Swiften/Parser/PayloadParsers/MUCUserPayloadParserFactory.h"  #include "Swiften/Parser/PayloadParsers/NicknameParserFactory.h" +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h"  using namespace boost; @@ -48,12 +50,14 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<IBBParser>("", "http://jabber.org/protocol/ibb")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StatusShowParser>("show")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StatusParser>("status"))); +	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<ReplaceParser>("replace", "http://swift.im/protocol/replace")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<BodyParser>("body")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<SubjectParser>("subject")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<PriorityParser>("priority")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<ErrorParser>("error")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<SoftwareVersionParser>("query", "jabber:iq:version")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<StorageParser>("storage", "storage:bookmarks"))); +	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<RosterItemExchangeParser>("x", "http://jabber.org/protocol/rosterx")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<RosterParser>("query", "jabber:iq:roster")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<DiscoInfoParser>("query", "http://jabber.org/protocol/disco#info")));  	factories_.push_back(shared_ptr<PayloadParserFactory>(new GenericPayloadParserFactory<DiscoItemsParser>("query", "http://jabber.org/protocol/disco#items"))); diff --git a/Swiften/Parser/PayloadParsers/IBBParser.cpp b/Swiften/Parser/PayloadParsers/IBBParser.cpp index f36dc43..8196295 100644 --- a/Swiften/Parser/PayloadParsers/IBBParser.cpp +++ b/Swiften/Parser/PayloadParsers/IBBParser.cpp @@ -27,7 +27,7 @@ void IBBParser::handleStartElement(const std::string& element, const std::string  			try {  				getPayloadInternal()->setSequenceNumber(boost::lexical_cast<int>(attributes.getAttribute("seq")));  			} -			catch (boost::bad_lexical_cast& e) { +			catch (boost::bad_lexical_cast&) {  			}  		}  		else if (element == "open") { @@ -42,7 +42,7 @@ void IBBParser::handleStartElement(const std::string& element, const std::string  			try {  				getPayloadInternal()->setBlockSize(boost::lexical_cast<int>(attributes.getAttribute("block-size")));  			} -			catch (boost::bad_lexical_cast& e) { +			catch (boost::bad_lexical_cast&) {  			}  		}  		else if (element == "close") { @@ -64,7 +64,7 @@ void IBBParser::handleEndElement(const std::string& element, const std::string&)  					data.push_back(c);  				}  			} -			getPayloadInternal()->setData(Base64::decode(std::string(&data[0], data.size()))); +			getPayloadInternal()->setData(Base64::decode(std::string(&data[0], data.size())).getDataVector());  		}  	}  } diff --git a/Swiften/Parser/PayloadParsers/PriorityParser.cpp b/Swiften/Parser/PayloadParsers/PriorityParser.cpp index bcbf67f..553a2b1 100644 --- a/Swiften/Parser/PayloadParsers/PriorityParser.cpp +++ b/Swiften/Parser/PayloadParsers/PriorityParser.cpp @@ -24,7 +24,7 @@ void PriorityParser::handleEndElement(const std::string&, const std::string&) {  		try {  			priority = boost::lexical_cast<int>(text_);  		} -		catch (boost::bad_lexical_cast& e) { +		catch (boost::bad_lexical_cast&) {  		}  		getPayloadInternal()->setPriority(priority);  	} diff --git a/Swiften/Parser/PayloadParsers/ReplaceParser.cpp b/Swiften/Parser/PayloadParsers/ReplaceParser.cpp new file mode 100644 index 0000000..8e5659f --- /dev/null +++ b/Swiften/Parser/PayloadParsers/ReplaceParser.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h" + +namespace Swift { + +	ReplaceParser::ReplaceParser() : level_(0) { +	} + +	void ReplaceParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) { +		if (level_ == 0) { +			std::string id = attributes.getAttribute("id"); +			getPayloadInternal()->setId(id); +		} +		level_++; +	} + +	void ReplaceParser::handleEndElement(const std::string&, const std::string&) { +		--level_; +	} + +	void ReplaceParser::handleCharacterData(const std::string&) { +	} + +} diff --git a/Swiften/Parser/PayloadParsers/ReplaceParser.h b/Swiften/Parser/PayloadParsers/ReplaceParser.h new file mode 100644 index 0000000..0789927 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/ReplaceParser.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Elements/Replace.h" +#include "Swiften/Parser/GenericPayloadParser.h" + +namespace Swift { +	class ReplaceParser : public GenericPayloadParser<Replace> { +		public: +			ReplaceParser(); +			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); +			virtual void handleEndElement(const std::string& element, const std::string&); +			virtual void handleCharacterData(const std::string& data); + +		private: +			int level_; +	}; +} diff --git a/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.cpp b/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.cpp new file mode 100644 index 0000000..ff2a73b --- /dev/null +++ b/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h" +#include "Swiften/Parser/SerializingParser.h" + +namespace Swift { + +RosterItemExchangeParser::RosterItemExchangeParser() : level_(TopLevel), inItem_(false) { +} + +void RosterItemExchangeParser::handleStartElement(const std::string& element, const std::string& /*ns*/, const AttributeMap& attributes) { +	if (level_ == PayloadLevel) { +		if (element == "item") { +			inItem_ = true; + +			currentItem_ = RosterItemExchangePayload::Item(); + +			currentItem_.setJID(JID(attributes.getAttribute("jid"))); +			currentItem_.setName(attributes.getAttribute("name")); + +			std::string action = attributes.getAttribute("action"); +			if (action == "add") { +				currentItem_.setAction(RosterItemExchangePayload::Item::Add); +			} +			else if (action == "modify") { +				currentItem_.setAction(RosterItemExchangePayload::Item::Modify); +			} +			else if (action == "delete") { +				currentItem_.setAction(RosterItemExchangePayload::Item::Delete); +			} +			else { +				// Add is default action according to XEP +				currentItem_.setAction(RosterItemExchangePayload::Item::Add); +			} +		} +	} +	else if (level_ == ItemLevel) { +		if (element == "group") { +			currentText_ = ""; +		} +	} +	++level_; +} + +void RosterItemExchangeParser::handleEndElement(const std::string& element, const std::string& /*ns*/) { +	--level_; +	if (level_ == PayloadLevel) { +		if (inItem_) { +			getPayloadInternal()->addItem(currentItem_); +			inItem_ = false; +		} +	} +	else if (level_ == ItemLevel) { +		if (element == "group") { +			currentItem_.addGroup(currentText_); +		} +	} +} + +void RosterItemExchangeParser::handleCharacterData(const std::string& data) { +	currentText_ += data; +} + +} diff --git a/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h b/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h new file mode 100644 index 0000000..3d6b8f4 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Elements/RosterItemExchangePayload.h" +#include "Swiften/Parser/GenericPayloadParser.h" + +namespace Swift { +	class SerializingParser; + +	class RosterItemExchangeParser : public GenericPayloadParser<RosterItemExchangePayload> { +		public: +			RosterItemExchangeParser(); + +			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); +			virtual void handleEndElement(const std::string& element, const std::string&); +			virtual void handleCharacterData(const std::string& data); + +		private: +			enum Level {  +				TopLevel = 0,  +				PayloadLevel = 1, +				ItemLevel = 2 +			}; +			int level_; +			bool inItem_; +			RosterItemExchangePayload::Item currentItem_; +			std::string currentText_; +	}; +} diff --git a/Swiften/Parser/PayloadParsers/RosterParser.cpp b/Swiften/Parser/PayloadParsers/RosterParser.cpp index ba19fbf..5fba30b 100644 --- a/Swiften/Parser/PayloadParsers/RosterParser.cpp +++ b/Swiften/Parser/PayloadParsers/RosterParser.cpp @@ -5,6 +5,9 @@   */  #include "Swiften/Parser/PayloadParsers/RosterParser.h" + +#include <boost/optional.hpp> +  #include "Swiften/Parser/SerializingParser.h"  namespace Swift { @@ -13,7 +16,13 @@ RosterParser::RosterParser() : level_(TopLevel), inItem_(false), unknownContentP  }  void RosterParser::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) { -	if (level_ == PayloadLevel) { +	if (level_ == TopLevel) { +		boost::optional<std::string> ver = attributes.getAttributeValue("ver"); +		if (ver) { +			getPayloadInternal()->setVersion(*ver); +		} +	} +	else if (level_ == PayloadLevel) {  		if (element == "item") {  			inItem_ = true;  			currentItem_ = RosterItemPayload(); diff --git a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp index 1cf7fcf..0d4a407 100644 --- a/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp +++ b/Swiften/Parser/PayloadParsers/StreamInitiationParser.cpp @@ -42,7 +42,7 @@ void StreamInitiationParser::handleStartElement(const std::string& element, cons  			try {  				currentFile.size = boost::lexical_cast<int>(attributes.getAttribute("size"));  			} -			catch (boost::bad_lexical_cast& e) { +			catch (boost::bad_lexical_cast&) {  			}  		}  		else if (element == "feature" && ns == FEATURE_NEG_NS) { diff --git a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp index 6ec825b..7feada1 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/FormParserTest.cpp @@ -77,7 +77,6 @@ class FormParserTest : public CppUnit::TestFixture {  						"</field>"  						"<field var=\"untyped\">"  							"<value>foo</value>" -							"<value>baz</value>"  						"</field>"  					"</x>")); @@ -114,8 +113,7 @@ class FormParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(JID("baz@fum.org"), boost::dynamic_pointer_cast<JIDMultiFormField>(payload->getFields()[8])->getValue()[1]);  			CPPUNIT_ASSERT_EQUAL(std::string("Tell all your friends about your new bot!"), payload->getFields()[8]->getDescription()); -			CPPUNIT_ASSERT_EQUAL(std::string("foo"), boost::dynamic_pointer_cast<UntypedFormField>(payload->getFields()[9])->getValue()[0]); -			CPPUNIT_ASSERT_EQUAL(std::string("baz"), boost::dynamic_pointer_cast<UntypedFormField>(payload->getFields()[9])->getValue()[1]); +			CPPUNIT_ASSERT_EQUAL(std::string("foo"), boost::dynamic_pointer_cast<TextSingleFormField>(payload->getFields()[9])->getValue());  		}  }; diff --git a/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp index b4229f2..2fc3e79 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/IBBParserTest.cpp @@ -32,7 +32,7 @@ class IBBParserTest : public CppUnit::TestFixture {  			IBB::ref ibb = parser.getPayload<IBB>();  			CPPUNIT_ASSERT(ibb->getAction() == IBB::Data); -			CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefgihjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\x0a"), ibb->getData()); +			CPPUNIT_ASSERT(ByteArray::create("abcdefgihjklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\x0a") == ibb->getData());  			CPPUNIT_ASSERT_EQUAL(4, ibb->getSequenceNumber());  		}  }; diff --git a/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp index 68a2e4f..e2b8be2 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp @@ -24,7 +24,7 @@ class PriorityParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(parser.parse("<priority>-120</priority>")); -			Priority::ref payload = boost::dynamic_pointer_cast<Priority>(parser.getPayload()); +			boost::shared_ptr<Priority> payload = boost::dynamic_pointer_cast<Priority>(parser.getPayload());  			CPPUNIT_ASSERT_EQUAL(-120, payload->getPriority());  		} @@ -33,7 +33,7 @@ class PriorityParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(parser.parse("<priority>invalid</priority>")); -			Priority::ref payload = boost::dynamic_pointer_cast<Priority>(parser.getPayload()); +			boost::shared_ptr<Priority> payload = boost::dynamic_pointer_cast<Priority>(parser.getPayload());  			CPPUNIT_ASSERT_EQUAL(0, payload->getPriority());  		}  }; diff --git a/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp new file mode 100644 index 0000000..7c34eb1 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/UnitTest/ReplaceTest.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Parser/PayloadParsers/ReplaceParser.h" +#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h" + +using namespace Swift; + +class ReplaceParserTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(ReplaceParserTest); +		CPPUNIT_TEST(testParseTrivial); +		CPPUNIT_TEST(testParseChild); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testParseTrivial() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<replace id='bad1' xmlns='http://swift.im/protocol/replace'/>")); +			Replace::ref payload = boost::dynamic_pointer_cast <Replace>(parser.getPayload()); +			CPPUNIT_ASSERT_EQUAL(std::string("bad1"), payload->getId()); +		} +		void testParseChild() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<replace id='bad1' xmlns='http://swift.im/protocol/replace' ><child xmlns='blah' id=\"hi\"/></replace>")); +			Replace::ref payload = boost::dynamic_pointer_cast <Replace>(parser.getPayload()); +			CPPUNIT_ASSERT_EQUAL(std::string("bad1"), payload->getId()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ReplaceParserTest); diff --git a/Swiften/Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp new file mode 100644 index 0000000..9533e15 --- /dev/null +++ b/Swiften/Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Parser/PayloadParsers/RosterItemExchangeParser.h" +#include "Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h" + +using namespace Swift; + +class RosterItemExchangeParserTest : public CppUnit::TestFixture +{ +		CPPUNIT_TEST_SUITE(RosterItemExchangeParserTest); +		CPPUNIT_TEST(testParse); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testParse() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse( +				"<x xmlns=\"http://jabber.org/protocol/rosterx\">" +					"<item action=\"add\" jid=\"foo@bar.com\" name=\"Foo @ Bar\">" +						"<group>Group 1</group>" +						"<group>Group 2</group>" +					"</item>" +					"<item action=\"modify\" jid=\"baz@blo.com\" name=\"Baz\"/>" +				"</x>")); + +			RosterItemExchangePayload* payload = dynamic_cast<RosterItemExchangePayload*>(parser.getPayload().get()); +			const RosterItemExchangePayload::RosterItemExchangePayloadItems& items = payload->getItems(); + +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size()); + +			CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com"), items[0].getJID()); +			CPPUNIT_ASSERT_EQUAL(std::string("Foo @ Bar"), items[0].getName()); +			CPPUNIT_ASSERT_EQUAL(RosterItemExchangePayload::Item::Add, items[0].getAction()); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items[0].getGroups().size()); +			CPPUNIT_ASSERT_EQUAL(std::string("Group 1"), items[0].getGroups()[0]); +			CPPUNIT_ASSERT_EQUAL(std::string("Group 2"), items[0].getGroups()[1]); + +			CPPUNIT_ASSERT_EQUAL(JID("baz@blo.com"), items[1].getJID()); +			CPPUNIT_ASSERT_EQUAL(std::string("Baz"), items[1].getName()); +			CPPUNIT_ASSERT_EQUAL(RosterItemExchangePayload::Item::Modify, items[1].getAction()); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), items[1].getGroups().size()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(RosterItemExchangeParserTest); diff --git a/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp index 1bcea0e..3102b74 100644 --- a/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp +++ b/Swiften/Parser/PayloadParsers/UnitTest/RosterParserTest.cpp @@ -17,6 +17,8 @@ class RosterParserTest : public CppUnit::TestFixture  		CPPUNIT_TEST_SUITE(RosterParserTest);  		CPPUNIT_TEST(testParse);  		CPPUNIT_TEST(testParse_ItemWithUnknownContent); +		CPPUNIT_TEST(testParse_WithVersion); +		CPPUNIT_TEST(testParse_WithEmptyVersion);  		CPPUNIT_TEST_SUITE_END();  	public: @@ -32,6 +34,8 @@ class RosterParserTest : public CppUnit::TestFixture  				"</query>"));  			RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get()); + +			CPPUNIT_ASSERT(!payload->getVersion());  			const RosterPayload::RosterItemPayloads& items = payload->getItems();  			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), items.size()); @@ -74,6 +78,24 @@ class RosterParserTest : public CppUnit::TestFixture  				"<baz xmlns=\"jabber:iq:roster\"><fum xmlns=\"jabber:iq:roster\">foo</fum></baz>"  				), items[0].getUnknownContent());  		} + +		void testParse_WithVersion() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:roster' ver='ver10'/>")); + +			RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get()); +			CPPUNIT_ASSERT(payload->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string("ver10"), *payload->getVersion()); +		} + +		void testParse_WithEmptyVersion() { +			PayloadsParserTester parser; +			CPPUNIT_ASSERT(parser.parse("<query xmlns='jabber:iq:roster' ver=''/>")); + +			RosterPayload* payload = dynamic_cast<RosterPayload*>(parser.getPayload().get()); +			CPPUNIT_ASSERT(payload->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string(""), *payload->getVersion()); +		}  };  CPPUNIT_TEST_SUITE_REGISTRATION(RosterParserTest); diff --git a/Swiften/Parser/PresenceParser.cpp b/Swiften/Parser/PresenceParser.cpp index 845ccf0..867155f 100644 --- a/Swiften/Parser/PresenceParser.cpp +++ b/Swiften/Parser/PresenceParser.cpp @@ -5,6 +5,7 @@   */  #include <iostream> +#include <boost/optional.hpp>  #include "Swiften/Parser/PresenceParser.h" @@ -15,31 +16,31 @@ PresenceParser::PresenceParser(PayloadParserFactoryCollection* factories) :  }  void PresenceParser::handleStanzaAttributes(const AttributeMap& attributes) { -	AttributeMap::const_iterator type = attributes.find("type"); -	if (type != attributes.end()) { -		if (type->second == "unavailable") { +	boost::optional<std::string> type = attributes.getAttributeValue("type"); +	if (type) { +		if (*type == "unavailable") {  			getStanzaGeneric()->setType(Presence::Unavailable);  		} -		else if (type->second == "probe") { +		else if (*type == "probe") {  			getStanzaGeneric()->setType(Presence::Probe);  		} -		else if (type->second == "subscribe") { +		else if (*type == "subscribe") {  			getStanzaGeneric()->setType(Presence::Subscribe);  		} -		else if (type->second == "subscribed") { +		else if (*type == "subscribed") {  			getStanzaGeneric()->setType(Presence::Subscribed);  		} -		else if (type->second == "unsubscribe") { +		else if (*type == "unsubscribe") {  			getStanzaGeneric()->setType(Presence::Unsubscribe);  		} -		else if (type->second == "unsubscribed") { +		else if (*type == "unsubscribed") {  			getStanzaGeneric()->setType(Presence::Unsubscribed);  		} -		else if (type->second == "error") { +		else if (*type == "error") {  			getStanzaGeneric()->setType(Presence::Error);  		}  		else { -			std::cerr << "Unknown Presence type: " << type->second << std::endl; +			std::cerr << "Unknown Presence type: " << *type << std::endl;  			getStanzaGeneric()->setType(Presence::Available);  		}  	} diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript index cbb2190..d9915ed 100644 --- a/Swiften/Parser/SConscript +++ b/Swiften/Parser/SConscript @@ -6,6 +6,7 @@ myenv.MergeFlags(swiften_env.get("LIBXML_FLAGS", ""))  myenv.MergeFlags(swiften_env.get("EXPAT_FLAGS", ""))  sources = [ +		"AttributeMap.cpp",  		"AuthRequestParser.cpp",  		"AuthChallengeParser.cpp",  		"AuthSuccessParser.cpp", @@ -37,6 +38,7 @@ sources = [  		"PayloadParsers/PrivateStorageParser.cpp",  		"PayloadParsers/RawXMLPayloadParser.cpp",  		"PayloadParsers/ResourceBindParser.cpp", +		"PayloadParsers/RosterItemExchangeParser.cpp",  		"PayloadParsers/RosterParser.cpp",  		"PayloadParsers/SecurityLabelParser.cpp",  		"PayloadParsers/SecurityLabelsCatalogParser.cpp", @@ -51,12 +53,15 @@ sources = [  		"PayloadParsers/DelayParser.cpp",  		"PayloadParsers/MUCUserPayloadParser.cpp",  		"PayloadParsers/NicknameParser.cpp", +		"PayloadParsers/ReplaceParser.cpp",  		"PlatformXMLParserFactory.cpp",  		"PresenceParser.cpp",  		"SerializingParser.cpp",  		"StanzaParser.cpp",  		"StreamErrorParser.cpp",  		"StreamFeaturesParser.cpp", +		"StreamManagementEnabledParser.cpp", +		"StreamResumeParser.cpp",  		"XMLParser.cpp",  		"XMLParserClient.cpp",  		"XMLParserFactory.cpp", diff --git a/Swiften/Parser/SerializingParser.cpp b/Swiften/Parser/SerializingParser.cpp index 43dfc51..03b9575 100644 --- a/Swiften/Parser/SerializingParser.cpp +++ b/Swiften/Parser/SerializingParser.cpp @@ -7,7 +7,6 @@  #include "Swiften/Parser/SerializingParser.h"  #include "Swiften/Serializer/XML/XMLTextNode.h"  #include "Swiften/Base/foreach.h" -#include <iostream>  namespace Swift { @@ -16,8 +15,9 @@ SerializingParser::SerializingParser() {  void SerializingParser::handleStartElement(const std::string& tag, const std::string&  ns, const AttributeMap& attributes) {  	boost::shared_ptr<XMLElement> element(new XMLElement(tag, ns)); -	for (AttributeMap::const_iterator i = attributes.begin(); i != attributes.end(); ++i) { -		element->setAttribute((*i).first, (*i).second); +	// FIXME: Ignoring attribute namespace +	foreach (const AttributeMap::Entry& e, attributes.getEntries()) { +		element->setAttribute(e.getAttribute().getName(), e.getValue());  	}  	if (elementStack_.empty()) { diff --git a/Swiften/Parser/StanzaParser.cpp b/Swiften/Parser/StanzaParser.cpp index 64c4901..051f37e 100644 --- a/Swiften/Parser/StanzaParser.cpp +++ b/Swiften/Parser/StanzaParser.cpp @@ -7,6 +7,7 @@  #include "Swiften/Parser/StanzaParser.h"  #include <iostream> +#include <boost/optional.hpp>  #include <cassert>  #include "Swiften/Parser/PayloadParser.h" @@ -39,17 +40,17 @@ void StanzaParser::handleStartElement(const std::string& element, const std::str  		currentPayloadParser_->handleStartElement(element, ns, attributes);  	}  	else { -		AttributeMap::const_iterator from = attributes.find("from"); -		if (from != attributes.end()) { -			getStanza()->setFrom(JID(from->second)); +		boost::optional<std::string> from = attributes.getAttributeValue("from"); +		if (from) { +			getStanza()->setFrom(JID(*from));  		} -		AttributeMap::const_iterator to = attributes.find("to"); -		if (to != attributes.end()) { -			getStanza()->setTo(JID(to->second)); +		boost::optional<std::string> to = attributes.getAttributeValue("to"); +		if (to) { +			getStanza()->setTo(JID(*to));  		} -		AttributeMap::const_iterator id = attributes.find("id"); -		if (id != attributes.end()) { -			getStanza()->setID(id->second); +		boost::optional<std::string> id = attributes.getAttributeValue("id"); +		if (id) { +			getStanza()->setID(*id);  		}  		handleStanzaAttributes(attributes);  	} diff --git a/Swiften/Parser/StreamFeaturesParser.cpp b/Swiften/Parser/StreamFeaturesParser.cpp index 377f215..1b3ad54 100644 --- a/Swiften/Parser/StreamFeaturesParser.cpp +++ b/Swiften/Parser/StreamFeaturesParser.cpp @@ -31,6 +31,9 @@ void StreamFeaturesParser::handleStartElement(const std::string& element, const  		else if (element == "compression" && ns == "http://jabber.org/features/compress") {  			inCompression_ = true;  		} +		else if (element == "ver" && ns == "urn:xmpp:features:rosterver") { +			getElementGeneric()->setHasRosterVersioning(); +		}  	}  	else if (currentDepth_ == 2) {  		if (inCompression_ && element == "method") { diff --git a/Swiften/Parser/StreamManagementEnabledParser.cpp b/Swiften/Parser/StreamManagementEnabledParser.cpp new file mode 100644 index 0000000..906e071 --- /dev/null +++ b/Swiften/Parser/StreamManagementEnabledParser.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/StreamManagementEnabledParser.h> + +using namespace Swift; + +StreamManagementEnabledParser::StreamManagementEnabledParser() : level(TopLevel) { +} + +StreamManagementEnabledParser::~StreamManagementEnabledParser() { +} + +void StreamManagementEnabledParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) { +	if (level == TopLevel) { +		if (attributes.getBoolAttribute("resume", false)) { +			getElementGeneric()->setResumeSupported(); +		} +		getElementGeneric()->setResumeID(attributes.getAttribute("id")); +	} +	++level; +} + +void StreamManagementEnabledParser::handleEndElement(const std::string&, const std::string&) { +	--level; +} diff --git a/Swiften/Parser/StreamManagementEnabledParser.h b/Swiften/Parser/StreamManagementEnabledParser.h index adc45ab..db616af 100644 --- a/Swiften/Parser/StreamManagementEnabledParser.h +++ b/Swiften/Parser/StreamManagementEnabledParser.h @@ -1,17 +1,27 @@  /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2011 Remko Tronçon   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once -#include "Swiften/Parser/GenericElementParser.h" -#include "Swiften/Elements/StreamManagementEnabled.h" +#include <Swiften/Parser/GenericElementParser.h> +#include <Swiften/Elements/StreamManagementEnabled.h>  namespace Swift {  	class StreamManagementEnabledParser : public GenericElementParser<StreamManagementEnabled> {  		public: -			StreamManagementEnabledParser() : GenericElementParser<StreamManagementEnabled>() {} +			StreamManagementEnabledParser(); +			~StreamManagementEnabledParser(); + +			virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap&); +			virtual void handleEndElement(const std::string&, const std::string&); + +		private: +			enum Level {  +				TopLevel = 0 +			}; +			int level;  	};  } diff --git a/Swiften/Parser/StreamResumeParser.cpp b/Swiften/Parser/StreamResumeParser.cpp new file mode 100644 index 0000000..f54dcf0 --- /dev/null +++ b/Swiften/Parser/StreamResumeParser.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Parser/StreamResumeParser.h> + +#include <boost/lexical_cast.hpp> + +using namespace Swift; + +StreamResumeParser::StreamResumeParser() : level(TopLevel) { +} + +StreamResumeParser::~StreamResumeParser() { +} + +void StreamResumeParser::handleStartElement(const std::string&, const std::string&, const AttributeMap& attributes) { +	if (level == TopLevel) { +		boost::optional<std::string> handledStanzasCount = attributes.getAttributeValue("h"); +		if (handledStanzasCount) { +			try { +				getElementGeneric()->setHandledStanzasCount(boost::lexical_cast<int>(*handledStanzasCount)); +			} +			catch (const boost::bad_lexical_cast &) { +			} +		} +		getElementGeneric()->setResumeID(attributes.getAttribute("previd")); +	} +	++level; +} + +void StreamResumeParser::handleEndElement(const std::string&, const std::string&) { +	--level; +} diff --git a/Swiften/Parser/StreamResumeParser.h b/Swiften/Parser/StreamResumeParser.h new file mode 100644 index 0000000..0ccd24c --- /dev/null +++ b/Swiften/Parser/StreamResumeParser.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Parser/GenericElementParser.h> +#include <Swiften/Elements/StreamResume.h> + +namespace Swift { +	class StreamResumeParser : public GenericElementParser<StreamResume> { +		public: +			StreamResumeParser(); +			~StreamResumeParser(); + +			virtual void handleStartElement(const std::string&, const std::string&, const AttributeMap&); +			virtual void handleEndElement(const std::string&, const std::string&); + +		private: +			enum Level {  +				TopLevel = 0 +			}; +			int level; +	}; +} diff --git a/Swiften/Parser/UnitTest/AttributeMapTest.cpp b/Swiften/Parser/UnitTest/AttributeMapTest.cpp index fb68f29..8e2ccff 100644 --- a/Swiften/Parser/UnitTest/AttributeMapTest.cpp +++ b/Swiften/Parser/UnitTest/AttributeMapTest.cpp @@ -14,6 +14,7 @@ using namespace Swift;  class AttributeMapTest : public CppUnit::TestFixture  {  		CPPUNIT_TEST_SUITE(AttributeMapTest); +		CPPUNIT_TEST(testGetAttribute_Namespaced);  		CPPUNIT_TEST(testGetBoolAttribute_True);  		CPPUNIT_TEST(testGetBoolAttribute_1);  		CPPUNIT_TEST(testGetBoolAttribute_False); @@ -24,39 +25,46 @@ class AttributeMapTest : public CppUnit::TestFixture  		CPPUNIT_TEST_SUITE_END();  	public: -		AttributeMapTest() {} +		void testGetAttribute_Namespaced() { +			AttributeMap testling; +			testling.addAttribute("lang", "", "nl"); +			testling.addAttribute("lang", "http://www.w3.org/XML/1998/namespace", "en"); +			testling.addAttribute("lang", "", "fr"); + +			CPPUNIT_ASSERT_EQUAL(std::string("en"), testling.getAttribute("lang", "http://www.w3.org/XML/1998/namespace")); +		}  		void testGetBoolAttribute_True() {  			AttributeMap testling; -			testling["foo"] = "true"; +			testling.addAttribute("foo", "", "true");  			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));  		}  		void testGetBoolAttribute_1() {  			AttributeMap testling; -			testling["foo"] = "1"; +			testling.addAttribute("foo", "", "1");  			CPPUNIT_ASSERT(testling.getBoolAttribute("foo"));  		}  		void testGetBoolAttribute_False() {  			AttributeMap testling; -			testling["foo"] = "false"; +			testling.addAttribute("foo", "", "false");  			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));  		}  		void testGetBoolAttribute_0() {  			AttributeMap testling; -			testling["foo"] = "0"; +			testling.addAttribute("foo", "", "0");  			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));  		}  		void testGetBoolAttribute_Invalid() {  			AttributeMap testling; -			testling["foo"] = "bla"; +			testling.addAttribute("foo", "", "bla");  			CPPUNIT_ASSERT(!testling.getBoolAttribute("foo", true));  		} diff --git a/Swiften/Parser/UnitTest/StanzaParserTest.cpp b/Swiften/Parser/UnitTest/StanzaParserTest.cpp index d57f798..2657750 100644 --- a/Swiften/Parser/UnitTest/StanzaParserTest.cpp +++ b/Swiften/Parser/UnitTest/StanzaParserTest.cpp @@ -40,8 +40,8 @@ class StanzaParserTest : public CppUnit::TestFixture {  			MyStanzaParser testling(factoryCollection_);  			AttributeMap attributes; -			attributes["foo"] = "fum"; -			attributes["bar"] = "baz"; +			attributes.addAttribute("foo", "", "fum"); +			attributes.addAttribute("bar", "", "baz");  			testling.handleStartElement("mystanza", "", attributes);  			testling.handleStartElement("mypayload1", "", attributes);  			testling.handleStartElement("child", "", attributes); @@ -107,9 +107,9 @@ class StanzaParserTest : public CppUnit::TestFixture {  			MyStanzaParser testling(factoryCollection_);  			AttributeMap attributes; -			attributes["to"] = "foo@example.com/blo"; -			attributes["from"] = "bar@example.com/baz"; -			attributes["id"] = "id-123"; +			attributes.addAttribute("to", "", "foo@example.com/blo"); +			attributes.addAttribute("from", "", "bar@example.com/baz"); +			attributes.addAttribute("id", "", "id-123");  			testling.handleStartElement("mystanza", "", attributes);  			testling.handleEndElement("mypayload1", ""); diff --git a/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp index 1cdaf54..9fdea88 100644 --- a/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp +++ b/Swiften/Parser/UnitTest/StreamFeaturesParserTest.cpp @@ -37,6 +37,7 @@ class StreamFeaturesParserTest : public CppUnit::TestFixture {  					"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"  					"<sm xmlns='urn:xmpp:sm:2'/>"  					"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>" +					"<ver xmlns=\"urn:xmpp:features:rosterver\"/>"  				"</stream:features>"));  			StreamFeatures::ref element = boost::dynamic_pointer_cast<StreamFeatures>(testling.getElement()); @@ -49,6 +50,7 @@ class StreamFeaturesParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(element->hasAuthenticationMechanism("DIGEST-MD5"));  			CPPUNIT_ASSERT(element->hasAuthenticationMechanism("PLAIN"));  			CPPUNIT_ASSERT(element->hasStreamManagement()); +			CPPUNIT_ASSERT(element->hasRosterVersioning());  		}  		void testParse_Empty() { diff --git a/Swiften/Parser/UnitTest/StreamManagementEnabledParserTest.cpp b/Swiften/Parser/UnitTest/StreamManagementEnabledParserTest.cpp new file mode 100644 index 0000000..07b7b31 --- /dev/null +++ b/Swiften/Parser/UnitTest/StreamManagementEnabledParserTest.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Parser/StreamManagementEnabledParser.h> +#include <Swiften/Parser/UnitTest/ElementParserTester.h> + +using namespace Swift; + +class StreamManagementEnabledParserTest : public CppUnit::TestFixture { +		CPPUNIT_TEST_SUITE(StreamManagementEnabledParserTest); +		CPPUNIT_TEST(testParse); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		void testParse() { +			StreamManagementEnabledParser testling; +			ElementParserTester parser(&testling); + +			CPPUNIT_ASSERT(parser.parse( +				"<enabled xmlns=\"urn:xmpp:sm:3\" id=\"some-long-sm-id\" resume=\"true\"/>")); + +			boost::shared_ptr<StreamManagementEnabled> element = boost::dynamic_pointer_cast<StreamManagementEnabled>(testling.getElement()); +			CPPUNIT_ASSERT(element->getResumeSupported()); +			CPPUNIT_ASSERT_EQUAL(std::string("some-long-sm-id"), element->getResumeID()); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(StreamManagementEnabledParserTest); diff --git a/Swiften/Parser/UnitTest/XMLParserTest.cpp b/Swiften/Parser/UnitTest/XMLParserTest.cpp index 426b7a0..27534a1 100644 --- a/Swiften/Parser/UnitTest/XMLParserTest.cpp +++ b/Swiften/Parser/UnitTest/XMLParserTest.cpp @@ -31,6 +31,8 @@ class XMLParserTest : public CppUnit::TestFixture {  		CPPUNIT_TEST(testParse_InErrorState);  		CPPUNIT_TEST(testParse_Incremental);  		CPPUNIT_TEST(testParse_WhitespaceInAttribute); +		CPPUNIT_TEST(testParse_AttributeWithoutNamespace); +		CPPUNIT_TEST(testParse_AttributeWithNamespace);  		CPPUNIT_TEST_SUITE_END();  	public: @@ -46,13 +48,13 @@ class XMLParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);  			CPPUNIT_ASSERT_EQUAL(std::string("iq"), client_.events[0].data); -			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.size()); -			CPPUNIT_ASSERT_EQUAL(std::string("get"), client_.events[0].attributes["type"]); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); +			CPPUNIT_ASSERT_EQUAL(std::string("get"), client_.events[0].attributes.getAttribute("type"));  			CPPUNIT_ASSERT_EQUAL(std::string(), client_.events[0].ns);  			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type);  			CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[1].data); -			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].attributes.size()); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[1].attributes.getEntries().size());  			CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[1].ns);  			CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type); @@ -76,7 +78,7 @@ class XMLParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[0].type);  			CPPUNIT_ASSERT_EQUAL(std::string("query"), client_.events[0].data); -			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.size()); +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), client_.events[0].attributes.getEntries().size());  			CPPUNIT_ASSERT_EQUAL(std::string("jabber:iq:version"), client_.events[0].ns);  			CPPUNIT_ASSERT_EQUAL(Client::StartElement, client_.events[1].type); @@ -205,6 +207,28 @@ class XMLParserTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(Client::EndElement, client_.events[2].type);  			CPPUNIT_ASSERT_EQUAL(std::string("presence"), client_.events[2].data);  		} + +		void testParse_AttributeWithoutNamespace() { +			ParserType testling(&client_); + +			CPPUNIT_ASSERT(testling.parse( +				"<query xmlns='http://swift.im' attr='3'/>")); + +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); +			CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); +			CPPUNIT_ASSERT_EQUAL(std::string(""), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace()); +		} + +		void testParse_AttributeWithNamespace() { +			ParserType testling(&client_); + +			CPPUNIT_ASSERT(testling.parse( +				"<query xmlns='http://swift.im' xmlns:f='http://swift.im/f' f:attr='3'/>")); + +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), client_.events[0].attributes.getEntries().size()); +			CPPUNIT_ASSERT_EQUAL(std::string("attr"), client_.events[0].attributes.getEntries()[0].getAttribute().getName()); +			CPPUNIT_ASSERT_EQUAL(std::string("http://swift.im/f"), client_.events[0].attributes.getEntries()[0].getAttribute().getNamespace()); +		}  	private:  		class Client : public XMLParserClient { diff --git a/Swiften/Parser/XMPPParser.cpp b/Swiften/Parser/XMPPParser.cpp index 1fb7682..adcfdf5 100644 --- a/Swiften/Parser/XMPPParser.cpp +++ b/Swiften/Parser/XMPPParser.cpp @@ -29,6 +29,7 @@  #include "Swiften/Parser/EnableStreamManagementParser.h"  #include "Swiften/Parser/StreamManagementEnabledParser.h"  #include "Swiften/Parser/StreamManagementFailedParser.h" +#include "Swiften/Parser/StreamResumeParser.h"  #include "Swiften/Parser/StanzaAckParser.h"  #include "Swiften/Parser/StanzaAckRequestParser.h"  #include "Swiften/Parser/StartTLSParser.h" @@ -182,6 +183,12 @@ ElementParser* XMPPParser::createElementParser(const std::string& element, const  	else if (element == "failed" && ns == "urn:xmpp:sm:2") {  		return new StreamManagementFailedParser();  	} +	else if (element == "resume" && ns == "urn:xmpp:sm:2") { +		return new StreamResumeParser(); +	} +	else if (element == "resumed" && ns == "urn:xmpp:sm:2") { +		return new StreamResumeParser(); +	}  	else if (element == "a" && ns == "urn:xmpp:sm:2") {  		return new StanzaAckParser();  	} diff --git a/Swiften/Presence/PayloadAddingPresenceSender.cpp b/Swiften/Presence/PayloadAddingPresenceSender.cpp index c3d1638..43071b3 100644 --- a/Swiften/Presence/PayloadAddingPresenceSender.cpp +++ b/Swiften/Presence/PayloadAddingPresenceSender.cpp @@ -34,7 +34,7 @@ bool PayloadAddingPresenceSender::isAvailable() const {  	return sender->isAvailable();  } -void PayloadAddingPresenceSender::setPayload(Payload::ref payload) { +void PayloadAddingPresenceSender::setPayload(boost::shared_ptr<Payload> payload) {  	this->payload = payload;  	if (lastSentPresence) {  		sendPresence(lastSentPresence); diff --git a/Swiften/Presence/PayloadAddingPresenceSender.h b/Swiften/Presence/PayloadAddingPresenceSender.h index ae82970..ba891a8 100644 --- a/Swiften/Presence/PayloadAddingPresenceSender.h +++ b/Swiften/Presence/PayloadAddingPresenceSender.h @@ -21,7 +21,7 @@ namespace Swift {  		public:  			PayloadAddingPresenceSender(PresenceSender*); -			void sendPresence(Presence::ref); +			void sendPresence(boost::shared_ptr<Presence>);  			bool isAvailable() const;  			/** @@ -30,11 +30,11 @@ namespace Swift {  			 * with an updated payload. Initial presence is reset when unavailable presence is  			 * sent.  			 */ -			void setPayload(Payload::ref); +			void setPayload(boost::shared_ptr<Payload>);  		private: -			Presence::ref lastSentPresence; +			boost::shared_ptr<Presence> lastSentPresence;  			PresenceSender* sender; -			Payload::ref payload; +			boost::shared_ptr<Payload> payload;  	};  } diff --git a/Swiften/QA/ClientTest/ClientTest.cpp b/Swiften/QA/ClientTest/ClientTest.cpp index 35bb096..09d357c 100644 --- a/Swiften/QA/ClientTest/ClientTest.cpp +++ b/Swiften/QA/ClientTest/ClientTest.cpp @@ -6,6 +6,7 @@  #include <boost/bind.hpp>  #include <boost/thread.hpp> +#include <iostream>  #include "Swiften/Client/Client.h"  #include "Swiften/Network/TimerFactory.h" diff --git a/Swiften/QA/ProxyProviderTest/.gitignore b/Swiften/QA/ProxyProviderTest/.gitignore new file mode 100644 index 0000000..9d4b9b8 --- /dev/null +++ b/Swiften/QA/ProxyProviderTest/.gitignore @@ -0,0 +1 @@ +ProxyProviderTest diff --git a/Swiften/QA/ProxyProviderTest/ProxyProviderTest.cpp b/Swiften/QA/ProxyProviderTest/ProxyProviderTest.cpp new file mode 100644 index 0000000..ddaee01 --- /dev/null +++ b/Swiften/QA/ProxyProviderTest/ProxyProviderTest.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 Thilo Cestonaro + * Licensed under the BSD License. + * See Documentation/Licenses/BSD.txt for more information. + */ + +#include <iostream> + +#include <Swiften/Network/PlatformProxyProvider.h> +#include <Swiften/Base/foreach.h> + +using namespace Swift; + +int main(void) +{ +	int ret = 0; +	HostAddressPort hap; +	 +	std::cout << "constructing PlatfromProxyProvider instance ..." << std::endl; +	PlatformProxyProvider ppp; + +	hap = ppp.getSOCKS5Proxy(); +	std::cout << "SOCKS5 Proxy configured: " << hap.isValid() << std::endl; +	if(hap.isValid()) { +		std::cout << "SOCKS5 Proxy: " << hap.getAddress().toString() << ":" << hap.getPort() << std::endl; +	} + +	hap = ppp.getHTTPConnectProxy(); +	std::cout << "HTTPConnect Proxy configured: " << hap.isValid() << std::endl; +	if(hap.isValid()) { +		std::cout << "HTTPConnect Proxy: " << hap.getAddress().toString() << ":" << hap.getPort() << std::endl; +	} + +	return ret; +} diff --git a/Swiften/QA/ProxyProviderTest/SConscript b/Swiften/QA/ProxyProviderTest/SConscript new file mode 100644 index 0000000..2eb123d --- /dev/null +++ b/Swiften/QA/ProxyProviderTest/SConscript @@ -0,0 +1,11 @@ +import os + +Import("env") + +myenv = env.Clone() +myenv.MergeFlags(myenv["SWIFTEN_FLAGS"]) +myenv.MergeFlags(myenv["SWIFTEN_DEP_FLAGS"]) + +myenv.Program("ProxyProviderTest", [ +		"ProxyProviderTest.cpp", +	]) diff --git a/Swiften/QA/SConscript b/Swiften/QA/SConscript index 25ba814..2f2be6e 100644 --- a/Swiften/QA/SConscript +++ b/Swiften/QA/SConscript @@ -5,7 +5,8 @@ SConscript(dirs = [  #		"ReconnectTest",  		"ClientTest",  #		"DNSSDTest", -		"StorageTest", +#		"StorageTest",  		"TLSTest",  		"ScriptedTests", +		"ProxyProviderTest",  	]) diff --git a/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp b/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp index 925c775..73df0a0 100644 --- a/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp +++ b/Swiften/QA/StorageTest/FileReadBytestreamTest.cpp @@ -7,6 +7,7 @@  #include <cppunit/extensions/HelperMacros.h>  #include <cppunit/extensions/TestFactoryRegistry.h> +#include <Swiften/Base/ByteArray.h>  #include "Swiften/FileTransfer/FileReadBytestream.h"  #include "SwifTools/Application/PlatformApplicationPathProvider.h" @@ -32,16 +33,16 @@ class FileReadBytestreamTest : public CppUnit::TestFixture {  		void testRead() {  			std::auto_ptr<FileReadBytestream> testling(createTestling()); -			ByteArray result = testling->read(10); +			std::vector<unsigned char> result = testling->read(10); -			CPPUNIT_ASSERT_EQUAL(std::string("/*\n * Copy"), result.toString()); +			CPPUNIT_ASSERT(ByteArray::create("/*\n * Copy") == result);  		}  		void testRead_Twice() {  			std::auto_ptr<FileReadBytestream> testling(createTestling());  			testling->read(10); -			ByteArray result = testling->read(10); +			ByteArray result(testling->read(10));  			CPPUNIT_ASSERT_EQUAL(std::string("right (c) "), result.toString());  		} diff --git a/Swiften/Queries/RawRequest.h b/Swiften/Queries/RawRequest.h index 477952f..e5b3a1d 100644 --- a/Swiften/Queries/RawRequest.h +++ b/Swiften/Queries/RawRequest.h @@ -31,7 +31,7 @@ namespace Swift {  			RawRequest(IQ::Type type, const JID& receiver, const std::string& data, IQRouter* router) : Request(type, receiver, boost::make_shared<RawXMLPayload>(data), router) {  			} -			virtual void handleResponse(Payload::ref payload, ErrorPayload::ref error) { +			virtual void handleResponse(boost::shared_ptr<Payload> payload, ErrorPayload::ref error) {  				if (error) {  					onResponse(ErrorSerializer().serializePayload(error));  				} diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h index eee89e9..88eda63 100644 --- a/Swiften/Queries/Request.h +++ b/Swiften/Queries/Request.h @@ -4,8 +4,7 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_Request_H -#define SWIFTEN_Request_H +#pragma once  #include <boost/shared_ptr.hpp>  #include <boost/optional.hpp> @@ -48,15 +47,15 @@ namespace Swift {  					const JID& receiver,   					IQRouter* router); -			virtual void setPayload(Payload::ref payload) { +			virtual void setPayload(boost::shared_ptr<Payload> payload) {  				payload_ = payload;  			} -			Payload::ref getPayload() const { +			boost::shared_ptr<Payload> getPayload() const {  				return payload_;  			} -			virtual void handleResponse(Payload::ref, ErrorPayload::ref) = 0; +			virtual void handleResponse(boost::shared_ptr<Payload>, boost::shared_ptr<ErrorPayload>) = 0;  		private:  			bool handleIQ(boost::shared_ptr<IQ>); @@ -70,5 +69,3 @@ namespace Swift {  			bool sent_;  	};  } - -#endif diff --git a/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.h b/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.h index 0700c65..5dd19b5 100644 --- a/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.h +++ b/Swiften/Queries/Requests/SubmitInBandRegistrationFormRequest.h @@ -26,11 +26,11 @@ namespace Swift {  			SetInBandRegistrationRequest(const JID& to, InBandRegistrationPayload::ref payload, IQRouter* router) : Request(IQ::Set, to, InBandRegistrationPayload::ref(payload), router) {  			} -			virtual void handleResponse(Payload::ref payload, ErrorPayload::ref error) { +			virtual void handleResponse(boost::shared_ptr<Payload> payload, ErrorPayload::ref error) {  				onResponse(payload, error);  			}  		public: -			boost::signal<void (Payload::ref, ErrorPayload::ref)> onResponse; +			boost::signal<void (boost::shared_ptr<Payload>, ErrorPayload::ref)> onResponse;  	};  } diff --git a/Swiften/Roster/GetRosterRequest.h b/Swiften/Roster/GetRosterRequest.h index 00cf77f..a3486f0 100644 --- a/Swiften/Roster/GetRosterRequest.h +++ b/Swiften/Roster/GetRosterRequest.h @@ -19,6 +19,12 @@ namespace Swift {  				return ref(new GetRosterRequest(router));  			} +			static ref create(IQRouter* router, const std::string& version) { +				ref result(new GetRosterRequest(router)); +				result->getPayloadGeneric()->setVersion(version); +				return result; +			} +  		private:  			GetRosterRequest(IQRouter* router) :  					GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router) { diff --git a/Swiften/Roster/RosterMemoryStorage.cpp b/Swiften/Roster/RosterMemoryStorage.cpp new file mode 100644 index 0000000..cbf4563 --- /dev/null +++ b/Swiften/Roster/RosterMemoryStorage.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Roster/RosterMemoryStorage.h> + +#include <boost/smart_ptr/make_shared.hpp> + +namespace Swift { + +RosterMemoryStorage::RosterMemoryStorage() { +} + +void RosterMemoryStorage::setRoster(boost::shared_ptr<RosterPayload> r) { +	roster.reset(); +	if (r) { +		roster = boost::make_shared<RosterPayload>(*r); +	} +} + +} diff --git a/Swiften/Roster/RosterMemoryStorage.h b/Swiften/Roster/RosterMemoryStorage.h new file mode 100644 index 0000000..b659d77 --- /dev/null +++ b/Swiften/Roster/RosterMemoryStorage.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Roster/RosterStorage.h> + +namespace Swift { +	class RosterMemoryStorage : public RosterStorage { +		public: +			RosterMemoryStorage(); + +			virtual boost::shared_ptr<RosterPayload> getRoster() const { +				return roster; +			} + +			virtual void setRoster(boost::shared_ptr<RosterPayload>); + +		private: +			boost::shared_ptr<RosterPayload> roster; +	}; +} diff --git a/Swiften/Roster/RosterStorage.cpp b/Swiften/Roster/RosterStorage.cpp new file mode 100644 index 0000000..6bf58de --- /dev/null +++ b/Swiften/Roster/RosterStorage.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Roster/RosterStorage.h> + +namespace Swift { + +RosterStorage::~RosterStorage() { +} + +} diff --git a/Swiften/Roster/RosterStorage.h b/Swiften/Roster/RosterStorage.h new file mode 100644 index 0000000..ba24cb3 --- /dev/null +++ b/Swiften/Roster/RosterStorage.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 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/Elements/RosterPayload.h> + +namespace Swift { +	class RosterStorage { +		public: +			virtual ~RosterStorage(); + +			virtual boost::shared_ptr<RosterPayload> getRoster() const = 0; +			virtual void setRoster(boost::shared_ptr<RosterPayload>) = 0; +	}; +} diff --git a/Swiften/Roster/SetRosterRequest.h b/Swiften/Roster/SetRosterRequest.h index e5ae974..606b431 100644 --- a/Swiften/Roster/SetRosterRequest.h +++ b/Swiften/Roster/SetRosterRequest.h @@ -19,11 +19,15 @@ namespace Swift {  			typedef boost::shared_ptr<SetRosterRequest> ref;  			static ref create(RosterPayload::ref payload, IQRouter* router) { -				return ref(new SetRosterRequest(payload, router)); +				return ref(new SetRosterRequest(JID(), payload, router)); +			} + +			static ref create(RosterPayload::ref payload, const JID& to, IQRouter* router) { +				return ref(new SetRosterRequest(to, payload, router));  			}  		private: -			SetRosterRequest(boost::shared_ptr<RosterPayload> payload, IQRouter* router) : Request(IQ::Set, JID(), boost::shared_ptr<RosterPayload>(payload), router) { +			SetRosterRequest(const JID& to, boost::shared_ptr<RosterPayload> payload, IQRouter* router) : Request(IQ::Set, to, boost::shared_ptr<RosterPayload>(payload), router) {  			}  			virtual void handleResponse(boost::shared_ptr<Payload> /*payload*/, ErrorPayload::ref error) { diff --git a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp index 4ef1cc1..4c98673 100644 --- a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp +++ b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp @@ -16,15 +16,24 @@  #include "Swiften/Client/DummyStanzaChannel.h"  #include "Swiften/Queries/IQRouter.h"  #include "Swiften/Roster/XMPPRosterImpl.h" +#include <Swiften/Roster/RosterMemoryStorage.h>  using namespace Swift;  class XMPPRosterControllerTest : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(XMPPRosterControllerTest); +		CPPUNIT_TEST(testGet_Response);  		CPPUNIT_TEST(testGet_EmptyResponse); +		CPPUNIT_TEST(testGet_NoRosterInStorage); +		CPPUNIT_TEST(testGet_NoVersionInStorage); +		CPPUNIT_TEST(testGet_VersionInStorage); +		CPPUNIT_TEST(testGet_ServerDoesNotSupportVersion); +		CPPUNIT_TEST(testGet_ResponseWithoutNewVersion); +		CPPUNIT_TEST(testGet_ResponseWithNewVersion);  		CPPUNIT_TEST(testAdd);  		CPPUNIT_TEST(testModify);  		CPPUNIT_TEST(testRemove); +		CPPUNIT_TEST(testRemove_RosterStorageUpdated);  		CPPUNIT_TEST(testMany);  		CPPUNIT_TEST_SUITE_END(); @@ -34,20 +43,36 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  			router_ = new IQRouter(channel_);  			xmppRoster_ = new XMPPRosterImpl();  			handler_ = new XMPPRosterSignalHandler(xmppRoster_); +			rosterStorage_ = new RosterMemoryStorage();  			jid1_ = JID("foo@bar.com");  			jid2_ = JID("alice@wonderland.lit");  			jid3_ = JID("jane@austen.lit");  		}  		void tearDown() { +			delete rosterStorage_;  			delete handler_;  			delete xmppRoster_;  			delete router_;  			delete channel_;  		} +		void testGet_Response() { +			std::auto_ptr<XMPPRosterController> testling(createController()); + +			testling->requestRoster(); +			boost::shared_ptr<RosterPayload> payload = boost::make_shared<RosterPayload>(); +			payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); +			payload->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); +			channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), payload)); + +			CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_)); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); +		} +  		void testGet_EmptyResponse() { -			XMPPRosterController controller(router_, xmppRoster_); +			XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);  			controller.requestRoster(); @@ -55,7 +80,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  		}  		void testAdd() { -			XMPPRosterController controller(router_, xmppRoster_); +			XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);  			boost::shared_ptr<RosterPayload> payload(new RosterPayload());  			payload->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); @@ -68,8 +93,115 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(jid1_));  		} +		void testGet_NoRosterInStorage() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); + +			testling->requestRoster(); + +			boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>(); +			CPPUNIT_ASSERT(roster->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion()); +		} + +		void testGet_NoVersionInStorage() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); +			rosterStorage_->setRoster(boost::make_shared<RosterPayload>()); + +			testling->requestRoster(); + +			boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>(); +			CPPUNIT_ASSERT(roster->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string(""), *roster->getVersion()); +		} + +		void testGet_VersionInStorage() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); +			boost::shared_ptr<RosterPayload> payload(new RosterPayload()); +			payload->setVersion("foover"); +			rosterStorage_->setRoster(payload); + +			testling->requestRoster(); + +			boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>(); +			CPPUNIT_ASSERT(roster->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string("foover"), *roster->getVersion()); +		} + +		void testGet_ServerDoesNotSupportVersion() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			boost::shared_ptr<RosterPayload> payload(new RosterPayload()); +			payload->setVersion("foover"); +			rosterStorage_->setRoster(payload); + +			testling->requestRoster(); + +			boost::shared_ptr<RosterPayload> roster = channel_->sentStanzas[0]->getPayload<RosterPayload>(); +			CPPUNIT_ASSERT(!roster->getVersion()); +		} + +		void testGet_ResponseWithoutNewVersion() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); +			boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload()); +			storedRoster->setVersion("version10"); +			storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); +			storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); +			rosterStorage_->setRoster(storedRoster); +			testling->requestRoster(); + +			channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr<RosterPayload>())); + +			CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid1_)); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); +			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); +			CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string("version10"), *rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid1_)); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); +		} + +		void testGet_ResponseWithNewVersion() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); +			boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload()); +			storedRoster->setVersion("version10"); +			storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); +			rosterStorage_->setRoster(storedRoster); +			testling->requestRoster(); + +			boost::shared_ptr<RosterPayload> serverRoster(new RosterPayload()); +			serverRoster->setVersion("version12"); +			serverRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); +			std::vector<std::string> groups; +			groups.push_back("foo"); +			groups.push_back("bar"); +			serverRoster->addItem(RosterItemPayload(jid3_, "Rabbit", RosterItemPayload::Both, groups)); +			channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), serverRoster)); + + +			CPPUNIT_ASSERT_EQUAL(2, handler_->getEventCount()); +			CPPUNIT_ASSERT(!xmppRoster_->getItem(jid1_)); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid2_)); +			CPPUNIT_ASSERT(xmppRoster_->getItem(jid3_)); +			CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID()); +			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string("version12"), *rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_)); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid3_)); +			CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(rosterStorage_->getRoster()->getItem(jid3_)->getGroups().size())); +		} +  		void testModify() { -			XMPPRosterController controller(router_, xmppRoster_); +			XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);  			boost::shared_ptr<RosterPayload> payload1(new RosterPayload());  			payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));  			channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); @@ -87,9 +219,9 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(std::string("Bob2"), xmppRoster_->getNameForJID(jid1_));  		} - +		  		void testRemove() { -			XMPPRosterController controller(router_, xmppRoster_); +			XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);  			boost::shared_ptr<RosterPayload> payload1(new RosterPayload());  			payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));  			channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); @@ -107,8 +239,31 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  		} +		void testRemove_RosterStorageUpdated() { +			std::auto_ptr<XMPPRosterController> testling(createController()); +			testling->setUseVersioning(true); +			boost::shared_ptr<RosterPayload> storedRoster(new RosterPayload()); +			storedRoster->setVersion("version10"); +			storedRoster->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both)); +			storedRoster->addItem(RosterItemPayload(jid2_, "Alice", RosterItemPayload::Both)); +			rosterStorage_->setRoster(storedRoster); +			testling->requestRoster(); +			channel_->onIQReceived(IQ::createResult("foo@bar.com", channel_->sentStanzas[0]->getID(), boost::shared_ptr<RosterPayload>())); + +			boost::shared_ptr<RosterPayload> payload2(new RosterPayload()); +			payload2->setVersion("version15"); +			payload2->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Remove)); +			channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id2", payload2)); + +			CPPUNIT_ASSERT(rosterStorage_->getRoster()); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT_EQUAL(std::string("version15"), *rosterStorage_->getRoster()->getVersion()); +			CPPUNIT_ASSERT(!rosterStorage_->getRoster()->getItem(jid1_)); +			CPPUNIT_ASSERT(rosterStorage_->getRoster()->getItem(jid2_)); +		} +  		void testMany() { -			XMPPRosterController controller(router_, xmppRoster_); +			XMPPRosterController controller(router_, xmppRoster_, rosterStorage_);  			boost::shared_ptr<RosterPayload> payload1(new RosterPayload());  			payload1->addItem(RosterItemPayload(jid1_, "Bob", RosterItemPayload::Both));  			channel_->onIQReceived(IQ::createRequest(IQ::Set, JID(), "id1", payload1)); @@ -171,12 +326,18 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture {  			handler_->reset();  		} +	 +	private: +			XMPPRosterController* createController() { +				return new XMPPRosterController(router_, xmppRoster_, rosterStorage_); +			}  	private:  		DummyStanzaChannel* channel_;  		IQRouter* router_;  		XMPPRosterImpl* xmppRoster_;  		XMPPRosterSignalHandler* handler_; +		RosterMemoryStorage* rosterStorage_;  		JID jid1_;  		JID jid2_;  		JID jid3_; diff --git a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp new file mode 100644 index 0000000..d89644a --- /dev/null +++ b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010-2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h> + +#include <boost/bind.hpp> +#include <cassert> + +using namespace Swift; + +XMPPRosterSignalHandler::XMPPRosterSignalHandler(Swift::XMPPRoster* roster) : eventCount(0) { +	lastEvent_ = None; +	roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1)); +	roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1)); +	roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3)); +} + +void XMPPRosterSignalHandler::handleJIDUpdated(const Swift::JID& jid, const std::string& oldName, const std::vector<std::string>& oldGroups) { +	assert(lastEvent_ == None); +	lastJID_ = jid; +	lastOldName_ = oldName; +	lastOldGroups_ = oldGroups; +	lastEvent_ = Update; +	eventCount++; +} diff --git a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h index 1bbd8e9..2cf1159 100644 --- a/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h +++ b/Swiften/Roster/UnitTest/XMPPRosterSignalHandler.h @@ -3,34 +3,25 @@   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ +  #pragma once -#include <boost/shared_ptr.hpp> -#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp>  #include <vector> -  #include "Swiften/Roster/XMPPRosterImpl.h" -using namespace Swift; - -  enum XMPPRosterEvents {None, Add, Remove, Update};  class XMPPRosterSignalHandler {  public: -	XMPPRosterSignalHandler(XMPPRoster* roster) { -		lastEvent_ = None; -		roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1)); -		roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1)); -		roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3)); -	} +	XMPPRosterSignalHandler(Swift::XMPPRoster* roster);  	XMPPRosterEvents getLastEvent() {  		return lastEvent_;  	} -	JID getLastJID() { +	Swift::JID getLastJID() {  		return lastJID_;  	} @@ -46,28 +37,28 @@ public:  		lastEvent_ = None;  	} +	int getEventCount() const { +		return eventCount; +	} +  private: -	void handleJIDAdded(const JID& jid) { +	void handleJIDAdded(const Swift::JID& jid) {  		lastJID_ = jid;  		lastEvent_ = Add; +		eventCount++;  	} -	void handleJIDRemoved(const JID& jid) { +	void handleJIDRemoved(const Swift::JID& jid) {  		lastJID_ = jid;  		lastEvent_ = Remove; +		eventCount++;  	} -	void handleJIDUpdated(const JID& jid, const std::string& oldName, const std::vector<std::string>& oldGroups) { -		CPPUNIT_ASSERT_EQUAL(None, lastEvent_); -		lastJID_ = jid; -		lastOldName_ = oldName; -		lastOldGroups_ = oldGroups; -		lastEvent_ = Update; -	} +	void handleJIDUpdated(const Swift::JID& jid, const std::string& oldName, const std::vector<std::string>& oldGroups);  	XMPPRosterEvents lastEvent_; -	JID lastJID_; +	Swift::JID lastJID_;  	std::string lastOldName_;  	std::vector<std::string> lastOldGroups_; - +	int eventCount;  }; diff --git a/Swiften/Roster/XMPPRosterController.cpp b/Swiften/Roster/XMPPRosterController.cpp index a294d35..bd7e079 100644 --- a/Swiften/Roster/XMPPRosterController.cpp +++ b/Swiften/Roster/XMPPRosterController.cpp @@ -7,20 +7,22 @@  #include "Swiften/Roster/XMPPRosterController.h"  #include <boost/bind.hpp> +#include <iostream>  #include "Swiften/Base/foreach.h"  #include "Swiften/Elements/RosterItemPayload.h"  #include "Swiften/Queries/IQRouter.h"  #include "Swiften/Roster/GetRosterRequest.h"  #include "Swiften/Roster/XMPPRosterImpl.h" +#include <Swiften/Roster/RosterStorage.h>  namespace Swift {  /**   * The controller does not gain ownership of these parameters.   */ -XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) { -	rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false)); +XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* rosterStorage) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster), rosterStorage_(rosterStorage), useVersioning(false) { +	rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, false, boost::shared_ptr<RosterPayload>()));  	rosterPushResponder_.start();  } @@ -30,12 +32,24 @@ XMPPRosterController::~XMPPRosterController() {  void XMPPRosterController::requestRoster() {  	xmppRoster_->clear(); -	GetRosterRequest::ref rosterRequest = GetRosterRequest::create(iqRouter_); -	rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true)); + +	boost::shared_ptr<RosterPayload> storedRoster = rosterStorage_->getRoster(); +	GetRosterRequest::ref rosterRequest; +	if (useVersioning) { +		std::string version = ""; +		if (storedRoster && storedRoster->getVersion()) { +			version = *storedRoster->getVersion(); +		} +		rosterRequest = GetRosterRequest::create(iqRouter_, version); +	} +	else { +		rosterRequest = GetRosterRequest::create(iqRouter_); +	} +	rosterRequest->onResponse.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1, true, storedRoster));  	rosterRequest->send();  } -void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial) { +void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial, boost::shared_ptr<RosterPayload> previousRoster) {  	if (rosterPayload) {  		foreach(const RosterItemPayload& item, rosterPayload->getItems()) {  			//Don't worry about the updated case, the XMPPRoster sorts that out. @@ -46,9 +60,33 @@ void XMPPRosterController::handleRosterReceived(boost::shared_ptr<RosterPayload>  			}  		}  	} +	else if (previousRoster) { +		// The cached version hasn't changed; emit all items +		foreach(const RosterItemPayload& item, previousRoster->getItems()) { +			if (item.getSubscription() != RosterItemPayload::Remove) { +				xmppRoster_->addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription()); +			} +			else { +				std::cerr << "ERROR: Stored invalid roster item" << std::endl; +			} +		} +	}  	if (initial) {  		xmppRoster_->onInitialRosterPopulated();  	} +	if (rosterPayload && rosterPayload->getVersion() && useVersioning) { +		saveRoster(*rosterPayload->getVersion()); +	} +} + +void XMPPRosterController::saveRoster(const std::string& version) { +	std::vector<XMPPRosterItem> items = xmppRoster_->getItems(); +	boost::shared_ptr<RosterPayload> roster(new RosterPayload()); +	roster->setVersion(version); +	foreach(const XMPPRosterItem& item, items) { +		roster->addItem(RosterItemPayload(item.getJID(), item.getName(), item.getSubscription(), item.getGroups())); +	} +	rosterStorage_->setRoster(roster);  }  } diff --git a/Swiften/Roster/XMPPRosterController.h b/Swiften/Roster/XMPPRosterController.h index eeb84f6..9313bb6 100644 --- a/Swiften/Roster/XMPPRosterController.h +++ b/Swiften/Roster/XMPPRosterController.h @@ -18,21 +18,29 @@  namespace Swift {  	class IQRouter;  	class XMPPRosterImpl; +	class RosterStorage;  	class XMPPRosterController {  		public: -			XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster); +			XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster, RosterStorage* storage);  			~XMPPRosterController();  			void requestRoster(); +			void setUseVersioning(bool b) { +				useVersioning = b; +			} +  		private: -			void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial); +			void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload, bool initial, boost::shared_ptr<RosterPayload> previousRoster); +			void saveRoster(const std::string& version);  		private:  			IQRouter* iqRouter_;  			RosterPushResponder rosterPushResponder_;  			XMPPRosterImpl* xmppRoster_; +			RosterStorage* rosterStorage_; +			bool useVersioning;  	};  } diff --git a/Swiften/SASL/SConscript b/Swiften/SASL/SConscript index 5a0cdef..085e49d 100644 --- a/Swiften/SASL/SConscript +++ b/Swiften/SASL/SConscript @@ -12,6 +12,7 @@ objects = myenv.SwiftenObject([  		"DIGESTMD5ClientAuthenticator.cpp",  	])  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) +  env.Append(UNITTEST_SOURCES = [  			File("UnitTest/PLAINMessageTest.cpp"),  			File("UnitTest/PLAINClientAuthenticatorTest.cpp"), diff --git a/Swiften/SConscript b/Swiften/SConscript index 641aadc..c8508d3 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -6,7 +6,7 @@ Import("env")  # Flags  ################################################################################ -swiften_dep_modules = ["BOOST", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI"] +swiften_dep_modules = ["BOOST", "GCONF", "LIBIDN", "ZLIB", "OPENSSL", "LIBXML", "EXPAT", "AVAHI"]  if env["SCONS_STAGE"] == "flags" :  	env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") @@ -77,25 +77,34 @@ if env["SCONS_STAGE"] == "build" :  			"Client/ClientSessionStanzaChannel.cpp",  			"Client/CoreClient.cpp",  			"Client/Client.cpp", +			"Client/ClientXMLTracer.cpp",  			"Client/ClientSession.cpp",  			"Client/MemoryStorages.cpp", -			"Client/FileStorages.cpp",  			"Client/NickResolver.cpp",  			"Client/NickManager.cpp",  			"Client/NickManagerImpl.cpp", +			"Client/Storages.cpp",  			"Compress/ZLibCodecompressor.cpp",  			"Compress/ZLibDecompressor.cpp",  			"Compress/ZLibCompressor.cpp",  			"Elements/DiscoInfo.cpp", +			"Elements/Presence.cpp",  			"Elements/Form.cpp", +			"Elements/StreamFeatures.cpp",  			"Elements/Element.cpp",  			"Elements/IQ.cpp",  			"Elements/Payload.cpp", +			"Elements/RosterItemExchangePayload.cpp",  			"Elements/RosterPayload.cpp",  			"Elements/Stanza.cpp", +			"Elements/StatusShow.cpp", +			"Elements/StreamManagementEnabled.cpp", +			"Elements/StreamResume.cpp", +			"Elements/StreamResumed.cpp",  			"Elements/VCard.cpp",  			"Elements/MUCOccupant.cpp",  			"Entity/Entity.cpp", +			"Entity/PayloadPersister.cpp",  			"MUC/MUC.cpp",  			"MUC/MUCManager.cpp",  			"MUC/MUCRegistry.cpp", @@ -107,6 +116,8 @@ if env["SCONS_STAGE"] == "build" :  			"Queries/Requests/GetInBandRegistrationFormRequest.cpp",  			"Queries/Requests/SubmitInBandRegistrationFormRequest.cpp",  			"Queries/Responders/SoftwareVersionResponder.cpp", +			"Roster/RosterStorage.cpp", +			"Roster/RosterMemoryStorage.cpp",  			"Roster/XMPPRoster.cpp",  			"Roster/XMPPRosterImpl.cpp",  			"Roster/XMPPRosterController.cpp", @@ -117,6 +128,9 @@ if env["SCONS_STAGE"] == "build" :  			"Serializer/CompressRequestSerializer.cpp",  			"Serializer/ElementSerializer.cpp",  			"Serializer/MessageSerializer.cpp", +			"Serializer/StreamManagementEnabledSerializer.cpp", +			"Serializer/StreamResumeSerializer.cpp", +			"Serializer/StreamResumedSerializer.cpp",  			"Serializer/ComponentHandshakeSerializer.cpp",  			"Serializer/PayloadSerializer.cpp",  			"Serializer/PayloadSerializerCollection.cpp", @@ -131,6 +145,7 @@ if env["SCONS_STAGE"] == "build" :  			"Serializer/PayloadSerializers/MUCUserPayloadSerializer.cpp",  			"Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.cpp",  			"Serializer/PayloadSerializers/ResourceBindSerializer.cpp", +			"Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp",  			"Serializer/PayloadSerializers/RosterSerializer.cpp",  			"Serializer/PayloadSerializers/SecurityLabelSerializer.cpp",  			"Serializer/PayloadSerializers/SecurityLabelsCatalogSerializer.cpp", @@ -160,6 +175,7 @@ if env["SCONS_STAGE"] == "build" :  			"Server/SimpleUserRegistry.cpp",  			"Server/UserRegistry.cpp",  			"Session/Session.cpp", +			"Session/SessionTracer.cpp",  			"Session/SessionStream.cpp",  			"Session/BasicSessionStream.cpp",  			"StringCodecs/Base64.cpp", @@ -191,6 +207,7 @@ if env["SCONS_STAGE"] == "build" :  			"StreamManagement",  			"Component",  			"Config", +			"AdHoc"  		])  	SConscript(test_only = True, dirs = [  			"QA", @@ -200,6 +217,8 @@ if env["SCONS_STAGE"] == "build" :  		])  	myenv = swiften_env.Clone() +	if myenv["PLATFORM"] != "darwin" and myenv["PLATFORM"] != "win32" and myenv.get("HAVE_GCONF", 0) : +		env.MergeFlags(env["GCONF_FLAGS"])  	if ARGUMENTS.get("swiften_dll", False) and myenv["PLATFORM"] == "posix" :  		myenv.Append(LINKFLAGS = ["-Wl,-soname,$SWIFTEN_LIBRARY_NAME"])  		myenv["SHLIBSUFFIX"] = "" @@ -237,8 +256,6 @@ if env["SCONS_STAGE"] == "build" :  			File("Elements/UnitTest/FormTest.cpp"),  			File("EventLoop/UnitTest/EventLoopTest.cpp"),  			File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"), -			File("FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp"), -			File("FileTransfer/UnitTest/IBBSendSessionTest.cpp"),  #			File("History/UnitTest/SQLiteHistoryManagerTest.cpp"),  			File("JID/UnitTest/JIDTest.cpp"),  			File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"), @@ -248,6 +265,8 @@ if env["SCONS_STAGE"] == "build" :  			File("MUC/UnitTest/MUCTest.cpp"),  			File("Network/UnitTest/HostAddressTest.cpp"),  			File("Network/UnitTest/ConnectorTest.cpp"), +			File("Network/UnitTest/ChainedConnectorTest.cpp"), +			File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"), @@ -256,6 +275,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"), +			File("Parser/PayloadParsers/UnitTest/RosterItemExchangeParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/RosterParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/IBBParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/SearchPayloadParserTest.cpp"), @@ -269,6 +289,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Parser/PayloadParsers/UnitTest/StorageParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/PrivateStorageParserTest.cpp"),  			File("Parser/PayloadParsers/UnitTest/VCardUpdateParserTest.cpp"), +			File("Parser/PayloadParsers/UnitTest/ReplaceTest.cpp"),  			File("Parser/UnitTest/AttributeMapTest.cpp"),  			File("Parser/UnitTest/IQParserTest.cpp"),  			File("Parser/UnitTest/MessageParserTest.cpp"), @@ -278,6 +299,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Parser/UnitTest/SerializingParserTest.cpp"),  			File("Parser/UnitTest/StanzaParserTest.cpp"),  			File("Parser/UnitTest/StreamFeaturesParserTest.cpp"), +			File("Parser/UnitTest/StreamManagementEnabledParserTest.cpp"),  			File("Parser/UnitTest/XMLParserTest.cpp"),  			File("Parser/UnitTest/XMPPParserTest.cpp"),  			File("Presence/UnitTest/PresenceOracleTest.cpp"), @@ -289,6 +311,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Queries/UnitTest/ResponderTest.cpp"),  			File("Roster/UnitTest/XMPPRosterImplTest.cpp"),  			File("Roster/UnitTest/XMPPRosterControllerTest.cpp"), +			File("Roster/UnitTest/XMPPRosterSignalHandler.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp"), @@ -296,6 +319,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Serializer/PayloadSerializers/UnitTest/ErrorSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/PrioritySerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/ResourceBindSerializerTest.cpp"), +			File("Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/SearchPayloadSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/SecurityLabelSerializerTest.cpp"), @@ -309,6 +333,7 @@ if env["SCONS_STAGE"] == "build" :  			File("Serializer/PayloadSerializers/UnitTest/VCardSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/StorageSerializerTest.cpp"),  			File("Serializer/PayloadSerializers/UnitTest/PrivateStorageSerializerTest.cpp"), +			File("Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp"),  			File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),  			File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"), @@ -347,7 +372,9 @@ if env["SCONS_STAGE"] == "build" :  				swiften_includes.append(include)  				if root.endswith("OpenSSL") :  					continue -				if file.startswith("CAres") or file.startswith("LibXML") or file.startswith("Expat") : +				if file.startswith("CAres") or file.startswith("LibXML") or file.startswith("Expat") or file.startswith("foreach") or file.startswith("Log.h") or file.startswith("format.h") : +					continue +				if file.find("ProxyProvider") != -1 :  					continue  				swiften_header += "#include <" + include + ">\n"  				swiften_includes.append(include) diff --git a/Swiften/Serializer/IQSerializer.h b/Swiften/Serializer/IQSerializer.h index 21ec300..784ce09 100644 --- a/Swiften/Serializer/IQSerializer.h +++ b/Swiften/Serializer/IQSerializer.h @@ -7,8 +7,6 @@  #ifndef SWIFTEN_IQSerializer_H  #define SWIFTEN_IQSerializer_H -#include <cassert> -  #include "Swiften/Serializer/GenericStanzaSerializer.h"  #include "Swiften/Elements/IQ.h"  #include "Swiften/Serializer/XML/XMLElement.h" diff --git a/Swiften/Serializer/PayloadSerializer.h b/Swiften/Serializer/PayloadSerializer.h index 34e6679..c4ad23b 100644 --- a/Swiften/Serializer/PayloadSerializer.h +++ b/Swiften/Serializer/PayloadSerializer.h @@ -4,15 +4,14 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#ifndef SWIFTEN_PAYLOADSERIALIZER_H -#define SWIFTEN_PAYLOADSERIALIZER_H - -#include <boost/shared_ptr.hpp> +#pragma once  #include <string> -#include "Swiften/Elements/Payload.h" +#include <boost/shared_ptr.hpp>  namespace Swift { +	class Payload; +  	class PayloadSerializer {  		public:  			virtual ~PayloadSerializer(); @@ -21,5 +20,3 @@ namespace Swift {  			virtual std::string serialize(boost::shared_ptr<Payload>) const = 0;  	};  } - -#endif diff --git a/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp b/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp index 0fa45ce..12a38a8 100644 --- a/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/CommandSerializer.cpp @@ -21,7 +21,7 @@ CommandSerializer::CommandSerializer() {  }  std::string CommandSerializer::serializePayload(boost::shared_ptr<Command> command)	const { -	XMLElement commandElement("command", "http://jabber.org/protocol/comands"); +	XMLElement commandElement("command", "http://jabber.org/protocol/commands");  	commandElement.setAttribute("node", command->getNode());  	if (!command->getSessionID().empty()) { diff --git a/Swiften/Serializer/PayloadSerializers/DelaySerializer.cpp b/Swiften/Serializer/PayloadSerializers/DelaySerializer.cpp index 4922042..bdf5505 100644 --- a/Swiften/Serializer/PayloadSerializers/DelaySerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/DelaySerializer.cpp @@ -7,6 +7,7 @@  #include "Swiften/Serializer/PayloadSerializers/DelaySerializer.h"  #include <boost/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp>  #include <Swiften/Base/String.h>  #include "Swiften/Serializer/XML/XMLElement.h" diff --git a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp index 53b4241..51cb90f 100644 --- a/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/FormSerializer.cpp @@ -129,14 +129,6 @@ boost::shared_ptr<XMLElement> FormSerializer::fieldToXML(boost::shared_ptr<FormF  		fieldType = "text-multi";  		multiLineify(boost::dynamic_pointer_cast<TextMultiFormField>(field)->getValue(), "value", fieldElement);  	} -	else if (boost::dynamic_pointer_cast<UntypedFormField>(field)) { -		std::vector<std::string> lines = boost::dynamic_pointer_cast<UntypedFormField>(field)->getValue(); -		foreach(const std::string& line, lines) { -			boost::shared_ptr<XMLElement> valueElement(new XMLElement("value")); -			valueElement->addNode(XMLTextNode::create(line)); -			fieldElement->addNode(valueElement); -		} -	}  	else {  		assert(false);  	} diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp index 1bbcbf2..a1f412b 100644 --- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp +++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp @@ -14,6 +14,7 @@  #include "Swiften/Serializer/PayloadSerializers/PrioritySerializer.h"  #include "Swiften/Serializer/PayloadSerializers/ErrorSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/RosterSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/MUCUserPayloadSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/MUCOwnerPayloadSerializer.h" @@ -40,6 +41,7 @@  #include "Swiften/Serializer/PayloadSerializers/InBandRegistrationPayloadSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/NicknameSerializer.h"  #include "Swiften/Serializer/PayloadSerializers/SearchPayloadSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h"  namespace Swift { @@ -51,6 +53,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {  	serializers_.push_back(new PrioritySerializer());  	serializers_.push_back(new ErrorSerializer());  	serializers_.push_back(new RosterSerializer()); +	serializers_.push_back(new RosterItemExchangeSerializer());  	serializers_.push_back(new MUCPayloadSerializer());  	serializers_.push_back(new MUCUserPayloadSerializer());  	serializers_.push_back(new MUCOwnerPayloadSerializer(this)); @@ -77,6 +80,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {  	serializers_.push_back(new InBandRegistrationPayloadSerializer());  	serializers_.push_back(new NicknameSerializer());  	serializers_.push_back(new SearchPayloadSerializer()); +	serializers_.push_back(new ReplaceSerializer());  	foreach(PayloadSerializer* serializer, serializers_) {  		addSerializer(serializer);  	} diff --git a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp index d7e1613..f19874a 100644 --- a/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.cpp @@ -6,6 +6,9 @@  #include "Swiften/Serializer/PayloadSerializers/MUCPayloadSerializer.h" +#include <boost/lexical_cast.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +  #include "Swiften/Serializer/XML/XMLElement.h"  #include <Swiften/Base/String.h> diff --git a/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h b/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h new file mode 100644 index 0000000..303b2b8 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Serializer/GenericPayloadSerializer.h" +#include "Swiften/Serializer/XML/XMLTextNode.h" +#include "Swiften/Elements/Replace.h" + +namespace Swift { +	class ReplaceSerializer : public GenericPayloadSerializer<Replace> { +		public: +			ReplaceSerializer() : GenericPayloadSerializer<Replace>() {} + +			virtual std::string serializePayload(boost::shared_ptr<Replace> replace) const { +				return "<replace id = '" + replace->getId() + "' xmlns='http://swift.im/protocol/replace'/>"; +			} +	}; +} diff --git a/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp b/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp new file mode 100644 index 0000000..c9ed6ea --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h" + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Serializer/XML/XMLTextNode.h" +#include "Swiften/Serializer/XML/XMLRawTextNode.h" +#include "Swiften/Serializer/XML/XMLElement.h" + +namespace Swift { + +RosterItemExchangeSerializer::RosterItemExchangeSerializer() : GenericPayloadSerializer<RosterItemExchangePayload>() { +} + +std::string RosterItemExchangeSerializer::serializePayload(boost::shared_ptr<RosterItemExchangePayload> roster)  const { +	XMLElement queryElement("x", "http://jabber.org/protocol/rosterx"); +	foreach(const RosterItemExchangePayload::Item& item, roster->getItems()) { +		boost::shared_ptr<XMLElement> itemElement(new XMLElement("item")); +		itemElement->setAttribute("jid", item.getJID()); +		itemElement->setAttribute("name", item.getName()); + +		switch (item.getAction()) { +			case RosterItemExchangePayload::Item::Add: itemElement->setAttribute("action", "add"); break; +			case RosterItemExchangePayload::Item::Modify: itemElement->setAttribute("action", "modify"); break; +			case RosterItemExchangePayload::Item::Delete: itemElement->setAttribute("action", "delete"); break; +		} + +		foreach(const std::string& group, item.getGroups()) { +			boost::shared_ptr<XMLElement> groupElement(new XMLElement("group")); +			groupElement->addNode(boost::shared_ptr<XMLTextNode>(new XMLTextNode(group))); +			itemElement->addNode(groupElement); +		} + +		queryElement.addNode(itemElement); +	} + +	return queryElement.serialize(); +} + +} diff --git a/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h b/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h new file mode 100644 index 0000000..ec2cc13 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "Swiften/Serializer/GenericPayloadSerializer.h" +#include "Swiften/Elements/RosterItemExchangePayload.h" + +namespace Swift { +	class RosterItemExchangeSerializer : public GenericPayloadSerializer<RosterItemExchangePayload> { +		public: +			RosterItemExchangeSerializer(); + +			virtual std::string serializePayload(boost::shared_ptr<RosterItemExchangePayload>)  const; +	}; +} diff --git a/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp index 40faf73..886676a 100644 --- a/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/RosterSerializer.cpp @@ -20,6 +20,9 @@ RosterSerializer::RosterSerializer() : GenericPayloadSerializer<RosterPayload>()  std::string RosterSerializer::serializePayload(boost::shared_ptr<RosterPayload> roster)  const {  	XMLElement queryElement("query", "jabber:iq:roster"); +	if (roster->getVersion()) { +		queryElement.setAttribute("ver", *roster->getVersion()); +	}  	foreach(const RosterItemPayload& item, roster->getItems()) {  		boost::shared_ptr<XMLElement> itemElement(new XMLElement("item"));  		itemElement->setAttribute("jid", item.getJID()); diff --git a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp index 70fb2ac..ee9f279 100644 --- a/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp +++ b/Swiften/Serializer/PayloadSerializers/StreamInitiationSerializer.cpp @@ -49,7 +49,7 @@ std::string StreamInitiationSerializer::serializePayload(boost::shared_ptr<Strea  		siElement.addNode(fileElement);  	} -	boost::shared_ptr<XMLElement> featureElement(new XMLElement("feature", "http://jabber.org/protocol/feature-neg")); +	boost::shared_ptr<XMLElement> featureElement(new XMLElement("feature", FEATURE_NEG_NS));  	if (streamInitiation->getProvidedMethods().size() > 0) {  		Form::ref form(new Form(Form::FormType));  		ListSingleFormField::ref field = ListSingleFormField::create(); diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp index e4a6661..5b52c0a 100644 --- a/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/FormSerializerTest.cpp @@ -96,13 +96,6 @@ class FormSerializerTest : public CppUnit::TestFixture {  			field->setDescription("Tell all your friends about your new bot!");  			form->addField(field); -			std::vector<std::string> values2; -			values2.push_back("foo"); -			values2.push_back("bar"); -			field = UntypedFormField::create(values2); -			field->setName("fum"); -			form->addField(field); -  			CPPUNIT_ASSERT_EQUAL(std::string(  				"<x type=\"form\" xmlns=\"jabber:x:data\">"  						"<field type=\"hidden\" var=\"FORM_TYPE\">" @@ -139,10 +132,6 @@ class FormSerializerTest : public CppUnit::TestFixture {  							"<value>foo@bar.com</value>"  							"<value>baz@fum.org</value>"  						"</field>" -						"<field var=\"fum\">" -							"<value>foo</value>" -							"<value>bar</value>" -						"</field>"  					"</x>"), testling.serialize(form));  		}  }; diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp new file mode 100644 index 0000000..8fe96e5 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/ReplaceSerializerTest.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h" + +using namespace Swift; + +class ReplaceSerializerTest: public CppUnit::TestFixture { +	CPPUNIT_TEST_SUITE(ReplaceSerializerTest); +	CPPUNIT_TEST(testSerialize); +	CPPUNIT_TEST_SUITE_END(); + +	public: +		ReplaceSerializerTest() {} + +		void testSerialize() { +			ReplaceSerializer testling; +			boost::shared_ptr<Replace> replace(new Replace()); +			replace->setId("bad1"); +			CPPUNIT_ASSERT_EQUAL(std::string("<replace id = 'bad1' xmlns='http://swift.im/protocol/replace'/>"), testling.serialize(replace)); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ReplaceSerializerTest); diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cpp new file mode 100644 index 0000000..f4de783 --- /dev/null +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterItemExchangeSerializerTest.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Serializer/PayloadSerializers/RosterItemExchangeSerializer.h" + +using namespace Swift; + +class RosterItemExchangeSerializerTest : public CppUnit::TestFixture +{ +		CPPUNIT_TEST_SUITE(RosterItemExchangeSerializerTest); +		CPPUNIT_TEST(testSerialize); +		CPPUNIT_TEST_SUITE_END(); + +	public: +		RosterItemExchangeSerializerTest() {} + +		void testSerialize() { +			RosterItemExchangeSerializer testling; +			boost::shared_ptr<RosterItemExchangePayload> roster(new RosterItemExchangePayload()); + +			RosterItemExchangePayload::Item item1; +			item1.setJID("foo@bar.com"); +			item1.setName("Foo @ Bar"); +			item1.setAction(RosterItemExchangePayload::Item::Add); +			item1.addGroup("Group 1"); +			item1.addGroup("Group 2"); +			roster->addItem(item1); + +			RosterItemExchangePayload::Item item2; +			item2.setJID("baz@blo.com"); +			item2.setName("Baz"); +			item2.setAction(RosterItemExchangePayload::Item::Modify); +			roster->addItem(item2); + +			std::string expectedResult =  +				"<x xmlns=\"http://jabber.org/protocol/rosterx\">" +					"<item action=\"add\" jid=\"foo@bar.com\" name=\"Foo @ Bar\">" +						"<group>Group 1</group>" +						"<group>Group 2</group>" +					"</item>" +					"<item action=\"modify\" jid=\"baz@blo.com\" name=\"Baz\"/>" +				"</x>"; + +			CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster)); +		} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(RosterItemExchangeSerializerTest); diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp index b8ceac3..61316df 100644 --- a/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp +++ b/Swiften/Serializer/PayloadSerializers/UnitTest/RosterSerializerTest.cpp @@ -11,16 +11,15 @@  using namespace Swift; -class RosterSerializerTest : public CppUnit::TestFixture -{ +class RosterSerializerTest : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(RosterSerializerTest);  		CPPUNIT_TEST(testSerialize);  		CPPUNIT_TEST(testSerialize_ItemWithUnknownContent); +		CPPUNIT_TEST(testSerialize_WithVersion); +		CPPUNIT_TEST(testSerialize_WithEmptyVersion);  		CPPUNIT_TEST_SUITE_END();  	public: -		RosterSerializerTest() {} -  		void testSerialize() {  			RosterSerializer testling;  			boost::shared_ptr<RosterPayload> roster(new RosterPayload()); @@ -77,6 +76,26 @@ class RosterSerializerTest : public CppUnit::TestFixture  			CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster));  		} + +		void testSerialize_WithVersion() { +			RosterSerializer testling; +			boost::shared_ptr<RosterPayload> roster(new RosterPayload()); +			roster->setVersion("ver20"); + +			std::string expectedResult = "<query ver=\"ver20\" xmlns=\"jabber:iq:roster\"/>"; + +			CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster)); +		} + +		void testSerialize_WithEmptyVersion() { +			RosterSerializer testling; +			boost::shared_ptr<RosterPayload> roster(new RosterPayload()); +			roster->setVersion(""); + +			std::string expectedResult = "<query ver=\"\" xmlns=\"jabber:iq:roster\"/>"; + +			CPPUNIT_ASSERT_EQUAL(expectedResult, testling.serialize(roster)); +		}  };  CPPUNIT_TEST_SUITE_REGISTRATION(RosterSerializerTest); diff --git a/Swiften/Serializer/PresenceSerializer.h b/Swiften/Serializer/PresenceSerializer.h index 3cb9aab..be31597 100644 --- a/Swiften/Serializer/PresenceSerializer.h +++ b/Swiften/Serializer/PresenceSerializer.h @@ -7,8 +7,6 @@  #ifndef SWIFTEN_PresenceSerializer_H  #define SWIFTEN_PresenceSerializer_H -#include <cassert> -  #include "Swiften/Serializer/GenericStanzaSerializer.h"  #include "Swiften/Elements/Presence.h" diff --git a/Swiften/Serializer/StanzaSerializer.cpp b/Swiften/Serializer/StanzaSerializer.cpp index cfc9a43..6bd2ef0 100644 --- a/Swiften/Serializer/StanzaSerializer.cpp +++ b/Swiften/Serializer/StanzaSerializer.cpp @@ -10,6 +10,7 @@  #include <typeinfo>  #include <iostream> +#include <Swiften/Base/foreach.h>  #include "Swiften/Serializer/XML/XMLElement.h"  #include "Swiften/Serializer/XML/XMLRawTextNode.h"  #include "Swiften/Serializer/PayloadSerializer.h" diff --git a/Swiften/Serializer/StreamFeaturesSerializer.cpp b/Swiften/Serializer/StreamFeaturesSerializer.cpp index 915433c..11744b4 100644 --- a/Swiften/Serializer/StreamFeaturesSerializer.cpp +++ b/Swiften/Serializer/StreamFeaturesSerializer.cpp @@ -6,6 +6,8 @@  #include "Swiften/Serializer/StreamFeaturesSerializer.h" +#include <boost/smart_ptr/make_shared.hpp> +  #include "Swiften/Serializer/XML/XMLElement.h"  #include "Swiften/Serializer/XML/XMLTextNode.h"  #include "Swiften/Base/foreach.h" @@ -49,6 +51,9 @@ std::string StreamFeaturesSerializer::serialize(boost::shared_ptr<Element> eleme  	if (streamFeatures->hasStreamManagement()) {  		streamFeaturesElement.addNode(boost::shared_ptr<XMLElement>(new XMLElement("sm", "urn:xmpp:sm:2")));  	} +	if (streamFeatures->hasRosterVersioning()) { +		streamFeaturesElement.addNode(boost::make_shared<XMLElement>("ver", "urn:xmpp:features:rosterver")); +	}  	return streamFeaturesElement.serialize();  } diff --git a/Swiften/Serializer/StreamManagementEnabledSerializer.cpp b/Swiften/Serializer/StreamManagementEnabledSerializer.cpp new file mode 100644 index 0000000..02cf948 --- /dev/null +++ b/Swiften/Serializer/StreamManagementEnabledSerializer.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Serializer/StreamManagementEnabledSerializer.h> + +#include <boost/shared_ptr.hpp> + +#include <Swiften/Elements/StreamManagementEnabled.h> +#include <Swiften/Serializer/XML/XMLElement.h> + +using namespace Swift; + +StreamManagementEnabledSerializer::StreamManagementEnabledSerializer() : GenericElementSerializer<StreamManagementEnabled>() { +} + +std::string StreamManagementEnabledSerializer::serialize(boost::shared_ptr<Element> el) const { +	boost::shared_ptr<StreamManagementEnabled> e(boost::dynamic_pointer_cast<StreamManagementEnabled>(el)); +	XMLElement element("enabled", "urn:xmpp:sm:2"); +	if (!e->getResumeID().empty()) { +		element.setAttribute("id", e->getResumeID()); +	} +	if (e->getResumeSupported()) { +		element.setAttribute("resume", "true"); +	} +	return element.serialize(); +} diff --git a/Swiften/Serializer/StreamManagementEnabledSerializer.h b/Swiften/Serializer/StreamManagementEnabledSerializer.h index fc7bd86..8ee9e31 100644 --- a/Swiften/Serializer/StreamManagementEnabledSerializer.h +++ b/Swiften/Serializer/StreamManagementEnabledSerializer.h @@ -10,16 +10,12 @@  #include "Swiften/Elements/StreamManagementEnabled.h"  #include "Swiften/Serializer/GenericElementSerializer.h" -#include "Swiften/Serializer/XML/XMLElement.h"  namespace Swift {  	class StreamManagementEnabledSerializer : public GenericElementSerializer<StreamManagementEnabled> {  		public: -			StreamManagementEnabledSerializer() : GenericElementSerializer<StreamManagementEnabled>() { -			} +			StreamManagementEnabledSerializer(); -			virtual std::string serialize(boost::shared_ptr<Element>) const { -				return XMLElement("enabled", "urn:xmpp:sm:2").serialize(); -			} +			virtual std::string serialize(boost::shared_ptr<Element>) const;  	};  } diff --git a/Swiften/Serializer/StreamResumeSerializer.cpp b/Swiften/Serializer/StreamResumeSerializer.cpp new file mode 100644 index 0000000..a7a2e3b --- /dev/null +++ b/Swiften/Serializer/StreamResumeSerializer.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Serializer/StreamResumeSerializer.h> + +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include <Swiften/Elements/StreamResume.h> +#include <Swiften/Serializer/XML/XMLElement.h> + +using namespace Swift; + +StreamResumeSerializer::StreamResumeSerializer() : GenericElementSerializer<StreamResume>() { +} + +std::string StreamResumeSerializer::serialize(boost::shared_ptr<Element> el) const { +	boost::shared_ptr<StreamResume> e(boost::dynamic_pointer_cast<StreamResume>(el)); +	XMLElement element("resume", "urn:xmpp:sm:2"); +	element.setAttribute("previd", e->getResumeID()); +	if (e->getHandledStanzasCount()) { +		element.setAttribute("h", boost::lexical_cast<std::string>(e->getHandledStanzasCount())); +	} +	return element.serialize(); +} diff --git a/Swiften/Serializer/StreamResumeSerializer.h b/Swiften/Serializer/StreamResumeSerializer.h new file mode 100644 index 0000000..6ea7365 --- /dev/null +++ b/Swiften/Serializer/StreamResumeSerializer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 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/Elements/StreamResume.h> +#include <Swiften/Serializer/GenericElementSerializer.h> + +namespace Swift { +	class StreamResumeSerializer : public GenericElementSerializer<StreamResume> { +		public: +			StreamResumeSerializer(); + +			virtual std::string serialize(boost::shared_ptr<Element>) const; +	}; +} diff --git a/Swiften/Serializer/StreamResumedSerializer.cpp b/Swiften/Serializer/StreamResumedSerializer.cpp new file mode 100644 index 0000000..d3beafb --- /dev/null +++ b/Swiften/Serializer/StreamResumedSerializer.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Serializer/StreamResumedSerializer.h> + +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include <Swiften/Elements/StreamResumed.h> +#include <Swiften/Serializer/XML/XMLElement.h> + +using namespace Swift; + +StreamResumedSerializer::StreamResumedSerializer() : GenericElementSerializer<StreamResumed>() { +} + +std::string StreamResumedSerializer::serialize(boost::shared_ptr<Element> el) const { +	boost::shared_ptr<StreamResumed> e(boost::dynamic_pointer_cast<StreamResumed>(el)); +	XMLElement element("resumed", "urn:xmpp:sm:2"); +	element.setAttribute("previd", e->getResumeID()); +	if (e->getHandledStanzasCount()) { +		element.setAttribute("h", boost::lexical_cast<std::string>(e->getHandledStanzasCount())); +	} +	return element.serialize(); +} diff --git a/Swiften/Serializer/StreamResumedSerializer.h b/Swiften/Serializer/StreamResumedSerializer.h new file mode 100644 index 0000000..38d846f --- /dev/null +++ b/Swiften/Serializer/StreamResumedSerializer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2011 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/Elements/StreamResumed.h> +#include <Swiften/Serializer/GenericElementSerializer.h> + +namespace Swift { +	class StreamResumedSerializer : public GenericElementSerializer<StreamResumed> { +		public: +			StreamResumedSerializer(); + +			virtual std::string serialize(boost::shared_ptr<Element>) const; +	}; +} diff --git a/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp index 65caa81..aa896c2 100644 --- a/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp +++ b/Swiften/Serializer/UnitTest/StreamFeaturesSerializerTest.cpp @@ -32,6 +32,7 @@ class StreamFeaturesSerializerTest : public CppUnit::TestFixture  			streamFeatures->setHasResourceBind();  			streamFeatures->setHasSession();  			streamFeatures->setHasStreamManagement(); +			streamFeatures->setHasRosterVersioning();  			CPPUNIT_ASSERT_EQUAL(std::string(  				"<stream:features>" @@ -47,6 +48,7 @@ class StreamFeaturesSerializerTest : public CppUnit::TestFixture  					"<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"  					"<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"  					"<sm xmlns=\"urn:xmpp:sm:2\"/>" +					"<ver xmlns=\"urn:xmpp:features:rosterver\"/>"  				"</stream:features>"), testling.serialize(streamFeatures));  		}  }; diff --git a/Swiften/Serializer/XMPPSerializer.cpp b/Swiften/Serializer/XMPPSerializer.cpp index 06f3558..19a30ce 100644 --- a/Swiften/Serializer/XMPPSerializer.cpp +++ b/Swiften/Serializer/XMPPSerializer.cpp @@ -4,36 +4,38 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "Swiften/Serializer/XMPPSerializer.h" +#include <Swiften/Serializer/XMPPSerializer.h>  #include <boost/bind.hpp>  #include <boost/smart_ptr/make_shared.hpp>  #include <iostream>  #include <cassert> -#include "Swiften/Elements/ProtocolHeader.h" -#include "Swiften/Base/foreach.h" -#include "Swiften/Serializer/CompressRequestSerializer.h" -#include "Swiften/Serializer/CompressFailureSerializer.h" -#include "Swiften/Serializer/StreamErrorSerializer.h" -#include "Swiften/Serializer/StreamFeaturesSerializer.h" -#include "Swiften/Serializer/AuthRequestSerializer.h" -#include "Swiften/Serializer/AuthFailureSerializer.h" -#include "Swiften/Serializer/AuthSuccessSerializer.h" -#include "Swiften/Serializer/AuthChallengeSerializer.h" -#include "Swiften/Serializer/AuthResponseSerializer.h" -#include "Swiften/Serializer/EnableStreamManagementSerializer.h" -#include "Swiften/Serializer/StreamManagementEnabledSerializer.h" -#include "Swiften/Serializer/StreamManagementFailedSerializer.h" -#include "Swiften/Serializer/StanzaAckSerializer.h" -#include "Swiften/Serializer/StanzaAckRequestSerializer.h" -#include "Swiften/Serializer/StartTLSRequestSerializer.h" -#include "Swiften/Serializer/StartTLSFailureSerializer.h" -#include "Swiften/Serializer/TLSProceedSerializer.h" -#include "Swiften/Serializer/MessageSerializer.h" -#include "Swiften/Serializer/PresenceSerializer.h" -#include "Swiften/Serializer/IQSerializer.h" -#include "Swiften/Serializer/ComponentHandshakeSerializer.h" +#include <Swiften/Elements/ProtocolHeader.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Serializer/CompressRequestSerializer.h> +#include <Swiften/Serializer/CompressFailureSerializer.h> +#include <Swiften/Serializer/StreamErrorSerializer.h> +#include <Swiften/Serializer/StreamFeaturesSerializer.h> +#include <Swiften/Serializer/AuthRequestSerializer.h> +#include <Swiften/Serializer/AuthFailureSerializer.h> +#include <Swiften/Serializer/AuthSuccessSerializer.h> +#include <Swiften/Serializer/AuthChallengeSerializer.h> +#include <Swiften/Serializer/AuthResponseSerializer.h> +#include <Swiften/Serializer/EnableStreamManagementSerializer.h> +#include <Swiften/Serializer/StreamManagementEnabledSerializer.h> +#include <Swiften/Serializer/StreamResumeSerializer.h> +#include <Swiften/Serializer/StreamResumedSerializer.h> +#include <Swiften/Serializer/StreamManagementFailedSerializer.h> +#include <Swiften/Serializer/StanzaAckSerializer.h> +#include <Swiften/Serializer/StanzaAckRequestSerializer.h> +#include <Swiften/Serializer/StartTLSRequestSerializer.h> +#include <Swiften/Serializer/StartTLSFailureSerializer.h> +#include <Swiften/Serializer/TLSProceedSerializer.h> +#include <Swiften/Serializer/MessageSerializer.h> +#include <Swiften/Serializer/PresenceSerializer.h> +#include <Swiften/Serializer/IQSerializer.h> +#include <Swiften/Serializer/ComponentHandshakeSerializer.h>  namespace Swift { @@ -56,6 +58,8 @@ XMPPSerializer::XMPPSerializer(PayloadSerializerCollection* payloadSerializers,  	serializers_.push_back(boost::make_shared<EnableStreamManagementSerializer>());  	serializers_.push_back(boost::make_shared<StreamManagementEnabledSerializer>());  	serializers_.push_back(boost::make_shared<StreamManagementFailedSerializer>()); +	serializers_.push_back(boost::make_shared<StreamResumeSerializer>()); +	serializers_.push_back(boost::make_shared<StreamResumedSerializer>());  	serializers_.push_back(boost::make_shared<StanzaAckSerializer>());  	serializers_.push_back(boost::make_shared<StanzaAckRequestSerializer>());  	serializers_.push_back(boost::make_shared<ComponentHandshakeSerializer>()); diff --git a/Swiften/Session/SessionTracer.cpp b/Swiften/Session/SessionTracer.cpp new file mode 100644 index 0000000..6d41e40 --- /dev/null +++ b/Swiften/Session/SessionTracer.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Session/SessionTracer.h> + +#include <iostream> +#include <boost/bind.hpp> + +namespace Swift { + +SessionTracer::SessionTracer(boost::shared_ptr<Session> session) : session(session) { +	session->onDataRead.connect(boost::bind(&SessionTracer::printData, this, '<', _1)); +	session->onDataWritten.connect(boost::bind(&SessionTracer::printData, this, '>', _1)); +} + +void SessionTracer::printData(char direction, const ByteArray& data) { +	std::cerr << direction << direction << " " << session->getLocalJID() << " "; +	for (unsigned int i = 0; i < 72 - session->getLocalJID().toString().size() - session->getRemoteJID().toString().size(); ++i) { +		std::cerr << direction; +	} +	std::cerr << " " << session->getRemoteJID()<< " " << direction << direction << std::endl; +	std::cerr << data.toString() << std::endl; +} + +} diff --git a/Swiften/Session/SessionTracer.h b/Swiften/Session/SessionTracer.h index cce45eb..51b8d16 100644 --- a/Swiften/Session/SessionTracer.h +++ b/Swiften/Session/SessionTracer.h @@ -6,29 +6,18 @@  #pragma once -#include <iostream> +#include <string>  #include "Swiften/Session/Session.h" -#include <string>  #include "Swiften/Base/ByteArray.h"  namespace Swift {  	class SessionTracer {  		public: -			SessionTracer(boost::shared_ptr<Session> session) : session(session) { -				session->onDataRead.connect(boost::bind(&SessionTracer::printData, this, '<', _1)); -				session->onDataWritten.connect(boost::bind(&SessionTracer::printData, this, '>', _1)); -			} +			SessionTracer(boost::shared_ptr<Session> session);  		private: -			void printData(char direction, const ByteArray& data) { -				std::cerr << direction << direction << " " << session->getLocalJID() << " "; -				for (unsigned int i = 0; i < 72 - session->getLocalJID().toString().size() - session->getRemoteJID().toString().size(); ++i) { -					std::cerr << direction; -				} -				std::cerr << " " << session->getRemoteJID()<< " " << direction << direction << std::endl; -				std::cerr << data.toString() << std::endl; -			} +			void printData(char direction, const ByteArray& data);  			boost::shared_ptr<Session> session;  	}; diff --git a/Swiften/StreamManagement/StanzaAckRequester.cpp b/Swiften/StreamManagement/StanzaAckRequester.cpp index f7d603b..4b626c9 100644 --- a/Swiften/StreamManagement/StanzaAckRequester.cpp +++ b/Swiften/StreamManagement/StanzaAckRequester.cpp @@ -7,6 +7,7 @@  #include "Swiften/StreamManagement/StanzaAckRequester.h"  #include <boost/numeric/conversion/cast.hpp> +#include <iostream>  #include "Swiften/Elements/Message.h" diff --git a/Swiften/StreamStack/CompressionLayer.h b/Swiften/StreamStack/CompressionLayer.h index b8293a8..7d8656e 100644 --- a/Swiften/StreamStack/CompressionLayer.h +++ b/Swiften/StreamStack/CompressionLayer.h @@ -27,7 +27,7 @@ namespace Swift {  				try {  					writeDataToChildLayer(compressor_.process(data));  				} -				catch (const ZLibException& e) { +				catch (const ZLibException&) {  					onError();  				}  			} @@ -36,7 +36,7 @@ namespace Swift {  				try {  					writeDataToParentLayer(decompressor_.process(data));  				} -				catch (const ZLibException& e) { +				catch (const ZLibException&) {  					onError();  				}  			} diff --git a/Swiften/StreamStack/ConnectionLayer.cpp b/Swiften/StreamStack/ConnectionLayer.cpp new file mode 100644 index 0000000..00b4289 --- /dev/null +++ b/Swiften/StreamStack/ConnectionLayer.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/StreamStack/ConnectionLayer.h> +#include <boost/bind.hpp> + +namespace Swift { + +ConnectionLayer::ConnectionLayer(boost::shared_ptr<Connection> connection) : connection(connection) { +	connection->onDataRead.connect(boost::bind(&ConnectionLayer::writeDataToParentLayer, this, _1)); +} + +ConnectionLayer::~ConnectionLayer() { +	connection->onDataRead.disconnect(boost::bind(&ConnectionLayer::writeDataToParentLayer, this, _1)); +} + + +} diff --git a/Swiften/StreamStack/ConnectionLayer.h b/Swiften/StreamStack/ConnectionLayer.h index 0da0900..bd9c093 100644 --- a/Swiften/StreamStack/ConnectionLayer.h +++ b/Swiften/StreamStack/ConnectionLayer.h @@ -6,9 +6,7 @@  #pragma once -#include "Swiften/Base/boost_bsignals.h"  #include <boost/shared_ptr.hpp> -#include <boost/bind.hpp>  #include "Swiften/StreamStack/LowLayer.h"  #include "Swiften/Network/Connection.h" @@ -16,13 +14,8 @@  namespace Swift {  	class ConnectionLayer : public LowLayer {  		public: -			ConnectionLayer(boost::shared_ptr<Connection> connection) : connection(connection) { -				connection->onDataRead.connect(boost::bind(&ConnectionLayer::writeDataToParentLayer, this, _1)); -			} - -			~ConnectionLayer() { -				connection->onDataRead.disconnect(boost::bind(&ConnectionLayer::writeDataToParentLayer, this, _1)); -			} +			ConnectionLayer(boost::shared_ptr<Connection> connection); +			~ConnectionLayer();  			void writeData(const ByteArray& data) {  				connection->write(data); diff --git a/Swiften/StreamStack/SConscript b/Swiften/StreamStack/SConscript index 022c695..06fcc03 100644 --- a/Swiften/StreamStack/SConscript +++ b/Swiften/StreamStack/SConscript @@ -6,6 +6,7 @@ sources = [  		"HighLayer.cpp",  		"LowLayer.cpp",  		"StreamStack.cpp", +		"ConnectionLayer.cpp",  		"TLSLayer.cpp",  		"WhitespacePingLayer.cpp",  		"XMPPLayer.cpp", diff --git a/Swiften/StreamStack/StreamStack.h b/Swiften/StreamStack/StreamStack.h index 562245e..c9ebecd 100644 --- a/Swiften/StreamStack/StreamStack.h +++ b/Swiften/StreamStack/StreamStack.h @@ -11,7 +11,6 @@  #include <vector>  #include "Swiften/Elements/Stanza.h" -#include "Swiften/Base/foreach.h"  namespace Swift {  	class XMPPLayer; @@ -30,8 +29,8 @@ namespace Swift {  			}  			template<typename T> T* getLayer() { -				foreach(StreamLayer* streamLayer, layers_) { -					T* layer = dynamic_cast<T*>(streamLayer); +				for (size_t i = 0; i < layers_.size(); ++i) { +					T* layer = dynamic_cast<T*>(layers_[i]);  					if (layer) {  						return layer;  					} diff --git a/Swiften/StringCodecs/SHA1.cpp b/Swiften/StringCodecs/SHA1.cpp index 9882f70..2703796 100644 --- a/Swiften/StringCodecs/SHA1.cpp +++ b/Swiften/StringCodecs/SHA1.cpp @@ -4,10 +4,14 @@   * See Documentation/Licenses/GPLv3.txt for more information.   */ +#include <Swiften/StringCodecs/SHA1.h> +  #include "Swiften/Base/Platform.h"  #pragma GCC diagnostic ignored "-Wold-style-cast" +using namespace Swift; +  /*  SHA-1 in C  By Steve Reid <steve@edmweb.com> @@ -25,21 +29,9 @@ A million repetitions of "a"  /* #define LITTLE_ENDIAN * This should be #define'd if true. */  /* #define SHA1HANDSOFF * Copies data before messing with it. */ -#include <boost/cstdint.hpp>  #include <stdio.h>  #include <string.h> -typedef struct { -		boost::uint32_t state[5]; -		boost::uint32_t count[2]; -		boost::uint8_t buffer[64]; -} SHA1_CTX; - -void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]); -void SHA1Init(SHA1_CTX* context); -void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len); -void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context); -  #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))  /* blk0() and blk() perform the initial expand. */ @@ -63,7 +55,7 @@ void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context);  /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]) +void SHA1::Transform(boost::uint32_t state[5], boost::uint8_t buffer[64])  {  boost::uint32_t a, b, c, d, e;  typedef union { @@ -118,7 +110,7 @@ static boost::uint8_t workspace[64];  /* SHA1Init - Initialize new context */ -void SHA1Init(SHA1_CTX* context) +void SHA1::Init(SHA1::CTX* context)  {  		/* SHA1 initialization constants */  		context->state[0] = 0x67452301; @@ -132,7 +124,7 @@ void SHA1Init(SHA1_CTX* context)  /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, boost::uint8_t* data, unsigned int len) +void SHA1::Update(SHA1::CTX* context, boost::uint8_t* data, unsigned int len)  {  unsigned int i, j; @@ -141,9 +133,9 @@ unsigned int i, j;  		context->count[1] += (len >> 29);  		if ((j + len) > 63) {  				memcpy(&context->buffer[j], data, (i = 64-j)); -				SHA1Transform(context->state, context->buffer); +				Transform(context->state, context->buffer);  				for ( ; i + 63 < len; i += 64) { -						SHA1Transform(context->state, &data[i]); +						Transform(context->state, &data[i]);  				}  				j = 0;  		} @@ -154,7 +146,7 @@ unsigned int i, j;  /* Add padding and return the message digest. */ -void SHA1Final(boost::uint8_t digest[20], SHA1_CTX* context) +void SHA1::Final(boost::uint8_t digest[20], SHA1::CTX* context)  {  boost::uint32_t i, j;  boost::uint8_t finalcount[8]; @@ -163,11 +155,11 @@ boost::uint8_t finalcount[8];  				finalcount[i] = (boost::uint8_t) ((context->count[(i >= 4 ? 0 : 1)]  				 >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */  		} -		SHA1Update(context, (boost::uint8_t *)("\200"), 1); +		Update(context, (boost::uint8_t *)("\200"), 1);  		while ((context->count[0] & 504) != 448) { -				SHA1Update(context, (boost::uint8_t *)("\0"), 1); +				Update(context, (boost::uint8_t *)("\0"), 1);  		} -		SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */ +		Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */  		for (i = 0; i < 20; i++) {  				digest[i] = (boost::uint8_t)  				 ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); @@ -179,24 +171,43 @@ boost::uint8_t finalcount[8];  		memset(context->count, 0, 8);  		memset(&finalcount, 0, 8);  #ifdef SHA1HANDSOFF  /* make SHA1Transform overwrite it's own static vars */ -		SHA1Transform(context->state, context->buffer); +		Transform(context->state, context->buffer);  #endif  }  // ----------------------------------------------------------------------------- -#include "Swiften/StringCodecs/SHA1.h" -  namespace Swift { +SHA1::SHA1() { +	Init(&context); +} + +SHA1& SHA1::update(const std::vector<unsigned char>& input) { +	std::vector<unsigned char> inputCopy(input); +	Update(&context, (boost::uint8_t*) ByteArray::data(inputCopy), inputCopy.size()); +	return *this; +} + +std::vector<unsigned char> SHA1::getHash() const { +	std::vector<unsigned char> digest; +	digest.resize(20); +	CTX contextCopy(context); +	Final((boost::uint8_t*) ByteArray::data(digest), &contextCopy); +	return digest; +} +  ByteArray SHA1::getHash(const ByteArray& input) { -	ByteArray inputCopy(input); +	CTX context; +	Init(&context); + +	std::vector<unsigned char> inputCopy(input.getVector()); +	Update(&context, (boost::uint8_t*) ByteArray::data(inputCopy), inputCopy.size()); +  	ByteArray digest;  	digest.resize(20); -	SHA1_CTX context; -	SHA1Init(&context); -	SHA1Update(&context, (boost::uint8_t*) inputCopy.getData(), inputCopy.getSize()); -	SHA1Final((boost::uint8_t*) digest.getData(), &context); +	Final((boost::uint8_t*) digest.getData(), &context); +  	return digest;  } diff --git a/Swiften/StringCodecs/SHA1.h b/Swiften/StringCodecs/SHA1.h index fc5ba0e..9c0232a 100644 --- a/Swiften/StringCodecs/SHA1.h +++ b/Swiften/StringCodecs/SHA1.h @@ -6,11 +6,38 @@  #pragma once +#include <vector> +#include <boost/cstdint.hpp> +  #include "Swiften/Base/ByteArray.h"  namespace Swift {  	class SHA1 {  		public: +			SHA1(); + +			SHA1& update(const std::vector<unsigned char>& data); +			std::vector<unsigned char> getHash() const; + +			/** +			 * Equivalent of: +			 *	SHA1().update(data),getHash(), but slightly more efficient and +			 *	convenient. +			 */  			static ByteArray getHash(const ByteArray& data); + +		private: +			typedef struct { +					boost::uint32_t state[5]; +					boost::uint32_t count[2]; +					boost::uint8_t buffer[64]; +			} CTX; +			static void Init(CTX* context); +			static void Transform(boost::uint32_t state[5], boost::uint8_t buffer[64]); +			static void Update(CTX* context, boost::uint8_t* data, unsigned int len); +			static void Final(boost::uint8_t digest[20], CTX* context); + +		private: +			CTX context;  	};  } diff --git a/Swiften/StringCodecs/UnitTest/SHA1Test.cpp b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp index 9434235..984f512 100644 --- a/Swiften/StringCodecs/UnitTest/SHA1Test.cpp +++ b/Swiften/StringCodecs/UnitTest/SHA1Test.cpp @@ -16,18 +16,65 @@ using namespace Swift;  class SHA1Test : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(SHA1Test);  		CPPUNIT_TEST(testGetHash); -		CPPUNIT_TEST(testGetHash_Twice); +		CPPUNIT_TEST(testGetHash_TwoUpdates); +		CPPUNIT_TEST(testGetHash_TwoGetHash);  		CPPUNIT_TEST(testGetHash_NoData); +		CPPUNIT_TEST(testGetHash_InterleavedUpdate); +		CPPUNIT_TEST(testGetHashStatic); +		CPPUNIT_TEST(testGetHashStatic_Twice); +		CPPUNIT_TEST(testGetHashStatic_NoData);  		CPPUNIT_TEST_SUITE_END();  	public:  		void testGetHash() { +			SHA1 sha; +			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<")); + +			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash()); +		} + +		void testGetHash_TwoUpdates() { +			SHA1 sha; +			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<")); +			sha.update(ByteArray::create("http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<")); + +			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash()); +		} + +		void testGetHash_TwoGetHash() { +			SHA1 sha; +			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<")); + +			sha.getHash(); + +			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash()); +		} + +		void testGetHash_InterleavedUpdate() { +			SHA1 sha; + +			sha.update(ByteArray::create("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<")); +			sha.getHash(); +			sha.update(ByteArray::create("http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<")); + +			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), sha.getHash()); +		} + + +		void testGetHash_NoData() { +			SHA1 sha; +			sha.update(std::vector<unsigned char>()); + +			CPPUNIT_ASSERT_EQUAL(ByteArray::create("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), sha.getHash()); +		} + +		void testGetHashStatic() {  			ByteArray result(SHA1::getHash("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<"));  			CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);  		} -		void testGetHash_Twice() { +		void testGetHashStatic_Twice() {  			ByteArray input("client/pc//Exodus 0.9.1<http://jabber.org/protocol/caps<http://jabber.org/protocol/disco#info<http://jabber.org/protocol/disco#items<http://jabber.org/protocol/muc<");  			SHA1::getHash(input);  			ByteArray result(SHA1::getHash(input)); @@ -35,7 +82,7 @@ class SHA1Test : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(ByteArray("\x42\x06\xb2\x3c\xa6\xb0\xa6\x43\xd2\x0d\x89\xb0\x4f\xf5\x8c\xf7\x8b\x80\x96\xed"), result);  		} -		void testGetHash_NoData() { +		void testGetHashStatic_NoData() {  			ByteArray result(SHA1::getHash(ByteArray()));  			CPPUNIT_ASSERT_EQUAL(ByteArray("\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09"), result); diff --git a/Swiften/VCards/SConscript b/Swiften/VCards/SConscript index e980ba3..c20c17d 100644 --- a/Swiften/VCards/SConscript +++ b/Swiften/VCards/SConscript @@ -3,6 +3,5 @@ Import("swiften_env")  objects = swiften_env.SwiftenObject([  			"VCardManager.cpp",  			"VCardStorage.cpp", -			"VCardFileStorage.cpp",  		])  swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiftob/Commands.cpp b/Swiftob/Commands.cpp index e39d23e..0e44a23 100644 --- a/Swiftob/Commands.cpp +++ b/Swiftob/Commands.cpp @@ -6,6 +6,7 @@  #include "Swiftob/Commands.h" +#include <Swiften/Base/foreach.h>  #include <iostream>  #include <boost/bind.hpp> diff --git a/Swiftob/LuaCommands.cpp b/Swiftob/LuaCommands.cpp index 7be818e..3843fb3 100644 --- a/Swiftob/LuaCommands.cpp +++ b/Swiftob/LuaCommands.cpp @@ -9,7 +9,9 @@  #include <boost/bind.hpp>  #include <vector>  #include <algorithm> +#include <iostream> +#include <Swiften/Base/foreach.h>  #include <Swiften/Client/Client.h>  #include <Swiften/Network/TimerFactory.h> diff --git a/Swiftob/LuaCommands.h b/Swiftob/LuaCommands.h index dc8e36e..f506a70 100644 --- a/Swiftob/LuaCommands.h +++ b/Swiftob/LuaCommands.h @@ -9,12 +9,9 @@  #include <string>  #include <vector> -extern "C" {  #include <lua.h>  #include <lauxlib.h>  #include <lualib.h> -} -  #include <boost/filesystem/fstream.hpp>  #include <boost/noncopyable.hpp>  #include "Swiften/Network/NetworkFactories.h" diff --git a/Swiftob/MUCs.cpp b/Swiftob/MUCs.cpp index 55bf313..0f9d7d2 100644 --- a/Swiftob/MUCs.cpp +++ b/Swiftob/MUCs.cpp @@ -6,6 +6,10 @@  #include "Swiftob/MUCs.h" +#include <boost/bind.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <Swiften/Base/foreach.h>  #include <Swiften/Client/Client.h>  #include <Swiften/MUC/MUC.h>  #include <Swiften/MUC/MUCManager.h> diff --git a/Swiftob/SConscript b/Swiftob/SConscript index 3928ff0..e955a22 100644 --- a/Swiftob/SConscript +++ b/Swiftob/SConscript @@ -14,7 +14,7 @@ if env["SCONS_STAGE"] == "build":  	myenv.MergeFlags(myenv["PLATFORM_FLAGS"])  	myenv.MergeFlags(myenv.get("LUA_FLAGS", {}))  	sources = [ -		"linit.c", # This is horrible! +		"linit.cpp",  		"Swiftob.cpp",  		"Users.cpp",  		"Commands.cpp", diff --git a/Swiftob/Users.cpp b/Swiftob/Users.cpp index 55ba4eb..e9344a0 100644 --- a/Swiftob/Users.cpp +++ b/Swiftob/Users.cpp @@ -8,6 +8,7 @@  #include <iostream> +#include <Swiften/Base/foreach.h>  #include <Swiften/Client/Client.h>  #include "Swiftob/MUCs.h" diff --git a/Swiftob/linit.c b/Swiftob/linit.cpp index 13c5b09..13c5b09 100644 --- a/Swiftob/linit.c +++ b/Swiftob/linit.cpp  | 
 Swift