diff --git a/libretroshare/src/retroshare/rschannels.h b/libretroshare/src/retroshare/rschannels.h index 13160cdd6..78ff1c312 100644 --- a/libretroshare/src/retroshare/rschannels.h +++ b/libretroshare/src/retroshare/rschannels.h @@ -61,6 +61,7 @@ class ChannelInfo uint32_t pngImageLen; time_t lastPost; + std::string destination_directory ; }; //! for storing a channel msgs thumbnail picture @@ -263,6 +264,10 @@ virtual void getPubKeysAvailableGrpIds(std::list& chanIds) = 0; */ virtual bool channelSetAutoDl(const std::string& chId, bool autoDl) = 0; +// sets the defautl destination directory for files downloaded in this channel. +// Default is "" which means Downloads/ + +virtual bool channelSetDestinationDirectory(const std::string& cid,const std::string& dir) = 0 ; /*! * get what autoDl is set to for the given channel id diff --git a/libretroshare/src/serialiser/rschannelitems.cc b/libretroshare/src/serialiser/rschannelitems.cc index 659f817ce..b9cd79f70 100644 --- a/libretroshare/src/serialiser/rschannelitems.cc +++ b/libretroshare/src/serialiser/rschannelitems.cc @@ -85,7 +85,23 @@ void RsChannelReadStatus::clear() return; } +std::ostream& RsChannelDestDirConfigItem::print(std::ostream &out, uint16_t indent = 0) +{ + printRsItemBase(out, "RsChannelDestDirConfigItem", indent); + uint16_t int_Indent = indent + 2; + + RsDistribChildConfig::print(out, int_Indent); + + for(uint32_t i=0;idest_dirs.size();++i) + { + s += GetTlvStringSize(item->dest_dirs[i].first) ; + s += GetTlvStringSize(item->dest_dirs[i].second) ; + } + + return s; +} uint32_t RsChannelSerialiser::sizeReadStatus(RsChannelReadStatus *item) { uint32_t s = 8; /* header */ @@ -279,6 +314,54 @@ uint32_t RsChannelSerialiser::sizeReadStatus(RsChannelReadStatus *item) return s; } +/* serialise the data to the buffer */ +bool RsChannelSerialiser::serialiseDestDirConfig(RsChannelDestDirConfigItem *item, void *data, uint32_t *pktsize) +{ + uint32_t tlvsize = sizeDestDirConfig(item); + uint32_t offset = 0; + + if (*pktsize < tlvsize) + return false; /* not enough space */ + + *pktsize = tlvsize; + + bool ok = true; + + ok &= setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize); +#ifdef RSSERIAL_DEBUG + std::cerr << "RsChannelSerialiser::serialiseDestDirConfig() Header: " << ok << std::endl; + std::cerr << "RsChannelSerialiser::serialiseDestDirConfig() Size: " << tlvsize << std::endl; +#endif + + /* skip the header */ + offset += 8; + + /* RsDistribMsg first */ + + ok &= setRawUInt32(data, tlvsize, &offset, item->save_type); +#ifdef RSSERIAL_DEBUG + std::cerr << "RsChannelSerialiser::serialiseDestDirConfig() save_type: " << ok << std::endl; +#endif + ok &= setRawUInt32(data, tlvsize, &offset, item->dest_dirs.size()); /* value */ + + for(uint32_t i=0;idest_dirs.size();++i) + { + ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GROUPID, item->dest_dirs[i].first) ; + ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PATH, item->dest_dirs[i].second) ; + } + +#ifdef RSSERIAL_DEBUG + std::cerr << "RsChannelSerialiser::serialiseDestDirConfig() msgReadStatus: " << ok << std::endl; +#endif + + if (offset != tlvsize) + { + ok = false; + std::cerr << "RsChannelSerialiser::serialiseDestDirConfig() Size Error! " << std::endl; + } + + return ok; +} /* serialise the data to the buffer */ bool RsChannelSerialiser::serialiseReadStatus(RsChannelReadStatus *item, void *data, uint32_t *pktsize) { @@ -337,7 +420,63 @@ bool RsChannelSerialiser::serialiseReadStatus(RsChannelReadStatus *item, voi return ok; } +RsChannelDestDirConfigItem *RsChannelSerialiser::deserialiseDestDirConfig(void *data, uint32_t *pktsize) +{ + /* get the type and size */ + uint32_t rstype = getRsItemId(data); + uint32_t rssize = getRsItemSize(data); + uint32_t offset = 0; + + + if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || (RS_SERVICE_TYPE_CHANNEL != getRsItemService(rstype)) || (RS_PKT_SUBTYPE_CHANNEL_DEST_DIR != getRsItemSubType(rstype))) + return NULL; /* wrong type */ + + if (*pktsize < rssize) /* check size */ + return NULL; /* not enough data */ + + /* set the packet length */ + *pktsize = rssize; + + bool ok = true; + + /* ready to load */ + RsChannelDestDirConfigItem *item = new RsChannelDestDirConfigItem(); + item->clear(); + + /* skip the header */ + offset += 8; + + /* RsDistribMsg first */ + ok &= getRawUInt32(data, rssize, &offset, &(item->save_type)); + + uint32_t size ; + ok &= getRawUInt32(data, rssize, &offset, &size) ; + + for(uint32_t i=0;idest_dirs.push_back(std::pair(chid,path)) ; + } + + if(offset != rssize) + { + ok = false; + std::cerr << "RsChannelSerialiser::deserialiseDestDirConfig() Size Error! " << std::endl; + } + + if (!ok) + { + delete item; + return NULL; + } + + return item; +} RsChannelReadStatus *RsChannelSerialiser::deserialiseReadStatus(void *data, uint32_t *pktsize) { @@ -413,6 +552,7 @@ uint32_t RsChannelSerialiser::size(RsItem *item) { RsChannelMsg* dcm; RsChannelReadStatus* drs; + RsChannelDestDirConfigItem* dd; if( NULL != ( dcm = dynamic_cast(item))) { @@ -422,6 +562,10 @@ uint32_t RsChannelSerialiser::size(RsItem *item) { return sizeReadStatus(drs); } + else if(NULL != (dd = dynamic_cast(item))) + { + return sizeDestDirConfig(dd); + } return false; } @@ -430,6 +574,7 @@ bool RsChannelSerialiser::serialise(RsItem *item, void *data, uint32_t *pkts { RsChannelMsg* dcm; RsChannelReadStatus* drs; + RsChannelDestDirConfigItem* dd; if( NULL != ( dcm = dynamic_cast(item))) { @@ -439,12 +584,19 @@ bool RsChannelSerialiser::serialise(RsItem *item, void *data, uint32_t *pkts { return serialiseReadStatus(drs, data, pktsize); } + else if(NULL != (dd = dynamic_cast(item))) + { + return serialiseDestDirConfig(dd, data, pktsize); + } return false; } RsItem *RsChannelSerialiser::deserialise(void *data, uint32_t *pktsize) { + if(data == NULL) + return NULL ; + /* get the type and size */ uint32_t rstype = getRsItemId(data); @@ -460,6 +612,8 @@ RsItem *RsChannelSerialiser::deserialise(void *data, uint32_t *pktsize) return deserialiseMsg(data, pktsize); case RS_PKT_SUBTYPE_CHANNEL_READ_STATUS: return deserialiseReadStatus(data, pktsize); + case RS_PKT_SUBTYPE_CHANNEL_DEST_DIR: + return deserialiseDestDirConfig(data, pktsize); default: return NULL; } diff --git a/libretroshare/src/serialiser/rschannelitems.h b/libretroshare/src/serialiser/rschannelitems.h index 726fed7a0..3eb20d5e9 100644 --- a/libretroshare/src/serialiser/rschannelitems.h +++ b/libretroshare/src/serialiser/rschannelitems.h @@ -37,6 +37,7 @@ const uint8_t RS_PKT_SUBTYPE_CHANNEL_MSG = 0x01; const uint8_t RS_PKT_SUBTYPE_CHANNEL_READ_STATUS = 0x02; +const uint8_t RS_PKT_SUBTYPE_CHANNEL_DEST_DIR = 0x03; /**************************************************************************/ @@ -85,8 +86,25 @@ public: /// a map which contains the read for messages within a forum std::map msgReadStatus; + std::string destination_directory ; }; +/*! + * This is used to store the destination directories of each channel + */ +class RsChannelDestDirConfigItem : public RsDistribChildConfig +{ +public: + RsChannelDestDirConfigItem() + : RsDistribChildConfig(RS_SERVICE_TYPE_CHANNEL, RS_PKT_SUBTYPE_CHANNEL_DEST_DIR) + { return; } + virtual ~RsChannelDestDirConfigItem() {} + + virtual void clear() { dest_dirs.clear() ; } + virtual std::ostream& print(std::ostream &out, uint16_t indent); + + std::vector > dest_dirs; +}; class RsChannelSerialiser: public RsSerialType { public: @@ -111,6 +129,9 @@ virtual uint32_t sizeReadStatus(RsChannelReadStatus* ); virtual bool serialiseReadStatus(RsChannelReadStatus* item, void* data, uint32_t *size); virtual RsChannelReadStatus *deserialiseReadStatus(void* data, uint32_t *size); +virtual uint32_t sizeDestDirConfig(RsChannelDestDirConfigItem* ); +virtual bool serialiseDestDirConfig(RsChannelDestDirConfigItem* item, void* data, uint32_t *size); +virtual RsChannelDestDirConfigItem *deserialiseDestDirConfig(void* data, uint32_t *size); }; /**************************************************************************/ diff --git a/libretroshare/src/services/p3channels.cc b/libretroshare/src/services/p3channels.cc index 34348d2b1..8bcbf4b1f 100644 --- a/libretroshare/src/services/p3channels.cc +++ b/libretroshare/src/services/p3channels.cc @@ -117,6 +117,7 @@ bool p3Channels::getChannelInfo(const std::string &cId, ChannelInfo &ci) ci.pop = gi->sources.size(); ci.lastPost = gi->lastPost; + ci.destination_directory = (mDestinationDirectories.find(cId)==mDestinationDirectories.end())?"":(mDestinationDirectories[cId]) ; ci.pngChanImage = gi->grpIcon.pngImageData; @@ -666,6 +667,16 @@ void p3Channels::getPubKeysAvailableGrpIds(std::list& grpIds) } +bool p3Channels::channelSetDestinationDirectory(const std::string& chId, const std::string& dest_dir) +{ + RsStackMutex stack(distribMtx); + + mDestinationDirectories[chId] = dest_dir ; + IndicateConfigChanged() ; + + return true ; +} + bool p3Channels::channelSetAutoDl(const std::string& chId, bool autoDl) { @@ -988,18 +999,28 @@ void p3Channels::locked_notifyGroupChanged(GroupInfo &grp, uint32_t flags, bool bool p3Channels::childLoadList(std::list& configSaves) { RsChannelReadStatus* drs = NULL; - std::list::iterator it; + RsChannelDestDirConfigItem *dd = NULL ; - for(it = configSaves.begin(); it != configSaves.end(); it++) + for(std::list::iterator it = configSaves.begin(); it != configSaves.end(); it++) { if(NULL != (drs = dynamic_cast(*it))) + processChanReadStatus(drs); // don't delete, since it's used later on. + else if(NULL != (dd = dynamic_cast(*it))) { - processChanReadStatus(drs); + mDestinationDirectories.clear() ; + + for(uint32_t i=0;idest_dirs.size();++i) + { + mDestinationDirectories[dd->dest_dirs[i].first] = dd->dest_dirs[i].second ; + + std::cerr << "p3Channels: setDestination directory or ChId " << dd->dest_dirs[i].first << " to " << dd->dest_dirs[i].second <& configSaves) void p3Channels::processChanReadStatus(RsChannelReadStatus* drs) { +// mReadStatus.push_back(drs); - - mReadStatus.push_back(drs); std::string chId = drs->channelId; statMap::iterator sit = drs->msgReadStatus.find(chId); @@ -1030,13 +1050,10 @@ void p3Channels::processChanReadStatus(RsChannelReadStatus* drs) mReadStatus.push_back(drs); saveList.push_back(drs); - - } std::list p3Channels::childSaveList() { - std::list::iterator lit = mReadStatus.begin(); statMap::iterator sit, mit; @@ -1057,7 +1074,17 @@ std::list p3Channels::childSaveList() } } - return saveList; + // Make a copy of saveList and add additional items we want to save. + // + std::list to_return = saveList ; + RsChannelDestDirConfigItem *dd = new RsChannelDestDirConfigItem ; + + for(std::map::const_iterator it(mDestinationDirectories.begin());it!=mDestinationDirectories.end();++it) + dd->dest_dirs.push_back(*it) ; + + to_return.push_back(dd) ; + + return to_return; } /****************************************/ diff --git a/libretroshare/src/services/p3channels.h b/libretroshare/src/services/p3channels.h index 0be306bdd..8ccece181 100644 --- a/libretroshare/src/services/p3channels.h +++ b/libretroshare/src/services/p3channels.h @@ -85,6 +85,7 @@ virtual bool channelEditInfo(const std::string &chId, ChannelInfo &ci); virtual void getPubKeysAvailableGrpIds(std::list& grpIds); virtual bool channelSetAutoDl(const std::string& chId, bool autoDl); virtual bool channelGetAutoDl(const std::string& chId, bool& autoDl); +virtual bool channelSetDestinationDirectory(const std::string& chId,const std::string& dest_dir) ; /***************************************************************************************/ /****************** Event Feedback (Overloaded form p3distrib) *************************/ @@ -123,6 +124,7 @@ void removeChannelReadStatusEntry(const std::string& cId); chanStatMap mMsgReadStatus; statMap mChannelStatus; + std::map mDestinationDirectories ; }; diff --git a/retroshare-gui/src/gui/ChannelFeed.cpp b/retroshare-gui/src/gui/ChannelFeed.cpp index 39dabef4e..f2fbfb843 100644 --- a/retroshare-gui/src/gui/ChannelFeed.cpp +++ b/retroshare-gui/src/gui/ChannelFeed.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -29,6 +30,8 @@ #include #include +#include + #include "ChannelFeed.h" #include "feeds/ChanMsgItem.h" @@ -197,6 +200,51 @@ void ChannelFeed::channelListCustomPopupMenu( QPoint /*point*/ ) contextMnu.addAction( shareKeyAct ); } + if(ci.channelFlags & RS_DISTRIB_SUBSCRIBED) + { + QMenu *directoryMenu = contextMnu.addMenu(QIcon(":/images/folderopen.png"),tr("Set destination directory")) ; + + QAction *specifyDestinationDirectoryAct = new QAction(QIcon(":/images/filefind.png"), tr("Other..."), &contextMnu); + directoryMenu->addAction(specifyDestinationDirectoryAct); + + // Now get the list of existing directories. + + std::list dirs ; + rsFiles->getSharedDirectories(dirs) ; + bool found = false ; + + for(std::list::const_iterator it(dirs.begin());it!=dirs.end();++it) + { + // check for existence of directory name + QFile directory(QString::fromUtf8((*it).filename.c_str())) ; + + if(!directory.exists()) continue ; + if(!(directory.permissions() & QFile::WriteOwner)) continue ; + + QAction *act ; + + if(ci.destination_directory == (*it).filename) + { + act = new QAction(QIcon(":/images/start.png"),QString::fromUtf8((*it).virtualname.c_str()),directoryMenu) ; + found = true ; + } + else + act = new QAction(QString::fromUtf8((*it).virtualname.c_str()),directoryMenu) ; + + act->setData(QString::fromUtf8((*it).filename.c_str())) ; + connect(act,SIGNAL(triggered()),this,SLOT(setDestinationDirectory())) ; + directoryMenu->addAction(act) ; + } + QAction *defaultDestinationDirectoryAct = new QAction(tr("[Default]"), &contextMnu); + defaultDestinationDirectoryAct->setData(QString()) ; + connect(defaultDestinationDirectoryAct,SIGNAL(triggered()),this,SLOT(setDestinationDirectory())) ; + + directoryMenu->addAction(defaultDestinationDirectoryAct); + + if(!found) + defaultDestinationDirectoryAct->setIcon(QIcon(":/images/start.png")) ; + } + if(ci.channelFlags & RS_DISTRIB_SUBSCRIBED) { contextMnu.addAction( unsubscribechannelAct ); @@ -220,6 +268,17 @@ void ChannelFeed::channelListCustomPopupMenu( QPoint /*point*/ ) contextMnu.exec(QCursor::pos()); } +void ChannelFeed::setDestinationDirectory() +{ + ChannelInfo ci; + if (!rsChannels->getChannelInfo(mChannelId, ci)) + return; + + std::string dest_dir(qobject_cast(sender())->data().toString().toUtf8().data()) ; + + std::cerr << "Setting new directory " << dest_dir << " to channel " << mChannelId << std::endl; + rsChannels->channelSetDestinationDirectory(mChannelId,dest_dir) ; +} void ChannelFeed::createChannel() { diff --git a/retroshare-gui/src/gui/ChannelFeed.h b/retroshare-gui/src/gui/ChannelFeed.h index 6ea6ecc5b..4ed710777 100644 --- a/retroshare-gui/src/gui/ChannelFeed.h +++ b/retroshare-gui/src/gui/ChannelFeed.h @@ -79,6 +79,7 @@ private slots: void editChannelDetail(); void shareKey(); void copyChannelLink(); + void setDestinationDirectory(); void channelMsgReadSatusChanged(const QString& channelId, const QString& msgId, int status);