#include #include "../shared/TclUtil.h" #include "../shared/CurrentRequest.h" #include "AlertConnection.h" #include "ServerSigner.h" #include "ScriptDispatcher.h" #include "TclAlertConnectionWrapper.h" // For simplicity there's only one connection. It can follow any number of // strategies, but it can only attach to one server at a time and it can only // log in as one user at a time. That seems sufficient. static AlertConnection *getConnection() { static AlertConnection *connection = new AlertConnection; return connection; } static ServerSigner serverSigner; static std::string getTclMetaDataNamespace(SocketInfo *socketInfo) { return ScriptDispatcher::getSocketTempNS(socketInfo) + "::alert_metadata"; } static std::string getTclMetaDataName(SocketInfo *socketInfo, int64_t id) { return getTclMetaDataNamespace(socketInfo) + "::" + ntoa(id); } static void alertsCallback(AlertConnection::Alerts const &alerts, AlertConnection::MetaData const &metaData, std::string const &callback, SocketInfo *socketInfo, int64_t id) { if (alerts.empty()) { // New metadata. TclList metaDataDescription; metaDataDescription<<"window_name"<runScript(socketInfo, script); } else { // Alerts. // This seems inefficient. // The entire idea of turning it all into a string then back is inefficient // for multiple reasons. Notice how we normally store the data in a // variable and the code in a separate string which can be cached; that's // done for performance reasons. std::string command = "unset -nocomplain ti::alerts_callback_data; " "set ti::alerts_callback_data "; TclList eventDescription; eventDescription<<"alerts"<runScript(socketInfo, command); } } static int alertsListenCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 3) || (objc > 4)) { Tcl_WrongNumArgs(interp, 1, objv, "collaborate callback ?options?"); return TCL_ERROR; } const bool saveToMru = false; // TODO read this from the options SocketInfo *const socketInfo = CurrentRequest::getSocketInfo(); const std::string collaborate = getString(objv[1]); const std::string callback = getString(objv[2]); const int64_t cancelId = getConnection()->listen(collaborate, saveToMru, socketInfo, [=](AlertConnection::Alerts const &alerts, AlertConnection::MetaData const &metaData, int64_t id) { alertsCallback(alerts, metaData, callback, socketInfo, id); }); const std::string result = serverSigner.encodeInt(cancelId); Tcl_SetObjResult(interp, makeTclString(result)); return TCL_OK; } static int alertsLoginCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if ((objc < 2) || (objc > 3)) { Tcl_WrongNumArgs(interp, 1, objv, "username password | user_id"); return TCL_ERROR; } if (objc == 2) { int userId; if (Tcl_GetIntFromObj(interp, objv[1], &userId) != TCL_OK) return TCL_ERROR; getConnection()->loginDirect(userId); } else { // objc == 3 getConnection()->loginProxy(getString(objv[1]), getString(objv[2])); } return TCL_OK; } static int alertsLogoutCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } getConnection()->logOut(); return TCL_OK; } static int alertsCancelCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "id"); return TCL_ERROR; } const int64_t id = serverSigner.decodeInt(getString(objv[1]), AlertConnection::UNUSED_ID); if (id != AlertConnection::UNUSED_ID) { getConnection()->cancel(id, [=](SocketInfo *socketInfo) { // Erase the variable containing the metadata. const std::string script = "unset -nocomplain " + getTclMetaDataName(socketInfo, id); ScriptDispatcher::getPrimary()->runScript(socketInfo, script); }); } return TCL_OK; } void installAlertConnectionCommands(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "ti::alerts_listen", alertsListenCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::alerts_login", alertsLoginCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::alerts_cancel", alertsCancelCmd, NULL, NULL); Tcl_CreateObjCommand(interp, "ti::alerts_logout", alertsLogoutCmd, NULL, NULL); }