|
From: <arn...@us...> - 2006-09-20 09:53:47
|
Revision: 654
http://svn.sourceforge.net/dcplusplus/?rev=654&view=rev
Author: arnetheduck
Date: 2006-09-20 02:53:26 -0700 (Wed, 20 Sep 2006)
Log Message:
-----------
Some minor fixes here and there
Modified Paths:
--------------
dcplusplus/trunk/client/AdcHub.cpp
dcplusplus/trunk/client/AdcHub.h
dcplusplus/trunk/client/ClientManager.cpp
dcplusplus/trunk/client/ClientManager.h
dcplusplus/trunk/client/FinishedManager.cpp
dcplusplus/trunk/client/NmdcHub.cpp
dcplusplus/trunk/client/NmdcHub.h
dcplusplus/trunk/client/QueueManager.cpp
dcplusplus/trunk/client/QueueManager.h
dcplusplus/trunk/client/SearchManager.cpp
dcplusplus/trunk/client/ShareManager.cpp
dcplusplus/trunk/windows/DirectoryListingFrm.cpp
dcplusplus/trunk/windows/SearchFrm.cpp
dcplusplus/trunk/windows/SearchFrm.h
Modified: dcplusplus/trunk/client/AdcHub.cpp
===================================================================
--- dcplusplus/trunk/client/AdcHub.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/AdcHub.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -49,21 +49,21 @@
}
OnlineUser& AdcHub::getUser(const u_int32_t aSID, const CID& aCID) {
- OnlineUser* u = findUser(aSID);
- if(u) {
- return *u;
+ OnlineUser* ou = findUser(aSID);
+ if(ou) {
+ return *ou;
}
User::Ptr p = ClientManager::getInstance()->getUser(aCID);
{
Lock l(cs);
- u = users.insert(make_pair(aSID, new OnlineUser(p, *this, aSID))).first->second;
+ ou = users.insert(make_pair(aSID, new OnlineUser(p, *this, aSID))).first->second;
}
- if(!aCID.isZero())
- ClientManager::getInstance()->putOnline(*u);
- return *u;
+ if(aSID != AdcCommand::HUB_SID)
+ ClientManager::getInstance()->putOnline(*ou);
+ return *ou;
}
OnlineUser* AdcHub::findUser(const u_int32_t aSID) const {
@@ -73,25 +73,35 @@
}
void AdcHub::putUser(const u_int32_t aSID) {
- Lock l(cs);
- SIDIter i = users.find(aSID);
- if(i == users.end())
- return;
+ OnlineUser* ou = 0;
+ {
+ Lock l(cs);
+ SIDIter i = users.find(aSID);
+ if(i == users.end())
+ return;
+ ou = i->second;
+ users.erase(i);
+ }
+
if(aSID != AdcCommand::HUB_SID)
- ClientManager::getInstance()->putOffline(*i->second);
- fire(ClientListener::UserRemoved(), this, *i->second);
- delete i->second;
- users.erase(i);
+ ClientManager::getInstance()->putOffline(*ou);
+
+ fire(ClientListener::UserRemoved(), this, *ou);
+ delete ou;
}
void AdcHub::clearUsers() {
- Lock l(cs);
- for(SIDIter i = users.begin(); i != users.end(); ++i) {
+ SIDMap tmp;
+ {
+ Lock l(cs);
+ users.swap(tmp);
+ }
+
+ for(SIDIter i = tmp.begin(); i != tmp.end(); ++i) {
if(i->first != AdcCommand::HUB_SID)
ClientManager::getInstance()->putOffline(*i->second);
delete i->second;
}
- users.clear();
}
@@ -312,29 +322,30 @@
FavoriteManager::getInstance()->addUserCommand(once ? UserCommand::TYPE_RAW_ONCE : UserCommand::TYPE_RAW, ctx, UserCommand::FLAG_NOSAVE, name, txt, getHubUrl());
}
-void AdcHub::sendUDP(const AdcCommand& cmd) {
- try {
- Socket s;
- s.create(Socket::TYPE_UDP);
-
+void AdcHub::sendUDP(const AdcCommand& cmd) throw() {
+ string command;
+ string ip;
+ short port;
+ {
Lock l(cs);
SIDMap::const_iterator i = users.find(cmd.getTo());
if(i == users.end()) {
dcdebug("AdcHub::sendUDP: invalid user\n");
return;
}
- OnlineUser& u = *i->second;
- string tmp = cmd.toString(u.getUser()->getCID());
- if(u.getIdentity().isUdpActive()) {
- try {
- s.writeTo(u.getIdentity().getIp(), (short)Util::toInt(u.getIdentity().getUdpPort()), tmp);
- } catch(const SocketException& e) {
- dcdebug("AdcHub::sendUDP: write failed: %s\n", e.getError().c_str());
- }
+ OnlineUser& ou = *i->second;
+ if(!ou.getIdentity().isUdpActive()) {
+ return;
}
- } catch(SocketException&) {
- dcdebug("Can't create UDP socket\n");
+ ip = ou.getIdentity().getIp();
+ port = static_cast<short>(Util::toInt(ou.getIdentity().getUdpPort()));
+ command = cmd.toString(ou.getUser()->getCID());
}
+ try {
+ udp.writeTo(ip, port, command);
+ } catch(const SocketException& e) {
+ dcdebug("AdcHub::sendUDP: write failed: %s\n", e.getError().c_str());
+ }
}
void AdcHub::handle(AdcCommand::STA, AdcCommand& c) throw() {
Modified: dcplusplus/trunk/client/AdcHub.h
===================================================================
--- dcplusplus/trunk/client/AdcHub.h 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/AdcHub.h 2006-09-20 09:53:26 UTC (rev 654)
@@ -73,6 +73,7 @@
typedef HASH_MAP<u_int32_t, OnlineUser*> SIDMap;
typedef SIDMap::iterator SIDIter;
+ Socket udp;
SIDMap users;
StringMap lastInfoMap;
mutable CriticalSection cs;
@@ -113,7 +114,7 @@
//Speaker<AdcHubListener>::fire(t, this, c);
}
- void sendUDP(const AdcCommand& cmd);
+ void sendUDP(const AdcCommand& cmd) throw();
virtual void on(Connecting) throw() { fire(ClientListener::Connecting(), this); }
virtual void on(Connected) throw();
Modified: dcplusplus/trunk/client/ClientManager.cpp
===================================================================
--- dcplusplus/trunk/client/ClientManager.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/ClientManager.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -51,6 +51,7 @@
}
c->addListener(this);
+
return c;
}
@@ -309,7 +310,7 @@
u.getClient().send(cmd);
} else {
try {
- s.writeTo(u.getIdentity().getIp(), static_cast<short>(Util::toInt(u.getIdentity().getUdpPort())), cmd.toString(getMe()->getCID()));
+ udp.writeTo(u.getIdentity().getIp(), static_cast<short>(Util::toInt(u.getIdentity().getUdpPort())), cmd.toString(getMe()->getCID()));
} catch(const SocketException&) {
dcdebug("Socket exception sending ADC UDP command\n");
}
@@ -376,7 +377,7 @@
port = 412;
for(SearchResult::Iter i = l.begin(); i != l.end(); ++i) {
SearchResult* sr = *i;
- s.writeTo(ip, port, sr->toSR(*aClient));
+ udp.writeTo(ip, port, sr->toSR(*aClient));
sr->decRef();
}
} catch(const SocketException& /* e */) {
Modified: dcplusplus/trunk/client/ClientManager.h
===================================================================
--- dcplusplus/trunk/client/ClientManager.h 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/ClientManager.h 2006-09-20 09:53:26 UTC (rev 654)
@@ -115,7 +115,7 @@
User::Ptr me;
- Socket s;
+ Socket udp;
string cachedIp;
CID pid;
Modified: dcplusplus/trunk/client/FinishedManager.cpp
===================================================================
--- dcplusplus/trunk/client/FinishedManager.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/FinishedManager.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -23,12 +23,12 @@
#include "ClientManager.h"
FinishedManager::~FinishedManager() throw() {
+ DownloadManager::getInstance()->removeListener(this);
+ UploadManager::getInstance()->removeListener(this);
+
Lock l(cs);
for_each(downloads.begin(), downloads.end(), DeleteFunction());
for_each(uploads.begin(), uploads.end(), DeleteFunction());
- DownloadManager::getInstance()->removeListener(this);
- UploadManager::getInstance()->removeListener(this);
-
}
void FinishedManager::remove(FinishedItem *item, bool upload /* = false */) {
Modified: dcplusplus/trunk/client/NmdcHub.cpp
===================================================================
--- dcplusplus/trunk/client/NmdcHub.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/NmdcHub.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -118,17 +118,17 @@
}
void NmdcHub::putUser(const string& aNick) {
- OnlineUser* u = NULL;
+ OnlineUser* ou = NULL;
{
Lock l(cs);
NickIter i = users.find(aNick);
if(i == users.end())
return;
- u = i->second;
+ ou = i->second;
users.erase(i);
}
- ClientManager::getInstance()->putOffline(*u);
- delete u;
+ ClientManager::getInstance()->putOffline(*ou);
+ delete ou;
}
void NmdcHub::clearUsers() {
@@ -136,8 +136,7 @@
{
Lock l(cs);
- u2 = users;
- users.clear();
+ u2.swap(users);
}
for(NickIter i = u2.begin(); i != u2.end(); ++i) {
@@ -261,33 +260,31 @@
i = j + 1;
- {
- Lock l(cs);
- u_int32_t tick = GET_TICK();
+ u_int32_t tick = GET_TICK();
+ clearFlooders(tick);
- seekers.push_back(make_pair(seeker, tick));
+ seekers.push_back(make_pair(seeker, tick));
- // First, check if it's a flooder
- for(FloodIter fi = flooders.begin(); fi != flooders.end(); ++fi) {
- if(fi->first == seeker) {
- return;
- }
+ // First, check if it's a flooder
+ for(FloodIter fi = flooders.begin(); fi != flooders.end(); ++fi) {
+ if(fi->first == seeker) {
+ return;
}
+ }
- int count = 0;
- for(FloodIter fi = seekers.begin(); fi != seekers.end(); ++fi) {
- if(fi->first == seeker)
- count++;
+ int count = 0;
+ for(FloodIter fi = seekers.begin(); fi != seekers.end(); ++fi) {
+ if(fi->first == seeker)
+ count++;
- if(count > 7) {
- if(seeker.compare(0, 4, "Hub:") == 0)
- fire(ClientListener::SearchFlood(), this, seeker.substr(4));
- else
- fire(ClientListener::SearchFlood(), this, seeker + STRING(NICK_UNKNOWN));
+ if(count > 7) {
+ if(seeker.compare(0, 4, "Hub:") == 0)
+ fire(ClientListener::SearchFlood(), this, seeker.substr(4));
+ else
+ fire(ClientListener::SearchFlood(), this, seeker + STRING(NICK_UNKNOWN));
- flooders.push_back(make_pair(seeker, tick));
- return;
- }
+ flooders.push_back(make_pair(seeker, tick));
+ return;
}
}
@@ -880,6 +877,16 @@
}
}
+void NmdcHub::clearFlooders(u_int32_t aTick) {
+ while(!seekers.empty() && seekers.front().second + (5 * 1000) < aTick) {
+ seekers.pop_front();
+ }
+
+ while(!flooders.empty() && flooders.front().second + (120 * 1000) < aTick) {
+ flooders.pop_front();
+ }
+}
+
// TimerManagerListener
void NmdcHub::on(Second, u_int32_t aTick) throw() {
if(state == STATE_CONNECTED && (getLastActivity() + getReconnDelay() * 1000) < aTick) {
@@ -891,18 +898,6 @@
connect();
}
- {
- Lock l(cs);
-
- while(!seekers.empty() && seekers.front().second + (5 * 1000) < aTick) {
- seekers.pop_front();
- }
-
- while(!flooders.empty() && flooders.front().second + (120 * 1000) < aTick) {
- flooders.pop_front();
- }
- }
-
Client::on(Second(), aTick);
}
Modified: dcplusplus/trunk/client/NmdcHub.h
===================================================================
--- dcplusplus/trunk/client/NmdcHub.h 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/NmdcHub.h 2006-09-20 09:53:26 UTC (rev 654)
@@ -114,6 +114,7 @@
void revConnectToMe(const OnlineUser& aUser);
void myInfo(bool alwaysSend);
void supports(const StringList& feat);
+ void clearFlooders(u_int32_t tick);
void updateFromTag(Identity& id, const string& tag);
Modified: dcplusplus/trunk/client/QueueManager.cpp
===================================================================
--- dcplusplus/trunk/client/QueueManager.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/QueueManager.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -721,18 +721,9 @@
}
}
-void QueueManager::getTargetsBySize(StringList& sl, int64_t aSize, const string& suffix) throw() {
+void QueueManager::getTargets(const TTHValue& tth, StringList& sl) {
Lock l(cs);
QueueItem::List ql;
- fileQueue.find(ql, aSize, suffix);
- for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
- sl.push_back((*i)->getTarget());
- }
-}
-
-void QueueManager::getTargetsByRoot(StringList& sl, const TTHValue& tth) {
- Lock l(cs);
- QueueItem::List ql;
fileQueue.find(ql, tth);
for(QueueItem::Iter i = ql.begin(); i != ql.end(); ++i) {
sl.push_back((*i)->getTarget());
@@ -753,8 +744,8 @@
QueueItem* q = userQueue.getNext(aUser);
- if(q == NULL)
- return NULL;
+ if(!q)
+ return 0;
userQueue.setRunning(q, aUser);
Modified: dcplusplus/trunk/client/QueueManager.h
===================================================================
--- dcplusplus/trunk/client/QueueManager.h 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/QueueManager.h 2006-09-20 09:53:26 UTC (rev 654)
@@ -99,8 +99,7 @@
void setPriority(const string& aTarget, QueueItem::Priority p) throw();
- void getTargetsBySize(StringList& sl, int64_t aSize, const string& suffix) throw();
- void getTargetsByRoot(StringList& sl, const TTHValue& tth);
+ void getTargets(const TTHValue& tth, StringList& sl);
QueueItem::StringMap& lockQueue() throw() { cs.enter(); return fileQueue.getQueue(); } ;
void unlockQueue() throw() { cs.leave(); }
Modified: dcplusplus/trunk/client/SearchManager.cpp
===================================================================
--- dcplusplus/trunk/client/SearchManager.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/SearchManager.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -220,6 +220,7 @@
if( (j = x.rfind(')')) == string::npos) {
return;
}
+
string hubIpPort = x.substr(i, j-i);
string url = ClientManager::getInstance()->findHub(hubIpPort);
@@ -237,9 +238,12 @@
StringList names = ClientManager::getInstance()->getHubNames(user->getCID());
hubName = names.empty() ? STRING(OFFLINE) : Util::toString(names);
}
- if(tth.empty())
+
+ if(tth.empty() && type == SearchResult::TYPE_FILE) {
return;
+ }
+
SearchResult* sr = new SearchResult(user, type, slots, freeSlots, size,
file, hubName, url, remoteIp, TTHValue(tth), Util::emptyString);
fire(SearchManagerListener::SR(), sr);
Modified: dcplusplus/trunk/client/ShareManager.cpp
===================================================================
--- dcplusplus/trunk/client/ShareManager.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/client/ShareManager.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -116,7 +116,6 @@
}
string ShareManager::translateFileName(const string& aFile) throw(ShareException) {
- RLock<> l(cs);
if(aFile == "MyList.DcLst") {
throw ShareException("NMDC-style lists no longer supported, please upgrade your client");
} else if(aFile == DownloadManager::USER_LIST_NAME || aFile == DownloadManager::USER_LIST_NAME_BZ) {
@@ -128,6 +127,8 @@
string file;
+ RLock<> l(cs);
+
// Check for tth root identifier
if(aFile.compare(0, 4, "TTH/") == 0) {
file = translateTTH(aFile.substr(4));
@@ -141,7 +142,6 @@
if(i == string::npos)
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
- RLock<> l(cs);
StringPairIter j = lookupVirtual(file.substr(1, i-1));
if(j == virtualMap.end()) {
throw ShareException(UserConnection::FILE_NOT_AVAILABLE);
@@ -1243,7 +1243,7 @@
i->second->getParent()->getFullName() + i->second->getName(), i->second->getTTH());
results.push_back(sr);
- ShareManager::getInstance()->setHits(ShareManager::getInstance()->getHits()+1);
+ ShareManager::getInstance()->addHits(1);
}
}
return;
Modified: dcplusplus/trunk/windows/DirectoryListingFrm.cpp
===================================================================
--- dcplusplus/trunk/windows/DirectoryListingFrm.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/windows/DirectoryListingFrm.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -632,7 +632,7 @@
n = 0;
targetMenu.AppendMenu(MF_STRING, IDC_DOWNLOADTO, CTSTRING(BROWSE));
targets.clear();
- QueueManager::getInstance()->getTargetsByRoot(targets, ii->file->getTTH());
+ QueueManager::getInstance()->getTargets(ii->file->getTTH(), targets);
if(targets.size() > 0) {
targetMenu.AppendMenu(MF_SEPARATOR);
Modified: dcplusplus/trunk/windows/SearchFrm.cpp
===================================================================
--- dcplusplus/trunk/windows/SearchFrm.cpp 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/windows/SearchFrm.cpp 2006-09-20 09:53:26 UTC (rev 654)
@@ -347,8 +347,11 @@
}
if(isHash) {
- if(Util::stricmp(Text::toT(aResult->getTTH().toBase32()), search[0]) != 0)
+ if(aResult->getType() != SearchResult::TYPE_FILE || TTHValue(Text::fromT(search[0])) != aResult->getTTH()) {
+ droppedResults++;
+ PostMessage(WM_SPEAKER, FILTER_RESULT);
return;
+ }
} else {
// match all here
for(TStringIter j = search.begin(); j != search.end(); ++j) {
@@ -357,23 +360,21 @@
)
{
droppedResults++;
- PostMessage(WM_SPEAKER, FILTER_RESULT, NULL);
+ PostMessage(WM_SPEAKER, FILTER_RESULT);
return;
}
}
}
}
- // Reject results without free slots or tth if selected
- // but always show directories
+ // Reject results without free slots
if((onlyFree && aResult->getFreeSlots() < 1))
{
droppedResults++;
- ctrlStatus.SetText(3, Text::toT(Util::toString(droppedResults) + ' ' + STRING(FILTERED)).c_str());
+ PostMessage(WM_SPEAKER, FILTER_RESULT);
return;
}
-
SearchInfo* i = new SearchInfo(aResult);
PostMessage(WM_SPEAKER, ADD_RESULT, (LPARAM)i);
}
@@ -400,10 +401,11 @@
ctrlStatus.SetText(3, _T(""));
return 0;
}
+
void SearchFrame::SearchInfo::view() {
try {
if(sr->getType() == SearchResult::TYPE_FILE) {
- QueueManager::getInstance()->add(Util::getTempPath() + Text::fromT(columns[COLUMN_FILENAME]),
+ QueueManager::getInstance()->add(Util::getTempPath() + sr->getFileName(),
sr->getSize(), sr->getTTH(), sr->getUser(),
QueueItem::FLAG_CLIENT_VIEW | QueueItem::FLAG_TEXT);
}
@@ -430,12 +432,13 @@
void SearchFrame::SearchInfo::DownloadWhole::operator()(SearchInfo* si) {
try {
+ QueueItem::Priority prio = WinUtil::isShift() ? QueueItem::HIGHEST : QueueItem::DEFAULT;
if(si->sr->getType() == SearchResult::TYPE_FILE) {
- QueueManager::getInstance()->addDirectory(Text::fromT(si->columns[COLUMN_PATH]), si->sr->getUser(), Text::fromT(tgt),
- WinUtil::isShift() ? QueueItem::HIGHEST : QueueItem::DEFAULT);
+ QueueManager::getInstance()->addDirectory(Text::fromT(si->columns[COLUMN_PATH]),
+ si->sr->getUser(), Text::fromT(tgt), prio);
} else {
- QueueManager::getInstance()->addDirectory(si->sr->getFile(), si->sr->getUser(), Text::fromT(tgt),
- WinUtil::isShift() ? QueueItem::HIGHEST : QueueItem::DEFAULT);
+ QueueManager::getInstance()->addDirectory(si->sr->getFile(), si->sr->getUser(),
+ Text::fromT(tgt), prio);
}
} catch(const Exception&) {
}
@@ -475,7 +478,7 @@
}
}
-void SearchFrame::SearchInfo::CheckSize::operator()(SearchInfo* si) {
+void SearchFrame::SearchInfo::CheckTTH::operator()(SearchInfo* si) {
if(firstTTH) {
tth = si->columns[COLUMN_TTH];
hasTTH = true;
@@ -486,19 +489,6 @@
}
}
- if(si->sr->getType() == SearchResult::TYPE_FILE) {
- if(ext.empty()) {
- ext = Util::getFileExt(si->columns[COLUMN_FILENAME]);
- size = si->sr->getSize();
- } else if(size != -1) {
- if((si->sr->getSize() != size) || (Util::stricmp(ext, Util::getFileExt(si->columns[COLUMN_FILENAME])) != 0)) {
- size = -1;
- }
- }
- } else {
- size = -1;
- }
-
if(firstHubs && hubs.empty()) {
hubs = ClientManager::getInstance()->getHubs(si->sr->getUser()->getCID());
firstHubs = false;
@@ -965,15 +955,11 @@
}
}
- SearchInfo::CheckSize cs = ctrlResults.forEachSelectedT(SearchInfo::CheckSize());
+ SearchInfo::CheckTTH cs = ctrlResults.forEachSelectedT(SearchInfo::CheckTTH());
- if(cs.size != -1 || cs.hasTTH) {
+ if(cs.hasTTH) {
targets.clear();
- if(cs.hasTTH) {
- QueueManager::getInstance()->getTargetsByRoot(targets, TTHValue(Text::fromT(cs.tth)));
- } else {
- QueueManager::getInstance()->getTargetsBySize(targets, cs.size, Text::fromT(cs.ext));
- }
+ QueueManager::getInstance()->getTargets(TTHValue(Text::fromT(cs.tth)), targets);
if(targets.size() > 0) {
targetMenu.AppendMenu(MF_SEPARATOR);
Modified: dcplusplus/trunk/windows/SearchFrm.h
===================================================================
--- dcplusplus/trunk/windows/SearchFrm.h 2006-09-19 19:20:45 UTC (rev 653)
+++ dcplusplus/trunk/windows/SearchFrm.h 2006-09-20 09:53:26 UTC (rev 654)
@@ -258,11 +258,9 @@
void operator()(SearchInfo* si);
const tstring& tgt;
};
- struct CheckSize {
- CheckSize() : size(-1), op(true), firstHubs(true), hasTTH(false), firstTTH(true) { }
+ struct CheckTTH {
+ CheckTTH() : op(true), firstHubs(true), hasTTH(false), firstTTH(true) { }
void operator()(SearchInfo* si);
- tstring ext;
- int64_t size;
bool firstHubs;
StringList hubs;
bool op;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|