diff --git a/TODO.txt b/TODO.txt index 653c4cb8e..72a315ff8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -71,6 +71,7 @@ Chat E [ ] add flags to allow distant chat from contact list only / everyone / noone / only signed ids. Libretroshare + E [ ] groups small packets in pqistreamer::handleoutgoing_locked(), and see if that removes some padding overhead E [ ] make sure at least one location is kept when removing old locations as it avoids lots of connection problems. M [ ] improve serialisation system. Lots of serialisation tasks (header, verifications, serialiser=>template, can be factored) M [ ] separate chat stuff from rsmsgs.h into rschat.h diff --git a/libbitdht/src/udp/udplayer.cc b/libbitdht/src/udp/udplayer.cc index 4a37abf16..df0ce8c84 100644 --- a/libbitdht/src/udp/udplayer.cc +++ b/libbitdht/src/udp/udplayer.cc @@ -32,7 +32,9 @@ #include #include #include +#ifndef WIN32 #include +#endif /*** * #define UDP_ENABLE_BROADCAST 1 diff --git a/libresapi/src/api/ApiPluginHandler.cpp b/libresapi/src/api/ApiPluginHandler.cpp new file mode 100644 index 000000000..fe3211eac --- /dev/null +++ b/libresapi/src/api/ApiPluginHandler.cpp @@ -0,0 +1,41 @@ +#include "ApiPluginHandler.h" + +namespace resource_api +{ + +ApiPluginHandler::ApiPluginHandler(StateTokenServer* statetokenserver, const RsPlugInInterfaces& ifaces) +{ + for(int i = 0; i < ifaces.mPluginHandler->nbPlugins(); i++) + { + RsPlugin* plugin = ifaces.mPluginHandler->plugin(i); + // if plugin is not loaded, pointer is null + if(plugin == 0) + continue; + std::string entrypoint; + ResourceRouter* child = plugin->new_resource_api_handler(ifaces, statetokenserver, entrypoint); + if(child != 0) + { + mChildren.push_back(child); + if(isNameUsed(entrypoint)) + { + std::cerr << "Cannot add plugin api entry point with name=" << entrypoint << ", becaus ethis name is already in use!" << std::endl; + } + else + { + std::cerr << "Added libresapi plugin with entrypoint " << entrypoint << std::endl; + addResourceHandler(entrypoint, child, &ResourceRouter::handleRequest); + } + } + } +} + +ApiPluginHandler::~ApiPluginHandler() +{ + for(std::vector::iterator vit = mChildren.begin(); vit != mChildren.end(); ++vit) + { + delete *vit; + } + mChildren.clear(); +} + +} // namespace resource_api diff --git a/libresapi/src/api/ApiPluginHandler.h b/libresapi/src/api/ApiPluginHandler.h new file mode 100644 index 000000000..ad29922f6 --- /dev/null +++ b/libresapi/src/api/ApiPluginHandler.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ResourceRouter.h" +#include + +namespace resource_api +{ + +// forwards all incoming requests to retroshare plugins +class ApiPluginHandler: public ResourceRouter +{ +public: + ApiPluginHandler(StateTokenServer* statetokenserver, const RsPlugInInterfaces& ifaces); + virtual ~ApiPluginHandler(); + +private: + std::vector mChildren; +}; + +} // namespace resource_api diff --git a/libresapi/src/api/ApiServer.cpp b/libresapi/src/api/ApiServer.cpp index 842308e2b..fccc3cc72 100644 --- a/libresapi/src/api/ApiServer.cpp +++ b/libresapi/src/api/ApiServer.cpp @@ -13,6 +13,8 @@ #include "JsonStream.h" #include "StateTokenServer.h" // for the state token serialisers +#include "ApiPluginHandler.h" + /* data types in json http://json.org/ string (utf-8 unicode) @@ -226,10 +228,11 @@ public: mPeersHandler(sts, ifaces.mNotify, ifaces.mPeers, ifaces.mMsgs), mIdentityHandler(ifaces.mIdentity), mForumHandler(ifaces.mGxsForums), - mServiceControlHandler(rsServiceControl), // TODO: don't use global variable here + mServiceControlHandler(ifaces.mServiceControl), mFileSearchHandler(sts, ifaces.mNotify, ifaces.mTurtle, ifaces.mFiles), mTransfersHandler(sts, ifaces.mFiles), - mChatHandler(sts, ifaces.mNotify, ifaces.mMsgs, ifaces.mPeers, ifaces.mIdentity, &mPeersHandler) + mChatHandler(sts, ifaces.mNotify, ifaces.mMsgs, ifaces.mPeers, ifaces.mIdentity, &mPeersHandler), + mApiPluginHandler(sts, ifaces) { // the dynamic cast is to not confuse the addResourceHandler template like this: // addResourceHandler(derived class, parent class) @@ -249,6 +252,8 @@ public: &TransfersHandler::handleRequest); router.addResourceHandler("chat", dynamic_cast(&mChatHandler), &ChatHandler::handleRequest); + router.addResourceHandler("apiplugin", dynamic_cast(&mApiPluginHandler), + &ChatHandler::handleRequest); } PeersHandler mPeersHandler; @@ -258,6 +263,7 @@ public: FileSearchHandler mFileSearchHandler; TransfersHandler mTransfersHandler; ChatHandler mChatHandler; + ApiPluginHandler mApiPluginHandler; }; ApiServer::ApiServer(): diff --git a/libresapi/src/api/ChatHandler.cpp b/libresapi/src/api/ChatHandler.cpp index b9f7bda4b..4a40bb38c 100644 --- a/libresapi/src/api/ChatHandler.cpp +++ b/libresapi/src/api/ChatHandler.cpp @@ -173,10 +173,8 @@ void ChatHandler::tick() } } - ChatId id = ChatId::makeBroadcastId(); { Lobby l; - l.id = id.toLobbyId(); l.name = "BroadCast"; l.topic = "Retroshare broadcast chat: messages are sent to all connected friends."; l.subscribed = true; diff --git a/libresapi/src/api/GetPluginInterfaces.cpp b/libresapi/src/api/GetPluginInterfaces.cpp index 74359d3d2..f2bac46b0 100644 --- a/libresapi/src/api/GetPluginInterfaces.cpp +++ b/libresapi/src/api/GetPluginInterfaces.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,8 @@ bool getPluginInterfaces(RsPlugInInterfaces& interfaces) interfaces.mDisc = rsDisc; interfaces.mDht = rsDht; interfaces.mNotify = rsNotify; + interfaces.mServiceControl = rsServiceControl; + interfaces.mPluginHandler = rsPlugins; // gxs interfaces.mGxsDir = ""; diff --git a/libresapi/src/api/ResourceRouter.cpp b/libresapi/src/api/ResourceRouter.cpp index fc4a0b72b..67898c3a8 100644 --- a/libresapi/src/api/ResourceRouter.cpp +++ b/libresapi/src/api/ResourceRouter.cpp @@ -55,4 +55,17 @@ ResponseTask* ResourceRouter::handleRequest(Request& req, Response& resp) return 0; } +bool ResourceRouter::isNameUsed(std::string name) +{ + std::vector >::iterator vit; + for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++) + { + if(vit->first == name) + { + return true; + } + } + return false; +} + } // namespace resource_api diff --git a/libresapi/src/api/ResourceRouter.h b/libresapi/src/api/ResourceRouter.h index 2e4879be3..2bb5930a3 100644 --- a/libresapi/src/api/ResourceRouter.h +++ b/libresapi/src/api/ResourceRouter.h @@ -1,6 +1,7 @@ #pragma once #include "ApiTypes.h" +#include namespace resource_api { @@ -20,6 +21,8 @@ public: void addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp)); template void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp)); + + bool isNameUsed(std::string name); private: class HandlerBase { @@ -61,6 +64,10 @@ private: template void ResourceRouter::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp)) { + if(isNameUsed(name)) + { + std::cerr << "ResourceRouter::addResourceHandler ERROR: name=" << name << " alerady in use. Not adding new Handler!" << std::endl; + } Handler* handler = new Handler(); handler->instance = instance; handler->method = callback; @@ -69,6 +76,10 @@ void ResourceRouter::addResourceHandler(std::string name, T* instance, ResponseT template void ResourceRouter::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp)) { + if(isNameUsed(name)) + { + std::cerr << "ResourceRouter::addResourceHandler ERROR: name=" << name << " alerady in use. Not adding new Handler!" << std::endl; + } InstantResponseHandler* handler = new InstantResponseHandler(); handler->instance = instance; handler->method = callback; diff --git a/libresapi/src/libresapi.pro b/libresapi/src/libresapi.pro index 80e4650ae..ed135aad1 100644 --- a/libresapi/src/libresapi.pro +++ b/libresapi/src/libresapi.pro @@ -1,92 +1,94 @@ -!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") - -TEMPLATE = lib -CONFIG += staticlib -CONFIG += create_prl -CONFIG -= qt -TARGET = resapi -TARGET_PRL = libresapi -DESTDIR = lib - -CONFIG += libmicrohttpd - -INCLUDEPATH += ../../libretroshare/src - -unix { - webui_files.path = "$${DATA_DIR}/webui" - webui_files.files = webfiles/* - INSTALLS += webui_files - - webui_img_files.path = "$${DATA_DIR}/webui/img" - webui_img_files.files = ../../retroshare-gui/src/gui/images/logo/logo_splash.png - INSTALLS += webui_img_files -} - -win32{ - DEFINES *= WINDOWS_SYS - INCLUDEPATH += . $$INC_DIR -} - -libmicrohttpd{ - linux { - CONFIG += link_pkgconfig - PKGCONFIG *= libmicrohttpd - } else { - mac { - INCLUDEPATH += /usr/local/include - LIBS *= /usr/local/lib/libmicrohttpd.a - } else { - LIBS *= -lmicrohttpd - } - } - SOURCES += \ - api/ApiServerMHD.cpp - - HEADERS += \ - api/ApiServerMHD.h -} - -SOURCES += \ - api/ApiServer.cpp \ - api/json.cpp \ - api/JsonStream.cpp \ - api/ResourceRouter.cpp \ - api/PeersHandler.cpp \ - api/Operators.cpp \ - api/IdentityHandler.cpp \ - api/ForumHandler.cpp \ - api/ServiceControlHandler.cpp \ - api/StateTokenServer.cpp \ - api/GxsResponseTask.cpp \ - api/FileSearchHandler.cpp \ - api/TransfersHandler.cpp \ - api/RsControlModule.cpp \ - api/GetPluginInterfaces.cpp \ - api/ChatHandler.cpp \ - api/LivereloadHandler.cpp \ - api/TmpBlobStore.cpp \ - util/ContentTypes.cpp - -HEADERS += \ - api/ApiServer.h \ - api/json.h \ - api/JsonStream.h \ - api/ApiTypes.h \ - api/ResourceRouter.h \ - api/PeersHandler.h \ - api/Operators.h \ - api/IdentityHandler.h \ - api/ForumHandler.h \ - api/ServiceControlHandler.h \ - api/GxsMetaOperators.h \ - api/StateTokenServer.h \ - api/GxsResponseTask.h \ - api/Pagination.h \ - api/FileSearchHandler.h \ - api/TransfersHandler.h \ - api/RsControlModule.h \ - api/GetPluginInterfaces.h \ - api/ChatHandler.h \ - api/LivereloadHandler.h \ - api/TmpBlobStore.h \ - util/ContentTypes.h +!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") + +TEMPLATE = lib +CONFIG += staticlib +CONFIG += create_prl +CONFIG -= qt +TARGET = resapi +TARGET_PRL = libresapi +DESTDIR = lib + +CONFIG += libmicrohttpd + +INCLUDEPATH += ../../libretroshare/src + +unix { + webui_files.path = "$${DATA_DIR}/webui" + webui_files.files = webfiles/* + INSTALLS += webui_files + + webui_img_files.path = "$${DATA_DIR}/webui/img" + webui_img_files.files = ../../retroshare-gui/src/gui/images/logo/logo_splash.png + INSTALLS += webui_img_files +} + +win32{ + DEFINES *= WINDOWS_SYS + INCLUDEPATH += . $$INC_DIR +} + +libmicrohttpd{ + linux { + CONFIG += link_pkgconfig + PKGCONFIG *= libmicrohttpd + } else { + mac { + INCLUDEPATH += /usr/local/include + LIBS *= /usr/local/lib/libmicrohttpd.a + } else { + LIBS *= -lmicrohttpd + } + } + SOURCES += \ + api/ApiServerMHD.cpp + + HEADERS += \ + api/ApiServerMHD.h +} + +SOURCES += \ + api/ApiServer.cpp \ + api/json.cpp \ + api/JsonStream.cpp \ + api/ResourceRouter.cpp \ + api/PeersHandler.cpp \ + api/Operators.cpp \ + api/IdentityHandler.cpp \ + api/ForumHandler.cpp \ + api/ServiceControlHandler.cpp \ + api/StateTokenServer.cpp \ + api/GxsResponseTask.cpp \ + api/FileSearchHandler.cpp \ + api/TransfersHandler.cpp \ + api/RsControlModule.cpp \ + api/GetPluginInterfaces.cpp \ + api/ChatHandler.cpp \ + api/LivereloadHandler.cpp \ + api/TmpBlobStore.cpp \ + util/ContentTypes.cpp \ + api/ApiPluginHandler.cpp + +HEADERS += \ + api/ApiServer.h \ + api/json.h \ + api/JsonStream.h \ + api/ApiTypes.h \ + api/ResourceRouter.h \ + api/PeersHandler.h \ + api/Operators.h \ + api/IdentityHandler.h \ + api/ForumHandler.h \ + api/ServiceControlHandler.h \ + api/GxsMetaOperators.h \ + api/StateTokenServer.h \ + api/GxsResponseTask.h \ + api/Pagination.h \ + api/FileSearchHandler.h \ + api/TransfersHandler.h \ + api/RsControlModule.h \ + api/GetPluginInterfaces.h \ + api/ChatHandler.h \ + api/LivereloadHandler.h \ + api/TmpBlobStore.h \ + util/ContentTypes.h \ + api/ApiPluginHandler.h diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index b7c208b0a..ef1c15f44 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -343,7 +343,6 @@ int RsGxsNetService::tick() mLastCleanRejectedMessages = now ; cleanRejectedMessages() ; } - return 1; } diff --git a/libretroshare/src/retroshare/rsplugin.h b/libretroshare/src/retroshare/rsplugin.h index 83936fe8e..579b7694c 100644 --- a/libretroshare/src/retroshare/rsplugin.h +++ b/libretroshare/src/retroshare/rsplugin.h @@ -49,6 +49,7 @@ class RsMsgs ; class RsGxsForums; class RsGxsChannels; class RsNotify; +class RsServiceControl; class p3LinkMgr ; class MainPage ; class QIcon ; @@ -75,6 +76,12 @@ class RsGcxs; class PgpAuxUtils; class p3Config; +namespace resource_api +{ + class ResourceRouter; + class StateTokenServer; +} + // Plugin API version. Not used yet, but will be in the future the // main value that decides for compatibility. // @@ -108,6 +115,8 @@ public: RsUtil::inited_ptr mDisc; RsUtil::inited_ptr mDht; RsUtil::inited_ptr mNotify; + RsUtil::inited_ptr mServiceControl; + RsUtil::inited_ptr mPluginHandler; // gxs std::string mGxsDir; @@ -147,6 +156,12 @@ class RsPlugin virtual p3Config *p3_config() const { return NULL ; } virtual uint16_t rs_service_id() const { return 0 ; } + + // creates a new resource api handler object. ownership is transferred to the caller. + // the caller should supply a statetokenserver, and keep it valid until destruction + // the plugin should return a entry point name. this is to make the entry point name independent from file names + virtual resource_api::ResourceRouter* new_resource_api_handler(const RsPlugInInterfaces& ifaces, resource_api::StateTokenServer* sts, std::string &entrypoint) const { return 0;} + // Shutdown virtual void stop() {} diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 129054fea..e9f6f3161 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -1523,6 +1523,8 @@ int RsServer::StartupRetroShare() interfaces.mDisc = rsDisc; interfaces.mDht = rsDht; interfaces.mNotify = mNotify; + interfaces.mServiceControl = serviceCtrl; + interfaces.mPluginHandler = mPluginsManager; // gxs interfaces.mGxsDir = currGxsDir; interfaces.mIdentity = mGxsIdService; diff --git a/retroshare-gui/src/gui/SearchDialog.cpp b/retroshare-gui/src/gui/SearchDialog.cpp index 553912f99..cdd853c84 100644 --- a/retroshare-gui/src/gui/SearchDialog.cpp +++ b/retroshare-gui/src/gui/SearchDialog.cpp @@ -300,16 +300,16 @@ void SearchDialog::initialiseFileTypeMappings() SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_AUDIO, "aac aif flac iff m3u m4a mid midi mp3 mpa ogg ra ram wav wma"); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_ARCHIVE, - "7z bz2 gz pkg rar sea sit sitx tar zip"); + "7z bz2 gz pkg rar sea sit sitx tar zip tgz"); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_CDIMAGE, - "iso nrg mdf"); + "iso nrg mdf bin"); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_DOCUMENT, - "doc odt ott rtf pdf ps txt log msg wpd wps" ); + "doc odt ott rtf pdf ps txt log msg wpd wps ods xls epub" ); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PICTURE, "3dm 3dmf ai bmp drw dxf eps gif ico indd jpe jpeg jpg mng pcx pcc pct pgm " "pix png psd psp qxd qxprgb sgi svg tga tif tiff xbm xcf"); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PROGRAM, - "app bat cgi com bin exe js pif py pl sh vb ws "); + "app bat cgi com bin exe js pif py pl sh vb ws bash"); SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_VIDEO, "3gp asf asx avi mov mp4 mkv flv mpeg mpg qt rm swf vob wmv"); SearchDialog::initialised = true; diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp index e87120c4a..d2fcc4a20 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp @@ -53,6 +53,8 @@ #define COLUMN_ID 3 #define COLUMN_COUNT 4 +#define ROLE_SORT Qt::UserRole + 1 + const static uint32_t timeToInactivity = 60 * 10; // in seconds /** Default constructor */ @@ -79,10 +81,25 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi muteAct = new QAction(QIcon(), tr("Mute participant"), this); distantChatAct = new QAction(QIcon(":/images/chat_24.png"), tr("Start private chat"), this); sendMessageAct = new QAction(QIcon(":/images/mail_new.png"), tr("Send Message"), this); + + QActionGroup *sortgrp = new QActionGroup(this); + actionSortByName = new QAction(QIcon(), tr("Sort by Name"), this); + actionSortByName->setCheckable(true); + actionSortByName->setChecked(true); + actionSortByName->setActionGroup(sortgrp); + + actionSortByActivity = new QAction(QIcon(), tr("Sort by Activity"), this); + actionSortByActivity->setCheckable(true); + actionSortByActivity->setChecked(false); + actionSortByActivity->setActionGroup(sortgrp); + connect(muteAct, SIGNAL(triggered()), this, SLOT(changePartipationState())); connect(distantChatAct, SIGNAL(triggered()), this, SLOT(distantChatParticipant())); connect(sendMessageAct, SIGNAL(triggered()), this, SLOT(sendMessage())); + + connect(actionSortByName, SIGNAL(triggered()), this, SLOT(sortParcipants())); + connect(actionSortByActivity, SIGNAL(triggered()), this, SLOT(sortParcipants())); // Add a button to invite friends. // @@ -94,7 +111,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi inviteFriendsButton->setToolTip(tr("Invite friends to this lobby")); mParticipantCompareRole = new RSTreeWidgetItemCompareRole; - mParticipantCompareRole->setRole(0, Qt::UserRole); + mParticipantCompareRole->setRole(COLUMN_ACTIVITY, ROLE_SORT); { QIcon icon ; @@ -179,6 +196,9 @@ void ChatLobbyDialog::participantsTreeWidgetCustomPopupMenu(QPoint) contextMnu.addAction(sendMessageAct); contextMnu.addSeparator(); contextMnu.addAction(muteAct); + contextMnu.addSeparator(); + contextMnu.addAction(actionSortByActivity); + contextMnu.addAction(actionSortByName); muteAct->setCheckable(true); @@ -450,6 +470,9 @@ void ChatLobbyDialog::updateParticipantsList() time_t tLastAct=widgetitem->text(COLUMN_ACTIVITY).toInt(); time_t now = time(NULL); + + widgetitem->setSizeHint(COLUMN_ICON, QSize(20,20)); + if(isParticipantMuted(it2->first)) widgetitem->setIcon(COLUMN_ICON, QIcon(":/icons/bullet_red_128.png")); @@ -472,7 +495,7 @@ void ChatLobbyDialog::updateParticipantsList() } } ui.participantsList->setSortingEnabled(true); - ui.participantsList->sortItems(COLUMN_NAME, Qt::AscendingOrder); + sortParcipants(); } /** @@ -762,3 +785,14 @@ void ChatLobbyDialog::showDialog(uint chatflags) dynamic_cast(MainWindow::getPage(MainWindow::ChatLobby))->setCurrentChatPage(this) ; } } + +void ChatLobbyDialog::sortParcipants() +{ + + if (actionSortByActivity->isChecked()) { + ui.participantsList->sortItems(COLUMN_ACTIVITY, Qt::DescendingOrder); + } else if (actionSortByName->isChecked()) { + ui.participantsList->sortItems(COLUMN_NAME, Qt::AscendingOrder); + } + +} diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h index a11a1d73b..3ab5c8a68 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h @@ -47,6 +47,7 @@ public: void setIdentity(const RsGxsId& gxs_id); bool isParticipantMuted(const RsGxsId &participant); ChatLobbyId id() const { return lobbyId ;} + void sortParcipants(); private slots: void participantsTreeWidgetCustomPopupMenu( QPoint point ); @@ -104,6 +105,8 @@ private: QAction *muteAct; QAction *distantChatAct; + QAction *actionSortByName; + QAction *actionSortByActivity; QWidgetAction *checkableAction; QAction *sendMessageAct;