diff options
499 files changed, 9602 insertions, 1616 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..3c6f965 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,9 @@ 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()); + roster_->getGroup(groupName)->setManualSort(roleToSortName(occupant.getRole())); if (joined_) { std::string joinString; MUCOccupant::Role role = occupant.getRole(); @@ -248,6 +251,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); } @@ -309,7 +322,9 @@ void MUCController::handleOccupantRoleChanged(const std::string& nick, const MUC if (occupant.getRealJID()) { realJID = occupant.getRealJID().get(); } - roster_->addContact(jid, realJID, nick, roleToGroupName(occupant.getRole()), avatarManager_->getAvatarPath(jid).string()); + std::string group(roleToGroupName(occupant.getRole())); + roster_->addContact(jid, realJID, nick, group, avatarManager_->getAvatarPath(jid).string()); + roster_->getGroup(group)->setManualSort(roleToSortName(occupant.getRole())); chatWindow_->addSystemMessage(str(format(QT_TRANSLATE_NOOP("", "%1% is now a %2%")) % nick % roleToFriendlyName(occupant.getRole()))); } diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h index ebdd6cd..6b05a8e 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 <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(); 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..2b56d1f 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,20 @@ GroupRosterItem::~GroupRosterItem() { } +void GroupRosterItem::setManualSort(const std::string& manualSortValue) { + manualSort_ = true; + bool changed = manualSortValue_ != manualSortValue; + manualSortValue_ = manualSortValue; + if (changed) { + onChildrenChanged(); + onDataChanged(); + } +} + +const std::string& GroupRosterItem::getSortableDisplayName() const { + return manualSort_ ? manualSortValue_ : RosterItem::getSortableDisplayName(); +} + bool GroupRosterItem::isExpanded() const { return expanded_; } @@ -210,7 +224,8 @@ void GroupRosterItem::handleChildrenChanged(GroupRosterItem* group) { } else { displayedChildren_.erase(std::remove(displayedChildren_.begin(), displayedChildren_.end(), group), displayedChildren_.end()); } - if (oldSize != getDisplayedChildren().size()) { + + if (oldSize != getDisplayedChildren().size() || sortDisplayed()) { onChildrenChanged(); onDataChanged(); } 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/Roster/UnitTest/RosterTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterTest.cpp index cbef787..4444e8a 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterTest.cpp @@ -21,6 +21,7 @@ class RosterTest : public CppUnit::TestFixture { CPPUNIT_TEST(testRemoveSecondContact); CPPUNIT_TEST(testRemoveSecondContactSameBare); CPPUNIT_TEST(testApplyPresenceLikeMUC); + CPPUNIT_TEST(testReSortLikeMUC); CPPUNIT_TEST_SUITE_END(); public: @@ -117,6 +118,22 @@ class RosterTest : public CppUnit::TestFixture { } + void testReSortLikeMUC() { + JID jid4a("a@b/c"); + JID jid4b("a@b/d"); + JID jid4c("a@b/e"); + roster_->addContact(jid4a, JID(), "Bird", "group1", ""); + roster_->addContact(jid4b, JID(), "Cookie", "group2", ""); + roster_->addContact(jid4b, JID(), "Ernie", "group1", ""); + roster_->getGroup("group1")->setManualSort("2"); + roster_->getGroup("group2")->setManualSort("1"); + GroupRosterItem* root = roster_->getRoot(); + const std::vector<RosterItem*> kids = root->getDisplayedChildren(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), kids.size()); + CPPUNIT_ASSERT_EQUAL(std::string("group2"), kids[0]->getDisplayName()); + CPPUNIT_ASSERT_EQUAL(std::string("group1"), kids[1]->getDisplayName()); + } + private: Roster *roster_; JID jid1_; 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..326660f 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.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. */ @@ -17,6 +17,7 @@ #include "SwifTools/TabComplete.h" +#include <QLabel> #include <QApplication> #include <QBoxLayout> #include <QCloseEvent> @@ -35,12 +36,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); @@ -69,10 +71,18 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt labelsWidget_->setSizeAdjustPolicy(QComboBox::AdjustToContents); midBarLayout->addWidget(labelsWidget_,0); + QWidget* inputBar = new QWidget(this); + layout->addWidget(inputBar); + QHBoxLayout* inputBarLayout = new QHBoxLayout(inputBar); + inputBarLayout->setContentsMargins(0,0,0,0); + inputBarLayout->setSpacing(2); input_ = new QtTextEdit(this); input_->setAcceptRichText(false); - layout->addWidget(input_); - + inputBarLayout->addWidget(input_); + correctingLabel_ = new QLabel(tr("Correcting"), this); + inputBarLayout->addWidget(correctingLabel_); + correctingLabel_->hide(); + inputClearing_ = false; contactIsTyping_ = false; @@ -118,11 +128,33 @@ 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())) { + beginCorrection(); + } else if (key == Qt::Key_Down && isCorrection_ && input_->textCursor().atBlockEnd()) { + cancelCorrection(); } else { messageLog_->handleKeyPressEvent(event); } } +void QtChatWindow::beginCorrection() { + QTextCursor cursor = input_->textCursor(); + cursor.select(QTextCursor::Document); + cursor.beginEditBlock(); + cursor.insertText(QString(lastSentMessage_)); + cursor.endEditBlock(); + isCorrection_ = true; + correctingLabel_->show(); +} + +void QtChatWindow::cancelCorrection() { + QTextCursor cursor = input_->textCursor(); + cursor.select(QTextCursor::Document); + cursor.removeSelectedText(); + isCorrection_ = false; + correctingLabel_->hide(); +} + void QtChatWindow::tabComplete() { if (!completer_) { return; @@ -150,7 +182,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 +236,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 +271,7 @@ void QtChatWindow::setContactChatState(ChatState::ChatStateType state) { QtTabbable::AlertType QtChatWindow::getWidgetAlertState() { if (contactIsTyping_) { return ImpendingActivity; - } + } if (unreadCount_ > 0) { return WaitingActivity; } @@ -265,7 +300,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 +315,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 +383,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,9 +413,11 @@ void QtChatWindow::returnPressed() { return; } messageLog_->scrollToBottom(); - onSendMessageRequest(Q2PSTRING(input_->toPlainText())); + lastSentMessage_ = QString(input_->toPlainText()); + onSendMessageRequest(Q2PSTRING(input_->toPlainText()), isCorrection_); inputClearing_ = true; input_->clear(); + cancelCorrection(); inputClearing_ = false; } @@ -382,7 +433,9 @@ void QtChatWindow::handleInputChanged() { } void QtChatWindow::show() { - QWidget::show(); + if (parentWidget() == NULL) { + QWidget::show(); + } emit windowOpening(); } @@ -399,7 +452,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..a95041e 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,21 +1,23 @@ /* - * 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_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; class QLineEdit; class QComboBox; +class QLabel; namespace Swift { class QtChatView; @@ -35,6 +37,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); @@ -75,18 +78,24 @@ namespace Swift { private: void updateTitleWithUnreadCount(); void tabComplete(); + void beginCorrection(); + void cancelCorrection(); std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time); int unreadCount_; bool contactIsTyping_; + LastLineTracker lastLineTracker_; QString contact_; + QString lastSentMessage_; QtChatView* messageLog_; QtChatTheme* theme_; QtTextEdit* input_; QComboBox* labelsWidget_; QtTreeWidget* treeWidget_; + QLabel* correctingLabel_; TabComplete* completer_; std::vector<SecurityLabelsCatalog::Item> availableLabels_; + bool isCorrection_; bool previousMessageWasSelf_; bool previousMessageWasSystem_; bool previousMessageWasPresence_; @@ -97,5 +106,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..3a62325 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,25 @@ 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) ) { emit unhandledKeyPressEvent(event); - } else { + } + else if ((key == Qt::Key_Up) + || (key == Qt::Key_Down) + ){ + emit unhandledKeyPressEvent(event); + QTextEdit::keyPressEvent(event); + } + 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/LinkLo |