museek+-0.2+svn20100315.r1208/0000755000175000017500000000000011347444621014670 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/cmake/0000755000175000017500000000000011347444614015752 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/cmake/FindEvent.cmake0000644000175000017500000000142411130707057020630 0ustar gandalfgandalf# Find Libevent # http://monkey.org/~provos/libevent/ # http://www.wallinfire.net/picviz/browser/trunk/cmake/FindEvent.cmake?rev=351 # # Once done, this will define: # # Event_FOUND - system has Event # Event_INCLUDE_DIRS - the Event include directories # Event_LIBRARIES - link these to use Event # if (Event_INCLUDE_DIRS AND Event_LIBRARIES) # Already in cache, be silent set(Event_FIND_QUIETLY TRUE) endif (Event_INCLUDE_DIRS AND Event_LIBRARIES) find_path(Event_INCLUDE_DIRS event.h PATHS ${_EventIncDir} PATH_SUFFIXES event) find_library(Event_LIBRARIES NAMES event PATHS ${_EventLinkDir}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Event DEFAULT_MSG Event_INCLUDE_DIRS Event_LIBRARIES) mark_as_advanced(Event_INCLUDE_DIRS Event_LIBRARIES) museek+-0.2+svn20100315.r1208/cmake/FindFAM.cmake0000644000175000017500000000220511065656035020156 0ustar gandalfgandalf# - Try to find the fam libraries # Once done this will define # # FAM_FOUND - system supports fam # FAM_INCLUDE_DIR - the fam include directory # FAM_LIBRARIES - libfam library FIND_PATH(FAM_INCLUDE_DIR fam.h ) FIND_LIBRARY(FAM_LIBRARIES NAMES fam ) FIND_LIBRARY(GAMIN_LIBRARIES NAMES gamin gamin-1) IF (NOT GAMIN_LIBRARIES AND FAM_LIBRARIES) message (STATUS "Please use Gamin instead of FAM if possible") ENDIF (NOT GAMIN_LIBRARIES AND FAM_LIBRARIES) if (GAMIN_LIBRARIES) message(STATUS "Found Gamin: good choice, it's better then FAM") set(FAM_LIBRARIES ${GAMIN_LIBRARIES}) endif (GAMIN_LIBRARIES) IF(FAM_INCLUDE_DIR AND FAM_LIBRARIES) SET(FAM_FOUND 1) if(NOT FAM_FIND_QUIETLY) if (GAMIN_LIBRARIES) message(STATUS "Found FAM (provided by Gamin): ${FAM_LIBRARIES}") else (GAMIN_LIBRARIES) message(STATUS "Found FAM: ${FAM_LIBRARIES}") endif (GAMIN_LIBRARIES) endif(NOT FAM_FIND_QUIETLY) ELSE(FAM_INCLUDE_DIR AND FAM_LIBRARIES) SET(FAM_FOUND 0 CACHE BOOL "Not found FAM") message(STATUS "NOT Found FAM, disabling it") ENDIF(FAM_INCLUDE_DIR AND FAM_LIBRARIES) MARK_AS_ADVANCED(FAM_INCLUDE_DIR FAM_LIBRARIES) museek+-0.2+svn20100315.r1208/muscan/0000755000175000017500000000000011347444614016160 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/muscan/muscan.10000644000175000017500000000520111347437561017531 0ustar gandalfgandalf.TH "MUSCAN" "1" "Release 0.2.0" "daelstorm" "Museek Daemon Plus" .SH "NAME" .LP \fBMuscan\fR \- File scanner for Museek .SH "SYNOPSIS" .B muscan [\-c <\fIfilename\fP>] [\-\-config <\fIfilename\fP>] [\-b] [\-\-buddy] [\-v] [\-\-verbose] [\-r] [\-\-rescan] [\-n] [\-\-noscan] [\-s <\fIdirectory\fP>] [\-\-share <\fIdirectory\fP>] [\-u <\fIdirectory\fP>] [\-\-unshare <\fIdirectory\fP>] [\-l] [\-\-list] [\-h] [\-\-help] .SH "DESCRIPTION" .LP Muscan scans paths & files to be shared by \fImuseekd\fP(1). It creates a database of files, with meta\-data for MP3 and OGG Vorbis files, if Vorbis support was compiled in. There are two available shares databases, Normal and Buddies\-Only. Buddies\-Only is an optional shares database that, if enabled, is only accessible by the users you've chosen as "Buddies". .LP Before running muscan, you will need a configured museekd, which can be done with \fImusetup\fP(1) or fI>musetup\-gtk\fP(1), and add some shared paths with either of the setup tools or with \fImuscan\fP(1). .SH "OPTIONS" .LP Muscan accepts the following options: .TP .B \-c <\fIfilename\fP>, \-\-config <\fIfilename\fP> Use a different config file. .TP .B \-b, \-\-buddy Select Buddy\-Only Shares. .TP .B \-s <\fIdirectory\fP>, \-\-share <\fIdirectory\fP> Add directory to shares. .TP .B \-u <\fIdirectory\fP>, \-\-unshare <\fIdirectory\fP> Remove directory from shares. .TP .B \-r, \-\-rescan Rescan shares. .TP .B \-n, \-\-noscan Do not rescan shares. .TP .B \-v, \-\-verbose Be verbose while scanning shares. .TP .B \-l, \-\-list Display list of shared directories. .TP .B \-h, \-\-help Display Help, version and exit. .SH "EXAMPLE" .LP To run a quick shares update, run: .LP \fBmuscan\fP or \fBmuscan \-b\fP .LP To rescan everything, run: .LP \fBmuscan \-r\fP or \fBmuscan \-b \-r\fP .LP Alternatively you use a different config file with: .LP \fBmuscan \-\-config config.xml\fP .LP .SH "FILES" .TP \fI~/.museekd/config.xml\fR The default location for the \fBmuseekd\fP config file. .TP \fI~/.museekd/config.shares\fR The default location for the Normal shares database. .TP \fI~/.museekd/config.shares.state\fR The default location for the active Normal shares database. .TP \fI~/.museekd/config.buddyshares\fR The default location for the Buddy shares database. .TP \fI~/.museekd/config.buddyshares.state\fR The default location for the active Buddy shares database's. .SH "AUTHORS" .LP Hyriand .LP daelstorm .SH "SEE ALSO" .LP \fImucous\fP(1) \fImulog\fP(1) \fImurmur\fP(1) \fImuscand\fP(1) \fImuseekcontrol\fP(1) \fImuseekd\fP(1) \fImuseeq\fP(1) \fImusetup\fP(1) \fImusetup\-gtk\fP(1) museek+-0.2+svn20100315.r1208/muscan/scanner.cc0000644000175000017500000001263411102357015020111 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include extern "C" { # include "mp3.h" } #ifndef HAVE_SCANDIR # include "scandir.hh" #endif #ifdef HAVE_VORBIS # include #endif #include #include #include #include #include "scanner.hh" using std::map; using std::vector; using std::string; using std::cerr; using std::cout; using std::endl; #ifdef HAVE_VORBIS # ifdef RELAYED_LIBVORBISFILE extern int libvorbisfile_is_present; # else static int libvorbisfile_is_present = 1; # endif #endif int Scanner_Verbosity = 0; void DirScanner::add(const string& path) { if(folders.find(path) == folders.end()) folders[path] = new_folder(path); } DirEntry* DirScanner::new_folder(bool fake) { NNLOG("museek.dirscanner", "new_folder %i", fake); return new DirScanner(fake); } DirEntry* DirScanner::new_folder(const string& path) { NNLOG("museek.dirscanner", "new_folder %s", path.c_str()); return new DirScanner(path); } void DirScanner::scan(const struct stat* s) { NNLOG("museek.dirscanner", "scan <...>"); bool uptodate = false; if (s != NULL) { uptodate = s->st_mtime == mtime; mtime = s->st_mtime; } if (! fake && ! uptodate) { real_scan(); return; } if(Scanner_Verbosity > 1 && ! fake) cout << "Skipping " << path << endl; map::iterator dit = folders.begin(); for(; dit != folders.end(); ++dit) { struct stat s2; if (stat((*dit).first.c_str(), &s2)) { (*dit).second->files.clear(); for(map::iterator it = (*dit).second->folders.begin(); it != (*dit).second->folders.end(); ++it) delete (*it).second; (*dit).second->folders.clear(); continue; } static_cast((*dit).second)->scan(&s2); } } FileEntry DirScanner::scan_file(const string& path) { NNLOG("museek.dirscanner", "scan file %s", path.c_str()); if(Scanner_Verbosity > 2) cout << "Identifying " << path << endl; FileEntry fe; mp3info info; int l = path.length(); if (l > 4) { if(tolower(path.substr(l-4, l)) == ".mp3") { if (mp3_scan(path.c_str(), &info)) { fe.attrs.push_back(info.bitrate); fe.attrs.push_back(info.length); fe.attrs.push_back(info.vbr); fe.ext = "mp3"; } else { cerr << "Invalid mp3 " << path << endl; } #ifdef HAVE_VORBIS } else if(libvorbisfile_is_present && tolower(path.substr(l-4, l)) == ".ogg") { FILE *f = fopen(path.c_str(), "r"); if (f == NULL) return fe; OggVorbis_File ovf; if(ov_open(f, &ovf, NULL, 0) != 0) { fclose(f); return fe; } vorbis_info *vinfo = ov_info(&ovf, -1); if (vinfo != NULL) { long bitrate = ov_bitrate(&ovf, -1); fe.attrs.push_back(bitrate / 1000); fe.attrs.push_back((int)ov_time_total(&ovf, -1)); if ((bitrate == vinfo->bitrate_nominal) && (bitrate == vinfo->bitrate_upper) && (bitrate == vinfo->bitrate_lower)) fe.attrs.push_back(0); else fe.attrs.push_back(1); fe.ext = "mp3"; } ov_clear(&ovf); #endif // HAVE_VORBIS } } return fe; } void DirScanner::real_scan() { NNLOG("museek.dirscanner", "real_scan"); if(Scanner_Verbosity > 0) cout << "Scanning " << path << endl; files.clear(); struct SCANDIR_ENTRY **temp; struct stat s; int n; if(path[path.size() - 1] == '/') path = path.substr(0, path.size() - 1); if (path.substr(path.rfind('/')+1).rfind(".") == 0 ) { cout << "Warning: " << path.c_str() << " is a hidden directory, not sharing." << endl; return; } #if SCANDIR_ENTRY != dirent char *x = strdup(path.c_str()); if((n = scandir(x, &temp, NULL, NULL)) < 0) { free(x); folders.clear(); return; } free(x); #else // SCANDIR_ENTRY == dirent if((n = scandir(path.c_str(), &temp, NULL, NULL)) < 0) { folders.clear(); return; } #endif mapnewfolders; while (n--) { string fn = temp[n]->d_name, full = path + "/" + fn; free(temp[n]); if ((fn == ".") || (fn == "..") || (stat(full.c_str(), &s) != 0)) continue; if ( fn.rfind(".") == 0 ) // Ignore dot-files continue; if(S_ISREG(s.st_mode)) { FileEntry fe = scan_file(full); fe.size = s.st_size; files[fn] = fe; } else if (S_ISDIR(s.st_mode)) { map::iterator dit = folders.find(full); if (dit != folders.end()) { newfolders[full] = (*dit).second; static_cast(newfolders[full])->scan(&s); } else { DirEntry* de = new_folder(full); static_cast(de)->scan(&s); newfolders[full] = de; } } } folders = newfolders; free(temp); } museek+-0.2+svn20100315.r1208/muscan/muscan.cc0000644000175000017500000001012411102357015017736 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include using std::vector; using std::string; using std::cout; using std::cerr; using std::endl; using std::map; void help() { cout << "muscan [-c --config PATH] [-b --buddy] [-v --verbose] [-r --rescan] [-n --noscan] [-s --share PATH]... [-u --unshare PATH]..." << endl; cout << "or:" << endl; cout << "muscan [-c --config PATH] -l --list" << endl; cout << "Version 0.2.0" << endl; exit(-1); } int main(int argc, char **argv) { string config_file = string(getenv("HOME")) + "/.museekd/config.xml"; vector add, remove; bool doList = false, rescan = false, noscan = false, doBuddy = false; for(int i = 1; i < argc; i++) { string arg = argv[i]; if(arg == "-c" || arg == "--config") { ++i; if(i == argc) help(); config_file = string(argv[i]); } else if(arg == "-s" || arg == "--share") { ++i; if(i == argc) help(); add.push_back(string(argv[i])); } else if(arg == "-u" || arg == "--unshare") { ++i; if(i == argc) help(); remove.push_back(string(argv[i])); } else if(arg == "-b" || arg == "--buddy") { doBuddy = true; } else if(arg == "-l" || arg == "--list") { doList = true; } else if(arg == "-v" || arg == "--verbose") { Scanner_Verbosity += 1; } else if(arg == "-r" || arg == "--rescan") { rescan = true; } else if(arg == "-n" || arg == "--noscan") { noscan = true; } else help(); } if (Scanner_Verbosity >= 2){ NNLOG.logEvent.connect(new NewNet::ConsoleOutput); NNLOG.enable("ALL"); } Muconf config(config_file); if(! config.hasDomain("shares") || ! config["shares"].hasKey("database")) { cerr << "config file '" << config_file << "' has incomplete or corrupt shares database" << endl; exit(-1); } if(! config.hasDomain("buddy.shares") || ! config["buddy.shares"].hasKey("database")) { cerr << "config file '" << config_file << "' has incomplete or corrupt buddy shares database" << endl; exit(-1); } DirScanner root; string state; if (doBuddy) { string s = config["buddy.shares"]["database"]; state += s + ".state"; } else { string s = config["shares"]["database"]; state += s + ".state"; } root.load(state); if(doList) { map::iterator fit = root.folders.begin(); for(; fit != root.folders.end(); ++fit) cout << (*fit).second->path << endl; return 0; } vector::iterator it = add.begin(); for(; it != add.end(); ++it) root.add(*it); it = remove.begin(); for(; it != remove.end(); ++it) { map::iterator fit = root.folders.find(*it); if(fit != root.folders.end()) { delete (*fit).second; root.folders.erase(fit); } } if(rescan) { add.clear(); map::iterator fit = root.folders.begin(); for(; fit != root.folders.end(); ++fit) add.push_back((*fit).first); it = add.begin(); root = DirScanner(); for(; it != add.end(); ++it) root.add(*it); } if(! noscan) root.scan(); root.save(state); DirScanner folded; root.fold(&folded); if (doBuddy) { folded.save(config["buddy.shares"]["database"]); } else { folded.save(config["shares"]["database"]); } return 0; } museek+-0.2+svn20100315.r1208/muscan/muscand.10000644000175000017500000000440711347437561017704 0ustar gandalfgandalf.TH "MUSCAND" "1" "Release 0.2.0" "daelstorm" "Museek Daemon Plus" .SH "NAME" .LP \fBMuscand\fR \- File scanner daemon for Museek .SH "SYNOPSIS" .B muscand [\-c <\fIfilename\fP>] [\-\-config <\fIfilename\fP>] [\-b] [\-\-buddy] [\-l] [\-\-list] [\-h] [\-\-help] [\-v] [\-\-verbose] [\-\-no\-reload] .SH "DESCRIPTION" .LP Muscand is a FAM service that scans paths already configured by \fImuscan\fP(1) or \fImusetup\fP(1). If it detects changes to the paths, it will update the Shares Database it is watching after a few seconds. There are two available shares databases, Normal and Buddies\-Only. Buddies\-Only is an optional shares database that, if enabled, is only accessible by the users you've chosen as "Buddies". .LP Before running muscand, you will need a configured museekd, which can be done with \fImusetup\fP(1) or fI>musetup\-gtk\fP(1), and add some shared paths with either of the setup tools or with \fImuscan\fP(1). .SH "OPTIONS" .LP Muscand accepts the following options: .TP .B \-c <\fIfilename\fP>, \-\-config <\fIfilename\fP> Use a different config file. .TP .B \-b, \-\-buddy Select Buddy\-Only Shares. .TP .B \-h, \-\-help Display Help, version and exit. .TP .B \-v, \-\-verbose Be verbose while scanning shares. .TP .B \-\-no\-reload Don't ask museekd to reload the database .SH "EXAMPLE" .LP To run this program the standard way type: .LP muscand .LP Alternativly you use a different config file with: .LP muscand \-\-config config.xml .LP Or, you can watch Buddy\-Only shares: .LP muscand \-\-buddy .LP .SH "FILES" .TP \fI~/.museekd/config.xml\fR The default location for the \fBmuseekd\fP config file. .TP \fI~/.museekd/config.shares\fR The default location for the Normal shares database. .TP \fI~/.museekd/config.shares.state\fR The default location for the active Normal shares database. .TP \fI~/.museekd/config.buddyshares\fR The default location for the Buddy shares database. .TP \fI~/.museekd/config.buddyshares.state\fR The default location for the active Buddy shares database's. .SH "AUTHORS" .LP Hyriand .LP daelstorm .SH "SEE ALSO" .LP \fImucous\fP(1) \fImulog\fP(1) \fImurmur\fP(1) \fImuscan\fP(1) \fImuseekcontrol\fP(1) \fImuseekd\fP(1) \fImuseeq\fP(1) \fImusetup\fP(1) \fImusetup\-gtk\fP(1) museek+-0.2+svn20100315.r1208/muscan/scanner.hh0000644000175000017500000000273511113306711020123 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SCANNER_HH__ #define __SCANNER_HH__ #include #include #include #include #include class DirScanner : public DirEntry { public: DirScanner(bool _f = true) : DirEntry(_f) { }; DirScanner(const std::string& _p) : DirEntry(_p) { }; void add(const std::string& path); virtual DirEntry* new_folder(bool fake); virtual DirEntry* new_folder(const std::string& path); void scan(const struct stat* = NULL); FileEntry scan_file(const std::string&); void real_scan(); }; extern int Scanner_Verbosity; #endif // __SCANNER_HH__ museek+-0.2+svn20100315.r1208/muscan/scandir.cc0000644000175000017500000000375411065656035020122 0ustar gandalfgandalf/* ******************************************************************************** * * * Viewmol * * * * S C A N D I R . C * * * * Copyright (c) Joerg-R. Hill, December 2000 * * * ******************************************************************************** * * $Id: scandir.c,v 1.2 2000/12/10 15:37:02 jrh Exp $ * $Log: scandir.c,v $ * Revision 1.2 2000/12/10 15:37:02 jrh * Release 2.3 * * Revision 1.1 1999/05/24 01:29:43 jrh * Initial revision * * modified by hyriand to fit in Museek */ #include #ifndef HAVE_SCANDIR #warning untested scandir implementation /* This function is only required for SunOS, all other supported OS have this function in their system libraries */ int scandir(const char *dir, struct dirent ***namelist, int (*select)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { DIR *d; struct dirent *entry; register int i=0; size_t entrysize; if ((d=opendir(dir)) == NULL) return(-1); *namelist=NULL; while ((entry=readdir(d)) != NULL) { if (select == NULL || (select != NULL && (*select)(entry))) { *namelist=(struct dirent **)realloc((void *)(*namelist), (size_t)((i+1)*sizeof(struct dirent *))); if (*namelist == NULL) return(-1); entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1; (*namelist)[i]=(struct dirent *)malloc(entrysize); if ((*namelist)[i] == NULL) return(-1); memcpy((*namelist)[i], entry, entrysize); i++; } } if (closedir(d)) return(-1); if (i == 0) return(-1); if (compar != NULL) qsort((void*)(*namelist), (size_t)i, sizeof(struct dirent *), (int (*)(const void*, const void*))compar); return(i); } int alphasort(const struct dirent **a, const struct dirent **b) { return(strcmp((*a)->d_name, (*b)->d_name)); } #endif // HAVE_SCANDIR museek+-0.2+svn20100315.r1208/muscan/CMakeLists.txt0000644000175000017500000000277711102361566020726 0ustar gandalfgandalfproject(Tools CXX) IF(PREFIX) SET(CMAKE_INSTALL_PREFIX ${PREFIX}) ELSE(PREFIX) SET(CMAKE_INSTALL_PREFIX "/usr") ENDIF(PREFIX) find_package(FAM) INCLUDE(FindPkgConfig REQUIRED) pkg_search_module(LIBXMLPP "libxml++-2.6") ADD_DEFINITIONS(${LIBXMLPP_CFLAGS}) set(TOOLS_SOURCES mp3.c scandir.cc scanner.cc ) add_library(Tools STATIC ${TOOLS_SOURCES}) if (C_MUSCAN) set(MUSCAN_SOURCES muscan.cc ${TOOLS_SOURCES} ) set(MUSCAND_SOURCES muscand.cc ${TOOLS_SOURCES} ) add_executable(muscan ${MUSCAN_SOURCES}) # Install the muscan binary to the 'bin' directory. install( TARGETS muscan DESTINATION bin ) set(MANPAGES muscan.1 ) MESSAGE("--> muscan (file scanner) will be installed..") # To build, or not build muscand if (FAM_LIBRARIES AND FAM_FOUND) add_executable(muscand ${MUSCAND_SOURCES}) target_link_libraries( muscand Muhelp ${ZLIB_LIBRARIES} ${FAM_LIBRARIES} ${VORBIS_LIBRARIES} ${ICONV_LIBRARIES} ${OS_LIBRARIES} ) set(MANPAGES ${MANPAGES} muscand.1 ) install( TARGETS muscand DESTINATION bin ) MESSAGE("--> muscand (FAM-based file scanning daemon) will be installed..") else(FAM_LIBRARIES AND FAM_FOUND) MESSAGE("!!! muscand will NOT be installed..") endif(FAM_LIBRARIES AND FAM_FOUND) target_link_libraries( muscan Muhelp ${ZLIB_LIBRARIES} ${VORBIS_LIBRARIES} ${ICONV_LIBRARIES} ${OS_LIBRARIES} ) install( FILES ${MANPAGES} DESTINATION ${MANUAL_DIRECTORY} ) endif (C_MUSCAN) museek+-0.2+svn20100315.r1208/muscan/muscand.cc0000644000175000017500000001705411111015130020100 0ustar gandalfgandalf#include #include #include #include #include #include #include #include using std::string; using std::map; using std::cout; using std::endl; using std::cerr; using std::vector; class FAMDirScanner; class FAMHandler { public: FAMHandler(const string& config_file, bool doBuddy, bool doReload); ~FAMHandler(); int load(); int run(); void save(); void remove(FAMDirScanner *); private: string shares, state; FAMConnection fc; vector nodes, pending; time_t save_at; FAMDirScanner *root; bool m_doReload; void add(FAMDirScanner *); void exists(FAMDirScanner *ds, const char *filename, bool force_file); void deleted(FAMDirScanner *ds, const char *filename); }; class FAMDirScanner : public DirScanner { public: FAMDirScanner(FAMHandler *_fh, bool _f = true) : DirScanner(_f), fh(_fh) { }; FAMDirScanner(FAMHandler *_fh, const string& _p) : DirScanner(_p), fh(_fh) { }; ~FAMDirScanner() { if(! fake) fh->remove(this); } DirEntry* new_folder(bool fake) { return new FAMDirScanner(fh, fake); } DirEntry* new_folder(const string& path) { return new FAMDirScanner(fh, path); } FAMHandler *fh; FAMRequest famRequest; bool isFake() { return fake; } }; FAMHandler::FAMHandler(const string& config_file, bool doBuddy, bool doReload) : save_at(0), root(0) { FAMCONNECTION_GETFD(&fc) = -1; if (Scanner_Verbosity >= 2){ NNLOG.logEvent.connect(new NewNet::ConsoleOutput); NNLOG.enable("ALL"); } m_doReload = doReload; Muconf config(config_file); if(! config.hasDomain("shares") || ! config["shares"].hasKey("database")) { cerr << "config file '" << config_file << "' incomplete or corrupt shares" << endl; exit(-1); } if(! config.hasDomain("buddy.shares") || ! config["buddy.shares"].hasKey("database")) { cerr << "config file '" << config_file << "' incomplete or corrupt buddy shares" << endl; exit(-1); } if (doBuddy) { string tmp = config["buddy.shares"]["database"]; shares = tmp; state = shares + ".state"; } else { string tmp = config["shares"]["database"]; shares = tmp; state = shares + ".state"; } } FAMHandler::~FAMHandler() { if(root) delete root; if(FAMCONNECTION_GETFD(&fc) != -1) FAMClose(&fc); } int FAMHandler::load() { if(root) delete root; if(FAMCONNECTION_GETFD(&fc) != -1) FAMClose(&fc); if(FAMOpen2(&fc, "muscand") < 0) { cerr << "couldn't connect to FAM" << endl; return -1; } root = new FAMDirScanner(this); root->load(state); add(root); return 0; } void FAMHandler::add(FAMDirScanner *h) { if(find(nodes.begin(), nodes.end(), h) != nodes.end()) cerr << "double insertion" << endl; else if(h->isFake()) cerr << "it's fake.." << endl; else { if(! h->path.empty()) { cerr << "Registering " << h->path << endl; pending.push_back(h); } } map::iterator it = h->folders.begin(), end = h->folders.end(); for(; it != end; ++it) add((FAMDirScanner*)(*it).second); } void FAMHandler::remove(FAMDirScanner *h) { if(h->isFake()) { cerr << "trying to remove a fake entry" << endl; return; } vector::iterator it = find(nodes.begin(), nodes.end(), h); if(it == nodes.end()) cerr << "Possible corruption (removal of non-existing entry)" << endl; else { FAMCancelMonitor(&fc, &h->famRequest); nodes.erase(it); } vector::iterator it2 = find(pending.begin(), pending.end(), h); if(it2 != pending.end()) { cerr << "Removing pending registration" << endl; pending.erase(it2); } } int FAMHandler::run() { int fam_fd = FAMCONNECTION_GETFD(&fc); while(1) { if(save_at && time(NULL) >= save_at) { cerr << "saving updated shares database" << endl; save(); save_at = 0; } fd_set rfds, wfds; struct timeval tv; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_SET(fam_fd, &rfds); if(! pending.empty()) FD_SET(fam_fd, &wfds); tv.tv_sec = 5; tv.tv_usec = 0; int retval = select(fam_fd+1, &rfds, &wfds, 0, &tv); if(retval == -1) { FAMClose(&fc); cerr << "Select error on FAM socket" << endl; return -1; } if(retval == 0) continue; if(FD_ISSET(fam_fd, &wfds) && pending.size()) { vector::iterator it = pending.begin(); FAMMonitorDirectory(&fc, (*it)->path.c_str(), &(*it)->famRequest, *it); nodes.push_back(*it); pending.erase(it); } if(FD_ISSET(fam_fd, &rfds)) { while(FAMPending(&fc)) { FAMEvent fe; if(FAMNextEvent(&fc, &fe) < 0) { FAMClose(&fc); cerr << "Read error on FAM socket" << endl; return -1; } if(fe.code == FAMExists || fe.code == FAMCreated || fe.code == FAMChanged) { exists((FAMDirScanner *)fe.userdata, fe.filename, fe.code == FAMChanged); } else if(fe.code == FAMDeleted) { deleted(static_cast(fe.userdata), fe.filename); } else cout << "ping " << fe.code << fe.filename << endl; } } } } void FAMHandler::save() { root->save(state); DirEntry folded; root->fold(&folded); folded.save(shares); #ifndef WIN32 if (m_doReload) system("killall -HUP museekd"); #endif // WIN32 } void FAMHandler::exists(FAMDirScanner *ds, const char *filename, bool force_file) { std::string path = ds->path + "/" + filename; if(ds->path == filename || find(nodes.begin(), nodes.end(), ds) == nodes.end()) return; struct stat s; if(stat(path.c_str(), &s) == -1) return; if(S_ISREG(s.st_mode)) { if(force_file || ds->files.find(filename) == ds->files.end()) { cout << "new file: " << path << endl; FileEntry fe = ds->scan_file(path); fe.size = s.st_size; ds->files[filename] = fe; save_at = time(0) + 15; } } else if(S_ISDIR(s.st_mode)) { if(ds->folders.find(path) == ds->folders.end()) { cout << "scanning: " << path << endl; ds->add(path); FAMDirScanner *ns = static_cast(ds->folders[path]); ns->scan(); add(ns); save_at = time(NULL) + 15; } } } void FAMHandler::deleted(FAMDirScanner *ds, const char *filename) { if(ds->path == filename) { cout << "hard deleting " << ds->path << " -- " << filename << endl; int ix = ds->path.rfind('/'); string parent = ds->path.substr(0, ix), child = ds->path.substr(ix + 1); vector::iterator it = nodes.begin(), end = nodes.end(); for(; it != end; ++it) { if((*it)->path == parent) { cout << "found it" << endl; (*it)->folders.erase(filename); delete ds; save_at = time(0) + 15; return; } } cout << parent << " -- " << child << endl; return; } else ds->files.erase(filename); } void help() { cout << "muscand [-c --config PATH] [-b --buddy] [-h --help] [-v --verbose] [--no-reload]" << endl; cout << "Version 0.2.0" << endl; exit(-1); } int main(int argc, char **argv) { #ifdef RELAYED_LIBFAM extern int libfam_is_present; if(! libfam_is_present) { cerr << "libfam not found, aborting" << endl; return -1; } #endif string config_file = string(getenv("HOME")) + "/.museekd/config.xml"; bool doBuddy = false; bool doReload = true; for(int i = 1; i < argc; i++) { string arg = argv[i]; if(arg == "-c" || arg == "--config") { ++i; config_file = string(argv[i]); } else if(arg == "-b" || arg == "--buddy") { ++i; doBuddy = true; } else if(arg == "-h" || arg == "--help") { ++i; help(); } else if(arg == "-v" || arg == "--verbose") { Scanner_Verbosity += 1; } else if(arg == "--no-reload") { doReload = false; } } FAMHandler fh(config_file, doBuddy, doReload); if(fh.load()) return -1; fh.run(); return 0; } museek+-0.2+svn20100315.r1208/muscan/mp3.c0000644000175000017500000001247511102357015017017 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "mp3.h" #define ENDIAN(head) ((head >> 24) | ((head & 0x00ff0000) >> 8) | ((head & 0x0000ff00) << 8) | (head << 24)) #define AMT 8192 char check_header(uint32 head) { if ((head & 0xffe00000) != 0xffe00000) return 0; if (! ((head >> 17) & 3)) return 0; if (((head >> 12) & 0xf) == 0xf) return 0; if (! ((head >> 12) & 0xf)) return 0; if (((head >> 10) & 0x3) == 0x3) return 0; if ((((head >> 19) & 1) == 1) && (((head >> 17) & 3) == 3) && ((head >> 16) & 1) == 1) return 0; if ((head & 0xffff0000) == 0xfffe0000) return 0; return -1; } uint32 find_header(FILE *f) { unsigned char *buf = (unsigned char *)malloc(4); off_t start = 0, end = 4, i, read = 4; uint32 *w, head; if (fread(buf, 1, 4, f) < 4) return 0; w = (uint32 *)buf; head = ENDIAN(*w); if (check_header(head)) return head; while (read > 0) { buf = (unsigned char *)realloc(buf, end + AMT); read = fread(&buf[end], 1, AMT, f); end += read; for (i = start; i < end; ++i) { if (buf[i] != 0xff) continue; if (i+4 > end) { start = i; break; } w = (uint32 *)&buf[i]; head = ENDIAN(*w); if (check_header(head)) { free(buf); return head; } i += 1; start = i; } } return 0; } typedef int bitrate_layer[16]; typedef bitrate_layer bitrate_mpeg[3]; bitrate_mpeg bitrate_table[2] = { { /* MPEG-2 & 2.5 */ {0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,-1}, /* Layer 1 */ {0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,-1}, /* Layer 2 */ {0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,-1} /* Layer 3 */ }, { /* MPEG-1 */ {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,-1}, /* Layer 1 */ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,-1}, /* Layer 2 */ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,-1} /* Layer 3 */ } }; typedef int samplerate_mpeg[4]; samplerate_mpeg samplerate_table[4] = { { 11025, 12000, 8000, -1}, /* MPEG-2.5 */ { -1, -1, -1, -1}, /* reserved */ { 22050, 24000, 16000, -1}, /* MPEG-2 */ { 44100, 48000, 32000, -1}, /* MPEG-1 */ }; void parse_header(uint32 head, mp3info *info, off_t flen) { info->mpeg_version = (head >> 19) & 3; info->layer = 4 - ((head >> 17) & 3); info->protection_bit = 1 - ((head >> 16) & 1); info->bitrate = (head >> 12) & 15; info->samplerate = (head >> 10) & 3; info->padding_bit = (head >> 9) & 1; info->private_bit = (head >> 8) & 1; info->mode = (head >> 6) & 3; info->mode_extension = (head >> 4) & 3; info->copyright = (head >> 3) & 1; info->original = (head >> 2) & 1; info->emphasis = head & 3; if (info->mpeg_version == 1) return; if (info->layer == 0) return; info->bitrate = bitrate_table[info->mpeg_version & 1][info->layer - 1][info->bitrate]; if (info->bitrate == -1) return; info->samplerate = samplerate_table[info->mpeg_version][info->samplerate]; if (info->samplerate == -1) return; if (info->layer == 1) { info->framelength = ((12.0 * (info->bitrate * 1000.0) / info->samplerate) + info->padding_bit) * 4.0; info->samplesperframe = 384.0; } else { info->framelength = (144.0 * (info->bitrate * 1000.0) / info->samplerate) + info->padding_bit; info->samplesperframe = 1152.0; } info->length = (flen / info->framelength) * (info->samplesperframe / info->samplerate); info->valid = 1; } void find_parse_Xing(FILE *f, mp3info *info) { char buf[8192]; off_t end, i; uint32 *tmp, bytes, frames; info->vbr = 0; fseek(f, 0, SEEK_SET); end = fread(buf, 1, 8192, f); for(i = 0; i < end; i++) { if (buf[i] != 'X') continue; if (strncmp(&buf[i], "Xing", 4) != 0) continue; tmp = (uint32 *)&buf[i+4]; if (ENDIAN(*tmp) & 3) { tmp++; frames = ENDIAN(*tmp); tmp++; bytes = ENDIAN(*tmp); info->vbr = 1; info->length = frames * info->samplesperframe / info->samplerate; info->bitrate = (bytes * 8.0 / info->length) / 1000; } return; } } char mp3_scan(const char *filename, mp3info *info) { uint32 head; FILE *f; off_t flen; info->valid = 0; f = fopen(filename, "rb"); if (!f) return 0; fseek(f, 0, SEEK_END); flen = ftell(f); fseek(f, 0, SEEK_SET); do { head = find_header(f); if (head) parse_header(head, info, flen); } while (! info->valid && head); if (info->valid) find_parse_Xing(f, info); fclose(f); return info->valid; } /* int main(void) { mp3info info; mp3_scan("01.mp3", &info); } */ museek+-0.2+svn20100315.r1208/muscan/mp3.h0000644000175000017500000000247411102357015017022 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __MP3SCAN_H__ #define __MP3SCAN_H__ typedef struct { char valid; char vbr; int mpeg_version; char layer; char protection_bit; int bitrate; int samplerate; char padding_bit; char private_bit; char mode; char mode_extension; char copyright; char original; char emphasis; double framelength; double samplesperframe; long length; } mp3info; char mp3_scan(const char *filename, mp3info *info); #endif /* __MP3SCAN_HH__ */ museek+-0.2+svn20100315.r1208/muscan/scandir.hh0000644000175000017500000000235211102357015020111 0ustar gandalfgandalf/* Tools - Tools for Museek (muscan) * * Copyright (C) 2003-2004 Hyriand * Copyright 2008 little blue poney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __SCANDIR_HH__ #define __SCANDIR_HH__ #ifndef HAVE_SCANDIR int scandir(const char *dir, struct dirent ***namelist, int (*select)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)); int alphasort(const struct dirent **a, const struct dirent **b); #endif // HAVE_SCANDIR #endif // __SCANDIR_HH__ museek+-0.2+svn20100315.r1208/NewNet/0000755000175000017500000000000011347444620016067 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/NewNet/nnpath.cpp0000644000175000017500000000574111102357015020060 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnpath.h" #include #include #include "platform.h" NewNet::Path::Path(const std::vector & path) { if(path.empty()) return; std::vector::const_iterator it, begin = path.begin(), end = path.end(); for(it = begin; it != end; ++it) { if(it != begin) m_Path += separator(); m_Path += (*it); } } bool NewNet::Path::isAbsolute() const { #ifndef WIN32 return ((! m_Path.empty()) && (m_Path[0] == '/')); #else return ((m_Path.length() >= 3) && (m_Path.substr(1, 2) == ":\\")); #endif // ! WIN32 } std::vector NewNet::Path::split() const { std::vector ret; if(m_Path.empty()) return ret; std::string path(m_Path); std::string::size_type ix = path.find(separator()); while(ix != std::string::npos) { ret.push_back(path.substr(0, ix)); path = path.substr(ix + 1); ix = path.find(separator()); } ret.push_back(path); return ret; } NewNet::Path NewNet::Path::simplified() const { std::string::size_type minElements = isAbsolute() ? 1 : 0; std::vector s(split()), r; std::vector::iterator it, begin = s.begin(), end = s.end(); for(it = begin; it != end; ++it) { if(((*it) == ".") || ((it != begin) && (*it).empty())) continue; else if((*it) == "..") { if(r.size() > minElements) r.pop_back(); else if(minElements == 0) r.push_back(*it); } else r.push_back(*it); } return Path(r); } NewNet::Path NewNet::Path::absolute(const std::string & base_) const { if(isAbsolute()) return *this; Path base(base_); if(base.path().empty()) base = currentDir(); else if(! base.isAbsolute()) base = base.absolute(); if(base.path().empty() || (base.path()[base.path().length() - 1] != separator())) return Path(base.path() + separator() + m_Path); else return Path(base.path() + m_Path); } NewNet::Path NewNet::Path::currentDir() { std::string path; char * buf = new char[PATH_MAX]; assert(getcwd(buf, PATH_MAX) != 0); path = buf; delete [] buf; return Path(path); } museek+-0.2+svn20100315.r1208/NewNet/nnclientsocket.h0000644000175000017500000001004011131133451021242 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_CLIENTSOCKET_H #define NEWNET_CLIENTSOCKET_H #include "nnsocket.h" #include "nnbuffer.h" #include "nnevent.h" namespace NewNet { //! Base class for network client sockets. /*! Provides a base class that will monitor the state of the socket and emit events when something happens. Note: this class is usually not used by your application. Applications should use TcpClientSocket or UnixClientSocket instead. */ class ClientSocket : public Socket { public: //! Create an empty client socket. /*! This will create an empty client socket. The client socket starts in an uninitialized state without a descriptor. */ ClientSocket() : Socket() { } //! Disconnect the client socket. /*! This immediately disconnects the client socket and invokes the disconnected event (except if invoke is false). */ virtual void disconnect(bool invoke = true); //! Process network events. /*! This method is called by the reactor, it will process the events specified in readyState(): read from the socket, detect disconnections and send data through the socket. */ virtual void process(); //! Append data to the send buffer. /*! This is a convenience function that will append data to the send buffer and will mark the flag that specifies that the socket wants to send data. */ void send(const unsigned char * data, size_t n) { m_SendBuffer.append(data, n); setDataWaiting(m_SendBuffer.count() > 0); } //! Return a reference to the send buffer. /*! Returns a reference to the send buffer. Note: if you manipulate the send buffer, be sure to call setDataWaiting(bool) to make sure the data waiting flag is set correctly. */ Buffer & sendBuffer() { return m_SendBuffer; } //! Return a reference to the receive buffer. /*! Returns a reference to the receive buffer. */ Buffer & receiveBuffer() { return m_ReceiveBuffer; } //! Invoked when the socket can't connect. /*! This event will be invoked when the socket detects it cannot connect to the remote host. */ Event cannotConnectEvent; //! Invoked when the socket is connected. /*! This event will be invoked when the socket detects it has successfully connected to the remote host. */ Event connectedEvent; //! Invoked when the socket is disconnected. /*! This event will be invoked when the socket has been disconnected from the remote host. */ Event disconnectedEvent; //! Invoked when there's new data waiting in the receive buffer. /*! This event will be invoked when the socket received information from the remote host. The information will be appended to the receive buffer. */ Event dataReceivedEvent; //! Invoked when data has been sent through the network socket. /*! This event will be invoked when data has been send to the remote host. */ Event dataSentEvent; private: Buffer m_SendBuffer, m_ReceiveBuffer; }; } #endif // NEWNET_CLIENTSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nntcpfactorysocket.h0000644000175000017500000000316111102357015022152 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_TCPFACTORYSOCKET_H #define NEWNET_TCPFACTORYSOCKET_H #include "nntcpserversocket.h" #include "nnfactorysocket.h" namespace NewNet { //! TCP/IP factory socket class. /*! This provides your application with a factory socket that can listen on a host and port and create TcpClientSockets. */ template class TcpFactorySocket : public FactorySocket { public: //! Create a new tcp/ip factory socket. /*! Create a new tcp/ip factory socket. Don't forget to call listen on serverSocket() and add serverSocket() to the reactor. */ TcpFactorySocket() : FactorySocket() { } }; } #endif // NEWNET_TCPFACTORYSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnguardobject.h0000644000175000017500000000620411102357015021055 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_GUARDOBJECT_H #define NEWNET_GUARDOBJECT_H #include #include namespace NewNet { class Object; //! Helper class for detecting object deletion. /*! This class is used to detect when objects are deleted. You can add callbacks that will get invoked when the object is deleted. All objects have a guard object embedded. See NewNet::Object. */ class GuardObject { public: //! The callback class for the guard object. /*! The callback class for the guard object. operator()(Object * p) will be invoked when the callback is added to a guard object and the guard object is deleted. */ class Callback { public: #ifndef DOXYGEN_UNDOCUMENTED virtual ~Callback() {} #endif // DOXYGEN_UNDOCUMENTED //! Override this and place your callback code here. /*! Override this and place your callback code here. */ virtual void operator()(Object * p) = 0; }; //! Create a new guard object. /*! Create a new guard object. */ GuardObject() { } #ifndef DOXYGEN_UNDOCUMENTED void emit(Object * p) { std::vector::iterator it, end = m_Callbacks.end(); for(it = m_Callbacks.begin(); it != end; ++it) (*it)->operator()(p); } #endif // DOXYGEN_UNDOCUMENTED //! Add a callback to the guard object. /*! Add a callback to the guard object so the callback will be invoked when the guard object is deleted. Note: this stores a regular pointer and not a RefPtr. */ GuardObject & operator+=(Callback * p) { m_Callbacks.push_back(p); return *this; } //! Remove a callback from the guard object. /*! Remove a callback from the guard object. It will no longer get invoked when the guard object is deleted. Note that since the guard object stores a regular pointer to the callback, it will not get deleted automatically. */ GuardObject & operator-=(Callback * p) { std::vector::iterator it; it = std::find(m_Callbacks.begin(), m_Callbacks.end(), p); if (it != m_Callbacks.end()) m_Callbacks.erase(it); return *this; } private: std::vector m_Callbacks; }; } #endif // NEWNET_GUARDOBJECT_H museek+-0.2+svn20100315.r1208/NewNet/util.h0000644000175000017500000000377511130624730017221 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_UTIL_H #define NEWNET_UTIL_H #include "platform.h" static inline long difftime(const struct timeval & a, const struct timeval & b) { long diff_s = a.tv_sec - b.tv_sec; long diff_u; diff_u = a.tv_usec - b.tv_usec; if(diff_u < 0) { diff_s -= 1; diff_u += 1000000; } else if(diff_u >= 1000000) { diff_s += 1; diff_u -= 1000000; } return (diff_s * 1000) + (diff_u / 1000); } #ifdef NN_NO_GETTIMEOFDAY // This piece of code came from a post in the CURL mailing list static inline int gettimeofday(struct timeval * tv, void * tz) { union { LONGLONG ns100; FILETIME ft; } now; GetSystemTimeAsFileTime(&now.ft); tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 1000000LL); return 0; } #endif // NN_NO_GETTIMEOFDAY inline static bool setnonblocking(int sock) { #ifndef WIN32 int mode = fcntl(sock, F_GETFL, 0); if (fcntl(sock, F_SETFL, mode|O_NONBLOCK) < 0) return false; #else u_long ioctlArg = 1; ioctlsocket(sock, FIONBIO, &ioctlArg); #endif // ! WIN32 return true; } #endif // NEWNET_UTIL_H museek+-0.2+svn20100315.r1208/NewNet/nntcpclientsocket.h0000644000175000017500000000351611131641225021766 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_TCPCLIENTSOCKET_H #define NEWNET_TCPCLIENTSOCKET_H #include "nnclientsocket.h" #include namespace NewNet { //! TCP/IP client socket. /*! A client socket that provides a streaming internet (TCP/IP) socket type. */ class TcpClientSocket : public ClientSocket { public: //! Create a new unconnected TCP/IP client socket. /*! This creates a new, unconnected TCP/IP client socket. Call connect() to connect the client socket to a remote host. */ TcpClientSocket() : ClientSocket() { } //! Connect to a remote host. /*! Creates a new descriptor and tries to connect it to the specified port on the specified host. */ void connect(const std::string & host, unsigned int port); void onConnectionTimeout(long); void onConnected(ClientSocket *); private: NewNet::WeakRefPtr::Callback> m_ConnectionTimeout; }; } #endif // NEWNET_TCPCLIENTSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnserversocket.cpp0000644000175000017500000000355411130624730021645 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnserversocket.h" #include "nnlog.h" #include "platform.h" #include "util.h" #include void NewNet::ServerSocket::disconnect() { if((descriptor() == -1) || (socketState() != SocketListening)) { NNLOG("newnet.net.warn", "Trying to disconnect an uninitialized server socket."); return; } closesocket(descriptor()); setSocketState(SocketDisconnected); disconnectedEvent(this); } void NewNet::ServerSocket::process() { if (readyState() & StateReceive) { int client = ::accept(descriptor(), 0, 0); if(client == -1) { if ((WSAGetLastError() == EAGAIN) || (WSAGetLastError() == EMFILE)) setReadyState(readyState() & ~StateReceive); else NNLOG("newnet.net.warn", "Ignoring error '%i' in ServerSocket::accept().", errno); } else { if (!setnonblocking(client)) NNLOG("newnet.net.warn", "Couldn't set socket %i to non blocking (errno: %i)", client, errno); acceptedEvent(client); } } } museek+-0.2+svn20100315.r1208/NewNet/nnserversocket.h0000644000175000017500000000526611102357015021312 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_SERVERSOCKET_H #define NEWNET_SERVERSOCKET_H #include "nnsocket.h" #include "nnevent.h" #include namespace NewNet { //! Base class for network server sockets. /*! Provides a base class that will monitor the state of the socket and emit events when something happens. Note: this class is usually not used by your application. Applications should use TcpServerSocket, UnixServerSocket or one of the associated factories instead. */ class ServerSocket : public Socket { public: //! Create an empty server socket. /*! This will create an empty server socket. The server socket starts in an uninitialized state without a descriptor. */ ServerSocket() : Socket() { } //! Disconnect the server socket. /*! Closes the server socket and clean up any remaining resources. */ virtual void disconnect(); //! Process network events. /*! Gets called by the reactor detects a new connection attempt on the server socket. When that happens, accept() is called. */ virtual void process(); //! Emitted when the socket can't start listening. /*! Subclasses emit this event when there's an error when it's attempting to start to listen. */ Event cannotListenEvent; //! Emitted when the socket starts listening. /*! Subclasses emit this when they start listening. */ Event listeningEvent; //! Emitted when a client has been accepted. /*! Emitted when a client has been accepted. The argument is the descriptor for the new client. */ Event acceptedEvent; //! Emitted when the server socket has been closed. /*! Emitted when the server socket has been closed. */ Event disconnectedEvent; }; } #endif // NEWNET_SERVERSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnlog.cpp0000644000175000017500000000446611102357015017710 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnlog.h" #include #include #include NewNet::Log NewNet::log; void NewNet::Log::operator()(const std::string & domain, const char * fmt, ...) { bool enabled = std::find(m_EnabledDomains.begin(), m_EnabledDomains.end(), domain) != m_EnabledDomains.end(); if(! (enabled || m_AllEnabled)) return; int size = 100; char * p, * np; if((p = (char *)malloc(size)) == 0) return; while(1) { va_list ap; va_start(ap, fmt); int n = vsnprintf(p, size, fmt, ap); va_end(ap); if(n > -1 && n < size) { LogNotify notice; notice.domain = domain; notice.message = p; logEvent(¬ice); free(p); return; } if(n > -1) size = n + 1; else size *= 2; if((np = (char *)realloc(p, size)) == 0) { free(p); return; } else p = np; } } void NewNet::Log::enable(const std::string & domain) { if(domain == "ALL") m_AllEnabled = true; else if(std::find(m_EnabledDomains.begin(), m_EnabledDomains.end(), domain) == m_EnabledDomains.end()) m_EnabledDomains.push_back(domain); } void NewNet::Log::disable(const std::string & domain) { if(domain == "ALL") { m_AllEnabled = false; return; } std::vector::iterator it; it = std::find(m_EnabledDomains.begin(), m_EnabledDomains.end(), domain); if(it != m_EnabledDomains.end()) m_EnabledDomains.erase(it); } museek+-0.2+svn20100315.r1208/NewNet/nntcpserversocket.h0000644000175000017500000000412711102357015022014 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_TCPSERVERSOCKET_H #define NEWNET_TCPSERVERSOCKET_H #include "nnserversocket.h" namespace NewNet { //! Implements a TCP/IP server class. /*! Implements a TCP/IP server class that can listen for incoming connections on a specified host and port. */ class TcpServerSocket : public ServerSocket { public: //! Create a TCP/IP server socket. /*! Creates a TCP/IP server socket. The socket isn't yet bound to anything, so call listen() to activate the server socket. */ TcpServerSocket() : ServerSocket(), m_ListenPort(0) { } //! Listen on a port on the specified host. /*! Starts listening on a port on the specified host. */ void listen(const std::string & host, unsigned int port); //! Listen on a port of any active network adapter. */ /*! Starts listening on the specified port of all active network adapters. */ void listen(unsigned int port) { listen(std::string(), port); } //! Return listen port /*! Return which port this server socket is listening on. */ unsigned int listenPort() { return m_ListenPort; } private: unsigned int m_ListenPort; }; } #endif // NEWNET_TCPSERVERSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnreactor.h0000644000175000017500000001351411131703176020232 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_REACTOR_H #define NEWNET_REACTOR_H #include "nnobject.h" #include "nnsocket.h" #include "nnrefptr.h" #include "nnevent.h" #include "util.h" #include #include /* Update timeout to 'ms' miliseconds after the current time if that's sooner than the current timeout, or if no timeout has been set yet. */ inline void fixtime(struct timeval & timeout, long ms, bool & timeout_set) { struct timeval now; gettimeofday(&now, 0); if((! timeout_set) || (difftime(timeout, now) > ms)) { timeout.tv_sec = now.tv_sec + (ms / 1000); timeout.tv_usec = now.tv_usec + (ms % 1000) * 1000; if(timeout.tv_usec >= 1000000) { timeout.tv_sec += 1; timeout.tv_usec -= 1000000; } timeout_set = true; } } namespace NewNet { //! Monitors sockets and timeouts. This is what drives your application. /*! The Reactor class provides your application with a main-loop. It monitors the sockets and waits for timeouts to occur. */ class Reactor : public Object { public: //! Constructor. /*! Create a new reactor */ Reactor(); #ifndef DOXYGEN_UNDOCUMENTED ~Reactor(); #endif // DOXYGEN_UNDOCUMENTED //! Add a socket to the reactor. /*! Add a socket to the reactor's watch list so that the socket will be able to receive and process events. Note: stores a RefPtr to the socket object */ void add(Socket * socket); //! Remove a socket from the reactor. /*! Remove a socket from the reactor's watch list. Note: The reactor stores a RefPtr to the socket, if the reactor holds the last reference to the socket, it will get deleted automatically. */ void remove(Socket * socket); //! Start the main loop. /*! Call this to start the reactor's main loop. The reactor will start listening for events and waiting for timeouts to happen. This method will not return until there are no sockets and timeouts left, or when Reactor::stop() is called. */ void run(); //! Stop the main loop. /*! Call this to exit the reactor's main loop. Note that the reactor will first process any pending events before exiting. */ void stop(); //! Convenience definition for timeouts. /*! A convenience definition for timeout callback. Note: Timeouts aren't actually used as events, but the Event::Callback class is used to define the callback for a timeout. */ typedef Event Timeout; //! Add a new timeout to the reactor. /*! Add a new timeout callback to the reactor so that the callback will be invoked after approximately msec miliseconds. Note: stores a RefPtr to the callback object. */ Timeout::Callback * addTimeout(long msec, Timeout::Callback * callback); //! Add a new timeout to the reactor. /*! Add a new timeout callback to the reactor so that the method of the specified object will be invoked after approximately msec miliseconds. Note: stores a RefPtr to the generated callback object. */ template Timeout::Callback * addTimeout(long msec, ObjectType * object, MethodType method) { return addTimeout(msec, Timeout::bind(object, method)); } //! Remove a timeout from the reactor. /*! This removes all pending timeouts that have 'callback' as a callback and frees the RefPtr on the callback object. */ void removeTimeout(Timeout::Callback * callback); //! Returns the maximum number of sockets that can be opened /*! On linux this is usually 1024 */ int maxSocketNo(); //! Returns the current number of sockets opened int currentSocketNo(); //! Returns the highest file descriptor currently used int maxFileDescriptor(); //! Invoked by libevent when a socket wakes up /*! Invoked by libevent when a socket wakes up */ void eventCallback(int, short, void *); private: struct event mEvTimeout; protected: //! Prepare sockets to be watched by the reactor. /*! Prepare sockets to be watched by the reactor. */ void checkSockets(struct timeval & timeout, bool & timeout_set); //! Check for timeouts and emit needed actions. Set up next reactor wake up. /*! Check for timeouts and emit needed actions. Set up next reactor wake up. */ bool checkTimeouts(struct timeval & timeout, bool & timeout_set); //! Set up every data needed for the next reactor cycle. /*! Set up every data needed for the next reactor cycle. */ bool prepareReactorData(); int m_maxSocketNo; int m_maxFD; std::vector > m_Sockets; #ifndef DOXYGEN_UNDOCUMENTED struct Timeouts; struct Timeouts * m_Timeouts; #ifdef WIN32 void * m_WsaData; #endif // WIN32 #endif }; } #ifndef DOXYGEN_UNDOCUMENTED typedef std::pair > TimeoutItem; struct NewNet::Reactor::Timeouts { std::vector timeouts; }; #endif #endif // NEWNET_REACTOR_H museek+-0.2+svn20100315.r1208/NewNet/nnunixserversocket.h0000644000175000017500000000342511102357015022211 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_UNIXSERVERSOCKET_H #define NEWNET_UNIXSERVERSOCKET_H #include "nnserversocket.h" namespace NewNet { //! Implements a unix socket server class. /*! Implements a unix socket server class that can listen for incoming connections on a specified path. */ class UnixServerSocket : public ServerSocket { public: //! Create a unix server socket. /*! Creates a unix server socket. The socket isn't yet bound to anything, so call listen() to activate the server socket. */ UnixServerSocket() : ServerSocket() { } //! Listen on a unix path. /*! Starts listening on the specified path. */ void listen(const std::string & path); //! Disconnect the server socket. /*! Closes the server socket and removes the unix socket file. */ virtual void disconnect(); private: std::string m_Path; }; } #endif // NEWNET_UNIXSERVERSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnclientsocket.cpp0000644000175000017500000001060511133071527021613 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnclientsocket.h" #include "nnlog.h" #include "platform.h" #include void NewNet::ClientSocket::disconnect(bool invoke) { if((socketState() == SocketUninitialized) || (descriptor() < 0)) { NNLOG("newnet.net.warn", "Trying to disconnect an uninitialized client socket."); if (invoke) disconnectedEvent(this); return; } closesocket(descriptor()); setSocketState(SocketDisconnected); if (invoke) disconnectedEvent(this); } void NewNet::ClientSocket::process() { if(socketState() == SocketConnecting) { if(readyState() & StateSend) { sockopt_t so_error; socklen_t so_len = sizeof(int); getsockopt(descriptor(), SOL_SOCKET, SO_ERROR, &so_error, &so_len); if(so_len != sizeof(int) || ! so_error) { NNLOG("newnet.net.debug", "Connected to host"); setSocketState(SocketConnected); connectedEvent(this); } else { NNLOG("newnet.net.warn", "Cannot connect to host, error: %i.", so_error); setSocketError(ErrorCannotConnect); cannotConnectEvent(this); return; } } } if(readyState() & StateException) { unsigned char buf; ssize_t received = ::recv(descriptor(), (char *)&buf, 1, MSG_OOB); if(received < 1) { NNLOG("newnet.net.warn", "Socket %u encountered error %i. Closing it.", descriptor(), errno); closesocket(descriptor()); setSocketError(ErrorUnknown); disconnectedEvent(this); return; } m_ReceiveBuffer.append(&buf, 1); dataReceivedEvent(this); } if(readyState() & StateReceive) { unsigned char buf[1024]; ssize_t received = ::recv(descriptor(), (char *)buf, 1024, 0); if(received == -1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { NNLOG("newnet.net.debug", "EAGAIN while receiving data on socket %i.", descriptor()); setReadyState(readyState() & ~StateReceive); } else { NNLOG("newnet.net.warn", "Socket %u encountered error %i. Closing it.", descriptor(), errno); closesocket(descriptor()); setSocketError(ErrorUnknown); disconnectedEvent(this); return; } } else if(received == 0) { NNLOG("newnet.net.debug", "Socket %u was disconnected.", descriptor()); closesocket(descriptor()); setSocketState(SocketDisconnected); disconnectedEvent(this); return; } else { NNLOG("newnet.net.debug", "Received %i bytes on socket %u.", received, descriptor()); if(downRateLimiter()) downRateLimiter()->transferred(received); m_ReceiveBuffer.append(buf, received); dataReceivedEvent(this); } } if((readyState() & StateSend) && dataWaiting()) { size_t n = std::min((size_t)1024, m_SendBuffer.count()); ssize_t sent = ::send(descriptor(), (const char *)m_SendBuffer.data(), n, 0); if(sent < 0) { if(errno == EAGAIN) setReadyState(readyState() & ~StateSend); else { NNLOG("newnet.net.warn", "Socket %u encountered error %i. Closing it.", descriptor(), errno); closesocket(descriptor()); setSocketError(ErrorUnknown); disconnectedEvent(this); return; } } else { NNLOG("newnet.net.debug", "Sent %i bytes to socket %u.", sent, descriptor()); if(upRateLimiter()) upRateLimiter()->transferred(sent); m_SendBuffer.seek(sent); setDataWaiting(m_SendBuffer.count() != 0); dataSentEvent(this); } } } museek+-0.2+svn20100315.r1208/NewNet/nnweakrefptr.h0000644000175000017500000000772411102357015020746 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_WEAKREFPTR_H #define NEWNET_WEAKREFPTR_H #include "nnbaseptr.h" #include "nnguardobject.h" namespace NewNet { class Object; //! A weak reference pointer class. /*! A weak reference pointer as implemented in this class points at NULL after the object it once pointed as is deleted. This is done by registering a callback to the object's guard object so that the pointer class will be notified when the object is deleted. */ template class WeakRefPtr : public BasePtr { private: #ifndef DOXYGEN_UNDOCUMENTED class GuardObjectCallback : public GuardObject::Callback { public: GuardObjectCallback(WeakRefPtr * ptr) : m_Ptr(ptr) { } void operator()(Object * p) { m_Ptr->objectDestroyed(p); } private: WeakRefPtr * m_Ptr; }; GuardObjectCallback * m_GuardObjectCallback; #endif // DOXYGEN_UNDOCUMENTED public: //! Create a new weak reference pointer that points at NULL. /*! Create a new weak reference pointer that points at NULL. */ WeakRefPtr() : BasePtr() { m_GuardObjectCallback = new GuardObjectCallback(this); } //! Create a new weak reference pointer that points at t. /*! Create a new weak reference pointer that points at t. */ WeakRefPtr(T * t) : BasePtr(t) { m_GuardObjectCallback = new GuardObjectCallback(this); if(t) t->guardObject() += m_GuardObjectCallback; } //! Create a new weak reference pointer that points at t. /*! Create a new weak reference pointer that points at t. */ WeakRefPtr(const WeakRefPtr& t) : BasePtr(t.m_Ptr) { m_GuardObjectCallback = new GuardObjectCallback(this); if(t.m_Ptr) t.m_Ptr->guardObject() += m_GuardObjectCallback; } //! Assign t's object to this pointer. /*! Assign t's object to this pointer. */ WeakRefPtr& operator=(const WeakRefPtr& t) { if(BasePtr::m_Ptr == t.m_Ptr) return *this; if(BasePtr::m_Ptr) BasePtr::m_Ptr->guardObject() -= m_GuardObjectCallback; BasePtr::m_Ptr = t.m_Ptr; if(t.m_Ptr) BasePtr::m_Ptr->guardObject() += m_GuardObjectCallback; return *this; } //! Assign object t to this pointer. /*! Assign object t to this pointer. */ WeakRefPtr& operator=(T * t) { if(BasePtr::m_Ptr == t) return *this; if(BasePtr::m_Ptr) BasePtr::m_Ptr->guardObject() -= m_GuardObjectCallback; BasePtr::m_Ptr = t; if(t) t->guardObject() += m_GuardObjectCallback; return *this; } #ifndef DOXYGEN_UNDOCUMENTED ~WeakRefPtr() { if(BasePtr::m_Ptr) BasePtr::m_Ptr->guardObject() -= m_GuardObjectCallback; delete m_GuardObjectCallback; } #endif // DOXYGEN_UNDOCUMENTED protected: #ifndef DOXYGEN_UNDOCUMENTED void objectDestroyed(Object * p) { #ifdef NN_PTR_DEBUG assert(p == BasePtr::m_Ptr); #endif BasePtr::m_Ptr = 0; } #endif // DOXYGEN_UNDOCUMENTED }; } #undef NN_PTR_CHECK #endif // NEWNET_REFPTR_H museek+-0.2+svn20100315.r1208/NewNet/nnbaseptr.h0000644000175000017500000000701711102357015020227 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_BASEPTR_H #define NEWNET_BASEPTR_H #ifdef NN_PTR_DEBUG #include #ifdef NN_PTR_DEBUG_ASSERT #define NN_PTR_CHECK { assert(m_Ptr != 0); } #else #include #define NN_PTR_CHECK { if(! m_Ptr) std::cerr << "Warning: Dereferencing NULL pointer" << std::endl; } #endif #else #define NN_PTR_CHECK #endif namespace NewNet { class Object; //! Base class for wrapped pointers. /*! This is a base wrapper class for pointers. This class is pretty useless when used directly, subclass it or use RefPtr or WeakRefPtr instead. */ template class BasePtr { public: //! Create a new BasePtr. /*! Create a new BasePtr. The pointer always points at NULL. */ BasePtr() : m_Ptr(0) { } //! Create a new initialized BasePtr. /*! Create a new BasePtr. The pointer will point at T * t. */ BasePtr(T * t) : m_Ptr(t) { } //! Return the pointer. /*! Return the pointer. */ T * ptr() const { return m_Ptr; } //! Cast operator to T *. /*! This provides a cast operator to T *. */ operator T*() const { return m_Ptr; } //! Dereferencing operator to T &. /*! This provides a dereferencing operator to T &. Dereferencing a NULL pointer is checked if compiled with -DNN_PTR_DEBUG. */ T & operator*() { NN_PTR_CHECK return *m_Ptr; } //! Dereferencing operator to const T &. /*! This provides a dereferencing operator to const T &. Dereferencing a NULL pointer is checked if compiled with -DNN_PTR_DEBUG. */ const T & operator*() const { NN_PTR_CHECK return *m_Ptr; } //! Dereferencing operator to T *. /*! This provides a dereferencing operator to T *. Dereferencing a NULL pointer is checked if compiled with -DNN_PTR_DEBUG. */ T * operator->() { NN_PTR_CHECK return m_Ptr; } //! Dereferencing operator to const T *. /*! This provides a dereferencing operator to const T *. Dereferencing a NULL pointer is checked if compiled with -DNN_PTR_DEBUG. */ const T * operator->() const { NN_PTR_CHECK return m_Ptr; } //! Determine wether the pointer is valid (not NULL). /*! Return true if the pointer is not NULL. */ bool isValid() const { return m_Ptr != 0; } //! Check if the pointer is NULL. /*! Returns true if the pointer is NULL. */ bool operator!() const { return m_Ptr == 0; } protected: //! The pointer value. /*! Provides the current pointer value. */ T * m_Ptr; }; } #undef NN_PTR_CHECK #endif // NEWNET_REFPTR_H museek+-0.2+svn20100315.r1208/NewNet/nnevent.h0000644000175000017500000002145311102357015017710 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_EVENT_H #define NEWNET_EVENT_H #include "nnobject.h" #include "nnrefptr.h" #include #include #include namespace NewNet { //! Simple event dispatcher class. /*! The NewNet::Event class provides a simple and easy to use event aggregation solution. Callbacks can be registered to an event that will be invoked upon emitting the event. */ template class Event : public Object { public: //! Abstract callback type for Event. /*! A callback can be added to an event so that operator()(T t) will be called when the event is emitted. */ class Callback : public Object { public: //! Constructor. Callback() { } //! Destructor. virtual ~Callback() { } //! Slot method. /*! Override this and implement your callback function. */ virtual void operator()(T t) = 0; #ifndef DOXYGEN_UNDOCUMENTED /* Callback was added to an Event */ void onConnected(Event * event) { m_Events.push_back(event); } /* Callback was disconnected from an Event */ void onDisconnected(Event * event) { typename std::vector::iterator it; it = std::find(m_Events.begin(), m_Events.end(), event); if (it != m_Events.end()) m_Events.erase(it); } #endif // DOXYGEN_UNDOCUMENTED //! Disconnect this callback from all events. /*! Calling this will result in the disconnection of the callback from all the events it is registered to. Note: Events store a RefPtr to the callbacks. The callback class might be deleted when disconnecting from all registered events. */ void disconnect() { /* Artificially increase our reference count. Otherwise we might get prematurely deleted when we get disconnected from the last Event */ ++(this->refCounter()); /* Disconnect from all the events we're registered to */ while(! m_Events.empty()) m_Events.front()->disconnect(this); /* Decrease the refrence count again and delete if necessary */ if(--(this->refCounter())) delete this; } private: /* Private copy constructor, you don't want this happening */ Callback(const Callback &) { } std::vector m_Events; }; private: #ifndef DOXYGEN_UNDOCUMENTED /* Callback to a method of a NewNet::Object */ template class BoundCallback : public Callback { private: /* We use this to track deletion of our Object */ class GuardObjectCallback : public GuardObject::Callback { public: GuardObjectCallback(BoundCallback * callback) : m_Callback(callback) { } /* If the Object is destroyed, notify our Callback */ void operator()(Object *) { m_Callback->onObjectDeleted(); } private: BoundCallback * m_Callback; }; GuardObjectCallback * m_GuardObjectCallback; public: BoundCallback(ObjectType * object, MethodType method) : m_Object(object), m_Method(method) { /* Register to the Object's delete guard */ m_GuardObjectCallback = new GuardObjectCallback(this); m_Object->guardObject() += m_GuardObjectCallback; } ~BoundCallback() { /* If the object is still valid, remove our delete callback from its delete guard */ if(m_Object) m_Object->guardObject() -= m_GuardObjectCallback; delete m_GuardObjectCallback; } void operator()(T t) { /* Since C++ methods are internally called as Class::Method(object, ...), this results in a valid C++ bound method call. */ if(m_Object) std::bind1st(std::mem_fun(m_Method), m_Object)(t); } protected: /* Object we're bound to was deleted, reset pointer and disconnect from all Events we're registered to */ void onObjectDeleted() { m_Object = 0; Callback::disconnect(); } private: /* Private copy constructor, you don't want this happening */ BoundCallback(const BoundCallback &) { } ObjectType * m_Object; MethodType m_Method; }; #endif // DOXYGEN_UNDOCUMENTED public: //! Constructor. /*! Create a new event to which you can register callbacks. */ Event() { } //! Copy constructor. /*! When an event is copied, all the callbacks registered to the original event will also be connected to this event. */ Event(const Event & that) { typename std::vector >::const_iterator it, end; end = that.m_Callbacks.end(); for(it = that.m_Callbacks.begin(); it != end; ++it) connect(*it); } //! Destructor. /*! Disconnects all callbacks from the event. Note: see clear(). */ virtual ~Event() { /* Disconnect all Callbacks */ clear(); } //! Empty the event callback list. /*! Call this to remove all the callbacks from this event. Note: the event stores a RefPtr to all the callbacks registered. Clearing the event may delete the callback if there are no other references to it. */ void clear() { while(! m_Callbacks.empty()) disconnect(m_Callbacks.front()); } //! Connect a callback to the event. /*! Add a callback to this event so that it will get invoked when the event is emitted. Note: stores a RefPtr to the callback. */ Callback * connect(Callback * callback) { /* Push a RefPtr to the Callback to our list */ m_Callbacks.push_back(callback); /* Notify the Callback that it's connected to us */ callback->onConnected(this); return callback; } //! Create a callback to a bound method. /*! This will construct a callback object that will invoke a method of an object. */ template static Callback * bind(ObjectType * object, MethodType method) { return new BoundCallback(object, method); } //! Connect callback to a method of an object to the event. /*! Add a callback to a method of an object so that it will get invoked when the event is emitted. Note: stores a RefPtr to the newly created callback. */ template Callback * connect(ObjectType * object, MethodType method) { return connect(bind(object, method)); } //! Disconnect a callback from the event. /*! Remove a callback from the invocation list. Note: the event stores a RefPtr to the callback. If the event holds the last RefPtr, the callback will be deleted. */ void disconnect(Callback * callback) { /* Artificially increase the reference count so it doesn't get deleted prematurely */ ++(callback->refCounter()); /* Delete the Callback from our list */ typename std::vector >::iterator it; it = std::find(m_Callbacks.begin(), m_Callbacks.end(), callback); if (it != m_Callbacks.end()) m_Callbacks.erase(it); /* Notify the Callback it was disconnected */ callback->onDisconnected(this); /* Decrease the reference count of the Callback and delete if necessary */ if(--(callback->refCounter())) delete callback; } //! Emit the event. /*! Emit the event, invokes the Callback::operator()(T t) method of all registered callbacks. */ void operator()(T t) { std::vector > callbacks(m_Callbacks); typename std::vector >::iterator it, end = callbacks.end(); for(it = callbacks.begin(); it != end; ++it) { (*(*it))(t); } } private: std::vector > m_Callbacks; }; } #endif // NEWNET_EVENT_H museek+-0.2+svn20100315.r1208/NewNet/nnobject.h0000644000175000017500000000540611102357015020035 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_OBJECT_H #define NEWNET_OBJECT_H #include "nnguardobject.h" #include "nnrefcounter.h" #ifdef NN_PTR_DEBUG #ifdef NN_PTR_DEBUG_ASSERT #include #else #include #endif // NN_PTR_DEBUG_ASSERT #endif // NN_PTR_DEBUG namespace NewNet { //! Base class for objects. /*! This provides a base class for NewNet objects. It provides a reference counter and a guard object for detecting deletion of the object. */ class Object { public: //! Constructor. /*! This constructs a new object. Note that you don't have to call this. It's an empty method. */ Object() { } //! Copy constructor. /*! Ensures that the reference count of the copied object is 0 and that the guard object is empty. */ Object(const Object &) { } //! Destructor. /*! When an object is destroyed, the guard object will be emitted so the registered callbacks get called. */ virtual ~Object() { m_GuardObject.emit(this); #ifdef NN_PTR_DEBUG #ifdef NN_PTR_DEBUG_ASSERT assert(m_RefCounter.count() == 0); #else if(m_RefCounter.count() != 0) std::cerr << "Warning: Object " << this << " deleted while refcount = " << m_RefCounter.count() << "." << std::endl; #endif // NN_PTR_DEBUG_ASSERT #endif // NN_PTR_DEBUG } //! Reference to the guard object. /*! This returns a reference to the object's guard object so you can hook in your own callbacks if you want to. */ GuardObject & guardObject() { return m_GuardObject; } //! Reference to the reference counter. /*! This returns a reference to the object's reference counter so you can manipulate it if you want to. */ RefCounter & refCounter() { return m_RefCounter; } private: GuardObject m_GuardObject; RefCounter m_RefCounter; }; } #endif // NEWNET_OBJECT_H museek+-0.2+svn20100315.r1208/NewNet/nnbuffer.h0000644000175000017500000000645611102357015020046 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_BUFFER_H #define NEWNET_BUFFER_H #include "nnobject.h" #include #include namespace NewNet { //! A character buffer class. /*! This class provides a simple character buffer that is used by ClientSocket to buffer incoming and outgoing network data. */ class Buffer : public NewNet::Object { public: //! Create an empty buffer. /*! Create an empty buffer. */ Buffer(); //! Copy an existing buffer. /*! Copy an existing buffer. */ Buffer(const Buffer & that); //! Copy an existing buffer. /*! Copy an exisiting buffer. */ Buffer & operator=(const Buffer & that); //! Destructor. /*! Frees all memory allocated by the buffer. */ ~Buffer(); //! Get a pointer to the start of the buffer. /*! Get a pointer to the start of the character buffer. */ unsigned char * data() { return m_Ptr + m_Pos; } //! Get a const pointer to the start of the buffer. /*! Get a const pointer to the start of the character buffer. */ const unsigned char * data() const { return m_Ptr + m_Pos; } //! Get the number of bytes that are in the buffer. /*! Get the number of bytes that are currently stored in the character buffer. */ size_t count() const { return m_Count; } //! Determine if the buffer is empty. /*! Determine if the character buffer is currently empty. */ bool empty() const { return m_Count == 0; } //! Seek forward in the buffer. /*! Seek forward in the character buffer. Note: this asserts that there are enough bytes in the buffer for the seek operation. It will raise a signal if there's not. Note: calling this may move or reallocate the buffer. All earlier results of data() will be invalidated. */ void seek(size_t n) { assert(n <= m_Count); m_Pos += n; m_Count -= n; } //! Append data to the buffer /*! Append data to the character buffer. Note: calling this may move or reallocate the buffer. All earlier results of data() will be invalidated. */ void append(const unsigned char * data, size_t n); //! Clear the buffer /*! Seeks to the end of the character buffer essentially clearing it. */ void clear() { seek(count()); } private: unsigned char * m_Ptr; size_t m_Pos, m_Count, m_Left; }; } #endif // NEWNET_BUFFER_H museek+-0.2+svn20100315.r1208/NewNet/CMakeLists.txt0000644000175000017500000000146711131370316020626 0ustar gandalfgandalfproject(NewNet CXX) # Find Event find_package(Event REQUIRED) include_directories(${Event_INCLUDE_DIRS}) if (Event_LIBRARIES AND EVENT_FOUND) set(NEWNET_SOURCES nnbuffer.cpp nnclientsocket.cpp nnlog.cpp nnpath.cpp nnratelimiter.cpp nntcpserversocket.cpp nnreactor.cpp nnserversocket.cpp nntcpclientsocket.cpp ) if(UNIX) set(NEWNET_SOURCES ${NEWNET_SOURCES} nnunixclientsocket.cpp nnunixserversocket.cpp ) endif(UNIX) add_library(NewNet STATIC ${NEWNET_SOURCES}) # Link the newnet binary to some libraries. target_link_libraries( NewNet ${Event_LIBRARIES} ) else(Event_LIBRARIES AND EVENT_FOUND) MESSAGE("!!! NewNet will NOT be installed..") endif(Event_LIBRARIES AND EVENT_FOUND) museek+-0.2+svn20100315.r1208/NewNet/nnunixclientsocket.cpp0000644000175000017500000000543611131641225022521 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnunixclientsocket.h" #include "nnlog.h" #include "nnreactor.h" #include "platform.h" #include void NewNet::UnixClientSocket::connect(const std::string & path) { assert((descriptor() == -1) || (socketState() == SocketUninitialized)); setSocketState(SocketConnecting); if(path.length() >= UNIX_PATH_MAX) { NNLOG("newnet.net.warn", "Unix socket path too long: '%s'.", path.c_str()); setSocketError(ErrorInvalidPath); cannotConnectEvent(this); return; } struct sockaddr_un address; memset(&address, 0, sizeof(address)); address.sun_family = AF_UNIX; memcpy(address.sun_path, path.c_str(), path.length()+1); NNLOG("newnet.net.debug", "Connecting to unix socket '%s'.", path.c_str()); int sock = socket(PF_UNIX, SOCK_STREAM, 0); fcntl(sock, F_SETFL, O_NONBLOCK); setDescriptor(sock); // Add a connection timeout if (reactor()) { m_ConnectionTimeout = reactor()->addTimeout(120000, this, &UnixClientSocket::onConnectionTimeout); } connectedEvent.connect(this, &UnixClientSocket::onConnected); if(::connect(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_un)) == 0) { // When using non blocking socket (most of the time), we don't get here. NNLOG("newnet.net.debug", "Connected to unix socket '%s'.", path.c_str()); setSocketState(SocketConnected); connectedEvent(this); return; } else if(errno != EINPROGRESS) { // When using non blocking socket (most of the time), we don't get here. NNLOG("newnet.net.warn", "Cannot connect to unix socket '%s', error: %i.", path.c_str(), errno); closesocket(sock); setSocketError(ErrorCannotConnect); cannotConnectEvent(this); return; } } void NewNet::UnixClientSocket::onConnectionTimeout(long) { cannotConnectEvent(this); } void NewNet::UnixClientSocket::onConnected(ClientSocket *) { if (reactor()) reactor()->removeTimeout(m_ConnectionTimeout); } museek+-0.2+svn20100315.r1208/NewNet/nnratelimiter.h0000644000175000017500000000604411102357015021107 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_RATELIMITER_H #define NEWNET_RATELIMITER_H #include "nnobject.h" namespace NewNet { //! Helper class for transfer rate limiting. /*! This provides a transfer rate tracker and a method to calculate the next window of opportunity (the moment the maximum transfer rate is no longer broken) */ class RateLimiter : public Object { public: //! Constructor. /*! Create a new rate limiter. The limit will be initialized to -1 which means that there will be no rate limiting. */ RateLimiter(); #ifndef DOCYGEN_UNDOCUMENTED ~RateLimiter(); #endif // DOXYGEN_UNDOCUMENTED //! Get the current transfer rate limit. /*! This returns the current transfer rate limit in bytes per second. 0 means that no traffic will be allowed to be sent. A value of -1 means that there is no limit set. */ ssize_t limit() const { return m_Limit; } //! Set the transfer rate limit. /*! This changes the current transfer rate limit. The limit is measured in bytes per second. A value of 0 means that no traffic will be allowed to pass. A value of -1 means that no limit is enforced. */ void setLimit(ssize_t limit) { m_Limit = limit; } //! Feed bytes to the collector. /*! This adds a frame of bytes to the rate limit collector. NewNet::ClientSocket calls this whenever it received or sent data of the socket. */ void transferred(ssize_t bytes); //! Next window of opportunity. /*! This will predict when the rate limit will be 'unbreached' and when data will be allowed to be transferred again. If the limit is set to 0 this always returns 60000 (60 seconds). If the limit is set to -1, it always returns 0. Otherwise, it returns the number of miliseconds until the next opportunity. */ long nextWindow(); private: /* Flush old data from the vector */ void flush(); ssize_t m_Limit; #ifndef DOXYGEN_UNDOCUMENTED // The collection data depends on platform specific types, hide it struct Data; struct Data * m_Data; #endif // DOXYGEN_UNDOCUMENTED }; } #endif // NEWNET_RATELIMITER_H museek+-0.2+svn20100315.r1208/NewNet/nnunixserversocket.cpp0000644000175000017500000000503111102357015022537 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnunixserversocket.h" #include "nnlog.h" #include "platform.h" #include void NewNet::UnixServerSocket::listen(const std::string & path) { if(path.length() >= UNIX_PATH_MAX) { NNLOG("newnet.net.warn", "Unix socket path too long: '%s'.", path.c_str()); setSocketError(ErrorInvalidPath); cannotListenEvent(this); } unlink(path.c_str()); struct sockaddr_un address; memset(&address, 0, sizeof(address)); address.sun_family = AF_UNIX; memcpy(address.sun_path, path.c_str(), path.length()); int sock = socket(PF_UNIX, SOCK_STREAM, 0); fcntl(sock, F_SETFL, O_NONBLOCK); mode_t old_umask = umask(0177); int ret = bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_un)); umask(old_umask); if(ret != 0) { NNLOG("newnet.net.warn", "Cannot bind unix socket to '%s', error: %i.", path.c_str(), errno); closesocket(sock); setSocketError(ErrorCannotBind); cannotListenEvent(this); return; } if (::listen(sock, 3) != 0) { NNLOG("newnet.net.warn", "Cannot listen on unix socket '%s', error: %i.", path.c_str(), errno); closesocket(sock); setSocketError(ErrorCannotListen); cannotListenEvent(this); return; } m_Path = path; setDescriptor(sock); setSocketState(SocketListening); listeningEvent(this); NNLOG("newnet.net.debug", "Listening on unix socket '%s'.", path.c_str()); } void NewNet::UnixServerSocket::disconnect() { if((descriptor() == -1) || (socketState() != SocketListening)) { NNLOG("newnet.net.warn", "Trying to disconnect an uninitialized unix server socket."); return; } ServerSocket::disconnect(); unlink(m_Path.c_str()); } museek+-0.2+svn20100315.r1208/NewNet/nnratelimiter.cpp0000644000175000017500000000542111140063167021444 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnratelimiter.h" #include "platform.h" #include "util.h" #include #include #include #include /* Keep at most x seconds of data in the rate buffer */ #define MAX_HISTORY 5 #ifndef DOXYGEN_UNDOCUMENTED typedef std::pair RateData; struct NewNet::RateLimiter::Data { std::vector rateData; }; #endif // DOXYGEN_UNDOCUMENTED NewNet::RateLimiter::RateLimiter() { m_Limit = -1; m_Data = new NewNet::RateLimiter::Data; } #ifndef DOXYGEN_UNDOCUMENTED NewNet::RateLimiter::~RateLimiter() { delete m_Data; } #endif // DOXYGEN_UNDOCUMENTED void NewNet::RateLimiter::transferred(ssize_t n) { struct timeval now; gettimeofday(&now, 0); m_Data->rateData.push_back(RateData(now, n)); } #ifndef timercmp #define timercmp(tvp, uvp, cmp)\ ((tvp)->tv_sec cmp (uvp)->tv_sec ||\ (tvp)->tv_sec == (uvp)->tv_sec &&\ (tvp)->tv_usec cmp (uvp)->tv_usec) #endif void NewNet::RateLimiter::flush() { /* Flush all entries that are older than MAX_HISTORY second(s) */ struct timeval tv; gettimeofday(&tv, 0); tv.tv_sec -= MAX_HISTORY; while((! m_Data->rateData.empty()) && timercmp(&tv, &m_Data->rateData.front().first, >)) m_Data->rateData.erase(m_Data->rateData.begin()); } long NewNet::RateLimiter::nextWindow() { flush(); if(m_Limit == -1) return 0; else if(m_Limit == 0) return 60000; ssize_t total = 0; std::vector::reverse_iterator it, end = m_Data->rateData.rend(); for(it = m_Data->rateData.rbegin(); it != end; ++it) { total += (*it).second; if(total >= m_Limit) { /* Rate limit will be 'unbreached' one second after this frame. */ struct timeval now; gettimeofday(&now, 0); struct timeval tv((*it).first); tv.tv_sec += 1; long d = difftime(tv, now); if(d <= 0) return 0; else return d; } } return 0; } museek+-0.2+svn20100315.r1208/NewNet/nnrefcounter.h0000644000175000017500000000506011102357015020737 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_REFCOUNTER_H #define NEWNET_REFCOUNTER_H #ifdef NN_PTR_DEBUG #ifdef NN_PTR_DEBUG_ASSERT #include #else #include #endif // NN_PTR_DEBUG_ASSERT #endif // NN_PTR_DEBUG namespace NewNet { //! Helper class for reference counting. /*! This class basically provides an int that's guaranteed to be 0 when the refcounter is constructed. Provides increment and decrement operators. The decrement operator will return true if the reference count drops to zero. */ class RefCounter { public: //! Create a new reference counter object. /*! Creates a new reference counter object with the reference count initialized at 0. */ RefCounter() : m_RefCount(0) { } //! Increment the reference count. /*! Increment the reference count by 1. */ void operator++() { m_RefCount += 1; } //! Decrement the reference count. /*! Decrement the reference count by 1. Returns true if the reference count drops to zero. Delete the owner object if that happens. */ bool operator--() { #ifdef NN_PTR_DEBUG #ifdef NN_PTR_DEBUG_ASSERT assert(m_RefCount > 0); #else if(m_RefCount == 0) { std::cerr << "Warning: Attempting to decrement refcount of unreferenced object!" << std::endl; return true; } #endif // NN_PTR_DEBUG_ASSERT #endif // NN_PTR_DEBUG m_RefCount -= 1; return m_RefCount == 0; } //! Return the reference count. /*! Return the value of this reference counter. */ unsigned int count() { return m_RefCount; } private: unsigned int m_RefCount; }; } #endif // NEWNET_REFCOUNTER_H museek+-0.2+svn20100315.r1208/NewNet/nnfactorysocket.h0000644000175000017500000000613111102357015021443 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_FACTORYSOCKET_H #define NEWNET_FACTORYSOCKET_H #include "nnreactor.h" namespace NewNet { //! Abstract base class for factory sockets. /*! This provides a base class for factory sockets: server sockets that automatically create a specified client socket type. Note: your application will most likely use TcpFactorySocket or UnixFactorySocket instead of using this class directly. */ template class FactorySocket : public Object { public: //! Construct a factory socket. /*! Create a new factory socket by instantiating a new socket of type ServerType using the default constructor. Note: stores a RefPtr to the newly created ServerType. */ FactorySocket() { m_ServerSocket = new ServerType; m_ServerSocket->acceptedEvent.connect(this, &FactorySocket::buildClient); } //! Construct a factory socket based on the specified server socket. /*! Create a new factory socket based on the the specified server socket. Note: this stores a RefPtr to the specified server socket. */ FactorySocket(ServerType * serverSocket) { m_ServerSocket = serverSocket; m_ServerSocket->acceptedEvent.connect(this, &FactorySocket::buildClient); } //! Returns the server socket. /*! Use this to make the factory socket listen on a port or path (depending on the server socket type used) or to add it to the reactor. */ ServerType * serverSocket() { return m_ServerSocket; } //! Invoked when a new client socket has been created. /*! This event is invoked when a new client socket has been created. Note: you don't have to add the created client to the reactor. FactorySocket does that for you. */ Event clientAcceptedEvent; private: void buildClient(int descriptor) { ClientType * client = new ClientType(); client->setDescriptor(descriptor); client->setSocketState(Socket::SocketConnected); if(m_ServerSocket->reactor()) m_ServerSocket->reactor()->add(client); clientAcceptedEvent(client); } NewNet::RefPtr m_ServerSocket; }; } #endif // NEWNET_FACTORYSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/platform.h0000644000175000017500000000543311102357015020057 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_PLATFORM_H #define NEWNET_PLATFORM_H #ifdef HAVE_CONFIG_H # include "config.h" # ifdef HAVE_STRING_H # include # endif // HAVE_STRING_H # ifdef HAVE_SYS_TIME_H # include # endif // HAVE_SYS_TIME_H # ifdef HAVE_SYS_TYPES_H # include # endif // HAVE_SYS_TYPES_H # ifdef HAVE_SYS_SELECT_H # include # endif // HAVE_SYS_SELECT_H # ifdef HAVE_SYS_SOCKET_H # include # endif // HAVE_SYS_SOCKET_H # ifdef HAVE_NETINET_IN_H # include # endif // HAVE_NETINET_IN_H # ifdef HAVE_NETINET_TCP_H # include # endif // HAVE_NETINET_TCP_H # ifdef HAVE_SYS_UN_H # include # endif // HAVE_SYS_UN_H # ifdef HAVE_NETDB_H # include # endif // HAVE_NETDB_H # ifdef HAVE_UNISTD_H # include # endif // HAVE_UNISTD_H # ifdef HAVE_FCNTL_H # include # endif // HAVE_FCNTL_H # ifdef HAVE_ERRNO_H # include # endif // HAVE_ERRNO_H # ifdef HAVE_SYS_STAT_H # include # endif // HAVE_SYS_STAT_H # ifdef HAVE_WINDOWS_H # include # endif // HAVE_WINDOWS_H # ifdef HAVE_WINSOCK_H # include # endif // HAVE_WINSOCK_H #else // HAVE_CONFIG_H # include # include # include # ifndef WIN32 # include # include # include # include # include # include # endif // ! WIN32 # include # include # include # include # ifdef WIN32 # include # include # endif // WIN32 #endif #ifndef UNIX_PATH_MAX # define UNIX_PATH_MAX 108 #endif #ifndef WIN32 typedef int sockopt_t; # define closesocket close # define WSAGetLastError() errno # define WSAEWOULDBLOCK EINPROGRESS #else typedef char sockopt_t; typedef int socklen_t; #endif // ! WIN32 #endif // NEWNET_PLATFORM_H museek+-0.2+svn20100315.r1208/NewNet/nntcpserversocket.cpp0000644000175000017500000000511211130624730022344 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nntcpserversocket.h" #include "nnlog.h" #include "platform.h" #include "util.h" #include void NewNet::TcpServerSocket::listen(const std::string & host, unsigned int port) { struct sockaddr_in address; memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(port); if(! host.empty()) { struct hostent *h = gethostbyname(host.c_str()); if (! h) { NNLOG("newnet.net.warn", "Cannot resolve '%s'.", host.c_str()); setSocketError(ErrorCannotResolve); cannotListenEvent(this); return; } memcpy(&(address.sin_addr.s_addr), *(h->h_addr_list), sizeof(address.sin_addr.s_addr)); } else address.sin_addr.s_addr = INADDR_ANY; int sock = socket(PF_INET, SOCK_STREAM, 0); if (!setnonblocking(sock)) NNLOG("newnet.net.warn", "Couldn't set socket %i to non blocking (errno: %i)", sock, errno); sockopt_t socket_option = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &socket_option, sizeof(int)); if(bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) != 0) { NNLOG("newnet.net.warn", "Cannot bind to '%s:%u', error: %i.", host.c_str(), port, errno); closesocket(sock); setSocketError(ErrorCannotBind); cannotListenEvent(this); return; } if (::listen(sock, 3) != 0) { NNLOG("newnet.net.warn", "Cannot listen on '%s:%u', error: %i.", host.c_str(), port, errno); closesocket(sock); setSocketError(ErrorCannotListen); cannotListenEvent(this); return; } m_ListenPort = port; setDescriptor(sock); setSocketState(SocketListening); listeningEvent(this); NNLOG("newnet.net.debug", "Listening on socket '%s:%u'.", host.c_str(), port); } museek+-0.2+svn20100315.r1208/NewNet/nnunixclientsocket.h0000644000175000017500000000342211131641225022157 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_UNIXCLIENTSOCKET_H #define NEWNET_UNIXCLIENTSOCKET_H #include "nnclientsocket.h" #include namespace NewNet { //! Unix client socket. /*! A client socket that provides a streaming unix domain socket type. */ class UnixClientSocket : public ClientSocket { public: //! Create a new unconnected unix client socket. /*! This creates a new, unconnected unix client socket. Call connect() to connect the client socket to unix path. */ UnixClientSocket() : ClientSocket() { } //! Connect to a unix path. /*! Creates a new descriptor and tries to connect it to the specified path. */ void connect(const std::string & path); void onConnectionTimeout(long); void onConnected(ClientSocket *); private: NewNet::WeakRefPtr::Callback> m_ConnectionTimeout; }; } #endif // NEWNET_UNIXCLIENTSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnlog.h0000644000175000017500000000533411130624730017352 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_LOG_H #define NEWNET_LOG_H #include #include #include #include "nnevent.h" namespace NewNet { //! Controllable logging class /*! This class will let you output messages to the console in a controlled fashion. It works by enabling and disabling domains where events can happen. */ class Log { public: typedef struct { std::string domain; std::string message; } LogNotify; //! Print a message. /*! Print a message if the specified domain is enabled. */ void operator() (const std::string & domain, const char*, ...); //! Enable a message domain. /*! This will enable printing messages of that domain. A special case is 'ALL' in which case all messages will be printed. */ void enable(const std::string & domain); //! Disable a message domain. /*! This will disable printing messages of that domain. */ void disable(const std::string & domain); //! Invoked when a message is logged. /*! This will be invoked when a message is logged in a domain that's enabled. */ NewNet::Event logEvent; private: bool m_AllEnabled; std::vector m_EnabledDomains; }; //! Console output class. /*! Connect an instance of this class to Log's logEvent event to output log messages to stderr. */ class ConsoleOutput : public Event::Callback { public: #ifndef DOXYGEN_UNDOCUMENTED void operator()(const Log::LogNotify * notice) { std::cout << "[" << notice->domain << "] " << notice->message << std::endl; } #endif }; //! Global logger instance. /*! Global logger instance, you can also reference this using the convenient NNLOG preprocessor definition. */ extern Log log; } #define NNLOG NewNet::log #endif // NEWNET_LOG_H museek+-0.2+svn20100315.r1208/NewNet/nnreactor.cpp0000644000175000017500000003022411136145464020567 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnreactor.h" #include "nnlog.h" #include "platform.h" #include "util.h" #include #include #include #include #include void eventCallback(int fd, short event, void *arg) { static_cast(arg)->eventCallback(fd, event, arg); } NewNet::Reactor::Reactor() { m_Timeouts = new Timeouts; #ifdef WIN32 m_WsaData = new WSADATA; WORD wVersionRequested = MAKEWORD(1, 1); assert(WSAStartup(wVersionRequested, (WSADATA *)m_WsaData) == 0); #endif // WIN32 // Set the FD limit to the maximum available // The maximum available can be modified in /etc/security/limits.conf (changing nofile parameter for your user) struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { rl.rlim_cur = rl.rlim_max; setrlimit(RLIMIT_NOFILE, &rl); } struct rlimit rlim; m_maxSocketNo = -1; if (getrlimit (RLIMIT_NOFILE, &rlim) >= 0) m_maxSocketNo = rlim.rlim_cur; NNLOG("newnet.net.debug", "%i file descriptors available for museekd.", m_maxSocketNo); event_init(); } #ifndef DOXYGEN_UNDOCUMENTED NewNet::Reactor::~Reactor() { #ifdef WIN32 WSACleanup(); delete (WSADATA *)m_WsaData; #endif // WIN32 delete m_Timeouts; } #endif // DOXYGEN_UNDOCUMENTED /* Check if any timeouts have expired. If so, invoke them and remove them from the queue. Also, update the timeout if a timeout should be called before the currently set timeout. */ bool NewNet::Reactor::checkTimeouts(struct timeval & timeout, bool & timeout_set) { bool retVal = false; struct timeval now; gettimeofday(&now, 0); std::vector::iterator it; std::vector timeouts = m_Timeouts->timeouts; // copy the timeouts in case it is modified somewhere else while iterating it = timeouts.begin(); while (it != timeouts.end()) { // Has the timeout expired? if(timercmp(&now, &(*it).first, >=)) { // Calculate how long the timeout is overdue gettimeofday(&now, 0); unsigned long diff = difftime(now, (*it).first); // Store the timeout callback and delete it from to-be-emitted list NewNet::RefPtr item = it->second; removeTimeout(item); it = timeouts.erase(it); // And emit it if (item.isValid()) item->operator()(diff); retVal = true; } else { if((! timeout_set) || (timercmp(&(*it).first, &timeout, <))) { /* If the timeout expires before the next cycle timeout, adjust the cycle timeout. */ timeout.tv_sec = (*it).first.tv_sec; timeout.tv_usec = (*it).first.tv_usec; timeout_set = true; } ++it; } } return retVal; } void NewNet::Reactor::add(Socket * socket) { if(socket->reactor()) { if(socket->reactor() == this) return; /* Proceed with caution here, otherwise the socket might get prematurely deleted: first create our shared reference, then remove it from the other reactor. */ m_Sockets.push_back(socket); socket->reactor()->remove(socket); } else m_Sockets.push_back(socket); socket->setReactor(this); } void NewNet::Reactor::remove(Socket * socket) { int fd = socket->descriptor(); NNLOG("newnet.net.debug", "removing socket %u from reactor", socket->descriptor()); // Removing a socket from the wrong reactor is a programming error, trap it. assert(socket->reactor() == this); socket->setReactor(0); std::vector >::iterator it; it = std::find(m_Sockets.begin(), m_Sockets.end(), socket); if (it != m_Sockets.end()) { // See if there is another socket using the same FD std::vector >::iterator itFD; bool found = false; for (itFD = m_Sockets.begin(); ((itFD != m_Sockets.end()) && !found); ++itFD) { if (((*itFD)->descriptor() == fd) && (socket != *itFD)) found = true; } if (!found) { // No other socket is using this FD stop watching it struct event * ev = socket->getEventData(); if (event_initialized(ev)) event_del(ev); } m_Sockets.erase(it); } } void NewNet::Reactor::run() { NNLOG("newnet.net.debug", "Running reactor. Libevent is using %s method.", event_get_method()); bool loop = true; while (loop) { loop = prepareReactorData(); } // Launch the main loop event_dispatch(); } bool NewNet::Reactor::prepareReactorData() { /* No timeout set yet */ bool timeout_set = false; struct timeval timeout; // Update the sockets watched by the reactor checkSockets(timeout, timeout_set); // Update the timeouts and call expired ones if (checkTimeouts(timeout, timeout_set)) return true; // Set a timer to come back here when needed if(timeout_set) { /* We know when we need to wake up, but how many sec/usec from now is that? */ struct timeval now; gettimeofday(&now, 0); timeout.tv_sec -= now.tv_sec; timeout.tv_usec -= now.tv_usec; if(timeout.tv_usec < 0) { timeout.tv_sec -= 1; timeout.tv_usec += 1000000; } if(timeout.tv_sec < 0) { timeout.tv_sec = 0; timeout.tv_usec = 0; } evtimer_del(&mEvTimeout); // delete potentially existing previous timeout evtimer_set(&mEvTimeout, ::eventCallback, this); evtimer_add(&mEvTimeout, &timeout); } if(timeout_set) NNLOG("newnet.net.debug", "Waiting at most %li ms until one of %i sockets wakes up (max FD: %i).", (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000), currentSocketNo(), maxFileDescriptor()); else NNLOG("newnet.net.debug", "Waiting indefinitely until one of %i sockets wakes up (max FD: %i).", currentSocketNo(), maxFileDescriptor()); return false; } void NewNet::Reactor::eventCallback(int fd, short event, void *arg) { NNLOG("newnet.net.debug", "Entering event callback for socket %i.", fd); bool loop = true; while (loop) { /* Make a copy of our socket list, as they might disappear because of events that occur and then our iterators go berserk */ std::vector > sockets(m_Sockets); std::vector >::iterator it, end = sockets.end(); for(it = sockets.begin(); it != end; ++it) { NewNet::Socket * sock = *it; struct event *evData = sock->getEventData(); // Let the sockets do their job if ((sock->descriptor() >= 0) && ((evData->ev_res & EV_READ) || (evData->ev_res & EV_WRITE))) { // Update the socket's ready state long upLimit = (! sock->upRateLimiter()) ? 0 : sock->upRateLimiter()->nextWindow(); long downLimit = (! sock->downRateLimiter()) ? 0 : sock->downRateLimiter()->nextWindow(); int state = 0; if ((downLimit == 0) && (evData->ev_res & EV_READ)) state |= NewNet::Socket::StateReceive; if ((upLimit == 0) && (evData->ev_res & EV_WRITE)) state |= NewNet::Socket::StateSend; sock->setReadyState(state); // If we have something to report, make the socket process the events. if(state) sock->process(); } } loop = prepareReactorData(); } } void NewNet::Reactor::checkSockets(struct timeval & timeout, bool & timeout_set) { /* Make a copy of our socket list, as they might disappear because of events that occur and then our iterators go berserk */ std::vector > sockets(m_Sockets); /* Check which events we want to hear about from which sockets */ std::vector >::iterator it, end = sockets.end(); int nfds = 0; for(it = sockets.begin(); it != end; ++it) { /* Convenience... */ NewNet::Socket * sock = *it; struct event *evData = sock->getEventData(); int fd = sock->descriptor(); if(fd == -1) continue; long n; // miliseconds to next window of opportunity short evFlags = 0; // event type flag to be used switch(sock->socketState()) { /* The socket is dead, no events are interesting */ case NewNet::Socket::SocketUninitialized: case NewNet::Socket::SocketDisconnecting: case NewNet::Socket::SocketDisconnected: case NewNet::Socket::SocketException: break; /* Listening socket, check for read-ready events */ case NewNet::Socket::SocketListening: evFlags = EV_READ; nfds = std::max(nfds, fd + 1); break; /* Connecting socket, check for write-ready events */ case NewNet::Socket::SocketConnecting: evFlags = EV_WRITE; nfds = std::max(nfds, fd + 1); break; /* Connected socket, if possible / allowed check for read, write */ case NewNet::Socket::SocketConnected: /* Check if we're allowed to receive, and if not, when we might be. */ n = (! sock->downRateLimiter()) ? 0 : sock->downRateLimiter()->nextWindow(); if(n == 0) evFlags = EV_READ; else { NNLOG("newnet.net.debug", "Download limiter for socket %i recommends %li ms sleep.", fd, n); fixtime(timeout, n, timeout_set); } /* Check if we want to send, if we're allowed to send. And if we're not allowed to send, when we might be. */ if(sock->dataWaiting()) { n = (! sock->upRateLimiter()) ? 0 : sock->upRateLimiter()->nextWindow(); if(n == 0) evFlags |= EV_WRITE; else { NNLOG("newnet.net.debug", "Upload rate limiter for socket %i reports next window in %li ms", fd, n); fixtime(timeout, n, timeout_set); } } nfds = std::max(nfds, fd + 1); break; } m_maxFD = nfds; if (evFlags > 0) { if (event_initialized(evData)) event_del(evData); event_set(evData, fd, evFlags, ::eventCallback, this); event_add(evData, NULL); } } } void NewNet::Reactor::stop() { event_loopexit(NULL); } NewNet::Reactor::Timeout::Callback * NewNet::Reactor::addTimeout(long msec, Timeout::Callback * callback) { // Calculate when the event has to occur struct timeval tv; gettimeofday(&tv, 0); tv.tv_sec += (msec / 1000); tv.tv_usec += (msec % 1000) * 1000; if(tv.tv_usec >= 1000000) { tv.tv_sec += 1; tv.tv_usec -= 1000000; } // Push the timeout on our queue m_Timeouts->timeouts.push_back(TimeoutItem(tv, callback)); // Return the callback, for convenience return callback; } void NewNet::Reactor::removeTimeout(Timeout::Callback * callback) { std::queue::iterator> purge; std::vector::iterator it, end = m_Timeouts->timeouts.end(); for(it = m_Timeouts->timeouts.begin(); it != end; ++it) { if((*it).second == callback) { purge.push(it); } } while(! purge.empty()) { m_Timeouts->timeouts.erase(purge.front()); purge.pop(); } } int NewNet::Reactor::maxSocketNo() { return m_maxSocketNo; } int NewNet::Reactor::currentSocketNo() { return m_Sockets.size(); } int NewNet::Reactor::maxFileDescriptor() { return m_maxFD; } museek+-0.2+svn20100315.r1208/NewNet/nnsocket.h0000644000175000017500000001751411135323374020071 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_SOCKET_H #define NEWNET_SOCKET_H #include "nnobject.h" #include "nnrefptr.h" #include "nnweakrefptr.h" #include "nnratelimiter.h" #include namespace NewNet { class Reactor; //! Base class for network sockets. /*! This provides a generic base class for both client and server sockets. The reactor doesn't differentiate between those two and just operates on this socket type. */ class Socket : public Object { public: //! Enumeration to describe the state of the socket. /*! This defines the current state of the socket. The value can be retrieved with socketState() and set with setSocketState(). */ typedef enum { SocketUninitialized, //!< The socket is uninitialized. SocketListening, //!< The socket is listening for client connections. SocketConnecting, //!< The socket is currently busy connecting. SocketConnected, //!< The socket is connected. SocketDisconnecting, //!< The socket is currently busy disconnecting. SocketDisconnected, //!< The socket is disconnected. SocketException //!< An error occured, the socket is dead. } SocketState; //! Enumeration to describe pending network events. /*! This defines what kind of network events are currently pending on the socket. The value can be retrieved using readyState() and set with setReadyState(). */ typedef enum { StateSend = 1, //!< The socket is ready to send data. StateReceive = 2, //!< Data was received, socket is ready to read. StateException = 4 //!< Out-of-band data was received. } ReadyState; //! Enumeration to describe the error that last occured. /*! This defines what kind of error made the state go into exception state. It can be retrieved using socketError() and set with setSocketError(). */ typedef enum { ErrorNoError, //!< No error at all, everything's fine. ErrorCannotResolve, //!< The socket was unable to resolve the destination host. ErrorInvalidPath, //!< The specified path was invalid. ErrorCannotConnect, //!< The socket was unable to connect to the remote end. ErrorCannotBind, //!< The socket couldn't bind to the specified address. ErrorCannotListen, //!< The socket couldn't listen on the specified address. ErrorUnknown //!< An unknown error occured. } SocketError; //! Create a new uninitialized socket. /*! This creates a new socket that has an invalid descriptor, is uninitialized, has no pending events, no error and no data waiting. */ Socket() : m_Reactor(0), m_FD(-1), m_SocketState(SocketUninitialized), m_ReadyState(0), m_SocketError(ErrorNoError), m_DataWaiting(false) { m_EventData = new struct event; m_EventData->ev_flags = 0; // This event has not been initialized } //! Return the associated reactor. /*! Return the reactor this socket is associated to, if any. */ Reactor * reactor() const { return m_Reactor; } //! Set the associated reactor. /*! This is called by the reactor when the socket is added to the reactor. Note: stores a regular pointer to the reactor. */ void setReactor(Reactor * reactor) { m_Reactor = reactor; } //! Return the socket's descriptor. /*! This returns the socket's descriptor. */ int descriptor() const { return m_FD; } //! Set the socket's descriptor. /*! This initializes the socket's descriptor. Usually called by subclasses when connecting or by socket factories when accepting a new client. */ void setDescriptor(int fd) { m_FD = fd; } //! Return the current socket state. /*! Retrieves the current socket state. */ SocketState socketState() const { return m_SocketState; } //! Set the current socket state. /*! Changes the current socket state. */ void setSocketState(SocketState socketState) { m_SocketState = socketState; } //! Return the socket's ready state. /*! Returns what kind of events are pending on the socket. */ int readyState() const { return m_ReadyState; } //! Set the socket's ready state. /*! Usually called by subclasses and the reactor to specify what kind of network events are pending. */ void setReadyState(int readyState) { m_ReadyState = readyState; } //! Return the socket's error state. /*! Returns what kind of error last occured on the socket. */ SocketError socketError() const { return m_SocketError; } //! Set the socket's error state. /*! Usually called by subclasses to specify what kind of error occured on the socket. */ void setSocketError(SocketError socketError) { m_SocketError = socketError; setSocketState(SocketException); } //! Return wether there's data waiting to be sent. /*! Called by the reactor to determine wether the socket has data waiting to be sent. */ bool dataWaiting() const { return m_DataWaiting; } //! Set the data waiting flag. /*! Called by subclasses to specify that there's data waiting to be sent */ void setDataWaiting(bool dataWaiting) { m_DataWaiting = dataWaiting; } //! Return the current download rate limiter. /*! Return the current download rate limiter. */ RateLimiter * downRateLimiter() { return m_DownRateLimiter; } //! Set the current download rate limiter. /*! Set the current download rate limiter. Note: stores a RefPtr to the rate limiter. */ void setDownRateLimiter(RateLimiter * limiter) { m_DownRateLimiter = limiter; } //! Return the current upload rate limiter. /*! Return the current upload rate limiter. */ RateLimiter * upRateLimiter() { return m_UpRateLimiter; } //! Set the current upload rate limiter. /*! Set the current upload rate limiter. Note: stores a RefPtr to the rate limiter. */ void setUpRateLimiter(RateLimiter * limiter) { m_UpRateLimiter = limiter; } //! Processor function. /*! This is called by the reactor when there are pending network events. */ virtual void process() { } //! Associate some libevent data to the socket. /*! Associate some libevent data to the socket. */ void setEventData(struct event & evData) { *m_EventData = evData; } //! Returns libevent data associated with the socket. /*! Returns libevent data associated with the socket. */ struct event * getEventData() { return m_EventData; } private: Reactor * m_Reactor; int m_FD; SocketState m_SocketState; int m_ReadyState; SocketError m_SocketError; bool m_DataWaiting; RefPtr m_DownRateLimiter, m_UpRateLimiter; struct event * m_EventData; }; } #endif // NEWNET_SOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nntcpclientsocket.cpp0000644000175000017500000000636711131641225022330 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nntcpclientsocket.h" #include "nnlog.h" #include "nnreactor.h" #include "platform.h" #include "util.h" #include void NewNet::TcpClientSocket::connect(const std::string & host, unsigned int port) { assert((descriptor() == -1) || (socketState() == SocketUninitialized)); setSocketState(SocketConnecting); NNLOG("newnet.net.debug", "Resolving host '%s'.", host.c_str()); struct hostent *h = gethostbyname(host.c_str()); if(! h) { NNLOG("newnet.net.warn", "Cannot resolve host '%s'.", host.c_str()); setSocketError(ErrorCannotResolve); cannotConnectEvent(this); return; } struct sockaddr_in address; memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; memcpy(&(address.sin_addr.s_addr), *(h->h_addr_list), sizeof(address.sin_addr.s_addr)); address.sin_port = htons(port); NNLOG("newnet.net.debug", "Connecting to host '%s:%u'.", host.c_str(), port); int s = socket(PF_INET, SOCK_STREAM, 0); if (!setnonblocking(s)) NNLOG("newnet.net.warn", "Couldn't set socket %i to non blocking (errno: %i)", s, errno); setDescriptor(s); if(s < 0) { NNLOG("newnet.net.warn", "Cannot connect to host '%s:%u', error: %i.", host.c_str(), port, WSAGetLastError()); setSocketError(ErrorCannotConnect); cannotConnectEvent(this); return; } // Add a connection timeout if (reactor()) { m_ConnectionTimeout = reactor()->addTimeout(120000, this, &TcpClientSocket::onConnectionTimeout); } connectedEvent.connect(this, &TcpClientSocket::onConnected); if(::connect(s, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) == 0) { // When using non blocking socket (most of the time), we don't get here. NNLOG("newnet.net.debug", "Connected to host '%s:%u'.", host.c_str(), port); setSocketState(SocketConnected); connectedEvent(this); } else if(WSAGetLastError() != WSAEWOULDBLOCK) { // When using non blocking socket (most of the time), we don't get here. NNLOG("newnet.net.warn", "Cannot connect to host '%s:%u', error: %i.", host.c_str(), port, WSAGetLastError()); setSocketError(ErrorCannotConnect); cannotConnectEvent(this); } } void NewNet::TcpClientSocket::onConnectionTimeout(long) { cannotConnectEvent(this); } void NewNet::TcpClientSocket::onConnected(ClientSocket *) { if (reactor()) reactor()->removeTimeout(m_ConnectionTimeout); } museek+-0.2+svn20100315.r1208/NewNet/nnbuffer.cpp0000644000175000017500000000364411102357015020375 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "nnbuffer.h" #include "platform.h" #define CHUNK_SIZE 8192 NewNet::Buffer::Buffer() : m_Ptr(0), m_Pos(0), m_Count(0), m_Left(0) { } NewNet::Buffer::Buffer(const Buffer & that) : m_Ptr(0), m_Pos(0), m_Count(0), m_Left(0) { append(that.data(), that.count()); } NewNet::Buffer & NewNet::Buffer::operator=(const Buffer & that) { m_Left += m_Pos + m_Count; m_Pos = m_Count = 0; append(that.data(), that.count()); return *this; } NewNet::Buffer::~Buffer() { free(m_Ptr); } void NewNet::Buffer::append(const unsigned char * data, size_t n) { if(m_Left < n) { if(m_Pos + m_Left >= n) { memmove(m_Ptr, m_Ptr + m_Pos, m_Count); m_Left += m_Pos; m_Pos = 0; } else { int newSize = (((n + m_Count + m_Pos) / CHUNK_SIZE) + 1) * CHUNK_SIZE; unsigned char * newPtr = (unsigned char *)realloc(m_Ptr, newSize); assert(newPtr != 0); m_Ptr = newPtr; m_Left = newSize - m_Count - m_Pos; } } memcpy(m_Ptr + m_Pos + m_Count, data, n); m_Count += n; m_Left -= n; } museek+-0.2+svn20100315.r1208/NewNet/nnunixfactorysocket.h0000644000175000017500000000315311102357015022350 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_UNIXFACTORYSOCKET_H #define NEWNET_UNIXFACTORYSOCKET_H #include "nnunixserversocket.h" #include "nnfactorysocket.h" namespace NewNet { //! Unix factory socket class. /*! This provides your application with a factory socket that can listen on a path and create UnixClientSockets. */ template class UnixFactorySocket : public FactorySocket { public: //! Create a new unix factory socket. /*! Create a new unix factory socket. Don't forget to call listen on serverSocket() and add serverSocket() to the reactor. */ UnixFactorySocket() : FactorySocket() { } }; } #endif // NEWNET_UNIXFACTORYSOCKET_H museek+-0.2+svn20100315.r1208/NewNet/nnrefptr.h0000644000175000017500000000611711102357015020071 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_REFPTR_H #define NEWNET_REFPTR_H #include "nnbaseptr.h" namespace NewNet { class Object; //! A reference counting pointer. /*! This provides a pointer type which does automatic deletion of objects by tracking the reference count. If the reference count drops to 0, the containing object will be disposed of. */ template class RefPtr : public BasePtr { public: //! Create a new reference counter pointer that points at NULL. /*! Create a new reference counter pointer that points at NULL. */ RefPtr() : BasePtr() { } //! Create a new reference counter pointer that points at t. /*! Create a new reference counter pointer that points at t. */ RefPtr(T * t) : BasePtr(t) { if(t) ++(t->refCounter()); } //! Create a new reference counter pointer that points at t. /*! Create a new reference counter pointer that points at t. */ RefPtr(const RefPtr& t) : BasePtr() { BasePtr::m_Ptr = t.m_Ptr; if(t.m_Ptr) ++(t.m_Ptr->refCounter()); } //! Assign operator. /*! Assign the object t is pointing at to this pointer as well. */ RefPtr& operator=(const RefPtr& t) { if(BasePtr::m_Ptr == t.m_Ptr) return *this; if(BasePtr::m_Ptr) { if(--(BasePtr::m_Ptr->refCounter())) delete BasePtr::m_Ptr; } BasePtr::m_Ptr = t.m_Ptr; if(t.m_Ptr) { ++(t.m_Ptr->refCounter()); } return *this; } //! Assign operator. /*! Make this pointer point at t. */ RefPtr& operator=(T * t) { if(BasePtr::m_Ptr == t) return *this; if(BasePtr::m_Ptr) { if(--(BasePtr::m_Ptr->refCounter())) delete BasePtr::m_Ptr; } BasePtr::m_Ptr = t; if(t) ++(t->refCounter()); return *this; } //! Destructor. /*! Decrements reference count of object and deletes it if the reference count drops to 0. */ ~RefPtr() { if(BasePtr::m_Ptr) { if(--(BasePtr::m_Ptr->refCounter())) delete BasePtr::m_Ptr; } } }; } #endif // NEWNET_REFPTR_H museek+-0.2+svn20100315.r1208/NewNet/nnpath.h0000644000175000017500000000543011102357015017520 0ustar gandalfgandalf/* NewNet - A networking framework in C++ Copyright (C) 2006-2007 Ingmar K. Steen (iksteen@gmail.com) Copyright 2008 little blue poney This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NEWNET_PATH_H #define NEWNET_PATH_H #include #include #include "nnobject.h" namespace NewNet { //! Helper class for file-path manipulation /*! This class provides helper functions for dealing with file-path manipulation */ class Path { public: //! Constructor /*! Default constructor leaves the path empty */ Path() { } //! Constructor. /*! Point at a specific path. */ Path(const std::string & path) : m_Path(path) { } //! Constructor. /*! Constructs the path from a vector of path pieces. */ Path(const std::vector & path); //! Return the current path as a string const std::string & path() const { return m_Path; } //! Return the platform's directory seperator. /*! Return the platform's directory seperator. */ static char separator() { #ifndef WIN32 return '/'; #else return '\\'; #endif // ! WIN32 } //! Check if path is absolute. /*! Checks if the current path is absolute. */ bool isAbsolute() const; //! Split the path. /*! Split the path at the directory separator. */ std::vector split() const; //! Return a simplified version of the path. /*! This removes any unnecessary and resolvable '..', '.' and empty elements from the path. */ Path simplified() const; //! Return an absolute version of the current path. /*! Return an absolute version of the current path. If the path isn't absolute already, base will be used as a base path. If base is an empty string, the current working directory will be used */ Path absolute(const std::string & base = std::string()) const; //! Return the current working directory. /*! This returns the current working directory */ static Path currentDir(); private: std::string m_Path; }; } #endif // NEWNET_PATH_H museek+-0.2+svn20100315.r1208/config.h.cmake0000644000175000017500000000175511065656035017376 0ustar gandalfgandalf#ifndef CONFIG_H #define CONFIG_H #cmakedefine HAVE_STDLIB_H 1 #cmakedefine HAVE_STDINT_H 1 #cmakedefine HAVE_STDDEF_H 1 #cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_STRING_H 1 #cmakedefine HAVE_ERRNO_H 1 #cmakedefine HAVE_FCNTL_H 1 #cmakedefine HAVE_NETDB_H 1 #cmakedefine HAVE_SYS_TYPES_H 1 #cmakedefine HAVE_SYS_TIME_H 1 #cmakedefine HAVE_SYS_SELECT_H 1 #cmakedefine HAVE_SYS_SOCKET_H 1 #cmakedefine HAVE_SYS_UN_H 1 #cmakedefine HAVE_SYS_STAT_H 1 #cmakedefine HAVE_NETINET_IN_H 1 #cmakedefine HAVE_NETINET_TCP_H 1 #cmakedefine HAVE_WINDOWS_H 1 #cmakedefine HAVE_WINSOCK_H 1 #define _FILE_OFFSET_BITS 64 #define _LARGEFILE_SOURCE 1 #define _LARGE_FILES 1 #cmakedefine ICONV_CONST const #ifndef ICONV_CONST # define ICONV_CONST #endif // ! ICONV_CONST #define ICONV_INPUT_TYPE ICONV_CONST char ** #cmakedefine HAVE_UINT #ifndef HAVE_UINT typedef unsigned int uint; #endif #cmakedefine HAVE_UINT32 #ifndef HAVE_UINT32 typedef @UINT32_TYPE@ uint32; #define HAVE_UINT32 #endif #endif // CONFIG_H museek+-0.2+svn20100315.r1208/LICENSE0000644000175000017500000000020011065656035015666 0ustar gandalfgandalfUnless explicitely mentioned otherwise, all files are distributed under the GNU Public License version 2 or newer (see COPYING) museek+-0.2+svn20100315.r1208/INSTALL0000755000175000017500000000475011131371217015721 0ustar gandalfgandalfRequirements: libxml++2.6-dev or libxml++1.0-dev libevent >= 1.3e GCC Python (by Python bindings) pyexpat (for musetup) CMake >= 2.6 (for building) QT >= 4.4 for museeq Optional: libvorbis-dev libogg-dev SWIG (for the mucipher Python bindings) Get binaries and help for Museek+ there: http://www.museek-plus.org ----------------------- I - Install using CMake ----------------------- CMake is the recommended way to build everything I.1 - DECLARATIONS (prefix with -D, set bool options on with 1; off with 0): ---------------------------------------------------------------------------- PREFIX: Where museek+ should be installed (default is /usr) MANDIR: Where man files should be installed (default is PREFIX/man) EVERYTHING: Install every components of museek+ (daemon, clients, bindings, etc.) NO_MUSEEKD: don't install museekd NO_MUSCAN: don't install muscan NO_SETUP: don't install musetup, musetup-gtk and musetup-qt NO_PYMUCIPHER: don't install python bindings for mucipher (hashing library: SHA1, MD5, ...) NO_MUSEEQ: don't install museeq (Qt4 client) BINDINGS: install python bindings for museek MURMUR: install PyGTK client MUCOUS: install Curses Python client CLIENTS: install some Python tools to museekd, featuring a command line client and a very primitive curses chat client. Museeq options: BINRELOC: Use binary relocation DATADIR: Change default data dir RELOAD_TRANSLATIONS: Update .ts files in src/museeq/translations I.2 - Commands -------------- # cd /path/to/src # mkdir workdir # cd workdir/ # cmake -DPREFIX=/usr .. # (or) cmake -DEVERYTHING=1 -DPREFIX=/usr .. # (or) cmake -DMUCOUS=0 -DPREFIX=/usr/local -DMANDIR=share/man # make # (or) make VERBOSE=1 # make install ---------------------------------------------------- II - Distutils (Python's built-in installation tool) ---------------------------------------------------- distutils doesn't provide for uninstalls, so be cautious with it. Enter a directory and issue these commands # python setup.py build # python setup.py install # (or) python setup.py install --prefix=/usr/local --------------- PyMucipher (requires SWIG, Python) # cd Mucipher/PyMucipher/ ............... Python Bindings # cd python-bindings/ ............... Python Clients (mulog, museekchat, museekcontrol, musirc.py) Requires: PyMucipher or PyCrypto, Python Bindings # cd python-clients/ ............... Setup tools (musetup, musetup-gtk, musetup-qt) # cd setup/ ............... Mucous Requires: PyMucipher or PyCrypto, Python Bindings # cd mucous/ museek+-0.2+svn20100315.r1208/mucous/0000755000175000017500000000000011347444621016203 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/mucous/pymucous/0000755000175000017500000000000011347444621020067 5ustar gandalfgandalfmuseek+-0.2+svn20100315.r1208/mucous/pymucous/MucousNetworking.py0000644000175000017500000011753011230674336023772 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. try: import messages, driver except: try: from museek import messages, driver except: print "WARNING: The Museek Message-Parsing modules, messages.py and/or driver.py were not found. Please install them into your '/usr/lib/python2.X/site-packages/museek' directory, or place them in a 'museek' subdirectory of the directory that contains the mucous python script." sys.exit() import threading import os, sys, time import curses.wrapper import select, socket ## Driver handle ## @param driver Museek driver bindings class Networking(driver.Driver): ## Constructor # @param self Networking (Driver Class) # @param mucous Mucous (Class) def __init__(self, mucous): driver.Driver.__init__(self, self.Error) ## @var mucous # Mucous (Class) self.mucous = mucous ## Connect to museekd, password, a museekd socket need to be set # @param self Networking (Driver Class) # If mucous cannot connect, enter a loop which allows commands to be inputted def connect(self): try: if not self.mucous.invalidpass: if self.mucous.Config["connection"]["passw"] != None: self.mucous.timers["nick"].cancel() self.mucous.timers["nick"] = threading.Timer(10.0, self.mucous.ThreadNickCheck) self.mucous.timers["nick"].start() driver.Driver.connect(self, self.mucous.Config["connection"]["interface"], self.mucous.Config["connection"]["passw"], messages.EM_CHAT | messages.EM_USERINFO| messages.EM_PRIVATE| messages.EM_TRANSFERS | messages.EM_USERSHARES | messages.EM_CONFIG | messages.EM_INTERESTS | messages.EM_DEBUG) #break else: raise Exception, "No Password Set" else: raise Exception, "INVPASS" except KeyboardInterrupt, e: # Ctrl-C Exits Mucous raise KeyboardInterrupt, "" except select.error, e: self.mucous.Help.Log("status", "Connection Error2 "+str( e) ) raise select.error, e except Exception, e: self.mucous.Help.Mode() if e == "INVPASS": self.mucous.Help.Log("status", "Incorrect Password, try another.") elif str(e) == str((111, 'Connection refused')): self.mucous.Help.Log("status", e[1] +", make sure the daemon is running, or change the interface.") else: self.mucous.Help.Log("status", "Connection Error "+str( e) ) if self.mucous.timers["nick"] != None: self.mucous.timers["nick"].cancel() def Error(self, message): self.mucous.Help.Log("debug", message) ## Recieve Messages from Museekd and collect new key presses # @param self Networking (Driver Class) def processWrap(self): while self.mucous._run: if self.socket is None: time.sleep(0.5) continue read, write, exception = select.select([self.socket], [], [self.socket], 0) if self.socket in read: self.process() time.sleep(0.05) ## Recieved Ping from museekd # @param self Networking (Driver Class) def cb_ping(self): self.mucous.Help.Log("debug", "Recieved ping from daemon...") ## Recieved Login Error from museekd # @param self Networking (Driver Class) # @param reason is a string containing the reason for login failure def cb_login_error(self, reason): try: self.mucous.timers["nick"].cancel() self.mucous.Spl["connected"] = 0 self.close() if reason == "INVPASS": self.mucous.invalidpass = True self.mucous.Help.Mode() self.mucous.Help.Log("status", "Couldn't log in to Museekd: Invalid Password") #self.connect() else: self.mucous.invalidpass = False self.mucous.Help.Log("status", "Couldn't log in to Museekd: " + reason) except Exception,e: self.mucous.Help.Log("debug", "cb_login_error: " + str(e)) ## Recieved Login Okay from museekd # @param self Networking (Driver Class) def cb_login_ok(self): try: self.mucous.invalidpass = False self.mucous.Spl["connected"] = 1 self.mucous.Help.Log("status", "Logging into Museek at "+ self.mucous.Config["connection"]["interface"]) self.mucous.timers["timeout"] = threading.Timer(self.mucous.timeout_time, self.mucous.AwayTimeout) self.mucous.timers["timeout"].start() except Exception,e: self.mucous.Help.Log("debug", "cb_login_ok: " + str(e)) ## Museekd notified that we are disconnected # @param self Networking (Driver Class) def cb_disconnected(self): try: if self.mucous.Spl["connected"] == 1: try: #driver.Driver.close(self) self.D.close() except: pass self.mucous.Spl["connected"] = 0 self.mucous.logs["onlinestatus"]="Closed" self.mucous.Muscan.timer.cancel() self.mucous.timers["nick"].cancel() self.mucous.ChatRooms.ticker_timer.cancel() self.mucous.timers["retry"].cancel() self.mucous.timers["clear"].cancel() self.mucous.timers["timeout"].cancel() self.mucous.username = None self.mucous.DrawOnlineStatus() for room in self.mucous.ChatRooms.rooms.keys(): msg = ("Disconnected from the Museek Daemon") self.mucous.ChatRooms.AppendChat("Status", room, '', msg) self.mucous.ChatRooms.rooms[room] = {} self.mucous.ChatRooms.tickers[room] = {} uploadlist = [] self.mucous.Transfers.uploads = {} self.mucous.Transfers.downloads = {} self.mucous.Transfers.transfers["downloads"] = {} self.mucous.Transfers.transfers["uploads"] = {} self.mucous.config = {} if self.mucous.mode == "chat": self.mucous.ChatRooms.Mode() elif self.mucous.mode == "transfer": self.mucous.Transfers.ModeTransfers() self.mucous.TerminalTitle() self.mucous.Transfers.DrawUploadCount("0") self.mucous.Transfers.DrawDownloadCount("0") self.mucous.refresh_windows() except Exception,e: self.mucous.Help.Log("debug", "cb_disconnected: " + str(e)) ## Museekd sent us a status message # @param self Networking (Driver Class) # @param type is a bool; False if the message relates to the Server / True for Peer # @param message is the message string def cb_status_message(self, type, message): try: if type == 1: stype = "Peer" elif type == 0: stype = "Server" self.mucous.Help.Log("status", "%s Message: %s" % (stype, message)) except Exception,e: self.mucous.Help.Log("debug", "cb_status_message: " +str( e) ) ## Museekd sent us a debug message # @param self Networking (Driver Class) # @param domain is a string the value of which is a debug type # @param message is the message string def cb_debug_message(self, domain, message): try: if domain in ["museek.note", "museek.warn"] : self.mucous.Help.Log("status", "%s Message: %s" % (domain, message)) except Exception,e: self.mucous.Help.Log("debug", "cb_debug_message: " +str( e) ) ## Museekd sent us the server state and our username # @param self Networking (Driver Class) # @param state is a bool; False if disconnected from the server / True if connected # @param username is a string def cb_server_state(self, state, username): try: self.mucous.username = username un = self.mucous.windows["border"]["username"] un.erase() un.addstr(self.mucous.dlang(self.mucous.username[:15]), self.mucous.colors["blackwhite"] ) un.refresh() #self.mucous.Transfers.DrawUploadCount("0") #self.mucous.Transfers.DrawDownloadCount("0") #self.mucous.Search.Count(0) if state: # self.mucous.Help.Log("status", "Connected to Server, username: " + username) self.mucous.logs["onlinestatus"]="Online" if self.mucous.ChatRooms.rooms.keys(): for room in self.mucous.ChatRooms.rooms.keys(): msg = ("Connected") self.mucous.ChatRooms.AppendChat("Status", room, '', msg) else: self.mucous.Help.Log("status", "Museek is not connected to Soulseek") self.mucous.logs["onlinestatus"]="Offline" if self.mucous.ChatRooms.rooms.keys(): for room in self.mucous.ChatRooms.rooms.keys(): msg = ("Disconnected from the Server") self.mucous.ChatRooms.AppendChat("Status", room, '', msg) #uploadlist = [] #self.mucous.Transfers.uploads = {} #self.mucous.Transfers.transfers["downloads"] = {} #self.mucous.Transfers.transfers["uploads"] = {} # Clear users from rooms for room in self.mucous.ChatRooms.rooms.keys(): self.mucous.ChatRooms.rooms[room] = [] if self.mucous.mode == "chat": self.mucous.ChatRooms.Mode() elif self.mucous.mode == "transfer": self.mucous.Transfers.ModeTransfers() self.mucous.DrawOnlineStatus() except Exception,e: self.mucous.Help.Log("debug", "cb_server_state: " +str( e) ) self.mucous.TerminalTitle() ## Time left of server privileges # @param self Networking (Driver Class) # @param time_left Seconds of privileges left def cb_server_privileges(self, time_left): try: time = time_left hours_i = time/3600 minutes_i = time/60 seconds_i = time - (60 * minutes_i) if minutes_i > 59: minutes_i = time/60 - (60 * hours_i) days = hours_i/24 hours = hours_i - (days*24) if time: stime = 'You have %d Days, %2.2d:%2.2d:%2.2d of privileges left' % (days, hours, minutes_i, seconds_i) else: stime = 'You have no global privileges.' self.mucous.Help.Log("status", stime) except Exception,e: self.mucous.Help.Log("debug", "cb_server_privileges: " +str( e) ) ## Recieved Room List # @param self Networking (Driver Class) # @param roomlist dict of [rooms][users][stats] def cb_room_list(self, roomlist): try: alpha_list = self.mucous.SortedDict() for name in roomlist: alpha_list[name] = roomlist[name] self.mucous.RoomsList.rooms = {} for x, y in alpha_list.items(): self.mucous.RoomsList.rooms[x] = y if self.mucous.mode=="roomlist": self.mucous.RoomsList.Mode() except Exception, e: self.mucous.Help.Log("debug", "CB Room List" + str(e)) ## Got Global Recommendations # @param self Networking (Driver Class) # @param recommendations list of recommendations [item, number of recommends] def cb_get_global_recommendations(self, recommendations): try: self.mucous.Recommendations.data["recommendations"] = self.mucous.SortedDict() for rec, num in recommendations.items(): self.mucous.Recommendations.data["recommendations"] [rec] = num if self.mucous.mode == "lists" and self.mucous.UsersLists.current == "interests": self.mucous.Recommendations.DrawInterests() except Exception, e: self.mucous.Help.Log("debug", "CB Get Global Recommendations" + str(e)) ## Got Similar Users list # @param self Networking (Driver Class) # @param users List of format [username, status=(0,1,2)] def cb_get_similar_users(self, users): try: self.mucous.Recommendations.data["similar_users"] = self.mucous.SortedDict() for rec, num in users.items(): self.mucous.Recommendations.data["similar_users"][rec] = num if self.mucous.mode == "lists" and self.mucous.UsersLists.current == "interests": self.mucous.Recommendations.DrawInterests() except Exception, e: self.mucous.Help.Log("debug", "CB Similar Users" + str(e)) ## Got Personal Recommendations # @param self Networking (Driver Class) # @param recommendations list of recommendations [item, number of recommends] def cb_get_recommendations(self, recommendations): try: self.mucous.Recommendations.data["recommendations"] = self.mucous.SortedDict() for rec, num in recommendations.items(): self.mucous.Recommendations.data["recommendations"] [rec] = num if self.mucous.mode == "lists" and self.mucous.UsersLists.current == "interests": self.mucous.Recommendations.DrawInterests() except Exception, e: self.mucous.Help.Log("debug", "CB Get Recommendations" + str(e)) ## Got Similar Users list for an Item # @param self Networking (Driver Class) # @param item string # @param users List of format [username, status=(0,1,2)] def cb_get_item_similar_users(self, item, users): try: self.mucous.Recommendations.data["similar_users"] = self.mucous.SortedDict() for rec, num in users.items(): self.mucous.Recommendations.data["similar_users"][rec] = num if self.mucous.mode == "lists" and self.mucous.UsersLists.current == "interests": self.mucous.Recommendations.DrawInterests() except Exception, e: self.mucous.Help.Log("debug", "CB Item Similar Users" + str(e)) ## Got Recommendations for an Item # @param self Networking (Driver Class) # @param item string # @param recommendations list of recommendations [item, number of recommends] def cb_get_item_recommendations(self, item, recommendations): try: self.mucous.Recommendations.data["recommendations"] = self.mucous.SortedDict() for rec, num in recommendations.items(): self.mucous.Recommendations.data["recommendations"] [rec] = num if self.mucous.mode == "lists" and self.mucous.UsersLists.current == "interests": self.mucous.Recommendations.DrawInterests() except Exception, e: self.mucous.Help.Log("debug", "CB Get Item Recommendations" + str(e)) ## Someone said something in a Chat Room # @param self Networking (Driver Class) # @param room Chat Room # @param user Username # @param text message def cb_room_said(self, room, user, text): try: #text = text.replace('\n', " ").replace('\t', " ") self.mucous.ChatRooms.SaidInRoom(room, user, text) except Exception, e: self.mucous.Help.Log("debug", "CB Room Said" + str(e)) ## Recieved Room state # @param self Networking (Driver Class) # @param roomlist Dict of rooms and the number of users in them # @param joinedrooms Dict of Rooms we have joined # @param tickers Dict of tickers [room][user] = ticker def cb_room_state(self, roomlist, joinedrooms, tickers): try: for rooms1, numbers in roomlist.items(): self.mucous.RoomsList.rooms[rooms1] = numbers for room in joinedrooms: self.mucous.ChatRooms.Joined(room, joinedrooms[room], tickers[room]) joined = self.mucous.ChatRooms.rooms.keys() joined.sort(key=str.lower) if joined == []: return if self.mucous.Config["rooms"]["default_room"] != None: if self.mucous.Config["rooms"]["default_room"] in joined: self.mucous.ChatRooms.current = self.mucous.Config["rooms"]["default_room"] self.mucous.ChatRooms.Change(self.mucous.Config["rooms"]["default_room"]) else: self.mucous.ChatRooms.JoinRoom(self.mucous.Config["rooms"]["default_room"]) if len(joined) != 0: self.mucous.ChatRooms.Change(joined[0]) else: self.mucous.ChatRooms.current = joined[0] self.mucous.ChatRooms.Change(joined[0]) except Exception, e: self.mucous.Help.Log("debug", "CB Room state" + str(e)) ## We Joined a room # @param self Networking (Driver Class) # @param room Room name # @param users Dict of users in the room def cb_room_joined(self, room, users, private, owner, operators): try: self.mucous.ChatRooms.Joined(room, users) if self.mucous.ChatRooms.current == None or self.mucous.ChatRooms.current == room: self.mucous.ChatRooms.Change(room) curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "CB Room Joined: " + str(e)) ## We left a room # @param self Networking (Driver Class) # @param room Room name def cb_room_left(self, room): try: self.mucous.ChatRooms.Left(room) except Exception, e: self.mucous.Help.Log("debug", "CB Room Left: " + str(e)) ## A user joined a room we are in # @param self Networking (Driver Class) # @param room Room name # @param user User name # @param data status, speed, downloads, files, dirs, other def cb_room_user_joined(self, room, user, data): try: self.mucous.ChatRooms.UserJoined( room, user, data) except Exception, e: self.mucous.Help.Log("debug", "CB Room User Joined" + str(e)) ## A user left a room we are in # @param self Networking (Driver Class) # @param room Room name # @param user User name def cb_room_user_left(self, room, user): try: did = "left" what = None if self.mucous.config != {}: if self.mucous.config.has_key("ignored") and user not in self.mucous.config["ignored"].keys(): self.mucous.ChatRooms.AppendStatus(user, room, did, what) else: pass # correct placement in roombox if self.mucous.mode == "chat" and self.mucous.ChatRooms.selected == "roombox": self.mucous.ChatRooms.rooms[room].sort(key=str.lower) if self.mucous.ChatRooms.rooms[room].index(user) < self.mucous.ChatRooms.scrolling[self.mucous.ChatRooms.selected]: self.mucous.ChatRooms.scrolling[self.mucous.ChatRooms.selected] -= 1 if user in self.mucous.ChatRooms.rooms[room]: self.mucous.ChatRooms.rooms[room].remove(user) if room in self.mucous.ChatRooms.tickers: if user in self.mucous.ChatRooms.tickers[room]: del self.mucous.ChatRooms.tickers[room][user] if self.mucous.mode == "chat" and self.mucous.ChatRooms.current == room: self.mucous.ChatRooms.DrawBox() for lines in self.mucous.ChatRooms.logs["rooms"][room][len(self.mucous.ChatRooms.logs["rooms"][self.mucous.ChatRooms.current]) - self.mucous.ChatRooms.dimensions["chat"]["height"]:]: # Update Chat history if user changes status if lines[2] == user: self.mucous.ChatRooms.Change(room) break curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "CB Room User Left" + str(e)) ## A user's status changed # @param self Networking (Driver Class) # @param user User name # @param status (1=away|2=online|3=offline) def cb_peer_status(self, user, status): try: if status == 1: what = "away" elif status == 2: what = "online" elif status == 0: what = "offline" if user in self.mucous.user["status"]: if self.mucous.user["status"][user] == status: return else: self.mucous.user["status"][user] = status else: self.mucous.user["status"][user] = status room = None did = "change" if self.mucous.config != {}: if "ignored" in self.mucous.config.keys(): if user not in self.mucous.config["ignored"].keys(): self.mucous.ChatRooms.AppendStatus(user, room, did, what) if self.mucous.mode == "chat": if self.mucous.ChatRooms.current != None: if user in self.mucous.ChatRooms.rooms[self.mucous.ChatRooms.current]: self.mucous.ChatRooms.DrawBox() curses.doupdate() elif self.mucous.mode in ("private", "info", "browse", "lists"): self.mucous.ModeReload(user) except Exception, e: self.mucous.Help.Log("debug", "CB Peer Status" + str(e)) ## Recieved a user's info # @param self Networking (Driver Class) # @param user the Username # @param info the description # @param picture the image, if it exists # @param uploads number of uploads slots # @param queue Length of queue # @param slotsfree has free slots? def cb_user_info(self, user, info, picture, uploads, queue, slotsfree): try: self.mucous.UserInfo.Recieved(user, info, picture, uploads, queue, slotsfree) except Exception, e: self.mucous.Help.Log( "debug", "cb_user_info: " + str(e)) ## Recieved a user's ip and port from the server # @param self Networking (Driver Class) # @param user the Username # @param ip ip address # @param port user's listen port def cb_peer_address(self, user, ip, port): try: if user not in self.mucous.requests["ip"]: return self.mucous.requests["ip"].remove(user) if self.mucous.geoip_fail==0: try: country = self.mucous.gi.country_name_by_addr( str(ip) ) self.mucous.Help.Log("status", "%s's IP: %s Port: %s Country: %s" % (user, str(ip), str(port), country) ) except Exception, e: self.mucous.Help.Log("debug", "CB Peer Address: " + str(e)) else: self.mucous.Help.Log("status","%s's IP: %s Port: %s" % (user, str(ip), str(port)) ) if self.mucous.mode not in ("status", "debug"): self.mucous.Alerts.setStatus("New IP") except Exception, e: self.mucous.Help.Log( "debug", "cb_peer_address: " + str(e)) ## Recieved peer stats from museekd # @param self Networking (Driver Class) # @param user the Username # @param avgspeed Average Speed of user # @param numdownloads Number of files user has downloaded # @param numfiles Number of files share # @param numdirs Number of directories shared def cb_peer_stats(self, user, avgspeed, numdownloads, numfiles, numdirs, slotsfull, country): try: self.mucous.user["statistics"][user] = avgspeed, numdownloads, numfiles, numdirs if user in self.mucous.requests["statistics"]: self.mucous.Help.Log("status", "Peer Stats for: %s Speed: %.2f Kbyte/s :: Downloads: %s :: Files: %s :: Directories: %s" % (user, (avgspeed/1024.0), numdownloads, numfiles, numdirs) ) self.mucous.requests["statistics"].remove(user) if user == self.mucous.username: self.mucous.data["mystats"] = user, avgspeed, numdownloads, numfiles, numdirs if self.mucous.mode == "setup": self.mucous.Setup.Mode() except Exception, e: self.mucous.Help.Log( "debug", "cb_peer_stats: " + str(e)) ## Recieved private message from the server # @param self Networking (Driver Class) # @param direction 0 == incoming; 1 == outgoing # @param timestamp (we use our own) # @param user username # @param message text def cb_private_message(self, direction, timestamp, user, message): try: self.mucous.PrivateChat.Recieved(direction, timestamp, user, message) except Exception, e: self.mucous.Help.Log( "debug", "cb_private_message: " + str(e)) ## Recieved a notification from the server of your away status # @param self Networking (Driver Class) # @param status away=1/online=0 def cb_server_status_set(self, status): try: self.mucous.Spl["status"] = status if status: stat = "Away" else: stat = "Online" self.mucous.logs["onlinestatus"]=stat self.mucous.DrawOnlineStatus() self.mucous.TerminalTitle() except Exception, e: self.mucous.Help.Log( "debug", "cb_server_status_set: " + str(e)) ## Recieved list of tickers for room # @param self Networking (Driver Class) # @param room Room name # @param tickers Dict of Users and Tickers def cb_room_tickers(self, room, tickers): try: for message, user in tickers.items(): if "room" not in self.mucous.ChatRooms.tickers: self.mucous.ChatRooms.tickers[room] = {} self.mucous.ChatRooms.tickers[room][user] = message except Exception, e: self.mucous.Help.Log( "debug", "cb_room_tickers: " + str(e)) ## A user in room set their ticker to message # @param self Networking (Driver Class) # @param room Room name # @param user User name # @param message ticker def cb_room_ticker_set(self, room, user, message): try: what = message did = "ticker" if self.mucous.config != {}: if "ignored" in self.mucous.config.keys(): if user not in self.mucous.config["ignored"].keys(): self.mucous.ChatRooms.AppendStatus(user, room, did, what) if room in self.mucous.ChatRooms.tickers.keys(): self.mucous.ChatRooms.tickers[room][user] = message except Exception, e: self.mucous.Help.Log( "debug", "cb_room_ticker_set: " + str(e)) ## New Search ticket recieved # @param self Networking (Driver Class) # @param query string searched for # @param ticket unique number associated with search def cb_search_ticket(self, query, ticket): try: self.mucous.Search.NewTicket(query, ticket) except Exception, e: self.mucous.Help.Log( "debug", "cb_search_ticket: " + str(e)) ## Recieved search results from a user # @param self Networking (Driver Class) # @param ticket unique number (used to organize results) # @param user username of user with results # @param free is there a free slot open? (True/False) # @param speed average speed of user # @param queue length of queue # @param results list of files [path, size, extension, list of attributes(bitrate, length, unused)] def cb_search_results(self, ticket, user, free, speed, queue, results): # search results try: self.mucous.Search.NewResults(ticket, user, free, speed, queue,results) except Exception, e: self.mucous.Help.Log("debug", "CB User Shares: " + str(e)) ## Recieved a user's shares # @param self Networking (Driver Class) # @param user Username # @param shares Dict of shares def cb_user_shares(self, user, shares): try: self.mucous.BrowseShares.Recieved(user, shares) except Exception, e: self.mucous.Help.Log("debug", "cb_search_results: " + str(e)) ## Recieved Transfer State from museekd # @param self Networking (Driver Class) # @param downloads list of instances of transfers # @param uploads list of instances of transfers def cb_transfer_state(self, downloads, uploads): try: for transfer in uploads: self.mucous.Transfers.transfers["uploads"][(transfer.user, transfer.path)] = [transfer.is_upload, transfer.user, transfer.path, int(transfer.state), transfer.error, transfer.filepos, transfer.filesize, transfer.rate, transfer.place] self.mucous.Transfers.DrawUploadCount(str(len(self.mucous.Transfers.transfers["uploads"].keys()))) for transfer in downloads: self.mucous.Transfers.transfers["downloads"][(transfer.user, transfer.path)] = [transfer.is_upload, transfer.user, transfer.path, int(transfer.state), transfer.error, transfer.filepos, transfer.filesize, transfer.rate, transfer.place] self.mucous.Transfers.DrawDownloadCount(str(len(self.mucous.Transfers.transfers["downloads"].keys()))) if self.mucous.mode == "transfer": if self.mucous.Config["mucous"]["transbox"] == "split": self.mucous.Transfers.UploadManager() self.mucous.Transfers.DownloadManager() curses.doupdate() else: if self.mucous.Transfers.Transfers.current == "uploads": self.mucous.Transfers.UploadManager() curses.doupdate() else: self.mucous.Transfers.DownloadManager() curses.doupdate() if self.mucous.Config["mucous"]["auto-retry"] == "yes": self.mucous.timers["retry"].cancel() self.mucous.timers["retry"] = threading.Timer(30.0, self.mucous.ThreadTransfersRetry) self.mucous.timers["retry"].start() if self.mucous.Config["mucous"]["auto-clear"] == "yes": self.mucous.timers["clear"].cancel() self.mucous.timers["clear"] = threading.Timer(30.0, self.mucous.ThreadTransfersClear) self.mucous.timers["clear"].start() except Exception, e: self.mucous.Help.Log("debug", "cb_transfer_state: " + str(e)) ## Recieved A Transfer update # @param self Networking (Driver Class) # @param transfer transfer instance def cb_transfer_update(self, transfer): try: if transfer.is_upload: self.mucous.Transfers.transfers["uploads"][(transfer.user, transfer.path)] = [transfer.is_upload, transfer.user, transfer.path, int(transfer.state), transfer.error, transfer.filepos, transfer.filesize, transfer.rate, transfer.place] if self.mucous.mode == "transfer": if self.mucous.Config["mucous"]["transbox"] == "split": self.mucous.Transfers.UploadManager() curses.doupdate() else: if self.mucous.Transfers.current == "uploads": self.mucous.Transfers.UploadManager() curses.doupdate() self.mucous.Transfers.DrawUploadCount(str(len(self.mucous.Transfers.transfers["uploads"].keys()))) else: self.mucous.Transfers.transfers["downloads"][(transfer.user, transfer.path)] = [transfer.is_upload, transfer.user, transfer.path, int(transfer.state), transfer.error, transfer.filepos, transfer.filesize, transfer.rate, transfer.place] if self.mucous.mode == "transfer": if self.mucous.Config["mucous"]["transbox"] == "split": self.mucous.Transfers.DownloadManager() curses.doupdate() else: if self.mucous.Transfers.current == "uploads": pass else: self.mucous.Transfers.DownloadManager() curses.doupdate() self.mucous.Transfers.DrawDownloadCount(str(len(self.mucous.Transfers.transfers["downloads"].keys()))) if self.mucous.mode == "transfer": if self.mucous.PopupMenu.show == True: self.mucous.PopupMenu.Create() except Exception, e: self.mucous.Help.Log("debug", "cb_transfer_update: " + str(e)) ## Removed a transfer # @param self Networking (Driver Class) # @param transfer transfer instance def cb_transfer_remove(self, transfer): try: user_path = transfer[1], transfer[2] if transfer[0]: del self.mucous.Transfers.transfers["uploads"][user_path] if self.mucous.mode == "transfer": if self.mucous.Config["mucous"]["transbox"] == "split": self.mucous.Transfers.UploadManager() curses.doupdate() else: if self.mucous.Transfers.current == "uploads": self.mucous.Transfers.UploadManager() curses.doupdate() self.mucous.Transfers.DrawUploadCount(str(len(self.mucous.Transfers.transfers["uploads"].keys()))) else: del self.mucous.Transfers.transfers["downloads"][user_path] if self.mucous.mode == "transfer": if self.mucous.Config["mucous"]["transbox"] == "split": self.mucous.Transfers.DownloadManager() curses.doupdate() else: if self.mucous.Transfers.current == "uploads": pass else: self.mucous.DownloadManager() curses.doupdate() self.mucous.Transfers.DrawDownloadCount(str(len(self.mucous.Transfers.transfers["downloads"].keys()))) except Exception, e: self.mucous.Help.Log("debug", "cb_transfer_remove: " + str(e)) ## Set a key/value in Mucous.config # @param self Networking (Driver Class) # @param domain parent of key # @param key to be modified # @param value key's new value def cb_config_set(self, domain, key, value): try: if self.mucous.config.has_key(domain) and key in self.mucous.config[domain].keys(): if not domain.startswith("museeq"): self.mucous.Help.Log("status", "Modified <"+key+"> in <" +domain+"> to <"+value + ">") else: if value == '' and domain is not "userinfo" and not domain.startswith("museeq"): self.mucous.Help.Log("status", "Added <"+key+"> to <" +domain+">") else: self.mucous.Help.Log("status", "Added <"+key+"> to <" +domain+"> and set to <"+value+">") if not self.mucous.config.has_key(domain): self.mucous.config[domain] = {} self.mucous.config[domain][key] = value self.mucous.ConfigUpdateDisplay(domain) except Exception, e: self.mucous.Help.Log("debug", "cb_config_set: " + str(e)) ## Delete a key from Mucous.config # @param self Networking (Driver Class) # @param domain parent of key # @param key to be removed def cb_config_remove(self, domain, key): try: if key in self.mucous.config[domain].keys(): self.mucous.Help.Log("status", "Removed <"+key+"> from <" +domain+">") del self.mucous.config[domain][key] self.mucous.ConfigUpdateDisplay(domain) except Exception, e: self.mucous.Help.Log("debug", "cb_config_remove: " + str(e)) ## Recieved a copy of museekd's config # copy it to Mucous.config at connection # @param self Networking (Driver Class) # @param museek_config copy of config for internal use def cb_config_state(self, museek_config): try: self.mucous.config = museek_config.copy() self.mucous.Help.Log("status", "Server is at: "+self.mucous.config["server"]["host"]+":"+self.mucous.config["server"]["port"]) self.mucous.UsersLists.ListBuddy() self.mucous.UsersLists.ListBan() self.mucous.UsersLists.ListIgnore() self.mucous.refresh_windows() self.mucous.Spl["museekconfigfile"] = os.path.expanduser("~/.museekd/config.xml") if self.mucous.config["shares"]["database"] != "": pos = self.mucous.config["shares"]["database"].rfind(".") file = self.mucous.config["shares"]["database"][:33]+".xml" if os.path.exists(file): self.mucous.Spl["museekconfigfile"] = file except Exception, e: self.mucous.Help.Log("debug", "cb_config_state: " + str(e)) # -- ^ Recieved Messages from Museekd^ ## Failsafe Sending of messages to Museekd # @param self Networking (class) # @param message messages instance def SendMessage(self, message): try: if self.mucous.Spl["connected"] == 0: return self.send(message) except Exception, e: self.mucous.Help.Log("debug", "SendMessage: " + str(e)) ## Abort transfer (remains in transfer list) # @param self Networking (class) # @param direction (1: upload, 0:download) # @param user username # @param path file to be transfered def TransferAbort(self, direction, user, path): ## Transfer messages message = messages.TransferAbort(direction, user, path) self.SendMessage(message) ## Remove transfer from transfer list # @param self Networking (class) # @param direction (1: upload, 0:download) # @param user username # @param path file to be transfered def TransferRemove(self, direction, user, path): message = messages.TransferRemove(direction, user, path) self.SendMessage(message) ## Check place in queue # @param self Networking (class) # @param user username # @param path file in queue def TransferUpdate(self, user, path): message = messages.TransferUpdate(user, path) self.SendMessage(message) ## Download a file # @param self Networking (class) # @param user username # @param path file to be transfered def DownloadFile(self, user, path): message = messages.DownloadFile(user, path) self.SendMessage(message) ## Download a file to a local directory # @param self Networking (class) # @param user username # @param path file to be transfered # @param directory local save directory def DownloadFileTo(self, user, path, directory): message = messages.DownloadFileTo(user, path, directory) self.SendMessage(message) ## Ask user to send the contents of a directory # @param self Networking (class) # @param user username # @param directory Directory to recieve def GetFolderContents(self, user, directory): message = messages.GetFolderContents(user, directory) self.SendMessage(message) ## Upload a file to user from path # @param self Networking (class) # @param user username # @param path file to be transfered def UploadFile(self, user, path): message = messages.UploadFile(user, path) self.SendMessage(message) ## Say message in room # @param self Networking (class) # @param room Room # @param message text def SayRoom(self, room, message): messages.SayRoom(room, message) self.SendMessage(message) ## Leave a Room # @param self Networking (class) # @param room leave this room def LeaveRoom(self, room): message = messages.LeaveRoom(room) self.SendMessage(message) ## Get Room List # @param self Networking (class) def RoomList(self): message = messages.RoomList() self.SendMessage(message) ## Say line in room # @param self Networking (class) # @param room A Room you are in # @param line message def SayRoom(self, room, line): message = messages.SayRoom(room, line) self.SendMessage(message) ## Set your ticker in room # @param self Networking (class) # @param room Room name # @param ticker message def RoomTickerSet(self, room, ticker): message = messages.RoomTickerSet(room, ticker) self.SendMessage(message) ## Join a room # @param self Networking (class) # @param room Room name def JoinRoom(self, room): message = messages.JoinRoom(room) self.SendMessage(message) ## Get a user's IP address and listen port # @param self Networking (class) # @param user Username def PeerAddress(self, user): message = messages.PeerAddress(user) self.SendMessage(message) ## Private Chat messages # @param self Networking (class) # @param direction 1 if outgoing 0 if incoming # @param user username of user message is coming from or going to # @param line message def PrivateMessage(self, direction, user, line): message = messages.PrivateMessage(direction, user, line) self.SendMessage(message) ## Get a user's away status # @param self Networking (class) # @param user username def PeerStatus(self, user): message = messages.PeerStatus(user) self.SendMessage(message) ## Does a user exist in the server's database? # @param self Networking (class) # @param user username def PeerExists(self, user): self.SendMessage(messages.PeerExists(user)) ## Get a user's statistics # @param self Networking (class) # @param user username def PeerStats(self, user): message = messages.PeerStats(user) self.SendMessage(message) ## Get a user's userinfo # @param self Networking (class) # @param user username def UserInfo(self, user): message = messages.UserInfo(user) self.SendMessage(message) ## Get a user's shares # @param self Networking (class) # @param user username def UserShares(self, user): message = messages.UserShares(user) self.SendMessage(message) ## Search for a string in one of three methods # @param self Networking (class) # @param searchtype (0:Global, 1:Buddy, 2:Rooms) # @param query Search string def Search(self, searchtype, query): message = messages.Search(searchtype, query ) self.SendMessage(message) ## Search a user's shares for a string # @param self Networking (class) # @param user Username # @param query Search string def UserSearch(self, user, query): message = messages.UserSearch(user, query ) self.SendMessage(message) ## Search via the Wishlist # @param self Networking (class) # @param query Search Query def WishListSearch(self, query): message = messages.WishListSearch(query ) self.SendMessage(message) ## Get a list of users with your interests # @param self Networking (class) def GetSimilarUsers(self): message = messages.GetSimilarUsers() self.SendMessage(message) ## Get a list of recommendations related to your interests # @param self Networking (class) def GetRecommendations(self): message = messages.GetRecommendations() self.SendMessage(message) ## Get a list of recommendations related based on popularity # @param self Networking (class) def GetGlobalRecommendations(self): message = messages.GetGlobalRecommendations() self.SendMessage(message) ## Add A liked interest # @param self Networking (class) # @param interest string def AddInterest(self, interest): message = messages.AddInterest(interest) self.SendMessage(message) ## Add a hated interest # @param self Networking (class) # @param interest string def AddHatedInterest(self, interest): message = messages.AddHatedInterest(interest) self.SendMessage(message) ## Remove a liked interest # @param self Networking (class) # @param interest string def RemoveInterest(self, interest): message = messages.RemoveInterest(interest) self.SendMessage(message) ## Remove a hated interest # @param self Networking (class) # @param interest string def RemoveHatedInterest(self, interest): message = messages.RemoveHatedInterest(interest) self.SendMessage(message) ## Check the amount of time of server privileges we have left # @param self Networking (class) def CheckPrivileges(self): message = messages.CheckPrivileges() self.SendMessage(message) ## Give a number of days of privileges ot a user (Must have privileges to give them) # @param self Networking (class) # @param user Username # @param days days of privileges def GivePrivileges(self, user, days): message = messages.GivePrivileges(user, days) self.SendMessage(message) ## Set your away status # @param self Networking (class) # @param status (1:away, 0:online) def SetStatus(self, status): message = messages.SetStatus(status) self.SendMessage(message) ## Museekd connect to server (Reconnect if connected) # @param self Networking (class) def ConnectServer(self): message = messages.ConnectServer() self.SendMessage(message) ## Museekd disconnect from server # @param self Networking (class) def DisconnectServer(self): message = messages.DisconnectServer() self.SendMessage(message) ## Museekd reload the shares db from disk # @param self Networking (class) def ReloadShares(self): message = messages.ReloadShares() self.SendMessage(message) ## Set an option in Museekd's config # @param self Networking (class) # @param domain parent of key # @param key key being changed # @param value value of key def ConfigSet(self, domain, key, value): message = messages.ConfigSet(domain, key, value) self.SendMessage(message) ## Remove an option from Museekd's config # @param self Networking (class) # @param domain parent of key # @param key key being changed def ConfigRemove(self, domain, key): message = messages.ConfigRemove(domain, key) self.SendMessage(message) ## Ping Museekd # @param self Networking (class) # @param num number that will be echoed back to us def Ping(self, num): message = messages.Ping(num) self.SendMessage(message) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousHelp.py0000644000175000017500000003773010572323003022524 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper #import traceback import time import sys ## Help logs and windows # class Help: ## Constructor # @param self Help (Class) # @param mucous Mucous (Class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var scrolling # dict of vertical scroll positions for help, debug self.scrolling = {"help":0, "debug":0} ## @var dimensions # Window placement self.dimensions = {} ## @var windows # Curses Window instances self.windows = {} ## @var log # dict containing help log lists self.log = {} self.log["private"] = ["Global Private Messaging commands:",\ "To start a Private Message:",\ "1) Type in the username you wish to PM below",\ "2) Press ",\ "3) Type your messages", "Use the commands below:",\ "/pm (Start PM or switch to user)",\ "/close (Close user's PM) ",\ "/close (Close current PM)",\ "/ip (Get the IP of current user)",\ "/msg (Send message to previous chosen user)"] self.log["userinfo"] = ["Global User Information commands:",\ "/userinfo ",\ "/stat ",\ "/ip ",\ "Or type the user name that you wish get recieve userinfo from, below.", "--"] self.log["chat"] = ["----[Chat Commands]----", \ "/join /part /leave ",\ "/j /talk /say ",\ "/users (lists of users in room)",\ "/autojoin (Toggle Autojoining room)",\ "/roomlistrefresh (redownload roomlist from server)",\ "/inrooms (list of joined rooms)",\ "/clearroom (clear , or current room)",\ "/pm (Private Message user)",\ "/msg (send message to last user)",\ "/url /urlcustom (command$command) (Requires X11)",\ "/urlreader (lynx|links|elinks|firefox|custom) (Requires X11)",\ "/np (XMMS/BMP Now playing script)",\ "/npcheck (display Now playing script command)",\ "/npset (Set Now playing script to command)",\ "/npprefix (set to np: or /me is now playing:)",\ "/alias /unalias "] self.log["tickers"] = ["-------- ",\ "/showtickers (Hide/Show tickers)",\ "/tickers (Scroll or Cycle through tickers)",\ "/listtick (List tickers in the Info mode)",\ "/tickroom (Choose room for setting tickers)",\ "/settick (Set ticker to message, and save it)",\ "/defaulttick (Set & save default ticker)",\ "/settemptick (Set ticker only for this session)"] self.log["connect"] = ["Connection Configuration",\ "/interface or ",\ "/password ",\ "/connect (Attempts to connect to Museekd)",\ "/disconnect (Disconnects from Museekd)",\ "/login (Login to Soulseek Server)",\ "/logout (Logout from Soulseek Server)",\ "/save (Writes settings to config)"] self.log["setup"] = ["Setup",\ "/autobuddy (Auto-buddy users you download from)",\ "/autoclear (auto-clear finished uploads)",\ "/autoretry (auto-retry failed/errored transfers)",\ "/password (Mucous' Interface password)",\ "/privbuddy (Toggle Privileging buddies)",\ "/onlybuddy (Toggle Only sharing to buddies)",\ "/slots (Set upload slots)",\ "/unhide (Toggle showing password)",\ "/rescan (Rescan Normal Shares)",\ "/rescanbuddy (Rescan Buddy-only Shares)",\ "/reload (Reload Shares - make changes take effect)",\ "/extra (Auto-Send Version info via PM if requested)",\ "/logdir /path (Set Logs Directory)",\ "/logging (Toggle Logging)"] self.log["user"] = ["----[User Commands]---- ",\ "/buddy /unbuddy ",\ "/ban /unban (Cannot access your files)",\ "/ignore /unignore (No messages in chat)",\ "/nuke /unnuke (Ban+Ignore)",\ "/trust /distrust (Can Upload to you)",\ "/stat /ip ",\ "/userinfo /giveprivs (Input days next)",\ "/away (Toggle your Online/Away Status)",\ "/autoaway (Go away after 900 seconds of inactivity)"] self.log["transfer"] = ["----[Transfer Commands]---- ",\ "/abortu /abortup (Abort Upload)",\ "/abortd /abortdown (Abort Download)",\ "/removeu /removeup (Remove Upload)",\ "/removed /removedown (Remove Download)",\ "/retry (Retry Download)",\ "/retryall (Retry all Downloads)",\ "/clearup (Clear failed/finished Uploads)",\ "/cleardown (Clear finished Download)",\ "/percent (Toggle Percent/Speed",\ "/transbox (Toggle Split/Tabbed Mode))"] self.log["modes"] = ["----[Mode Commands]---- ",\ "/chat (Chat Mode)",\ "/transfer (Transfer Mode)",\ "/info (Info Mode)",\ "/browse (Browse Mode)",\ "/private (Private Message Mode)",\ "/search (Search Mode)",\ "/buddylist (Buddylist Mode)",\ "/banlist (Banlist Mode)",\ "/ignorelist (Ignorelist Mode)",\ "/roomlist (Roomlist Mode)",\ "/setup (Setup Mode)",\ "/help /debug (Help & Debug Mode)"] self.log["helpcommands"] = ["-------- ",\ "/help (This Message)",\ "/help connect (Connection Commands)",\ "/help setup (Setup Commands)",\ "/help mode (Mode Commands)",\ "/help chat (Chatroom Commands)",\ "/help ticker (Ticker Commands)",\ "/help user (User Commands)",\ "/help transfer (Transfer Commands)",\ "/help browse (Browse Commands)",\ "/help search (Search Commands)",\ "/help download (Download Commands)",\ "/help keys (Special Keys)",\ "/quit (Close Mucous)"] self.log["search"] = [\ "/search (Switch to Search Mode)",\ "/searchfor (Global Search)",\ "/searchbuddy (Buddy Search)",\ "/searchroom (Room Search)",\ "/searchuser (Search only one user's shares)",\ "/download (Download File with number)",\ "/downdir (Download directory of File)",\ "/close (Close current search)",\ "/clearsearchs (Removes all searches)",\ "/filter (limit displayed files)",\ "Mouse: Right-Click (popup menu)",\ "Press Alt-Left, Alt-Right and Insert to change the current widget's setting.",\ "Press Alt-T to switch between the different widgets"] self.log["search"] = ["--------"] + self.log["search"] self.log["browse"] = ["--------",\ "/browse (Switch to Browse Mode)",\ "/buser (Browse User)",\ "/bsearch (Search thru Shares)",\ "/download (Download file with this number)",\ "/close (close current shares)",\ "Press Insert to toggle between shares.",\ "Alt-X (Expand/Collapse directory)",\ "/browsewidth (Resize Directories window)",\ "--File System browsing commands--",\ "cd (change dir) get, getdir (download)",\ "First, type in the user you wish to browse, below.", \ "Right-click on directories or files to display the popup menu."] self.log["download"] = ["--------",\ "/downuser (Set download user)",\ "/downpath (Download file from user)",\ "/downpathdir (Download dir from user)"] self.log["keys"] =["------------ ",\ "No guarantees that these HotKeyBar work with your terminal",\ "ESC or Alt + [, ], <-, -> (Change Room / Transfer display mode)",\ "Insert (Same as above)",\ "Tab (Completes nicks)",\ "Home/End (switches Upload & Download scrolling)",\ "Up, PageUp (Scroll Up a line, a page)",\ "Down, PageDown (Scroll Down a line, a page)",\ "F1->Chat Rooms F6->Browse Users",\ "F2->Private Messages F7->User Lists/Interests",\ "F3->Transfers F8->Rooms List",\ "F4->Search F9->Setup",\ "F5->Info F10->Help"] self.log["debug"] = [] self.log["help"] =[\ " _____ __ __ ____ ____ __ __ ______",\ " / \| | \_/ ___\/ _ \| | \/ ___/",\ "| Y Y \ | /\ \__( <_> ) | /\___ \ ",\ "|__|_| /____/ \___ >____/|____//____ >",\ " \/ \/ \/",\ "/help (This Message) /quit (Shut down Mucous)",\ "/help connect (Connection Commands) /help setup (Setup Commands)",\ "/help mode (Mode Commands) /help user (User Commands)",\ "/help chat (Chatroom Commands) /help ticker (Ticker Commands)",\ "/help transfer (Transfer Commands) /help download (Download Commands)",\ "/help browse (Browse Commands) /help search (Search Commands)",\ "/help keys (Special Keys)"] ## Create window, draw title, # call Help.Format # call Mucous.SetEditTitle # call Mucous.Alert.Check # @param self Help (Class) def Mode(self): try: self.mucous.UseAnotherEntryBox() # Cleanup stale windows if "text" in self.windows: del self.windows["text"] if "border" in self.windows: del self.windows["border"] if self.mucous.mode not in ("help", "debug"): self.mucous.mode = "debug" if self.mucous.mode == "help": logfile = self.log["help"] elif self.mucous.mode == "debug": logfile = self.log["debug"] self.mucous.PopupMenu.show = False s = self.dimensions["help"] = {"height": self.mucous.h-7, "width": self.mucous.w-2, "top": 2, "left": 1, "start": 0} mw = self.windows["border"] = curses.newwin(s["height"]+2, s["width"]+2, s["top"]-1, s["left"]-1) mw.attron(self.mucous.colors["green"]) mw.border() mw.attroff(self.mucous.colors["green"]) try: if self.mucous.mode == "help": mw.addstr(0, 3, "< Help Mode >", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(0, 18, "< >", self.mucous.colors["green"]) mw.addstr(0, 20, "Debug Mode", curses.A_BOLD) elif self.mucous.mode == "debug": mw.addstr(0, 3, "< >", self.mucous.colors["green"] ) mw.addstr(0, 5, "Help Mode", curses.A_BOLD) mw.addstr(0, 18, "< Debug Mode >", self.mucous.colors["green"] | curses.A_BOLD) except: pass mw.refresh() tw = self.windows["text"] = mw.subwin(s["height"], s["width"], s["top"], s["left"]) tw.scrollok(0) tw.idlok(1) self.scrolling["help"] = -1 self.scrolling["debug"] = -1 self.Format() self.mucous.SetEditTitle("Use /help") #if self.Alerts.log in ("New Help", "New Bug", "New Status"): # self.Alerts.setStatus("") self.mucous.Alerts.Check() curses.doupdate() except Exception, e: # self.Log("debug", ": " + str(e)) self.mucous.ChatRooms.AppendChat("Status", self.mucous.ChatRooms.current, "Format: ", str(e)) pass ## Format lines and then # Call Draw for each line # @param self Help (Class) def Format(self): try: w = self.dimensions["help"] tw = self.windows["text"] size = w["height"] * w["width"] if self.mucous.mode == "help": logfile = self.log["help"] elif self.mucous.mode == "debug": logfile = self.log["debug"] # DEBUGGING wrapped_lines = [] for lines in logfile: list_of_strings = self.mucous.FormatData.StringCutWidth(lines, w) for string in list_of_strings: wrapped_lines.append(string) if self.scrolling[self.mucous.mode] == -1: self.scrolling[self.mucous.mode] = len(wrapped_lines) clipped_list, self.scrolling[self.mucous.mode], w["start"] = self.mucous.FormatData.scrollbox(wrapped_lines, self.scrolling[self.mucous.mode], w["height"]) del wrapped_lines count = 0 blanked_lines = [] for lines in clipped_list: s, ls = self.mucous.FormatData.StringAddBlanks(lines, w) blanked_lines.append(s) clipped_list = blanked_lines del blanked_lines count = 0 total_lines = 0 tw.erase() for line in clipped_list: try: if line is clipped_list[-1]: line = line[:-1] self.Draw(self.mucous.mode, line, count) count += 1 except Exception, e: self.mucous.ChatRooms.AppendChat("Status", self.mucous.ChatRooms.current, 'ERR', str(e) ) tw.refresh() except Exception, e: self.mucous.ChatRooms.AppendChat("Status", self.mucous.ChatRooms.current, "Format: ", str(e)) ## Append anything to the Log (converted to string) # @param self Help (Class) # @param htype Help type (help, status, debug) # @param s data to be appended to the log def Log(self, htype, s): try: s = str(s) s = s.replace("\t", " ") if htype == "help": if "\n" in s: lis = s.split("\n") for line in lis: self.log["help"].append("%s" %line ) else: self.log["help"].append("%s" %s ) elif htype in ("status", "debug"): timestamp = time.strftime("%H:%M:%S") if htype == "status": ex = '' else: ex = "BUG " if "\n" in s: lis = s.split("\n") for line in lis: newline = "" for character in line: if curses.ascii.isctrl(character): character = curses.ascii.unctrl(character) newline += character if line is lis[0]: self.log["debug"].append("%s %s%s" % (timestamp,ex,newline)) else: self.log["debug"].append("%s%s" % (ex,newline)) else: self.log["debug"].append("%s %s%s" %(timestamp, ex,s)) if htype == "debug": ex = "BUG " tbe = sys.exc_info() for line in tbe: if line is tbe[0]: self.log["debug"].append("%s %s%s" % (timestamp,ex,line)) #else: self.log["debug"].append("%s%s" % (ex,line)) tb = self.mucous.traceback.extract_tb(sys.exc_info()[2]) for line in tb: if type(line) is tuple: xline = "" for item in line: xline += str(item) + " " line = xline newline = "" for character in line: if curses.ascii.isctrl(character): character = curses.ascii.unctrl(character) newline += character if line is tb[0]: self.log["debug"].append("%s %s%s" % (timestamp,ex,newline)) else: self.log["debug"].append("%s%s" % (ex,newline)) if self.mucous.mode in ( "help", "debug", "status"): #self.Draw( htype, s, 0) self.scrolling[self.mucous.mode] = -1 self.Format() self.mucous.Alerts.Check() curses.doupdate() else: if htype not in self.mucous.Alerts.alert["HELP"]: if htype == "help": self.mucous.Alerts.alert["HELP"].append("help") elif htype == "status": self.mucous.Alerts.alert["HELP"].append("status") elif htype == "debug": self.mucous.Alerts.alert["HELP"].append("debug") self.mucous.Alerts.Check() except Exception, e: self.mucous.ChatRooms.AppendChat("Status", self.mucous.ChatRooms.current, 'ERR', str(e) ) pass ## Draw a line in the Help log # @param self Help (Class) # @param htype Help type (help, status, debug) # @param s data to be drawn # @param count scroll position def Draw(self, htype, s, count): try: if self.mucous.mode in ( "help", "debug", "status"): tw = self.windows["text"] w = self.dimensions["help"] if count + w["start"] == self.scrolling[self.mucous.mode]: attr = curses.A_BOLD else: attr = curses.A_NORMAL lastmessage = "" for character in self.mucous.dlang(s): if curses.ascii.isctrl(character): character = curses.ascii.unctrl(character) lastmessage += character if self.mucous.mode == "help" and htype == "help": tw.addstr(lastmessage, attr) elif self.mucous.mode == "debug" and htype in( "status", "debug"): tw.addstr(lastmessage, attr) else: self.mucous.Alerts.setStatus("New Help") else: self.mucous.Alerts.setStatus("New Help") except: # Exception, e: #self.ChatRooms.AppendChat("Status", self.ChatRooms.current, "Draw: ", str(e)) pass museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousShares.py0000644000175000017500000006143710560763057023100 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper ## Browse Shares # Files and Directory viewer class BrowseShares: ## Constructor # @param self BrowseShares (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var dimensions # Window placement self.dimensions = {} ## @var windows # Curses Window instances self.windows = {} ## @var requests # Users whose shares we've requested (Discard other requests) self.requests = [] ## @var current # Currently shown user's shares self.current = None ## @var current_dir # Current Directory self.current_dir = None ## @var users # Users whose info we have self.users = [] ## @var logs # dict containing descriptions and stats of users self.logs = {} ## @var help # Instructions self.help = self.mucous.Help.log["browse"] ## @var files # Current user's files in current directory self.files = [] ## @var dirs # Current user's directories self.dirs = [] ## @var scrolling # dict containing vertical scroll position for files, directories self.scrolling = {"files": 0, "directories": 0} ## @var selected # Selected window self.selected = "directories" ## @var results # dict of users with dict of shares self.results = {} ## @var collapsed # dict of users with list of collapsed directories self.collapsed = {} ## @var browse_num # dict of numbers related to the current files in the current directory self.browse_num = {} ## @var bfilter # Filter string for files self.bfilter = None self.dirswithtree = None ## Get the currently selected user and file # @param self BrowseShares (class) # @return user, path def CurrentFile(self): user = self.current if self.files != []: path = self.current_dir+"\\"+self.files[self.scrolling["files"]] else: path = self.current_dir return user, path ## Get the currently selected user and directory # @param self BrowseShares (class) # @return user, directory def CurrentDir(self): if self.selected == "files": w = self.dimensions["browse"] directory = self.current_dir user = self.current elif self.selected == "directories": w = self.dimensions["directories"] directory = self.current_dir user = self.current return user, directory ## Get the user and path from file number # @param self BrowseShares (class) # @param number number of file # @return user, path def GetDownloadFromNum(self, number): try: number = int(number) user = self.current path = self.current_dir+"\\"+self.files[number] return user, path except Exception, e: self.mucous.Help.Log("debug", "download_path_file: " + str(e)) ## Create windows and Call draw functions # @param self BrowseShares (class) def Mode(self): try: self.dirswithtree = None self.mucous.mode = "browse" self.mucous.UseAnotherEntryBox() self.mucous.PopupMenu.show = False # Cleanup stale windows if "text" in self.windows: del self.windows["text"] if "border" in self.windows: del self.windows["border"] if "dirwin" in self.windows: del self.windows["dirwin"] if "dirborder" in self.windows: del self.windows["dirborder"] if "browsebar" in self.windows: del self.windows["browsebar"] w = self.dimensions["browse"] = {"height": self.mucous.h-11, "width": self.mucous.w-self.mucous.Config["mucous"]["browse_width"], "top": 5, "left": self.mucous.Config["mucous"]["browse_width"]-1, "start": 0} # Files Border wbb = self.windows["border"] = curses.newwin(w["height"]+2, w["width"]+2, w["top"]-1, w["left"]-1) # Directories Border self.windows["dirborder"] = curses.newwin(w["height"]+2, self.mucous.w-w["width"]-2, w["top"]-1, 0) self.DrawBrowseWin() # Files Text tbb = self.windows["text"] = wbb.subwin(w["height"], w["width"], w["top"], w["left"]) tbb.scrollok(0) tbb.idlok(1) tbb.noutrefresh() d = self.dimensions["directories"] = {"height": w["height"], "width": self.mucous.w-w["width"]-4, "top": w["top"], "left":1} # Directories Text dw = self.windows["dirwin"] = self.windows["dirborder"].subwin(d["height"], d["width"], d["top"], d["left"]) dw.erase() dw.noutrefresh() # self.scrolling["files"] = self.scrolling["directories"] = 0 # Vars self.files = [] self.dirs = [] self.windows["browsebar"] = curses.newwin(1, self.mucous.w, w["top"]+w["height"]+1, 0) self.windows["browsebar"].erase() self.windows["browsebar"].noutrefresh() self.mucous.Alerts.Check() self.mucous.HotKeyBar() self.FormatBrowse() curses.doupdate() del w except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.Mode: " + str(e)) ## Recieved shares # @param self BrowseShares (class) # @param user Username # @param shares Shares dict def Recieved(self, user, shares): try: if user not in self.requests: return self.requests.remove(user) if user not in self.users: self.users.append(user) self.current = user if self.mucous.mode != "browse": self.mucous.Alerts.alert["BROWSE"].append(self.current) self.mucous.Alerts.Check() self.results[user] = {} self.collapsed[user] = [] #self.num[user] = 0 self.results[user]["dirs"] = [] # Debugging #self.mucous.Help.Log("debug", shares.keys()) ######### if shares != {}: sdirs = shares.keys() sdirs.sort(key=str.lower) for item in sdirs: #item.rsplit("\\", 1) s = item.split("\\") path = '' parent = s[0] for seq in s[1:]: parent += "\\" path = parent+seq if path not in self.results[user]["dirs"]: self.results[user]["dirs"].append(path) parent = path self.results[user]["dirs"].sort(key=str.lower) else: self.results[user]["dirs"].append("Empty Shares") self.current_dir = self.results[user]["dirs"][0] self.results[user]["shares"] = shares for dirs, files in shares.items(): self.results[user][dirs] = files #result_list = [] if self.mucous.mode == "browse": self.mucous.SetEditTitle("Browse "+user+"'s files in " + self.current_dir + " ") self.Mode() except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.Recieved: " + str(e)) ## Draw Browse Window Border # @param self BrowseShares (class) def DrawBrowseWin(self): try: w = self.dimensions["browse"] mw = self.windows["border"] if self.mucous.BrowseShares.selected == "files": mw.attron(self.mucous.colors["green"]) else: mw.attroff(self.mucous.colors["green"]) mw.border() try: if self.mucous.BrowseShares.selected == "files": attr = self.mucous.colors["green"] | curses.A_BOLD else: attr = curses.A_BOLD if self.current == None: mw.addstr(0, 3, "< Browse users >", attr) else: mw.addstr(0, 1, "", attr) except: pass mw.noutrefresh() self.windows["dirborder"].erase() if self.mucous.BrowseShares.selected == "directories": self.windows["dirborder"].attron(self.mucous.colors["green"]) attr = self.mucous.colors["green"] | curses.A_BOLD else: attr = curses.A_BOLD self.windows["dirborder"].border() self.windows["dirborder"].attroff(self.mucous.colors["green"] ) self.windows["dirborder"].addstr(0, 1, "< Directories >", attr) self.windows["dirborder"].addstr(w["height"]+1, 1, "< Width - %s + >" % self.mucous.Config["mucous"]["browse_width"], attr) self.windows["dirborder"].noutrefresh() if self.current == None: self.scrolling["directories"] = 0 self.mucous.SetEditTitle("Choose a user to Browse Shares") self.mucous.DrawInstructionsButtons() else: self.mucous.DrawInstructionsButtons() #s = "Browse "+self.current+"'s files in " #ls = len(s) #self.mucous.SetEditTitle( self.current_dir[:self.mucous.w-8] ) except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.DrawBrowseWin: " + str(e)) ## Parse Directories and display extremely formatted list # expensive (HIGH CPU Usage) # @param self BrowseShares (class) def FormatBrowseDirs(self): try: d = self.dimensions["directories"] w = self.dimensions["browse"] tw = self.windows["text"] Dirwin = self.windows["dirwin"] if self.current == None: # Clear windows, display browse help Dirwin.erase() Dirwin.noutrefresh() tw.erase() count = 0 for lines in self.help: try: self.DrawBrowseFileText(lines, count , self.scrolling["files"]) except Exception, e: self.mucous.Help.Log("debug", "Browse mode" + str(e)) tw.noutrefresh() return # If the default help isn't displayed #tempdirs.sort(key=str.lower) # List, Directory, Scroll position collapsed = self.collapsed[self.current] if self.dirswithtree == None: self.dirswithtree = [] num = 0 self.dirs = [] parents = [] tempdirs = self.results[self.current]["dirs"] for directory in tempdirs: parent = "\\".join(directory.split("\\")[:-1]) if parent in tempdirs and parent not in parents: parents.append(parent) for directory in tempdirs: try: parent = "\\".join(directory.split("\\")[:-1]) a = 0 for dirs in collapsed: if parent.startswith(dirs+"\\") or parent == dirs: a = 1 break if a == 1: num += 1 continue if directory in parents: if directory in collapsed: self.dirswithtree.append([directory, 2]) self.dirs.append(directory) else: for dir in collapsed: if dir in directory: pass else: self.dirswithtree.append([directory, 1]) self.dirs.append(directory) else: for dir in collapsed: if dir in directory: pass else: self.dirswithtree.append([directory, 0]) self.dirs.append(directory) except Exception, e: for dir in collapsed: if dir in directory: pass else: self.dirswithtree.append([directory, 0]) self.dirs.append(directory) num += 1 if self.mucous.BrowseShares.selected == "directories": clipped_list, self.scrolling["directories"], start = self.mucous.FormatData.scrollbox(self.dirswithtree, self.scrolling["directories"], d["height"]) else: clipped_list, self.scrolling["directories"], start = self.mucous.FormatData.scrollbox(self.dirswithtree, self.scrolling["directories"], d["height"]) #self.mucous.Help.Log("debug", self.scrolling["directories"]) self.dimensions["directories"]["start"] = start count = 0 Dirwin.erase() # Display directory tree for s, has_child in clipped_list: try: dir = s.split("\\") pre_spaces = " " * (len(dir)-2) Dirwin.addstr(pre_spaces) if has_child == 1: modifier = "[-]" Dirwin.addstr("[") Dirwin.addstr("-", self.mucous.colors["red"] | curses.A_BOLD) Dirwin.addstr("]") size = 3 elif has_child == 2: modifier = "[+]" Dirwin.addstr("[") Dirwin.addstr("+", self.mucous.colors["green"] | curses.A_BOLD) Dirwin.addstr("]") size = 3 else: modifier = "|\\" Dirwin.addstr("|\\") size = 2 string = dir[-1][:d["width"]-len(pre_spaces)-size] self.mucous.dlang(string) string += " " * ( d["width"] -len(string) -len(pre_spaces)-size) # Spaces before directory, to make it look like a tree #string = (" " * (len(dir)-2)) + modifier #string += self.mucous.dlang(dir[-1][:d["width"]-len(string)]) #string += " " * ( d["width"] -len(string) ) if count +d["start"] == self.scrolling["directories"]: Dirwin.addstr(string, self.mucous.colors["green"]) else: Dirwin.addstr(string) count += 1 except Exception, e: pass #self.mucous.Help.Log("debug", str(e)) del clipped_list except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.FormatBrowseDirs: " + str(e)) ## Format Files and Directories (with calls to functions) # @param self BrowseShares (class) # @param FormatDirs Format and Draw Dirs (True/False) def FormatBrowse(self, FormatDirs=True): try: if "directories" not in self.dimensions: return if FormatDirs: self.FormatBrowseDirs() if self.current != None: self.current_dir = self.dirs[self.scrolling["directories"]] self.mucous.SetEditTitle(self.current_dir ) self.DrawFiles( self.current, self.current_dir) self.FileBar( self.current, self.current_dir) self.windows["dirwin"].noutrefresh() self.mucous.DrawTabs(self.users, self.current) except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.FormatBrowse: " + str(e)) ## Send a request to museekd to browse this user # @param self BrowseShares (class) # @param user Username def Get(self, user): try: if user not in self.requests: self.requests.append(user) self.mucous.D.UserShares(user) except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.Get" + str(e)) ## Draw file's number, name and size # @param self BrowseShares (class) # @param line line from list # @param count line position in list # @param sup scroll position def DrawBrowseFileText(self, line, count, sup): try: w = self.dimensions["browse"] tw = self.windows["text"] this_line = self.mucous.dlang( line ) if len(this_line) > self.dimensions["browse"]["width"]: crop = len(this_line) - self.dimensions["browse"]["width"] this_line = this_line[:-crop] if count + w["start"] == sup: attr = self.mucous.colors["blackwhite"] |curses.A_REVERSE|curses.A_BOLD nattr = self.mucous.colors["cyan"] |curses.A_REVERSE|curses.A_BOLD else: attr = curses.A_NORMAL nattr = self.mucous.colors["cyan"] if self.current == None: tw.addstr(this_line, attr ) else: tw.addstr(this_line[:4], attr ) tw.addch(curses.ACS_VLINE, attr) tw.addstr(this_line[5:12], nattr ) tw.addch(curses.ACS_VLINE, attr) tw.addstr(this_line[13:], attr ) z = w["width"]-len(this_line) space = " " * ( z ) tw.addstr(space, attr) except Exception, e: #self.mucous.Help.Log("debug", "BrowseShares.BrowseFileText: " + str(e)) pass ## Draw files in a user's directory # @param self BrowseShares (class) # @param user Username # @param directory Directory of file list def DrawFiles(self, user, directory): try: if self.mucous.mode != "browse": self.mucous.Alerts.alert["BROWSE"].append(user) self.mucous.Alerts.setStatus("Browse: %s" % user) return tw = self.windows["text"] w = self.dimensions["browse"] self.browse_num[user] = 0 browse_list = [] count =0 # file, stats[ size, ftype, [bitrate, length ] ] if directory not in self.results[user]["shares"] or self.results[user]["dirs"] == {}: tw.erase() tw.addstr("Empty..") tw.refresh() self.files = [] return length_list = len(str(len(self.results[user][directory].keys() ) ) ) list1 = self.results[user][directory].keys() list1.sort(key=str.lower) for file in list1: stats = self.results[user][directory][file] count += 1 self.browse_num[user] = self.browse_num[user] +1 size = str(self.mucous.FormatData.Humanize(stats[0])) if len(size) < 6: size = ' '* (6-len(size)) + size ftype =stats[1] if ftype.upper() in ('OGG', 'MP3') and stats[2] != []: bitrate =str(stats[2][0]) if bitrate == '': bitrate = '0' length =str(stats[2][1]) if length != '' and length != None: minutes = int(length)/60 seconds = str( int(length) - (60 * minutes)) if len(seconds) < 2: seconds = '0' + seconds length = str(minutes)+":"+str(seconds) else: length = "0:00" bitrate = '0' else: ftype = "None" length = "0:00" bitrate = '0' filename = directory + "\\" + file #result_list = user, filename # Activate Number for Result if len(str(count)) < 4: s = " " * (4 - len(str(count))) else: s = '' size = " " * (7 - len(str(size))) + size[:7] line = "%s%s|%s|%s" % ( s, str(count), size, file ) browse_list.append(line) self.files = list1 if self.bfilter != None: a = [] for path in browse_list: if re.match( self.bfilter, path): a.append(path) browse_list = a del a clipped_list, self.scrolling["files"], self.dimensions["browse"]["start"] = self.mucous.FormatData.scrollbox(browse_list, self.scrolling["files"], w["height"]) sup = self.scrolling["files"] count = 0 tw.erase() for line in clipped_list: self.DrawBrowseFileText(line, count, sup) count += 1 tw.refresh() except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.DrawFiles: " + str(e)) ## Draw File's stats in FileBar # @param self BrowseShares (class) # @param user Username # @param directory Directory of file list def FileBar(self, user, directory): try: self.windows["browsebar"].erase() num = self.scrolling["files"] if directory not in self.results[user]["shares"]: self.windows["browsebar"].refresh() return if self.results[user]["shares"][directory].keys() != []: list1 = self.results[user]["shares"][directory].keys() list1.sort(key=str.lower) file = list1[num] stats = self.results[user]["shares"][directory][file] else: self.windows["browsebar"].refresh() return size = self.mucous.FormatData.Humanize(stats[0]) ftype =stats[1] if ftype == '': ftype = "None" length = "0:00" bitrate = '0' else: bitrate =str(stats[2][0]) if bitrate == '': bitrate = '0' length =str(stats[2][1]) if length != '' and length != None: minutes = int(length)/60 seconds = str( int(length) - (60 * minutes)) if len(seconds) < 2: seconds = '0' + seconds length = str(minutes)+":"+str(seconds) else: length = "0:00" #l=len('['+str(num+1)+'] '+" Size: " + str(size)+" Length: " + length + " Bitrate: " + bitrate) atr = self.mucous.colors["cyan"] | curses.A_BOLD self.windows["browsebar"].addstr("[") self.windows["browsebar"].addstr(str(num+1), atr ) self.windows["browsebar"].addstr("] | Size: ") self.windows["browsebar"].addstr(str(size), atr) self.windows["browsebar"].addstr(" | ") self.windows["browsebar"].addstr(bitrate, atr) self.windows["browsebar"].addstr("Kbps | Length: ") self.windows["browsebar"].addstr(length, atr) self.windows["browsebar"].refresh() except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.FileBar: " + str(e)) ## Change Directory to line # @param self BrowseShares (class) # @param line string that should contain a valid path def ChangeDir(self, line): try: if self.current_dir == '' or line[0:1] == '\\' or line[1:2] == ":": if self.current in self.results.keys(): if line[0:1] == '\\': if line in self.results[self.current].keys(): self.current_dir = line else: self.mucous.Help.Log("status", "No such directory: %s user:" % (line[1:], self.current)) if line[1:2] == ":": if line in self.results[self.current].keys(): self.current_dir = line else: self.mucous.Help.Log("status", "No such directory: %s user:" % (line, self.current)) else: if line in self.results[self.current].keys(): self.current_dir = line else: self.mucous.Help.Log("status", "No such directory: %s user:" % (line, self.current)) elif line =='..': z = self.ParentDir() if z != 0: self.current_dir = z else: self.mucous.Help.Log("status", "No parent directory, User: " + self.current) else: if self.current_dir + '\\'+line in self.results[self.current].keys(): self.current_dir += '\\'+line else: self.mucous.Help.Log("status", "No such directory: %sUser: " % (line, self.current) ) if self.current == None: self.mucous.SetEditTitle("Choose a user to Browse Shares") else: s = "Browse "+self.current+"'s files in " ls = len(s) self.mucous.SetEditTitle(s + self.current_dir[:self.mucous.w-ls-4] + " ") except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.ChangeDir: " + str(e)) ## Get Parent directory of current_dir # @param self BrowseShares (class) # @return directory def ParentDir(self): try: splitit = self.current_dir splitit = splitit.split("\\") s = len(splitit) directory='' for r in range(s-1): if r == 0: directory += splitit[r] else: directory += "\\"+splitit[r] if directoryz in self.results[self.current]["shares"].keys(): return directory else: return None except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.ParentDir: " + str(e)) ## Close Tab of user # @param self BrowseShares (class) # @param user Username whose shared are being closed def Close(self, user): try: if user in self.users: self.users.remove(user) self.scrolling["directories"] = 0 if user in self.logs: del self.logs[user] if user in self.results: del self.results[user] if self.users != []: if self.current == user: self.current = self.users[0] else: self.current = None if user in self.mucous.Alerts.alert["BROWSE"] and user != "__default": self.mucous.Alerts.alert["BROWSE"].remove(user) self.Mode() except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.Close: " + str(e)) ## Mouse Coordinates in the Browse Shares Mode # @param self is BrowseShares (class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def MouseBrowse(self, x,y,z,event): try: d = self.dimensions["directories"] w = self.dimensions["browse"] if y in (1, 2, 3): if len(self.users) == 1 and self.current == None: self.current = self.users[0] self.Mode() if len(self.users) > 1: if self.current == None: self.current = self.users[0] else: self.current, match = self.mucous.edit.MouseClickTab(x, self.current) if match == None: s = self.users.index(self.current) self.current = self.users[s-1] sdirs = self.results[self.current].keys() sdirs.sort(key=str.lower) self.current_dir=sdirs[0] self.Mode() elif y == w["top"] + w["height"]: if x < self.mucous.Config["mucous"]["browse_width"]: if x in (7,8,9,10): if self.mucous.Config["mucous"]["browse_width"] > 20: self.mucous.Config["mucous"]["browse_width"] -= 1 self.Mode() elif x in (12,13,14,15,16,17): if self.mucous.Config["mucous"]["browse_width"] < self.mucous.w-20: self.mucous.Config["mucous"]["browse_width"] += 1 self.Mode() elif y == w["top"]-1: if x >= self.mucous.w-17 and self.current != None: self.current = None self.Mode() elif x in range(d["width"]) and y >= d["top"] and y <= d["top"] + d["height"]: if self.current == None: return if self.mucous.BrowseShares.selected != "directories": self.mucous.BrowseShares.selected = "directories" self.mucous.BrowseShares.DrawBrowseWin() self.scrolling["directories"] = y - d["top"] + d["start"] self.FormatBrowse() if event in ( 4096, 16384): self.mucous.ChatRooms.DrawBox() self.mucous.PopupMenu.Create("browse-dirs", 0, True) else: self.mucous.ChatRooms.DrawBox() curses.doupdate() elif x >= w["left"] and y >= w["top"] and y <= w["top"] + w["height"]: if self.current == None: return if self.mucous.BrowseShares.selected != "files": self.mucous.BrowseShares.selected = "files" self.mucous.BrowseShares.DrawBrowseWin() self.scrolling["files"] = y - w["top"]+ w["start"] self.FormatBrowse() if event in ( 4096, 16384): #self.mucous.DrawBox() self.mucous.PopupMenu.Create("browse-files", 0, True) #else: #self.ChatRooms.DrawBox() curses.doupdate() elif y in ( self.mucous.h-5, self.mucous.h-6): if x>= self.mucous.w-27 and x < self.mucous.w-18: self.mucous.PopupMenu.Create("encoding", 0, True) return elif x >=self.mucous.w-10 and x < self.mucous.w-1 and self.current != None: self.Close(self.current) except Exception, e: self.mucous.Help.Log("debug", "BrowseShares.MouseBrowse: " +str(e) ) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousSetup.py0000644000175000017500000012003410662543102022726 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import threading import os import curses.wrapper ## Setup and view mucous and museekd config options # class Setup: ## Constructor # @param self Setup (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var windows # dict containing instances of curses windows self.windows = {"border":{}, "option":{}} ## @var input # determines how to parse input enter in Setup self.input = "default" ## @var current # Currently shown settings self.current = "mucous" ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## @var modes # Setup modes self.modes = ["mucous", "museek", "shares", "userinfo", "logs"] ## @var password # display password self.password = False self.numberboxes = ["cycletime", "scrolltime", "roomsize", "slots"] self.switchorder = [] ## Reset input to 'default' and redraw setup # @param self Setup (class) def Default(self): try: self.input = "default" self.Mode() self.mucous.ModeTopbar() except Exception, e: self.mucous.Help.Log("debug", "Setup.Default: "+ str(e)) ## Create window, display contents # @param self is Setup (Class) def Mode(self): self.mucous.UseAnotherEntryBox() self.mucous.mode = "setup" self.mucous.PopupMenu.show = False try: # Cleanup stale windows if "setup" in self.windows["border"]: del self.windows["border"]["setup"] w = self.dimensions["setup"] = {"height": self.mucous.h-5, "width": self.mucous.w, "top": 1, "left": 0} mw = self.windows["border"]["setup"] = curses.newwin(w["height"], w["width"], w["top"], w["left"]) mw.erase() mw.border() mw.addstr(w["height"]-1, 2, "< Use the mouse, if possible. Otherwise: /help setup >") if self.current == "mucous": #self.mucous.SetEditTitle("Mucous Setup") try: mw.addstr(0, 1, "< Mucous >", curses.A_BOLD) mw.addstr(0, 15, "< Museek >") mw.addstr(0, 30, "< Shares >") mw.addstr(0, 45, "< Userinfo >") mw.addstr(0, 60, "< Logs >") except: pass elif self.current == "museek": try: mw.addstr(0, 1, "< Mucous >") mw.addstr(0, 15, "< Museek >", curses.A_BOLD) mw.addstr(0, 30, "< Shares >") mw.addstr(0, 45, "< Userinfo >") mw.addstr(0, 60, "< Logs >") except: pass elif self.current == "shares": try: mw.addstr(0, 1, "< Mucous >") mw.addstr(0, 15, "< Museek >") mw.addstr(0, 30, "< Shares >", curses.A_BOLD) mw.addstr(0, 45, "< Userinfo >") mw.addstr(0, 60, "< Logs >") except: pass elif self.current == "userinfo": try: mw.addstr(0, 1, "< Mucous >") mw.addstr(0, 15, "< Museek >") mw.addstr(0, 30, "< Shares >") mw.addstr(0, 45, "< Userinfo >", curses.A_BOLD) mw.addstr(0, 60, "< Logs >") except: pass elif self.current == "logs": try: mw.addstr(0, 1, "< Mucous >") mw.addstr(0, 15, "< Museek >") mw.addstr(0, 30, "< Shares >") mw.addstr(0, 45, "< Userinfo >") mw.addstr(0, 60, "< Logs >", curses.A_BOLD) except: pass self.InputTitle() mw.noutrefresh() # Create buttons, settings and immediately Delete them to aviod leaks if self.current in ("shares"): self.DrawShares() elif self.current in ("userinfo"): self.DrawUserinfo() elif self.current in ("logs"): self.DrawLogs() elif self.current == "mucous": self.DrawMucous() elif self.current == "museek": self.DrawMuseek() except Exception, e: self.mucous.Help.Log("debug", "Setup.Mode: " + str(e) ) self.mucous.HotKeyBar() curses.doupdate() ## Draw Mucous settings # @param self is Setup (Class) def DrawMucous(self): w = self.dimensions["setup"] mw = self.windows["border"]["setup"] self.switchorder = ["default", "interface", "interface-password", "showtickers", "tickertype", "cycletime", "scrolltime", "autoclear", "autoretry", "autobuddy", "beep", "roomsize", "encoding", "url-custom", "url-reader", "save"] # Interface interface ="" if "connection" in self.mucous.Config: interface = self.mucous.dlang( self.mucous.Config["connection"]["interface"] ) self.SetupButton("Museek Interface", interface ,2,1, 3,32, edit=(self.input=="interface")) if "connection" in self.mucous.Config: password = self.mucous.dlang( str(self.mucous.Config["connection"]["passw"]) ) if self.password == False and not (self.input=="interface-password"): value = '*' * len(password) else: value = password else: value = "" self.SetupButton("Interface Password", value, 5,1,3,32, edit=(self.input=="interface-password")) self.SetupCheck("Show Tickers: /showtickers", self.mucous.Config["tickers"]["tickers_enabled"] ,2,34,1,45, True, selected=(self.input=="showtickers")) self.SetupCheck("Cycle Tickers: /tickers", self.mucous.Config["tickers"]["ticker_cycle"] ,3,34,1,45, True, True, selected=(self.input=="tickertype")) self.SetupCheck("Scroll Tickers: /tickers", self.mucous.Config["tickers"]["ticker_scroll"] ,4,34,1,45, True, True, selected=(self.input=="tickertype")) self.SetupCheck("Auto-clear Transfers: /autoclear", self.mucous.Config["mucous"]["auto-clear"],9,1,1,45, True, selected=(self.input=="autoclear")) self.SetupCheck("Auto-retry Transfers: /autoretry", self.mucous.Config["mucous"]["auto-retry"],10,1, 1,45, True, selected=(self.input=="autoretry")) self.SetupCheck("Auto-Buddy downloads: /autobuddy", self.mucous.Config["mucous"]["autobuddy"],11,1,1,45, True, selected=(self.input=="autobuddy")) self.SetupCheck("Beep: /beep", self.mucous.Config["mucous"]["beep"],12,1,1,45, True, selected=(self.input=="beep")) self.SetupButton("< Ticker Time >", " - +\n - +", 5,39, 4,17, optionbold=(self.input in ("scrolltime", "cycletime")) , titlepad=False) self.EditLine("%.2f" % float(self.mucous.Config["tickers"]["cycletime"]), 6,43, 1,5, edit=(self.input=="cycletime") ) self.EditLine("%.2f" % float(self.mucous.Config["tickers"]["scrolltime"]), 7,43, 1,5, edit=(self.input=="scrolltime") ) # Minimum Room size for Roomlist self.SetupButton("", " - +", 9,39, 3,17, optionbold=(self.input=="roomsize"), titlepad=False) self.EditLine(str(self.mucous.Config["mucous"]["roomlistminsize"]), 10,43, 1,8, edit=(self.input=="roomsize") ) self.SetupButton("Encoding", self.mucous.Config["mucous"]["language"], 12,39,3,17, optionbold=(self.input=="encoding")) # Custom URL prefix ="" if "url custom" in self.mucous.Config["mucous"]: prefix = self.mucous.dlang(self.mucous.Config["mucous"]["url custom"]) self.SetupButton("Custom URL Reader", prefix,15,1,3,38, edit=(self.input=="url-custom")) self.SetupButton("URL Reader", self.mucous.dlang(self.mucous.Config["mucous"]["url reader"]), 15,39,3,17, optionbold=(self.input=="url-reader")) # Save button #self.SetupButton(None, " Save Config", 15,49,3,16, optionbold=(self.input=="save")) self.SetupButton(None, " Save Config", self.mucous.h-8,self.mucous.w-17,3,16, optionbold=(self.input=="save")) ## Draw Museek settings # @param self is Setup (Class) def DrawMuseek(self): w = self.dimensions["setup"] mw = self.windows["border"]["setup"] self.switchorder = ["default", "server-host", "server-port", "soulseek-username", "soulseek-password", "museek-interface-password", "connectmode", "only_buddies", "privilege_buddies", "have_buddy_shares", "trusting_uploads", "user_warnings", "slots", "download-dir", "incomplete-dir", "save"] # Server self.SetupButton("Server", "Host:\nPort:\nName:\nPass:", 2,1,6,38, optionbold=(self.input in("server-host", "server-port", "soulseek-username", "soulseek-password"))) if "server" in self.mucous.config: value = self.mucous.dlang( self.mucous.config["server"]["host"]) else: value = "" self.EditLine(value, 3,8,1,30, edit=(self.input=="server-host") ) if "server" in self.mucous.config: value = self.mucous.dlang( self.mucous.config["server"]["port"]) else: value = "" self.EditLine(value, 4,8,1,30, edit=(self.input=="server-port") ) if "server" in self.mucous.config: value = self.mucous.dlang(self.mucous.config["server"]["username"]) else: value = "" self.EditLine(value, 5,8,1,30, edit=(self.input=="soulseek-username") ) if "server" in self.mucous.config: value = self.mucous.dlang(self.mucous.config["server"]["password"]) if self.password == False and not (self.input=="soulseek-password") : value = "*" * len(value) else: value = "" self.EditLine(value, 6,8,1,30, edit=(self.input=="soulseek-password") ) if "interfaces" in self.mucous.config: if self.password == True or self.input == "museek-interface-password": value = self.mucous.dlang( self.mucous.config["interfaces"]["password"] ) else: value = "*" * len(self.mucous.config["interfaces"]["password"]) else: value = "" self.SetupButton("< Interface Password >", value ,2,39,3,26, False, False, edit=(self.input == "museek-interface-password") ) #------------------------ value = "" if self.mucous.config.has_key("transfers"): value = self.mucous.config["transfers"]["only_buddies"] self.SetupCheck("Share to Buddies-Only", value, 8,1,1,30, True, selected=(self.input=="only_buddies")) if self.mucous.config.has_key("transfers"): value = self.mucous.config["transfers"]["privilege_buddies"] else: value = "" self.SetupCheck("Buddies get Privileges", value, 9,1,1,30, True, selected=(self.input=="privilege_buddies")) if self.mucous.config.has_key("transfers"): value = self.mucous.config["transfers"]["have_buddy_shares"] else: value = "" self.SetupCheck("Enable Buddy-Only shares", value, 10,1,1,30, True, selected=(self.input=="have_buddy_shares")) if self.mucous.config.has_key("transfers"): value = self.mucous.config["transfers"]["trusting_uploads"] else: value = "" self.SetupCheck("Allow Trusted users to send you files", value, 11,1,1,self.mucous.w-3, True, selected=(self.input=="trusting_uploads")) if self.mucous.config.has_key("transfers"): value = self.mucous.config["transfers"]["user_warnings"] else: value = "" self.SetupCheck("Send automatic warnings via Private Chat", value, 12,1,1,self.mucous.w-3, True, selected=(self.input=="user_warnings")) if self.mucous.config.has_key("clients"): value = self.mucous.config["clients"]["connectmode"] else: value = "" self.SetupButton("", value ,5,49,3,16, optionbold=(self.input=="connectmode"), titlepad=False) self.SetupButton("", " - +", 8,49,3,16, optionbold=(self.input=="slots"), titlepad=False) if self.mucous.config.has_key("transfers"): value = str(self.mucous.config["transfers"]["upload_slots"]) else: value = 0 self.EditLine(value, 9,53, 1,8, edit=(self.input=="slots") ) dirwin = curses.newwin(4,self.mucous.w-2,13,1) dirwin.border() dirwin.addstr(0, 1, "< Download/Incomplete Directories >", curses.A_BOLD) dirwin.noutrefresh() if self.mucous.config.has_key("transfers"): value = self.mucous.dlang(self.mucous.config["transfers"]["download-dir"]) if (self.input=="download-dir"): value = "/" + value else: value = "" self.EditLine(value, 14,2,1,self.mucous.w-4, edit=(self.input=="download-dir") ) if self.mucous.config.has_key("transfers"): value = self.mucous.dlang(self.mucous.config["transfers"]["incomplete-dir"]) if (self.input=="incomplete-dir"): value = "/" + value else: value = "" self.EditLine(value, 15,2,1,self.mucous.w-4, edit=(self.input=="incomplete-dir") ) del dirwin ## Draw Shares config options # @param self is Setup (Class) def DrawShares(self): w = self.dimensions["setup"] mw = self.windows["border"]["setup"] self.switchorder = ["default", "listnormal", "adddir", "rmdir", "rescannormal", "updatenormal", "listbuddy", "addbuddydir", "rmbuddydir", "rescanbuddy", "updatebuddy", "reloadshares"] # First Row self.SetupButton("Normal", "List Shared", 3,1,3,16, optionbold=(self.input=="listnormal")) self.SetupButton(None, "Add Directory", 3,17,3,16, optionbold=(self.input=="adddir")) self.SetupButton(None, "Remove Dir", 3,33,3,16, optionbold=(self.input=="rmdir")) self.SetupButton(None, "Click on the buttons to run the local muscan.\nWhen you click on Add/Remove Directory, type in the directory below, and start with '//' instead of just a '/'", 2,49,10,self.mucous.w-50) # Second Row self.SetupButton(None, "Rescan Shares",6,1,3,16, optionbold=(self.input=="rescannormal")) self.SetupButton(None, "Update Shares", 6,17,3,16, optionbold=(self.input=="updatenormal")) # Third Row self.SetupButton("Buddy-only", "List Shared", 9,1,3,16, optionbold=(self.input=="listbuddy")) self.SetupButton(None, "Add Directory", 9,17,3,16, optionbold=(self.input=="addbuddydir")) self.SetupButton(None, "Remove Dir", 9,33,3,16, optionbold=(self.input=="rmbuddydir")) # Fourth Row self.SetupButton(None, "Rescan Shares", 12,1,3,16, optionbold=(self.input=="rescanbuddy")) self.SetupButton(None, "Update Shares", 12,17,3,16, optionbold=(self.input=="updatebuddy")) self.SetupButton(None, "Reload Shares", 12,49,3,16, optionbold=(self.input=="reloadshares")) museekconfigfile = self.mucous.Spl["museekconfigfile"] # Fifth Row self.SetupButton("Museek Config File", museekconfigfile, 15,1,3,self.mucous.w-2) ## Draw Log file options # @param self is Setup (Class) def DrawLogs(self): w = self.dimensions["setup"] mw = self.windows["border"]["setup"] self.switchorder = ["default", "logging", "logdir", "save"] self.SetupCheck("Log Chat messages? ", self.mucous.Config["mucous"]["logging"],2,1,1,30, True, selected=(self.input=="logging")) value = os.path.expanduser(self.mucous.Config["mucous"]["log_dir"]) if self.input=="logdir": value = "/" + value self.SetupButton("Log Directory", value, 5,1,3,self.mucous.w-2, edit=(self.input=="logdir") ) self.SetupButton(None, " Save Config", self.mucous.h-8,self.mucous.w-17,3,16, optionbold=(self.input=="save")) ## Draw User Info # @param self is Setup (Class) def DrawUserinfo(self): w = self.dimensions["setup"] mw = self.windows["border"]["setup"] self.switchorder = ["default", "userinfo", "userimage"] info = "" if self.mucous.config.has_key("userinfo"): if "text" in self.mucous.config["userinfo"]: info = self.mucous.config["userinfo"]["text"] self.SetupButton("Your Userinfo", info, 2,1,self.mucous.h-8,self.mucous.w-18, edit=(self.input == "userinfo")) bwin = curses.newwin(8,16,2,self.mucous.w-17) bwin.border() try: bwin.addstr(0, 1, "< Stats >", curses.A_BOLD) except: pass bwin.noutrefresh() statswin = bwin.subwin(6,14,3,self.mucous.w-16) statswin.scrollok(1) if self.mucous.data["mystats"] != []: try: statswin.addstr("Files: %s\nDirs: %s\nSpeed: %sKB/s\nDownloads: %s" % (str(self.mucous.data["mystats"][3]), str(self.mucous.data["mystats"][4]), str(self.mucous.data["mystats"][1]/1024), str(self.mucous.data["mystats"][2])), self.mucous.colors["cyan"] ) #self.data["mystats"] = user, avgspeed, numdownloads, numfiles, numdirs except: pass else: if self.mucous.username != None: self.mucous.D.PeerStats(self.mucous.username) statswin.noutrefresh() del statswin del bwin inputimage = curses.newwin(1,13,self.mucous.h-6,1) inputimage.erase() inputimage.addstr( "Your Image: ") inputimage.noutrefresh() del inputimage if self.mucous.config.has_key("userinfo") and self.mucous.config["userinfo"].has_key("image"): if (self.input=="userimage"): value = "/" + self.mucous.config["userinfo"]["image"] else: value = self.mucous.config["userinfo"]["image"] self.EditLine(value, self.mucous.h-6,14,1,self.mucous.w-2-14, edit=(self.input=="userimage") ) ## Update input line's title # @param self is Setup (Class) def InputTitle(self): si = self.input inputmodes = ["default","interface", "custom-url", "interface-password", "museek-interface-password", "museek-interface-bind", "server-host","server-port", "soulseek-username", "soulseek-password", "slots", "download-dir", "incomplete-dir", "userinfo", "userimage", "adddir", "rmdir", "addbuddydir", "rmbuddydir"] if si == "default": self.mucous.SetEditTitle("Setup Mode") elif si == "interface": self.mucous.SetEditTitle("Set Interface") elif si == "interface-password": self.mucous.SetEditTitle("Set Mucous's Interface password") elif si == "url-reader": self.mucous.SetEditTitle("Set a program to open HTTP URLs") elif si == "url-custom": self.mucous.SetEditTitle("Set custom URL handler: command$command") elif si == "save": self.mucous.SetEditTitle("Save Mucous's Config") elif si == "museek-interface-password": self.mucous.SetEditTitle("Set Museek's Interface password") elif si == "museek-interface-bind": self.mucous.SetEditTitle("Add a Museek Interface") elif si == "server-host": self.mucous.SetEditTitle("Set Server Address") elif si == "server-port": self.mucous.SetEditTitle("Set Server Port") elif si == "soulseek-username": self.mucous.SetEditTitle("Set Soulseek Username") elif si == "soulseek-password": self.mucous.SetEditTitle("Set Soulseek Password") elif si == "slots": self.mucous.SetEditTitle("Set Number of Upload Slots to:") elif si == "download-dir": self.mucous.SetEditTitle("Set completed download directory") elif si == "incomplete-dir": self.mucous.SetEditTitle("Set incompleted download directory") elif si == "userinfo": self.mucous.SetEditTitle("Set Your UserInfo") elif si == "userimage": self.mucous.SetEditTitle("Set Your UserImage") elif si == "adddir": self.mucous.SetEditTitle("Add directory to your normal shares") elif si == "rmdir": self.mucous.SetEditTitle("Remove directory from your normal shares") elif si == "logdir": self.mucous.SetEditTitle("Set the directory Chat logs are saved in") elif si == "logging": self.mucous.SetEditTitle("Toggle Logging") elif si == "addbuddydir": self.mucous.SetEditTitle("Add directory to your buddy shares") elif si == "rmbuddydir": self.mucous.SetEditTitle("Remove directory from your buddy shares") else: self.mucous.SetEditTitle("Setup Mode") ## Parse input entry line for interest and match it with Setup.input # @param self is Setup (Class) # @param line is a text string def InputSetup(self, line): try: inputs = ["default", "interface", "interface-password", "showtickers", "tickertype", "autoclear", "autoretry", "autobuddy", "beep", "cycletime", "scrolltime", "encoding", "roomsize", "url-custom", "url-reader", "save", "server-host", "server-port", "soulseek-username", "soulseek-password", "museek-interface-password", "connectmode", "only_buddies", "privilege_buddies", "have_buddy_shares", "trusting_uploads", "user_warnings", "slots", "download-dir", "incomplete-dir", "listnormal", "adddir", "rmdir", "rescannormal", "updatenormal", "listbuddy", "addbuddydir", "rmbuddydir", "rescanbuddy", "updatebuddy", "reloadshares", "logging", "logdir", "userinfo", "userimage"] if self.input not in inputs: self.mucous.SetEditTitle(self.input) return line = self.mucous.dlang(line) # Mucoous if self.input == "interface": self.mucous.Config["connection"]["interface"] = line elif self.input=="interface-password": self.mucous.Config["connection"]["passw"] = line elif self.input=="showtickers": self.mucous.ChatRooms.ToggleTickersDisplay() elif self.input=="tickertype": self.mucous.ChatRooms.ToggleTickers() elif self.input=="autoclear": self.ToggleAutoClear() return elif self.input=="autoretry": self.ToggleAutoRetry() return elif self.input=="autobuddy": self.ToggleAutoBuddy() return elif self.input=="beep": self.mucous.ToggleBeep() self.Mode() return elif self.input=="cycletime": #if line.isdigit(): try:self.mucous.Config["tickers"]["cycletime"] = str(float(line)) except:pass elif self.input=="scrolltime": try:self.mucous.Config["tickers"]["scrolltime"] = str(float(line)) except:pass elif self.input=="encoding": self.mucous.Config["mucous"]["language"]=self.mucous.FormatData.RotateList("right", self.mucous.encodings, self.mucous.Config["mucous"]["language"], "no") self.Mode() return elif self.input=="roomsize": if line.isdigit(): self.mucous.Config["mucous"]["roomlistminsize"] = int(line) elif self.input=="url-custom": if "$" in line: self.mucous.Config["mucous"]["url custom"] = line elif self.input=="url-reader": _list = ["lynx", "links", "elinks", "firefox", "custom"] self.mucous.Config["mucous"]["url reader"] = self.mucous.FormatData.RotateList("right", _list, self.mucous.Config["mucous"]["url reader"], "no") self.Mode() return elif self.input=="save": self.mucous.config_manager.update_config() # Museek elif self.input=="museek-interface-password": self.mucous.D.ConfigSet("interfaces", "password", line) elif self.input=="museek-interface-bind": self.mucous.D.ConfigSet("interfaces.bind", line) elif self.input=="connectmode": self.ToggleConnectMode() return elif self.input=="server-host": self.mucous.D.ConfigSet("server", "host", line) elif self.input=="server-port": self.mucous.D.ConfigSet("server", "port", line) elif self.input=="soulseek-username": self.mucous.D.ConfigSet("server", "username", line) elif self.input=="soulseek-password": self.mucous.D.ConfigSet("server", "password", line) elif self.input == "only_buddies": self.ToggleOnlyBuddies() elif self.input == "privilege_buddies": self.TogglePrivilegeBuddies() elif self.input == "have_buddy_shares": self.ToggleHaveBuddyShares() elif self.input == "trusting_uploads": self.ToggleTrustedUploads() elif self.input == "user_warnings": self.ToggleUserWarnings() elif self.input == "slots": if line.isdigit(): self.mucous.D.ConfigSet("transfers", "upload_slots", line) elif self.input=="download-dir": self.mucous.D.ConfigSet("transfers", "download-dir", line) elif self.input=="incomplete-dir": self.mucous.D.ConfigSet("transfers", "incomplete-dir", line) # Shares elif self.input=="listnormal": self.mucous.Muscan.ListNormal() elif self.input=="rescannormal": self.mucous.Muscan.RescanNormal() elif self.input=="updatenormal": self.mucous.Muscan.UpdateNormal() elif self.input=="listbuddy": self.mucous.Muscan.ListBuddy() elif self.input=="rescanbuddy": self.mucous.Muscan.RescanBuddy() elif self.input=="updatebuddy": self.mucous.Muscan.UpdateBuddy() elif self.input=="reloadshares": self.mucous.D.ReloadShares() elif self.input=="adddir": if line == "": return self.mucous.Muscan.Command(["muscan", "-s", line]) self.mucous.Help.Log("status", "Adding "+line+" to normal shares. Scanning will begin.") elif self.input=="rmdir": if line == "": return self.mucous.Muscan.Command(["muscan", "-u", line]) self.mucous.Help.Log("status", "Removing "+line+" from normal shares. Please rescan or update.") elif self.input=="addbuddydir": if line == "": return self.mucous.Muscan.Command(["muscan", "-b", "-s", line]) self.mucous.Help.Log("status", "Adding "+line+" to buddy shares. Scanning will begin.") elif self.input=="rmbuddydir": if line == "": return self.mucous.Muscan.Command(["muscan", "-b", "-u", line]) self.mucous.Help.Log("status", "Removing "+line+" from buddy shares. Please rescan or update.") # Userinfo elif self.input=="userinfo": try: line = line.replace('\\n', '\n') self.mucous.D.ConfigSet("userinfo", "text", line) except Exception, e: self.mucous.Help.Log("debug", "set userinfo: "+str( e)) elif self.input == "userimage": try: self.mucous.D.ConfigSet("userinfo", "image", line) except Exception, e: self.mucous.Help.Log("debug", "set userinfo image: "+str( e)) # Logging elif self.input == "logdir": self.mucous.Config["mucous"]["log_dir"] = line self.Mode() elif self.input == "logging": self.mucous.ToggleLogging() self.Mode() return if self.input != "default": self.SetInput() #self.input = "default" #self.Mode() except Exception,e: self.mucous.Help.Log("debug", "InputSetup: " + str(e)) ## Draw Mucous settings # @param self is Setup (Class) def Switch(self, key): if key == "KEY_DOWN": self.input = self.mucous.FormatData.RotateList("right", self.switchorder, self.input, "no") elif key == "KEY_UP": self.input = self.mucous.FormatData.RotateList("left", self.switchorder, self.input, "no") else: return self.Mode() def ChangeSize(self, direction): #self.numberboxes = ["cycletime", "scrolltime", "roomsize", "slots"] if self.input == "cycletime": difference = 0.5 value = float(self.mucous.Config["tickers"]["cycletime"]) if direction == "+": value += difference elif direction == "-": value -= difference if float(value) < 1.0: value = str(1.0) self.mucous.Config["tickers"]["cycletime"] = value elif self.input == "scrolltime": difference = 0.1 value = float(self.mucous.Config["tickers"]["scrolltime"]) if direction == "+": value += difference elif direction == "-": value -= difference if float(value) < 0.1: value = str(0.1) self.mucous.Config["tickers"]["scrolltime"] = value elif self.input == "roomsize": difference = 1 value = int(self.mucous.Config["mucous"]["roomlistminsize"]) if direction == "+": value += difference elif direction == "-": value -= difference if value < 1: value = 1 self.mucous.Config["mucous"]["roomlistminsize"] = value elif self.input == "slots": difference = 1 value = int(self.mucous.config["transfers"]["upload_slots"]) if direction == "+": value += difference elif direction == "-": value -= difference if value < 0: value = 0 value = str(value) self.mucous.D.ConfigSet("transfers", "upload_slots", value) self.Mode() ## Toggle Museekd's Connection Method # @param self is Setup (Class) def ToggleConnectMode(self): if "clients" in self.mucous.config: if self.mucous.config["clients"]["connectmode"] == "passive": self.mucous.D.ConfigSet("clients", "connectmode", "active") elif self.mucous.config["clients"]["connectmode"] == "active": self.mucous.D.ConfigSet("clients", "connectmode", "passive") self.Mode() def ToggleOnlyBuddies(self): if self.mucous.config["transfers"]["only_buddies"] == "true": self.mucous.D.ConfigSet("transfers", "only_buddies", "false") elif self.mucous.config["transfers"]["only_buddies"] == "false": self.mucous.D.ConfigSet("transfers", "only_buddies", "true") def ToggleHaveBuddyShares(self): if self.mucous.config["transfers"]["have_buddy_shares"] == "true": self.mucous.D.ConfigSet("transfers", "have_buddy_shares", "false") elif self.mucous.config["transfers"]["have_buddy_shares"] == "false": self.mucous.D.ConfigSet("transfers", "have_buddy_shares", "true") def TogglePrivilegeBuddies(self): if self.mucous.config["transfers"]["privilege_buddies"] == "true": self.mucous.D.ConfigSet("transfers", "privilege_buddies", "false") elif self.mucous.config["transfers"]["privilege_buddies"] == "false": self.mucous.D.ConfigSet("transfers", "privilege_buddies", "true") def ToggleTrustedUploads(self): if self.mucous.config["transfers"]["trusting_uploads"]== "true": self.mucous.D.ConfigSet("transfers", "trusting_uploads", "false") elif self.mucous.config["transfers"]["trusting_uploads"] == "false": self.mucous.D.ConfigSet("transfers", "trusting_uploads", "true") def ToggleUserWarnings(self): if self.mucous.config["transfers"]["user_warnings"] == "true": self.mucous.D.ConfigSet("transfers", "user_warnings", "false") elif self.mucous.config["transfers"]["user_warnings"] == "false": self.mucous.D.ConfigSet("transfers", "user_warnings", "true") def ToggleAutoClear(self): if self.mucous.Config["mucous"]["auto-clear"] == "yes": self.mucous.Config["mucous"]["auto-clear"] = "no" self.mucous.timers["clear"].cancel() self.mucous.timers["clear"] = threading.Timer(30.0, self.mucous.ThreadTransfersClear) else: self.mucous.Config["mucous"]["auto-clear"] ="yes" self.mucous.timers["clear"].cancel() self.mucous.timers["clear"] = threading.Timer(30.0, self.mucous.ThreadTransfersClear) self.mucous.timers["clear"].start() self.Mode() def ToggleAutoBuddy(self): if self.mucous.Config["mucous"]["autobuddy"] == "yes": self.mucous.Config["mucous"]["autobuddy"] = "no" elif self.mucous.Config["mucous"]["autobuddy"] == "no": self.mucous.Config["mucous"]["autobuddy"] = "yes" self.Mode() def ToggleAutoRetry(self): if str(self.mucous.Config["mucous"]["auto-retry"]) == "yes": self.mucous.Config["mucous"]["auto-retry"] = "no" self.mucous.timers["retry"].cancel() else: self.mucous.Config["mucous"]["auto-retry"] ="yes" self.mucous.timers["retry"].cancel() self.mucous.timers["retry"] = threading.Timer(30.0, self.mucous.ThreadTransfersRetry) self.mucous.timers["retry"].start() self.Mode() ## Create a Button # @param self Setup (class) # @param option # @param x # @param y # @param height # @param width # @param edit If False, delete references to curses windows; If True, use as input window def EditLine(self, option, x, y, height, width, edit=False): try: window = curses.newwin(height,width,x,y) if edit: window.attron(self.mucous.colors["green"] | curses.A_BOLD) color = self.mucous.colors["green"] | curses.A_BOLD else: color = self.mucous.colors["cyan"] #inputimage2 = curses.newwin(1, self.mucous.w-2-14, self.mucous.h-6, 14) window.erase() window.bkgdset("_") window.scrollok(0) try: window.addstr( option[:width] , color) except Exception,e: pass #self.mucous.Help.Log("debug", "EditLine: "+str(e)) window.noutrefresh() if edit: self.mucous.UseAnotherEntryBox(window, height, width, x, y, option, wrap=False) else: del window except Exception,e: self.mucous.Help.Log("debug", "EditLine: "+str(e)) ## Create a Button # @param self Setup (class) # @param title # @param option # @param x # @param y # @param height # @param width # @param optionbold True/False # @param titlepad True/False # @param border Draw a border around button # @param edit If False, delete references to curses windows; If True, use as input window def SetupButton(self, title, option, x, y, height, width, optionbold=False, titlepad=True, border=True, edit=False): try: winborder = curses.newwin(height,width,x,y) if edit or optionbold: winborder.attron(self.mucous.colors["green"]) color = self.mucous.colors["green"] | curses.A_BOLD else: color = curses.A_BOLD winborder.erase() if border: winborder.border() if title != None: if titlepad: winborder.addstr(0,1, "< %s >" % title, color) else: winborder.addstr(0,1, title, color) winborder.noutrefresh() win = curses.newwin(height-2,width-2,x+1,y+1) if edit: win.attron(self.mucous.colors["green"] | curses.A_BOLD) win.scrollok(1) if option != None: if optionbold: win.addstr(option, self.mucous.colors["cyan"] | curses.A_BOLD) else: win.addstr(option, self.mucous.colors["cyan"]) win.noutrefresh() if edit: #self.mucous.UseAnotherEntryBox(win, height-2, width-2, x+1, y+1, option, wrap=(height-2>1)) self.mucous.UseAnotherEntryBox(win, height-2, width-2, x+1, y+1, option, wrap=True) else: del win del winborder except Exception,e: self.mucous.Help.Log("debug", "SetupButton: "+title+" "+ str(e)) ## Create a Check box or a toggle button # @param self Setup (class) # @param title # @param option # @param x # @param y # @param height # @param width # @param titlebold True/False # @param toggle True/False (is a togglebutton) # @param selected def SetupCheck(self, title, option, x, y, height, width, titlebold=False, toggle=False, selected=False, underlined=False): try: checked = "[x]" unchecked = "[ ]" toggled = "(*)" nottoggled = "( )" win = curses.newwin(height,width,x,y) if selected: if underlined: attr = self.mucous.colors["green"] | curses.A_UNDERLINE else: attr = self.mucous.colors["green"] else: attr = curses.A_NORMAL win.erase() if option in (True, "True", "true", "yes"): if toggle: z = toggled else: z = checked else: if toggle: z = nottoggled else: z = unchecked win.addstr(z+" ", self.mucous.colors["cyan"]) if title != None: if titlebold: win.addstr(title, attr | curses.A_BOLD) else: win.addstr(title, attr) win.noutrefresh() del win except Exception,e: self.mucous.Help.Log("debug", "SetupButton: "+title+" "+ str(e)) ## Mouse Coordinates in the Setup Mode # @param self is Setup (Class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def Mouse(self, x,y,z,event): try: if y == 1: if x >=1 and x <=12: if self.current != "mucous": self.current="mucous" self.input = "default" elif x >=16 and x <=26: if self.current != "museek": self.current = "museek" self.input = "default" elif x >=31 and x <=41: if self.current != "shares": self.current="shares" self.input = "default" elif x >=46 and x <=57: if self.current != "userinfo": self.current="userinfo" self.input = "default" elif x >=60 and x <=69: if self.current != "logs": self.current="logs" self.input = "default" self.SetInput() return if self.current=="mucous": if y in (2, 3, 4) and x >=1 and x <=35: self.SetInput("interface") elif y in (5, 6, 7) and x >=1 and x <=35: self.SetInput("interface-password") elif y == 2 and x >=39 and x <=75: self.input="showtickers" self.mucous.ChatRooms.ToggleTickersDisplay() elif y in (3, 4) and x >=39 and x <=75: self.input="tickertype" self.mucous.ChatRooms.ToggleTickers() elif y in (5, 6, 7, 8) and x >= 39 and x <= 55: # Minimum size of rooms displayed in room list if y in (5, 6): self.input="cycletime" if x >=40 and x <=43: self.ChangeSize("-") elif x >=49 and x <=55: self.ChangeSize("+") else: self.Mode() elif y in (7, 8): self.input="scrolltime" if x >=40 and x <=43: self.ChangeSize("-") elif x >=49 and x <=55: self.ChangeSize("+") else: self.Mode() elif y == 9 and x >=1 and x <=38: self.input="autoclear" self.ToggleAutoClear() elif y in (9, 10, 11) and x >= 39 and x <= 55: # Minimum size of rooms displayed in room list self.input="roomsize" if x >=39 and x <43: self.ChangeSize("-") elif x >=51 and x <=55: self.ChangeSize("+") else: self.Mode() elif y == 10 and x >=1 and x <=38: self.input="autoretry" self.ToggleAutoRetry() elif y ==11 and x >=1 and x <=38: # Toggle Autobuddy self.input="autobuddy" self.ToggleAutoBuddy() elif y == 12 and x >=1 and x <=38: self.input="beep" self.mucous.ToggleBeep() elif y in (12, 13, 14) and x >=39 and x <=55: # Change charset, encoding, language that text is piped thru if "language" in self.mucous.Config["mucous"]: self.input="encoding" self.mucous.Config["mucous"]["language"]=self.mucous.FormatData.RotateList("right", self.mucous.encodings, self.mucous.Config["mucous"]["language"], "no") self.Mode() elif y in (15, 16, 17) and x >= 1 and x <= 55: if x >=1 and x <=38: # Edit custom URL handler process self.SetInput("url-custom") elif x >=39 and x <=55: self.input="url-reader" # Cycle thru list of URL handlers _list = ["lynx", "links", "elinks", "firefox", "custom"] self.mucous.Config["mucous"]["url reader"] = self.mucous.FormatData.RotateList("right", _list, self.mucous.Config["mucous"]["url reader"], "no") self.Mode() else: self.SetInput() elif y in (self.mucous.h-8, self.mucous.h-7, self.mucous.h-6): if x >= self.mucous.w-17: #self.mucous.h-8,self.mucous.w-17 # Save self.input="save" self.mucous.config_manager.update_config() self.Mode() else: self.SetInput() else: self.SetInput() elif self.current=="museek" and "transfers" in self.mucous.config: if y in (2, 3) and x >=1 and x <=35: self.SetInput("server-host") elif y == 4 and x >=1 and x <=35: self.SetInput("server-port") elif y == 5 and x >=1 and x <=35: self.SetInput("soulseek-username") elif y == 6 and x >=1 and x <=35: self.SetInput("soulseek-password") elif y in (2, 3, 4) and x >=40 and x <=63: self.SetInput("museek-interface-password") elif y in (5,6,7) and x >=50 and x <=63: self.input="connectmode" self.ToggleConnectMode() return elif y==8 and x >=1 and x <=30: self.input="only_buddies" self.ToggleOnlyBuddies() elif y==9 and x >=1 and x <=30: self.input="privilege_buddies" self.TogglePrivilegeBuddies() elif y==10 and x >=1 and x <=30: self.input="have_buddy_shares" self.ToggleHaveBuddyShares() elif y==11 and x >=1 and x <=50: self.input="trusting_uploads" self.ToggleTrustedUploads() elif y==12 and x >=1 and x <=50: self.input="user_warnings" self.ToggleUserWarnings() elif y in (8,9,10) and x >=49 and x <=64: self.input="slots" if x >=49 and x <=53: self.ChangeSize("-") elif x >=60 and x <=64: self.ChangeSize("+") else: self.Mode() elif y in (13, 14): # Download Directory self.SetInput("download-dir") elif y in (15, 16): # Incomplete Download Directory self.SetInput("incomplete-dir") else: self.SetInput() elif self.current=="shares": if y in ( 3, 4, 5): if x >= 1 and x <= 16: self.input="listnormal" self.mucous.Muscan.ListNormal() elif x >= 17 and x <= 32: self.input="adddir" elif x >= 33 and x < 49: self.input="rmdir" elif y in ( 6, 7, 8): if x >= 1 and x <= 16: self.input="rescannormal" self.mucous.Muscan.RescanNormal() elif x >= 17 and x <= 32: self.input="updatenormal" self.mucous.Muscan.UpdateNormal() elif y in ( 9, 10, 11): if x >= 1 and x <= 16: self.input="listbuddy" self.mucous.Muscan.ListBuddy() elif x >= 17 and x <= 32: self.input="addbuddydir" elif x >= 33 and x < 49: self.input="rmbuddydir" elif y in ( 12, 13, 14): if x >= 1 and x <= 16: self.input="rescanbuddy" self.mucous.Muscan.RescanBuddy() elif x >= 17 and x <= 32: self.input="updatebuddy" self.mucous.Muscan.UpdateBuddy() elif x >= 49 and x <= 66: self.input="reloadshares" self.mucous.D.ReloadShares() else: self.SetInput() return self.Mode() elif self.current=="userinfo": if y > 1 and y < self.mucous.h-7 and x < self.mucous.w-17: self.SetInput("userinfo") elif y <= self.mucous.h-5 and y >= self.mucous.h-7: self.SetInput("userimage") else: self.SetInput() elif self.current=="logs": if y >= 2 and y <= 3 and x < 37: self.input="logging" self.mucous.ToggleLogging() self.Mode() elif y >= 5 and y <= 7 : self.SetInput("logdir") elif y in (self.mucous.h-8, self.mucous.h-7, self.mucous.h-6): if x >= self.mucous.w-17: # Save self.input="save" self.mucous.config_manager.update_config() self.Mode() else: self.SetInput() else: self.SetInput() except Exception, e: self.mucous.Help.Log("debug", "Setup.Mouse: " +str(e) ) def SetInput(self, input=None): if input == None: self.input = "default" self.mucous.UseAnotherEntryBox() else: self.input = input self.Mode() #self.InputTitle() museek+-0.2+svn20100315.r1208/mucous/pymucous/__init__.py0000644000175000017500000000007410435676725022212 0ustar gandalfgandalf# Copyright (c) 2006 daelstorm. All rights reserved. # pass museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousUserInfo.py0000644000175000017500000002373610662543102023373 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper ## UserInfo text and user statistics # Tabbed buttons display along the top # Left window contains userinfo description # Right window contains statistics class UserInfo: ## Constructor # @param self UserInfo (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var requests # Users whose info we've requested (Discard other requests) self.requests = [] ## @var current # Currently shown user's info self.current = None ## @var users # Users whose info we have self.users = [] ## @var logs # dict containing descriptions and stats of users self.logs = {} ## @var windows # dict containing instances of curses windows self.windows = {} ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## @var scrolling # dict of users contain vertical position in their user info self.scrolling = {} ## @var scrolling_status # int of scroll position in instructions self.scrolling_status = 0 ## Request userinfo from a user # @param self UserInfo (class) # @param user the Username def Get(self, user): if user not in self.requests: self.requests.append(user) self.mucous.D.UserInfo(user) ## Recieved userinfo # If a picture is included, save it to disk as username.image # @param self UserInfo (class) # @param user the Username # @param info the description # @param picture the image, if it exists # @param uploads number of uploads slots # @param queue Length of queue # @param slotsfree has free slots? def Recieved(self, user, info, picture, uploads, queue, slotsfree): if user not in self.requests: return self.requests.remove(user) self.scrolling[user] = 0 message = info.replace('\r', "").split('\n') pic = False if picture != '': import imghdr format = imghdr.what(None, picture) r = file("%s.%s" % (self.mucous.config_dir+str(user), format), 'wb') r.write(picture) r.close() self.StatsLog( "Saved UserImage as: %s.%s" % (str(user), format)) pic = True self.Log(user, message, uploads, queue, slotsfree, pic) if self.mucous.mode != "info": #self.mucous.Alerts.setStatus("New UserInfo") #self.mucous.Alerts.alert["INFO"].append(user) self.mucous.Alerts.Add(user, "INFO") self.mucous.HotKeyBar() ## Draw windows # @param self UserInfo (class) def Mode(self): try: self.mucous.mode = "info" self.mucous.UseAnotherEntryBox() self.mucous.PopupMenu.show = False # Cleanup stale windows if "text" in self.windows: del self.windows["text"] if "border" in self.windows: del self.windows["border"] if "infostats" in self.windows: del self.windows["infostats"] if "statsborder" in self.windows: del self.windows["statsborder"] w = self.dimensions["info"] = {"height": self.mucous.h-10, "width": self.mucous.w-20, "top": 5, "left": 1} mw = self.windows["border"] = curses.newwin(w["height"]+2, w["width"]+2, w["top"]-1, w["left"]-1) mw.attron(self.mucous.colors["green"]) mw.border() mw.attroff(self.mucous.colors["green"]) try: mw.addstr(0, 3, "< Info Mode >", self.mucous.colors["green"] | curses.A_BOLD) except: pass tw = self.windows["info"] = mw.subwin(w["height"], w["width"], w["top"], w["left"]) tw.scrollok(0) tw.idlok(1) #self.scrolling = -1 self.mucous.SetEditTitle("Get info about user:") sw = self.dimensions["infostats"]= {"height": self.mucous.h-10, "width": 16, "top": 5, "left": self.mucous.w-17} isw = self.windows["statsborder"] = curses.newwin(sw["height"]+2, sw["width"]+2, sw["top"]-1, sw["left"]-1) isw.border() isw.addstr(0, 2, "< Stats >") isw.noutrefresh() itw = self.windows["infostats"] = isw.subwin(sw["height"], sw["width"], sw["top"], sw["left"]) itw.scrollok(1) mw.noutrefresh() self.DrawText() itw.noutrefresh() # queue, uploads, speed, downloads, files, directories, freeslots self.mucous.DrawTabs(self.users, self.current) self.DrawStats() self.mucous.Alerts.Check() curses.doupdate() #self.HotKeyBar() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.Mode: " + str(e)) ## Select another user # @param self UserInfo (class) # @param direction left/right list scrolling def Select(self, direction): try: if self.current == None: self.current = self.users[0] self.Mode() return place = self.mucous.FormatData.RotateList(direction, self.users, self.current, "yes" ) if self.current != place: self.current = place self.Mode() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.Select: " + str(e)) ## Draw text in text window # @param self UserInfo (class) def DrawText(self): try: scrolltext = "info" w = self.dimensions["info"] lang = self.mucous.Config["mucous"]["language"] tw = self.windows["info"] if self.current != None: self.mucous.DrawInstructionsButtons() # Display UserInfo & Stats clipped_list, self.scrolling[self.current], w["start"] = self.mucous.FormatData.wrap_n_clip(self.logs[self.current][0], self.scrolling[self.current], w) else: # Display instructions, IP info, and stats clipped_list, self.scrolling_status, w["start"] = self.mucous.FormatData.wrap_n_clip( self.mucous.Help.log["userinfo"], self.scrolling_status, w) attrs = curses.A_BOLD; attr = curses.A_NORMAL count = 0 tw.erase() if self.current == None: scroll = self.scrolling_status else: scroll = self.scrolling[self.current] for lines in clipped_list: try: lines, ls = self.mucous.FormatData.StringAddBlanks(lines, w) if count + w["start"] == scroll: tw.addstr(self.mucous.dlang(lines), attrs) else: tw.addstr(self.mucous.dlang(lines), attr) count += 1 except Exception, e: pass tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.DrawText: " + str(e)) ## Draw statistics in info stats windo # @param self UserInfo (class) def DrawStats(self): try: itw = self.windows["infostats"] itw.erase() if self.current != None and self.mucous.mode=="info": userinfo = self.logs[self.current] if int(userinfo[1][2]): slots = "Yes" else: slots = "No" if userinfo[1][3]: image = "Yes" else: image = "No" itw.addstr('Slots: %s' % str(userinfo[1][0]) + \ '\nQueue: %s' % str(userinfo[1][1]) + \ '\nFree: %s' % slots +\ '\nImage: %s' % image) if self.current in self.mucous.user["statistics"].keys(): try: stats = self.mucous.user["statistics"][self.current] itw.addstr('\nSpeed: %.2fKB' % (stats[0]/1024.0)) itw.addstr('\nDown: %s' % str(stats[1])) itw.addstr('\nFiles: %s' % str(stats[2])) itw.addstr('\nDirs: %s' % str(stats[3])) except: pass itw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.DrawStats: " + str(e)) ## Mouse Coordinates in the User Info Mode # @param self is UserInfo (class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def Mouse(self, x,y,z,event): try: if y in (2, 3, 4): if len(self.users) >= 1: if self.current == None: self.current = self.users[0] self.current, match = self.mucous.edit.MouseClickTab(x, self.current) if match == None: s = self.users.index(self.current) self.current = self.users[s-1] #self.DrawStats() self.Mode() if self.current != None: if y in (5,6): if x >=self.mucous.w-19-2-16 and x < self.mucous.w-12: self.current=None self.Mode() elif y in ( self.mucous.h-3, self.mucous.h-4, self.mucous.h-5): if x >=self.mucous.w-10 and x < self.mucous.w-1: self.Close(self.current) except Exception, e: self.mucous.Help.Log("debug", "MouseUserInfo: " +str(e) ) ## Close userinfo # @param self UserInfo (class) # @param user the Username def Close(self, user): try: if user in self.users: self.users.remove(user) if self.users != []: for users in self.users: self.current = users break else: self.current = None if user in self.mucous.Alerts.alert["INFO"]: self.mucous.Alerts.alert["INFO"].remove(user) if self.mucous.mode == 'info': self.Mode() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.Close: " + str(e)) ## Append stats to text log # @param self UserInfo (class) # @param s string def StatsLog(self, s): try: s= self.mucous.dlang(s) if "\n" in s: lis = s.split("\n") for line in lis: self.mucous.Help.log["userinfo"].append("%s" % line) else: self.mucous.Help.log["userinfo"].append("%s" % s) if self.mucous.mode == "info" and self.current == None: self.DrawText() except Exception, e: self.mucous.Help.Log("debug", "StatsLog: " + str(e)) ## Store UserInfo, stats in Log # @param self UserInfo (class) # @param user username # @param description user's info/description # @param user username # @param uploads number of uploads slots # @param queue Length of queue # @param slotsfree has free slots (True/False) # @param pic has a picture (True/False) def Log(self, user, description, uploads, queue, slotsfree, pic): try: if self.current == None: self.current = user if user not in self.logs: self.logs[user] = [] if user not in self.users: self.users.append(user) self.logs[user] = description, [uploads, queue, slotsfree, pic] if user not in self.mucous.user["statistics"].keys(): self.mucous.D.PeerStats(user) if self.mucous.mode == 'info': self.Mode() except Exception, e: self.mucous.Help.Log("debug", "UserInfo.Log: " + str(e)) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousSearch.py0000644000175000017500000006676410560770347023070 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper import re ## Search for files # class Search: ## Constructor # @param self Search (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var logs # dict of tickets with lists of results self.logs = {} ## @var windows # dict containing instances of curses windows self.windows = {} ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## @var sorted_search # list of sorted results self.sorted_search = None ## @var sfilter # Display results that match this case-insensitive string self.sfilter = None ## @var current # Current ticket self.current = None ## @var tickets # key is the stringified ticket number; value is the string query self.tickets = { } ## @var results # results [ ticket ] [ num ] [ str(ticket), user, free, speed, queue, result[0], result[1], ftype, result[3] ] self.results = { } ## @var numresults # dict of tickets with number of search results recieved self.numresults = { } ## @var viewing # list of search results self.viewing = [] ## @var scrolling # vertical scroll position self.scrolling = 0 ## @var order # Sorting order (default is by number) :: num, user, free, speed, que, path, sizefile, bitrate, time self.order = "num" ## @var reverse # Reverse sorting self.reverse = False ## @var method # Searching method (Global by default) self.method = "globally" ## @var username # User to search shares of self.username = None ## @var input # Input text entry point self.input = "default" ## @var help # Search commands self.help = ["Search commands:"] + self.mucous.Help.log["search"] + ["Or, type in the query, below."] self.switchorder = ["default", "methods", "user", "sort", "reverse", "results", "filter", "tabs"] ## Reset input to 'default' and redraw search # @param self Search (class) def Default(self): try: self.input = "default" self.Mode() self.mucous.ModeTopbar() except Exception, e: self.mucous.Help.Log("debug", "Search.Default: "+ str(e)) ## Create Search window # @param self Search (class) def Mode(self): try: self.mucous.mode = "search" self.mucous.UseAnotherEntryBox() self.mucous.PopupMenu.show = False # Cleanup stale windows if "query" in self.windows: del self.windows["query"] if "border" in self.windows: del self.windows["border"] if "stats" in self.windows: del self.windows["stats"] if "options" in self.windows: del self.windows["options"] w = self.dimensions = {"height": self.mucous.h-12, "width": self.mucous.w-2, "top": 5, "left": 1, "start": 0} mw = self.windows["border"] = curses.newwin(w["height"]+2, w["width"]+2, w["top"]-1, w["left"]-1) if self.input == "results": color = self.mucous.colors["greenblack"] mw.attron(self.mucous.colors["green"]) mw.border() #mw.attroff(self.mucous.colors["green"]) else: mw.border() try: mw.addstr(0, 3, "< Search >") except: pass if self.sfilter != None: sfilter = "Filter: " +self.sfilter else: sfilter = "Filter: Disabled" lfil = len(sfilter) if self.input == "filter": mw.addstr(0,15, "< ") mw.addstr(0,17, "Filter: ", self.mucous.colors["green"] | curses.A_BOLD) self.mucous.Setup.EditLine(self.mucous.dlang(self.sfilter),4, 25,1,lfil, edit=(self.input=="filter") ) mw.addstr(0,25+lfil, " >") else: mw.addstr(0,15, "< ") if self.sfilter != None: mw.addstr(0,17, self.mucous.dlang(sfilter), self.mucous.colors["cyan"] | curses.A_BOLD) else: mw.addstr(0,17, self.mucous.dlang(sfilter), self.mucous.colors["red"] | curses.A_BOLD) mw.addstr(0,17+lfil, " >") self.SortBar() tw = self.windows["query"] = mw.subwin(w["height"], w["width"], w["top"], w["left"]) tw.scrollok(0) tw.idlok(1) tw.attroff(self.mucous.colors["green"]) self.windows["stats"] = curses.newwin(1, self.mucous.w, self.mucous.h-6, 0) self.windows["stats"].erase() self.windows["stats"].noutrefresh() self.windows["options"] = curses.newwin(1, self.mucous.w, self.mucous.h-5, 0) self.windows["options"].erase() self.windows["options"].noutrefresh() self.Draw() self.mucous.Setup.SetupCheck("Globally", (self.method=="globally"), self.mucous.h-5, 0,1,13, True, True, selected=(self.method=="globally"), underlined=(self.input=="methods")) self.mucous.Setup.SetupCheck("Buddies", (self.method=="buddies"), self.mucous.h-5, 13,1,13, True, True, selected=(self.method=="buddies"), underlined=(self.input=="methods")) self.mucous.Setup.SetupCheck("Rooms", (self.method=="rooms"), self.mucous.h-5, 25,1,10, True, True, selected=(self.method=="rooms"), underlined=(self.input=="methods")) self.mucous.Setup.SetupCheck("User:", (self.method=="user"), self.mucous.h-5, 48,1,10, True, True, selected=(self.method=="user"), underlined=(self.input=="methods")) self.mucous.Setup.SetupCheck("Wishlist", (self.method=="wishlist"), self.mucous.h-5, 35,1,13, True, True, selected=(self.method=="wishlist"), underlined=(self.input=="methods")) #self.mucous.Setup.SetupCheck(self.method.capitalize(), False,self.mucous.h-5, 0,1,15, True, True, selected=(self.method=="globally")) self.mucous.Setup.EditLine(self.username, self.mucous.h-5, 58,1,15, edit=(self.input=="user") ) if self.method != None: if self.method == "user": if self.username != None: title = "Search "+self.method.capitalize()+" "+self.username+"'s shares" else: title = "Search "+self.method.capitalize()+" (Pick a user with /searchuser)" else: title = "Search "+self.method.capitalize()+" for:" else: title = "" self.mucous.SetEditTitle(title, selected=(self.input=="default")) #self.mucous.DrawTabs(self.results.keys(), self.current) self.mucous.DrawTabs(self.tickets.keys(), self.current, selected=(self.input=="tabs")) if self.current != None: self.DrawInstructionsButtons() self.mucous.Alerts.Check() else: self.mucous.HotKeyBar() curses.doupdate() except Exception,e: self.mucous.Help.Log("debug", "Search.Mode: "+str(e)) ## Draw Buttons for switching with the mouse to Instructions view # @param self Search (class) def DrawInstructionsButtons(self): try: gi = "Instructions" w = self.dimensions pos = w["width"]-3-len(gi) mw = self.windows["border"] mw.addstr(0,pos, "< ") mw.addstr(0,pos+2, gi, self.mucous.colors["cyan"] | curses.A_BOLD) mw.addstr(0,pos+2+len(gi), " >") vertex = w["height"]+1 mw.addstr(vertex,self.mucous.w-11, "< ") mw.addstr(vertex,self.mucous.w-9, "Close ", self.mucous.colors["cyan"] | curses.A_BOLD) mw.addstr(vertex,self.mucous.w-3, ">") mw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "DrawInstructionsButtons: " + str(e)) def MethodSwitch(self, direction=None): # HotKeyBar to switch types of searches if direction == None: direction = "right" _list = [ "globally", "buddies", "rooms", "wishlist", "user" ] self.method = self.mucous.FormatData.RotateList(direction, _list, self.method, "no") #if self.method == "user": #self.input="user" #else: #self.input="default" self.Mode() ## Draw Mucous settings # @param self is Setup (Class) def Switch(self, direction=None): if direction == None: direction = "right" self.input = self.mucous.FormatData.RotateList(direction, self.switchorder, self.input, "no") self.Mode() ## New Search Ticket recieved # @param self Search (class) # @param query string searched for # @param ticket unique number associated with search def NewTicket(self, query, ticket): try: self.tickets[str(ticket)] = query if self.current == None: self.current = str(ticket) if self.mucous.mode == "search": self.Mode() self.Stats("status", "Started search for: %s" % query, str(ticket), 0) self.results[str(ticket)] = {} self.numresults[str(ticket)] = 0 except Exception,e: self.mucous.Help.Log("debug", "Search.NewTicket: "+str(e)) ## New Search Results recieved # @param self Search (class) # @param ticket unique number (used to organize results) # @param user username of user with results # @param free is there a free slot open? (True/False) # @param speed average speed of user # @param queue length of queue # @param results list of files [path, size, extension, list of attributes(bitrate, length, unused)] def NewResults(self, ticket, user, free, speed, queue, results): try: if str(ticket) not in self.tickets: return for result in results: result_list = [] # Create Result List for future use # clear it next interation # Count Search Result path, size, extension, attributes = result num = self.numresults[str(ticket)] num += 1 self.numresults[str(ticket)] = num # Send Num of Results to Search Window ftype = result[2] if ftype in ('', None): if result[0][-4:-3] == ".": ftype = result[0][-3:] ftype = ftype.upper() result_list = str(ticket), user, free, speed, queue, path, size, ftype, attributes self.results[str(ticket)][num] = result_list if self.mucous.mode != "search" or self.current != str(ticket): if str(ticket) not in self.mucous.Alerts.alert["SEARCH"]: self.mucous.Alerts.alert["SEARCH"].append( str(ticket) ) self.mucous.Alerts.Check() else: if self.current == str(ticket) and self.mucous.PopupMenu.show != True: self.FormatResults(str(ticket)) else: self.Count(num) self.mucous.DrawTabs(self.tickets.keys(), self.current) except Exception,e: self.mucous.Help.Log("debug", "Search.NewResults: "+str(e)) ## Get Download from number # @param self Search (class) # @param num number # @return user, path def GetDownloadFromNum(self, num): try: number = int(num) if not self.results[self.current].has_key(number): self.mucous.Help.Log("debug", "Search.GetDownloadFromNum: No such number") return None, None user = self.results[self.current][number][1] path = self.results[self.current][number][5] return user, path except Exception,e: self.mucous.Help.Log("debug", "Search.GetDownloadFromNum: "+str(e)) ## Download Search result # @param self Search (class) # @param user Username # @param path Path of search result # @param getdir If True, Download the directory if False, the file def DownloadSearch(self, user, path, getdir=False): try: if user == None or path == None: return if getdir: self.mucous.Transfers.FolderDownload(user, path) else: self.mucous.Transfers.RetryDownload(user, path) self.mucous.AutobuddyUser(user) except Exception, e: self.mucous.Help.Log("debug", "DownloadSearch: " + str(e)) ## Draw Sort Bar # @param self Search (class) def SortBar(self): try: w = self.dimensions ls = ("Num", "User", "Free", "Speed", "Que", "Path", "Size", "File", "Bitrate", "Time") mw = self.windows["border"] mw.addstr(w["height"]+1, 1, "< | | | | | | | | | >") pos = 0 for i in ls: if i == self.order.capitalize(): if self.input == "sort": attrib = self.mucous.colors["green"]| curses.A_BOLD | curses.A_UNDERLINE else: attrib = self.mucous.colors["green"]| curses.A_BOLD mw.addstr(w["height"]+1, 3+pos, self.order.capitalize(), attrib) else: mw.addstr(w["height"]+1, 3+pos, i, self.mucous.colors["red"] | curses.A_BOLD) pos += len(i) + 1 pos = 56 mw.addstr(w["height"]+1, pos, "< >") pos = 58 if self.reverse: rcolor = "green" else: rcolor = "red" if self.input == "reverse": attrib = self.mucous.colors[rcolor] | curses.A_BOLD | curses.A_UNDERLINE else: attrib = self.mucous.colors[rcolor] | curses.A_BOLD mw.addstr(w["height"]+1, pos, "Reverse", attrib) mw.noutrefresh() except Exception,e: self.mucous.Help.Log("debug", "Search.SortBar: "+str(e)) ## Draw Search Instructions/Help or call Search.FormatResults # @param self Search (class) def Draw(self): try: if self.current == None: tw = self.windows["query"] tw.erase() w = self.dimensions for lines in self.help: try: lines, ls = self.mucous.FormatData.StringAddBlanks(lines, w) tw.addstr(self.mucous.dlang(lines)) except Exception, e: #self.mucous.Help.Log("debug", e) pass tw.noutrefresh() else: try: if self.mucous.PopupMenu.show == True: raise Exception, "popup" self.FormatResults(self.current) except Exception, e: #self.mucous.Help.Log("debug", e) pass except Exception,e: self.mucous.Help.Log("debug", "Search.Draw: "+str(e)) ## Draw the number of Search results in the Search counter # @param self Search (class) # @param s string def Count(self, s): try: if "searchstatus" in self.mucous.windows["border"]: del self.mucous.windows["border"]["searchstatus"] ssw =self.mucous.windows["border"]["searchstatus"] = curses.newwin(1, 15, 0, 47) ssw.bkgdset(" ", self.mucous.colors["blackwhite"] | curses.A_REVERSE | curses.A_BOLD) if s != None: if self.mucous.logs["search_count"] == ["Results: ", s]: return self.mucous.logs["search_count"] = "Results: ", s try: ssw.erase() ssw.addstr(self.mucous.logs["search_count"][0], self.mucous.colors["blackwhite"] ) ssw.addstr(str(self.mucous.logs["search_count"][1]), self.mucous.colors["blackwhite"] ) ssw.refresh() except Exception, e: pass #self.mucous.Help.Log( "debug", "Search Status: " + str(e)) except Exception, e: self.mucous.Help.Log("debug", "Search.Count: " + str(e)) ## Clear all search data # @param self Search (class) def Clear(self): try: self.tickets = {} self.logs = {} self.numresults = {} self.results = {} self.current = None self.username = None self.Count(0) self.Mode() except Exception, e: self.mucous.Help.Log("debug", "Search.Clear: " + str(e)) ## Close Search tab # @param self Search (class) # @param ticket ticket to be closed def Close(self, ticket): try: ticket = str(ticket) if ticket in self.tickets.keys(): del self.tickets[ticket] if ticket in self.results.keys(): del self.results[ticket] if ticket in self.numresults.keys(): del self.numresults[ticket] if ticket in self.logs: del self.logs[ticket] if self.tickets.keys() != []: self.current = self.tickets.keys()[0] else: self.current = None if ticket in self.mucous.Alerts.alert["SEARCH"]: self.mucous.Alerts.alert["SEARCH"].remove(ticket) self.Mode() except Exception, e: self.mucous.Help.Log("debug", "Search.Close: " + str(e)) ## Format Results for this ticket to fit search window and send them to Search.Stats # @param self Search (class) # @param this_ticket ticket def FormatResults(self, this_ticket): if self.mucous.mode != "search": return try: tw = self.windows["query"] sorting_list = {} if str(this_ticket) not in self.results: self.Stats("status", 0, "Empty.....", 0 ) return for numbers, results in self.results[str(this_ticket)].items(): ticket, user, free, speed, queue, path, size, ftype, extended = results if this_ticket == ticket and self.current == ticket: if ftype.upper() in ('MP3', 'OGG'): if extended != []: bitrate = extended[0] time = extended[1] else: bitrate = 0 time = 0 else: bitrate = 0 time = 0 if time in ('', None): time = 0 if self.order == "num": sorting_list[numbers] = 0 elif self.order == "user": sorting_list[numbers] = user elif self.order == "free": sorting_list[numbers] = free elif self.order == "speed": sorting_list[numbers] = speed elif self.order == "que": sorting_list[numbers] = queue elif self.order == "path": sorting_list[numbers] = path elif self.order == "size": sorting_list[numbers] = size elif self.order == "file": sorting_list[numbers] = ftype elif self.order == "bitrate": sorting_list[numbers] = bitrate elif self.order == "time": sorting_list[numbers] = time #self.mucous.Help.Log("status",sorting_list) #self.mucous.Help.Log("status",sorting_list) slist = self.mucous.FormatData.sortbyvalue (sorting_list) # Filter search while browsing if self.sfilter != None: s = [] searchfilter = re.compile('.*' +str(self.sfilter) + '.*', re.DOTALL | re.I) for x,y in slist: if not self.results[str(this_ticket)].has_key(x): continue z = self.results[str(this_ticket)][x] for c in (z[1], z[5]) : if re.match(searchfilter, c): s.append(x) break self.sorted_search = s else: s = [] for x,y in slist: s.append(x) self.sorted_search = s if self.reverse == True: self.sorted_search.reverse() self.logs[str(this_ticket)] = [] if self.scrolling == -1: self.scrolling = len(self.sorted_search) clipped_list, self.scrolling, self.dimensions["start"] = self.mucous.FormatData.scrollbox(self.sorted_search, self.scrolling, self.dimensions["height"]) tw.erase() count = 0 self.viewing = clipped_list for number in clipped_list: #self.format_this_search(n) self.Stats("result", number, str( self.results[this_ticket][number][0] ), count ) count += 1 tw.noutrefresh() self.Count(self.numresults[self.current]) #self.mucous.DrawTabs(self.results.keys(), self.current) self.mucous.DrawTabs(self.tickets.keys(), self.current) ##self.mucous.Help.Log("debug", "Search.Start: " + str(clipped_list)) except Exception, e: self.mucous.Help.Log("debug", "Search.FormatResults: " + str(e)) ## Draw Result and Stats (if it's the current scroll position) # @param self Search (class) # @param typer Type of Result ( # @param result result number # @param ticket Ticket # @param count Clipped list position def Stats(self, typer, result, ticket, count): if str(ticket) not in self.logs: return try: if self.mucous.mode != "search": return if self.current != str(ticket): return tw = self.windows["query"] ss = self.windows["stats"] if typer == "status": ss.erase() ss.addstr(self.mucous.dlang(result), self.mucous.colors["cyan"]) ss.noutrefresh() elif typer == "result": number = result ticket, user, free, speed, queue, path, size, ftype, extended = self.results[ticket][number] size = self.mucous.FormatData.byte_format(size) if ftype.upper() in ('MP3', 'OGG'): if extended != []: bitrate = extended[0] length = int(extended[1]) minutes = length/60 seconds = str(length - (60 * minutes)) if len(seconds) < 2: seconds = '0' + seconds else: bitrate = '0' minutes = '0' seconds = '00' length = 0 else: bitrate = '0' minutes = '0' seconds = '00' length = 0 if free: free = 'Y' else: free = 'N' if count + self.dimensions["start"]== self.scrolling: attr = curses.A_REVERSE | curses.A_BOLD attrc = self.mucous.colors["cybg"]| curses.A_BOLD ss.erase() ss.addstr("F: ") atr = self.mucous.colors["cyan"] | curses.A_BOLD ss.addstr(free, atr ) ss.addstr(" | Q:") ss.addstr(str(queue), atr ) ss.addstr(" | ") ss.addstr(user[:15], atr ) ss.addstr(" | ") ss.addstr(str(speed/1024), atr ) ss.addstr("KB/s | Size: ") ss.addstr(str(size), atr ) if bitrate != '0' and length != 0: ss.addstr(" | ") ss.addstr(str(bitrate), atr ) ss.addstr("Kbps| Len: ") ss.addstr(str(minutes), atr ) ss.addstr(":") ss.addstr(str(seconds), atr ) ss.noutrefresh() else: attr = curses.A_NORMAL attrc = self.mucous.colors["cyan"] try: extra = '' sn = len(str(number)) sr = len(str(self.numresults[ticket])) if sn < sr : nub = (" " * (sr - sn)) + str(number) else: nub = str(number) f = path.split("\\") file = f[-1] directory = "\\".join(f[:-1]) ## Shrink directory and file to fit in line if len(nub)+2+len(path) >= self.mucous.w-2: pos = self.mucous.w-2-len(str(nub)+"| ")-len(directory)-len(file) if abs(pos) > len(directory): a = abs(pos) - len(directory) file = file[:-a] directory = '' else: directory = directory[-pos:] extra = " " * (self.mucous.w-len(str(nub))- len(directory)-len(file)-4) tw.addstr(nub, attrc) tw.addch(curses.ACS_VLINE, attr) tw.addstr(""+directory+"\\", attr) tw.addstr(file, attrc) if extra != "": tw.addstr(extra, attr) except Exception, e: pass except Exception, e: self.mucous.Help.Log("debug", "Search Log: " + str(e)) ## Parse input functions # @param self is Search (Class) # @param key is the character pressed def Input(self, key): if self.mucous.mode != "search": return elif key in ("popup"): if self.mucous.PopupMenu.show == True: self.mucous.PopupMenu.Clear() return if self.mucous.Search.current != "default__": self.mucous.PopupMenu.show = True self.mucous.PopupMenu.Create("search", 0) elif key == "switch": self.mucous.Search.Switch(direction="right") return elif key in ( "KEY_HOME", "KEY_END"): self.mucous.edit.ScrollText(key) return elif key in( "KEY_LEFT", chr(91), chr(60), "KEY_RIGHT", chr(93), chr(62), "KEY_IC"): if key in ("KEY_LEFT", chr(91), chr(60)): direction = "left" elif key in ("KEY_RIGHT", chr(93), chr(62), "KEY_IC"): direction = "right" if self.input in ("default", "tabs"): if len(self.mucous.Search.tickets.keys()) >= 1: place = self.mucous.FormatData.RotateList(direction, self.mucous.Search.tickets.keys(), self.mucous.Search.current, "yes" ) if self.mucous.Search.current != place: self.mucous.Search.current = place self.mucous.Search.Mode() elif self.input == "sort": place = self.mucous.FormatData.RotateList(direction, [ "num", "user", "free", "speed", "que", "path", "size", "file", "bitrate", "time"], self.mucous.Search.order, "no" ) if self.mucous.Search.order != place: self.mucous.Search.order = place self.mucous.Search.SortBar() if self.mucous.Search.current != None: self.mucous.Search.FormatResults(self.mucous.Search.current) curses.doupdate() elif self.input == "methods": self.MethodSwitch(direction) ## Parse input entry line for seaerch # @param self is Search (Class) # @param line is a text string def LineInput(self, line): if self.mucous.mode != "search": return if self.input == "default" and line != "": # Normal Search query = line if self.method == "globally": if len(query) > 2 and query != 'mp3': self.mucous.D.Search(0, query ) # Buddies Search elif self.method == "buddies": self.mucous.D.Search(1, query ) # Rooms Search elif self.method == "rooms": self.mucous.D.Search(2, query ) elif self.method == "user": if self.username != None: self.mucous.D.UserSearch(self.username, query ) elif self.method == "wishlist": self.mucous.D.WishListSearch(query ) elif self.input == "user": if line == "": line = None self.username = line elif self.input == "results": position = self.scrolling-self.dimensions["start"] if len(self.viewing)-1 >= position: number = self.viewing[position] user, path = self.GetDownloadFromNum(number) self.DownloadSearch(user, path) elif self.input == "reverse": if self.reverse == True: self.reverse = False elif self.reverse == False: self.reverse= True elif self.input == "filter": if line == "": line = None self.sfilter = line if self.input != "default": self.SetInput() def SetInput(self, input=None): if input == None: self.input = "default" self.mucous.UseAnotherEntryBox() else: self.input = input self.Mode() ## Mouse Coordinates in the Search Mode # @param self is Search (Class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def Mouse(self, x,y,z,event): try: w = self.dimensions change = 0 if y >= w["top"] and y < w["top"] + w["height"] and x >= w["left"] and x < w["left"] +w["width"]: if self.current != None: #self.mucous.Help.Log("debug", "%d:%d::%d" %(x,y,w["top"]) ) y1 = y- w["top"] if self.input != "results": self.input = "results" self.Mode() return if y1 + w["start"] in range(self.numresults[self.current]): self.scrolling = y1 + w["start"] self.Draw() if event in ( 4096, 16384): self.mucous.PopupMenu.Create("search", 0, True) return elif y in (1, 2, 3): if len(self.tickets.keys()) >= 1: if self.current == None: self.current = self.tickets.keys()[0] self.current, match = self.mucous.edit.MouseClickTab(x, self.current) if match == None: s = self.tickets.keys().index(self.current) self.current = self.tickets.keys()[s-1] self.input = "tabs" self.Mode() return #(*) Globally ( ) Buddies ( ) Rooms ( ) Wishlist ( ) User: elif y == self.mucous.h-5: if x < 59: # Toggle type of search oldmethod = self.method if x < 13: self.method = "globally" elif x < 25: self.method = "buddies" elif x < 35: self.method = "rooms" elif x < 50: self.method = "wishlist" elif x < 59: self.method = "user" if self.method != oldmethod: self.Mode() #self.MethodSwitch() elif x >= 59: self.input = "user" self.Mode() return elif y == self.mucous.h-7: #m< Num|User|Free|Speed|Que|Path|Size|File|Bitrate|Time >< Reverse > if x >= 2 and x <= 6 and self.order != "num": self.order = "num" change = 1 elif x >= 7 and x <= 11 and self.order != "user": self.order = "user" change = 1 elif x >= 12 and x <= 16 and self.order != "free": self.order = "free" change = 1 elif x >= 17 and x <= 22 and self.order != "speed": self.order = "speed" change = 1 elif x >= 23 and x <= 26 and self.order != "que": self.order = "que" change = 1 elif x >= 27 and x <= 31 and self.order != "path": self.order = "path" change = 1 elif x >= 33 and x <= 36 and self.order != "size": self.order = "size" change = 1 elif x >= 37 and x <= 41 and self.order != "file": self.order = "file" change = 1 elif x >= 42 and x <= 49 and self.order != "bitrate": self.order = "bitrate" change = 1 elif x >= 50 and x <= 54 and self.order != "time": self.order = "time" change = 1 elif x >= 56 and x <= self.mucous.w-10: if self.reverse == True: self.reverse = False elif self.reverse == False: self.reverse= True change = 1 elif x >=self.mucous.w-10 and x < self.mucous.w-1: self.Close(self.current) elif y == 4: if x > 15 and x < 36: self.input="filter" self.Mode() elif x >=self.mucous.w-18: if self.current != None: self.current=None change = 1 else: if self.input != "default": change = 1 if change == 1: self.input = "default" self.Mode() except Exception, e: self.mucous.Help.Log("debug", "Search.Mouse: " +str(e) ) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousPrivateChat.py0000644000175000017500000003271010662543102024043 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import os import curses.wrapper import time ## Private Chat tabs # class PrivateChat: ## Constructor # @param self PrivateChat (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var current # Currently shown user's private chat self.current = None ## @var users # Users whose info we have self.users = [] ## @var logs # dict of users with lists of old + new chat messages self.logs = {} ## @var windows # dict containing instances of curses windows self.windows = {} ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## @var scrolling # vertical scroll position self.scrolling = -1 self.switchorder = ["default", "chat", "tabs"] self.input = "default" ## Create window & tabs and draw them # @param self PrivateChat (class) def Mode(self): self.mucous.mode = "private" self.mucous.UseAnotherEntryBox() self.mucous.PopupMenu.show = False try: # Cleanup stale windows if "text" in self.windows: del self.windows["text"] w = self.dimensions = {"height": self.mucous.h-8, "width": self.mucous.w, "top": 4, "left": 0} tw = self.windows["text"] = curses.newwin(w["height"], w["width"], w["top"], w["left"]) tw.scrollok(0) tw.idlok(1) ibw = self.mucous.windows["inputborder"] self.scrolling = -1 self.Draw() if self.current != None: self.mucous.SetEditTitle("Send message to: " + self.current) try: blah = None if "encoding.users" in self.mucous.config: if self.current in self.mucous.config["encoding.users"]: blah = self.mucous.config["encoding.users"][self.current] else: blah = self.mucous.config["encoding"]["network"] if blah != None: ibw.addstr(0, self.mucous.w-17-len(blah)-4, "<" + (" " *( len(blah) +2) )+ ">") ibw.addstr(0, self.mucous.w-17-len(blah)-2, blah, self.mucous.colors["cyan"] | curses.A_BOLD) ibw.addstr(0, self.mucous.w-10, "< ") ibw.addstr(0, self.mucous.w-8, "Close ", self.mucous.colors["cyan"] | curses.A_BOLD) ibw.addstr(0, self.mucous.w-2, ">") except: pass ibw.noutrefresh() self.mucous.windows["input"].noutrefresh() self.mucous.Alerts.Check() else: self.mucous.SetEditTitle("Set a user to Private Message") self.mucous.HotKeyBar() if self.mucous.Alerts.log == "New PM" or self.mucous.Alerts.log[:5] =="PM: ": self.mucous.Alerts.setStatus("") pmusers = self.logs.keys() pmusers.sort(key=str.lower) self.mucous.DrawTabs(pmusers, self.current) except Exception, e: self.mucous.Help.Log("debug", "PrivateChat.Mode: " +str(e)) curses.doupdate() def ClearLog(self): if self.current is None: return self.logs[self.current] = [] ## Close a user's chat # @param self PrivateChat (class) # @param user username def Close(self, user): try: if user in self.logs.keys(): del self.logs[user] if self.logs.keys() != []: for users in self.logs.keys(): self.current = users break else: self.current = None if user in self.mucous.Alerts.alert["PRIVATE"]: self.mucous.Alert.alert["PRIVATE"].remove(user) if self.mucous.mode == 'private': self.Mode() except Exception, e: self.mucous.Help.Log("debug", "PrivateChat.Close: %s" % str(e)) ## Draw chat text # @param self PrivateChat (class) def Draw(self): try: scrolltext = "private" w = self.dimensions tw = self.windows["text"] tw.erase() if self.current == None: # Instructions for lines in self.mucous.Help.log["private"]: try: lines, ls = self.mucous.FormatData.StringAddBlanks(lines, w) #tw.addstr(self.mucous.dlang(lines)) tw.addstr(self.mucous.dlang(lines)) except Exception, e: self.mucous.Help.Log("debug", "private display: " + str(e)) tw.noutrefresh() return # Private chat log if self.current not in self.logs: tw.noutrefresh() return listcolors = {} wrapped_lines = [] merged_lines = [] for timestamp, user, message in self.logs[self.current]: message = message.replace("\t", " ") message = self.mucous.dlang(message) if "\\n" in message: message = message.split("\\n") else: message = [message] for line in message: if line is message[0]: # first line if line[:4] == "/me ": color = self.mucous.colors['green'] merged_lines = "%s * %s %s"% (timestamp, user, line[4:]) elif user == '': color = self.mucous.colors['cyan'] merged_lines = "%s %s"% (timestamp, line) else: color = curses.A_NORMAL merged_lines = "%s [%s] %s"% (timestamp, user, line) else: # Second and greater lines if message[0][:4] == "/me ": color = self.mucous.colors['green'] elif user == '': color = self.mucous.colors['cyan'] else: color = curses.A_NORMAL merged_lines = "- %s" % line list_of_strings = self.mucous.FormatData.StringCutWidth(merged_lines, w) for line in list_of_strings: wrapped_lines.append(line) listcolors[line] = color if len(self.logs[self.current]): del merged_lines del list_of_strings if self.scrolling == -1: self.scrolling = len(wrapped_lines) clipped_list, self.scrolling, w["start"] = self.mucous.FormatData.scrollbox(wrapped_lines, self.scrolling, w["height"]) attrs = curses.A_BOLD #| curses.A_UNDERLINE attr = curses.A_NORMAL count = 0 for lines in clipped_list: color = listcolors[lines] try: lines, ls = self.mucous.FormatData.StringAddBlanks(lines, w) if count + w["start"] == self.scrolling: tw.addstr(self.mucous.dlang(lines), attrs | color ) else: tw.addstr(self.mucous.dlang(lines), attr | color ) count += 1 except Exception, e: #self.mucous.Help.Log("debug", "private display: " + str(e)) pass tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "PrivateChat.Draw: " + str(e)) ## Recieved a Private Message # @param self PrivateChat (class) # @param direction 0 == incoming; 1 == outgoing # @param timestamp (we use our own) # @param user username # @param message text def Recieved(self,direction, timestamp, user, message): try: message = message.replace("\n", "\\n") ctcpversion = 0 if message == curses.ascii.ctrl("A")+"VERSION"+curses.ascii.ctrl("A"): message = "CTCP VERSION" ctcpversion = 1 if user not in self.logs.keys(): self.logs[user] = [] if self.mucous.Config["mucous"]["logging"] in ("yes"): self.ImportLogs(user) if self.mucous.Config["mucous"]["logging"] in ("yes"): if direction == 0: self.mucous.FileLog("private", time.strftime("%d %b %Y %H:%M:%S"), user, "["+user+"]\t"+ message ) elif direction == 1: self.mucous.FileLog("private", time.strftime("%d %b %Y %H:%M:%S"), user, "["+self.mucous.username+"]\t"+ message ) self.Log(direction, user, message) if ctcpversion == 1 and direction == 0: if self.mucous.Config["mucous"]["extra_requests"] == "Yes": self.Send(user, "Mucous %s" % self.mucous.Version) if self.current == None: self.current = user if self.mucous.mode != "private": self.mucous.Alerts.Add(user, "PRIVATE") #self.Alerts.setStatus("New PM") #if user not in self.Alerts.alert["PRIVATE"]: #self.Alerts.alert["PRIVATE"].append(user) self.mucous.HotKeyBar() self.mucous.Beep() elif self.mucous.mode == "private" and self.current != user: self.mucous.Alerts.Add(user, "PRIVATE") self.mucous.HotKeyBar() self.mucous.Beep() if self.mucous.mode == "private": if self.current == user: self.Mode() elif self.current != user and self.current != None: pmusers = self.logs.keys() pmusers.sort(key=str.lower) self.mucous.DrawTabs(pmusers, self.current) #self.mucous.Alerts.setStatus("PM: "+user) except Exception ,e: self.mucous.Help.Log("debug", "PrivateChat.Recieved: " + str(e)) ## Send Private Message # @param self PrivateChat (class) # @param user username # @param message text def SendEncoded(self, user, message): try: self.mucous.D.PrivateMessage(1, user, self.mucous.dencode_language(message)) except: pass ## Split messages with newlines before sending message # @param self PrivateChat (class) # @param user username # @param message string def Send(self, user, message): try: #Username is already utf-8ified lang = self.mucous.Config["mucous"]["language"] if '\\n' in message: splited = message.split('\\n') if len(splited) > 7: for i in range(8): self.SendEncoded(user, splited[i]) else: for i in range(len(splited)): self.SendEncoded(user, splited[i]) elif '\n' in message: splited = message.split('\n') if len(splited) > 5: for i in range(5): self.SendEncoded(user, splited[i]) else: for i in range(len(splited)): self.SendEncoded(user, splited[i]) else: self.SendEncoded(user, message) if message == curses.ascii.ctrl("A")+"VERSION"+curses.ascii.ctrl("A"): message = "CTCP VERSION" if self.mucous.Config["mucous"]["logging"] in ("yes"): self.mucous.FileLog("private", time.strftime("%d %b %Y %H:%M:%S"), user, "["+self.mucous.username+"]\t" +message ) #pmtype = "outgoing" self.Log(1, user, message) if self.mucous.Alerts.log == "New PM": self.mucous.Alerts.setStatus("") if self.current == None: self.current = user if self.mucous.mode == "private": self.Mode() elif self.current == user: if self.mucous.mode == "private": self.Mode() elif self.current != user and self.current != None: pmusers = self.logs.keys() pmusers.sort(key=str.lower) self.mucous.DrawTabs(pmusers, self.current) except Exception ,e: self.mucous.Help.Log("debug", "PrivateChat.Send: " + str(e)) ## Split messages with newlines before sending message # @param self PrivateChat (class) # @param user username def Start(self, user): try: self.current = user if user not in self.logs.keys(): self.logs[user] = [] if self.mucous.Config["mucous"]["logging"] in ("yes"): self.ImportLogs(user) if self.mucous.mode == 'private': self.Mode() except Exception, e: self.mucous.Help.Log("debug", "PrivateChat.Start: " + str(e)) ## Append Private Message to Chat Log # @param self PrivateChat (class) # @param direction 0 == incoming; 1 == outgoing # @param user username # @param message text def Log(self, direction, user, message): try: timestamp = time.strftime("%H:%M:%S") if user not in self.logs.keys(): self.logs[user]=[''] if message[:4] == "/me ": if direction: self.logs[user].append([timestamp, self.mucous.username, message]) else: self.logs[user].append([timestamp, user, message]) else: if direction: self.logs[user].append([timestamp, self.mucous.username, message]) else: self.logs[user].append([timestamp, user, message]) except Exception, e: self.mucous.Help.Log( "debug", "PrivateChat.Log: " + str(e)) ## Read old Logfiles and import them to the chat log # @param self PrivateChat (class) # @param username username def ImportLogs(self, username): try: # Read from Private Chat Logs if "\\" in username: username = username.replace("/", "\\") if os.path.exists(os.path.expanduser(self.mucous.Config["mucous"]["log_dir"])+"/private"+"/"+username): path = os.path.expanduser(self.mucous.Config["mucous"]["log_dir"])+"/private"+"/"+username f = open(path, "r") a = f.read() f.close() lines = a.split("\n" ) numlines = -30 if len(lines) <= abs(numlines): numlines = 0 for line in lines[numlines:]: if line == "": continue timex = line[12:20] user = line[22:] if line.find("\t") == -1: # old format user = user[:user.find("]")] message = line[21+len(user)+3:] else: # new format with Tab user = user[:user.find("\t")-1] message = line[line.find("\t")+1:] self.logs[username].append([timex, user, message]) self.logs[username].append([time.strftime("%H:%M:%S"), "", "------ Old Chat Above ------"]) except Exception,e: self.mucous.Help.Log("debug", "PrivateChat.ImportLogs: " +str( e) ) ## Mouse Coordinates in the Private Chat Mode # @param self is PrivateChat (class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def Mouse(self, x,y,z,event): try: if self.current == None: return if y in (2, 3, 4): if len(self.logs.keys()) > 1: pmusers = self.logs.keys() pmusers.sort(key=str.lower) self.current, match = self.mucous.edit.MouseClickTab(x, self.current) if match == None: s = pmusers.index(self.current) self.current = pmusers[s-1] self.Start(self.current) self.Mode() if y == self.mucous.h-3 or y == self.mucous.h-4: if x>= self.mucous.w-27 and x < self.mucous.w-18: self.mucous.PopupMenu.Create("encoding", 0, True) elif x >=self.mucous.w-10 and x < self.mucous.w-1: self.Close(self.current) except Exception, e: self.mucous.Help.Log("debug", "PrivateChat.Mouse: " +str(e) ) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousLists.py0000644000175000017500000004353010662543102022731 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper ## Users Lists # class UsersLists: ## Constructor # @param self UsersLists (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var logs # holds temporary data, until the next Mucous.config update self.logs = {"buddied": [], "banned": [], "ignored": [], "trusted": []} ## @var scrolling # dict containing scroll position for buddied, banned, ignored ad trusted lists self.scrolling = {"buddied": 0, "banned": 0, "ignored": 0, "trusted": 0} ## @var current # default list is buddied self.current = "buddied" ## @var windows # dict containing curses window instances self.windows = {"text":{}, "border":{} } ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## Pick List to display # @param self UsersLists (class) def ModeLists(self): self.mucous.mode = "lists" self.mucous.PopupMenu.show = False if self.current == "buddied": self.ModeBuddy() elif self.current == "banned": self.ModeBan() elif self.current == "ignored": self.ModeIgnore() elif self.current == "trusted": self.ModeTrust() elif self.current == "interests": self.mucous.Recommendations.ModeInterests() ## Display Trusted users list # @param self UsersLists (class) def ModeTrust(self): try: self.mucous.UseAnotherEntryBox() self.current = "trusted" #self.DestroyOldWindows() self.mucous.PopupMenu.show = False s = self.dimensions[self.current] = {"height": self.mucous.h-7, "top": 2, "left": 1, "width": self.mucous.w-2, "start": 0} self.ListTrust() self.DrawListsWindows() self.FormatLists(s, "trusted") self.mucous.HotKeyBar() curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ModeTrust: " + str(e)) ## Display Buddied users list # @param self UsersLists (class) def ModeBuddy(self): try: self.mucous.UseAnotherEntryBox() self.current = "buddied" self.mucous.PopupMenu.show = False s = self.dimensions[self.current] = {"height": self.mucous.h-7, "top": 2, "left": 1, "width": self.mucous.w-2, "start": 0} self.ListBuddy() self.DrawListsWindows() self.FormatLists(s, "buddied") self.mucous.HotKeyBar() curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ModeBuddy: " + str(e)) ## Display Banned users list # @param self UsersLists (class) def ModeBan(self): try: self.mucous.UseAnotherEntryBox() self.current = "banned" self.mucous.PopupMenu.show = False s = self.dimensions[self.current] = {"height": self.mucous.h-7, "top": 2, "left": 1, "width": self.mucous.w-2, "start": 0} self.DrawListsWindows() self.ListBan() self.FormatLists(s, "banned") self.mucous.HotKeyBar() curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ban_mode: " + str(e)) ## Display Ignored users list # @param self UsersLists (class) def ModeIgnore(self): try: self.mucous.UseAnotherEntryBox() self.current = "ignored" self.mucous.PopupMenu.show = False s = self.dimensions[self.current] = {"height": self.mucous.h-7, "top": 2, "left": 1, "width": self.mucous.w-2, "start": 0} self.DrawListsWindows() self.ListIgnore() self.FormatLists(s, "ignored") self.mucous.HotKeyBar() curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ModeIgnore: " + str(e)) ## Format lists to fit inside window dimensions # @param self UsersLists (class) # @param window Window dimensions dict # @param mode list name def FormatLists(self, window, mode): try: if self.logs[mode] != None and self.logs[mode] != []: clipped_list, self.scrolling[mode], self.dimensions[self.current]["start"] = self.mucous.FormatData.scrollbox(self.logs[mode], self.scrolling[mode], window["height"]) count = 0 try: self.windows["border"][self.current].addstr(self.mucous.h-6, self.mucous.w-18, "< "+str(len(self.logs[mode]))+" >", self.mucous.colors["green"] | curses.A_BOLD) self.windows["border"][self.current].noutrefresh() except: pass self.windows["text"][self.current].erase() for lines in clipped_list: self.DrawLists(lines, count, self.current) count += 1 self.windows["text"][self.current].noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "FormatLists: " + str(e)) ## Draw List Window borders # @param self UsersLists (class) def DrawListsWindows(self): try: # Cleanup stale windows if self.current in self.windows["text"]: del self.windows["text"][self.current] if self.current in self.windows["border"]: del self.windows["border"][self.current] s = self.dimensions[self.current] mw = self.windows["border"][self.current] = curses.newwin(s["height"]+2, s["width"]+2, s["top"]-1, s["left"]-1) mw.attron(self.mucous.colors["green"]) mw.border() mw.attroff(self.mucous.colors["green"]) if self.current =="buddied": mw.addstr(0, 3, "< Buddied >", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(0, 16, "< Banned >", self.mucous.colors["green"]) mw.addstr(0, 28, "< Ignored >", self.mucous.colors["green"]) mw.addstr(0, 40, "< Trusted >", self.mucous.colors["green"]) mw.addstr(0, 52, "< Interests >", self.mucous.colors["green"]) self.mucous.SetEditTitle("Add Buddy:") elif self.current =="banned": mw.addstr(0, 3, "< Buddied >", self.mucous.colors["green"] ) mw.addstr(0, 16, "< Banned >", self.mucous.colors["green"]| curses.A_BOLD) mw.addstr(0, 28, "< Ignored >", self.mucous.colors["green"]) mw.addstr(0, 40, "< Trusted >", self.mucous.colors["green"]) mw.addstr(0, 52, "< Interests >", self.mucous.colors["green"]) self.mucous.SetEditTitle("Ban User:") elif self.current =="ignored": mw.addstr(0, 3, "< Buddied >", self.mucous.colors["green"] ) mw.addstr(0, 16, "< Banned >", self.mucous.colors["green"]) mw.addstr(0, 28, "< Ignored >", self.mucous.colors["green"]| curses.A_BOLD) mw.addstr(0, 40, "< Trusted >", self.mucous.colors["green"]) mw.addstr(0, 52, "< Interests >", self.mucous.colors["green"]) self.mucous.SetEditTitle("Ignore User:") elif self.current =="trusted": mw.addstr(0, 3, "< Buddied >", self.mucous.colors["green"] ) mw.addstr(0, 16, "< Banned >", self.mucous.colors["green"]) mw.addstr(0, 28, "< Ignored >", self.mucous.colors["green"]) mw.addstr(0, 40, "< Trusted >", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(0, 52, "< Interests >", self.mucous.colors["green"]) self.mucous.SetEditTitle("Add Trusted:") mw.noutrefresh() tw = self.windows["text"][self.current] = mw.subwin(s["height"], s["width"], s["top"], s["left"]) tw.scrollok(0) tw.idlok(1) except Exception, e: self.mucous.Help.Log("debug", "DrawLists: " + str(e)) ## Draw List Contents # @param self UsersLists (class) # @param line list of [attributes, username, note] # @param count number in list (Add to window["start"] to get the current line) # @param window window name (buddied, banned, ignored, trusted) def DrawLists(self, line, count, window): try: start = self.dimensions[window]["start"] tw = self.windows["text"][self.current] # attributes can contain ['trusted', 'banned', 'ignored', and 'buddied'] attributes, username, note = line tabbeduser = self.mucous.dlang(username[:20]) while len(tabbeduser) < 24: tabbeduser += ' ' try: if username in self.mucous.user["status"].keys(): if self.mucous.user["status"][username] == 1: tw.addstr('* ', self.mucous.colors["yellow"]|curses.A_BOLD) elif self.mucous.user["status"][username] == 2: tw.addstr('* ', self.mucous.colors["green"]|curses.A_BOLD) elif self.mucous.user["status"][username] == 0: tw.addstr('* ', self.mucous.colors["red"]|curses.A_BOLD) else: tw.addstr('* ', curses.A_BOLD ) pos = 2 if self.current == "buddied": attrcount = 0 try: if 'trusted' in attributes: color = self.mucous.colors["cyan"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') color = self.mucous.colors["green"] | curses.A_BOLD tw.addstr('^', color) if 'banned' in attributes: color = self.mucous.colors["red"] | curses.A_BOLD tw.addstr('v', color) else: tw.addstr(' ') if 'ignored' in attributes: color = self.mucous.colors["yellow"] | curses.A_BOLD tw.addstr('v ', color) else: tw.addstr(' ') except Exception, e: self.mucous.Help.Log("debug", "display list text" + str(e)) pass elif self.current == "banned": try: if 'trusted' in attributes: color = self.mucous.colors["cyan"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') if 'buddies' in attributes: color = self.mucous.colors["green"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') color = self.mucous.colors["red"] | curses.A_BOLD tw.addstr('v', color) if 'ignored' in attributes: color = self.mucous.colors["yellow"] | curses.A_BOLD tw.addstr('v ', color) else: tw.addstr(' ') except: self.mucous.Help.Log("debug", "display list text" + str(e)) elif self.current == "trusted": try: color = self.mucous.colors["cyan"] | curses.A_BOLD tw.addstr('^', color) if 'buddies' in attributes: color = self.mucous.colors["green"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') if 'banned' in attributes: color = self.mucous.colors["red"] | curses.A_BOLD tw.addstr('v', color) else: tw.addstr(' ') if 'ignored' in attributes: color = self.mucous.colors["yellow"] | curses.A_BOLD tw.addstr('v ', color) else: tw.addstr(' ') except Exception, e: self.mucous.Help.Log("debug", "display list text" + str(e)) elif self.current == "ignored": try: if 'trusted' in attributes: color = self.mucous.colors["cyan"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') if 'buddies' in attributes: color = self.mucous.colors["green"] | curses.A_BOLD tw.addstr('^', color) else: tw.addstr(' ') if 'banned' in attributes: color = self.mucous.colors["red"] | curses.A_BOLD tw.addstr('v', color) else: tw.addstr(' ') color = self.mucous.colors["yellow"] | curses.A_BOLD tw.addstr('v ', color) except Exception, e: self.mucous.Help.Log("debug", "display list text" + str(e)) #else: #tw.addstr(tabbeduser) #stats = note = '' color = curses.A_NORMAL pos +=5 if count + start == self.scrolling[self.current]: attrib = curses.A_BOLD | curses.A_REVERSE | color attrib2 = curses.A_BOLD | curses.A_REVERSE else: attrib = curses.A_BOLD | color attrib2 = curses.A_BOLD tw.addstr(tabbeduser, attrib) if username in self.mucous.user["statistics"]: stats = " %sKB/s" % str(self.mucous.user["statistics"][username][0]/1024) while len(stats) < 9: stats += " " files = str(self.mucous.user["statistics"][username][2]) while len(files) < 7: files = " " + files stats += files while len(stats) < 18: stats += " " tw.addstr( stats, attrib2) else: stats = " 0KB/s 0 " tw.addstr(stats, attrib2) width = len(tabbeduser) + len(stats) + len(note) + 5 subtract = self.mucous.w - width if subtract < 0: tw.addstr(note[:len(note)+subtract], attrib2) else: tw.addstr(note, attrib2) pos += len(tabbeduser) + len(stats) + len(note) if self.dimensions[window]["width"] - pos > 0: spaces = " " * (self.dimensions[window]["width"] - pos) tw.addstr(spaces, attrib2) except Exception, e: pass except Exception, e: self.mucous.Help.Log("debug", "DrawLists: " + str(e)) ## Mouse Coordinates in the Users Lists # @param self is mucous # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number # @return def Mouse(self, x,y,z,event): try: if self.current == "interests": w = self.mucous.Recommendations.dimensions[self.mucous.Recommendations.selected] else: w = self.dimensions[self.current] if y == 1: if x >= 4 and x <= 15: self.current="buddied" self.ModeLists() elif x >= 17 and x <= 27: self.current="banned" self.ModeLists() elif x >= 29 and x <= 40: self.current="ignored" self.ModeLists() elif x >= 42 and x <= 51: self.current="trusted" self.ModeLists() elif x >= 52 and x <= 64: self.current="interests" self.ModeLists() return if self.current == "interests": return self.mucous.Recommendations.MouseInterests(x,y,z,event) elif self.current in ("buddied", "banned", "ignored", "trusted"): # clicking on items in lists if y >= w["top"] and y < w["top"] + w["height"] and x >= w["left"] and x < w["left"] +w["width"]: y -= w["top"] this_list = self.logs[self.current] if y + w["start"] in range(len(this_list)): self.scrolling[self.current] = y + w["start"] if event in ( 4096, 16384): self.SelectLists() self.mucous.PopupMenu.Create("lists", 0, True) else: self.SelectLists() except Exception, e: self.mucous.Help.Log("debug", "UsersLists.Mouse: " +str(e) ) ## ReDraw the current List # @param self UsersLists (class) def SelectLists(self): try: this_list = self.logs[self.current] if self.scrolling[self.current] > len(this_list): self.scrolling[self.current] = len(this_list) clipped_list, self.scrolling[self.current], self.dimensions[self.current]["start"] = self.mucous.FormatData.scrollbox(this_list, self.scrolling[self.current], self.mucous.h-7) count = 0 self.windows["text"][self.current].erase() for lines in clipped_list: self.DrawLists(lines, count, self.current) count += 1 self.windows["text"][self.current].refresh() except Exception, e: self.mucous.Help.Log("debug", "SelectLists: " + str(e)) ## Rebuild Buddied List from Mucous.config # @param self UsersLists (class) def ListBuddy(self): try: if not self.mucous.config.has_key("buddies"): return self.logs["buddied"] = [] alpha_list = self.mucous.config["buddies"].keys() alpha_list.sort(key=str.lower) for user in alpha_list: note = self.mucous.config["buddies"][user] attributes = [] if self.mucous.config.has_key("trusted") and self.mucous.config["trusted"].has_key(user): attributes.append("trusted") if self.mucous.config.has_key("ignored") and self.mucous.config["ignored"].has_key(user): attributes.append("ignored") if self.mucous.config.has_key("banned") and self.mucous.config["banned"].has_key(user): attributes.append("banned") self.logs["buddied"].append([attributes, user, note]) except Exception, e: self.mucous.Help.Log("debug", "ListBuddy: " + str(e)) ## Rebuild Trusted List from Mucous.config # @param self UsersLists (class) def ListTrust(self): try: if not self.mucous.config.has_key("trusted"): return self.logs["trusted"] = [] alpha_list = self.mucous.config["trusted"].keys() alpha_list.sort(key=str.lower) for user in alpha_list: note = self.mucous.config["trusted"][user] attributes = [] if self.mucous.config.has_key("ignored") and self.mucous.config["ignored"].has_key(user): attributes.append("ignored") if self.mucous.config.has_key("banned") and self.mucous.config["banned"].has_key(user): attributes.append("banned") if self.mucous.config.has_key("buddies") and self.mucous.config["buddies"].has_key(user): attributes.append("buddies") self.logs["trusted"].append([attributes, user, note]) except Exception, e: self.mucous.Help.Log("debug", "ListTrust: " + str(e)) ## Rebuild ListBan from Mucous.config # @param self UsersLists (class) def ListBan(self): try: if not self.mucous.config.has_key("banned"): return self.logs["banned"] = [] alpha_list = self.mucous.config["banned"].keys() alpha_list.sort(key=str.lower) for user in alpha_list: note = self.mucous.config["banned"][user] attributes = [] if self.mucous.config.has_key("ignored") and self.mucous.config["ignored"].has_key(user): attributes.append("ignored") if self.mucous.config.has_key("buddies") and self.mucous.config["buddies"].has_key(user): attributes.append("buddies") if self.mucous.config.has_key("trusted") and self.mucous.config["trusted"].has_key(user): attributes.append("trusted") self.logs["banned"].append([attributes, user, note]) except Exception, e: self.mucous.Help.Log("debug", "ListBan: " + str(e)) ## Rebuild ListIgnore from Mucous.config # @param self UsersLists (class) def ListIgnore(self): try: if not self.mucous.config.has_key("ignored"): return self.logs["ignored"] = [] alpha_list = self.mucous.config["ignored"].keys() alpha_list.sort(key=str.lower) for user in alpha_list: note = self.mucous.config["ignored"][user] attributes = [] if self.mucous.config.has_key("banned") and self.mucous.config["banned"].has_key(user): attributes.append("banned") if self.mucous.config.has_key("buddies") and self.mucous.config["buddies"].has_key(user): attributes.append("buddies") if self.mucous.config.has_key("trusted") and self.mucous.config["trusted"].has_key(user): attributes.append("trusted") self.logs["ignored"].append([attributes, user, note]) except Exception, e: self.mucous.Help.Log("debug", "ListIgnore: " + str(e)) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousChatRooms.py0000644000175000017500000014065110662543102023534 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import threading import os import curses.wrapper import time ## Chat Rooms # class ChatRooms: ## Constructor # @param self ChatRooms (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var shape # Layout of windows self.shape = self.mucous.Config["mucous"]["roombox"] ## @var dimensions # Window placement self.dimensions = {} ## @var windows # Curses Window instances self.windows = {"text": {}, "border": {} } ## @var scrolling # dict containing vertical scroll position for chatroom, roombox and roomstatus self.scrolling = {"chatroom": -1, "roombox": 0, "roomstatus": -1 } ## @var current # current room self.current = None ## @var selected # selected window self.selected = "chatroom" ## @var logs # dict containing logs for chatroom, roombox and roomstatus self.logs = {"rooms": {},"roombox": {} , "roomstatus": {} } ## @var rooms # dict of users in rooms self.rooms = {} ## @var numticker # position of ticker self.numticker = 0 self.tickersize = 150 self.drawing_ticker = False ## @var tickers # dict of rooms containing lists of tickers self.tickers = {} if self.mucous.Config["tickers"]["ticker_cycle"] == "yes": time = float(self.mucous.Config["tickers"]["cycletime"]) elif self.mucous.Config["tickers"]["ticker_scroll"] == "yes": time = float(self.mucous.Config["tickers"]["scrolltime"]) ## @var ticker_timer # Timer instance for displaying tickers self.ticker_timer = threading.Timer(time, self.DrawTicker) self.linewrapped = None ## Create and draw current chat room's window and contents # Cleanup stale windows first # Calls: SetRoom # @param self ChatRooms (class) def Mode(self): try: self.mucous.mode = "chat" self.mucous.UseAnotherEntryBox() self.mucous.PopupMenu.show = False self.linewrapped = None # Arrangements: cs = None if "roomstatus" in self.windows["text"]: del self.windows["text"]["roomstatus"] if "roomstatus" in self.windows["border"]: del self.windows["border"]["roomstatus"] if "chat" in self.windows["text"]: del self.windows["text"]["chat"] if "chat" in self.windows["border"]: del self.windows["border"]["chat"] if self.shape == "big": w = self.dimensions["chat"] = {"height": self.mucous.h-13, "width": self.mucous.w-15, "top": 8, "left": 16} cs = self.dimensions["roomstatus"] = {"height": 4, "width": w["width"]-2, "top": 2, "left": w["left"]} elif self.shape == "widelist": w = self.dimensions["chat"] = {"height": self.mucous.h-13, "width": self.mucous.w-25, "top": 8, "left": 26} cs = self.dimensions["roomstatus"] ={"height": 4, "width": w["width"]-2, "top": 2, "left": w["left"]} elif self.shape in ("noroombox", "small"): w = self.dimensions["chat"] = {"height": self.mucous.h-13, "width": self.mucous.w, "top": 8, "left": 1} if self.shape in ("noroombox"): cs = self.dimensions["roomstatus"] = {"height": 4, "width": w["width"]-2, "top": 2, "left": 1} #cs["height"]-2, cs["width"]-1, cs["top"]+1,cs["left"]+1 elif self.shape in ("small"): cs = self.dimensions["roomstatus"] = {"height": 4, "width": w["width"]-15-2, "top": 2, "left": 16} elif self.shape == "rightlist": w = self.dimensions["chat"] = {"height": self.mucous.h-13, "width": self.mucous.w-15, "top": 8, "left": 1} cs = self.dimensions["roomstatus"] = {"height": 4, "width": w["width"]-2, "top": 2, "left": 1} elif self.shape == "nostatuslog": w = self.dimensions["chat"] = {"height": self.mucous.h-7, "width": self.mucous.w-15, "top": 2, "left": 16} elif self.shape == "chat-only": w = self.dimensions["chat"] = {"height": self.mucous.h-7, "width": self.mucous.w, "top": 2, "left": 1} if cs != None: brw = self.windows["border"]["roomstatus"] = curses.newwin(cs["height"]+2, cs["width"]+2, cs["top"]-1, cs["left"]-1) btw= self.windows["text"]["roomstatus"] = brw.subwin(cs["height"], cs["width"], cs["top"],cs["left"]) btw.scrollok(0) brw.leaveok(1) btw.leaveok(1) self.dimensions["chat"]["start"] = 0 try: mw = self.windows["border"]["chat"] = curses.newwin(w["height"]+2, w["width"], w["top"]-1, w["left"]-1) if self.mucous.username == None: #mw.border() mw.noutrefresh() tw =self.windows["text"]["chat"] = self.windows["border"]["chat"].subwin(w["height"], w["width"], w["top"], w["left"]-1) except Exception, e: self.mucous.Help.Log("debug", "Chat Mode: " + str(e)) mw.leaveok(1) tw.scrollok(0) tw.leaveok(1) tw.idlok(1) if self.mucous.Alerts.log in ( "New Chat", "Nick Mention"): self.mucous.Alerts.setStatus("") self.Change(self.current) curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ChatRooms.Mode: " + str(e)) def ClearLog(self): if self.current is None or self.current not in self.logs["rooms"]: return self.logs["rooms"][self.current] = [] if self.mucous.mode == "chat": self.Change(self.current) ## Joined a room # @param self ChatRooms (class) # @param tickers dictionary (users: tickets) def CleanTickers(self, tickers): if tickers is None: return {} CleanedTickers = {} for user, ticket in tickers.items(): CleanedTickers[user] = ticket.strip() return CleanedTickers ## Joined a room # @param self ChatRooms (class) def Joined(self, name, users, tickers=None): try: if name not in self.logs["rooms"]: self.logs["rooms"][name] = [] self.logs["roomstatus"][name] = [] self.OldLogs(name) for user in users: self.mucous.user["status"][user] = users[user][0] self.mucous.user["statistics"][user] = users[user][1], users[user][2 ], users[user][3], users[user][4] self.rooms[name] = users.keys() CleanedTickers = self.CleanTickers(tickers) if tickers: self.tickers[name] = CleanedTickers else: self.tickers[name] = {} except Exception, e: self.mucous.Help.Log("debug", "ChatRooms.Joined: " + str(e)) ## Leave a room or current room # @param self ChatRooms (class) # @param room Chat room to be left :: the current room is left if none is selected def Leave(self, room=None): if room: if room in self.rooms: self.mucous.D.LeaveRoom(room) return if self.current: self.mucous.D.LeaveRoom(self.current) ## Left a room # @param self ChatRooms (class) # @param room room that was left def Left(self, room): joined = self.rooms.keys() joined.sort(key=str.lower) if room == self.current: if len(joined) == 1: self.Change(None) else: ix = joined.index(room) if ix > 0: ix -= 1 elif ix == 0: ix = -1 self.Change(joined[ix]) self.AppendChat("Status", joined[ix], '', "Left room %s" % room) del self.rooms[room] del self.tickers[room] del self.logs["roomstatus"][room] del self.logs["rooms"][room] if joined == []: self.Change(None) self.DrawChatWin() self.windows["text"]["chat"].noutrefresh() else: joined.sort(key=str.lower) if room in self.mucous.Alerts.alert["CHAT"]: del self.mucous.Alerts.alert["CHAT"][room] curses.doupdate() if self.mucous.Alerts.log == "%s" % room[:14]: self.mucous.Alerts.setStatus("") def SaidInRoom(self, room, user, text): text = text.replace('\t', " ").replace(chr(10), "\\n") if text[:4] == "/me ": self.AppendChat("Me", room, user, text[4:]) if self.mucous.username in text[4:]: if self.mucous.mode != "chat": self.mucous.Alerts.setStatus(room) self.mucous.Alerts.alert["CHAT"][room] = "nick" self.mucous.Beep() elif self.mucous.mode == "chat" and self.current != room: self.mucous.Alerts.setStatus(room[:14]) self.mucous.Alerts.alert["CHAT"][room] = "nick" self.mucous.Beep() else: if self.mucous.mode != "chat": self.mucous.Alerts.setStatus("%s" % room) if room not in self.mucous.Alerts.alert["CHAT"]: self.mucous.Alerts.alert["CHAT"][room] = "normal" elif self.mucous.mode == "chat" and self.current != room: self.mucous.Alerts.setStatus(room) if room not in self.mucous.Alerts.alert["CHAT"]: self.mucous.Alerts.alert["CHAT"][room] = "normal" else: if self.mucous.username in text: self.AppendChat("Mentioned", room, user, text) if self.mucous.mode != "chat": self.mucous.Alerts.setStatus(room) self.mucous.Beep() self.mucous.Alerts.alert["CHAT"][room] = "nick" elif self.mucous.mode == "chat" and self.current != room: self.mucous.Alerts.setStatus(room) self.mucous.Beep() self.mucous.Alerts.alert["CHAT"][room] = "nick" else: self.AppendChat("Normal", room, user, text) if self.mucous.mode != "chat": self.mucous.Alerts.setStatus( room) if room not in self.mucous.Alerts.alert["CHAT"]: self.mucous.Alerts.alert["CHAT"][room] = "normal" elif self.mucous.mode == "chat" and self.current != room: self.mucous.Alerts.setStatus(room) if room not in self.mucous.Alerts.alert["CHAT"]: self.mucous.Alerts.alert["CHAT"][room] = "normal" self.mucous.HotKeyBar() if self.mucous.Config["mucous"]["logging"] == "yes": message = "[%s]\t%s" % (user, text) self.mucous.FileLog("rooms", time.strftime("%d %b %Y %H:%M:%S"), room, message ) ## Say message In Chat room or current room # :: Split \n (newlines) into seperate messages # @param self ChatRooms (class) # @param room Chat room # @param message test def SayInChat(self, room, message): try: message = self.mucous.dencode_language(message) if room == None or message == None: return if '\\n' in message: splited = message.split('\\n') if len(splited) > 5: for i in range(5): self.mucous.D.SayRoom(room, splited[i]) self.mucous.Help.Log("debug", "Your chat message was really long, so it was cut to keep you from getting muted.") else: for i in range(len(splited)): self.mucous.D.SayRoom(room, splited[i]) else: self.mucous.D.SayRoom(room, message) except Exception, e: self.mucous.Help.Log("debug", "SayInChat: " + str(e)) ## Append status change to log # @param self ChatRooms (Class) # @param user Username # @param room Chat room # @param did Is one of [ticker, join, part, change] # @param what contains (the ticker if did is ticker) or (away, offline, online if change) def AppendStatus(self, user, room, did, what): try: yes = 0 if room not in self.logs["roomstatus"]: self.logs["roomstatus"][room] = [] oldlen = len(self.logs["roomstatus"][room]) if did == "ticker" and what != '': if user in self.rooms[room]: if room == self.current: yes =1 elif did == "join": self.logs["roomstatus"][room].append("%s %s joined" % (time.strftime("%H:%M:%S"), user)) if room == self.current: yes =1 elif did == "left": self.logs["roomstatus"][room].append("%s %s left" % (time.strftime("%H:%M:%S"), user)) if room == self.current: yes =1 elif did == "change": for rooms11 in self.rooms.keys(): if user in self.rooms[rooms11]: string = "%s %s is %s" % (time.strftime("%H:%M:%S"), user, what) if self.logs["roomstatus"][rooms11] == []: self.logs["roomstatus"][rooms11].append(string) if rooms11 == self.current: yes =1 elif string[10:] != self.logs["roomstatus"][rooms11][-1][10:]: self.logs["roomstatus"][rooms11].append(string) if rooms11 == self.current: yes = 1 if "roomstatus" not in self.windows["text"]: return tw = self.windows["text"]["roomstatus"] if self.mucous.mode == "chat" and yes == 1: if self.scrolling["roomstatus"] >= oldlen -1: if len(self.logs["roomstatus"][room]) > 305: del self.logs["roomstatus"][room][0] self.scrolling["roomstatus"] = -1 self.DrawStatusText() except Exception, e: self.mucous.Help.Log("debug", "AppendStatus: " + str(e)) ## Join a room # @param self is ChatRooms (Class) # @param room is a text string def JoinRoom(self, room): try: self.mucous.D.JoinRoom( self.mucous.dlang( room ) ) except Exception,e: self.mucous.Help.Log("debug", "JoinRoom: " + str(e)) ## A user joined a room we are in # @param self is ChatRooms (Class) # @param room Room name # @param user User name # @param data status, speed, downloads, files, dirs, other def UserJoined(self, room, user, data): try: status, speed, downloads, files, dirs, other = data s = self.dimensions["chat"] did = "join" what = data if self.mucous.config != {}: if "ignored" in self.mucous.config.keys() and user not in self.mucous.config["ignored"].keys(): self.AppendStatus(user, room, did, what) else: self.AppendStatus(user, room, did, what) if user not in self.rooms[room]: self.rooms[room].append(user) self.mucous.user["statistics"][user] = speed, downloads, files, dirs self.mucous.user["status"][user] = status # correct placement in roombox if self.mucous.mode == "chat" and self.current == room: if self.selected == "roombox": self.rooms[room].sort(key=str.lower) if self.rooms[room].index(user) < self.scrolling[self.selected]: self.scrolling[self.selected] += 1 self.DrawBox() for lines in self.logs["rooms"][self.current][ len(self.logs["rooms"][self.current]) - s["height"]:]: # Update Chat history if user changes status if lines[2] == user: self.SetRoom(self.current) break curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "ChatRooms.UserJoined: " + str(e)) def DrawTickerScroll(self, tickers): longstring = "" for user in tickers: if self.mucous.config.has_key("ignored") and self.mucous.config["ignored"].has_key(user): continue if not self.tickers[self.current].has_key(user): continue message = self.mucous.dlang(self.tickers[self.current][user])[:self.tickersize] if len(message) == self.tickersize: message += "..." longstring += "[%s] %s " % (user, message) if self.shape in ("nostatuslog", "chat-only"): bw = self.windows["border"]["chat"] s = self.dimensions["chat"] padd = -3; posy = 0; posx = 2 else: bw = self.windows["border"]["roomstatus"] s = self.dimensions["roomstatus"] padd = 0; posy = 5; posx = 1 if self.numticker >= len(longstring): self.numticker = 0 part = longstring[self.numticker:self.numticker+s["width"]-2+padd] while len(part) < s["width"]-2 +padd: part += longstring[:(s["width"]-2+padd - len(part))] fill = (s["width"]-2 - len(part) +padd) * " " try: fullmessage = "" for m in part: fullmessage += curses.unctrl(m) bw.addstr(posy, posx, "<%s%s>" %(fullmessage[:s["width"]-3], fill)) bw.refresh() except Exception, error: self.Help.Log("debug", error) self.numticker += 1 #if self.numticker >= len(tickers): #self.numticker = 0 self.ticker_timer.cancel() self.ticker_timer = threading.Timer(float(self.mucous.Config["tickers"]["scrolltime"]), self.DrawTicker) self.ticker_timer.start() def DrawTickerCycle(self, tickers): if self.numticker >= len(tickers): self.numticker = 0 names = tickers[self.numticker] n = len(names) try: if self.mucous.PopupMenu.show == True: raise Exception, "Noticker" if self.shape not in ("nostatuslog", "chat-only"): if "roomstatus" not in self.windows["border"]: return bw = self.windows["border"]["roomstatus"] s = self.dimensions["roomstatus"] tick = str(self.tickers[self.current][names][:s["width"]-7-n]) fill = (s["width"]-6-len(tick)-len(names)) * " " string = "< [%s] %s%s>" % (names, tick, fill) bw.addstr(5, 1, self.mucous.dlang( string )) bw.refresh() elif self.shape in ("nostatuslog", "chat-only"): mw = self.windows["border"]["chat"] s = self.dimensions["chat"] tick = str(self.tickers[self.current][names][:s["width"]-25-n]) fill = (s["width"]-25-len(tick)-len(names)) * " " string = "< [%s] %s%s>" %(names, tick, fill) mw.addstr(0, 18, self.mucous.dlang( string )) mw.refresh() except Exception, error: self.Help.Log("debug", error) self.numticker += 1 self.ticker_timer.cancel() self.ticker_timer = threading.Timer(float(self.mucous.Config["tickers"]["cycletime"]), self.DrawTicker) self.ticker_timer.start() ## Loop and Draw the tickers in one of two ways (scrolling, cycling) # :: Scrolling shows the entire ticker, while Cycling shows only the part that fits in the viewable area # @param self is ChatRooms (Class) def DrawTicker(self): if self.mucous.mode != "chat" or self.current not in self.tickers or self.mucous.Config["tickers"]["tickers_enabled"] != 'yes': self.drawing_ticker = False self.ticker_timer.cancel() return if self.drawing_ticker: return if self.mucous.PopupMenu.show: self.ticker_timer.cancel() self.ticker_timer = threading.Timer(float(self.mucous.Config["tickers"]["scrolltime"]), self.DrawTicker) self.ticker_timer.start() return self.drawing_ticker = True try: sorted_tickers = self.tickers[self.current].keys() if sorted_tickers == []: self.ticker_timer.cancel() try: self.DrawStatusWin() self.DrawStatusText() curses.doupdate() except: pass else: sorted_tickers.sort(key=str.lower) if self.mucous.Config["tickers"]["ticker_scroll"] == "yes": self.DrawTickerScroll(sorted_tickers) else: self.DrawTickerCycle(sorted_tickers) curses.doupdate() except Exception,e: self.mucous.Help.Log("debug", "ChatRooms.DrawTicker: " + str(e)) self.drawing_ticker = False ## Draw the chat window's border # @param self is ChatRooms (Class) def DrawChatWin(self): try: s = self.dimensions["chat"] mw = self.windows["border"]["chat"] if self.selected == "chatroom": mw.attron(self.mucous.colors["green"]) mw.hline(0, 0, curses.ACS_HLINE, s["width"]-1) mw.hline(s["height"]+1, 0, curses.ACS_HLINE, s["width"]-1) mw.addstr(0, 0, "Oo", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(0, 3, "< Chat Rooms >", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(0, s["width"]-1, "^", self.mucous.colors["green"] | curses.A_BOLD) try: mw.addstr(s["height"]+1, s["width"]-1, "v", self.mucous.colors["green"] | curses.A_BOLD) except: pass mw.addstr(s["height"]+1, 2, "< "+str(abs(self.scrolling["chatroom"]))+" >", self.mucous.colors["green"] | curses.A_BOLD) if self.current != None: if len(self.linewrapped) -1 <= abs(self.scrolling["chatroom"]): mw.addstr(s["height"]+1, 10, "< AutoScrolling >", self.mucous.colors["green"] | curses.A_BOLD) else: mw.hline(0, 0, curses.ACS_HLINE, s["width"]-1) mw.hline(s["height"]+1, 0, curses.ACS_HLINE, s["width"]-1) mw.addstr(0, 0, "Oo", curses.A_BOLD) mw.addstr(0, 3, "< Chat Rooms >", curses.A_BOLD) mw.addstr(0, s["width"]-1, "^", curses.A_BOLD) try: mw.addstr(s["height"]+1, s["width"]-1, "v", curses.A_BOLD) except: pass # mw.addstr(s["height"]+1, 2, "< "+str(abs(self.scrolling["chatroom"]))+" >", curses.A_BOLD) mw.noutrefresh() except Exception,e : self.mucous.Help.Log("debug", "ChatRooms.DrawChatWin: " + str(e)) ## Mouse Coordinates in the Chat Rooms Mode # @param self is ChatRooms (class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def MouseChat(self, x, y, z, event): try: w = self.dimensions["chat"] if y == w["top"]-1 and x >= w["left"]-1 and x < w["left"]+3: self.ChatLayout() return # Clickable room switch if "roombox" in self.dimensions and self.shape not in ( "noroombox", "chat-only"): roombox = self.dimensions["roombox"] if y >= roombox["top"]-1 and y < roombox["top"] + roombox["height"] and x < roombox["width"] + roombox["left"] and x > roombox["left"]: if self.selected != "roombox": self.selected = "roombox" self.Mode() y -= roombox["top"] if "start" not in roombox: return if y + roombox["start"] in range(len(self.logs["roombox"][self.current])): sup = y + roombox["start"] if event in ( 4096, 16384): if sup != self.scrolling["roombox"]: self.scrolling["roombox"] = sup self.DrawBox() self.mucous.PopupMenu.Create("roombox", 0, True) curses.doupdate() else: if sup != self.scrolling["roombox"]: self.scrolling["roombox"] = sup self.DrawBox() curses.doupdate() return elif y == roombox["top"] + roombox["height"] and x < roombox["width"] + roombox["left"] and x > roombox["left"]: self.mucous.ModifyConfig("autojoin", self.current, '') if y == self.mucous.h-3 or y == self.mucous.h-4: if x>= self.mucous.w-27 and x < self.mucous.w-18: self.mucous.PopupMenu.Create("encoding", 0, True) elif x >= self.mucous.w-17 and x < self.mucous.w-1: joined = self.rooms.keys() joined.sort(key=str.lower) if not self.current in joined: ix = 0 else: ix = joined.index(self.current) if x >= self.mucous.w-9 and x < self.mucous.w-1: # Next Button ix += 1 elif x <= self.mucous.w-10 and x >= self.mucous.w-17: # Prev Button ix -= 1 else: return if ix < 0: ix = -1 elif ix >= len(joined): ix = 0 self.Change(joined[ix]) elif y in (w["top"] + w["height"], w["top"] + w["height"]-1) and x >= w["left"] + w["width"]-5 and x <= w["left"] + w["width"]: self.mucous.key = "KEY_NPAGE" self.mucous.edit.ScrollText("KEY_NPAGE") elif y in ( w["top"], w["top"]+1) and x >= w["left"] + w["width"]-5 and x <= w["left"] + w["width"]: self.mucous.key = "KEY_PPAGE" self.mucous.edit.ScrollText("KEY_PPAGE") else: if y >= w["top"]-1 and y < w["top"] + w["height"] +1 and x >= w["left"] -1 and x < w["left"] +w["width"]+1: if self.selected != "chatroom": self.selected = "chatroom" self.Mode() if "roomstatus" in self.dimensions and self.shape not in ( "nostatuslog", "chat-only") and self.selected != "roomstatus": w = self.dimensions["roomstatus"] if y >= w["top"]-1 and y < w["top"] + w["height"] +1 and x >= w["left"] -1 and x < w["left"] +w["width"]+1: self.selected = "roomstatus" self.Mode() except Exception, e: self.mucous.Help.Log("debug", "MouseChat: " +str(e) ) ## Read Old Chat Room Logs from Disk # @param self is ChatRooms (class) # @param room room's name used to get log file def OldLogs(self, room): try: # Read from Chat Room Logs if "\\" in room: room = room.replace("/", "\\") if not os.path.exists( os.path.expanduser( self.mucous.Config["mucous"]["log_dir"]) +"/rooms/" + room): return path = os.path.expanduser( self.mucous.Config["mucous"]["log_dir"]) + "/rooms/" + room f = open(path, "r") a = f.read() f.close() lines = a.split("\n" ) numlines = -100 if abs(numlines) > len(lines): numlines = 0 if len(lines) <= abs(numlines): numlines = 0 lastday = None for line in lines[numlines:]: if line == "": continue line = line.replace("\\n", "\n") timex = line[12:20] month = line[3:6] day = line[:2] year = line[7:11] if lastday != None and day != lastday: self.logs["rooms"][room].append(["Date", "--------", "", "%s %s %s" % (day, month, year)]) lastday = day if line[21] == "[": user = line[22:] if line.find("\t") == -1: # old format user = user[:user.find("]")] message = line[21+len(user)+3:] else: # new format with Tab user = user[:user.find("\t")-1] message = line[line.find("\t")+1:] else: user = line[21:] user = user[:user.find(" ")] message = line[21+len(user)+1:] if message[:4] == "/me ": full_message = ["Me", timex, user, message[4:]] else: full_message = ["Normal", timex, user, message] self.logs["rooms"][room].append(full_message) self.AppendChat("Status", room, '', "Connected to Museek") #self.logs["rooms"][room].append(["Status", "--------", "", "Connected to Museek"]) except Exception,e: self.mucous.Help.Log("debug", "OldLogs: " +str( e) ) ## Draw Status Window Text # @param self is ChatRooms (class) def DrawStatusText(self): try: if self.shape in ("nostatuslog", "chat-only"): return if self.mucous.PopupMenu.show == True: return if self.mucous.mode != "chat": return s = self.dimensions["chat"] tw = self.windows["text"]["roomstatus"] w = self.dimensions["roomstatus"] tw.erase() tw.idlok(1) if self.current == None or self.logs["roomstatus"][self.current] == []: tw.noutrefresh() return if self.scrolling["roomstatus"] == -1: self.scrolling["roomstatus"] = len(self.logs["roomstatus"][self.current]) clipped_list, self.scrolling["roomstatus"], self.dimensions["roomstatus"]["start"] = self.mucous.FormatData.scrollbox(self.logs["roomstatus"][self.current], self.scrolling["roomstatus"], self.dimensions["roomstatus"]["height"]) count = 0 try: for line in clipped_list: #self.mucous.Help.Log("debug", line +str(self.scrolling["roomstatus"])) if len(line) > w["width"]: line = line [:w["width"] -len(line) ] else: line += " " * (w["width"] -len(line)) if count + self.dimensions["roomstatus"]["start"] == self.scrolling["roomstatus"]: tw.addstr(self.mucous.dlang( line) , curses.A_BOLD) else: tw.addstr(self.mucous.dlang( line )) count += 1 except Exception, e: pass tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "DrawStatusText: " + str(e)) ## Draw the contents of the user-box # A status asterix (*) followed by a username # @param self is ChatRooms (class) # @param user User's name # @param start Position to indicate highlight status def DrawBoxUsers(self, user, start): # RoomBox List Display try: w = self.dimensions["roombox"] mw = self.windows["border"]["roombox"] tw = self.windows["text"]["roombox"] if self.current == None or self.logs["roombox"][self.current] == []: tw.addstr("No one") return try: if user in self.mucous.user["status"]: if self.mucous.user["status"][user] == 1: tw.addstr('* ', self.mucous.colors["yellow"]|curses.A_BOLD) elif self.mucous.user["status"][user] == 2: tw.addstr('* ', self.mucous.colors["green"]|curses.A_BOLD) elif self.mucous.user["status"][user] == 0: tw.addstr('* ', self.mucous.colors["red"]|curses.A_BOLD) else: tw.addstr('* ', curses.A_BOLD) if self.mucous.config.has_key("banned") and user in self.mucous.config["banned"].keys(): if self.scrolling["roombox"] == self.logs["roombox"][self.current].index(user): attrib = curses.A_BOLD | curses.A_REVERSE | self.mucous.colors["red"] else: attrib = self.mucous.colors["red"]| curses.A_BOLD elif self.mucous.config.has_key("ignored") and user in self.mucous.config["ignored"].keys(): if self.scrolling["roombox"] == self.logs["roombox"][self.current].index(user): attrib = curses.A_BOLD | curses.A_REVERSE | self.mucous.colors["yellow"] else: attrib = self.mucous.colors["yellow"]| curses.A_BOLD elif self.mucous.config.has_key("trusted") and user in self.mucous.config["trusted"].keys(): if self.scrolling["roombox"] == self.logs["roombox"][self.current].index(user): attrib = curses.A_BOLD | curses.A_REVERSE | self.mucous.colors["cyan"] else: attrib = self.mucous.colors["cyan"] | curses.A_BOLD elif self.mucous.config.has_key("buddies") and user in self.mucous.config["buddies"].keys(): if self.scrolling["roombox"] == self.logs["roombox"][self.current].index(user): attrib = curses.A_BOLD | curses.A_REVERSE | self.mucous.colors["green"] else: attrib = self.mucous.colors["green"]| curses.A_BOLD else: if self.scrolling["roombox"] == self.logs["roombox"][self.current].index(user): attrib = curses.A_BOLD | curses.A_REVERSE else: attrib = curses.A_NORMAL if len(user[:w["twidth"]-2]) < w["twidth"]-2: space = " " * ( w["twidth"]-2 - len(user[:w["twidth"]-2])) else: space ='' tw.addstr(self.mucous.dlang(user[:w["twidth"]-2])+space, attrib) except: pass except Exception, e: self.mucous.Help.Log("debug", "DrawBoxUsers " +str(e)) ## Create the window and border of the user-box # @param self is ChatRooms (class) def DrawBox(self): # RoomBox Shape Display try: if self.mucous.mode != 'chat' or self.shape == "noroombox": return # Cleanup stale windows if "roombox" in self.windows["text"]: del self.windows["text"]["roombox"] if "roombox" in self.windows["border"]: del self.windows["border"]["roombox"] if self.shape in ("big", "nostatuslog", "widelist", "rightlist"): w = self.dimensions["chat"] if self.shape == "rightlist": s = self.dimensions["roombox"] = {"height": self.mucous.h-7, "top": 2, "left": (w["width"]), "width": self.mucous.w-w["width"], "start": -1 } else: s = self.dimensions["roombox"] = {"height": self.mucous.h-7, "top": 2, "left": 0, "width": self.mucous.w-w["width"], "start": -1 } # Create wi mw = self.windows["border"]["roombox"] = curses.newwin(s["height"]+2, s["width"], s["top"]-1, s["left"]) if self.selected == "roombox": mw.attron(self.mucous.colors["green"]) mw.attroff(self.mucous.colors["green"]) if self.shape == "rightlist": self.dimensions["roombox"]["twidth"] = s["width"]-1 self.dimensions["roombox"]["tleft"] = s["left"]+1 else: self.dimensions["roombox"]["twidth"] = s["width"]-1 self.dimensions["roombox"]["tleft"] = s["left"] tw = self.windows["text"]["roombox"] = mw.subwin(s["height"], s["twidth"], s["top"], s["tleft"]) elif self.shape == "small": s = self.dimensions["roombox"] = {"height": 4, "top": 2, "left": 0, "width": 15} mw = self.windows["border"]["roombox"] = curses.newwin(s["height"]+2, s["width"], s["top"]-1, s["left"]) if self.selected == "roombox": mw.attron(self.mucous.colors["green"]) mw.attroff(self.mucous.colors["green"]) self.dimensions["roombox"]["twidth"] = s["width"] -1 tw = self.windows["text"]["roombox"] = mw.subwin(s["height"], s["twidth"], s["top"], s["left"]) if self.shape in ("big", "small", "nostatuslog", "widelist", "rightlist"): tw.scrollok(0) tw.idlok(1) mw.leaveok(1) tw.leaveok(1) if self.current != None: try: if self.selected == "roombox": mw.addstr(0, 0, "Users: "+str(len(self.rooms[self.current])), self.mucous.colors["green"]|curses.A_BOLD) else: mw.addstr(0, 0, "Users: "+str(len(self.rooms[self.current])), curses.A_BOLD) except: pass if self.selected == "roombox": cs = self.mucous.colors["green"] |curses.A_BOLD else: cs = curses.A_BOLD if "autojoin" in self.mucous.config and self.current in self.mucous.config["autojoin"].keys(): mw.addstr(self.dimensions["roombox"]["height"]+1, 0, "[x] AutoJoined", cs) else: cs = curses.A_BOLD mw.addstr(self.dimensions["roombox"]["height"]+1, 0, "[ ] AutoJoined", cs) mw.noutrefresh() if self.current != None: self.logs["roombox"][self.current] = [] if len( self.rooms[self.current] ) > 0: self.logs["roombox"][self.current] = self.rooms[self.current] self.logs["roombox"][self.current].sort(key=str.lower) try: if self.logs["roombox"][self.current] != []: clipped_list, self.scrolling["roombox"], self.dimensions["roombox"]["start"] = self.mucous.FormatData.scrollbox(self.logs["roombox"][self.current], self.scrolling["roombox"], self.dimensions["roombox"]["height"]) # Draw users self.FormatBox() else: tw.addstr("* Empty") tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "RSB: " + str(e)) #curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "DrawBox " +str(e)) ## Start displaying tickets in one second # @param self is ChatRooms (class) def TickersStartTimer(self): try: if self.mucous.mode == "chat": if self.rooms.keys() != []: self.numticker = 0 self.ticker_timer.cancel() self.ticker_timer = threading.Timer(1.0, self.DrawTicker) self.ticker_timer.start() except Exception, e: self.mucous.Help.Log("debug", "TickersStartTimer: " + str(e)) ## Toggle whether tickers are displayed or not # @param self is ChatRooms (class) def ToggleTickersDisplay(self): try: if str(self.mucous.Config["tickers"]["tickers_enabled"]) == 'no': self.mucous.Config["tickers"]["tickers_enabled"] = 'yes' elif str(self.mucous.Config["tickers"]["tickers_enabled"]) == 'yes': self.mucous.Config["tickers"]["tickers_enabled"] = 'no' if self.mucous.mode=="setup": self.mucous.Setup.Mode() except Exception, e: self.mucous.Help.Log("debug", "ToggleTickersDisplay: "+str(e)) ## Toggle the way Tickers are displayed (Scrolling, Cycling) # @param self is ChatRooms (class) def ToggleTickers(self): try: if self.mucous.Config["tickers"]["ticker_cycle"] == 'no': self.mucous.Config["tickers"]["ticker_cycle"] = 'yes' if str(self.mucous.Config["tickers"]["ticker_scroll"]) == 'yes': self.mucous.Config["tickers"]["ticker_scroll"] = 'no' elif self.mucous.Config["tickers"]["ticker_cycle"] == 'yes': self.mucous.Config["tickers"]["ticker_cycle"] = 'no' if str(self.mucous.Config["tickers"]["ticker_scroll"]) == 'no': self.mucous.Config["tickers"]["ticker_scroll"] = 'yes' #self.ticker_timer.cancel() if self.mucous.mode=="chat": self.DrawStatusWin() self.DrawStatusText() curses.doupdate() elif self.mucous.mode=="setup": self.mucous.Setup.Mode() except Exception, e: self.mucous.Help.Log("debug", "ToggleTickers: "+str(e)) ## Interator for Chat Room User List # Clears, and starts drawing from the scroll position def FormatBox(self): try: w = self.dimensions["roombox"] lol = self.logs["roombox"][self.current] mw = self.windows["border"]["roombox"] tw = self.windows["text"]["roombox"] tw.erase() clipped_list, self.scrolling["roombox"], self.dimensions["roombox"]["start"] = self.mucous.FormatData.scrollbox(lol, self.scrolling["roombox"], w["height"]) for lines in clipped_list: self.DrawBoxUsers(lines, w["start"]) tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "FormatBox: " + str(e)) ## Format Current Chat Room # @param self is ChatRooms (class) def FormatChatText(self): try: if self.current != None: w = self.dimensions["chat"] selected_log = self.logs["rooms"][self.current] #if self.scrolling["chatroom"] == -1 or self.scrolling["chatroom"] > w["height"]: #if len(selected_log) > w["height"]: #selected_log = selected_log[:] #lol = self.LineWrap(selected_log, w) if self.linewrapped == None: self.linewrapped = self.LineWrap(selected_log, w) if self.scrolling["chatroom"] == -1: self.scrolling["chatroom"] = len(self.linewrapped) clipped_list, self.scrolling["chatroom"], self.dimensions["chat"]["start"] = self.mucous.FormatData.scrollbox(self.linewrapped, self.scrolling["chatroom"], w["height"]) self.windows["text"]["chat"].erase() for lines in clipped_list: self.DrawChatText(lines) self.DrawChatWin() self.windows["text"]["chat"].noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "FormatChatText: " + str(e)) ## Insanely complex line wraping for chat messages # Needed for proper scrolling # @param self is ChatRooms (class) # @param the_list self.logs["rooms"][self.current] # @param w self.dimensions["chat"] # @return cut_list def LineWrap(self, the_list, w): # we wrap text here so that scrolling works properly... try: pos = 0 cut_list = [] for mtype, timestamp, username, message in the_list: length = 0 message = self.mucous.dlang(message) #mtype, timestamp, username, message = line[0], line[1], line[2], line[3] if mtype == "Me": #username = self.mucous.dlang(username) pre = " * %s " % username s = "%s" % message length += len(timestamp) + len(pre) # elif mtype == "List": # room = self.current # pre = "Users in %s: "% room # length += len(pre) # for user, color in message: # length += len(self.dlang(user)) elif mtype in ("Mentioned", "Normal", "Date"): if username != "": # Universal Timestamp if mtype in ("Date"): # Mucous debugging message length += len(timestamp) + 2 else: # Normal user chat length += len(timestamp) + 4 #length += len(self.dlang(username)) length += len(username) if "\n" in message: messagez = message.split('\n') # Wrap first line firstmsg = messagez[0] wit = len(timestamp) + 4 + len(username) lm = len(firstmsg) mess = lm - ( (wit + lm ) - w["width"]) cut_list.append( [ mtype, timestamp, username, firstmsg[:mess] ] ) restmess = firstmsg[mess:] div = ( len(restmess)/w["width"] ) + 1 spaces= (w["width"] * div) - len(restmess) for seq in range(div): if mtype == "Me": cut_list.append(['cutme', '', '', restmess[:w["width"]] ]) else: cut_list.append(['cut', '', '', restmess[:w["width"]] ]) restmess = restmess[w["width"]:] # Prepend -- to all following lines m = [] for messages in messagez[1:]: m.append("--"+messages) # Wrap each of the following lines for messages in m: lm = len(messages) restmess = messages div = ( len(restmess)/w["width"] ) + 1 spaces= (w["width"] * div) - len(restmess) for seq in range(div): if mtype == "Me": cut_list.append(['cutme', '', '', restmess[:w["width"]] ]) else: cut_list.append(['cut', '', '', restmess[:w["width"]] ]) restmess = restmess[w["width"]:] pos += 1 continue # Short message if length +len(message) <= w["width"]: cut_list.append([mtype, timestamp, username, message]) # long message elif length +len(message) > w["width"]: lm = len(message) mess = lm - ( (length + lm ) - w["width"]) cut_list.append( [ mtype, timestamp, username, message[:mess] ] ) restmess = message[mess:] div = ( len(restmess)/w["width"] ) + 1 spaces= (w["width"] * div) - len(restmess) for seq in range(div): #self.mucous.Help.Log("debug", str(div)+"--" + restmess[:w["width"]] ) if mtype == "Me": cut_list.append(['cutme', '', '', restmess[:w["width"]] ]) else: cut_list.append(['cut', '', '', restmess[:w["width"]] ]) restmess = restmess[w["width"]:] pos += 1 return cut_list except Exception, e: # Exceptions are Inevitable self.mucous.Help.Log("debug", "LineWrap: " + str(e)) ## Draw Chat Log Window Text # @param self is ChatRooms (class) # @param roomlinenumber number of line in chat room log def DrawChatText(self, roomlinenumber): try: room = self.current mtype, timestamp, username, message2 = roomlinenumber lang = self.mucous.Config["mucous"]["language"] w = self.dimensions["chat"] room = self.mucous.dlang(room) length = 0 tw = self.windows["text"]["chat"] message = "" message = message2 try: if mtype == "Me": # /me message tw.addstr(timestamp) username = self.mucous.dlang(username) pre = " * %s " % username tw.addstr(pre, self.mucous.colors["green"] | curses.A_BOLD) s = message length += len(timestamp) + len(pre)+ len(s) tw.addstr(s, self.mucous.colors["green"] | curses.A_BOLD) elif mtype == "List": # List of users in Room pre = "Users in %s: "% room #self.textwin.addstr(pre) length += len(pre) for username, color in message: username = self.mucous.dlang(username) length += len(username) if color == "Me": tw.addstr(username, curses.A_BOLD) elif color == "Left": tw.addstr(username, self.mucous.colors["yellow"]) elif color == "Banned": tw.addstr(username, self.mucous.colors["red"]) elif color == "Buddies": tw.addstr(username, self.mucous.colors["green"]) elif color == "NotLast": tw.addstr(username) elif color == "Normal": tw.addstr(username) elif mtype == "cut": s = message tw.addstr(s) length += len(s) elif mtype == "cutme": s = message tw.addstr(s, self.mucous.colors["green"] | curses.A_BOLD) length += len(s) elif mtype == "Date": self.mucous.FormatData.Hline(tw, curses.ACS_HLINE, 8) # Mucous status message pre = " " tw.addstr( pre) length += len(timestamp) + len(pre) #name = self.mucous.dlang(username) #tw.addstr(name) suf = " " tw.addstr(" ") length += len(suf) s = message tw.addstr(s, self.mucous.colors["cyan"] | curses.A_BOLD) length += len(s) if length < w["width"]: length += 1 tw.addstr(" ") if length < w["width"]: ll = w["width"] - length length += ll self.mucous.FormatData.Hline(tw, curses.ACS_HLINE, ll ) elif mtype == "Status": tw.addstr(timestamp+" ") length += len(timestamp)+1 s = message tw.addstr("< ", self.mucous.colors["cyan"] ) tw.addstr(s, self.mucous.colors["cyan"] | curses.A_BOLD) tw.addstr(" >", self.mucous.colors["cyan"] ) length += len(s) + 4 else: # Universal Timestamp tw.addstr(timestamp) # Normal user chat pre = " [" tw.addstr(pre) length += len(timestamp) + len(pre) name = self.mucous.dlang(username) length += len(name) if username == self.mucous.username: tw.addstr(username , curses.A_BOLD ) elif username not in self.rooms[room]: tw.addstr(name, self.mucous.colors["yellow"]) elif self.mucous.config.has_key("banned") and username in self.mucous.config["banned"].keys(): tw.addstr(name, self.mucous.colors["red"]) elif self.mucous.config.has_key("buddies") and username in self.mucous.config["buddies"].keys(): tw.addstr(name, self.mucous.colors["green"]) else: tw.addstr(name) suf = "] " length += len(suf) tw.addstr(suf) if mtype == "Mentioned": x = message.split(" ") for e in x: if self.mucous.username not in e: length += len(e) tw.addstr(e) elif self.mucous.username in e: length += len(e) tw.addstr(e, self.mucous.colors["cyan"] | curses.A_BOLD) if e is not x[-1]: if length < w["width"]: length += 1 tw.addstr(" ") elif mtype == "Normal": s = message length += len(s) tw.addstr(s) except Exception, e: pass #self.mucous.Help.Log("debug", "DrawChatText: " + str(e)) # Exceptions are Inevitable try: if length < w["width"]: tw.addstr(" " * (w["width"] - length)) except Exception, e: pass except Exception, e: self.mucous.Help.Log("debug", "DrawChatText: " + str(e)) ## Append message to Chat Log # @param self is ChatRooms (class) # @param mtype type of message # @param room room # @param user username # @param message text def AppendChat(self, mtype, room, user, message): try: if room == None: room = self.current if self.linewrapped != None: if self.scrolling["chatroom"] >= len(self.linewrapped) -1: self.scrolling["chatroom"] = -1 if room == self.current: self.linewrapped = None full_message = [mtype, time.strftime("%H:%M:%S"), user, message] if len( self.logs["rooms"][room] ) >= 700: del self.logs["rooms"][room][0] self.logs["rooms"][room].append(full_message) if self.mucous.mode == "chat": if room == self.current and self.selected == "chatroom": if self.scrolling["chatroom"] == -1: self.FormatChatText() elif room == self.current and self.selected == "roombox": temp = self.scrolling["chatroom"] self.scrolling["chatroom"] = -1 self.FormatChatText() self.scrolling["chatroom"] = temp except Exception,e : self.mucous.Help.Log("debug", "AppendChat: " + str(e)) ## Draw Chat Room Status Window Border # @param self is ChatRooms (class) def DrawStatusWin(self): try: if self.shape in ("nostatuslog", "chat-only"): return w = self.dimensions["chat"] bw = self.windows["border"]["roomstatus"] if self.mucous.PopupMenu.show == True: raise Exception, "popup" if self.shape in ("noroombox", "big", "small", "rightlist", "widelist"): if self.selected == "roomstatus": bw.attron(self.mucous.colors["green"]) else: bw.attroff(self.mucous.colors["green"]) bw.border() bw.addstr(0, 3, "<") bw.addstr(0, 4, " Status Log ", self.mucous.colors["blue"] | curses.A_BOLD) bw.addstr(0, 16, ">") bw.noutrefresh() except: pass def WindowCycle(self): _list = None if self.shape == "chat-only": self.selected = "chatroom" return elif self.shape == "nostatuslog": _list = ["chatroom", "roombox"] elif self.shape == "noroombox": _list = ["chatroom", "roomstatus"] else: _list = ["chatroom", "roomstatus", "roombox"] if _list != None: self.selected = self.mucous.FormatData.RotateList("right", _list, self.selected, "no") self.Mode() ## Switch to another Chat Window Layout # @param self is ChatRooms (class) def ChatLayout(self): try: # [ "small","big","widelist","rightlist","nostatuslog","chat-only","noroombox"] if self.shape == "noroombox": self.shape = "small" self.selected = "roombox" elif self.shape == "small": self.shape = "big" self.selected = "roombox" elif self.shape == "big": self.shape = "widelist" self.selected = "roombox" elif self.shape == "widelist": self.shape = "rightlist" self.selected = "roombox" elif self.shape == "rightlist": self.shape = "nostatuslog" self.selected = "chatroom" elif self.shape == "nostatuslog": self.shape = "chat-only" self.selected = "chatroom" elif self.shape == "chat-only": self.shape = "noroombox" self.selected = "chatroom" self.mucous.Config["mucous"]["roombox"] = self.shape self.Mode() except Exception, e: self.mucous.Help.Log("debug", "ChatLayout: " + str(e)) ## Change Room (Reset scrolling) # @param self is ChatRooms (class) # @param r room name def Change(self, r): self.scrolling["chatroom"] = self.scrolling["roomstatus"] = -1 self.scrolling["roombox"] = 0 self.SetRoom(r) self.TickersStartTimer() ## Change Room # @param self is ChatRooms (class) # @param r room name def SetRoom(self, room): try: self.linewrapped = None self.current = room if self.mucous.mode != "chat": return # Change title in edit window if self.shape not in ("chat-only", "nostatuslog"): self.DrawStatusWin() self.DrawStatusText() self.mucous.SetEditTitle(self.current) # Display Next-room hotspot's text try: # Encoding button if self.current != None: if self.current in self.mucous.config["encoding.rooms"]: blah = self.mucous.config["encoding.rooms"][self.current] else: blah = self.mucous.config["encoding"]["network"] ibw = self.mucous.windows["inputborder"] ibw.addstr(0, self.mucous.w-17-len(blah)-4, "<" + (" " *( len(blah) +2) )+ ">") ibw.addstr(0, self.mucous.w-17-len(blah)-2, blah, self.mucous.colors["cyan"] | curses.A_BOLD) # Previous, Next Buttons ibw.addstr(0, self.mucous.w-17, "< >") ibw.addstr(0, self.mucous.w-15, "Prev", self.mucous.colors["cyan"] | curses.A_BOLD) ibw.addstr(0, self.mucous.w-9, "< >") ibw.addstr(0, self.mucous.w-7,"Next", self.mucous.colors["cyan"] | curses.A_BOLD) # Clean screen ibw.noutrefresh() except Exception, e: pass try: self.windows["input"].noutrefresh() except: pass self.DrawBox() # Display chat log if self.selected == "chatroom": self.windows["border"]["chat"].attron(self.mucous.colors["green"]) else: self.windows["border"]["chat"].attroff(self.mucous.colors["green"]) self.FormatChatText() # Clear Alert log if "%s" % self.current == self.mucous.Alerts.log: self.mucous.Alerts.setStatus("") try: self.windows["text"]["chat"].noutrefresh() except: pass self.mucous.Alerts.Check() except Exception, e: self.mucous.Help.Log("debug", "SetRoom: " + str(e)) museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousFormat.py0000644000175000017500000002002110662543102023051 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. from UserDict import UserDict import curses.wrapper, curses.ascii ## Dictionary that's sorted alphabetically # @param UserDict dictionary to be alphabetized class SortedDict(UserDict): ## Constructor # @param self SortedDict def __init__(self): self.__keys__ = [] self.__sorted__ = True UserDict.__init__(self) ## Set key # @param self SortedDict # @param key dict key # @param value dict value def __setitem__(self, key, value): if not self.__dict__.has_key(key): self.__keys__.append(key) self.__sorted__ = False UserDict.__setitem__(self, key, value) ## Delete key # @param self SortedDict # @param key dict key def __delitem__(self, key): self.__keys__.remove(key) UserDict.__delitem__(self, key) ## Get keys # @param self SortedDict # @return __keys__ def keys(self): if not self.__sorted__: self.__keys__.sort() self.__sorted__ = True return self.__keys__ ## Get items # @param self SortedDict # @return list of keys and items def items(self): if not self.__sorted__: self.__keys__.sort() self.__sorted__ = True for key in self.__keys__: yield key, self[key] ## Parse data and reformat it to fit inside the constraints of its window # class FormatData: ## Constructor # @param self FormatData (class) # @param mucous Mucous (Class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## Determine Line number and list size to height # We assume that this list has already been wrapped # @param self FormatData (class) # @param current_list list to be shrunk # @param hightlight_position where our vertical scroll position is # @param height how many lines are in the window # @return list, scrolling position, start position def scrollbox(self, current_list, hightlight_position, height): # Limit text to what fits inside the window try: length = len(current_list) if hightlight_position < 0: hightlight_position = 0 elif hightlight_position > length -1: hightlight_position = length-1 start = hightlight_position-height/2 if start < 0: to = hightlight_position + (height-height/2)-start start = 0 else: to = hightlight_position + (height-height/2) if to >= length: start = abs(to - length - start) to =length if length < height: start =0 #self.Help.Log("debug", "s%se%su%s"%(start,to,hightlight_position) ) if current_list[start:to] != [None]: clipped_list = current_list[start:to] else: clipped_list = [] return clipped_list, hightlight_position, start except Exception, e: self.mucous.Help.Log("debug", "scrollbox: " + str(e)) ## Convert a number into a human-readable string # Formats are: 0GB, 0MB, 0KB, 0B # @param self FormatData # @param filesize can be integer in a string or an integer # @return formatted size (string) def byte_format(self, filesize): try: filesize = int(filesize) if filesize >= 1073741824: filefrmt = str(filesize/1024/1024/1024) +"GB" elif filesize >= 1048576 and filesize <= 1073741824: filefrmt = str(filesize/1024/1024) +"MB" elif filesize < 1048576 and filesize >= 1024: filefrmt = str(filesize/1024) +"KB" elif filesize < 1024 and filesize > 0: filefrmt = str(filesize) +" B" elif filesize == 0: filefrmt = '0' return filefrmt except Exception, e: self.mucous.Help.Log("debug", "byte_format: " + str(e)) ## Another int-to-string Conversion # Formats are: 0.0GB, 0.0MB, 0.0KB, 0 # @param self FormatData # @param size filesize can be integer in a string or an integer # @return formatted size (string) def Humanize(self, size): if size is None: return None try: s = int(size) if s >= 1000*1024*1024: r = "%.1fGB" % ((float(s) / (1024.0*1024.0*1024.0))) elif s >= 1000*1024: r = "%.1fMB" % ((float(s) / (1024.0*1024.0))) elif s >= 1000: r = "%.1fKB" % ((float(s) / 1024.0)) else: r = str(size) return r except Exception, e: return size ## Sort a dict by values # Return the sorted dict # @param self FormatData (class) # @param dict the dict def sortbyvalue(self, dict): try: """ Return a list of (key, value) pairs, sorted by value. """ _swap2 = lambda (x,y): (y,x) mdict = map(_swap2, dict.items()) mdict.sort() mdict = map(_swap2, mdict) return mdict except Exception, e: self.mucous.Help.Log("debug", "sortbyvalue: " + str(e)) ## Pad string to fill width of window # @param self FormatData (class) # @param s string # @param w window_dimensions dict # @return padded string, length of string def StringAddBlanks(self, s, w): try: #, total_lines s = str(s); ls = len(s) if ls > w["width"]: # Add spaces if longer than a single line div = (ls/w["width"]) + 1 length = (w["width"] * div) - ls if length != 0: s += (length * " ") #total_lines += div else: # Add spaces till end of first and only line s += " " * (w["width"] - ls) return s, ls #, total_lines except Exception, e: self.mucous.Help.Log("debug", "StringAddBlanks: " + str(e)) ## Select an item in the list one place away # @param self FormatData (class) # @param direction (left or right) # @param _list the list # @param place current position in list # @param sort do we alphabetically sort this list? (true/false) # @return new or old place def RotateList(self, direction, _list, place, sort): try: if not _list: return place if sort == "yes": _list.sort(key=str.lower) if not place in _list: if direction == "left": ix = -1 elif direction == "right": ix = 0 else: ix = _list.index(place) if direction == "left": ix -= 1 elif direction == "right": ix += 1 if ix < 0: ix = -1 elif ix >= len(_list): ix = 0 if ix != None: place = _list[ix] return place except Exception, e: self.mucous.Help.Log("debug", "RotateList: " +str(e) ) ## Break a long string into a list of strings that each fit inside the required width # @param self FormatData (class) # @param string our long string # @param w the window dimensions dict contains "width" # @return list def StringCutWidth(self, string, w): try: s = str(string) ls = len(s) list_of_strings = [] if ls > w["width"]: div = (ls/w["width"]) + 1 # Cut long lines into multiple lines for seq in range(div): list_of_strings.append(s[:w["width"]]) s = s[w["width"]:] else: # Short line added to list list_of_strings.append(s) return list_of_strings except Exception, e: self.mucous.Help.Log("debug", ": " + str(e)) ## Break a list apart and wrap each line in it to the required width and clip it so only the lines that fit are # @param self FormatData (class) # @param the_list list of strings # @param scroll scrolling position # @param w the window dimensions dict contains "width" and "start" # @return clipped_list, numlines, start # @return list, scroll position, start position def wrap_n_clip(self, the_list, scroll, w): try: wrapped_lines = [] for lines in the_list: #lines = str(lines) lines1 = "" for a in lines: if curses.ascii.isctrl(a): a = curses.ascii.unctrl(a) lines1 += a list_of_strings = self.StringCutWidth(lines1, w) for string in list_of_strings: wrapped_lines.append(string) if scroll == -1 or scroll > len(wrapped_lines): scroll = len(wrapped_lines) clipped_list, numlines, w["start"] = self.scrollbox(wrapped_lines, scroll, w["height"]) return clipped_list, scroll, w["start"] except Exception, e: self.mucous.Help.Log("debug", "wrap_n_clip " +str(e)) ## Add a number of characters to the current cursor position of a window # @param self FormatData (class) # @param window the curses window # @param character the special character # @param number how many times to display the character def Hline(self, window, character, number): while number: window.addch(character) number -= 1 museek+-0.2+svn20100315.r1208/mucous/pymucous/MucousRoomsList.py0000644000175000017500000001632310540541762023573 0ustar gandalfgandalf# This is part of the Mucous Museek Client, and distributed under the GPLv2 # Copyright (c) 2006 daelstorm. import curses.wrapper ## RoomsList # Scrollable and sortable list of rooms on the server class RoomsList: ## Constructor (create initial variables) # @param self RoomsList (class) # @param mucous Mucous (class) def __init__(self, mucous): ## @var mucous # Mucous (Class) self.mucous = mucous ## @var windows # dict containing instances of curses windows self.windows = {} ## @var scrolling # vertical position in list self.scrolling = 0 ## @var dimensions # dict containing placement data for windows self.dimensions = {} ## @var rooms # dict containing rooms and their sizes; updated by server self.rooms = {} ## @var sizedrooms # Sorted rooms; no size data; rebuilt every time viewed self.sizedrooms = [] ## Build windows and display rooms # @param self RoomsList (class)# def Mode(self): try: self.mucous.UseAnotherEntryBox() self.mucous.mode = "roomlist" self.mucous.PopupMenu.show = False s = self.dimensions = {"height": self.mucous.h-7, "top": 2, "left": 1, "width": self.mucous.w-2} self.DrawWindow() self.sizedrooms = [] alpharooms = [] if self.mucous.Config["mucous"]["rooms_sort"] in ("alpha", "alpha-reversed"): for rooms in self.rooms.keys(): alpharooms.append(rooms) alpharooms.sort(key=str.lower) if self.mucous.Config["mucous"]["rooms_sort"] =="alpha-reversed": alpharooms.reverse() elif self.mucous.Config["mucous"]["rooms_sort"] in ("size", "size-reversed"): bigsizes = [] bigsizes = self.mucous.FormatData.sortbyvalue (self.rooms) if self.mucous.Config["mucous"]["rooms_sort"] == "size": bigsizes.reverse() for rooms, sizes in bigsizes: alpharooms.append(rooms) for rooms9 in alpharooms: if self.rooms[rooms9] >= self.mucous.Config["mucous"]["roomlistminsize"]: self.sizedrooms.append(rooms9) self.Format() self.mucous.HotKeyBar() curses.doupdate() except Exception, e: self.mucous.Help.Log("debug", "RoomsList.Mode: " + str(e)) ## Mouse Coordinates in the RoomsList # @param self is RoomsList (Class) # @param x is the horizontal position from the left # @param y is the vertical postion from the top # @param z is unimportant # @param event is the mouse event (button, doubleclick, etc) represented by a number def Mouse(self, x,y,z,event): try: if y == self.mucous.h-5: if x >= 14 and x <= 22: self.mucous.Config["mucous"]["rooms_sort"] = "alpha" self.Mode() elif x >= 24 and x <= 35: self.mucous.Config["mucous"]["rooms_sort"] = "alpha-reversed" self.Mode() elif x >= 37 and x <= 46: self.mucous.Config["mucous"]["rooms_sort"] = "size" self.Mode() elif x >= 47 and x <= 56: self.mucous.Config["mucous"]["rooms_sort"] = "size-reversed" self.Mode() elif x >= self.mucous.w-16: self.mucous.D.RoomList() w = self.dimensions if y >= w["top"] and y < w["top"] + w["height"] and x >= w["left"] and x < w["left"] +w["width"]: y -= w["top"] if y + w["start"] in range(len(self.sizedrooms)): self.scrolling = y + w["start"] self.Format() return self.sizedrooms[self.scrolling] except Exception, e: self.mucous.Help.Log("debug", "RoomsList.Mouse: " +str(e) ) ## Build and display RoomsList border and sort bar # @param self is RoomsList (Class) def DrawWindow(self): try: # Cleanup stale windows if "text" in self.windows: del self.windows["text"] if "border" in self.windows: del self.windows["border"] s = self.dimensions mw = self.windows["border"] = curses.newwin(s["height"]+2, s["width"]+2, s["top"]-1, s["left"]-1) mw.attron(self.mucous.colors["green"]) mw.border() mw.attroff(self.mucous.colors["green"]) mw.addstr(0, 3, "< Room List >", self.mucous.colors["green"] | curses.A_BOLD) pos = 3 sorta = "< " sort = "Sort by:" sortnaz = " Name A-Z" sortnza = " Name Z-A" sorts90 =" Size 9-0" sorts09 =" Size 0-9" quick = self.mucous.Config["mucous"]["rooms_sort"] sortnaz_color = sortnza_color = sorts90_color = sorts09_color = curses.A_NORMAL if quick == "size": sorts90_color = self.mucous.colors["green"] elif quick == "size-reversed": sorts09_color = self.mucous.colors["green"] elif quick == "alpha-reversed": sortnza_color = self.mucous.colors["green"] elif quick == "alpha": sortnaz_color = self.mucous.colors["green"] mw.addstr(self.mucous.h-6, pos, sorta, self.mucous.colors["green"] | curses.A_BOLD) pos += 2 mw.addstr(self.mucous.h-6, pos, sort, curses.A_BOLD) pos += len(sort) mw.addstr(self.mucous.h-6, pos, sortnaz, sortnaz_color | curses.A_BOLD) pos += len(sortnaz) mw.addstr(self.mucous.h-6, pos, " |", self.mucous.colors["green"] | curses.A_BOLD) pos += 2 mw.addstr(self.mucous.h-6, pos, sortnza, sortnza_color | curses.A_BOLD) pos += len(sortnza) mw.addstr(self.mucous.h-6, pos, " |", self.mucous.colors["green"] | curses.A_BOLD) pos += 2 mw.addstr(self.mucous.h-6, pos, sorts90, sorts90_color | curses.A_BOLD) pos += len(sorts90) mw.addstr(self.mucous.h-6, pos, " |", self.mucous.colors["green"] | curses.A_BOLD) pos += 2 mw.addstr(self.mucous.h-6, pos, sorts09, sorts09_color | curses.A_BOLD) pos += len(sorts09) mw.addstr(self.mucous.h-6, pos, " >", self.mucous.colors["green"] | curses.A_BOLD) mw.addstr(self.mucous.h-6, self.mucous.w-15, "< Refresh >", self.mucous.colors["green"] | curses.A_BOLD) self.mucous.SetEditTitle("Join a Room") mw.noutrefresh() tw = self.windows["text"] = mw.subwin(s["height"], s["width"], s["top"], s["left"]) tw.attron(self.mucous.colors["green"]) #tw.border() tw.attroff(self.mucous.colors["green"]) #self.scrolling self.scrolling = 0 tw.noutrefresh() except Exception, e: self.mucous.Help.Log("debug", "RoomsList.DrawWindow: " + str(e)) ## Display one room # @param self is RoomsList (Class) # @param roomitem a string # @param count position in the compacted list # @param start padding required for position to be accurate def Draw(self, roomitem, count, start): try: if count + start == self.scrolling: attrib = curses.A_BOLD | curses.A_REVERSE else: attrib = curses.A_NORMAL num = str(self.rooms[roomitem]) while len(num) < 10: num += " " string = num + self.mucous.dlang(roomitem) if len(string) < self.mucous.w-2: spaces = " " * (self.mucous.w-2 - len(string)) else: string = string[:self.mucous.w-2] spaces = '' self.windows["text"].addstr(string+spaces, attrib) except Exception, e: pass # Always errors #self.mucous.Help.Log("debug", "RoomsList.Draw: " + str(e)) ## Display all rooms; Calls Draw # @param self is RoomsList (Class) def Format(self): try: self.windows["text"].erase() clipped_list, self.scrolling, self.dimensions["start"] = self.mucous.FormatData.scrollbox(self.sizedrooms, self.scrolling, self.mucous.h-7) count =0 for rooms10 in clipped_list: self.Draw(rooms10, count, self.dimensions["start"]) count += 1 self.windows["text"].refresh() except Exception, e: print e self.mucous.Help.Log("debug", "RoomsList.Format: " + str(e)) museek+-0.2+svn20100315.r1208/mucous/pymucous/ConfigParser.py0000644000175000017500000003356011065656035023033 0ustar gandalfgandalf"""Configuration file parser. Modified by hyriand, 2003-2004. A setup file consists of sections, lead by a "[section]" header, and followed by "name: value" entries, with continuations and such in the style of RFC 822. The option values can contain format strings which refer to other values in the same section, or values in a special [DEFAULT] section. For example: something: %(dir)s/whatever would resolve the "%(dir)s" to the value of dir. All reference expansions are done late, on demand. Intrinsic defaults can be specified by passing them into the ConfigParser constructor as a dictionary. class: ConfigParser -- responsible for for parsing a list of configuration files, and managing the parsed database. methods: __init__(defaults=None) create the parser and specify a dictionary of intrinsic defaults. The keys must be strings, the values must be appropriate for %()s string interpolation. Note that `__name__' is always an intrinsic default; it's value is the section's name. sections() return all the configuration section names, sans DEFAULT has_section(section) return whether the given section exists has_option(section, option) return whether the given option exists in the given section options(section) return list of configuration options for the named section read(filenames) read and parse the list of named configuration files, given by name. A single filename is also allowed. Non-existing files are ignored. readfp(fp, filename=None) read and parse one configuration file, given as a file object. The filename defaults to fp.name; it is only used in error messages (if fp has no `name' attribute, the string `' is used). get(section, option, raw=0, vars=None) return a string value for the named option. All % interpolations are expanded in the return values, based on the defaults passed into the constructor and the DEFAULT section. Additional substitutions may be provided using the `vars' argument, which must be a dictionary whose contents override any pre-existing defaults. getint(section, options) like get(), but convert value to an integer getfloat(section, options) like get(), but convert value to a float getboolean(section, options) like get(), but convert value to a boolean (currently case insensitively defined as 0, false, no, off for 0, and 1, true, yes, on for 1). Returns 0 or 1. remove_section(section) remove the given file section and all its options remove_option(section, option) remove the given option from the given section set(section, option, value) set the given option write(fp) write the configuration state in .ini format """ import re import types __all__ = ["NoSectionError","DuplicateSectionError","NoOptionError", "InterpolationError","InterpolationDepthError","ParsingError", "MissingSectionHeaderError","ConfigParser", "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] DEFAULTSECT = "DEFAULT" MAX_INTERPOLATION_DEPTH = 10 from MucousFormat import SortedDict # exception classes class Error(Exception): def __init__(self, msg=''): self._msg = msg Exception.__init__(self, msg) def __repr__(self): return self._msg __str__ = __repr__ class NoSectionError(Error): def __init__(self, section): Error.__init__(self, 'No section: %s' % section) self.section = section class DuplicateSectionError(Error): def __init__(self, section): Error.__init__(self, "Section %s already exists" % section) self.section = section class NoOptionError(Error): def __init__(self, option, section): Error.__init__(self, "No option `%s' in section: %s" % (option, section)) self.option = option self.section = section class InterpolationError(Error): def __init__(self, reference, option, section, rawval): Error.__init__(self, "Bad value substitution:\n" "\tsection: [%s]\n" "\toption : %s\n" "\tkey : %s\n" "\trawval : %s\n" % (section, option, reference, rawval)) self.reference = reference self.option = option self.section = section class InterpolationDepthError(Error): def __init__(self, option, section, rawval): Error.__init__(self, "Value interpolation too deeply recursive:\n" "\tsection: [%s]\n" "\toption : %s\n" "\trawval : %s\n" % (section, option, rawval)) self.option = option self.section = section class ParsingError(Error): def __init__(self, filename): Error.__init__(self, 'File contains parsing errors: %s' % filename) self.filename = filename self.errors = [] def append(self, lineno, line): self.errors.append((lineno, line)) self._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line) class MissingSectionHeaderError(ParsingError): def __init__(self, filename, lineno, line): Error.__init__( self, 'File contains no section headers.\nfile: %s, line: %d\n%s' % (filename, lineno, line)) self.filename = filename self.lineno = lineno self.line = line class ConfigParser: def __init__(self, defaults=None): self.__sections = SortedDict() #self.__sections = {} if defaults is None: self.__defaults = SortedDict() #self.__defaults = {} else: #self.__defaults = {} self.__defaults = SortedDict() for key, value in defaults.items(): self.__defaults[key] = value def defaults(self): return self.__defaults def sections(self): """Return a list of section names, excluding [DEFAULT]""" # self.__sections will never have [DEFAULT] in it return self.__sections.keys() def add_section(self, section): """Create a new section in the configuration. Raise DuplicateSectionError if a section by the specified name already exists. """ if section in self.__sections.keys(): raise DuplicateSectionError(section) self.__sections[section] = {} def has_section(self, section): """Indicate whether the named section is present in the configuration. The DEFAULT section is not acknowledged. """ return section in self.__sections.keys() def options(self, section): """Return a list of option names for the given section name.""" try: opts = self.__sections[section].copy() except KeyError: raise NoSectionError(section) opts.update(self.__defaults) if '__name__' in opts: del opts['__name__'] return opts.keys() def read(self, filenames): """Read and parse a filename or a list of filenames. Files that cannot be opened are silently ignored; this is designed so that you can specify a list of potential configuration file locations (e.g. current directory, user's home directory, systemwide directory), and all existing configuration files in the list will be read. A single filename may also be given. """ if isinstance(filenames, types.StringTypes): filenames = [filenames] for filename in filenames: try: fp = open(filename) except IOError: continue self.__read(fp, filename) fp.close() def readfp(self, fp, filename=None): """Like read() but the argument must be a file-like object. The `fp' argument must have a `readline' method. Optional second argument is the `filename', which if not given, is taken from fp.name. If fp has no `name' attribute, `' is used. """ if filename is None: try: filename = fp.name except AttributeError: filename = '' self.__read(fp, filename) def get(self, section, option, raw=0, vars=None): """Get an option value for a given section. All % interpolations are expanded in the return values, based on the defaults passed into the constructor, unless the optional argument `raw' is true. Additional substitutions may be provided using the `vars' argument, which must be a dictionary whose contents overrides any pre-existing defaults. The section DEFAULT is special. """ d = self.__defaults.copy() try: d.update(self.__sections[section]) except KeyError: if section != DEFAULTSECT: raise NoSectionError(section) # Update with the entry specific variables if vars is not None: d.update(vars) option = self.optionxform(option) try: value = d[option] except KeyError: raise NoOptionError(option, section) if raw: return value return self._interpolate(section, option, value, d) def _interpolate(self, section, option, rawval, vars): # do the string interpolation value = rawval depth = MAX_INTERPOLATION_DEPTH while depth: # Loop through this until it's done depth -= 1 if value.find("%(") != -1: try: value = value % vars except KeyError, key: raise InterpolationError(key, option, section, rawval) else: break if value.find("%(") != -1: raise InterpolationDepthError(option, section, rawval) return value def __get(self, section, conv, option): return conv(self.get(section, option)) def getint(self, section, option): return self.__get(section, int, option) def getfloat(self, section, option): return self.__get(section, float, option) _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, '0': False, 'no': False, 'false': False, 'off': False} def getboolean(self, section, option): v = self.get(section, option) if v.lower() not in self._boolean_states: raise ValueError, 'Not a boolean: %s' % v return self._boolean_states[v.lower()] def optionxform(self, optionstr): return optionstr.lower() def has_option(self, section, option): """Check for the existence of a given option in a given section.""" if not section or section == DEFAULTSECT: option = self.optionxform(option) return option in self.__defaults.keys() elif section not in self.__sections.keys(): return 0 else: option = self.optionxform(option) return (option in self.__sections[section] or option in self.__defaults.keys()) def set(self, section, option, value): """Set an option.""" if not section or section == DEFAULTSECT: sectdict = self.__defaults else: try: sectdict = self.__sections[section] except KeyError: raise NoSectionError(section) sectdict[self.optionxform(option)] = value def write(self, fp): """Write an .ini-format representation of the configuration state.""" if self.__defaults: fp.write("[%s]\n" % DEFAULTSECT) for (key, value) in self.__defaults.items(): fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) fp.write("\n") for section in self.__sections.keys(): fp.write("[%s]\n" % section) keys = self.__sections[section].keys() keys.sort() for key in keys: value = self.__sections[section][key] if key != "__name__": fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) fp.write("\n") def remove_option(self, section, option): """Remove an option.""" if not section or section == DEFAULTSECT: sectdict = self.__defaults else: try: sectdict = self.__sections[section] except KeyError: raise NoSectionError(section) option = self.optionxform(option) existed = option in sectdict if existed: del sectdict[option] return existed def remove_section(self, section): """Remove a file section.""" existed = section in self.__sections.keys() if existed: del self.__sections[section] return existed # # Regular expressions for parsing section headers and options. # SECTCRE = re.compile( r'\[' # [ r'(?P
[^]]+)' # very permissive! r'\]' # ] ) OPTCRE = re.compile( r'(?P