diff options
author | Mario Sergio Fujikawa Ferreira <lioux@FreeBSD.org> | 2006-11-22 02:30:39 +0000 |
---|---|---|
committer | Mario Sergio Fujikawa Ferreira <lioux@FreeBSD.org> | 2006-11-22 02:30:39 +0000 |
commit | 0441b8205f78852ae629b4e203b0aa699268b350 (patch) | |
tree | 292932db31f69b151ccd5a6e6caa7a3c99a8007d /net-p2p | |
parent | c9a026f88077dd34e8d6105aa6e2eaffeed7de9a (diff) | |
download | ports-0441b8205f78852ae629b4e203b0aa699268b350.tar.gz ports-0441b8205f78852ae629b4e203b0aa699268b350.zip |
Notes
Diffstat (limited to 'net-p2p')
-rw-r--r-- | net-p2p/mldonkey-devel/Makefile | 6 | ||||
-rw-r--r-- | net-p2p/mldonkey-devel/files/patch-cvs-2006112100 | 19589 | ||||
-rw-r--r-- | net-p2p/mldonkey-devel/files/patch-src__daemon__driver__driverMain.ml | 11 |
3 files changed, 19592 insertions, 14 deletions
diff --git a/net-p2p/mldonkey-devel/Makefile b/net-p2p/mldonkey-devel/Makefile index 2e2de0bb28b1..cdd6239d59b1 100644 --- a/net-p2p/mldonkey-devel/Makefile +++ b/net-p2p/mldonkey-devel/Makefile @@ -7,7 +7,7 @@ PORTNAME= mldonkey PORTVERSION= 2.8.1 -PORTREVISION= 2 +PORTREVISION= 3 CATEGORIES+= net-p2p MASTER_SITES= ${MASTER_SITE_SOURCEFORGE_EXTENDED} \ ${MASTER_SITE_SAVANNAH} @@ -87,8 +87,8 @@ CONFIGURE_ARGS+=--disable-gd LIB_DEPENDS+= gd.4:${PORTSDIR}/graphics/gd .endif -DOCFILES= Authors.txt Bugs.txt ChangeLog Developers.txt FAQ.html \ - Install.txt Readme.txt Todo.txt ed2k_links.txt +DOCFILES= Authors.txt Bugs.txt ChangeLog Developers.txt \ + Install.txt Todo.txt ed2k_links.txt PORTDOCS= ${DOCFILES} PKGMESSAGE= ${WRKDIR}/pkg-message diff --git a/net-p2p/mldonkey-devel/files/patch-cvs-2006112100 b/net-p2p/mldonkey-devel/files/patch-cvs-2006112100 new file mode 100644 index 000000000000..6a153b3b328d --- /dev/null +++ b/net-p2p/mldonkey-devel/files/patch-cvs-2006112100 @@ -0,0 +1,19589 @@ +Index: config/Makefile.config.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/config/Makefile.config.in,v +retrieving revision 1.62 +retrieving revision 1.63 +diff -u -r1.62 -r1.63 +--- config/Makefile.config.in 26 Aug 2006 12:04:25 -0000 1.62 ++++ config/Makefile.config.in 23 Oct 2006 12:18:27 -0000 1.63 +@@ -58,6 +58,7 @@ + GD_JPG=@GD_JPG@ + GD_PNG=@GD_PNG@ + GD_LIBS=@GD_LIBS@ ++GD_STATIC_LIBS=@GD_STATIC_LIBS@ + GD_CFLAGS=@GD_CFLAGS@ + GD_LDFLAGS=@GD_LDFLAGS@ + +Index: config/Makefile.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/config/Makefile.in,v +retrieving revision 1.169 +retrieving revision 1.173 +diff -u -r1.169 -r1.173 +--- config/Makefile.in 16 Aug 2006 19:08:21 -0000 1.169 ++++ config/Makefile.in 21 Nov 2006 22:29:58 -0000 1.173 +@@ -206,8 +206,6 @@ + $(NET)/cobs.ml \ + $(NET)/terminal.ml + +-# $(NET)/tcpClientSocket.ml +- + XML_SRCS= \ + $(XML)/xml_types.ml $(XML)/xml_parser.mly $(XML)/xml_lexer.mll \ + $(XML)/xml_dtd.ml $(XML)/xmlParser.ml $(XML)/xml.ml +@@ -435,7 +433,6 @@ + $(SRC_BITTORRENT)/bTStats.ml \ + $(SRC_BITTORRENT)/bTTracker.ml \ + $(SRC_BITTORRENT)/bTChooser.ml \ +- $(SRC_BITTORRENT)/bTShare.ml \ + $(SRC_BITTORRENT)/bTClients.ml \ + $(SRC_BITTORRENT)/bTInteractive.ml \ + $(SRC_BITTORRENT)/bTMain.ml +@@ -530,6 +527,9 @@ + + ifeq ("$(GD)", "yes") + GD_LIBS_flags=-cclib "-lgd $(GD_LIBS)" -ccopt "$(GD_LDFLAGS)" ++ ifneq ("$(GD_STATIC_LIBS)", "") ++ GD_STATIC_LIBS_opt=-cclib "-lgd $(GD_STATIC_LIBS)" -ccopt "$(GD_LDFLAGS)" ++ endif + DRIVER_SRCS= \ + $(CDK)/gd.ml \ + $(CDK)/gdstubs.c \ +@@ -1921,7 +1921,7 @@ + ./svg_converter.byte $@ + + .ml.cmx : +- $(OCAMLOPT) $(DEVFLAGS) $(PLUGIN_FLAG) $(OFLAGS) $(INCLUDES) -c $< ++ $(OCAMLOPT) $(DEVFLAGSOPT) $(DEVFLAGS) $(PLUGIN_FLAG) $(OFLAGS) $(INCLUDES) -c $< + + .ml.cmo : + $(OCAMLC) $(DEVFLAGS) $(OFLAGS) $(INCLUDES) -c $< +Index: config/configure.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/config/configure.in,v +retrieving revision 1.283 +retrieving revision 1.288 +diff -u -r1.283 -r1.288 +--- config/configure.in 17 Sep 2006 18:42:57 -0000 1.283 ++++ config/configure.in 31 Oct 2006 15:40:05 -0000 1.288 +@@ -143,7 +143,7 @@ + SYSTEM=hpux + ;; + *darwin*|*rhapsody*|*macosx*) +- SYSTEM=macosx ++ SYSTEM=macos + AC_CHECK_PROG(SED, sed, sed) + if test "$ac_cv_prog_SED" = "sed"; then + FIX_BROKEN_CPP="| sed -n '/^\#pragma/!p'" +@@ -234,7 +234,7 @@ + AC_CHECK_PROG(CUT, cut, cut) + if test "$ac_cv_prog_CUT" = "cut"; then + SUB_VERSION3="CVS" +- if [ test "$SYSTEM" = "freebsd"] || [ test "$SYSTEM" = "netbsd"] || ( [ test "$SYSTEM" = "macosx" ] && [ test "$ac_cv_prog_STAT" != "gstat" ] ); then ++ if [ test "$SYSTEM" = "freebsd"] || [ test "$SYSTEM" = "netbsd"] || ( [ test "$SYSTEM" = "macos" ] && [ test "$ac_cv_prog_STAT" != "gstat" ] ); then + SCM_VERSION=`$STAT -f "%Sm" ./CVS/Entries | $SED -e 's/\(.*\) \(.*\) \(.*\) \(.*\)/\4-\1-\2 \3/'` + else + if [ test "$SYSTEM" = "openbsd"]; then +@@ -279,9 +279,9 @@ + MLDONKEY_VERSION=$MLDONKEY_VERSION.$SUB_VERSION3 + fi + +-REQUIRED_OCAML=3.09.2 ++REQUIRED_OCAML=3.09.3 + DOWNLOAD_OCAML_MAJOR=3.09 +-DOWNLOAD_OCAML=3.09.2 ++DOWNLOAD_OCAML=3.09.3 + + REQUIRED_LABLGTK=1.2.7 + +@@ -477,9 +477,10 @@ + + AC_PATH_PROG(OCAMLC,ocamlc.opt,"",[$LOCAL_DIR/bin:$PATH]) + AC_CHECK_TOOL(OCAMLC,ocamlc,ocamlrun ocamlc) ++AC_PATH_PROG(CAMLP4, camlp4,"",[$LOCAL_DIR/bin:$PATH]) + + BUILD_OCAML=no +-if [ test -z "$OCAMLC" ] || [ test "$REQUIRED_OCAML" = "CVS" ]; then ++if [ test -z "$OCAMLC" ] || [ test -z "$CAMLP4" ] || [ test "$REQUIRED_OCAML" = "CVS" ]; then + BUILD_OCAML=yes + else + OCAMLVERSION=`$OCAMLC -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' ` +@@ -487,7 +488,7 @@ + "$REQUIRED_OCAML"*) ;; + 3.09.0*) ;; + 3.09.1*) ;; +- 3.09.3*) ;; ++ 3.09.2*) ;; + 3.08.4*) ;; + 3.08.3*) ;; + *) +@@ -640,7 +641,7 @@ + "$REQUIRED_OCAML"*) ;; + 3.09.0*) ;; + 3.09.1*) ;; +- 3.09.3*) ;; ++ 3.09.2*) ;; + 3.08.4*) ;; + 3.08.3*) ;; + *) +@@ -789,6 +790,10 @@ + AC_CHECK_PROG(GDLIBCONFIG, gdlib-config, gdlib-config) + if test "$ac_cv_prog_GDLIBCONFIG" = "gdlib-config"; then + GD_LIBS=`$GDLIBCONFIG --libs` ++ $GDLIBCONFIG --static-libs > /dev/null 2>&1 ++ if test "$?" = "0"; then ++ GD_STATIC_LIBS=`$GDLIBCONFIG --static-libs` ++ fi + GD_LIBS2="$LIBS -lgd $GD_LIBS" + LIBS=$GD_LIBS2 + GD_CFLAGS=`$GDLIBCONFIG --cflags` +@@ -1330,11 +1335,6 @@ + echo "---------------------------------------------------------" + fi + +-case "$OCAMLVERSION" in +- 3.06* | 3.07* | 3.08* ) TYPE_FORMAT="";; +- *) TYPE_FORMAT="type ('a,'b,'c) ml_format = ('a,'b, 'c,'c) format4 type ('a,'b,'c) format = ('a,'b, 'c) ml_format";; +-esac +- + if test "$OS_FILES" = "mingw"; then + OCAMLDEP_OPTIONS="-slash" + fi +@@ -1351,7 +1351,6 @@ + AC_SUBST(CXX) + AC_SUBST(FIX_BROKEN_CPP) + AC_SUBST(CONFIG_INCLUDES) +-AC_SUBST(TYPE_FORMAT) + AC_SUBST(OCAMLC) + AC_SUBST(OCAMLLIB) + AC_SUBST(OCAMLOPT) +@@ -1411,6 +1410,7 @@ + AC_SUBST(GD_PNG) + AC_SUBST(GDGRAPHICS) + AC_SUBST(GD_LIBS) ++AC_SUBST(GD_STATIC_LIBS) + AC_SUBST(GD_CFLAGS) + AC_SUBST(GD_LDFLAGS) + AC_SUBST(BZIP2) +Index: config/mldonkey.rc.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/config/mldonkey.rc.in,v +retrieving revision 1.6 +retrieving revision 1.7 +diff -u -r1.6 -r1.7 +--- config/mldonkey.rc.in 19 Jul 2005 19:01:38 -0000 1.6 ++++ config/mldonkey.rc.in 1 Oct 2006 17:46:11 -0000 1.7 +@@ -18,17 +18,16 @@ + { + BLOCK "000004b0" // LANG_NEUTRAL,UNICODE_CP + { +- VALUE "Comments","MLdonkey is distributed under the terms of the GNU General Public License Version 2. Sourcecode is available at http://www.mldonkey.net\000" +- VALUE "CompanyName", "mldonkey team\000" +- VALUE "FileDescription", "MLdonkey - multiuser P2P daemon\000" +- VALUE "FileVersion", "\000" +- VALUE "InternalName", "Counter Counter\000" +- VALUE "LegalCopyright", " (C) 2000-2005 mldonkey team (see README)\000" +- // VALUE "LegalTrademarks"," "\000" +- VALUE "OriginalFilename", "mlnet.exe\000" +- VALUE "ProductName", "MLdonkey - multiuser P2P daemon\000" +- VALUE "ProductVersion", "\000" +- VALUE "SpecialBuild","\000" ++ VALUE "Comments", "MLdonkey is distributed under the terms of the GNU General Public License Version 2. Sourcecode is available at http://mldonkey.sf.net" ++ VALUE "CompanyName", "MLdonkey team, http://mldonkey.sf.net" ++ VALUE "FileDescription", "MLdonkey - multiuser P2P daemon" ++ VALUE "FileVersion", "@MLDONKEY_VERSION@" ++ VALUE "InternalName", "MLdonkey" ++ VALUE "LegalCopyright", "Copyright © 2000-2006 MLdonkey team (see README)" ++ VALUE "OriginalFilename", "mlnet.exe" ++ VALUE "ProductName", "MLdonkey - multiuser P2P daemon" ++ VALUE "ProductVersion", "@MLDONKEY_VERSION@" ++ VALUE "SpecialBuild", "" + } + } + BLOCK "VarFileInfo" +Index: distrib/Authors.txt +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/Authors.txt,v +retrieving revision 1.5 +retrieving revision 1.6 +diff -u -r1.5 -r1.6 +--- distrib/Authors.txt 4 Feb 2006 22:26:36 -0000 1.5 ++++ distrib/Authors.txt 23 Oct 2006 12:58:35 -0000 1.6 +@@ -36,12 +36,9 @@ + Schlumpf + su_blanc + bogeyman +- +-rlimit code taken from Ocaml-annexlib: +-Shawn Wagner <raevnos@pennmush.org> +-http://raevnos.pennmush.org/code/extlib/ +- +-Fasttrack plugin from giFT-fasttrack: +-Markus Kern ++jave + + This product includes GeoLite data created by MaxMind, available from http://maxmind.com/ ++ ++A complete overview about 3rd party libraries used can be found here: ++http://mldonkey.sourceforge.net/3rdParty +Index: distrib/Bugs.txt +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/Bugs.txt,v +retrieving revision 1.2 +retrieving revision 1.3 +diff -u -r1.2 -r1.3 +--- distrib/Bugs.txt 4 Feb 2004 12:13:36 -0000 1.2 ++++ distrib/Bugs.txt 23 Oct 2006 12:58:35 -0000 1.3 +@@ -1,7 +1,9 @@ +-Before feeling a bug report form on +- http://savannah.gnu.org/bugs/?group=mldonkey ++Before sending a bug report form on ++ http://savannah.nongnu.org/bugs/?group=mldonkey + please, check that the bug is not already registered (then, add a comment). + ++To send a feature request please use the Task tracker ++ http://savannah.nongnu.org/task/?group=mldonkey + -------------------------------------------------------------------------- + If you think your bug is a very important one, and nobody has fixed it in a + long time, it might be that we don't have enough information to reproduce it +Index: distrib/ChangeLog +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/ChangeLog,v +retrieving revision 1.1015 +retrieving revision 1.1108 +diff -u -r1.1015 -r1.1108 +--- distrib/ChangeLog 17 Sep 2006 18:42:57 -0000 1.1015 ++++ distrib/ChangeLog 21 Nov 2006 22:34:33 -0000 1.1108 +@@ -14,6 +14,280 @@ + ChangeLog + ========= + ++2006/11/21 ++5579: Remove BasicSocket.[mini|maxi], replace them with Pervasives functions ++- small fix for DonkeyOptions.max_allowed_connected_servers ++5578: Remove unused files ++- src/utils/net/tcpClientSocket.ml ++- src/utils/net/tcpClientSocket.mli ++- src/utils/net/tcpSocket.mli ++5577: EDK: Send correct SUI tags ++- sometimes MLDonkey sent SUI=true tag when CryptoPP was not linked ++ ++2006/11/20 ++5568: EDK: Support CIDR and IP ranges in server_black_list (pango) ++5574: allowed_ips: Fix list usage when 0.0.0.0/0 is part of the list (pango) ++5570: Some log messages in gettext module (Schlumpf) ++5569: HTML: Fix display bug in server list after js popups ++ introduced by patch #5549 (Schlumpf) ++5564: HTML: Remove non-working option use_html_frames (Schlumpf) ++5563: EDK: Fix broken log message when master server changes (Schlumpf) ++ ++2006/11/15 ++5556: web_infos: new option rss_preprocessor used for fixing broken RSS feeds, ++ safer process spawning for command "!" (pango) ++- broken feeds like http://thepiratebay.org/rss.php?cat=D601 can now be parsed ++ directly using (for example) xmllint, if a feed can not be read be MLDonkey ++ its piped through rss_preprocessor and read again ++5560: EDK: Parse some more server.met fields, log unknown server tags ++ ++2006/11/14 ++5424: web_infos/rss: shell:// type url ++5549: EDK: Parse all data from server.met, new HTML javascript popup ++5553: EDK/OV/KAD: small updates (bogeyman) ++- EDK: log unknown client tags with verbosity mct ++- OV/KAD: ignore OvernetPeerNotFound and log number of peers every ++ 60 secs when logging ++5551: web_infos: Fix mtime detection, old files were not updated ++5550: debug_fileinfo: Print [a|c|m]time values ++5530: GD: Improve graph output (skeeve, Schlumpf) ++- html_mods_vd_gfx_h_grid_variable ++ "Stretch hourly until at program start", default true ++- html_mods_vd_gfx_h_grid_time ++ "Max hours on time scale per grid (0 = no limit)", default 0 ++- html_mods_vd_gfx_subgrid ++ "Number of shown subgrids on graph (0 = no subgrids)", default 0 ++ ++2006/11/13 ++5548: New search parameters: "-and", "-or", "-not", removed "-without" ++ (Schlumpf) ++5546: Some sharing updates ++- solved bug 10957, updating the prio of an already shared dir is now possible ++- fix bug where missing shared dirs with strategy incoming_* where not recreated ++- remove "network = []" from downloads.ini, currently not supported ++- created workaround to fix bug on MinGW: no files were shared. Introduced by ++ patch 5475, but source of problem is Ocaml bug 4159 ++5547: HTML: Fix unicode display in vd & upstats javascript popups ++ ++2006/11/12 ++5545: EDK: Do not show empty server message lines in GUI ++5509: Common: Merge file_print functions, ++ BT: print BT-specific source infos in Telnet (thx to jave) ++5544: Clean up code to avoid otags warnings (pango) ++5543: Improve exception handling, fix some indentions (pango) ++5542: CommonSources: Work-around division-by-zero bug in Ocaml ++ on Alpha platform (pango) ++ ++2006/11/09 ++5526: Multiuser: Internal restructuring, new commands ++- from ftp://ftp.berlios.de/pub/mldonkey/pango/userdb-cleanups_v2.patch (pango) ++ - create commonUserDb.mli to protect userdb data from other modules ++ - cleanups ++- replace strings in commonFile with multiuser commonTypes.userdb/groupdb ++- implement security checks when core starts ++ - user "admin" must exist ++ - group "mldonkey" must exist and must have admin status ++- update HTML interface, command "users" ++ - create link to remove a group from a user ++ - create link to change group admin status ++ - new column group members ++- Telnet: Show all data in command "users" ++- do not allow removal of users or groups with downloads, ++ groups with members, user "admin" and group "mldonkey" ++- filter files shown with command "downloaders" ++- fixed bug where wrong group list was displayed in HTML, vd #file_num ++- Display user and groups columns ++ new options html_mods_vd_user & html_mods_vd_group to en-/disable display in HTML, vd ++ - Javascript popups show User:Group infos ++ - Telnet support ++- implement new commands ++ - usergroupadd <user> <group> : add a group to a mldonkey user ++ - usergroupdel user> <group> : remove a group from a mldonkey user ++ - userdgroup <user> <group|None> : change user default group ++ - groupdel <group> : remove an unused mldonkey group ++ - groupadmin <group> <true|false> : change group admin status ++- Restrict commands to admin users: ++ - bw_toggle ++ - enable ++ - disable ++ ++2006/11/06 ++5527: mlguistarter: print correct syntax (fixes Debian bug #396754) ++ ++2006/11/05 ++5481: Overnet: Small updates ++- do not print opcode 18 (OvernetNoResult) as unknown message ++- parse bcp type bcp://xxxxxxxxxxxxxxxxxxxxxxxxxxx:ip:tcpport:udpport ++- print debug log message when a new source was added ++5513: HTML: Let webinterface work in a HTML frame (ported from Knockers Mulus) ++- third button row does not work yet, patches welcome ++5521: BT: Fix non-working EDK upload when BT is enabled, ++ introduced by patch 5461 ++ ++2006/10/31 ++5508: OV/KAD eMule style search and some small fixes (bogeyman) ++5486: UDP bandwidth monitor fix (bogeyman) ++5505: EDK: Support aMule/Hydranode style OS_INFO tag ++ ++2006/10/30 ++5488: Multiuser: New commands ++- groups -> displays groups of the logged-in user ++- dgroup -> displays default group of the logged-in user ++- restrict command "unshare" to admin users ++ ++2006/10/29 ++5475: Multiuser: Implement user_commit_dir ++- directories with strategy incoming_files are shared recursively now ++- support several directories with incoming_* strategies, use the ++ first one with enough space to commit the finished file ++ ++2006/10/27 ++5499: Configure: find camlp4 in local, fix patch 5479 (dunk) ++ ++2006/10/26 ++5477: BT: Add downloads to share list after core restart ++ ++2006/10/25 ++5474: updated bw_toggle ++- fix cosmetic bug, wrong values were displayed ++ as a result if option_hook changed values ++5489: HTML: Improve serverlist display (Schlumpf) ++5487: Update URLs, change mldonkey.net to mldonkey.org ++5485: HTML: Print sharing strategies in command "shares" ++5461: Release slot, fix missing filenames in upstats ++- Each downloading file can be set to status "Release", this can ++ be done by clicking the "R" column in HTML, Transfers or by ++ using the new "release <file_num>" command to toggle the state. ++- A new option max_release_slots exists, default 20% of the default ++ 5 max_upload_slots. This means 1 upload slot is available per default ++ and granted for files with status "Release" when requested. ++- Show filenames in pending upload slots list ++5484: Fix DNS test, test other domains besides www.mldonkey.net ++ ++2006/10/23 ++5474: New command bw_toggle (ported from Knockers Mulus client) ++- two new options: max_hard_upload_rate_2 and max_hard_download_rate_2 ++- new command bw_toggle, quickly switch between two bandwidth options ++5480: Update Mozilla protocol handler, cleanup docs in distrib/ ++5479: Configure: Force presence of camlp4 ++5478: Support gdlib-config --static-libs function (new on Debian Etch) ++5476: Urladd: Change default period to 0 (load file only when core starts) ++ ++2006/10/21 ++5473: Log: Redirect CryptoPP messages to MLDonkey logfile (Schlumpf) ++5472: Urladd: New optional parameter period (in hours) (thx to Schlumpf) ++5471: HTML: New colums for pending slots list: SUI, GeoIP, Filename ++5470: Options: New type percent_option, values are bound to be >= 0 and <= 100 ++5469: HTML: Implement 404 error page for unknown URLs ++ ++2006/10/20 ++5419: EDK: Re-implement titanesel.ws links - service is up again (thx to sk38) ++ ++2006/10/13 ++5458: OV/KAD: tweak and bugfix the search (bogeyman) ++ ++2006/10/12 ++5454: OV/KAD: Block blocked ips + small Overnet updates (bogeyman) ++5451: HTML: clickable new messages indicator (jave) ++ ++2006/10/09 ++5428: HTML: Fix style sheet errors (rwruck) ++5446: EDK: Small update for EDK publish patch 5430 ++- new option max_published_files ++ maximum number of files published to servers per minute, eMule default 200 ++- bug fix for patch 5430, publish also to non-preferred servers ++ ++2006/10/08 ++5430: EDK: Improve file publishing ++- publish no more than 200 files/minute to avoid server-side blacklisting, ++ eMule uses the same limit, ++ least published files are published first (thx to pango) ++- respect server hard_limit, never publish more files to servers ++- HTML: in server list display number of files published by server, by clicking ++ on this number the list of files is displayed (new command server_shares num) ++- HTML: diplay master server status, only master server are used for publishing ++- HTML: in upstats display number of server the file was published to, ++ also display server name + IP in javascript popup ++- bug fix: properly update DonkeyGlobals.master_server to be used in ++ DonkeyClient.read_first_message, this is used when replying to non-Overnet ++ clients so they know to which server MLdonkey is connected to, ++- remove development option become_master_delay ++5445: Self-test charset conversion, disable conversion if test fails ++5444: BT: Correctly display client connected time, ++ also allow correct upload speed calculation (tradie) ++5443: BT: do not allow connections with ourselves (tradie) ++ ++2006/10/06 ++5442: BT: Verbose error messages when torrent is sent from GUI, ++ BT-multiuser: Protect command seeded_torrents ++5441: BT: Re-enable all trackers when file is resumed ++5440: HTML: Strip CR from multiline dllink input to fix FileTP filenames ++ ++2006/10/03 ++5439: Increase required ocaml version to 3.09.3 (schlumpf), ++ remove old TYPE_FORMAT stuff needed for Ocaml < 3.06 (pango) ++ ++2006/10/02 ++5297: In addition to previous patch 5297 force conversion of allowed_ips to ++ IP blocklist when $MLDONKEY_DIR points to an existing directory ++ and ini files are created for the first time ++5429: Fix compile bug in Ocaml 3.08.3 (thx to eike for reporting) ++ ++2006/10/01 ++5404: New command porttest, support for eMule- and Azureus-style porttest ++ (thx to pango for Azureus result parsing) ++5421: HTML: Add "Users" to options frame (unease) ++5429: New common lprintf_file_nl function ++5432: Updates and fixes for the Win resource file (schlumpf) ++ ++2006/09/26 ++5407: BT: Improve handling of tracker error messages ++- print additional information in telnet, vd #num already present in HTML ++- show tracker errors in Telnet and HTML popups over tracker info lines ++- pause torrents with no valid trackers left ++5336: EDK: Fix lowid support (krissn) ++5427: EDK: Increase hash speed when threads are available (pango) ++ ++2006/09/25 ++5426: CommonSources: Reduce CPU load when refilling queues ++ of many non-BT downloads (pango) ++5425: FileTP: Support options file_started_cmd and pause_new_downloads ++5423: Print warning for empty admin password only if allowed_ips was altered ++5422: Command "sources": Display only downloading files ++ ++2006/09/24 ++5405: BT: Use field "encoding" from .torrent to convert strings to UTF-8 ++5419: EDK: Remove titanesel.ws links ++5418: Gnutella/G2/Fasttrack: Support client_bind_addr ++5417: Edonkey comments: Telnet support, UTF-8 output in HTML ++5416: Makefile.in: New variable $DEVFLAGSOPT for .cmx files (jave) ++5415: New command option: rem disc - remove all disconnected servers ++ ++2006/09/23 ++5414: Edonkey comments, update gui prot, some bt peer ids (zet) ++- Reimplement edonkey file comments with ratings (ro) ++ (fixes exploitable DOS introduced in patch #5371) ++- Add options "comments_filter", "max_comments_per_file", "max_comment_length" ++- GUI protocol updated for comments, stats, libmagic, users/groups ++- BT: Identify some more peer ids, as well as the reserved bits ++- Fix some html code and other bugs ++- Minor code cleanup ++ ++2006/09/22 ++5411: Portinfo: Rename gift_port to gift_port GUI ++ ++2006/09/19 ++5406: Multiuser: Small bug fixed in recover_temp (mu2.patch) ++5406: Main multiuser patch, see docs/multiuser.txt for details ++ thx to jave, pango, zet and many other people who have helped ++ to make this work possible ++- this patch is experimental, if it breaks, you can keep the pieces;-) ++- multigroup_usercommit.patch and multigroup_su.patch are not included ++- this patch is still not finished, the To-Do list in docs/multiuser.txt ++ is still long, also GUI protocol updates have to be implemented. ++ To manage users, groups and files, its best to use the HTML interface, ++ multiuser commands can also be used in Telnet interface. ++------------------------------------------------------------------------------- + 2006/09/17 version 2.8.1 = tag release-2-8-1 + 5401: Fix question whether to compile Ocaml with some bash versions (pango) + 5400: Allow use of Ocaml 3.09.3, keep 3.09.2 as default +Index: distrib/Developers.txt +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/Developers.txt,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -r1.7 -r1.8 +--- distrib/Developers.txt 2 May 2005 17:50:33 -0000 1.7 ++++ distrib/Developers.txt 23 Oct 2006 12:58:35 -0000 1.8 +@@ -1,5 +1,5 @@ + +- Yes, we are looking for developpers. If you have some programming skills, ++ Yes, we are looking for developers. If you have some programming skills, + you are welcome to help the development of mldonkey. There are different ways + to help us. You can see a bug, or a missing feature, and fix it, and then + send us a patch. If you want to spend more time, you can fix a lot of bugs, or +Index: distrib/FAQ.html +=================================================================== +RCS file: distrib/FAQ.html +diff -N distrib/FAQ.html +--- distrib/FAQ.html 2 May 2005 17:50:33 -0000 1.6 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,1129 +0,0 @@ +-<html> +-<head> +-<META name="robots" content="noindex, nofollow"> +-<title>mldonkey FAQ</title> +-</head> +-<body bgcolor="#ffffff"> +- +- +-<center> +- +- <H1> <a href=index.html> mldonkey </a>: Frequently Asked Questions </H1> +- +- +- <a href=faq.html> English </a>, +- <a href=faq.html.de> Deutsch </a>, +- <a href=faq.html.fr> Francais </a> +-o <a href=faq.html.es>Español</a> +- +-</center> +- +-<ol> +- +- <a name=index_back> +- <li> <h3> Background </h3> </a> +- <ol> +- <li> <strong> <a href=#back1> What is mldonkey ? </a> </strong> +- <li> <strong> <a href=#back2> Why use Objective-Caml to program mldonkey ? </a> </strong> +- <li> <strong> <a href=#back3> Where can I find support on mldonkey ? </a> </strong> +- <li> <strong> <a href=#back4> Where can I download mldonkey ? </a> </strong> +- <li> <strong> <a href=#back5> Where can I find mldonkey for Windows ? </a> </strong> +- <li> <strong> <a href=#back6> What's new with MLdonkey 2.00 ? </a> </strong> +- </ol> +- +- <a name=index_run> +- <li> <h3> Running mldonkey </h3> </a> +- +- <ol> +- <li> <strong> <a href=#run1> How should I start mldonkey ? </a> </strong> +- <li> <strong> <a href=#run2> How can I control mldonkey ? </a> </strong> +- <li> <strong> <a href=#run3> Where/what are mldonkey configuration files ? </a> </strong> +- <li> <strong> <a href=#run4> How can I control the bandwidth used by mldonkey ? </a> </strong> +- <li> <strong> <a href=#run5> When I modify an option in the config file, mldonkey overwrites it with its old value ? </a> </strong> +- <li> <strong> <a href=#run6> Where are the files I have downloaded with mldonkey ? </a> </strong> +- <li> <strong> <a href=#run7> Why does MLdonkey automatically pause some of my downloads ? </a> </strong> +- <li> <strong> <a href=#run8> How can I reach mldonkey WEB interface +-if my firewall only allows port 80 ? </a> </strong> +- </ol> +- +- <a name=index_gui> +- <li> <h3> Running the GTK GUI </h3> </a> +- +- <ol> +- <li> <strong> <a href=#gui1> When I start the GUI, I get a lot of error messages, and the GUI terminates with a "Segmentation Fault" ? </a> </strong> +- <li> <strong> <a href=#gui2> I can't connect to mldonkey from a remote host, neither by telnet, WWW or the GUI ? </a> </strong> +- <li> <strong> <a href=#gui3> The GUI is immediatly disconnected from the core, or keeps connecting and disconnecting very fast ? </a> </strong> +- </ol> +- +- <a name=index_build> +- <li> <h3> Building mldonkey </h3> </a> +- +- <ol> +- <li> <strong> <a href=#build1> How can I download the latest sources of mldonkey ? </a> </strong> +- <li> <strong> <a href=#build2> What do I need to compile mldonkey on my system ? </a> </strong> +- <li> <strong> <a href=#build3> How do I compile mldonkey on my system ? </a> </strong> +- <li> <strong> <a href=#build4> I just updated from CVS, and I get an error while compiling ? </a> </strong> +- </ol> +- +- <a name=index_edonkey> +- <li> <h3> The eDonkey plugin </h3> </a> +- +- <ol> +- <li> <strong> <a href=#edonkey0> What is the eDonkey network ? </a> </strong> +- <li> <strong> <a href=#edonkey1> Why can't mldonkey connect to any server ? </a> </strong> +- <li> <strong> <a href=#edonkey2> How can I import my old edonkey files under mldonkey ? </a> </strong> +- <li> <strong> <a href=#edonkey3> Where can I find files on eDonkey without searching on servers ? </a> </strong> +- <li> <strong> <a href=#edonkey4> Why does mldonkey only connect 5 +-servers, whereas <tt> max_connected_servers </tt> is greater ? </a> </strong> +- <li> <strong> <a href=#edonkey5> What's about Overnet ? </a> </strong> +- <li> <strong> <a href=#edonkey6> I'm behind a firewall, what should I do ? </a> </strong> +- <li> <strong> <a href=#edonkey7> How can I share multiple directories ? </a> </strong> +- </ol> +- +- <a name=index_soulseek> +- <li> <h3> The Soulseek plugin </h3> </a> +- +- <ol> +- <li> <strong> <a href=#slsk0> What is the Soulseek network ? </a> </strong> +- <li> <strong> <a href=#slsk1> I can't connect to the server ? </a> </strong> +- </ol> +- +- <a name=index_limewire> +- <li> <h3> The LimeWire plugin </h3> +- +- <ol> +- <li> <strong> <a href=#limewire0> What is the LimeWire network ? </a> </strong> +- <li> <strong> <a href=#limewire1> When I search for files, I receive unrelated results ? </a> </strong> +- </ol> +- +- <li> <h3> The other plugins </h3> +- <ol> +- <li> <strong> <a href=#other1> What is the current Development Status of +-mldonkey plugins ? </a> </strong> +- </ol> +-</ol> +- +- +- +- +- +- +-<ol> +- +-<hr> +-<li> <h2> Background </H2> +- +-<ol> +- <li> <h3> <a name=back1> What is mldonkey ? </a> </h3> +- +-Formerly, mldonkey was a Linux client for the eDonkey network, built from +-a reverse-engineered version of the protocol. Now, it is also able to connect +-to multiple networks (gnutella, Direct-Connect, Soulseek, etc). +- +-<p> +-MLdonkey runs on most Unix clones: Linux (x86, alpha, sparc), Mac OS X, +-Solaris x86, Free/OpenBSD, etc. +- +-<p> +- +-It runs as a daemon, in the background, that can run, downloading and sharing +-files, 24 hours a day. You can interact with your mldonkey daemon, either +-locally or remotely, by a telnet interface, a WEB interface or a GTK +-GUI. Other GUIs have also been created for mldonkey. +- +- <li> <h3> <a name=back2> Why use Objective-Caml to program mldonkey ? </a> </h3> +- +-Most programmers associate functional languages with languages theory, +-lambda-calculus, recursivity, and non-mutable variables, ie the most +-boring lectures at university. +- +-<p> +- Fortunately, Objective-Caml is not only a functional language: it also +-provides many other programming paradigms, such as imperative constructs +-(<tt>while</tt> and <tt>for</tt> loops, mutable variables, records), +-object-oriented constructs, and a powerful C-interface. Moreover, its +-native-code compiler carefully checks your program for typing errors (no +-more segfaults !), and produces a very efficient code (even faster than C +-on many examples). +- +-<p> +-If you don't believe me, check the following links: +-<ul> +-<li> +-<a href="http://www.bagley.org/~doug/shootout/"> A comparison between many languages </a> +-<li> Two Objective Caml programs win first and second prizes at the +-<a href="http://www.cs.cornell.edu/icfp/"> ICFP'2000 programming contest </a>, +- to write a ray-tracer program. +-<li> An Objective Caml program ranks first at the +-<a href="http://www.cs.virginia.edu/~jks6b/icfp/"> ICFP'1999 programming contest </a>. +-</ul> +- +-<li> <h3> <a name=back3> Where can I find support on mldonkey ? </a> </h3> +- +-The first place is the +-<a href="http://savannah.nongnu.org/projects/mldonkey/"> MLdonkey Project Site</a> +- where you will find: +- +-<ul> +- <li> The <a href="mailto:mldonkey-users@mail.freesoftware.fsf.org"> +-mldonkey-users@mail.freesoftware.fsf.org </a> Mailing-list, where you can +-either contact the developpers or ask other users for advises. +- <li> The +-<a href="http://savannah.nongnu.org/bugs/?group=mldonkey"> Bug Report +-system </a> +- to report bugs appearing when running mldonkey. +- <li> The +-<a href="http://savannah.nongnu.org/support/?group=mldonkey"> +-Support system </a> where you can ask for support in running mldonkey. +-</ul> +- +-<p> You can also try to read/post in the +-<a href="http://www.mldonkeyworld.com/"> +- MLDonkey Forums </a> or on the IRC Channel #mldonkey on irc.freenode.net . +- +- <li> <h3> <a name=back4> Where can I download mldonkey ? </a> </h3> +- +-The latest stable binaries can be downloaded from the +-<a href="http://savannah.nongnu.org/download/mldonkey/"> +- Project Download page </a>. +- +-If you want a more recent version of mldonkey, you need to check out the +-sources from the CVS, and compile it yourself. +-See <a href=#index_build> Building Mldonkey </a> for help. +- +-<li> <h3> <a name=back5> Where can I find mldonkey for Windows ? </a> </h3> +- +-MLdonkey does not run very well under Cygwin on Windows. Moreover, there +-is no native port of MLdonkey to Windows, so you will not be able to run +-it on these systems. +- +- <p> +- +- If you have some knowledge of MinGW, you might try to compile +- Objective-Caml and mldonkey. It would probably run better than under +- Cygwin. Note that mldonkey does not use threads, and the select call +- is only used to descriminate between sockets (read/write/connect/accept). +- +-<li> <h3> <a name=back6> What's new with MLdonkey 2.00 ? </a> </h3> +- +- If you were using MLdonkey 1.16, you might be interested in knowing what is new +- with MLdonkey 2.00. Here are the main improvements: +- +- <ul> +- +- <li> Overnet support. +- <li> Completely New GUI: upload panel, icons, configuration of many options. +- <li> Post-filtering of results (see Overnet or LimeWire plugins). +- <li> Multi-networks support (sources and CVS only). +- <li> Many bug fixes: memory leak, "too many open file descrs", sharing, +- upload/download rates, ... +- </ul> +- +-</ol> +- +-<hr> +-<li> <h2> Running mldonkey </H2> +- +-<ol> +- +-<li> <H3> <a name=run1> How should I start mldonkey ? </a> </H3> +- +-mldonkey will install its configuration files in the directory where you +-start it. So, first, choose the right directory, where you want it to run. +-Then, copy the "servers.ini" file that is in the distribution in this +-directory. I suppose here that mldonkey is started in its directory. +- +-Start mldonkey with: +- +-<pre> +-./mldonkey +-</pre> +- +-It should display some debug info. DON'T CLOSE THE TERMINAL WHERE IT +-WAS STARTED, otherwise it might get blocked on terminal output. +- +- Once you are sure it works correctly, you can dump the debug info in a file +-(but it can become very big) or better in /dev/null, the next time you +-start it: +- +-<pre> +-./mldonkey &> /dev/null +-</pre> +- +-<li> <h3> <a name=run2> How can I control mldonkey ? </a> </h3> +- +- You have three different ways to control the mldonkey daemon (note that, by +-default, mldonkey is configured to accept only control connections from the +-host running mldonkey (<a href=#gui2> more info </a>)): +- +-<ul> +- +-<li> The telnet interface: the telnet interface allows you to control +-mldonkey locally or remotely from a terminal with simple commands: +-<pre> +-telnet localhost 4000 +-</pre> +-where <tt> localhost </tt> is the host running mldonkey and 4000 is the +-default port for the telnet interface. +- +-<p> +-There are many commands available. Use the <tt> help </tt> command to +-display all of them. +- +-<li> The WEB interface: the WEB interface allows you to control mldonkey +-through your favorite navigator. The default url is: +- +-<pre> +-http://localhost:4080/ +-</pre> +- +-where <tt> localhost </tt> is the host running mldonkey and 4080 is the +-default port for the WEB interface. +- +-<li> The GTK GUI: you can use a powerful GTK interface to control mldonkey. +-It is called <tt> mldonkey_gui </tt>. Use the File::Setting menu to +-configure how to connect to your mldonkey daemon. +- +- +-</ul> +- +-<li> <h3> <a name=run3> Where/what are mldonkey configuration files ? </a> </h3> +- +-MLdonkey creates its configuration files in the directory where it is +-started. All of them terminate with a .ini extension. You should not +-modify them while mldonkey is running since it periodically overwrites +-them. Instead you should either stop it, or modify the options using one of +-the interfaces. +- +-<ul> +- +-<li> <tt> downloads.ini </tt> : the basic options (and historically, the +-edonkey plugin options) +-<li> <tt> files.ini </tt> : the list of files being currently downloaded, +-and informations needed to recover the download after a stop. +-<li> <tt> servers.ini </tt> : the list of all known servers for all networks. +-<li> <tt> friends.ini </tt> : the list of your friends (peers you like to +-browse files) on all networks. +- +-<li> Other configuration files are used by mldonkey plugins, normally one +-per network. +- +-<li> <tt> ~/.mldonkey_gui.ini </tt> : the GUI configuration file is the +-only one which is not stored in the mldonkey directory. +- +-</ul> +- +-<li> <h3> <a name=run4> How can I control the bandwidth used by mldonkey ? </a> </H3> +- +-There are two options in the downloads.ini file: max_hard_upload_rate and +-max_hard_download_rate. Setting these options to 0 means that there are no +-limits. They are expressed in kilobytes/second (not kilobits/second !). +-The upload limit both applies to the files which are downloaded from you, +-and for the messages you send to ask for files: be careful not to limit +-your upload too much ! +- +-<p> +-<table border="1" cellpadding="5" align=center> +- +-<tr> +-<td align=center> </td> +-<td align=center colspan=2> Low Bandwidth Usage </td> +-<td align=center colspan=2> High Bandwidth Usage </td> +-</tr> +- +-<tr> +-<td align=center> Your Connection Type </td> +-<td align=center> max_hard_upload_rate </td> +-<td align=center> max_hard_download_rate </td> +-<td align=center> max_hard_upload_rate </td> +-<td align=center> max_hard_download_rate </td> +-</tr> +- +-</tr> +-<td> T1 and more </td> +-<td align=center> 50 </td> +-<td align=center> 0 </td> +-<td align=center> 0 </td> +-<td align=center> 0 </td> +-</tr> +- +-</tr> +-<td> Cable/ADSL 512/128 kbs </td> +-<td align=center> 2 </td> +-<td align=center> 6 </td> +-<td align=center> 6 </td> +-<td align=center> 12 </td> +-</tr> +- +-</tr> +- +-If you have troubles with these values, find the best one corresponding to +-your link and send us the information to fix this table ! +- +-</table> +- +-<li> <h3> <a name=run5> When I modify an option in the config file, mldonkey overwrites it +-with its old value ? </a> </h3> +- +-There are two cases: +-<ul> +- <li> When you edit a config file, mldonkey must not run. Indeed, mldonkey +-saves its configuration periodically, overwritting any changes made in the +-files. +- <li> Be careful with the syntax. If mldonkey can't parse the config file, it +-will generate a new file with the old values for all options that couldn't +-be read correctly. You must remember that you must put filenames and directory +-names between "" (they are not always required if there are no special +-characters inside the name (such as spaces, slashes, etc...), so mldonkey can +-remove them in some cases). +- +-</ul> +- +-<li> <h3> <a name=run6> Where are the files I have downloaded with +-mldonkey ? </a> </h3> +- +-Files being currently downloaded are temporarily stored in the <tt> +-temp/ </tt> directory at the level of mldonkey config files. Files in this +-directory are identified by their MD4 (for edonkey files) or by a random +-identifier. +- +-<p> When the download of a file is finished, the file is added to a list +-of files that have to be "committed". These files are not renamed, so they +-are still kept in the temp directory. +- +- <p> To move finished downloads into the <tt> incoming/ </tt> directory with +-their final name, you need to use the command <tt> commit </tt> in the +-telnet, or the "Save" buttons in the GUI. +- +-<li> <h3> <a name=run7> Why does MLdonkey automatically pause some of my downloads ? </a> </h3> +- +- When mldonkey receives data for a file from the network, it tries to write +-this data in the file in the temp/ directory. If, for some reason, it cannot +-write the data, it immediatly pauses the file. +- +-<p> This can happen for different reasons: +- +-<ul> +- <li> You have no space left on the disk. You can verify it with the "df" +- command, and free some space for the downloads to continue. +- <li> The user running mldonkey has not write permissions to write in +- your temp/ directory, or on the file temporary file. +-</ul> +- +-<li> <h3> <a name=run8> How can I reach mldonkey WEB interface +-if my firewall only allows port 80 ? </a> </h3> +- +-If you want to access the WEB interface (port 4080) through a firewall, +-and the firewall only allows port 80, and you don't want to run mldonkey +-as root, you can set up the apache WEB server to redirect requests to mldonkey: +- +-<ul> +- <li> Edit your "httpd.conf" : Insert the following lines somewhere in the +-"Section 2: main server configuration": +- +-<pre> +-RewriteEngine on +-ProxyRequests on +-RewriteRule /mldonkey http://localhost:4080/$1 [P,L] +-RewriteRule /(submit.*) http://localhost:4080/$1 [P,L] +-RewriteRule /(files.*) http://localhost:4080/$1 [P,L] +-ProxyPassReverse /(.*) http://localhost:4080/$1 +-</pre> +- +-where, of course, <tt> localhost </tt> is your mldonkey host, and <tt> +-4080 </tt> is the <tt> http_port </tt> of mldonkey. +- +-<li> With these rules, <tt> http://aaa.bbb.ccc.ddd/mldonkey </tt>, +-where <tt>aaa.bbb.ccc.ddd</tt> is the IP address of the host running the +-apache server, should connect to your mldonkey. +- +-</ul> +- +-</ol> +- +-<hr> +-<li> <h2> Running the GTK GUI </h2> +- +-<ol> +- +-<li> <H3> <a name=gui1> When I start the GUI, I get a lot of error messages, +-and the GUI terminates with a "Segmentation Fault" ? </a> </H3> +- +-You probably use the "static" binary of mldonkey GUI. Download the +-"shared" binaries from this site, and use that GUI. This bug is caused by +-an incompatibility between the statically linked GTK library and the +-themes installed on your distribution. +- +-<li> <H3> <a name=gui2> I can't connect to mldonkey from a remote host, +-neither by telnet, WWW or the GUI ? </a> </H3> +- +-You need to modify the "allowed_ips" option in the downloads.ini file to +-define which hosts are allowed to control your mldonkey core. +- +-You can either stop mldonkey, and edit the downloads.ini file: +- +-<pre> +-allowed_ips = ["127.0.0.1"; "192.168.255.255"; "65.64.43.63"] +-</pre> +- +-for example allows your localhost (127.0.0.1) to control mldonkey, all the +-hosts from the network 192.168.*.*, and the host 65.64.43.63. +- +-If you don't want to stop mldonkey, just connect locally with the telnet, +-and type: +- +-<pre> +-set allowed_ips "127.0.0.1 192.168.255.255 65.64.43.63" +-</pre> +- +-<li> <h3> <a name=gui3> The GUI is immediatly disconnected from the core, or keeps connecting and disconnecting very fast ? </a> </h3> +- +-There are two cases to consider: +- +-<ul> +- +-<li> You have set a password in the core to access it through the GUI: you +-must enter the password in the GUI too (use the <tt> Files :: Settings +-</tt> menu for that. +- +- <li> You have two GUIs connected to the same core: the core (1.99 and +-later) only allows one GUI to be connected simultaneously. When you connect, +-it immediatly disconnects the previous GUI. This can only work if the +-previous GUI does not try to reconnect automatically: <tt> mldonkey_gui </tt> +-will not reconnect, but maybe you are using another contributed GUI, which +-tries to automatically reconnect, and make the core disconnect your GUI ? +-Then, tell its author to remove this feature or to put an option to disable +-it. +- +-</ul> +- +-</ol> +- +-<hr> +-<li> <h2> Building mldonkey </h2> +- +-<ol> +- <li> <h3> <a name=build1> How can I download the latest sources +-of mldonkey ? </a> </h3> +- +- Checkout the sources of mldonkey from the +-<a href="http://savannah.nongnu.org/cvs/?group_id=1409"> CVS repository </a>. +-Here is the standard procedure: +- +-<!-- +-or download the +-<a href="http://freesoftware.fsf.org/cvs.backups/mldonkey.tar.gz"> +-CVS tarball +-</a> (be careful, . +---> +- +-<pre> +-cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/mldonkey login +-(password: just hit enter) +-cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/mldonkey co mldonkey +-</pre> +- +- +- <li> <h3> <a name=build2> What do I need to compile mldonkey on my system ? </a> </h3> +- +- You need ocaml-3.06 (<a href="http://caml.inria.fr/ocaml/distrib.html"> +-Ocaml site </a>) installed +- for the code +- and <a href="http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk.html"> +-lablgtk </a> compiled in native code with ocaml 3.06 for the GTK gui. +- +-The easiest way is to download binaries for your system. Otherwise, use +-the following lines to install them: +- +-<ul> +- <li> Installing Objective-Caml 3.06 in <tt>/usr/local/bin</tt> +- +-<pre> +- ~/tmp> tar zxf ocaml-3.06.tar.gz +- ~/tmp> cd ocaml-3.06 +- ~/tmp/ocaml-3.06> ./configure +- ~/tmp/ocaml-3.06> make world opt opt.opt +- ~/tmp/ocaml-3.06> make install +-</pre> +- +-<li> LablGTK 1.2.3 for example +- +-<pre> +- ~/tmp> tar zxf lablgtk-1.2.3.tar.gz +- ~/tmp> cd lablgtk-1.2.3 +- ~/tmp/lablgtk-1.2.3> make configure +- ~/tmp/lablgtk-1.2.3> make +- ~/tmp/lablgtk-1.2.3> make opt +- ~/tmp/lablgtk-1.2.3> make install +-</pre> +- +-Do not forget the "make opt" which is required to get a native-code version +-of the libraries. +- +-</ul> +- +- <li> <h3> <a name=build3> How do I compile mldonkey on my system ? </a> </h3> +- +- +- To compile the core, you just need to use the following commands: +- <pre> +- cd mldonkey +- ./configure [...options...] +- make depend +- make +- </pre> +- +- You should now have a "mldonkey" executable ... No "make install" is +- provided. +- +-<p> If you get strange errors from make, you probably need to use "gmake" +-on your system. +- +- <p> +- You can use the following options for the configure: +-<ul> +-<li> --enable-batch : install Objective-Caml and Lablgtk locally just +-to be able to compile mldonkey. +-<li> --disable-multinet: allows you to only compile support for edonkey +-<li> --disable-opennap: allows you to remove support for Open Napster +-<li> --disable-limewire: allows you to remove support for LimeWire +-<li> --disable-directconnect: allows you to remove support for Direct Connect +-<li> --disable-soulseek: allows you to remove support for SoulSeek +-<li> --disable-openft: allows you to remove support for OpenFT +-<li> --disable-donkey: allows you to remove support for eDonkey +-</ul> +- +-<li> <h3> <a name=build4> I just updated from CVS, and I get an error while compiling ? </a> </h3> +- +- After an update from the CVS, you MUST restart the configure script, and +-rebuild the dependencies: +- +- <pre> +- cd mldonkey +- cvs login +- cvs -z3 up -d +- ./configure [...options...] +- make depend +- make +- </pre> +- +- If the error messages are related to camlzip, a workaround is to edit +-manually the file <tt> config/Makefile.config </tt>, and to change the line +-<tt> ZLIB=no </tt> and <tt> SOULSEEK=no </tt> (only soulseek requires +-camlzip). +- +-</ol> +- +-<hr> +-<li> <h2> The eDonkey plugin </h2> +- +-<ol> +- +-<li> <H3> <a name=edonkey0> What is the eDonkey network ? </a> </H3> +- +- The <a href="http://www.edonkey2000.com"> eDonkey network </a> is a network +-specialized in sharing really big files (between 0 to 1.4 GB). +- +-<p> +-This network offers a set of characteristics that cannot be found on other networks: +-<ul> +- <li> Files can be downloaded from different sources at the same time. +- <li> Files are identified by uniq identifiers (MD4) that can be used to find +- new sources for a file. +- <li> Files are checked using checksums, and chunks (9MB) that are corrupted +- are downloaded again. +- <li> Files chunks can be downloaded in any order. +- <li> Files are shared as soon as a checked chunk has been downloaded, to +- speed up the spreading of files. +-</ul> +- +-<li> <H3> <a name=edonkey1> Why can't mldonkey connect to any server ? </a> </H3> +- +-<ul> +- <li> First, check that you have some servers known by mldonkey: connect +-to mldonkey by telnet (telnet localhost 4000) and use the "vma" command to +-display all known servers. +- <li> If you have no servers known, you need to add a list of servers for +-mldonkey to connect to them: you can either: +- <ul> +- <li> Manually add them in the telnet :) +- <li> Import a Windows server.met file (downloaded on the WEB): +- use the "servers" command in the telnet, for example: +- +-<pre> +-servers "/mnt/windows/bureau/edonkey server list/server.met" +-</pre> +- +- <li> You can restart mldonkey after copying the "servers.ini" file that +-was in the distribution. +- </ul> +- <li> If you already have a list of known servers, maybe the list is not +-up-to-date, and maybe the servers are full. Try to add some new servers +-like above. +-</ul> +- +-<li> <h3> <a name=edonkey2> How can I import my old edonkey files under mldonkey ? </a> </h3> +- +-<ul> +- <li> Use the <tt>import</tt> command with the donkey directory containing your old +-config as parameter. If your old edonkey was running on +-<tt> /home/bidule/edonkey </tt> (mldonkey should find a "pref.met" file in +-this directory), you can send the following command on mldonkey console: +- +-<pre> +-import "/home/bidule/edonkey" +-</pre> +- +- <li> This might not work for a Windows configuration, since the absolute +-path under windows is not the same as under linux (different mount points). +-In this case, retry this command, but before, make sure that: +- +-<ul> +- <li> The files you want to import are in a "temp" directory, inside the +-directory you give to the "import" command. +- <li> Remove the "pref.met" file, so that mldonkey does not try to use the +-temp directory specified in this file. +-</ul> +- +-</ul> +- +- <li> <h3> <a name=edonkey3> Where can I find files on eDonkey without searching on servers ? </a> </h3> +- +- There are two kinds of WEB sites where you can find <a href=#edonkey4> +-ed2k:// links </a> for files on eDonkey: +- +-<ul> +- <li> Recommandation Engines: on these Web sites, you will find commented +- files, that you are sure to find complete on the eDonkey network. +- +-<ul> +- <li> The most famous is <a href="http://www.sharereactor.com"> Share +-Reactor </a> +-</ul> +- +- <li> Search Engines: these Web sites allow you to search for files +-that are currently available on some servers. However, you have no +-guaranty they are complete: +- +-<ul> +- <li> <a href="http://www.filedonkey.com"> File Donkey </a> +- <li> <a href="http://www.jigle.com"> Jigle </a> +-</ul> +-</ul> +- +- <li> <h3> <a name=edonkey4> Why does mldonkey only connect 5 +-servers, whereas <tt> max_connected_servers </tt> is greater ? </a> </h3> +- +-Being connected to several servers can be useful to spread the files you +-are uploading, and to initially find more sources to begin a download. +- +-<p> However, each server on which you are connected is not available for +-another user. For this reason, you should not stay connected to too many +-servers. mldonkey enforces this policy by limiting to 5 the number of +-servers on which you remain connected for a long time, whatever the value +-of <tt> max_connected_servers </tt> is. +- +-<p> Thus, you should simply see <tt> max_connected_servers </tt> as the +-number of servers mldonkey tries to connect concurrently to at the +-startup, not as the number of servers he will remain connected to. +- +-<li> <h3> <a name=edonkey5> What's about Overnet ? </a> </h3> +- +- <a href="http://www.overnet.com"> Overnet </a> is a network using the same +- download protocol as edonkey, but a different +- <a href="http://citeseer.nj.nec.com/529075.html"> search/localisation protocol </a>, +- that doesn't need servers. +- +- <p> +- +- MLdonkey 2.00 is able to search for files and sources on Overnet. For that, you must +- make sure that: +- +- <ol> +- +- <li> The "overnet_search_sources" and "overnet_search_keywords" options are set to +- true. +- +- <li> You have a (recent) list of Overnet peers that you can connect to. For that, you +- can: +- +- <ul> +- <li> Use the "servers.ini" file from the most recent distribution of mldonkey. It +- contains a long list of Overnet peers. +- +- <li> Use the "boot IP PORT" command, to add a new peer. +- +- <li> Use the "ovweb URL" command, to download a .ocl file from url URL +- containing a list of recent peers. +- +- </ul> +- +- You can use the "ovstats" command, to display how many peers you have been able +- to connect to (well, they have replied to a connect UDP packet). +- +- <li> If you have enabled "overnet_search_keywords", replies to search can arrive from +- Overnet peers. These results are not very accurate (they only need to match only +- one of the keywords, instead of all for example). +- +- <p> +- +- As for all networks, you can activate post-filtering of results (very useful on +- Gnutella too): set the "filter_search" option to true, and maybe the "filter_search_delay" +- option (period between results updates in seconds). Then, only results matching exactly +- your query will be displayed. +- +- </ol> +- +- <li> <h3> <a name=edonkey6> I'm behind a firewall, what should I do ? </a> </h3> +- +-MLdonkey uses two ports for incoming connections, one on TCP (default is +-4662) and the other one for UDP (4666). Thus, you should open these ports +-in your firewall, and forward them to the computer running mldonkey. More +-generally, you can do that for all ports between 4660 and 4670. +- +-<p> Allowing incoming connections is important as it is the only way for +-you to connect other sources which are behind a firewall. +- +-<p> If you have no control on the firewall, and your administrator blocks +-port 4660 to 4670, you should change the port used by mldonkey in the configuration +-file <tt> downloads.ini </tt> : <tt> port </tt> is the TCP port, while the +-UDP port is always computed as <tt> port + 4 </tt>. +- +-<li> <h3> <a name=edonkey7> How can I share multiple directories ? </a> </h3> +- +-Just edit the <tt> shared_directories </tt> option in <tt> downloads.ini </tt>: +- +-<pre> +-shared_directories = [ "/usr/share/documents"; "/usr/share/videos"] +-</pre> +- +-</ol> +- +- +-<hr> +-<li> <h2> The Soulseek plugin </h2> +- +-<ol> +- +-<li> <H3> <a name=slsk0> What is the Soulseek network ? </a> </H3> +- +-The <a href="http://www.soulseek.org"> Soulseek Network </a> is a network +-similar to Napster. +- +- <p> You connect on a single server, and there, you can search for (mainly +-audio) files, you can join rooms to chat with other users, and you can browse +-other users files. +- +-<li> <H3> <a name=slsk1> Why can't mldonkey connect to any server ? </a> </H3> +- +-There is only one server on Soulseek, and the one you have in soulseek.ini +-is probably not up-to-date. Normally, mldonkey will download another one, so +-that you will have to servers in your list of servers. +- +-<p> +-By default, mldonkey_gui does not display the servers you are not +-connected to. So you have to use the "Display All Servers" button, and ask +-mldonkey to try to connect to the second soulseek server. +- +-<p> +-Currently, the server is <tt> mail.slsk.org </tt>, port 2242. +- +-<p> +-Mldonkey behavior will be improved to automatically connect to the current +- server from the soulseek site Web page. +- +-</ol> +- +-<hr> +-<li> <h2> The LimeWire plugin </h2> +- +-<ol> +- +- <li> <h3> <a name=limewire0> What is the LimeWire network ? </a> </h3> +- +- The <a href="http://www.limewire.org"> LimeWire network </a> is a +-network built upon the <a href="http://www.gnutella.org"> Gnutella network </a>. +- +-<p> +-The main difference is that all nodes in this network do not broadcast each +-received message to its neighbours. Instead, special nodes, called +-ultra-peers, with higher connectivity are dedicated for this task, whereas +-other smaller nodes (such as mldonkey) only connect and ask ultra-peers. +- +-<p> +-Currently, the LimeWire support in MLdonkey also allows mldonkey client to +-use <a href="http://www.gnucleus.org"> Gnucleus </a>, +-<a href="http://www.morpheus.com"> Morpheus </a>, and MyNapster +-ultra-peers as servers. +- +-<p> +-On this network, you can mainly search for small files, you have no way to +-chat nor browse other peers. +- +- <li> <h3> <a name=limewire1> When I search for files, I receive unrelated results ? </a> </h3> +- +-On Gnutella, there is no specified protocol for asking complex queries to +-other peers. Instead, each peer is free to interpret your query as it +-wants and reply what it wants. +- +-<p> +-In particular, multiple-words queries are often understood as logical-or +-queries between words, ie each document that contains at least one word is returned. +- +-<p> +- As for Overnet, you can activate post-filtering of sources. Then, results +- which are received are checked by mldonkey against the query and only displayed if +- they are accurate. Post-Filtering is activated by setting the "filter_search" option +- to true, and modifying the "filter_search_delay" depending on the period you +- want between updates (for example, 30 means that results will be added only every +- 30 seconds). +- +-</ol> +- +-<hr> +-<li> <h2> The other plugins </h2> +- +-<ol> +-<li> <h3> <a name=other1> What is the current Development Status of +-mldonkey plugins ? </a> </h3> +- +- The current Development Status of mldonkey (in the CVS) at 2002/09/09 is +-shown on the following table (also have a look at the +-<a href="http://savannah.nongnu.org/cgi-bin/viewcvs/*checkout*/mldonkey/mldonkey/distrib/ChangeLog?rev=HEAD&content-type=text/plain"> +-ChangeLog +-</a>) +- +- +-<table border="1" cellpadding="5"> +-<tr bgcolor="#80FF80"> +-<td align=center width="12%"> Features </td> +-<td align=center width="9%"> <a href=http://www.edonkey2000.com/> eDonkey </a> </td> +-<td align=center width="9%"> <a href=http://www.neo-modus.com/> Direct +-Connect </a> </td> +-<td align=center width="9%"> <a href=http://opennap.sourceforge.net/> Open +-Napster </a> </td> +-<td align=center width="9%"> <a href=http://www.limewire.org> Gnutella +-LimeWire </a> </td> +-<td align=center width="9%"> <a href=http://www.slsk.org> Soulseek </a> </td> +-<td align=center width="9%"> <a href=http://gift.sourceforge.net> OpenFT </a> </td> +-</tr> +- +- +-<tr> +-<td> Download Server List </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-</tr> +- +- +-<tr> +-<td> Server Connect </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-</tr> +- +- +-<tr> +-<td> View Server Users </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +-<tr bgcolor=yellow> +-<td> Search Files </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> (web) </td> +-<td align=center> CVS </td> +-</tr> +- +-<tr> +-<td> Browse Peer </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +-<tr bgcolor=yellow> +-<td> Download Files </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-</tr> +- +-<tr> +-<td> Recover Files </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-</tr> +- +-<tr> +-<td> Recover Sources </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-</tr> +- +-<tr> +-<td> Pause Downloads </td> +-<td align=center> 1.16 </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-</tr> +- +-<tr> +-<td> Cancel Downloads </td> +-<td align=center> 1.16 (not CVS) </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-</tr> +- +- +-<tr bgcolor=yellow> +-<td> Upload Files </td> +-<td align=center> 1.16 </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +- +-<tr> +-<td> Upload File List </td> +-<td align=center> 1.16 </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +- +-<tr> +-<td> Friends </td> +-<td align=center> 1.16 </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +-<tr> +-<td> Public Messages </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> CVS </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +- +-<tr> +-<td> Private Messages </td> +-<td align=center> 1.16 </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +-<tr> +-<td> Import Windows Config </td> +-<td align=center> 1.16 </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-<td align=center> </td> +-</tr> +- +-<tr> +-<td> Config File </td> +-<td align=center> downloads.ini </td> +-<td align=center> directconnect.ini </td> +-<td align=center> opennap.ini </td> +-<td align=center> limewire.ini </td> +-<td align=center> soulseek.ini </td> +-<td align=center> openft.ini </td> +-</tr> +- +-<tr> +-<td> Disable Network </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-<td align=center> CVS </td> +-</tr> +- +-</table> +- +- +-</ol> +- +-</ol> +- +-<p> +- +-If you have any comments, if you want to add some questions, or improve +-this FAQ, send a mail to <a href="mailto:mldonkey@mldonkey.net"> +-mldonkey@mldonkey.net </a> +- +-</body> +-</html> +Index: distrib/Install.txt +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/Install.txt,v +retrieving revision 1.10 +retrieving revision 1.11 +diff -u -r1.10 -r1.11 +--- distrib/Install.txt 3 Feb 2006 00:45:15 -0000 1.10 ++++ distrib/Install.txt 23 Oct 2006 12:58:35 -0000 1.11 +@@ -63,17 +63,17 @@ + is compiled. + + ------------------------------------------------------------------------ +-1) Installing required tools: Objective-Caml 3.08.3 and LablGTK ++1) Installing required tools: Objective-Caml 3.09.3 and LablGTK + +- 1.1) Objective-Caml 3.08.4 (from http://pauillac.inria.fr/caml) ++ 1.1) Objective-Caml 3.09.3 (from http://pauillac.inria.fr/caml) + +- ~/tmp> wget http://caml.inria.fr/distrib/ocaml-3.08/ocaml-3.08.4.tar.gz +- ~/tmp> tar zxf ocaml-3.08.4.tar.gz +- ~/tmp> cd ocaml-3.08.4 ++ ~/tmp> wget http://caml.inria.fr/distrib/ocaml-3.09/ocaml-3.09.3.tar.gz ++ ~/tmp> tar zxf ocaml-3.09.3.tar.gz ++ ~/tmp> cd ocaml-3.09.3 + +- ~/tmp/ocaml-3.08.4> ./configure +- ~/tmp/ocaml-3.08.4> make world.opt +- ~/tmp/ocaml-3.08.4> make install ++ ~/tmp/ocaml-3.09.3> ./configure ++ ~/tmp/ocaml-3.09.3> make world.opt ++ ~/tmp/ocaml-3.09.3> make install + + 1.2.1) LablGTK 1.2.7 for GTK1 + (from http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk.html) +@@ -86,22 +86,22 @@ + ~/tmp/lablgtk-1.2.7> make opt + ~/tmp/lablgtk-1.2.7> make install + +- 1.2.2) LablGTK-2.4.0 for GTK2 ++ 1.2.2) LablGTK-2.6.0 for GTK2 + (from http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/lablgtk.html) + +- ~/tmp> wget http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/dist/lablgtk-2.4.0.tar.gz +- ~/tmp> tar zxf lablgtk-2.4.0.tar.gz +- ~/tmp> cd lablgtk-2.4.0 +- ~/tmp/lablgtk-2.4.0> ./configure +- ~/tmp/lablgtk-2.4.0> make world +- ~/tmp/lablgtk-2.4.0> make install ++ ~/tmp> wget http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/dist/lablgtk-2.6.0.tar.gz ++ ~/tmp> tar zxf lablgtk-2.6.0.tar.gz ++ ~/tmp> cd lablgtk-2.6.0 ++ ~/tmp/lablgtk-2.6.0> ./configure ++ ~/tmp/lablgtk-2.6.0> make world ++ ~/tmp/lablgtk-2.6.0> make install + + 2) Compiling mldonkey with GUI: + + To build GTK1 newgui: ./configure --enable-gui=newgui1 + To build GTK1 oldgui: ./configure --enable-gui=oldgui + To build GTK2 GUI: ./configure --enable-gui[=newgui2] +- lablgtk-1.2.7 and lablgtk-2.4.0 can both be installed at the same time. ++ lablgtk-1.2.7 and lablgtk-2.6.0 can both be installed at the same time. + + ~/tmp/mldonkey> make + +Index: distrib/Readme.txt +=================================================================== +RCS file: distrib/Readme.txt +diff -N distrib/Readme.txt +--- distrib/Readme.txt 1 May 2005 17:43:11 -0000 1.16 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,433 +0,0 @@ +- *** THIS FILE IS OUTDATED. PLEASE HAVE A LOOK AT THE *** +- *** WIKI PAGE FOR BASIC INFORMATION ABOUT INSTALLING *** +- +- +- MLDonkey +- ======== +- +-Release: 1.16 +-Authors: [b8]_bavard (Communication engine) and [b8]_Zoggy (GUI) +- +- MLDonkey is a door to the 'donkey' network, a decentralized network used to +-exchange big files on the Internet. It is written in a wonderful language, +-called Objective-Caml, and present most features of the basic Windows donkey +-client, plus some more: +- - It should work on most UNIX-compatible platforms. +- - You can remotely command your client, either by telnet, on a WEB browser, +- or with the GTK interface. +- - You can connect to several servers, and each search will query all the +- connected servers. +- - You can select mp3s by bitrates in queries (useful ?). +- - You can select the name of a downloaded file before moving it to your +- incoming directory. +- - You can have several queries in the graphical user interface at the same +- time. +- - You can remember your old queries results in the command-line interface. +- - You can search in the history of all files you have seen on the network. +- +- +-USAGE: +-====== +- +- This package contains three files: 'mldonkey', 'mldonkey_gui' and +-servers.ini'. +- +-'mldonkey' is the main program, a daemon which is used to download files. +-It takes no argument, and outputs some debugging messages on his terminal, +-so you should not close it. So, to start your program in the background, +-you can use: +- +-prompt> ./mldonkey > mldonkey.log & +- +- 'mldonkey' expects to find several .ini files in the directory +-where it is started. You can use the file provided in this package, or your +-old one if you already used 'mldonkey' before. It contains a list of servers +-that were available when the release was done. You can edit 'downloads.ini' +-to modify its parameters before starting 'mldonkey', but you can also +-modify the parameters in the graphical user interface 'mldonkey_gui'. +- +- 'mldonkey_gui', the graphical user interface, can be started by: +- +-prompt> ./mldonkey_gui & +- +- 'mldonkey_gui' doesn't need to be started in the same directory as +-'mldonkey'. Without parameters, it expects to find 'mldonkey' running on the +-same computer on the default port. If you started 'mldonkey' on another +-computer, you should specify the hostname (myhost.mydomain.mydot for example) +-on the command line: +- +-prompt> ./mldonkey_gui myhost.mydomain.mydot +- +- If you also modified the GUI port, you can also specify it (here 9999 for +-example) on the command line: +- +-prompt> ./mldonkey_gui myhost.mydomain.mydot 9999 +- +- You can also start the GUI, and modify these settings in the Options panel, +-and then try to reconnect. This is anyway necessary if you have put a +-password. +- +- Instead of using the GUI, you can also telnet to the daemon: +- +-prompt> telnet localhost 4000 +- +-(on your local computer) or +- +-prompt> telnet myhost.mydomain.mydot +- +-(if the client was started on that host). +- +-Using the config the standard core : +-=================================== +- Connect to mldonkey by telnet: +- +-prompt> telnet localhost 4000 +- +- Use the 'import' command with the donkey directory containing your old +-config as parameter. +- +-import '/.../donkey2000' +- +- All the files which were currently downloading will be moved to mldonkey +-temp directory, and the server list will be imported too. +- +- If you only want to import the server.met file, use the 'servers' command: +- +-servers '/.../donkey2000/server.met' +- +- Filenames containing special characters (such as spaces) should be put +-inside ''. +- +- +-Frequently Asked Questions +-========================== +- +-*) How can I contact the authors ? +----------------------------------- +- +-You can reach us for a short time on +- +- mldonkey@mldonkey.net +- mldonkey-users@non-gnu.org +- +-Please don't bother us too much with questions on how to use mldonkey. We +-prefer bug reports, containing USEFUL information to find the bug. You can +-also submit bugs on the savannah WEB site: +- +- http://www.freesoftware.fsf.org/mldonkey/ +- +-For advices on how to use mldonkey, you can check several forums: +- +- http://www.mldonkeyworld.com/ +- http://www.mldonkey.org/ (German forum) +- +-*) What about the sources ? What about the protocol ? +------------------------------------------------------ +- +-All sources are available on the savannah WEB site: +- +- http://www.freesoftware.fsf.org/mldonkey/ +- +-*) The GUI/telnet/WEB can't connect to the core. +------------------------------------------------ +- +-Since version 1.12, there is an option to control which computers can +-connect to mldonkey: allowed_ips +- +-By default, this option only allows your local computer to connect to the +-core. You can change this either by editing the downloads.ini file, or, in +-the console, using the +- +-set allowed_ips '127.0.0.1 A.B.C.D ...' +- +-command, where A.B.C.D ... are IP addresses separated by spaces. +- +-*) I added a password, and now the connection between the client and +- the GUI is immediatly aborded at startup. What should I do ? +--------------------------------------------------------------- +- +- Start the GUI. In the Options panel, type your password, and ENTER. +- Then reconnect to the client (menu or CTRL-R). +- +-*) How can I see the upload information ? +------------------------------------------ +- +-In mldonkey, you cannot directly know the current upload state of your +-core. There is a 'upstats' command which can be used to get sorted +-information on the files which have been requested. +- +-Two options are used to control upload: +-- 'max_upload_rate' controls the maximal bandwidth you accept to provide +-on upload. The minimun is 1 (kB/s). +-- 'shared_directories' is the list of directories that you want to share. +-By default, the list is empty, since the temp/ and incoming/ dirs are always +-shared. +- +-You can also disable upload for short periods of time with the 'nu' command. +-Before you must have ran mldonkey at least 5*m minutes if you want to +-disable upload for m minutes. You cannot disable upload for more than +-5 hours per day. Your credit can be displayed with the 'vu' command. +- +-*) How can I modify parameters that don't appear in the GUI nor in the +-command-line client ? +----------------------- +- +-mldonkey uses a file called 'downloads.ini'. You must stop your donkey client +-(use the Kill menu in the GUI or the 'kill' command in the telnet client). +-Then, edit this file and change the values of the options you want. +- +-Some options can also be modified by the 'set' command (in the command-line +-client or in the console of the GUI). These options appear when you use +-the 'vo' command. +- +-*) How can I communicate with the client ? +------------------------------------------- +- +-There are three ways to communicate with the client. In all the cases, you +-can run the client on one computer and control it from another computer. +-In these examples, we suppose they both run on the local computer 'localhost': +- +- 1) Use the GUI, called 'mldonkey_gui': +- +-prompt> ./mldonkey_gui localhost +- +- 2) Use the command-line client: +- +-prompt> telnet localhost 4000 +- +- 3) Use a WEB browser: +- +-prompt> lynx http://localhost:4080/ +- +-*) How can use the GUI on MacOS X ? +------------------------------------ +- +-First, you need to have an X server installed. See for more information: +- +- http://fink.sourceforge.net/doc/x11/index.php +- +-You will find a script in the tar file to start the GUI, that set the +-correct library path for most systems. If it doesn't work, you need to +-modify it according to your non-standard configuration. +- +-*) Previewing doesn't work ? +----------------------------- +- +-mldonkey tries to start a script, specified by the 'previewer' option. +-By default, this option calls 'mldonkey_previewer', which must be in your +-path. Its first arg is the name of the file on the local disk, while its +-second arg is the name of the file on the donkey network. +- +-*) I started mldonkey, and it didn't connect to any server ! +------------------------------------------------------------ +- +-Depending on your list of servers, the process of finding a server available +-can take a while. You can try to modify some options, such as the server +-connection timeout or the delay between connections attempts. If you know +-a good server, use 'c 34' if 34 is for example the number of the server +-in the list 'vma'. You can also select some servers in the GUI, and use the +-connect button/menu. +- +-*) Which ports should I open on my firewall ? +--------------------------------------------- +- +-By default, mldonkey uses ports 4662 for tcp connections, and port 4666 for +-udp connections. If you change the tcp port, udp port will be tcp_port + 4. +-Therefore, you should allow your firewall to send incoming connections and +-messages on these ports to your local network. +- +- +-Help on the command-line interface +-================================== +- +-In a different shell, telnet to your application by: +- +-prompt> telnet localhost 4000 +- +-4000 is the default port for connecting with the command-line client. +-Command-line client is disabled if you have set a password. +- +-Then, use ? to find some help on available commands. Some commands are only +-available in the graphical interface (setting options for example), others +-only in the command-line interface (import of old donkey config for example). +- +-Here is the output of the help command for version 1.12: +-n <ip> [<port>]: add a server +-vu : view upload credits +-nu <m> : disable upload during <m> minutes (multiple of 5) +-import <dirname> : import the config from dirname +-x <num> : disconnect from server +-servers <filename> : add the servers from a server.met file +-commit : move downloaded files to incoming directory +-vd <num>: view file info +-reshare : check shared files for removal +-vm : list connected servers +-vma : list all known servers +-q : close telnet +-kill : save and kill the server +-save : save +-d <size> <md4> : download this file +-upstats : statistics on upload +-port <port> : change connection port +-vo : print options +-set <option_name> <option_value> : change option value +-vr [<num>]: view results of a search +-forget <num> : forget search <num> +-ls <query> : local search +-s <query> : search for files +- +- With special args: +- -minsize <size> +- -maxsize <size> +- -media <Video|Audio|...> +- -Video +- -Audio +- -format <format> +- -field <field> <fieldvalue> +- -not <word> +- -and <word> +- -or <word> : +- +-vs : view all queries +-cancel <num> : cancel download +-xs : extended search +-clh : clear local history +-c [<num>]: connect to more servers (or to server <num>) +- +- +-Help on the graphical user interface +-==================================== +-1. Servers page: +- +-The server page presents the list of known servers. For each server, the list +-provides information on its IP address, port, connexion status, name, +-description, number of users and number of files. +- +-The list is automatically updated when new servers are discovered. MLDonkey +-tries to automatically connect to servers until enough (see the +-'Max connected servers' option) of them have replied. +- +-To add a server: enter its IP and Port, and press the 'Add server' button. +-To remove a server: select the server, and click the 'Remove' button. +-To connect a server: select the server, and click the 'Connect' button. +-To view server users: select the server, and click the 'View Users' button. +- If the list of users is already available, it is automatically displayed in +- the list on the right when the server is selected. +-To add some user to your friend list: select the users, and click the +- 'Add to friends' button. Direct users (those with a valid IP address) +- are immediatly added, while others are only added when they are +- connected. +-The 'Connect more servers' button: if not enough servers are connected, +- you can click this button to speed connections to servers. +-The 'Remove old servers' button: this button allows to clean the list of +- servers which have not been connected for a while (see the +- 'Max server age' option). +- +-2. Downloads page: +- +-When you select a file, its full name is displayed under the list of +-files. A bar indicates which chunks of the file are available: +-red means the chunks is not available and blue is for chunks which are +-available on only one client. Black is for chunks that are available on +-several clients, whereas green means you already have the chunk. +-Chunks are 9mb of consecutive bytes in the file. +- +-You can put ed2k:// URLs (those found on www.sharereactor.com) in the +-upper entry (after the ed2k: label). Press ENTER then. +- +-If you started a download and lost the config files for some reason, you can +-recover it in the upper right entry (Recover MD4) if it is still present under +-this MD4 in your temp/ directory. +- +-When selecting a file in the 'Downloading' list, the GUI will display the list +-of its locations on the right side. You can select some of these locations, +-and click the 'Add to friends' button to add them to your friends. +- +-When a download is finished, files are displayed in the 'Downloaded' list. +-You can use the 'Save all files' button to move these files to your +-incoming/ directory. This is not done automatically. You can also click on the +-right button of the mouse to have a contextual menu where you can select the +-name of the file. You can edit the tags of mp3 too in this menu. +- +-3. Friends page: +- +-Friends are displayed on the left, with the list of their files on the +-right. You can remove a friend by selecting it and clicking the 'Remove' +-button. You can also search all connected servers for a friend by +-putting its name in the 'Find Friend' entry and pressing ENTER. +- +-You can select files in the list on the right, then click +-'Download Selected Files' to add them to your download list. +- +-4. Queries page: +- +-'Max Hits' is not working yet. +- +-Use the 'Stop' button to stop receiving results for a query. +- +-5. Options page: +- +-This page can be used to update the simple options of the configuration +-file 'downloads.ini', which can be found in the directory where +-'donkey_downloads' was started. +- +-Interesting options are: +- +-'Name': your name on the donkey network. +- +-'Max Connected Servers': the maximum number of servers you can remain +-connected to. This is an old option, that was useful before UDP packets. +-1 is now enough, since all servers will eventually be searched by UDP. +- +-'Upload limit': default is 30 kB/s (good for ADSL/Cable). You can't set it +- under 1kB/s. If you have a large bandwith, set it to 500 kB/s or + +- +-'Client hostname': the name of the host were your client is running if not +- the same host as the graphical interface. +- +-'Password': the password used to control your client. +- +-The different ports that can be modified in the Option panel can be used to +-allow several clients to run on the same computer. Moreover, several user +-interfaces can connect to the same client at the same time (command-line +-interfaces and graphical interfaces). +- +-6. Console +- +-In the console, you have access to the command-line commands. +- +-Using auxiliary programs for local indexation (in development) +-============================================= +- +-mldonkey now uses auxiliary programs to help find results to search. +-Currently, two types of programs are supported: +- +-- Finder (set by the 'local_index_find_cmd' option) +- The finder receives a query on its standard input, and replies by +- the results on its standard output. +- +-Query format: the query finishes with 'end_query' on one line. On each line +-of the query, there is one keyword, a colon :, and a value. Keywords are: +- words, minsize, maxsize, minrate, media, format, title, album, artist +- +-Result format: the result finishes with 'end result' on one line. On each line +-of the result, there is one keyword, a colon :, and a value. +-Keywords are: +- Required: md4, size +- Optional: name, format, type, string_tag, int_tag +- +-There can be several name, string_tag, int_tag lines. The value on the +-string_tag line should be the name of the tag, a colon : and the value of +-the tag. Idem for int_tag, but the value should be an integer. +- +-- Indexer (set by the 'local_index_add_cmd' option) +-The indexer is called each time a new result is received by mldonkey, +-and the result is given on its standard input in the same format as specified +-above. It can be used to add the result to the index that is used by the +-Finder. +- +-Known bugs: +-=========== +- * When clicking on the columns it sorts on that column, when +- clicking again it should do a reverse sort on it, but it doesn't. +- +Index: distrib/Todo.txt +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/Todo.txt,v +retrieving revision 1.10 +retrieving revision 1.11 +diff -u -r1.10 -r1.11 +--- distrib/Todo.txt 1 Nov 2004 11:22:59 -0000 1.10 ++++ distrib/Todo.txt 23 Oct 2006 12:58:35 -0000 1.11 +@@ -9,15 +9,6 @@ + (1) Why is the QRT sent so often ? + (2) Implement PUSH in Gnutella and Fasttrack + +-(9) When a file is commited, it should not be shared anymore in temp/. +- Moreover, in temp, it should not be shared under is URN name. +-(10) CANCEL A FILE, AND RESTART THE DOWNLOAD: <<<<<<<<< +- +- +- +- +- +-Before 2.6: + * Re-implement contributors in CommonSwarming + * Re-implement already downloaded files. + +@@ -27,7 +18,6 @@ + downloaded chunks for itself or the primary network. + * Does the PartialChunk strategy work ? + * Why are chunks verified before completeness ? +- * After a while, shared files are not shared anymore ! why ??? + * When a connection succeeds and downloads, we should retry the connection + ASAP. + * How to remove servers from disabled networks ? +@@ -61,30 +51,14 @@ + + BUGS: + +-* "FT not respecting max sources per file settings" +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=3761 +-FT plugin AFAIK has no limits regarding the size of the serverlist +- +-* FT plugin is not able to get a list of working peers +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=7776 +- +-* "Search results appear in the wrong search" +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=6956 +- + Flush buffers after do_at_exit (wait 5 secondes after exit ?). +-Share only on some networks. +-Add information in the GIU protocol on who uploads what. + Send relative times in the GUI protocol +-IP blocking in net/ : http://www.peerguardian.net/pgipdb/guarding.p2p + + ************************************************************************ + Edonkey: +- * Extension to download small files immediatly (files smaller than 10k for +- example) + * Verify that make_xs is always synchronized with sources searches. + * Add command 'export_temp' so that they can be used from emule/edonkey + * EmuleMobile ? +- * BUG: commited files are not shared + * BUG: MLdonkey client generates "Exceeding Block Boundaries" errors which + loses bandwidth + * Implement more Emule packets +@@ -93,8 +67,6 @@ + OP_REASKACK (0x91) (size=2) + OP_FILENOTFOUND (0x92) + * Extended search doesnot work after connect because no ping was sent. +- * BUG: Cancel and Redownload doesnot work +- * BUG: Files randomly pause + + BitTorrent: + * Implement file availability as in edonkey. +@@ -113,7 +85,6 @@ + downloading clients + + Gnutella2: +- * Why doesn't TigerTree work on Mac ? + * Allow browsing other clients. + * ADD urn:sha1:<HASH> and urn:tree:tiger/:<HASH> to QRT + * Search using all URI (ed2k, etc...) +@@ -124,6 +95,7 @@ + * Send more information in HTTP/1.1 headers (alt-locs, thex) + + Soulseek: ++ * Network does currently not work due to missing bindings with common module + * In case of Message from server LOGIN FAILURE INVALIDPASS + prevent any new reconnection without changing the password. Done ? + + Implement more of the protocol +@@ -138,10 +110,12 @@ + + Display the number of new message per room + + Opennap: ++ * Network does currently not work due to missing bindings with common module + * Register files on server + * Implement Upload completely + + Direct-Connect: ++ * Network does currently not work due to missing bindings with common module + * Implement Upload completely + * How do you know your IP in Direct-Connect if you are behind a firewall + * Send replies to active searches +@@ -154,110 +128,12 @@ + Core: + * LittleEndian: is buf_int correct with negative values + * Change ClientKind so that it does not depend on edonkey anymore. +- * Configurable map to translate characters appearing in the file names. + * BUG: What happens when the writes are buffered, mtime does not correspond to + the real value ? We should probably call Unix2.flush_all before saving + the config. +- * Download one file from multiple networks: +- 1) A download must be started on a verified network (donkey, bittorrent +- or gnutella) +- 2) Complementary downloads can be started from other networks: +- 2a) Start a download as attached to a main download +- 2b) The attached download does not create a main file, but a set of +- chunks that are potentially downloaded +- 2c) If the download of the attached file is finished, and the file +- can be verified, then the attchaed download can be permuted +- with the main download (the attached download becomes the main +- download, and reciprocally). +- * Associate kinds with networks, and only download useful urls +- * Make difference between Subscribe and Submit searches clear: Submit +- searches all networks and stops when all servers have replied. +- Subscribe is a long term search, it should query the servers +- periodically, and display new results (and probably save them). +- Not implemented yet anyway. + * CD get and Collections +- * Change temporary files names to allow recover on all networks. +- * Socks 5 support + * The core sends more File_info messages than File_downloaded, which is + not normal ! + * Send messages to GUI with a classifier (to be able to display messages + in different consoles, server console, download console, clients console) + +-*********************************************************************** +-GUI: +- * Suppress all classes in newgui: object-oriented programming makes +- modifying the GUI even harder, and is not useful at all. We should +- replace classes by simple records when possible. +- * Give more information on why a connection failed: we should now at +- which stage a connection has failed, when it was, and when was the +- last correct connection. +- * Interactive downloads (popups for one file with progress bar): for +- example, it could be used when starting a download under bittorrent +- (use a network flag to say when it should be used ?) from a +- WEB navigator so that the user see a popup from the GUI immediatly +- and see the progress of the download (as does the python bittorrent). +- * Change the color of tabs when things change +- * Add information about: +- * When a download was started and when a file was last seen _complete_: +- 3d/8d : we have been downloading this file for 8 days, and +- saw it complete three days ago +- --/8d : idem, but we have never seen it complete +- +- +-*********************************************************************** +- +- And bug reports +- +-*********************************************************************** +-Fasttrack +-========= +-FT not respecting max sources per file settings +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=3761 +- +- +-GUI bugs +-======== +-"max hits" doesn't work +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=3675 +- +-Source-state error in GUI +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=2443 +- +-Commited file is not removed from GUI +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=3705 +- +-Right click to file option save doesn't show all file names +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=3179 +- +-"old" gtk gui column sizes +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=6917 +- +- +-HTML_mods bugs +-============== +-Cancel and Pause-"Feature" at same time +-http://savannah.nongnu.org/bugs/?func=detailitem&item_id=3227 +- +- +-Requests for GUI enhancements +-============================= +-Change the serach-for field into a combo box +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=3445 +- +-Remember current tab on exit +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=3476 +- +-Better GUI for identifying what files are in good states +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=4316 +- +-ed2k-links of all files available (might be best for html_mods) +-https://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=4378 +- +- +-Others +-====== +-Wrong filename used when downloading ed2k links? (Patch attached) +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=4080 +- +-Handling of foreign characters +-http://savannah.nongnu.org/bugs/index.php?func=detailitem&item_id=4154 +Index: distrib/ed2k_mozilla/README +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/ed2k_mozilla/README,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -r1.7 -r1.8 +--- distrib/ed2k_mozilla/README 14 Dec 2005 20:15:39 -0000 1.7 ++++ distrib/ed2k_mozilla/README 23 Oct 2006 12:58:35 -0000 1.8 +@@ -1,10 +1,10 @@ +-Mozilla MLdonkey/eMule Protocol Handler 1.7 +-Copyright (C) 2003 - 2005 Simon Peter <dn.tlp@gmx.net> ++Firefox MLdonkey/eMule Protocol Handler 1.8 ++Copyright (C) 2003 - 2006 Simon Peter <dn.tlp@gmx.net> + + Description: + ------------ +-This is a protocol handler for Mozilla and Firefox (see +-www.mozilla.org) that forwards some link types to a running MLdonkey ++This is a protocol handler for Firefox 1.5 and later (see ++www.mozilla.com) that forwards some link types to a running MLdonkey + (see www.mldonkey.net) or eMule (see www.emule-project.net) client. + + Supported protocols are ed2k:, magnet: and sig2dat:. For eMule, only +@@ -15,13 +15,13 @@ + specific questions to Dan Fritz. + + The protocol handler is implemented in JavaScript and should run on +-any platform supported by Mozilla and Firefox. ++any platform supported by Firefox. + + Prerequisites: + -------------- + The Web interface of your Donkey client must be enabled and access for +-the host this extension should be running on must be enabled in order +-for this extension to work. ++the host, this extension should be running on, must be enabled in ++order for this extension to work. + + For eMule, it is advised that you set your web interface template to + default (eMule.tmpl). It may work with other templates but this has +@@ -29,8 +29,8 @@ + + Installation: + ------------- +-Just open the .xpi installer file in Mozilla/Firefox and follow the +-on-screen instructions. ++Just open the .xpi installer file in Firefox and follow the on-screen ++instructions. + + If you are on a UNIX derivative system (e.g. Linux, BSD, MacOS X and + the like) and like to install this extension for all users on your +@@ -51,48 +51,34 @@ + + Configuration: + -------------- +-The protocol handler is configurable through Mozilla's preferences +-scheme. The preferences can be set in multiple ways, depending on your +-browser version. +- +-For Mozilla 1.3, newer versions and Firefox, the preferences are +-accessible by visiting the URL "about:config" inside the browser. This +-opens a page containing a table of all available preference +-options. Right clicking on the table opens a menu to create, modify +-and reset the options. Note that the preference defaults of this +-protocol handler are internal and NOT visible initially in the list! +-To set your own preferences, you have to create them. +- +-For Mozilla versions below 1.3, you can set the preferences by +-creating a file 'user.js' in your profile directory (this is +-$HOME/.mozilla/default/<profile>/ on UNIX systems, where <profile> has +-to be replaced by some cryptic string -- just look in that directory) +-and set the options there by adding lines of the form: +-user_pref("option", "value"); to it. ++The protocol handler is GUI configurable through Firefox' extensions ++menu, for Firefox versions 1.5 and later. Go to the extensions menu, ++by clicking on Tools->Extensions from the main bar of pull-down ++menus. There, select the MLdonkey Protocol Handler and click on ++Options. A GUI configuration dialog should pop up, which allows you to ++set all options. The following preference options are available: + +-The following preference options are available: +- +-Option: network.mldonkey.server ++Option: Server + Default: localhost + Description: Hostname on which MLdonkey/eMule is running + +-Option: network.mldonkey.port ++Option: Port + Default: 4080 +-Description: Port of the MLdonkey/eMule WEB service ++Description: Port of the MLdonkey/eMule web service + +-Option: network.mldonkey.pass ++Option: Password + Default: (empty) + Description: Password for eMule clients (MLdonkey is handled +-automatically by Mozilla's password manager) ++automatically by Firefox' password manager) + +-Option: network.mldonkey.mode ++Option: Mode + Default: mldonkey + Description: Donkey client to send the data to. This can be set to +-'mldonkey' or 'emule' (without the quotes) to talk to an MLdonkey or +-an eMule client, respectively. ++'mldonkey', 'emule' (without the quotes) to talk to an MLdonkey or ++eMule client, respectively. + +-All these options are string options. Remember to restart your browser +-before any of them can take effect! ++Remember to restart your browser before any of these options can take ++effect! + + Deinstallation: + --------------- +@@ -100,16 +86,25 @@ + the extensions dialog by clicking on Extensions in the Tools + menu. Select the plugin there and click on Uninstall. + +-On Mozilla, you have to manually deinstall the plugin by removing the +-file 'ed2kprotocol.js' from Mozilla's components directory. On a UNIX +-system, this normally is /usr/lib/mozilla/components. +- + News: + ----- ++Changes for version 1.8: ++- Support for GUI configuration through Firefox' extensions menu ++ (thanks to David Ciecierski <dawid.ciecierski@googlemail.com>). ++ ++ ATTENTION upgraders: If you manually set configuration options ++ through the about:config dialog for a previous version of this ++ extension, you first have to go to this dialog and reset all user ++ set options back to their defaults in order to be able to use the ++ new GUI configuration dialog! ++ ++- Since this version, only Firefox version 1.5 and higher is ++ supported. ++ + Changes for version 1.7: + - eMule communication is now faster (thanks to ZZ +- http://forum.emule-project.net/index.php?showuser=9079) +-- Added support for eMule 0.46a ++ http://forum.emule-project.net/index.php?showuser=9079). ++- Added support for eMule 0.46a. + - eMule response is now displayed in the same way as MLdonkey + responses. + +@@ -161,13 +156,14 @@ + + The Initial Developer of the Original Code is + Simon Peter <dn.tlp@gmx.net>. +-Portions created by the Initial Developer are Copyright (C) 2003 - 2005 ++Portions created by the Initial Developer are Copyright (C) 2003 - 2006 + the Initial Developer. All Rights Reserved. + + Contributor(s): + Sven Koch + Len Walter <len@unsw.edu.au> + Dan Fritz <templar_of_ni@yahoo.se> ++David Ciecierski <dawid.ciecierski@gmail.com> + + Alternatively, the contents of this file may be used under the terms of + either the GNU General Public License Version 2 or later (the "GPL"), or +Index: distrib/ed2k_mozilla/mldonkey_protocol_handler-1.7.xpi +=================================================================== +RCS file: distrib/ed2k_mozilla/mldonkey_protocol_handler-1.7.xpi +diff -N distrib/ed2k_mozilla/mldonkey_protocol_handler-1.7.xpi +Binary files /tmp/cvspWYDF7 and /dev/null differ +Index: distrib/ed2k_mozilla/mldonkey_protocol_handler-1.8.xpi +=================================================================== +RCS file: distrib/ed2k_mozilla/mldonkey_protocol_handler-1.8.xpi +diff -N distrib/ed2k_mozilla/mldonkey_protocol_handler-1.8.xpi +Binary files /dev/null and /tmp/cvsJexOXS differ +Index: distrib/ed2k_mozilla/src/contents.rdf +=================================================================== +RCS file: distrib/ed2k_mozilla/src/contents.rdf +diff -N distrib/ed2k_mozilla/src/contents.rdf +--- distrib/ed2k_mozilla/src/contents.rdf 8 Jun 2003 12:48:08 -0000 1.2 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,13 +0,0 @@ +-<?xml version="1.0"?> +-<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +- xmlns:chrome="http://www.mozilla.org/rdf/chrome#"> +- +-<!-- list all the packages being supplied --> +- <RDF:Seq about="urn:mozilla:package:root"> +- <RDF:li resource="urn:mozilla:package:hdl"/> +- </RDF:Seq> <!-- package information --> +- <RDF:Description about="urn:mozilla:package:ed2k" +- chrome:displayName="Mozilla MLdonkey Protocol Handler" +- chrome:author="Simon Peter <dn.tlp@gmx.net>" +- chrome:name="ed2k"> +- </RDF:Description> </RDF:RDF> +Index: distrib/ed2k_mozilla/src/install.js +=================================================================== +RCS file: distrib/ed2k_mozilla/src/install.js +diff -N distrib/ed2k_mozilla/src/install.js +--- distrib/ed2k_mozilla/src/install.js 14 Dec 2005 20:15:39 -0000 1.7 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,56 +0,0 @@ +-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+-/* ***** BEGIN LICENSE BLOCK *****
+- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+- *
+- * The contents of this file are subject to the Mozilla Public License Version
+- * 1.1 (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- * http://www.mozilla.org/MPL/
+- *
+- * Software distributed under the License is distributed on an "AS IS" basis,
+- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+- * for the specific language governing rights and limitations under the
+- * License.
+- *
+- * The Original Code is the MLdonkey protocol handler installer.
+- *
+- * The Initial Developer of the Original Code is
+- * Simon Peter <dn.tlp@gmx.net>.
+- * Portions created by the Initial Developer are Copyright (C) 2003 - 2005
+- * the Initial Developer. All Rights Reserved.
+- *
+- * Contributor(s):
+- * Len Walter <len@unsw.edu.au>
+- *
+- * Alternatively, the contents of this file may be used under the terms of
+- * either the GNU General Public License Version 2 or later (the "GPL"), or
+- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+- * in which case the provisions of the GPL or the LGPL are applicable instead
+- * of those above. If you wish to allow use of your version of this file only
+- * under the terms of either the GPL or the LGPL, and not to allow others to
+- * use your version of this file under the terms of the MPL, indicate your
+- * decision by deleting the provisions above and replace them with the notice
+- * and other provisions required by the GPL or the LGPL. If you do not delete
+- * the provisions above, a recipient may use your version of this file under
+- * the terms of any one of the MPL, the GPL or the LGPL.
+- *
+- * ***** END LICENSE BLOCK ***** */
+-
+-const G_MSG = "Mozilla MLdonkey Protocol Handler 1.6";
+-const G_NAME = "ed2k";
+-const G_VER = "1.7";
+-
+-var err = initInstall(G_MSG, G_NAME, G_VER);
+-logComment("initInstall: " + err);
+-
+-addFile("protocol handler", "components/ed2kprotocol.js", getFolder("Components"), "");
+-
+-// needed for older versions to see the new component
+-var f = getFolder("Program", "component.reg");
+-if (File.exists(f)) File.remove(f);
+-
+-err = getLastError();
+-if (err==SUCCESS)
+- performInstall();
+-else
+- cancelInstall(err);
+Index: distrib/ed2k_mozilla/src/install.rdf +=================================================================== +RCS file: distrib/ed2k_mozilla/src/install.rdf +diff -N distrib/ed2k_mozilla/src/install.rdf +--- distrib/ed2k_mozilla/src/install.rdf 14 Dec 2005 20:15:40 -0000 1.3 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,29 +0,0 @@ +-<?xml version="1.0"?> +- +-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +- xmlns:em="http://www.mozilla.org/2004/em-rdf#"> +- +- <Description about="urn:mozilla:install-manifest"> +- <em:id>{bc72206d-b3ce-4b49-88b9-d59b388a0cde}</em:id> +- <em:version>1.7</em:version> +- +- <!-- Target Application this extension can install into, +- with minimum and maximum supported versions. --> +- <em:targetApplication> +- <Description> +- <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> +- <em:minVersion>0.7</em:minVersion> +- <em:maxVersion>1.9</em:maxVersion> +- </Description> +- </em:targetApplication> +- +- <!-- Front End MetaData --> +- <em:name>Mozilla MLdonkey Protocol Handler</em:name> +- <em:description>Mozilla/Firefox MLdonkey Protocol Handler</em:description> +- <em:creator>Simon Peter</em:creator> +- <em:contributor>Len Walter</em:contributor> +- <em:contributor>Sven Koch</em:contributor> +- <em:contributor>Dan Fritz</em:contributor> +- <em:homepageURL>http://www.informatik.uni-oldenburg.de/~dyna/mldonkey/</em:homepageURL> +- </Description> +-</RDF> +Index: distrib/ed2k_mozilla/src/components/ed2kprotocol.js +=================================================================== +RCS file: distrib/ed2k_mozilla/src/components/ed2kprotocol.js +diff -N distrib/ed2k_mozilla/src/components/ed2kprotocol.js +--- distrib/ed2k_mozilla/src/components/ed2kprotocol.js 14 Dec 2005 20:15:40 -0000 1.3 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,251 +0,0 @@ +-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +-/* ***** BEGIN LICENSE BLOCK ***** +- * Version: MPL 1.1/GPL 2.0/LGPL 2.1 +- * +- * The contents of this file are subject to the Mozilla Public License Version +- * 1.1 (the "License"); you may not use this file except in compliance with +- * the License. You may obtain a copy of the License at +- * http://www.mozilla.org/MPL/ +- * +- * Software distributed under the License is distributed on an "AS IS" basis, +- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +- * for the specific language governing rights and limitations under the +- * License. +- * +- * The Original Code is the MLdonkey protocol handler 1.7. +- * +- * The Initial Developer of the Original Code is +- * Simon Peter <dn.tlp@gmx.net>. +- * Portions created by the Initial Developer are Copyright (C) 2003 - 2005 +- * the Initial Developer. All Rights Reserved. +- * +- * Contributor(s): +- * Sven Koch +- * Len Walter <len@unsw.edu.au> +- * Dan Fritz (eMule bindings) +- * +- * Alternatively, the contents of this file may be used under the terms of +- * either the GNU General Public License Version 2 or later (the "GPL"), or +- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +- * in which case the provisions of the GPL or the LGPL are applicable instead +- * of those above. If you wish to allow use of your version of this file only +- * under the terms of either the GPL or the LGPL, and not to allow others to +- * use your version of this file under the terms of the MPL, indicate your +- * decision by deleting the provisions above and replace them with the notice +- * and other provisions required by the GPL or the LGPL. If you do not delete +- * the provisions above, a recipient may use your version of this file under +- * the terms of any one of the MPL, the GPL or the LGPL. +- * +- * ***** END LICENSE BLOCK ***** */ +- +-/***** Defines *****/ +- +-// components defined in this file +-const ED2KPROT_HANDLER_CONTRACTID = +- "@mozilla.org/network/protocol;1?name=ed2k"; +-const ED2KPROT_HANDLER_CID = +- Components.ID("{af8d664a-d002-438f-84a3-01f3a8ff325b}"); +- +-const MAGNETPROT_HANDLER_CONTRACTID = +- "@mozilla.org/network/protocol;1?name=magnet"; +-const MAGNETPROT_HANDLER_CID = +- Components.ID("{3e022170-58b0-4548-ba4c-1f47d54c7767}"); +- +-const SIG2DATPROT_HANDLER_CONTRACTID = +- "@mozilla.org/network/protocol;1?name=sig2dat"; +-const SIG2DATPROT_HANDLER_CID = +- Components.ID("{2a2e71ea-e857-4c71-9c93-04ff681df88a}"); +- +-// components used in this file +-const NS_IOSERVICE_CID = "{9ac9e770-18bc-11d3-9337-00104ba0fd40}"; +-const NS_PREFSERVICE_CONTRACTID = "@mozilla.org/preferences-service;1"; +-const URI_CONTRACTID = "@mozilla.org/network/simple-uri;1"; +-const NS_WINDOWWATCHER_CONTRACTID = "@mozilla.org/embedcomp/window-watcher;1"; +-const INPUTSTREAMCHANNEL_CONTRACTID = "@mozilla.org/network/input-stream-channel;1"; +- +-// interfaces used in this file +-const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler; +-const nsIURI = Components.interfaces.nsIURI; +-const nsISupports = Components.interfaces.nsISupports; +-const nsIIOService = Components.interfaces.nsIIOService; +-const nsIPrefService = Components.interfaces.nsIPrefService; +-const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher; +-const nsIChannel = Components.interfaces.nsIChannel; +- +-// some misc. constants +-const PREF_BRANCH = "network.mldonkey."; +-const WND_WIDTH = 320; +-const WND_HEIGHT = 200; +- +-const WND_EMULE_WIDTH = 450; +-const WND_EMULE_HEIGHT = 75; +- +-// configuration (and defaults) +-cfgServer = "localhost"; +-cfgPort = "4080"; +-myWnd = null; +-cfgMode = "mldonkey" +-cfgPass = "" +- +-/***** MLdonkeyProtocolHandler *****/ +- +-function MLdonkeyProtocolHandler(scheme) +-{ +- this.scheme = scheme; +- this.readPreferences(PREF_BRANCH); +-} +- +-// attribute defaults +-MLdonkeyProtocolHandler.prototype.defaultPort = -1; +-MLdonkeyProtocolHandler.prototype.protocolFlags = nsIProtocolHandler.URI_NORELATIVE; +- +-MLdonkeyProtocolHandler.prototype.allowPort = function(aPort, aScheme) +-{ +- return false; +-} +- +-MLdonkeyProtocolHandler.prototype.newURI = function(aSpec, aCharset, aBaseURI) +-{ +- var uri = Components.classes[URI_CONTRACTID].createInstance(nsIURI); +- uri.spec = aSpec; +- return uri; +-} +- +-MLdonkeyProtocolHandler.prototype.newChannel = function(aURI) +-{ +- var myUri = ""; +- var myTitle = ""; +- var myWidth = WND_WIDTH; +- var myHeight = WND_HEIGHT +- +- if (cfgMode == "mldonkey") { +- myTitle = "MLDonkey"; +- +- // rewrite the URI into a http URL to the mldonkey client +- myURI = "http://"; +- // if(cfgUser != "") myURI += cfgUser + ":" + cfgPass + "@"; +- myURI += cfgServer + ":" + cfgPort + "/submit?q=dllink+" + +- encodeURIComponent(decodeURI(aURI.spec)); +- } else { +- // eMule mode +- myTitle = "eMule"; +- myWidth = WND_EMULE_WIDTH; +- myHeight = WND_EMULE_HEIGHT +- +- // rewrite the URI into a http URL to the eMule client +- myURI = "http://"; +- myURI += cfgServer + ":" + cfgPort + "/?w=password&p=" + cfgPass; +- myURI += "&cat=0&c=" + encodeURIComponent(decodeURI(aURI.spec)); +- } +- +- // open up a window with our newly generated http URL +- var wwatch = Components.classes[NS_WINDOWWATCHER_CONTRACTID].getService(nsIWindowWatcher); +- if(myWnd == null || myWnd.closed == true) +- myWnd = wwatch.openWindow(wwatch.activeWindow, myURI, myTitle, +- "width=" + myWidth + ", height=" + myHeight, null); +- else +- myWnd.location.href = myURI; +- +- // return a fake empty channel so current window doesn't change +- var chan = Components.classes[INPUTSTREAMCHANNEL_CONTRACTID].createInstance(nsIChannel); +- return chan; +-} +- +-MLdonkeyProtocolHandler.prototype.readPreferences = function(pref_branch) +-{ +- // get preferences branch +- var PrefService = Components.classes[NS_PREFSERVICE_CONTRACTID].getService(nsIPrefService); +- var myPrefs = PrefService.getBranch(null); // Mozilla bug #107617 +- +- // read preferences (if available) +- if(myPrefs.getPrefType(pref_branch + "server") == myPrefs.PREF_STRING) +- cfgServer = myPrefs.getCharPref(pref_branch + "server"); +- if(myPrefs.getPrefType(pref_branch + "port") == myPrefs.PREF_STRING) +- cfgPort = myPrefs.getCharPref(pref_branch + "port"); +- if(myPrefs.getPrefType(pref_branch + "mode") == myPrefs.PREF_STRING) +- cfgMode = myPrefs.getCharPref(pref_branch + "mode"); +- if(myPrefs.getPrefType(pref_branch + "pass") == myPrefs.PREF_STRING) +- cfgPass = myPrefs.getCharPref(pref_branch + "pass"); +-} +- +-/***** MLdonkeyProtocolHandlerFactory *****/ +- +-function MLdonkeyProtocolHandlerFactory(scheme) +-{ +- this.scheme = scheme; +-} +- +-MLdonkeyProtocolHandlerFactory.prototype.createInstance = function(outer, iid) +-{ +- if(outer != null) throw Components.results.NS_ERROR_NO_AGGREGATION; +- +- if(!iid.equals(nsIProtocolHandler) && !iid.equals(nsISupports)) +- throw Components.results.NS_ERROR_INVALID_ARG; +- +- return new MLdonkeyProtocolHandler(this.scheme); +-} +- +-var factory_ed2k = new MLdonkeyProtocolHandlerFactory("ed2k"); +-var factory_magnet = new MLdonkeyProtocolHandlerFactory("magnet"); +-var factory_sig2dat = new MLdonkeyProtocolHandlerFactory("sig2dat"); +- +-/***** Ed2kzillaModule *****/ +- +-var Ed2kzillaModule = new Object(); +- +-Ed2kzillaModule.registerSelf = function(compMgr, fileSpec, location, type) +-{ +- compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); +- +- // register ed2k protocol handler +- compMgr.registerFactoryLocation(ED2KPROT_HANDLER_CID, +- "ED2K protocol handler", +- ED2KPROT_HANDLER_CONTRACTID, +- fileSpec, location, type); +- +- // register magnet protocol handler +- compMgr.registerFactoryLocation(MAGNETPROT_HANDLER_CID, +- "Magnet protocol handler", +- MAGNETPROT_HANDLER_CONTRACTID, +- fileSpec, location, type); +- +- // register sig2dat protocol handler +- compMgr.registerFactoryLocation(SIG2DATPROT_HANDLER_CID, +- "Sig2dat protocol handler", +- SIG2DATPROT_HANDLER_CONTRACTID, +- fileSpec, location, type); +-} +- +-Ed2kzillaModule.unregisterSelf = function(compMgr, fileSpec, location) +-{ +- compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar); +- +- // unregister our components +- compMgr.unregisterFactoryLocation(ED2KPROT_HANDLER_CID, fileSpec); +- compMgr.unregisterFactoryLocation(MAGNETPROT_HANDLER_CID, fileSpec); +- compMgr.unregisterFactoryLocation(SIG2DATPROT_HANDLER_CID, fileSpec); +-} +- +-Ed2kzillaModule.getClassObject = function(compMgr, cid, iid) +-{ +- if(!iid.equals(Components.interfaces.nsIFactory)) +- throw Components.results.NS_ERROR_NOT_IMPLEMENTED; +- +- if(cid.equals(ED2KPROT_HANDLER_CID)) return factory_ed2k; +- if(cid.equals(MAGNETPROT_HANDLER_CID)) return factory_magnet; +- if(cid.equals(SIG2DATPROT_HANDLER_CID)) return factory_sig2dat; +- +- throw Components.results.NS_ERROR_NO_INTERFACE; +-} +- +-Ed2kzillaModule.canUnload = function(compMgr) +-{ +- return true; // our objects can be unloaded +-} +- +-/***** Entrypoint *****/ +- +-function NSGetModule(compMgr, fileSpec) +-{ +- return Ed2kzillaModule; +-} +Index: distrib/html_themes/ease/commands.html +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/html_themes/ease/commands.html,v +retrieving revision 1.1 +retrieving revision 1.2 +diff -u -r1.1 -r1.2 +--- distrib/html_themes/ease/commands.html 14 Jan 2004 20:43:57 -0000 1.1 ++++ distrib/html_themes/ease/commands.html 25 Oct 2006 11:34:45 -0000 1.2 +@@ -57,14 +57,14 @@ + class="bu bbig bbig2" + onMouseOver="mOvr(this,'mOvr1');" + onMouseOut="mOut(this);" +-onClick="top.output.location.href='http://www.mldonkey.net/'">Homepage ++onClick="top.output.location.href='http://www.mldonkey.org/'">Homepage + </td> + <td + title="English/German support forums" + class="bu bbig bbig2" + onMouseOver="mOvr(this,'mOvr1');" + onMouseOut="mOut(this);" +-onClick="top.output.location.href='http://www.mldonkeyworld.com/'">Forums ++onClick="top.output.location.href='http://www.mldonkey.org/'">Forums + </td> + <td + title="Kill/Close the MLdonkey core" +Index: distrib/html_themes/old/commands.html +=================================================================== +RCS file: /sources/mldonkey/mldonkey/distrib/html_themes/old/commands.html,v +retrieving revision 1.1 +retrieving revision 1.2 +diff -u -r1.1 -r1.2 +--- distrib/html_themes/old/commands.html 14 Jan 2004 20:43:57 -0000 1.1 ++++ distrib/html_themes/old/commands.html 25 Oct 2006 11:34:45 -0000 1.2 +@@ -13,14 +13,14 @@ + class="bu bsmall1 b2" + onMouseOver="mOvr(this,'mOvr1');" + onMouseOut="mOut(this);" +-onClick="top.output.location.href='http://www.mldonkey.net/'">Homepage ++onClick="top.output.location.href='http://www.mldonkey.org/'">Homepage + </td> + <td + title="English/German support forums" + class="bu bsmall1 b2" + onMouseOver="mOvr(this,'mOvr1');" + onMouseOut="mOut(this);" +-onClick="top.output.location.href='http://www.mldonkeyworld.com/'">Forums ++onClick="top.output.location.href='http://www.mldonkey.org/'">Forums + </td> + <td + title="Options" +Index: docs/multiuser.txt +=================================================================== +RCS file: docs/multiuser.txt +diff -N docs/multiuser.txt +--- /dev/null 1 Jan 1970 00:00:00 -0000 ++++ docs/multiuser.txt 9 Nov 2006 21:32:25 -0000 1.2 +@@ -0,0 +1,158 @@ ++Description of multiuser patch ++============================== ++This file provides some HowTos and internals about the new multiuser ++functionality of MLDonkey. The goal is to provide a p2p-service to be ++used by more than user and where each user has its own environment ++provided by the daemon. ++ ++Some basics and definitions ++=========================== ++User "admin" and all users belonging to a group where group_admin = true can ++see all files in any case and can use all functions of MLDonkey. ++ ++file_owner in this text means the user which owns a downloading file, ++file_group means the group the file belongs to, file_owner must be a member of ++this group, both values are saved in files.ini. ++ ++New options (displayed options are default values) ++================================================== ++users.ini ++--------- ++- "users" is kept unchanged for compatibility, all users from "users2" ++ are saved in "users" as well, so password are updated. ++- "users2" is extended with these settings: ++ ++A list of groups the user belongs to, this user can view all files ++which belong to one of the groups ++ user_groups = [] ++ ++The default group of the user, the user must also be a member of this ++group. File_group of new downloads started by the user are automatically ++assigned to this value. This value can be None, this means the file is a ++private one only visible to the file_owner (and admins, of course). ++ user_default_group = mldonkey ++ ++E-mail address to sent commit notifications to. Global option "mail" ++can still be used for admins, if both addresses match only one mail is sent. ++ user_mail = "" ++ ++Commit files to <incoming>/<user_commit_dir> ++The current incoming directory is appended with user_commit_dir. ++All incoming dirs are shared recursively now to share these files ++committed into user specific dirs. ++ user_commit_dir = "" ++ ++Like global option max_concurrent_downloads this implements a user-specific ++limit of the maximum number of concurrent files a user can download. Other ++downloads are queued, this is done by round-robin. If the sum of ++user_max_concurrent_downloads from all users is bigger than ++max_concurrent_downloads less downloads than user_max_concurrent_downloads ++are in downloading state. 0 means no user-specific limit. ++Users can change file priorities the control which files are not queued. ++ user_max_concurrent_downloads = 0 ++ ++- groups, new option ++At least one group named "mldonkey" with group_admin = true must exist ++and will be re-created on startup if missing. ++ ++Option to control if the group has admin rights. All users belonging to such a ++group have the same rights as user "admin". ++ group_admin = true ++ ++ ++files.ini ++--------- ++- each file has two new options in files.ini ++file_owner: the incoming directory of the owner is used for commit, ++ if the user does not exist "admin" is used. If this data ++ field does not exist, the file will belong to user "admin". ++file_group: default value for a new download is user_default_group ++ if file_owner is not member of file_group or the group does not ++ exist, the user_default_group of file_owner is used. ++ ++downloads.ini ++------------- ++These two options control the display of user/group column in HTML, vd ++ html_mods_vd_user false ++ html_mods_vd_group false ++ ++ ++Commands to control multiuser features/data ++=========================================== ++chgrp <group> <num> ++change group of download <num> to <group>, group = none for private file ++ ++chown <user> <num> ++change owner of download <num> to <user> ++ ++dgroup ++print default group of logged-in user ++ ++groupadd <group> <admin: true | false> ++add new mldonkey group, only admin users can use this command ++ ++groupadmin <group> <admin: true | false> ++change group admin status, only admin users can use this command ++ ++groupdel <group> ++remove an unused mldonkey group, only admin users can use this command ++only possible if group has no members ++ ++groups ++print groups of logged-in user ++ ++passwd <passwd> ++change own password ++ ++useradd <user> <passwd> ++add new mldonkey user/change user password, only admin users can use this command ++ ++usercommit <user> <dir> ++change user specific commit directory ++ ++userdel <user> ++remove a mldonkey user, only admin users can use this command, user "admin" can not be removed ++deleting a user is only possible if the user does not own any downloads ++ ++userdgroup <user> <group|None> ++change user default group ++ ++userdls <user> <num> ++change number of allowed concurrent downloads, only admin users can use this command ++ ++usergroupadd <user> <group> ++add a group to a mldonkey user, only admin users can use this command ++ ++usergroupdel <user> <group> ++remove a group from a mldonkey user ++ ++usermail <user> <mail> ++change user mail address ++ ++users ++use this command in HTML interface for a small GUI to control users ++ ++whoami ++print logged-in user name ++ ++ ++Updating from a non-multiuser MLDonkey ++====================================== ++When updating all files will have file_owner "admin" and file_group "mldonkey". ++All existing users will have user_default_group = "mldonkey" and ++user_groups = ["mldonkey"]. This means all users can use all features of ++MLDonkey and see all files in use by MLDonkey core, just like before. ++ ++To hide user downloads from each other, create a new group with ++group_admin = false and assign all users to this group and remove them ++from all admin groups ++ ++ ++Additional features ++=================== ++- file_completed_cmd has new environment variables $FILE_OWNER and $FILE_GROUP ++- remove option enable_user_config, replaced by membership of admin groups ++ ++To-Do ++====== ++- Suggestions ? +Index: packages/rpm/mldonkey.spec.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/packages/rpm/mldonkey.spec.in,v +retrieving revision 1.5 +retrieving revision 1.6 +diff -u -r1.5 -r1.6 +--- packages/rpm/mldonkey.spec.in 27 Jun 2006 10:38:34 -0000 1.5 ++++ packages/rpm/mldonkey.spec.in 25 Oct 2006 11:34:45 -0000 1.6 +@@ -8,7 +8,7 @@ + Summary: %{summary} + License: GPL + Source0: %{name}.sources.tar.bz2 +-URL: http://www.mldonkey.net/ ++URL: http://www.mldonkey.org + Group: System/Servers + BuildRoot: %{_tmppath}/%{name}-buildroot + +Index: packages/slackware/slack-desc +=================================================================== +RCS file: /sources/mldonkey/mldonkey/packages/slackware/slack-desc,v +retrieving revision 1.1 +retrieving revision 1.2 +diff -u -r1.1 -r1.2 +--- packages/slackware/slack-desc 5 Nov 2005 16:21:25 -0000 1.1 ++++ packages/slackware/slack-desc 25 Oct 2006 11:34:46 -0000 1.2 +@@ -6,6 +6,5 @@ + mldonkey: Overnet, Bittorrent, Gnutella (Bearshare, Limewire, etc), Gnutella2 + mldonkey: (Shareaza), Fasttrack (Kazaa, Imesh, Grobster), Soulseek, etc. + mldonkey: +-mldonkey: http://www.mldonkey.net + mldonkey: http://www.mldonkey.org + mldonkey: +Index: src/daemon/common/commonClient.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonClient.ml,v +retrieving revision 1.30 +retrieving revision 1.33 +diff -u -r1.30 -r1.33 +--- src/daemon/common/commonClient.ml 14 Sep 2006 17:31:00 -0000 1.30 ++++ src/daemon/common/commonClient.ml 5 Nov 2006 14:09:38 -0000 1.33 +@@ -33,9 +33,8 @@ + mutable impl_client_type : client_type; + mutable impl_client_state : host_state; + mutable impl_client_update : int; +- mutable impl_client_has_slot : bool; +- mutable impl_client_has_friend_slot : bool; +- mutable impl_client_upload : shared option; ++ mutable impl_client_slot : slot_kind; ++ mutable impl_client_upload : file option; + mutable impl_client_num : int; + mutable impl_client_val : 'a; + mutable impl_client_ops : 'a client_ops; +@@ -102,8 +101,7 @@ + impl_client_type = 0; + impl_client_state = NewHost; + impl_client_update = 1; +- impl_client_has_slot = false; +- impl_client_has_friend_slot = false; ++ impl_client_slot = NoSlot; + impl_client_upload = None; + impl_client_num = 0; + impl_client_val = 0; +@@ -322,24 +320,26 @@ + let set_initialized c = + set_client_type c (client_type c lor client_initialized_tag) + +-let client_has_a_slot c = +- (as_client_impl c).impl_client_has_slot ++let client_slot c = ++ (as_client_impl c).impl_client_slot + +-let client_has_a_friend_slot c = +- (as_client_impl c).impl_client_has_friend_slot ++let client_has_a_slot c = ++ match (as_client_impl c).impl_client_slot with ++ NoSlot -> false ++ | _ -> true + + let client_upload c = + (as_client_impl c).impl_client_upload + + let set_client_upload c sh = +- (as_client_impl c).impl_client_upload <- sh; ++ (as_client_impl c).impl_client_upload <- Some sh; + client_must_update c + +-let set_client_has_a_slot c b = ++let set_client_has_a_slot c slot = + let impl = as_client_impl c in +- if not b && impl.impl_client_has_slot then begin +- impl.impl_client_has_slot <- false; +- impl.impl_client_has_friend_slot <- false; ++ match slot with ++ NoSlot -> if client_has_a_slot c then begin ++ impl.impl_client_slot <- NoSlot; + uploaders := Intmap.remove (client_num c) !uploaders; + client_must_update c; + (* +@@ -353,17 +353,15 @@ + *) + Unix32.close_all () + end +- else +- if b && not impl.impl_client_has_slot then begin ++ | slot -> if not (client_has_a_slot c) then begin + uploaders := Intmap.add (client_num c) c !uploaders; +- impl.impl_client_has_slot <- true; +- impl.impl_client_has_friend_slot <- is_friend c; ++ impl.impl_client_slot <- slot; + client_must_update c + end + + let set_client_disconnected c reason = + let impl = as_client_impl c in +- set_client_has_a_slot c false; ++ set_client_has_a_slot c NoSlot; + + match impl.impl_client_state with + Connected n -> set_client_state c (NotConnected (reason, n)) +@@ -402,6 +400,7 @@ + let counter = ref 0 in + H.iter (fun _ -> incr counter) clients_by_num; + Printf.bprintf buf " clients: %d\n" !counter; ++ Printf.bprintf buf " uploaders: %d\n" (Intmap.length !uploaders); + ) + + let clients_get_all () = +@@ -473,7 +472,7 @@ + client_disconnect c; + if !verbose then lprintf_nl "disconnected client %d: [%s %s] %s after %d %s of silence." + (client_num c) +- i.GuiTypes.client_software ++ (GuiTypes.client_software i.GuiTypes.client_software i.GuiTypes.client_os) + i.GuiTypes.client_release + i.GuiTypes.client_name + ctime +@@ -498,6 +497,7 @@ + T.client_connect_time = BasicSocket.last_time (); + T.client_kind = Indirect_location ("", Md4.Md4.null, Ip.null, 0); + T.client_software = ""; ++ T.client_os = None; + T.client_release = ""; + T.client_emulemod = ""; + T.client_downloaded = 0L; +Index: src/daemon/common/commonClient.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonClient.mli,v +retrieving revision 1.12 +retrieving revision 1.13 +diff -u -r1.12 -r1.13 +--- src/daemon/common/commonClient.mli 16 Aug 2006 19:12:08 -0000 1.12 ++++ src/daemon/common/commonClient.mli 25 Oct 2006 11:12:38 -0000 1.13 +@@ -2,9 +2,8 @@ + mutable impl_client_type : CommonTypes.client_type; + mutable impl_client_state : CommonTypes.host_state; + mutable impl_client_update : int; +- mutable impl_client_has_slot : bool; +- mutable impl_client_has_friend_slot : bool; +- mutable impl_client_upload : CommonTypes.shared option; ++ mutable impl_client_slot : CommonTypes.slot_kind; ++ mutable impl_client_upload : CommonTypes.file option; + mutable impl_client_num : int; + mutable impl_client_val : 'a; + mutable impl_client_ops : 'a client_ops; +@@ -70,11 +69,11 @@ + val check_client_implementations : unit -> unit + val client_can_upload : CommonTypes.client -> int -> unit + val client_enter_upload_queue : CommonTypes.client -> unit +-val client_upload : CommonTypes.client -> CommonTypes.shared option +-val set_client_upload : CommonTypes.client -> CommonTypes.shared option -> unit ++val client_upload : CommonTypes.client -> CommonTypes.file option ++val set_client_upload : CommonTypes.client -> CommonTypes.file -> unit ++val client_slot : CommonTypes.client -> CommonTypes.slot_kind + val client_has_a_slot : CommonTypes.client -> bool +-val client_has_a_friend_slot : CommonTypes.client -> bool +-val set_client_has_a_slot : CommonTypes.client -> bool -> unit ++val set_client_has_a_slot : CommonTypes.client -> CommonTypes.slot_kind -> unit + + val uploaders : CommonTypes.client Intmap.t ref + +Index: src/daemon/common/commonComplexOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonComplexOptions.ml,v +retrieving revision 1.60 +retrieving revision 1.69 +diff -u -r1.60 -r1.69 +--- src/daemon/common/commonComplexOptions.ml 1 Sep 2006 16:22:14 -0000 1.60 ++++ src/daemon/common/commonComplexOptions.ml 21 Nov 2006 22:34:33 -0000 1.69 +@@ -27,6 +27,7 @@ + open CommonServer + open CommonNetwork + open CommonOptions ++open CommonUserDb + open CommonTypes + open CommonFile + open Gettext +@@ -102,6 +103,61 @@ + impl.impl_file_age <- + normalize_time (get_value "file_age" value_to_int) + with _ -> ()); ++ ++ (try ++ impl.impl_file_release <- ++ get_value "file_release" value_to_bool ++ with _ -> ()); ++ ++ let file_user = ++ let filename = get_value "file_filename" value_to_string in ++ try ++ let u = get_value "file_owner" value_to_string in ++ begin ++ try ++ user2_user_find u ++ with Not_found -> ++ lprintf_nl "file_owner %s of %s does not exist, changing to %s" ++ u filename admin_user.user_name; ++ admin_user ++ end ++ with Not_found -> ++ lprintf_nl "file_owner of %s is empty, changing to %s" ++ filename admin_user.user_name; ++ admin_user ++ in ++ set_file_owner file file_user; ++ ++ let file_group = ++ let filename = get_value "file_filename" value_to_string in ++ let dgroup = user2_print_user_default_group file_user in ++ try ++ match (get_value "file_group" stringvalue_to_option) with ++ None -> None ++ | Some g -> ++ begin ++ try ++ let g = user2_group_find g in ++ if List.mem g file_user.user_groups then ++ Some g ++ else ++ begin ++ lprintf_nl "file_owner %s is not member of file_group %s, changing file_group of %s to user_default_group %s" ++ file_user.user_name g.group_name filename dgroup; ++ file_user.user_default_group ++ end ++ with Not_found -> ++ lprintf_nl "file_group %s of %s not found, changing to user_default_group %s of user %s" ++ g filename dgroup file_user.user_name; ++ file_user.user_default_group ++ end ++ with Not_found -> ++ lprintf_nl "file_group of %s is empty, changing to user_default_group %s of user %s" ++ filename dgroup file_user.user_name; ++ file_user.user_default_group ++ in ++ set_file_group file file_group; ++ + set_file_state file file_state; + + (try +@@ -138,6 +194,9 @@ + ("file_filenames", List + (List.map string_to_value impl.impl_file_filenames)) :: + ("file_age", IntValue (Int64.of_int impl.impl_file_age)) :: ++ ("file_release", bool_to_value impl.impl_file_release) :: ++ ("file_owner", string_to_value (file_owner file).user_name) :: ++ ("file_group", option_to_stringvalue (match file_group file with Some g -> Some g.group_name | None -> None)) :: + (file_to_option file) + ) + +@@ -512,7 +571,7 @@ + "-movies", "avi -minsize 650000000 -1cd"; + "-mp3s", "mp3 -minsize 3000000 -maxsize 10000000"; + "-albums", "album -minsize 30000000 -maxsize 150000000"; +- "-nosex", "-without xxx"; ++ "-nosex", "-not xxx"; + ] + + let customized_queries = +@@ -664,7 +723,7 @@ + sharing_directories = false; + sharing_extensions = []; + sharing_recursive = false; +- sharing_minsize = Int64.one; ++ sharing_minsize = zero; + sharing_maxsize = Int64.max_int; + } + +@@ -673,7 +732,7 @@ + sharing_directories = true; + sharing_extensions = []; + sharing_recursive = false; +- sharing_minsize = Int64.one; ++ sharing_minsize = zero; + sharing_maxsize = Int64.max_int; + } + +@@ -681,8 +740,8 @@ + sharing_incoming = true; + sharing_directories = false; + sharing_extensions = []; +- sharing_recursive = false; +- sharing_minsize = Int64.one; ++ sharing_recursive = true; ++ sharing_minsize = zero; + sharing_maxsize = Int64.max_int; + } + +@@ -691,7 +750,7 @@ + sharing_directories = true; + sharing_extensions = []; + sharing_recursive = false; +- sharing_minsize = Int64.one; ++ sharing_minsize = zero; + sharing_maxsize = Int64.max_int; + } + +@@ -726,7 +785,7 @@ + sharing_directories = false; + sharing_extensions = []; + sharing_recursive = true; +- sharing_minsize = Int64.one; ++ sharing_minsize = zero; + sharing_maxsize = Int64.max_int; + }; + +@@ -758,7 +817,7 @@ + + ) + +-let sharing_strategies name = ++let sharing_strategy name = + match name with + | "incoming_files" -> sharing_incoming_files + | "incoming_directories" -> sharing_incoming_directories +@@ -790,16 +849,18 @@ + in + let shdir_priority = get_value_safe "priority" value_to_int 0 + in ++(* + let shdir_networks = get_value_safe "networks" + (value_to_list value_to_string) [] + in ++*) + let shdir_strategy = get_value_safe "strategy" + value_to_string "only_directory" + in + { + shdir_dirname = shdir_dirname; + shdir_strategy = shdir_strategy; +- shdir_networks = shdir_networks; ++ shdir_networks = []; (* shdir_networks; *) + shdir_priority = shdir_priority; + } + end +@@ -825,8 +886,10 @@ + let shared_directory_to_value s = + let list = [ + "dirname", filename_to_value s.shdir_dirname; ++(* + "networks", + list_to_value string_to_value s.shdir_networks; ++*) + "strategy", + string_to_value s.shdir_strategy; + "priority", int_to_value s.shdir_priority; +@@ -839,8 +902,21 @@ + shared_directory_to_value + + end +- +- ++ ++let default_incoming_files = { ++ shdir_dirname = Filename.concat "incoming" "files"; ++ shdir_priority = 0; ++ shdir_networks = []; ++ shdir_strategy = "incoming_files"; ++ } ++ ++let default_incoming_directories = { ++ shdir_dirname = Filename.concat "incoming" "directories"; ++ shdir_priority = 0; ++ shdir_networks = []; ++ shdir_strategy = "incoming_directories"; ++ } ++ + let shared_directories = + define_option CommonOptions.path_section ["shared_directories" ] + " Incoming and shared directories. +@@ -852,8 +928,8 @@ + Finished BT multifile downloads are committed to the first directory + with strategy incoming_directories. Other downloads are committed + to the first directory with the strategy incoming_files. +- If more than one directory has one of the incoming_* strategies +- it will be ignored on commit, but they are shared nonetheless. ++ MLdonkey searches all shared_directories with incoming_* strategies ++ on commit and uses the first one with enough free diskspace. + Other strategies can be found in searches.ini, section customized_sharing." + (list_option SharedDirectoryOption.t) + [ +@@ -863,64 +939,92 @@ + shdir_networks = []; + shdir_strategy = "all_files"; + }; +- { +- shdir_dirname = "incoming/files"; +- shdir_priority = 0; +- shdir_networks = []; +- shdir_strategy = "incoming_files"; +- }; +- { +- shdir_dirname = "incoming/directories"; +- shdir_priority = 0; +- shdir_networks = []; +- shdir_strategy = "incoming_directories"; +- } ++ default_incoming_files; ++ default_incoming_directories; + ] + + + let search_incoming_files () = +- try +- List.find (fun s -> s.shdir_strategy = "incoming_files") +- !!shared_directories +- with Not_found -> +- let dirname = Filename.concat "incoming" "files" in +- let s = { +- shdir_dirname = dirname; +- shdir_priority = 0; +- shdir_networks = []; +- shdir_strategy = "incoming_files"; +- } +- in +- shared_directories =:= s :: !!shared_directories; +- s +- +-let incoming_files () = +- let dir = search_incoming_files () in +- Unix2.safe_mkdir dir.shdir_dirname; +- Unix2.can_write_to_directory dir.shdir_dirname; +- dir ++ let list = ++ List.filter (fun s -> s.shdir_strategy = "incoming_files") !!shared_directories ++ in ++ match list with ++ | [] -> shared_directories =:= default_incoming_files :: !!shared_directories; ++ [default_incoming_files] ++ | l -> l + + let search_incoming_directories () = +- try +- List.find (fun s -> s.shdir_strategy = "incoming_directories") +- !!shared_directories +- with Not_found -> +- let dirname = Filename.concat "incoming" "directories" in +- let s = { +- shdir_dirname = dirname; +- shdir_priority = 0; +- shdir_networks = []; +- shdir_strategy = "incoming_directories"; +- } +- in +- shared_directories =:= s :: !!shared_directories; +- s ++ let list = ++ List.filter (fun s -> s.shdir_strategy = "incoming_directories") !!shared_directories ++ in ++ match list with ++ | [] -> shared_directories =:= default_incoming_directories :: !!shared_directories; ++ [default_incoming_directories] ++ | l -> l ++ ++exception Incoming_full ++ ++let incoming_dir usedir ?user ?needed_space ?network () = ++ ++ let directories = ++ if usedir then ++ search_incoming_directories () ++ else ++ search_incoming_files () ++ in ++ ++ let dirname_user = ++ match user with ++ | None -> "" ++ | Some user -> user.user_commit_dir ++ in ++ ++(* ++ let dirname_network = ++ match network with ++ | None -> "" ++ | Some network -> network ++ in ++*) ++(* todo: make the dir naming order user configurable *) ++ let compute_dir_name dir = ++ let dirname = Filename2.normalize (Filename.concat dir dirname_user) in ++(* let dirname = Filename.concat dirname dirname_network in *) ++ dirname ++ in ++ ++ let checkdir = ++ try ++ List.find (fun d -> ++ let dirname = compute_dir_name d.shdir_dirname in ++(* check if temp_directory and incoming are on different partitions *) ++ try ++ if (Unix.stat dirname).Unix.st_dev <> (Unix.stat !!temp_directory).Unix.st_dev then ++ begin ++ match needed_space with ++ | None -> true ++ | Some needed_space -> ++ match Unix32.diskfree dirname with ++ Some v -> v >= needed_space ++ | _ -> true ++ end ++ else true ++ with _ -> true ++ ) directories ++ with Not_found -> raise Incoming_full; ++ in ++ ++ let newdir = { ++ shdir_dirname = (compute_dir_name checkdir.shdir_dirname); ++ shdir_priority = checkdir.shdir_priority; ++ shdir_networks = checkdir.shdir_networks; ++ shdir_strategy = checkdir.shdir_strategy; ++ } ++ in ++ Unix2.safe_mkdir newdir.shdir_dirname; ++ Unix2.can_write_to_directory newdir.shdir_dirname; ++ newdir + +-let incoming_directories () = +- let dir = search_incoming_directories () in +- Unix2.safe_mkdir dir.shdir_dirname; +- Unix2.can_write_to_directory dir.shdir_dirname; +- dir + + let _ = + (* Check the definition of the incoming_files and incoming_directories in +@@ -929,8 +1033,8 @@ + option_hook shared_directories (fun _ -> + if not !verification then begin + verification := true; +- ignore (incoming_files ()); +- ignore (incoming_directories ()); ++ ignore (incoming_dir false ()); ++ ignore (incoming_dir true ()); + verification := false + end + ) +@@ -1038,19 +1142,15 @@ + Pervasives.really_input ic s 0 size; + header, s) in + Tar.output otar header s +- with e -> +- let error = Printexc2.to_string e in +- if error = "Gzip.Error(\"error during compression\")" +- && Autoconf.windows && arg = "fasttrack.ini" then begin +- (* for whatever reason this error is raised on Windows, +- but fasttrack.ini is stored correctly *) +- if !verbose then +- lprintf_nl "Tar: Windows specific pseudo error %s in %s" error arg +- end +- else begin ++ with ++ | (Gzip.Error "error during compression") as e when Autoconf.windows && arg = "fasttrack.ini" -> ++ (* for whatever reason this error is raised on Windows, ++ but fasttrack.ini is stored correctly *) ++ if !verbose then ++ lprintf_nl "Tar: Windows specific pseudo error %s in %s" (Printexc2.to_string e) arg ++ | e -> + failed_files := arg :: !failed_files; +- lprintf_nl "Tar: error %s in %s" error arg +- end ++ lprintf_nl "Tar: error %s in %s" (Printexc2.to_string e) arg + ) files); + if !failed_files <> [] then + failwith (Printf.sprintf "Tar: error backing up %s" +@@ -1141,7 +1241,7 @@ + let reserved_fds = 40 in (* ini files, dynamic libs, etc. *) + + let total_files = (* maximum number of files in use at the same time *) +- (maxi (List.length !!files) !!max_concurrent_downloads) + !!max_upload_slots + reserved_fds ++ (max (List.length !!files) !!max_concurrent_downloads) + !!max_upload_slots + reserved_fds + in + + let wanted_socks = !!max_opened_connections + total_files in +@@ -1158,7 +1258,7 @@ + else + begin + let new_max_opened_connections = +- maxi (max_all_sockets - total_files) (max_all_sockets / 2) ++ max (max_all_sockets - total_files) (max_all_sockets / 2) + in + lprintf_nl "max_opened_connections is set too high (%d), reducing to %d" + !!max_opened_connections new_max_opened_connections; +Index: src/daemon/common/commonComplexOptions.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonComplexOptions.mli,v +retrieving revision 1.17 +retrieving revision 1.20 +diff -u -r1.17 -r1.20 +--- src/daemon/common/commonComplexOptions.mli 19 Mar 2006 17:38:08 -0000 1.17 ++++ src/daemon/common/commonComplexOptions.mli 9 Nov 2006 21:32:26 -0000 1.20 +@@ -34,15 +34,15 @@ + (string * CommonTypes.query_entry) list + val special_queries : (string * string) list Options.option_record + +-val sharing_strategies : string -> CommonTypes.sharing_strategy ++val sharing_strategies : (string * CommonTypes.sharing_strategy) list Options.option_record ++val sharing_strategy : string -> CommonTypes.sharing_strategy + + val shared_directories : + CommonTypes.shared_directory list Options.option_record + +-val incoming_files : unit -> CommonTypes.shared_directory +-val incoming_directories : unit -> CommonTypes.shared_directory +-val search_incoming_files : unit -> CommonTypes.shared_directory +-val search_incoming_directories : unit -> CommonTypes.shared_directory ++val incoming_dir : bool -> ?user:CommonTypes.userdb -> ?needed_space:int64 -> ?network:string -> unit -> CommonTypes.shared_directory ++val search_incoming_files : unit -> CommonTypes.shared_directory list ++val search_incoming_directories : unit -> CommonTypes.shared_directory list + + val sharing_only_directory : CommonTypes.sharing_strategy + +Index: src/daemon/common/commonFile.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonFile.ml,v +retrieving revision 1.60 +retrieving revision 1.70 +diff -u -r1.60 -r1.70 +--- src/daemon/common/commonFile.ml 5 Sep 2006 14:18:24 -0000 1.60 ++++ src/daemon/common/commonFile.ml 15 Nov 2006 12:37:13 -0000 1.70 +@@ -25,6 +25,7 @@ + open CommonTypes + open CommonOptions + open CommonGlobals ++open CommonUserDb + + let log_prefix = "[cF]" + +@@ -41,10 +42,12 @@ + (*************************************************************************) + + type 'a file_impl = { ++ mutable impl_file_owner : userdb; ++ mutable impl_file_group : groupdb option; + mutable impl_file_update : int; + mutable impl_file_state : file_state; + +- mutable impl_file_comment : string list; ++ mutable impl_file_comment : string; + mutable impl_file_num : int; + mutable impl_file_val : 'a; + mutable impl_file_ops : 'a file_ops; +@@ -59,6 +62,7 @@ + mutable impl_file_filenames : string list; + mutable impl_file_magic : string option; + mutable impl_file_priority: int; (* normal = 0, low < 0, high > 0 *) ++ mutable impl_file_release : bool; + mutable impl_file_last_seen : int; + mutable impl_file_probable_name : string option; + } +@@ -75,6 +79,7 @@ + has been changed. The method should not perform the move, just know that + it will happen soon. *) + mutable op_file_save_as : ('a -> string -> unit); ++ mutable op_file_shared : ('a -> CommonTypes.shared option); + mutable op_file_to_option : ('a -> (string * option_value) list); + mutable op_file_cancel : ('a -> unit); + mutable op_file_pause : ('a -> unit); +@@ -85,9 +90,10 @@ + mutable op_file_recover : ('a -> unit); + mutable op_file_all_sources : ('a -> client list); + mutable op_file_active_sources : ('a -> client list); ++ mutable op_file_comment : ('a -> string); + mutable op_file_set_priority : ('a -> int -> unit); +- mutable op_file_print_html : ('a -> Buffer.t -> unit); +- mutable op_file_print_sources_html : ('a -> Buffer.t -> unit); ++ mutable op_file_print : ('a -> CommonTypes.ui_conn -> unit); ++ mutable op_file_print_sources : ('a -> CommonTypes.ui_conn -> unit); + mutable op_file_files : ('a -> 'a file_impl -> file list); + (* added in 2.5.27 to remove use of network names in global modules *) + mutable op_file_debug : ('a -> string); +@@ -130,9 +136,12 @@ + impl_file_filenames = []; + impl_file_magic = None; + impl_file_priority = 0; ++ impl_file_release = false; + impl_file_last_seen = 0; +- impl_file_comment = []; ++ impl_file_comment = ""; + impl_file_probable_name = None; ++ impl_file_owner = admin_user; ++ impl_file_group = Some system_user_default_group; + } + + let dummy_file = as_file dummy_file_impl +@@ -198,16 +207,18 @@ + let file = as_file_impl file in + file.impl_file_ops.op_file_to_option file.impl_file_val + +- (* +-let file_print (file : file) buf = +- let file = as_file_impl file in +- file.impl_file_ops.op_file_print file.impl_file_val buf +- *) +- + let file_save_as (file : file) name = + let file = as_file_impl file in + file.impl_file_ops.op_file_save_as file.impl_file_val name + ++let file_shared (file : file) = ++ let file = as_file_impl file in ++ file.impl_file_ops.op_file_shared file.impl_file_val ++ ++let file_comment (file : file) = ++ let file = as_file_impl file in ++ file.impl_file_ops.op_file_comment file.impl_file_val ++ + let file_network (file : file) = + let file = as_file_impl file in + file.impl_file_ops.op_file_network +@@ -216,7 +227,17 @@ + let file = as_file_impl file in + file.impl_file_ops.op_file_info file.impl_file_val + +-let file_pause (file : file) = ++let file_owner file = ++ (as_file_impl file).impl_file_owner ++ ++let file_group file = ++ (as_file_impl file).impl_file_group ++ ++let user2_allow_file_admin file user = ++ user2_is_admin user || file_owner file = user ++ ++let file_pause (file : file) user = ++ if user2_allow_file_admin file user then + let file = as_file_impl file in + match file.impl_file_state with + | FileDownloading | FileQueued -> +@@ -224,7 +245,8 @@ + file.impl_file_ops.op_file_pause file.impl_file_val + | _ -> () + +-let file_resume (file : file) = ++let file_resume (file : file) user = ++ if user2_allow_file_admin file user then + let file = as_file_impl file in + match file.impl_file_state with + | FilePaused | FileAborted _ -> +@@ -232,20 +254,47 @@ + file.impl_file_ops.op_file_resume file.impl_file_val + | _ -> () + ++let set_file_release file status user = ++ if user2_allow_file_admin file user then ++ let impl = as_file_impl file in ++ impl.impl_file_release <- status ++ ++let file_release (file: file) = ++ let impl = as_file_impl file in ++ impl.impl_file_release ++ ++let set_file_owner file owner = ++ (as_file_impl file).impl_file_owner <- owner ++ ++let set_file_group file group = ++ (as_file_impl file).impl_file_group <- group ++ ++let user2_filter_files files gui_user = ++ let newlist = List.filter ++ (fun file -> user2_can_view_file gui_user (file_owner file) (file_group file)) files in ++ newlist ++ ++let user2_num_user_dls user = ++ let n = ref 0 in ++ H.iter (fun f -> if file_owner f = user then incr n) files_by_num; ++ !n ++ ++let user2_num_group_dls group = ++ let n = ref 0 in ++ H.iter (fun f -> if file_group f = Some group then incr n) files_by_num; ++ !n ++ + let set_file_state file state = + let impl = as_file_impl file in + update_file_state impl state + + let set_file_comment file comment = +- if not (List.mem comment (as_file_impl file).impl_file_comment) then +- (as_file_impl file).impl_file_comment <- +- (as_file_impl file).impl_file_comment @ [(HashComments.merge file_comments comment)] ++ let impl = as_file_impl file in ++ impl.impl_file_comment <- comment + + let file_comment file = +- (as_file_impl file).impl_file_comment +- +-let file_comment_length file = +- List.length (as_file_impl file).impl_file_comment ++ let impl = as_file_impl file in ++ impl.impl_file_comment + + let file_best_name (file : file) = + let file = as_file_impl file in +@@ -316,7 +365,9 @@ + try impl.impl_file_ops.op_file_active_sources impl.impl_file_val with _ -> [] + + (* Default for networks that don't implement it *) +-let default_file_print_sources_html file buf = ++let default_file_print_sources file o = ++ let buf = o.conn_buf in ++ if use_html_mods o then begin + let cfile = as_file file in + let allsources = ref (file_all_sources cfile) in + if List.length !allsources > 0 then begin +@@ -346,7 +397,8 @@ + ("", "sr br ar", Printf.sprintf "%d" (client_num c)); + ("", "sr br", cinfo.GuiTypes.client_name); + ("", "sr br", addr); +- ("", "sr br", cinfo.GuiTypes.client_software); ++ (GuiTypes.client_software cinfo.GuiTypes.client_software cinfo.GuiTypes.client_os, ++ "sr br", GuiTypes.client_software_short cinfo.GuiTypes.client_software cinfo.GuiTypes.client_os); + ("", "sr ar", (size_of_int64 cinfo.GuiTypes.client_uploaded)); + ("", "sr ar br", (size_of_int64 cinfo.GuiTypes.client_downloaded)); ]; + +@@ -357,16 +409,26 @@ + Printf.bprintf buf "\\</table\\>\\</div\\>\\<br\\>"; + + end ++ end ++ else ++ let cfile = as_file file in ++ let srcs = ref (file_all_sources cfile) in ++ Printf.bprintf buf "%d sources:\n" (List.length !srcs); ++ let print_source c = ++ Printf.bprintf buf " [%4d] " (client_num c); ++ client_bprint c buf; ++ in ++ List.iter print_source !srcs; ++ () + +- +-let file_print_sources_html (file : file) buf = ++let file_print_sources (file : file) conn = + let file = as_file_impl file in +- try file.impl_file_ops.op_file_print_sources_html file.impl_file_val buf with _ -> +- default_file_print_sources_html file buf ++ try file.impl_file_ops.op_file_print_sources file.impl_file_val conn with _ -> ++ default_file_print_sources file conn + +-let file_print_html file buf = ++let file_print file o = + let impl = as_file_impl file in +- impl.impl_file_ops.op_file_print_html impl.impl_file_val buf ++ impl.impl_file_ops.op_file_print impl.impl_file_val o + + let file_find num = + H.find files_by_num (as_file { +@@ -469,7 +531,9 @@ + let set_file_magic file magic = + match magic with + None -> () +- | Some magic -> (as_file_impl file).impl_file_magic <- Some (HashMagic.merge files_magic magic) ++ | Some magic -> ++ (as_file_impl file).impl_file_magic <- Some (intern magic); ++ file_must_update file + + let check_magic file = + let check file = +@@ -488,9 +552,10 @@ + impl.impl_file_last_seen <- age + + let file_preview (file : file) = +- let cmd = Printf.sprintf "%s \"%s\" \"%s\"" !!previewer +- (file_disk_name file) (file_best_name file) in +- ignore (Sys.command cmd) ++ ignore( ++ Unix.create_process !!previewer ++ [| Filename2.basename !!previewer; file_disk_name file; file_best_name file |] ++ Unix.stdin Unix.stdout Unix.stderr) + + (*************************************************************************) + (* *) +@@ -627,6 +692,77 @@ + ("", "sr", Printf.sprintf "%d" (file_priority file)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ if user2_allow_file_admin file o.conn_user.ui_user then ++ let optionlist = ref "" in ++ user2_users_iter (fun user -> ++ if user <> (file_owner file) then ++ optionlist := !optionlist ^ Printf.sprintf "\\<option value=\\\"%s\\\"\\>%s\\</option\\>\n" user.user_name user.user_name; ++ ); ++ ++ html_mods_td buf [("Change file owner by selecting an alternate user", "sr br", "User"); ++ ("Change owner", "sr", Printf.sprintf " ++\\<script type=\\\"text/javascript\\\"\\> ++\\<!-- ++function submitChownForm(i) { ++var formID = document.getElementById(\\\"chownForm\\\" + i) ++var v = formID.newOwner.value; ++parent.fstatus.location.href='submit?q=chown+'+v+'+%d'; ++} ++//--\\> ++\\</script\\>" (file_num file) ++ ^ "\\<table border=0 cellspacing=0 cellpadding=0\\>\\<tr\\>" ++ ^ "\\<form name=\\\"chownForm1\\\" id=\\\"chownForm1\\\" action=\\\"javascript:submitChownForm(1);\\\"\\>" ++ ^ "\\<td\\>" ++ ^ "\\<select name=\\\"newOwner\\\" id=\\\"newOwner\\\" " ++ ^ "style=\\\"padding: 0px; font-size: 10px; font-family: verdana\\\" onchange=\\\"this.form.submit()\\\"\\>" ++ ^ Printf.sprintf "\\<option value=\\\"%s\\\" selected\\>%s\\</option\\>\n" (file_owner file).user_name (file_owner file).user_name ++ ^ !optionlist ^ "\\</select\\>\\</td\\>\\</form\\>\\</tr\\>\\</table\\>" ++ ) ]; ++ ++ else ++ html_mods_td buf [("File owner", "sr br", "User"); ("", "sr", (file_owner file).user_name)]; ++ ++ Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ if user2_allow_file_admin file o.conn_user.ui_user && ++ o.conn_user.ui_user.user_groups <> [] then ++ let optionlist = ++ if (file_group file) = None then ++ ref "" ++ else ++ ref "\\<option value=\\\"None\\\"\\>None\\</option\\>\n" ++ in ++ user2_user_groups_iter (file_owner file) (fun group -> ++ if Some group <> (file_group file) then ++ optionlist := !optionlist ^ Printf.sprintf "\\<option value=\\\"%s\\\"\\>%s\\</option\\>\n" group.group_name group.group_name; ++ ); ++ ++ html_mods_td buf [("Change file group by selecting an alternate group", "sr br", "Group"); ++ ("Change group", "sr", Printf.sprintf " ++\\<script type=\\\"text/javascript\\\"\\> ++\\<!-- ++function submitChgrpForm(i) { ++var formID = document.getElementById(\\\"chgrpForm\\\" + i) ++var v = formID.newGroup.value; ++parent.fstatus.location.href='submit?q=chgrp+'+v+'+%d'; ++} ++//--\\> ++\\</script\\>" (file_num file) ++ ^ "\\<table border=0 cellspacing=0 cellpadding=0\\>\\<tr\\>" ++ ^ "\\<form name=\\\"chgrpForm1\\\" id=\\\"chgrpForm1\\\" action=\\\"javascript:submitChgrpForm(1);\\\"\\>" ++ ^ "\\<td\\>" ++ ^ "\\<select name=\\\"newGroup\\\" id=\\\"newGroup\\\" " ++ ^ "style=\\\"padding: 0px; font-size: 10px; font-family: verdana\\\" onchange=\\\"this.form.submit()\\\"\\>" ++ ^ Printf.sprintf "\\<option value=\\\"%s\\\" selected\\>%s\\</option\\>\n" (user2_print_group (file_group file)) (user2_print_group (file_group file)) ++ ^ !optionlist ^ "\\</select\\>\\</td\\>\\</form\\>\\</tr\\>\\</table\\>" ++ ) ]; ++ ++ else ++ html_mods_td buf [("File group", "sr br", "Group"); ++ ("", "sr", (match file_group file with ++ Some group -> Printf.sprintf "%s" group.group_name ++ | None -> "None"))]; ++ ++ Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ + ("Number of file sources", "sr br", "Sources"); + ("", "sr", Printf.sprintf "%d" (List.length srcs)) ]; +@@ -671,30 +807,14 @@ + ("", "sr", magic) ] + | _ -> ()); + +- ( +- if file_comment file <> [] then +- begin +- let list_header = ref true in +- List.iter (fun s -> +- Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); +- if !list_header then html_mods_td buf [ +- ("File comments", "sr br", "File comments"); +- ("", "sr", s) ] +- else +- html_mods_td buf [ +- ("", "sr br", ""); +- ("", "sr", s) ]; +- if !list_header then list_header := false) (file_comment file); +- end); +- +- file_print_html file buf; ++ file_print file o; + + Printf.bprintf buf "\\</tr\\>\\</table\\>\\</div\\>"; + Printf.bprintf buf "\\</td\\>\\</tr\\>\\</table\\>\\</div\\>\\<br\\>"; + + end else + begin +- Printf.bprintf buf "[%-s %5d]\n%s\n%s%s\nTotal %10s\nPartial %10s\npriority %d\n" ++ Printf.bprintf buf "[%-s %5d]\n%s\n%s%s\nTotal %10s\nPartial %10s\npriority %d\nOwner/Group: %s/%s\n" + n.network_name + (file_num file) + (shorten (file_best_name file) 80) +@@ -704,7 +824,11 @@ + (string_of_uids info.G.file_uids) + (Int64.to_string info.G.file_size) + (Int64.to_string info.G.file_downloaded) +- (file_priority file); ++ (file_priority file) ++ (file_owner file).user_name ++ (match file_group file with ++ Some group -> Printf.sprintf "%s" group.group_name ++ | None -> "private"); + Printf.bprintf buf "Chunks: [%-s]\n" + (match info.G.file_chunks with + | None -> "" +@@ -713,24 +837,12 @@ + None -> () + | Some filename -> + Printf.bprintf buf "Probable name: %s\n" filename); +- List.iter (fun name -> Printf.bprintf buf " (%s)\n" name) info.G.file_names ++ List.iter (fun name -> Printf.bprintf buf " (%s)\n" name) info.G.file_names; ++ file_print file o + end; + + (try +- +- if !!print_all_sources then begin +- if use_html_mods o then +- file_print_sources_html file buf +- else begin +- Printf.bprintf buf "%d sources:\n" (List.length srcs); +- let print_source c = +- Printf.bprintf buf " [%4d] " (client_num c); +- client_bprint c buf; +- in +- List.iter print_source srcs; +- end; +- end +- ++ if !!print_all_sources then file_print_sources file o + with _ -> ()) + + let file_print_ed2k_link filename filesize md4hash = +@@ -869,6 +981,7 @@ + op_file_commit = (fun _ _ -> ni_ok network "file_commit"); + op_file_save_as = (fun _ _ -> ni_ok network "file_save_as"); + (* op_file_print = (fun _ _ -> ni_ok network "file_print"); *) ++ op_file_shared = (fun _ -> None); + op_file_to_option = (fun _ -> fni network "file_to_option"); + op_file_cancel = (fun _ -> ni_ok network "file_cancel"); + op_file_info = (fun _ -> fni network "file_info"); +@@ -881,9 +994,10 @@ + op_file_set_format = (fun _ -> fni network "file_set_format"); + op_file_all_sources = (fun _ -> fni network "file_all_sources"); + op_file_active_sources = (fun _ -> fni network "file_active_sources"); ++ op_file_comment = (fun _ -> ni_ok network "file_comment"; ""); + op_file_set_priority = (fun _ _ -> ni_ok network "file_set_priority"); +- op_file_print_html = (fun _ _ -> ni_ok network "file_print_html"); +- op_file_print_sources_html = (fun _ _ -> fni network "file_print_sources_html"); ++ op_file_print = (fun _ _ -> ni_ok network "file_print_html"); ++ op_file_print_sources = (fun _ _ -> fni network "file_print_sources"); + op_file_debug = (fun _ -> ""); + op_file_proposed_filenames = (fun impl -> []); + } +@@ -906,6 +1020,8 @@ + lprintf_nl "op_file_commit"; + if c.op_file_save_as == cc.op_file_save_as then + lprintf_nl "op_file_save_as"; ++ if c.op_file_shared == cc.op_file_shared then ++ lprintf_nl "op_file_shared"; + if c.op_file_cancel == cc.op_file_cancel then + lprintf_nl "op_file_cancel"; + if c.op_file_pause == cc.op_file_pause then +@@ -924,10 +1040,10 @@ + lprintf_nl "op_file_all_sources"; + if c.op_file_active_sources == cc.op_file_active_sources then + lprintf_nl "op_file_active_sources"; +- if c.op_file_print_html == cc.op_file_print_html then +- lprintf_nl "op_file_print_html"; +- if c.op_file_print_sources_html == cc.op_file_print_sources_html then +- lprintf_nl "op_file_print_sources_html"; ++ if c.op_file_print == cc.op_file_print then ++ lprintf_nl "op_file_print"; ++ if c.op_file_print_sources == cc.op_file_print_sources then ++ lprintf_nl "op_file_print_sources"; + ) !files_ops; + lprint_newline () + +@@ -949,12 +1065,6 @@ + ) files_by_num; + Printf.bprintf buf " files: %d\n" !counter; + Printf.bprintf buf " files_ops: %d\n" (List.length !files_ops); +- let counter = ref 0 in +- HashMagic.iter (fun _ -> incr counter) files_magic; +- Printf.bprintf buf " files_magic: %d\n" !counter; +- let counter = ref 0 in +- HashComments.iter (fun _ -> incr counter) file_comments; +- Printf.bprintf buf " files_comments: %d\n" !counter + ) + + +@@ -1072,7 +1182,7 @@ + let module T = GuiTypes in + { + T.file_fields = T.Fields_file_info.all; +- T.file_comment = ""; ++ T.file_comment = impl.impl_file_comment; + T.file_name = impl.impl_file_best_name; + T.file_names = impl.impl_file_filenames; + T.file_num = impl.impl_file_num; +@@ -1095,4 +1205,14 @@ + T.file_chunks_age = [||]; + T.file_uids = []; + T.file_sub_files = []; ++ T.file_magic = impl.impl_file_magic; ++ T.file_comments = []; ++ T.file_user = impl.impl_file_owner.user_name; ++ T.file_group = user2_print_group impl.impl_file_group; ++ T.file_release = impl.impl_file_release; + } ++ ++let lprintf_file_nl file fmt = ++ lprintf_nl2 ("[" ^ (file_network file).network_shortname ^ ++ "] [file_num " ^ (string_of_int (file_num file)) ^ "]" ^ ++ "[temp " ^ (file_disk_name file) ^ "]") fmt +Index: src/daemon/common/commonFile.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonFile.mli,v +retrieving revision 1.20 +retrieving revision 1.30 +diff -u -r1.20 -r1.30 +--- src/daemon/common/commonFile.mli 5 Sep 2006 14:18:24 -0000 1.20 ++++ src/daemon/common/commonFile.mli 12 Nov 2006 12:44:24 -0000 1.30 +@@ -18,10 +18,12 @@ + *) + + type 'a file_impl = { ++ mutable impl_file_owner : CommonTypes.userdb; ++ mutable impl_file_group : CommonTypes.groupdb option; + mutable impl_file_update : int; + mutable impl_file_state : CommonTypes.file_state; + +- mutable impl_file_comment : string list; ++ mutable impl_file_comment : string; + mutable impl_file_num : int; + mutable impl_file_val : 'a; + mutable impl_file_ops : 'a file_ops; +@@ -36,6 +38,7 @@ + mutable impl_file_filenames : string list; + mutable impl_file_magic : string option; + mutable impl_file_priority : int; ++ mutable impl_file_release : bool; + mutable impl_file_last_seen : int; + mutable impl_file_probable_name : string option; + } +@@ -43,6 +46,7 @@ + mutable op_file_network : CommonTypes.network; + mutable op_file_commit : 'a -> string -> unit; + mutable op_file_save_as : 'a -> string -> unit; ++ mutable op_file_shared : 'a -> CommonTypes.shared option; + mutable op_file_to_option : 'a -> (string * Options.option_value) list; + mutable op_file_cancel : 'a -> unit; + mutable op_file_pause : 'a -> unit; +@@ -53,9 +57,10 @@ + mutable op_file_recover : 'a -> unit; + mutable op_file_all_sources : 'a -> CommonTypes.client list; + mutable op_file_active_sources : 'a -> CommonTypes.client list; ++ mutable op_file_comment : 'a -> string; + mutable op_file_set_priority : 'a -> int -> unit; +- mutable op_file_print_html: 'a -> Buffer.t -> unit; +- mutable op_file_print_sources_html : 'a -> Buffer.t -> unit; ++ mutable op_file_print: 'a -> CommonTypes.ui_conn -> unit; ++ mutable op_file_print_sources : 'a -> CommonTypes.ui_conn -> unit; + mutable op_file_files : ('a -> 'a file_impl -> CommonTypes.file list); + mutable op_file_debug : 'a -> string; + mutable op_file_proposed_filenames : 'a -> string list; +@@ -75,10 +80,14 @@ + val update_file_state : 'a file_impl -> CommonTypes.file_state -> unit + val file_to_option : CommonTypes.file -> (string * Options.option_value) list + val file_save_as : CommonTypes.file -> string -> unit ++val file_shared : CommonTypes.file -> CommonTypes.shared option ++val file_comment : CommonTypes.file -> string + val file_network : CommonTypes.file -> CommonTypes.network + val file_info : CommonTypes.file -> GuiTypes.file_info +-val file_pause : CommonTypes.file -> unit +-val file_resume : CommonTypes.file -> unit ++val file_pause : CommonTypes.file -> CommonTypes.userdb -> unit ++val file_resume : CommonTypes.file -> CommonTypes.userdb -> unit ++val set_file_release : CommonTypes.file -> bool -> CommonTypes.userdb -> unit ++val file_release : CommonTypes.file -> bool + val set_file_state : CommonTypes.file -> CommonTypes.file_state -> unit + val file_best_name : CommonTypes.file -> string + val set_file_best_name : CommonTypes.file -> string -> string -> int -> unit +@@ -90,7 +99,7 @@ + val file_preview : CommonTypes.file -> unit + val file_all_sources : CommonTypes.file -> CommonTypes.client list + val file_active_sources : CommonTypes.file -> CommonTypes.client list +-val file_print_sources_html : CommonTypes.file -> Buffer.t -> unit ++val file_print_sources : CommonTypes.file -> CommonTypes.ui_conn -> unit + val files_ops : (int file_ops * int file_ops) list ref + val new_file_ops : CommonTypes.network -> 'a file_ops + val check_file_implementations : unit -> unit +@@ -118,8 +127,7 @@ + val set_file_last_seen : CommonTypes.file -> int -> unit + val file_debug : CommonTypes.file -> string + val set_file_comment : CommonTypes.file -> string -> unit +-val file_comment : CommonTypes.file -> string list +-val file_comment_length : CommonTypes.file -> int ++val file_comment : CommonTypes.file -> string + val file_magic : CommonTypes.file -> string option + val set_file_magic : CommonTypes.file -> string option -> unit + val check_magic : CommonTypes.file -> unit +@@ -136,3 +144,13 @@ + + val forceable_download : CommonTypes.result_info list ref + val impl_file_info : 'a file_impl -> GuiTypes.file_info ++ ++val user2_filter_files : CommonTypes.file list -> CommonTypes.userdb -> CommonTypes.file list ++val user2_num_user_dls : CommonTypes.userdb -> int ++val user2_num_group_dls : CommonTypes.groupdb -> int ++val user2_allow_file_admin : CommonTypes.file -> CommonTypes.userdb -> bool ++val set_file_owner : CommonTypes.file -> CommonTypes.userdb -> unit ++val file_owner : CommonTypes.file -> CommonTypes.userdb ++val set_file_group : CommonTypes.file -> CommonTypes.groupdb option -> unit ++val file_group : CommonTypes.file -> CommonTypes.groupdb option ++val lprintf_file_nl : CommonTypes.file -> ('a, unit, unit) Pervasives.format -> 'a +Index: src/daemon/common/commonGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonGlobals.ml,v +retrieving revision 1.71 +retrieving revision 1.76 +diff -u -r1.71 -r1.76 +--- src/daemon/common/commonGlobals.ml 16 Sep 2006 15:36:08 -0000 1.71 ++++ src/daemon/common/commonGlobals.ml 21 Nov 2006 22:34:33 -0000 1.76 +@@ -117,7 +117,7 @@ + with e -> slen + in + let diff_len_utf8_ascii = slen - len in +- let max_len = maxi limit 10 in ++ let max_len = max limit 10 in + if len > max_len then + let prefix = String.sub name 0 (max_len - 7 + diff_len_utf8_ascii) in + let suffix = String.sub name (len - 4 + diff_len_utf8_ascii) 4 in +@@ -177,7 +177,7 @@ + cc.control_state <- cc.control_state + 1 + + let connection_next_try cc = +- cc.control_last_try + mini (cc.control_min_reask * cc.control_state) ++ cc.control_last_try + min (cc.control_min_reask * cc.control_state) + cc.control_min_reask + + let connection_can_try cc = +@@ -238,7 +238,7 @@ + float_of_int (if !!max_hard_upload_rate = 0 then + 10000 * 1024 + else +- maxi (!!max_hard_upload_rate * 1024) 1024) *. 0.90; ++ max (!!max_hard_upload_rate * 1024) 1024) *. 0.90; + ); + option_hook max_hard_download_rate (fun _ -> + check_ul_dl_ratio (); +@@ -481,17 +481,18 @@ + + let log_chat_message i num n s = + Fifo.put chat_message_fifo (last_time(),i,num,n,s); +- try ++ (try + Unix2.tryopen_write_gen !messages_log [Open_creat; Open_wronly; Open_append] + 0o600 (fun oc -> + Printf.fprintf oc "%s: %s (%s): %s\n" (Date.simple (BasicSocket.date_of_int (last_time ()))) n i s) + with e -> + lprintf_nl "[ERROR] Exception %s while trying to log message to %s" +- (Printexc2.to_string e) !messages_log; ++ (Printexc2.to_string e) !messages_log); + + while (Fifo.length chat_message_fifo) > !!html_mods_max_messages do + ignore(Fifo.take chat_message_fifo) + done ++ + let last_message_log = ref 0 + + +@@ -499,7 +500,7 @@ + let debug_clients = ref Intset.empty + + let default_user = { +- ui_user_name = CommonUserDb.admin_user; ++ ui_user = CommonUserDb.admin_user; + ui_user_searches = []; + ui_last_search = None; + ui_last_results = []; +@@ -513,7 +514,7 @@ + match list with + [] -> + let u = { +- ui_user_name = user; ++ ui_user = (CommonUserDb.user2_user_find user); + ui_user_searches = []; + ui_last_search = None; + ui_last_results = []; +@@ -522,7 +523,7 @@ + ui_users := u :: !ui_users; + u + | u :: tail -> +- if u.ui_user_name = user then u else iter tail ++ if u.ui_user = (CommonUserDb.user2_user_find user) then u else iter tail + in + iter !ui_users + +@@ -617,8 +618,12 @@ + put time sample short_delay_bandwidth_samples; + trimto 5 short_delay_bandwidth_samples + ++let history_step = 5 + let history_size = 720 ++let history_h_step = history_size * history_step + let history_h_size = 720 ++let history_timeflag = ref 0. ++let history_h_timeflag = ref 0. + + let upload_history = Fifo.create () + let download_history = Fifo.create () +@@ -637,7 +642,7 @@ + let update_download_history () = + Fifo.put download_history (download_usage ()); + let len = ref (Fifo.length download_history) in +- while !len > history_size do ++ while !len > history_size+1 do + ignore (Fifo.take download_history); + decr len + done +@@ -645,7 +650,7 @@ + let update_upload_history () = + Fifo.put upload_history (upload_usage ()); + let len = ref (Fifo.length upload_history) in +- while !len > history_size do ++ while !len > history_size+1 do + ignore (Fifo.take upload_history); + decr len + done +@@ -653,7 +658,7 @@ + let update_h_download_history () = + Fifo.put download_h_history ((List.fold_left (+) 0 (Fifo.to_list download_history)) / ((Fifo.length download_history))); + let len = ref (Fifo.length download_h_history) in +- while !len > history_h_size do ++ while !len > history_h_size+1 do + ignore (Fifo.take download_h_history); + decr len + done +@@ -661,19 +666,19 @@ + let update_h_upload_history () = + Fifo.put upload_h_history ((List.fold_left (+) 0 (Fifo.to_list upload_history)) / ((Fifo.length upload_history))); + let len = ref (Fifo.length upload_h_history) in +- while !len > history_h_size do ++ while !len > history_h_size+1 do + ignore (Fifo.take upload_h_history); + decr len + done + + let detected_link_capacity link = +- List.fold_left maxi 0 (Fifo.to_list link) ++ List.fold_left max 0 (Fifo.to_list link) + + let detected_uplink_capacity () = +- List.fold_left maxi 0 (Fifo.to_list upload_history) ++ List.fold_left max 0 (Fifo.to_list upload_history) + + let detected_downlink_capacity () = +- List.fold_left maxi 0 (Fifo.to_list download_history) ++ List.fold_left max 0 (Fifo.to_list download_history) + + + let new_tag name v = +@@ -890,18 +895,36 @@ + activity := new_activity () + ) + +-module HashMagic = Weak.Make(struct ++module StringIntern = Weak.Make(struct + type t = string + let hash s = Hashtbl.hash s + let equal x y = x = y + end) + +-let files_magic = HashMagic.create 100 ++let intern_table = StringIntern.create 1000 ++let intern s = StringIntern.merge intern_table s + +-module HashComments = Weak.Make(struct +- type t = string +- let hash s = Hashtbl.hash s +- let equal x y = x = y +- end) ++let print_command_result o buf result = ++ if use_html_mods o then ++ html_mods_table_one_row buf "serversTable" "servers" [ ++ ("", "srh", result); ] ++ else ++ Printf.bprintf buf "%s" result + +-let file_comments = HashComments.create 1000 ++let _ = ++ Heap.add_memstat "CommonGlobals" (fun level buf -> ++ let counter = ref 0 in ++ StringIntern.iter (fun f -> incr counter;) intern_table; ++ Printf.bprintf buf " intern_table: %d\n" !counter; ++ Printf.bprintf buf " core_gui_fifo: %d\n" (Fifo.length core_gui_fifo); ++ Printf.bprintf buf " gui_core_fifo: %d\n" (Fifo.length gui_core_fifo); ++ Printf.bprintf buf " chat_message_fifo: %d\n" (Fifo.length chat_message_fifo); ++ Printf.bprintf buf " upload_history: %d\n" (Fifo.length upload_history); ++ Printf.bprintf buf " download_history: %d\n" (Fifo.length download_history); ++ Printf.bprintf buf " upload_h_history: %d\n" (Fifo.length upload_h_history); ++ Printf.bprintf buf " download_h_history: %d\n" (Fifo.length download_h_history); ++ Printf.bprintf buf " bandwidth_samples: %d\n" (Fifo.length bandwidth_samples); ++ Printf.bprintf buf " short_delay_bandwidth_samples: %d\n" (Fifo.length short_delay_bandwidth_samples); ++ Printf.bprintf buf " dummy_sample: %d\n" (Array.length dummy_sample); ++ Printf.bprintf buf " activities: %d\n" (Fifo.length activities); ++ ) +Index: src/daemon/common/commonInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonInteractive.ml,v +retrieving revision 1.80 +retrieving revision 1.84 +diff -u -r1.80 -r1.84 +--- src/daemon/common/commonInteractive.ml 5 Sep 2006 14:18:24 -0000 1.80 ++++ src/daemon/common/commonInteractive.ml 9 Nov 2006 21:32:26 -0000 1.84 +@@ -40,6 +40,7 @@ + open CommonServer + open CommonTypes + open CommonComplexOptions ++open CommonUserDb + + let log_prefix = "[cInt]" + +@@ -179,7 +180,11 @@ + ("DLFILES", string_of_int (List.length !!files)); + ("INCOMING", incoming); + ("NETWORK", network.network_name); +- ("ED2K_HASH", (file_print_ed2k_link filename (file_size file) info.G.file_md4))] ++ ("ED2K_HASH", (file_print_ed2k_link filename (file_size file) info.G.file_md4)); ++ ("FILE_OWNER",(file_owner file).user_name); ++ ("FILE_GROUP",user2_print_group (file_group file)); ++ ] ++ + with e -> + lprintf_nl "Exception %s while executing %s" + (Printexc2.to_string e) !!file_completed_cmd +@@ -207,26 +212,13 @@ + (try + let file_name = file_disk_name file in + let incoming = +- if Unix2.is_directory file_name then +- incoming_directories () +- else +- incoming_files () ++ incoming_dir ++ (Unix2.is_directory file_name) ++ ~needed_space:(file_size file) ++ ~user:(file_owner file) ++ () + in + +-(* check if temp_directory and incoming are on different partitions *) +- if (Unix.stat incoming.shdir_dirname).Unix.st_dev <> +- (Unix.stat !!temp_directory).Unix.st_dev +- then +- begin +- match Unix32.diskfree incoming.shdir_dirname with +- Some v -> if v < (file_size file) then begin +- send_dirfull_warning incoming.shdir_dirname true +- (Printf.sprintf "can not commit %s" (file_best_name file)); +- raise Incoming_full +- end +- | _ -> () +- end; +- + let new_name = file_commited_name incoming.shdir_dirname file in + if Unix2.is_directory file_name then begin + Unix2.safe_mkdir new_name; +@@ -275,11 +267,15 @@ + with e -> + lprintf_nl "Exception %s in file_commit secondaries" (Printexc2.to_string e); + ) secondary_files +- with e -> +- lprintf_nl "Exception in file_commit: %s" (Printexc2.to_string e)) ++ with ++ Incoming_full -> ++ send_dirfull_warning "" true ++ (Printf.sprintf "all incoming dirs are full, can not commit %s" (file_best_name file)) ++ | e -> lprintf_nl "Exception in file_commit: %s" (Printexc2.to_string e)) + | _ -> assert false + +-let file_cancel file = ++let file_cancel file user = ++ if user2_allow_file_admin file user then + try + let impl = as_file_impl file in + if impl.impl_file_state <> FileCancelled then +@@ -308,7 +304,8 @@ + lprintf_nl "Exception in file_cancel: %s" (Printexc2.to_string e) + + let mail_for_completed_file file = +- if !!mail <> "" then ++ let usermail = (file_owner file).user_mail in ++ if !!mail <> "" || usermail <> "" then begin + let module M = Mailer in + let info = file_info file in + let line1 = "mldonkey has completed the download of:\r\n\r\n" in +@@ -320,11 +317,8 @@ + (let age = (BasicSocket.last_time ()) - info.G.file_age in Date.time_to_string age "verbose") + in + +- let line3 = if (file_comment file) = [] then "" else +- let buf = Buffer.create 1000 in +- Printf.bprintf buf "\r\nComments:\r\n"; +- List.iter (fun s -> Printf.bprintf buf "%s\r\n" s;) (file_comment file); +- Buffer.contents buf ++ let line3 = if (file_comment file) = "" then "" else ++ Printf.sprintf "\r\nComment: %s\r\n" (file_comment file) + in + + let subject = if !!filename_in_subject then +@@ -333,12 +327,8 @@ + Printf.sprintf "mldonkey@%s, file received" (Unix.gethostname ()) + in + +- let incoming = +- if Unix2.is_directory (file_disk_name file) then +- incoming_directories () +- else +- incoming_files () +- in ++(* TODO: This information can be wrong *) ++ let incoming = incoming_dir (Unix2.is_directory (file_disk_name file)) () in + + let line4 = if !!url_in_mail = "" then "" else + Printf.sprintf "\r\n<%s/%s/%s>\r\n" !!url_in_mail incoming.shdir_dirname (Url.encode (file_best_name file)) +@@ -348,13 +338,22 @@ + Printf.sprintf "\r\nauto_commit is disabled, file is not committed to incoming" + in + +- let mail = { +- M.mail_to = !!mail; +- M.mail_from = !!mail; ++ let line6 = ++ Printf.sprintf "\r\nUser/Group: %s:%s\r\n" (file_owner file).user_name (user2_print_group (file_group file)) ++ in ++ ++ let send_mail address admin = ++ let mail = { ++ M.mail_to = address; ++ M.mail_from = address; + M.mail_subject = subject; +- M.mail_body = line1 ^ line2 ^ line3 ^ line4 ^ line5; ++ M.mail_body = line1 ^ line2 ^ line3 ^ line4 ^ line5 ^ (if admin then line6 else ""); + } in +- M.sendmail !!smtp_server !!smtp_port !!add_mail_brackets mail ++ M.sendmail !!smtp_server !!smtp_port !!add_mail_brackets mail ++ in ++ if !!mail <> "" then send_mail !!mail true; (* Multiuser ToDo: this mail is for the admin user, optional? *) ++ if usermail <> "" && usermail <> !!mail then (try send_mail usermail false with Not_found -> ()) ++ end + + let file_completed (file : file) = + try +@@ -502,7 +501,7 @@ + let display_bw_stats = ref false + + let start_download file = +- if !!pause_new_downloads then file_pause file; ++ if !!pause_new_downloads then file_pause file admin_user; + if !!file_started_cmd <> "" then + MlUnix.fork_and_exec !!file_started_cmd + [| +@@ -521,7 +520,7 @@ + | Some s -> + let result = List.assoc (int_of_string arg) user.ui_last_results in + let files = CommonResult.result_download +- result [] false in ++ result [] false user.ui_user in + List.iter start_download files; + "download started" + with +@@ -940,92 +939,123 @@ + and the ones with lowest priority in FileQueued state, if there + is a max_concurrent_downloads constraint. + +-In the future, we could try to mix this with the multi-users +-system to give some fairness between downloads of different +-users. +- + **************************************************************) + + open CommonFile + ++type user_file_list = { ++ file_list : file list; ++ downloads_allowed : int option; ++} ++ + let force_download_quotas () = +- let files = List.sort (fun f1 f2 -> +- let v = file_priority f2 - file_priority f1 in +- if v <> 0 then v else +- (** +- * [egs] do not start downloading +- * a small file against an already active download +- **) +- let d1 = file_downloaded f1 in +- let d2 = file_downloaded f2 in +- if d1 = 0L && d2 > 0L then 1 +- else +- if d1 > 0L && d2 = 0L then -1 +- else +- (* Try to download in priority files with fewer bytes missing +- Rationale: once completed, it may allow to recover some disk space *) +- let r1 = file_size f1 -- d1 in +- let r2 = file_size f2 -- d2 in +- if r1 = r2 then 0 else +- if r2 < r1 then 1 else -1 +- ) !!CommonComplexOptions.files in +- +- (** move running and queued downloads from [list] to [files] +- accumulator, until a drop of priority (or end of list) is +- encountered; Then submit the batches of downloads with same +- priority to [iter_line]. +- +- @param ndownloads number of running downloads no longer in [list] +- @param nqueued number of queued downloads in [files] +- *) +- let rec iter list priority files ndownloads nqueued = +- match list, files with +- | [], [] -> () +- | [], _ -> +- iter_line list priority files ndownloads nqueued +- | f :: tail , _ :: _ when file_priority f < priority -> +- iter_line list priority files ndownloads nqueued +- | f :: tail, files -> +- match file_state f with +- | FileDownloading -> +- iter tail (file_priority f) (f :: files) (ndownloads+1) nqueued +- | FileQueued -> +- iter tail (file_priority f) (f :: files) ndownloads (nqueued+1) +- | _ -> +- iter tail (file_priority f) files ndownloads nqueued +- +- (** queue or unqueue downloads from [files] list to match quotas *) +- and iter_line list priority files ndownloads nqueued = +- if ndownloads > !!max_concurrent_downloads then +- match files with +- | [] -> assert false +- | f :: tail -> +- match file_state f with +- | FileDownloading -> +- set_file_state f FileQueued; +- iter_line list priority tail (ndownloads-1) nqueued +- | _ -> iter_line list priority tail ndownloads (nqueued-1) +- else +- if ndownloads < !!max_concurrent_downloads && nqueued > 0 then +- match files with +- | [] -> assert false +- | f :: tail -> +- match file_state f with +- | FileQueued -> +- set_file_state f FileDownloading; +- iter_line list priority tail (ndownloads+1) (nqueued-1) +- | _ -> iter_line list priority tail ndownloads nqueued +- else +- iter list priority [] ndownloads 0 + +- in +- if not !all_temp_queued then +- iter files max_int [] 0 0 ++ let queue_files files = ++ List.iter (fun file -> ++ if file_state file = FileDownloading then ++ set_file_state file FileQueued ++ ) files in ++ ++ let queue_user_file_list (_user, user_file_list) = ++ queue_files user_file_list.file_list in ++ ++ if !all_temp_queued then ++ queue_files !!CommonComplexOptions.files + else +- List.iter (fun f -> +- if file_state f = FileDownloading then +- set_file_state f FileQueued +- ) files ++ ++ (* create the assoc list of downloads of each user *) ++ let files_by_user = List.fold_left (fun acc f -> ++ let owner = CommonFile.file_owner f in ++ try ++ let owner_file_list = List.assoc owner acc in ++ (owner, { owner_file_list with ++ file_list = f :: owner_file_list.file_list }) :: ++ List.remove_assoc owner acc ++ with Not_found -> ++ (owner, { ++ downloads_allowed = ++ (match owner.user_max_concurrent_downloads with ++ | 0 -> None ++ | i -> Some i); ++ file_list = [f] }) :: acc ++ ) [] !!CommonComplexOptions.files in ++ ++ (* sort each user's list separately *) ++ let files_by_user = List.map (fun (owner, owner_file_list) -> ++ owner, { owner_file_list with ++ file_list = List.sort (fun f1 f2 -> ++ let v = compare (file_priority f2) (file_priority f1) in ++ if v <> 0 then v else ++ (* [egs] do not start downloading a small file ++ against an already active download *) ++ let d1 = file_downloaded f1 in ++ let d2 = file_downloaded f2 in ++ let active1 = d1 > 0L in ++ let active2 = d2 > 0L in ++ if not active1 && active2 then 1 ++ else if active1 && not active2 then -1 ++ else ++ (* Try to download in priority files with fewer bytes missing ++ Rationale: once completed, it may allow to recover some disk space *) ++ let remaining1 = file_size f1 -- d1 in ++ let remaining2 = file_size f2 -- d2 in ++ compare remaining1 remaining2 ++ ) owner_file_list.file_list } ++ ) files_by_user in ++ ++ (* sort the assoc list itself with user with highest quota first *) ++ let files_by_user = ++ List.sort (fun (_owner1, { downloads_allowed = allowed1 }) ++ (_owner2, { downloads_allowed = allowed2 }) -> ++ match allowed1, allowed2 with ++ | None, None -> 0 ++ | None, _ -> -1 ++ | _, None -> 1 ++ | Some allowed1, Some allowed2 -> compare allowed2 allowed1 ++ ) files_by_user in ++ ++ (* serve users round-robin, starting with the one with highest quota *) ++ let rec iter downloads_left to_serve served = ++ if downloads_left = 0 then begin ++ List.iter queue_user_file_list to_serve; ++ List.iter queue_user_file_list served ++ end else ++ match to_serve with ++ | [] -> ++ if served = [] then () (* nothing left to rotate *) ++ else (* new round *) ++ iter downloads_left served [] ++ | (_owner, { file_list = [] }) :: others -> ++ (* user satisfied, remove from lists *) ++ iter downloads_left others served ++ | ((_owner, { downloads_allowed = Some 0 }) as first) :: others -> ++ (* reached quota, remove from future rounds *) ++ queue_user_file_list first; ++ iter downloads_left others served ++ | (owner, { file_list = first_file :: other_files; ++ downloads_allowed = allowed }) :: others -> ++ let is_downloading = ++ match file_state first_file with ++ | FileDownloading -> true ++ | FileQueued -> ++ set_file_state first_file FileDownloading; ++ true ++ | _ -> false in ++ if is_downloading then ++ iter (downloads_left - 1) others ++ ((owner, { ++ file_list = other_files; ++ downloads_allowed = match allowed with ++ | None -> None ++ | Some i -> Some (i - 1) ++ }) :: served) ++ else ++ iter downloads_left others ++ ((owner, { ++ file_list = other_files; ++ downloads_allowed = allowed ++ }) :: served) in ++ iter !!max_concurrent_downloads files_by_user [] + + let _ = + option_hook max_concurrent_downloads (fun _ -> +Index: src/daemon/common/commonMessages.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonMessages.ml,v +retrieving revision 1.55 +retrieving revision 1.60 +diff -u -r1.55 -r1.60 +--- src/daemon/common/commonMessages.ml 26 Aug 2006 12:05:53 -0000 1.55 ++++ src/daemon/common/commonMessages.ml 5 Nov 2006 14:11:29 -0000 1.60 +@@ -47,103 +47,354 @@ + string_option + " + body { +-background: @color_background@; margin-top: 3px; margin-left: 5px; margin-right: 5px; +-font-family: Verdana, sans-serif; font-size: 12px; +-scrollbar-face-color: @color_scrollbar_face@; scrollbar-shadow-color: @color_scrollbar_face@; +-scrollbar-highlight-color: @color_scrollbar_highlight@; scrollbar-3dlight-color: @color_some_scrollbar@; +-scrollbar-darkshadow-color: @color_some_scrollbar@; scrollbar-track-color: @color_background@; +-scrollbar-arrow-color: @color_some_scrollbar@; } +-table.commands { border: @color_general_border@ solid 1px; background: @color_background@ } +-table.topcommands { background: @color_background@; border: @color_general_border@ solid 1px; border-top: @color_scrollbar_highlight@ solid 1px; border-left: @color_scrollbar_highlight@ solid 1px; } +-pre { color: @color_general_text@; font-family: Courier, Arial, Helvetica, sans-serif; font-size: 12px; } +-p { color: @color_general_text@; font-family: Verdana, Courier, Arial, Helvetica, sans-serif; font-size: 12px; } +-input.txt { background: @color_input_text@ } +-input.txt2 { background: @color_bbig_background@; +-font: 12px courier; padding: 0px; +-width: 38px; height: 18px; line-height: 14px; color: @color_general_text@; +-border-right: @color_some_border@ 2px solid; border-top: @color_general_border@ 1px solid; border-left: @color_general_border@ 1px solid; border-bottom: @color_some_border@ 2px solid; } +-input.but2 { background: @color_bsmall3@; +-border: 0px; padding: 0px; font: bold 10px verdana; +-width: 36px; height: 14px; } +-input.but { background: @color_input_button@ }; +- +-a:link,a:active,a:visited { text-decoration: none; font-face: verdana; +-font-size: 10px; color: @color_anchor@; } +-a:hover { color: @color_anchor_hover@; text-decoration: underline;} ++ background: @color_background@; ++ margin-top: 3px; ++ margin-left: 5px; ++ margin-right: 5px; ++ font-family: Verdana, sans-serif; ++ font-size: 12px; ++ } ++table.commands { ++ border: @color_general_border@ solid 1px; ++ background: @color_background@; ++ } ++table.topcommands { ++ background: @color_background@; ++ border: @color_general_border@ solid 1px; ++ border-top: @color_scrollbar_highlight@ solid 1px; ++ border-left: @color_scrollbar_highlight@ solid 1px; ++ } ++pre { ++ color: @color_general_text@; ++ font-family: Courier, Arial, Helvetica, sans-serif; ++ font-size: 12px; ++ } ++p { ++ color: @color_general_text@; ++ font-family: Verdana, Courier, Arial, Helvetica, sans-serif; ++ font-size: 12px; ++ } ++input.txt { ++ background: @color_input_text@; ++ } ++input.txt2 { ++ background: @color_bbig_background@; ++ font: 12px courier; ++ padding: 0px; ++ width: 38px; ++ height: 18px; ++ line-height: 14px; ++ color: @color_general_text@; ++ border-right: @color_some_border@ 2px solid; ++ border-top: @color_general_border@ 1px solid; ++ border-left: @color_general_border@ 1px solid; ++ border-bottom: @color_some_border@ 2px solid; ++ } ++input.but2 { ++ background: @color_bsmall3@; ++ border: 0px; ++ padding: 0px; ++ font: bold 10px verdana; ++ width: 36px; ++ height: 14px; ++ } ++input.but { ++ background: @color_input_button@; ++ } ++a:link, a:active, a:visited { ++ text-decoration: none; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_anchor@; ++ } ++a:hover { ++ color: @color_anchor_hover@; ++ text-decoration: underline; ++ } + .bu { +-vertical-align: middle; white-space: nowrap; +-background: @color_chunk3@; color: @color_foreground_text_for_top_buttons@; +-font-family: Verdana; font-size: 9px; line-height: 12px; +-margin-top: 0px; margin-bottom: 0px; +-padding-left: 6px; padding-right: 6px; padding-top: 1px; padding-bottom: 1px; +-border: @color_some_border@ 0px solid; } ++ vertical-align: middle; ++ white-space: nowrap; ++ background: @color_chunk3@; ++ color: @color_foreground_text_for_top_buttons@; ++ font-family: Verdana; ++ font-size: 9px; ++ line-height: 12px; ++ margin-top: 0px; ++ margin-bottom: 0px; ++ padding-left: 6px; ++ padding-right: 6px; ++ padding-top: 1px; ++ padding-bottom: 1px; ++ border: @color_some_border@ 0px solid; ++ } + .bbig { +-text-align: center; font-size: 10px; font-family: Verdana; font-weight: 500; +-border-top: @color_scrollbar_highlight@ 1px solid; border-left: @color_scrollbar_highlight@ 1px solid; border-bottom: @color_general_border@ 1px solid; border-right: @color_general_border@ 1px solid; +-padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; +-color: @color_general_text@; background: @color_bbig_background@; } ++ text-align: center; ++ font-size: 10px; ++ font-family: Verdana; ++ font-weight: 500; ++ border-top: @color_scrollbar_highlight@ 1px solid; ++ border-left: @color_scrollbar_highlight@ 1px solid; ++ border-bottom: @color_general_border@ 1px solid; ++ border-right: @color_general_border@ 1px solid; ++ padding-left: 4px; ++ padding-right: 4px; ++ padding-top: 1px; ++ padding-bottom: 1px; ++ color: @color_general_text@; ++ background: @color_bbig_background@; ++ } + .bbigm { +-text-align: center; font: bold 10px verdana; +-border-top: @color_scrollbar_highlight@ 1px solid; border-left: @color_scrollbar_highlight@ 1px solid; border-bottom: @color_general_border@ 1px solid; border-right: @color_general_border@ 1px solid; +-padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; +-color: @color_general_text@; background: @color_bsmall3@; } +-.bsmall { background: @color_bsmall_back@; } +-.bsmall1 { background: @color_bbig_background@; } +-.bsmall2 { background: @color_bsmall2@; } +-.bsmall3 { background: @color_bsmall3@; } +-.bbig2 { background: @color_bsmall3@; } +-.bbig3 { background: @color_scrollbar_face@; } +-.b1 { border-left: @color_border_of_top_buttons@ solid 1px; border-top: @color_border_of_top_buttons@ solid 1px; border-right: @color_border_of_top_buttons@ solid 1px; border-bottom: @color_border_of_top_buttons@ solid 1px; } +-.b2 { border-left: @color_border_of_top_buttons@ solid 0px; border-top: @color_border_of_top_buttons@ solid 1px; border-right: @color_border_of_top_buttons@ solid 1px; border-bottom: @color_border_of_top_buttons@ solid 1px; } +-.b3 { border-left: @color_border_of_top_buttons@ solid 1px; border-top: @color_border_of_top_buttons@ solid 0px; border-right: @color_border_of_top_buttons@ solid 1px; border-bottom: @color_border_of_top_buttons@ solid 1px; } +-.b4 { border-left: @color_border_of_top_buttons@ solid 0px; border-top: @color_border_of_top_buttons@ solid 0px; border-right: @color_border_of_top_buttons@ solid 1px; border-bottom: @color_border_of_top_buttons@ solid 1px; } +-.bb1 { border-left: @color_general_border@ solid 1px; border-top: @color_scrollbar_highlight@ solid 1px; border-right: @color_scrollbar_highlight@ solid 1px; border-bottom: @color_general_border@ solid 1px; } +-.bb2 { border-left: @color_big_buttons_and_border_highlight@ solid 1px; border-top: @color_scrollbar_highlight@ solid 1px; border-right: @color_scrollbar_highlight@ solid 0px; border-bottom: @color_general_border@ solid 1px; } +-.bb3 { border-left: @color_big_buttons_and_border_highlight@ solid 1px; border-top: @color_scrollbar_highlight@ solid 1px; border-right: @color_general_border@ solid 0px; border-bottom: @color_general_border@ solid 0px; } +-.bb4 { border-left: @color_big_buttons_and_border_highlight@ solid 1px; border-top: @color_scrollbar_highlight@ solid 1px; border-right: @color_general_border@ solid 1px; border-bottom: @color_general_border@ solid 0px; } +-.src { border-left: @color_general_border@ solid 0px; border-top: @color_general_border@ solid 0px; border-right: @color_general_border@ solid 1px; border-bottom: @color_general_border@ solid 1px; } +-.srctd { font-family: Verdana; font-size: 8px; } +-td.fbig { color: @color_general_text@; cursor: pointer; padding-left: 2px; padding-right: 2px; font-family: Verdana; font-size: 10px; background: @color_fbig_background@; +-border-top: @color_general_border@ solid 1px; border-left: @color_general_border@ solid 1px; } +-td.pr { border-right: @color_general_border@ solid 1px; } +-td.fbigb { border-top: @color_general_border@ solid 0px; border-bottom: @color_general_border@ solid 1px; } +-td.fbigpad { padding-top: 2px; padding-bottom: 2px; } +-td, tr {font-size: 12px; font-face: verdana; } +-td.sr { white-space: nowrap; padding-top: 2px; padding-bottom: 2px; padding-left: 4px; padding-right: 4px; font-family: verdana; font-size: 10px; color: @color_general_text@; } +-td.srp { white-space: nowrap; padding-top: 2px; padding-bottom: 2px; padding-left: 0px; padding-right: 4px; font-family: verdana; font-size: 10px; color: @color_one_td_text@; } +-td.srw { padding-top: 2px; padding-bottom: 2px; padding-left: 4px; padding-right: 4px; font-family: verdana; font-size: 10px; color: @color_general_text@; } +-td.srh { cursor: pointer; vertical-align: top; background: @color_table_header_background@; white-space: nowrap; padding-top: 2px; padding-bottom: 2px; padding-left: 4px; padding-right: 4px; +-font-family: verdana; font-size: 10px; color: @color_general_text@; } +-td.total { border-top: @color_general_border@ solid 1px; border-bottom: @color_general_border@ solid 1px; } +-tr.dl-1, td.dl-1 { background: @color_dl1_back@; } +-tr.dl-2, td.dl-2 { background: @color_dl2_back@; } +-.mOvr1, tr.mOvr1 {background: @color_mOver1_back@; cursor: pointer; } +-.mOvr2, tr.mOvr2 {background: @color_mOver2_back@; cursor: pointer; } +-.mOvr3, tr.mOvr3 {background: @color_mOver3_back@; cursor: pointer; } ++ text-align: center; ++ font: bold 10px verdana; ++ border-top: @color_scrollbar_highlight@ 1px solid; ++ border-left: @color_scrollbar_highlight@ 1px solid; ++ border-bottom: @color_general_border@ 1px solid; ++ border-right: @color_general_border@ 1px solid; ++ padding-left: 4px; ++ padding-right: 4px; ++ padding-top: 1px; ++ padding-bottom: 1px; ++ color: @color_general_text@; ++ background: @color_bsmall3@; ++ } ++.bsmall { ++ background: @color_bsmall_back@; ++ } ++.bsmall1 { ++ background: @color_bbig_background@; ++ } ++.bsmall2 { ++ background: @color_bsmall2@; ++ } ++.bsmall3 { ++ background: @color_bsmall3@; ++ } ++.bbig2 { ++ background: @color_bsmall3@; ++ } ++.bbig3 { ++ background: @color_scrollbar_face@; ++ } ++.b1 { ++ border-left: @color_border_of_top_buttons@ solid 1px; ++ border-top: @color_border_of_top_buttons@ solid 1px; ++ border-right: @color_border_of_top_buttons@ solid 1px; ++ border-bottom: @color_border_of_top_buttons@ solid 1px; ++ } ++.b2 { ++ border-left: @color_border_of_top_buttons@ solid 0px; ++ border-top: @color_border_of_top_buttons@ solid 1px; ++ border-right: @color_border_of_top_buttons@ solid 1px; ++ border-bottom: @color_border_of_top_buttons@ solid 1px; ++ } ++.b3 { ++ border-left: @color_border_of_top_buttons@ solid 1px; ++ border-top: @color_border_of_top_buttons@ solid 0px; ++ border-right: @color_border_of_top_buttons@ solid 1px; ++ border-bottom: @color_border_of_top_buttons@ solid 1px; ++ } ++.b4 { ++ border-left: @color_border_of_top_buttons@ solid 0px; ++ border-top: @color_border_of_top_buttons@ solid 0px; ++ border-right: @color_border_of_top_buttons@ solid 1px; ++ border-bottom: @color_border_of_top_buttons@ solid 1px; ++ } ++.bb1 { ++ border-left: @color_general_border@ solid 1px; ++ border-top: @color_scrollbar_highlight@ solid 1px; ++ border-right: @color_scrollbar_highlight@ solid 1px; ++ border-bottom: @color_general_border@ solid 1px; ++ } ++.bb2 { ++ border-left: @color_big_buttons_and_border_highlight@ solid 1px; ++ border-top: @color_scrollbar_highlight@ solid 1px; ++ border-right: @color_scrollbar_highlight@ solid 0px; ++ border-bottom: @color_general_border@ solid 1px; ++ } ++.bb3 { ++ border-left: @color_big_buttons_and_border_highlight@ solid 1px; ++ border-top: @color_scrollbar_highlight@ solid 1px; ++ border-right: @color_general_border@ solid 0px; ++ border-bottom: @color_general_border@ solid 0px; ++ } ++.bb4 { ++ border-left: @color_big_buttons_and_border_highlight@ solid 1px; ++ border-top: @color_scrollbar_highlight@ solid 1px; ++ border-right: @color_general_border@ solid 1px; ++ border-bottom: @color_general_border@ solid 0px; ++ } ++.src { ++ border-left: @color_general_border@ solid 0px; ++ border-top: @color_general_border@ solid 0px; ++ border-right: @color_general_border@ solid 1px; ++ border-bottom: @color_general_border@ solid 1px; ++ } ++.srctd { ++ font-family: Verdana; ++ font-size: 8px; ++ } ++td.fbig { ++ color: @color_general_text@; ++ cursor: pointer; ++ padding-left: 2px; ++ padding-right: 2px; ++ font-family: Verdana; ++ font-size: 10px; ++ background: @color_fbig_background@; ++ border-top: @color_general_border@ solid 1px; ++ border-left: @color_general_border@ solid 1px; ++ } ++td.pr { ++ border-right: @color_general_border@ solid 1px; ++ } ++td.fbigb { ++ border-top: @color_general_border@ solid 0px; ++ border-bottom: @color_general_border@ solid 1px; ++ } ++td.fbigpad { ++ padding-top: 2px; ++ padding-bottom: 2px; ++ } ++td, tr { ++ font-size: 12px; ++ font-family: verdana; ++ } ++td.sr { ++ white-space: nowrap; ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 4px; ++ padding-right: 4px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_general_text@; ++ } ++td.srp { ++ white-space: nowrap; ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 0px; ++ padding-right: 4px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_one_td_text@; ++ } ++td.srw { ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 4px; ++ padding-right: 4px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_general_text@; ++ } ++td.srh { ++ cursor: pointer; ++ vertical-align: top; ++ background: @color_table_header_background@; ++ white-space: nowrap; ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 4px; ++ padding-right: 4px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_general_text@; ++ } ++td.total { ++ border-top: @color_general_border@ solid 1px; ++ border-bottom: @color_general_border@ solid 1px; ++ } ++tr.dl-1, td.dl-1 { ++ background: @color_dl1_back@; ++ } ++tr.dl-2, td.dl-2 { ++ background: @color_dl2_back@; ++ } ++.mOvr1, tr.mOvr1 { ++ background: @color_mOver1_back@; ++ cursor: pointer; ++ } ++.mOvr2, tr.mOvr2 { ++ background: @color_mOver2_back@; ++ cursor: pointer; ++ } ++.mOvr3, tr.mOvr3 { ++ background: @color_mOver3_back@; ++ cursor: pointer; ++ } + table.uploaders, table.friends, table.bw_stats, table.vo, table.cs, table.servers, + table.shares, table.downloaders, table.scan_temp, table.upstats, table.messages, + table.shares, table.vc, table.results, table.networkInfo, table.memstats { +- margin-right: auto; +- margin-left: auto; +- border: 1; +- border: @color_general_border@ solid 1px; +- border-collapse: collapse; } +-table.sourcesInfo, table.serversC { width: 100%; margin-right: auto; margin-left: auto; border: 1; border: @color_general_border@ solid 1px; border-collapse: collapse; } +-table.sources {border: 1; border: @color_general_border@ solid 1px; border-collapse: collapse; } +-table.main { margin-right: auto; margin-left: auto; } ++ margin-right: auto; ++ margin-left: auto; ++ border: @color_general_border@ solid 1px; ++ border-collapse: collapse; ++ } ++table.sourcesInfo, table.serversC { ++ width: 100%; ++ margin-right: auto; ++ margin-left: auto; ++ border: @color_general_border@ solid 1px; ++ border-collapse: collapse; ++ } ++table.sources { ++ border: @color_general_border@ solid 1px; ++ border-collapse: collapse; ++ } ++table.main { ++ margin-right: auto; ++ margin-left: auto; ++ } + div.main, div.uploaders, div.friends, div.cs, div.shares, div.upstats, div.servers, div.serversC, div.vo, +-div.downloaders, div.messages, div.vc, div.bw_stats, div.scan_temp, div.results, div.memstats { text-align: center; } +-td.srb { padding-top: 1px; padding-bottom: 1px; font-size: 10px; font-family: Verdana; white-space: nowrap; border-right: @color_general_border@ solid 1px; border-bottom: @color_general_border@ solid 1px; +-border-left: @color_general_border@ solid 1px; border-top: @color_general_border@ solid 0px; padding-left: 3px; padding-right: 3px; } +-td.act { font-size: 10px; font-weight: 700; } +-td.br {border-right: @color_general_border@ dotted 1px;} +-td.ar {text-align: right;} +-td.al {text-align: left;} +-td.ac {text-align: center;} +-td.chunk0 { height:12px; background: @color_chunk0@} +-td.chunk1 { height:12px; background: @color_chunk1@} +-td.chunk2 { height:12px; background: @color_chunk2@} +-td.chunk3 { height:12px; background: @color_chunk3@} ++div.downloaders, div.messages, div.vc, div.bw_stats, div.scan_temp, div.results, div.memstats { ++ text-align: center; ++ } ++td.srb { ++ padding-top: 1px; ++ padding-bottom: 1px; ++ font-size: 10px; ++ font-family: Verdana; ++ white-space: nowrap; ++ border-right: @color_general_border@ solid 1px; ++ border-bottom: @color_general_border@ solid 1px; ++ border-left: @color_general_border@ solid 1px; ++ border-top: @color_general_border@ solid 0px; ++ padding-left: 3px; ++ padding-right: 3px; ++ } ++td.act { ++ font-size: 10px; ++ font-weight: 700; ++ } ++td.br { ++ border-right: @color_general_border@ dotted 1px; ++ } ++td.ar { ++ text-align: right; ++ } ++td.al { ++ text-align: left; ++ } ++td.ac { ++ text-align: center; ++ } ++td.chunk0 { ++ height: 12px; ++ background: @color_chunk0@; ++ } ++td.chunk1 { ++ height: 12px; ++ background: @color_chunk1@; ++ } ++td.chunk2 { ++ height: 12px; ++ background: @color_chunk2@; ++ } ++td.chunk3 { ++ height: 12px; ++ background: @color_chunk3@; ++ } + " + + let html_js_mods0 = define_option message_section ["html_js_mods0"] +@@ -161,23 +412,28 @@ + src.className=mOvrClass; + } + function mSub(target,cmd) { +- if (target != \"\") { +- if (cmd==\"kill\") { +- if (confirm(\"Are you sure?\")) { +- top[target].location.href=\"submit?q=\" + cmd; +- } +- } else { +- if (cmd.substring(0,6)==\"custom\") {top[target].location.href=\"submit?\" + cmd;} +- else {top[target].location.href=\"submit?q=\" + cmd; +- } ++if (target != '') { ++ if (cmd=='kill') { ++ if (confirm('Are you sure?')) { ++ parent.document.getElementsByName(target).item(0).src='submit?q=' + cmd; ++ } ++ } ++ else { ++ if (cmd.substring(0,6)=='custom') { ++ parent.document.getElementsByName(target).item(0).src='submit?' + cmd; ++ } ++ else { ++ parent.document.getElementsByName(target).item(0).src='submit?q=' + cmd; ++ } + } +- } else { +- location.href=\"submit?q=\" + cmd; +- } ++} ++else { ++ parent.document.getElementsByName(target).item(0).src='submit?q=' + cmd; ++} + } + function showTab(t){ +- for (i=1; i<=6; i++) document.getElementById(\"tab\" + i).style.display = \"none\"; +- document.getElementById(\"tab\" + t).style.display = \"block\"; ++ for (i=1; i<=6; i++) document.getElementById('tab' + i).style.display = 'none'; ++ document.getElementById(\"tab\" + t).style.display = 'block'; + } + var _tabLast=null; + function _rObj (s,ar) { +@@ -415,41 +671,168 @@ + "Download CSS - style 0" + string_option + " +-body{ background-color:@color_vd_page_background@;color: @color_general_text@; font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 13px; margin-top: 10px; margin: 2;} +-td,pre { color: @color_general_text@; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10px; } +-table.downloaders { margin-right: auto; margin-left: auto; border: 1; border: @color_general_border@ solid 1px; } +-div.main { text-align: center; } +-table.main { margin-right: auto; margin-left: auto; } +-td.loaded{ padding-top: 0px; padding-bottom: 0px; background-color:@color_vd_downloaded@; font-size:1px; line-height: 2px; } +-td.remain{ padding-top: 0px; padding-bottom: 0px; background-color:@color_vd_remaining@; font-size:1px; line-height: 2px; } +-td.downloaded{ font-family: Verdana; font-weight: 500; font-size: 12px; color: @color_general_text@; } +-td.dl { white-space: nowrap; padding-top: 2px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; font-family: verdana; font-size: 10px; color: @color_general_text@; } +-td.dlheader { cursor: pointer; color: @color_general_text@; font-family: Verdana, serif; font-size: 10px; +-border-bottom: solid 1px; background: @color_table_header_background@; padding-left: 3px; +-padding-right: 3px; } +-input.checkbox { background: @color_table_header_background@; vertical-align: middle; height: 10px; width: 10px; } +-td.sr { white-space: nowrap; padding-top: 2px; padding-bottom: 2px; padding-left: 5px; padding-right: 5px; font-family: verdana; font-size: 10px; color: @color_general_text@; } +-table { cellspacing: 0; cellpadding: 0; } +-td.ar { text-align: right; } +-td.al { text-align: left; } +-td.ac { text-align: center; } +-td.brs { border-right: @color_general_border@ solid 1px; padding-left: 2px; padding-right: 2px; text-align: center; } +-td.np { padding-left: 2px; padding-right: 0px; text-align: center; } +-td.big { border-top: @color_general_border@ solid 1px; border-left: @color_general_border@ solid 1px; } +-td.pr { border-right: @color_general_border@ solid 1px; } +-.bigbutton { color: @color_general_text@; font-family: Verdana, serif; font-size: 10px; background: @color_background@; border: @color_background@ solid 1px; cursor: pointer; } ++body { ++ background-color: @color_vd_page_background@; ++ color: @color_general_text@; ++ font-family: Verdana, Arial, Helvetica, sans-serif; ++ font-size: 13px; ++ margin-top: 10px; ++ margin: 2; ++ } ++td, pre { ++ color: @color_general_text@; ++ font-family: Verdana, Arial, Helvetica, sans-serif; ++ font-size: 10px; ++ } ++table.downloaders { ++ margin-right: auto; ++ margin-left: auto; ++ border: @color_general_border@ solid 1px; ++ } ++div.main { ++ text-align: center; ++ } ++table.main { ++ margin-right: auto; ++ margin-left: auto; ++ } ++td.loaded { ++ padding-top: 0px; ++ padding-bottom: 0px; ++ background-color: @color_vd_downloaded@; ++ font-size: 1px; ++ line-height: 2px; ++ } ++td.remain { ++ padding-top: 0px; ++ padding-bottom: 0px; ++ background-color: @color_vd_remaining@; ++ font-size: 1px; ++ line-height: 2px; ++ } ++td.downloaded { ++ font-family: Verdana; ++ font-weight: 500; ++ font-size: 12px; ++ color: @color_general_text@; ++ } ++td.dl { ++ white-space: nowrap; ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 5px; ++ padding-right: 5px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_general_text@; ++ } ++td.dlheader { ++ cursor: pointer; ++ color: @color_general_text@; ++ font-family: Verdana, serif; ++ font-size: 10px; ++ border-bottom: solid 1px; ++ background: @color_table_header_background@; ++ padding-left: 3px; ++ padding-right: 3px; ++ } ++input.checkbox { ++ background: @color_table_header_background@; ++ vertical-align: middle; ++ height: 10px; ++ width: 10px; ++ } ++td.sr { ++ white-space: nowrap; ++ padding-top: 2px; ++ padding-bottom: 2px; ++ padding-left: 5px; ++ padding-right: 5px; ++ font-family: verdana; ++ font-size: 10px; ++ color: @color_general_text@; ++ } ++table { ++ border-spacing: 0px; ++ } ++td { ++ padding: 0px; ++ } ++td.ar { ++ text-align: right; ++ } ++td.al { ++ text-align: left; ++ } ++td.ac { ++ text-align: center; ++ } ++td.brs { ++ border-right: @color_general_border@ solid 1px; ++ padding-left: 2px; ++ padding-right: 2px; ++ text-align: center; ++ } ++td.np { ++ padding-left: 2px; ++ padding-right: 0px; ++ text-align: center; ++ } ++td.big { ++ border-top: @color_general_border@ solid 1px; ++ border-left: @color_general_border@ solid 1px; ++ } ++td.pr { ++ border-right: @color_general_border@ solid 1px; ++ } ++.bigbutton { ++ color: @color_general_text@; ++ font-family: Verdana, serif; ++ font-size: 10px; ++ background: @color_background@; ++ border: @color_background@ solid 1px; ++ cursor: pointer; ++ } + .headbutton { +- font-family: Verdana, serif; font-size: 10px; border: @color_table_header_background@ solid 1px; background: @color_table_header_background@; +- padding-left: 5px; padding-right: 5px; cursor: pointer; } +-tr.dl-1 { background: @color_dl1_back@; } +-tr.dl-2 { background: @color_dl2_back@; } +-tr.mOvrDL, .mOvrDL { background: @color_mOver1_back@; cursor: pointer; } +-input { font-family: tahoma; font-size: 10px; } +-a{ text-decoration: none; font-weight: bold;} +-a:link,a:active,a:visited { color: @color_download_anchor@; } +-a:hover { color: @color_download_anchor_hover@; text-decoration: underline; } +-a.extern:visited,a.extern:hover,a.extern:active { color: @color_external_anchor@; } +-.extern:hover { color: @color_external_anchor_hover@; } ++ font-family: Verdana, serif; ++ font-size: 10px; ++ border: @color_table_header_background@ solid 1px; ++ background: @color_table_header_background@; ++ padding-left: 5px; ++ padding-right: 5px; ++ cursor: pointer; ++ } ++tr.dl-1 { ++ background: @color_dl1_back@; ++ } ++tr.dl-2 { ++ background: @color_dl2_back@; ++ } ++tr.mOvrDL, .mOvrDL { ++ background: @color_mOver1_back@; ++ cursor: pointer; ++ } ++input { ++ font-family: tahoma; ++ font-size: 10px; ++ } ++a { ++ text-decoration: none; ++ font-weight: bold; ++ } ++a:link,a:active,a:visited { ++ color: @color_download_anchor@; ++ } ++a:hover { ++ color: @color_download_anchor_hover@; ++ text-decoration: underline; ++ } ++a.extern:visited,a.extern:hover,a.extern:active { ++ color: @color_external_anchor@; ++ } ++.extern:hover { ++ color: @color_external_anchor_hover@; ++ } + " + + let download_html_js_mods0 = define_option message_section ["download_html_js_mods0"] +@@ -598,6 +981,9 @@ + <TD class=\"bu bbig\" title=\"Bandwidth statistics (set html_mods_bw_refresh_delay)\" + onMouseOver=\"mOvr(this,'mOvr1');\" onMouseOut=\"mOut(this);\" + onClick=\"mSub('fstatus','bw_stats');mSub('output','gdstats')\">Bandwidth stats</TD> ++<TD class=\"bu bbig\" title=\"Bandwidth toggle\" ++onMouseOver=\"mOvr(this,'mOvr1');\" onMouseOut=\"mOut(this);\" ++onClick=\"mSub('fstatus','bw_stats');mSub('output','bw_toggle')\">Bandwidth toggle</TD> + </TR></TBODY></TABLE></DIV> + + <DIV ID=\"tab2\" style=\"display: none\"> +@@ -693,6 +1079,9 @@ + <TD class=\"bu bbig\" title=\"Settings\" + onMouseOver=\"mOvr(this,'mOvr1');\" onMouseOut=\"mOut(this);\" + onClick=\"mSub('fstatus','version');mSub('output','voo+1')\">Settings</TD> ++<TD class=\"bu bbig\" title=\"Users\" ++onMouseOver=\"mOvr(this,'mOvr1');\" onMouseOut=\"mOut(this);\" ++onclick=\"mSub('fstatus','version');mSub('output','users')\">Users</TD> + <TD class=\"bu bbig\" title=\"View/edit shared directories\" + onMouseOver=\"mOvr(this,'mOvr1');\" onMouseOut=\"mOut(this);\" + onClick=\"mSub('fstatus','version');mSub('output','shares')\">Shares</TD> +@@ -827,7 +1216,7 @@ + " + <table width=\"100%\" border=\"0\"> + <tr> +-<td align=\"left\" valign=\"middle\" width=\"*\"><a href=\"http://www.mldonkey.net/\" $O><b>MLDonkey Home</b></a></td> ++<td align=\"left\" valign=\"middle\" width=\"*\"><a href=\"http://www.mldonkey.org/\" $O><b>MLDonkey Home</b></a></td> + <form action=\"submit\" $O name=\"cmdFormular\" onSubmit=\"return CheckInput();\"> + <td><input type=\"text\" name=\"q\" size=60 value=\"\"></td> + <td><input type=\"submit\" value=\"Execute\"></td> +@@ -861,7 +1250,7 @@ + <td><a href=\"submit?q=commit\" onMouseOver=\"window.status='Move finished downloads to incoming directory';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $S>Commit</a></td> + <td><a href=\"submit?q=vr\" onMouseOver=\"window.status='View results to your queries';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $O>Search results</a></td> + <td><a href=\"submit?q=ovweb\" onMouseOver=\"window.status='Boot Overnet peers from http list';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $S>Load Overnet peers</a></td> +-<td><a class=\"extern\" href=\"http://www.mldonkeyworld.com/\" onMouseOver=\"window.status='MLDonkey World';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $O>English forum</a></td> ++<td><a class=\"extern\" href=\"http://mldonkey.sf.net/forums/\" onMouseOver=\"window.status='MLDonkey World';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $O>English forum</a></td> + <td><a class=\"extern\" href=\"http://www.mldonkey.org/\" onMouseOver=\"window.status='German Forum';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $O>German forum</a></td> + <td><a href=\"submit?q=kill\" onMouseOver=\"window.status='Save and quit MLDonkey';return true;\" onMouseOut=\"window.status='';return true;\" onFocus=\"this.blur();\" $O>Kill MLDonkey</a></td> + </tr> +@@ -1051,8 +1440,8 @@ + color_vd_remaining = "#000"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1094,8 +1483,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1134,8 +1523,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1174,8 +1563,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1214,8 +1603,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1254,8 +1643,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1294,8 +1683,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#000"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1334,8 +1723,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#D4C9B7"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +@@ -1374,8 +1763,8 @@ + color_vd_remaining = "#EEE"; + color_general_text = "#D4C9B7"; + color_general_border = "#000"; +- color_anchor = "#000"; +- color_anchor_hover = "#000"; ++ color_anchor = "#0000ff"; ++ color_anchor_hover = "#0000ff"; + color_download_anchor = "#000"; + color_download_anchor_hover = "#000"; + color_external_anchor = "#000"; +Index: src/daemon/common/commonNetwork.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonNetwork.ml,v +retrieving revision 1.29 +retrieving revision 1.32 +diff -u -r1.29 -r1.32 +--- src/daemon/common/commonNetwork.ml 5 Sep 2006 14:15:19 -0000 1.29 ++++ src/daemon/common/commonNetwork.ml 1 Oct 2006 17:53:59 -0000 1.32 +@@ -99,6 +99,8 @@ + lprintf_nl "op_network_clean_servers"; + if c.op_network_display_stats == cc.op_network_display_stats then + lprintf_nl "op_network_display_stats"; ++ if c.op_network_stat_info_list == cc.op_network_stat_info_list then ++ lprintf_nl "op_network_stat_info_list"; + if c.op_network_info == cc.op_network_info then + lprintf_nl "op_network_info"; + if c.op_network_clean_exit == cc.op_network_clean_exit then +@@ -107,6 +109,10 @@ + lprintf_nl "op_network_reset"; + if c.op_network_ports == cc.op_network_ports then + lprintf_nl "op_network_ports"; ++ if c.op_network_porttest_start == cc.op_network_porttest_start then ++ lprintf_nl "op_network_porttest_start"; ++ if c.op_network_porttest_result == cc.op_network_porttest_result then ++ lprintf_nl "op_network_porttest_result"; + ) !networks_ops; + lprint_newline () + +@@ -127,6 +133,8 @@ + let network_clean_exit n = try n.op_network_clean_exit () with _ -> true + let network_reset n = try n.op_network_reset () with _ -> () + let network_ports n = n.op_network_ports () ++let network_porttest_start n = n.op_network_porttest_start () ++let network_porttest_result n = n.op_network_porttest_result () + + let networks_iter f = + List.iter (fun r -> +@@ -257,7 +265,7 @@ + op_network_update_options = (fun _ -> ni_ok name "update_options"); + op_network_disable = (fun _ -> ni_ok name "disable"); + op_network_server_of_option = (fun _ -> fni name "op_network_server_of_option"); +- op_network_file_of_option = (fun _ _ -> fni name "op_network_file_of_option"); ++ op_network_file_of_option = (fun _ _ _ -> fni name "op_network_file_of_option"); + op_network_client_of_option = (fun _ -> fni name "op_network_client_of_option"); + op_network_recover_temp = (fun _ -> ni_ok name "recover_temp"); + op_network_search = (fun _ _ -> ni_ok name "search"); +@@ -268,16 +276,19 @@ + op_network_close_search = (fun _ -> ni_ok name "close_search"); + op_network_extend_search = (fun _ _ -> ni_ok name "extend search"); + op_network_clean_servers = (fun _ -> ni_ok name "clean servers"); +- op_network_parse_url = (fun _ -> ni_ok name "parse_url"; "", false); ++ op_network_parse_url = (fun _ _ -> ni_ok name "parse_url"; "", false); + op_network_info = (fun _ -> fni name "network_info"); + op_network_connected = (fun _ -> ni_ok name "connected"; false); + op_network_add_server = (fun _ -> fni name "op_network_add_server"); +- op_network_gui_message = (fun _ -> ni_ok name "gui_message"); +- op_network_download = (fun _ -> fni name "network_download"); ++ op_network_gui_message = (fun _ _ -> ni_ok name "gui_message"); ++ op_network_download = (fun _ _ -> fni name "network_download"); + op_network_display_stats = (fun _ _ -> ni_ok name "display_stats"); ++ op_network_stat_info_list = (fun _ -> []); + op_network_clean_exit = (fun _ -> true); + op_network_reset = (fun _ -> ni_ok name "reset"); + op_network_ports = (fun _ -> ni_ok name "ports"; []); ++ op_network_porttest_start = (fun _ -> ni_ok name "porttest_start"); ++ op_network_porttest_result = (fun _ -> fni name "porttest_result"); + } + in + let rr = (Obj.magic r: network) in +Index: src/daemon/common/commonNetwork.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonNetwork.mli,v +retrieving revision 1.15 +retrieving revision 1.18 +diff -u -r1.15 -r1.18 +--- src/daemon/common/commonNetwork.mli 5 Sep 2006 14:15:19 -0000 1.15 ++++ src/daemon/common/commonNetwork.mli 9 Nov 2006 21:32:26 -0000 1.18 +@@ -48,6 +48,8 @@ + val register_commands : (string * string * CommonTypes.arg_kind * string) list -> unit + val network_connect_servers : CommonTypes.network -> unit + val network_ports : CommonTypes.network -> (int * string) list ++val network_porttest_start : CommonTypes.network -> unit ++val network_porttest_result : CommonTypes.network -> CommonTypes.network_porttest + val network_forget_search : CommonTypes.network -> CommonTypes.search -> unit + val network_close_search : CommonTypes.network -> CommonTypes.search -> unit + val network_private_message : CommonTypes.network -> string -> string -> unit +@@ -56,6 +58,6 @@ + CommonTypes.search -> CommonTypes.extend_search -> unit + val network_connected : CommonTypes.network -> bool + val network_clean_servers : CommonTypes.network -> unit +-val network_parse_url : CommonTypes.network -> string -> string * bool ++val network_parse_url : CommonTypes.network -> string -> CommonTypes.userdb -> string * bool + val network_info : CommonTypes.network -> CommonTypes.network_info + val commands_by_kind : (string, (string * string) list ref) Hashtbl.t +Index: src/daemon/common/commonOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonOptions.ml,v +retrieving revision 1.176 +retrieving revision 1.189 +diff -u -r1.176 -r1.189 +--- src/daemon/common/commonOptions.ml 16 Sep 2006 15:36:59 -0000 1.176 ++++ src/daemon/common/commonOptions.ml 21 Nov 2006 22:34:33 -0000 1.189 +@@ -145,7 +145,8 @@ + end; + + lprintf_nl "MLDonkey is working in %s" file_basedir; +- if not (Sys.file_exists file_basedir) then begin ++ if not (Sys.file_exists file_basedir) || ++ not (Sys.file_exists (Filename.concat file_basedir "downloads.ini")) then begin + lprint_newline (); + lprintf_nl "creating new MLDonkey base directory in %s\n" file_basedir; + created_new_base_directory := Some file_basedir +@@ -199,6 +200,11 @@ + exit 2 + end; + ++ (* Charset conversion self-test *) ++ let filename = "abcdefghijklmnopqrstuvwxyz" in ++ let conv_filename = Charset.to_locale filename in ++ if filename <> conv_filename then Charset.conversion_enabled := false; ++ + Unix2.can_write_to_directory (Filename2.temp_directory ()); + + if (String2.starts_with (Filename.basename Sys.argv.(0)) "mlnet") then begin +@@ -333,9 +339,7 @@ + + Options.set_string_wrappers ip_range_list_option + (fun list -> +- List.fold_left (fun s ip -> +- Printf.sprintf "%s %s" (Ip.string_of_range ip) s +- ) "" list ++ String.concat " " (List.map Ip.string_of_range (List.rev list)) + ) + (fun s -> + let list = String2.tokens s in +@@ -357,6 +361,7 @@ + String2.tokens + + let is_not_spam = ref (fun _ -> true) ++let is_not_comment_spam = ref (fun _ -> true) + + + +@@ -578,6 +583,14 @@ + < 4 -> download limited to upload * 3" + int_option 50 + ++let max_hard_upload_rate_2 = define_option current_section ["max_hard_upload_rate_2"] ++ "Second maximal upload rate for easy toggling (use bw_toggle)" ++ int_option 5 ++ ++let max_hard_download_rate_2 = define_option current_section ["max_hard_download_rate_2"] ++ "Second maximal download rate for easy toggling (use bw_toggle)" ++ int_option 20 ++ + let max_opened_connections = define_option current_section ["max_opened_connections"] + "Maximal number of opened connections" + int_option 200 +@@ -590,6 +603,10 @@ + "How many slots can be used for upload" + int_option 5 + ++let max_release_slots = define_option current_section ["max_release_slots"] ++ "How many percent of upload slots can be used for downloading files tagged as release" ++ percent_option 20 ++ + let friends_upload_slot = define_option current_section ["friends_upload_slot"] + "Set aside a single reserved slot to upload to friends" + bool_option true +@@ -725,6 +742,14 @@ + "Whether to display the Comments column in vd output" + bool_option true + ++let html_mods_vd_user = define_expert_option current_section ["html_mods_vd_user"] ++ "Whether to display the User column in vd output" ++ bool_option false ++ ++let html_mods_vd_group = define_expert_option current_section ["html_mods_vd_group"] ++ "Whether to display the Group column in vd output" ++ bool_option false ++ + let html_mods_vd_active_sources = define_expert_option current_section ["html_mods_vd_active_sources"] + "Whether to display the Active Sources column in vd output" + bool_option true +@@ -774,13 +799,25 @@ + bool_option true + + let html_mods_vd_gfx_x_size = define_expert_option current_section ["html_mods_vd_gfx_x_size"] +- "Graph x size in vd output ( 320 < x < 3600 )" +- int_option 630 ++ "Graph x size in vd output ( 365 < x < 3665 )" ++ int_option 795 + + let html_mods_vd_gfx_y_size = define_expert_option current_section ["html_mods_vd_gfx_y_size"] + "Graph y size in vd output ( 200 < y < 1200 )" + int_option 200 + ++let html_mods_vd_gfx_h_dynamic = define_expert_option current_section ["html_mods_vd_gfx_h_dymamic"] ++ "Dynamic grid width, start with 1 h/grid, maximum html_mods_vd_gfx_h_grid_time h/grid" ++ bool_option true ++ ++let html_mods_vd_gfx_h_grid_time = define_expert_option current_section ["html_mods_vd_gfx_h_grid_time"] ++ "Max hours on time scale per grid (0 = no limit)" ++ int_option 0 ++ ++let html_mods_vd_gfx_subgrid = define_expert_option current_section ["html_mods_vd_gfx_subgrid"] ++ "Number of shown subgrids on graph (0 = no subgrids)" ++ int_option 0 ++ + let html_mods_vd_gfx_tag = define_expert_option current_section ["html_mods_vd_gfx_tag"] + "Draw tag graph" + bool_option false +@@ -986,6 +1023,10 @@ + "URLs of RSS feeds" + (list_option Url.option) [] + ++let rss_preprocessor = define_expert_option current_section ["rss_preprocessor"] ++ "If MLDonkey can not read broken RSS feeds, use this program to preprocess them" ++ string_option "xmllint" ++ + let ip_blocking_descriptions = define_expert_option current_section ["ip_blocking_descriptions"] + "Keep IP blocking ranges descriptions in memory" + bool_option false +@@ -1288,10 +1329,6 @@ + ones in allowed_commands" + bool_option false + +-let enable_user_config = define_option current_section ["enable_user_config"] +- "Are all users allowed to change MLDonkey options?" +- bool_option true +- + let allow_browse_share = define_option current_section ["allow_browse_share"] + "Allow others to browse our share list (0: none, 1: friends only, 2: everyone" + allow_browse_share_option 1 +@@ -1300,6 +1337,10 @@ + "Regexp of messages to filter out, example: string1|string2|string3" + string_option "Your client is connecting too fast" + ++let comments_filter = define_option current_section ["comments_filter"] ++ "Regexp of comments to filter out, example: string1|string2|string3" ++ string_option "http://|https://|www\\." ++ + + + +@@ -1417,10 +1458,6 @@ + "The realm shown when connecting with a WEB browser" + string_option "MLdonkey" + +-let use_html_frames = define_expert_option current_section ["use_html_frames"] +- "This option controls whether the WEB interface should use frames or not" +- bool_option true +- + let html_frame_border = define_expert_option current_section ["html_frame_border"] + "This option controls whether the WEB interface should show frame borders or not" + bool_option true +@@ -1516,7 +1553,7 @@ + + let compaction_overhead = define_expert_option current_section ["compaction_overhead"] + "The percentage of free memory before a compaction is triggered" +- int_option 25 ++ percent_option 25 + + let space_overhead = define_expert_option current_section ["space_overhead"] + "The major GC speed is computed from this parameter. This is the memory +@@ -1524,7 +1561,7 @@ + unreachable blocks. It is expressed as a percentage of the memory used + for live data. The GC will work more (use more CPU time and collect + blocks more eagerly) if space_overhead is smaller." +- int_option 80 ++ percent_option 80 + + let max_displayed_results = define_expert_option current_section ["max_displayed_results"] + "Maximal number of results displayed for a search" +@@ -1535,6 +1572,13 @@ + int_option 14 + + ++let max_comments_per_file = define_expert_option current_section ["max_comments_per_file"] ++ "Maximum number of comments per file" ++ int_option 100 ++ ++let max_comment_length = define_expert_option current_section ["max_comment_length"] ++ "Maximum length of file comments" ++ int_option 256 + + + (*************************************************************************) +@@ -1748,7 +1792,7 @@ + (!!minor_heap_size * 1024) }; + ); + option_hook client_buffer_size (fun _ -> +- TcpBufferedSocket.max_buffer_size := maxi 10000000 !!client_buffer_size ++ TcpBufferedSocket.max_buffer_size := max 10000000 !!client_buffer_size + ); + if Autoconf.has_gd then + option_hook html_mods_vd_gfx_png (fun _ -> +@@ -1908,14 +1952,25 @@ + in aux 0 + + let _ = +- option_hook messages_filter (fun _ -> +- is_not_spam := if !!messages_filter <> "" then +- let r = Str.regexp_case_fold (quote_unquote_bars !!messages_filter) in +- (fun s -> try ++ let regex_fun str = ++ if str <> "" then ++ let r = Str.regexp_case_fold (quote_unquote_bars str) in ++ (fun s -> ++ try + ignore (Str.search_forward r s 0); + false +- with Not_found -> true) +- else (fun _ -> true)) ++ with Not_found -> true ++ ) ++ else (fun _ -> true) ++ in ++ ++ option_hook messages_filter (fun _ -> ++ is_not_spam := regex_fun !!messages_filter ++ ); ++ ++ option_hook comments_filter (fun _ -> ++ is_not_comment_spam := regex_fun !!comments_filter ++ ) + + let http_proxy = ref None + +Index: src/daemon/common/commonResult.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonResult.ml,v +retrieving revision 1.9 +retrieving revision 1.10 +diff -u -r1.9 -r1.10 +--- src/daemon/common/commonResult.ml 17 May 2006 08:52:43 -0000 1.9 ++++ src/daemon/common/commonResult.ml 19 Sep 2006 17:07:42 -0000 1.10 +@@ -142,13 +142,13 @@ + result_source_network = 0; + } + +-let result_download rs names force = ++let result_download rs names force user = + let r = IndexedResults.get_result rs in + let files = ref [] in + CommonNetwork.networks_iter (fun n -> + (* Temporarily download results only from the network that returned the result *) + if (n.network_num = r.result_source_network) then +- files := (n.op_network_download r) :: !files ++ files := (n.op_network_download r user) :: !files + ); + !files + +Index: src/daemon/common/commonResult.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonResult.mli,v +retrieving revision 1.5 +retrieving revision 1.7 +diff -u -r1.5 -r1.7 +--- src/daemon/common/commonResult.mli 12 May 2006 21:02:38 -0000 1.5 ++++ src/daemon/common/commonResult.mli 9 Nov 2006 21:32:26 -0000 1.7 +@@ -78,7 +78,7 @@ + val find_result : int -> StoredResult.stored_result + val dummy_result : CommonTypes.result_info + val result_download : +- StoredResult.stored_result -> 'a -> 'b -> CommonTypes.file list ++ StoredResult.stored_result -> 'a -> 'b -> CommonTypes.userdb -> CommonTypes.file list + val results_iter : (int -> StoredResult.stored_result -> unit) -> unit + val update_result : StoredResult.result -> unit + val update_result2 : +Index: src/daemon/common/commonSearch.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonSearch.ml,v +retrieving revision 1.17 +retrieving revision 1.18 +diff -u -r1.17 -r1.18 +--- src/daemon/common/commonSearch.ml 19 Jan 2006 00:44:47 -0000 1.17 ++++ src/daemon/common/commonSearch.ml 13 Nov 2006 13:14:49 -0000 1.18 +@@ -117,8 +117,12 @@ + | "-network" :: name :: args -> + net := (network_find_by_name name).network_num; + iter args q +- | "-without" :: name :: args -> ++ | "-not" :: name :: args -> + iter args ((QAndNot (QHasWord name, QHasWord name)) :: q) ++ | "-and" :: name :: args -> ++ iter args ((QAnd (QHasWord name, QHasWord name)) :: q) ++ | "-or" :: name :: args -> ++ iter args ((QOr (QHasWord name, QHasWord name)) :: q) + | s :: args -> + if s.[0] = '-' then + let args = +@@ -134,16 +138,19 @@ + iter args ((QHasWord(s)) :: q) + in + let q = iter args [] in +- (match q with ++ (match (List.rev q) with + [] -> failwith "Void query" +- | [QAndNot _] -> failwith "Bad without query" + | q1 :: tail -> + List.fold_left (fun q1 q2 -> + match q2 with +- QAndNot (QHasWord x,_) -> ++ QAndNot (QHasWord x, _) -> + QAndNot (q1, QHasWord x) ++ | QAnd (QHasWord x, _) -> ++ QAnd (q1, QHasWord x) ++ | QOr (QHasWord x, _) -> ++ QOr (q1, QHasWord x) + | _ -> +- QAnd (q1,q2) ++ QAnd (q1,q2) + ) q1 tail), !net + + +Index: src/daemon/common/commonServer.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonServer.ml,v +retrieving revision 1.33 +retrieving revision 1.38 +diff -u -r1.33 -r1.38 +--- src/daemon/common/commonServer.ml 6 Aug 2006 13:59:05 -0000 1.33 ++++ src/daemon/common/commonServer.ml 19 Nov 2006 23:04:25 -0000 1.38 +@@ -53,6 +53,7 @@ + mutable op_server_connect : ('a -> unit); + mutable op_server_disconnect : ('a -> unit); + mutable op_server_users : ('a -> user list); ++ mutable op_server_published : ('a -> file list); + mutable op_server_query_users : ('a -> unit); + mutable op_server_find_user : ('a -> string -> unit); + mutable op_server_cid : ('a -> Ip.t); +@@ -108,12 +109,15 @@ + T.server_users = None; + T.server_banner = ""; + T.server_preferred = false; ++ T.server_master = false; + T.server_version = ""; + T.server_max_users = 0L; + T.server_soft_limit = 0L; + T.server_hard_limit = 0L; + T.server_lowid_users = 0L; + T.server_ping = 0; ++ T.server_published_files = 0; ++ T.server_features = None; + } + + let server_num s = +@@ -183,6 +187,10 @@ + let s = as_server_impl s in + s.impl_server_ops.op_server_users s.impl_server_val + ++let server_published s = ++ let s = as_server_impl s in ++ s.impl_server_ops.op_server_published s.impl_server_val ++ + let server_cid s = + let s = as_server_impl s in + s.impl_server_ops.op_server_cid s.impl_server_val +@@ -212,6 +220,7 @@ + op_server_disconnect = (fun _ -> ni_ok network "server_disconnect"); + op_server_find_user = (fun _ -> fni network "find_user"); + op_server_query_users = (fun _ -> ni_ok network "query_users"); ++ op_server_published = (fun _ -> fni network "published"); + op_server_users = (fun _ -> fni network "users"); + op_server_cid = (fun _ -> fni network "cid"); + op_server_low_id = (fun _ -> fni network "low_id"); +@@ -362,24 +371,30 @@ + info.G.server_banner + + let server_print_html_header buf ext = ++ if !!html_mods_use_js_tooltips then Printf.bprintf buf ++"\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top: ++-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; ++ + html_mods_table_header buf "serversTable" (Printf.sprintf "servers%s" ext) ([ + ( "1", "srh", "Server number", "#" ) ; + ( "0", "srh", "Connect|Disconnect", "C/D" ) ; + ( "0", "srh", "Remove", "Rem" ) ; + ( "0", "srh", "Preferred", "P" ) ; ++ ( "0", "srh", "Master servers", "M" ) ; + ( "0", "srh", "[Hi]gh or [Lo]w ID", "ID" ) ; + ( "0", "srh", "Network name", "Network" ) ; + ( "0", "srh", "Connection status", "Status" ) ; +- ] @ (if !Geoip.active then [( "0", "srh", "Country Code/Name", "CC" )] else []) @ [ + ( "0", "srh br", "IP address", "IP address" ) ; ++ ] @ (if !Geoip.active then [( "0", "srh br", "Country Code/Name", "CC" )] else []) @ [ + ( "1", "srh ar", "Number of connected users", "Users" ) ; + ( "1", "srh ar br", "Max number of users", "MaxUsers" ) ; + ( "1", "srh ar br", "LowID users", "LowID" ) ; +- ( "1", "srh ar br", "Number of files indexed on server", "Files" ) ; ++ ( "1", "srh ar", "Number of files indexed on server", "Files" ); ++ ( "1", "srh ar br", "Number of published files on server", "Publ" ); + ( "1", "srh ar", "Soft file limit", "Soft" ) ; + ( "1", "srh ar br", "Hard file limit", "Hard" ) ; + ( "0", "srh ar br", "Ping (ms)", "Ping" ) ; +- ( "0", "srh", "Server version", "Version" ) ; ++ ( "0", "srh br", "Server version", "Version" ) ; + ( "0", "srh", "Server name", "Name" ) ; + ( "0", "srh", "Server details", "Details" ) ]) + +@@ -397,80 +412,79 @@ + let buf = o.conn_buf in + + if use_html_mods o then begin +- let snum = (server_num s) in ++ let snum = (server_num s) in ++ let ip_port_string = ++ Printf.sprintf "%s:%s%s" ++ (Ip.string_of_addr info.G.server_addr) ++ (string_of_int info.G.server_port) ++ (if info.G.server_realport <> 0 ++ then "(" ^ (string_of_int info.G.server_realport) ^ ")" ++ else "" ++ ) ++ in ++ ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"" ++ (html_mods_cntr ()); ++ ++ (if !!html_mods_use_js_tooltips then ++ Printf.bprintf buf " onMouseOver=\\\"mOvr(this);setTimeout('popLayer(\\\\\'%s %s<br>%s\\\\\')',%d);setTimeout('hideLayer()',%d);return true;\\\" onMouseOut=\\\"mOut(this);hideLayer();setTimeout('hideLayer()',%d)\\\"\\>" ++ info.G.server_name ip_port_string ++ (match info.G.server_features with ++ | None -> "" ++ | Some f -> "server features: " ^ f) ++ !!html_mods_js_tooltips_wait ++ !!html_mods_js_tooltips_timeout ++ !!html_mods_js_tooltips_wait); + +- Printf.bprintf buf " +- \\<tr class=\\\"dl-%d\\\"\\> +- \\<td class=\\\"srb\\\" %s \\>%d\\</td\\> +- %s +- %s +- %s +- \\<td class=\\\"sr\\\" %s\\</td\\> +- \\<td class=\\\"sr\\\"\\>%s\\</td\\> +- \\<td class=\\\"sr\\\"\\>%s\\</td\\> +- %s +- \\<td class=\\\"sr br\\\"\\>%s:%s\\</td\\> +- \\<td class=\\\"sr ar\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar br\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar br\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar br\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar br\\\"\\>%Ld\\</td\\> +- \\<td class=\\\"sr ar br\\\"\\>%d\\</td\\> +- \\<td class=\\\"sr br\\\"\\>%s\\</td\\> +- \\<td class=\\\"sr\\\"\\>%s\\</td\\> +- \\<td width=\\\"100%%\\\" class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>\n" +- (html_mods_cntr ()) ++ Printf.bprintf buf ++" \\<td class=\\\"srb\\\" %s \\>%d\\</td\\> %s %s %s" + (match impl.impl_server_state with +- Connected _ -> Printf.sprintf "title=\\\"Server Banner\\\" +- onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" +- onClick=\\\"location.href='submit?q=server_banner+%d'\\\"" snum ++ Connected _ -> ++ Printf.sprintf "title=\\\"Server Banner\\\" ++ onClick=\\\"location.href='submit?q=server_banner+%d'\\\"" ++ snum + | _ -> "") + snum + ( +- if server_blocked s && (match impl.impl_server_state with +- NotConnected _ -> true +- | _ -> false) then "\\<td class=\\\"srb\\\"\\>blk\\</td\\>" else +- Printf.sprintf +- "\\<TD class=\\\"srb\\\" onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" title=\\\"Connect|Disconnect\\\" +- onClick=\\\"parent.fstatus.location.href='submit?q=%s+%d'\\\"\\>%s\\</TD\\>" +- (match impl.impl_server_state with +- NotConnected _ -> "c" +- | _ -> "x") ++ let not_connected = ++ match impl.impl_server_state with ++ | NotConnected _ -> true ++ | _ -> false ++ in ++ if server_blocked s && not_connected ++ then "\\<td class=\\\"srb\\\"\\>blk\\</td\\>" ++ else Printf.sprintf ++ "\\<td class=\\\"srb\\\" title=\\\"Connect|Disconnect\\\" ++ onClick=\\\"parent.fstatus.location.href='submit?q=%s+%d'\\\"\\>%s\\</td\\>" ++ (if not_connected then "c" else "x") + snum +- (match impl.impl_server_state with +- NotConnected _ -> "Conn" +- | _ -> "Disc") ++ (if not_connected then "Conn" else "Disc") + ) + ( + Printf.sprintf +- "\\<TD class=\\\"srb\\\" onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" title=\\\"Remove server\\\" +- onClick=\\\"parent.fstatus.location.href='submit?q=rem+%d'\\\"\\>Rem\\</TD\\>" ++ "\\<td class=\\\"srb\\\" title=\\\"Remove server\\\" ++ onClick=\\\"parent.fstatus.location.href='submit?q=rem+%d'\\\"\\>Rem\\</td\\>" + snum + ) + ( + if info.G.server_preferred then begin + Printf.sprintf +- "\\<TD class=\\\"srb\\\" onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" title=\\\"Unset preferred\\\" +- onClick=\\\"parent.fstatus.location.href='submit?q=preferred+false+%s'\\\"\\>T\\</TD\\>" ++ "\\<td class=\\\"srb\\\" title=\\\"Unset preferred\\\" ++ onClick=\\\"parent.fstatus.location.href='submit?q=preferred+false+%s'\\\"\\>P\\</td\\>" + (Ip.string_of_addr info.G.server_addr) + end else begin + Printf.sprintf +- "\\<TD class=\\\"srb\\\" onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" title=\\\"Set preferred\\\" +- onClick=\\\"parent.fstatus.location.href='submit?q=preferred+true+%s'\\\"\\>F\\</TD\\>" ++ "\\<td class=\\\"srb\\\" title=\\\"Set preferred\\\" ++ onClick=\\\"parent.fstatus.location.href='submit?q=preferred+true+%s'\\\"\\>-\\</td\\>" + (Ip.string_of_addr info.G.server_addr) + end +- ) +- (if n.network_name = "Donkey" then +- begin ++ ); ++ ++ let id_title, id_text = ++ match n.network_name with ++ "Donkey" -> begin + match impl.impl_server_state with +- | Connected _ -> +- begin ++ Connected _ -> begin + let cid = (server_cid s) in + let (label,shortlabel,our_ip) = + if not (server_low_id s) then +@@ -478,49 +492,58 @@ + (if !!set_client_ip <> cid then + Printf.sprintf "(clientIP: %s)" + (Ip.to_string !!set_client_ip) +- else "" +- ) +- ) +- else +- ("LowID","Lo","") ++ else "")) ++ else ("LowID","Lo","") + in +- Printf.sprintf +- "title=\\\"%s: %s = %s %s\\\" \\>%s" ++ Printf.sprintf "%s: %s = %s %s" + label + (Int64.to_string (Ip.to_int64 (Ip.rev cid))) + (Ip.to_string cid) + our_ip +- shortlabel ++ ,shortlabel + end +- | _ -> "\\>" ++ | _ -> "","" + end +- else "\\>" +- ) +- n.network_name +- (match impl.impl_server_state with +- NotConnected _ -> if server_blocked s then "IP blocked" +- else (string_of_connection_state impl.impl_server_state) +- | _ -> (string_of_connection_state impl.impl_server_state)) +- (if !Geoip.active then +- Printf.sprintf "\\<td class=\\\"sr\\\" title=\\\"%s\\\" \\>%s\\</td\\>" cn cc +- else "") +- (Ip.string_of_addr info.G.server_addr) +- (Printf.sprintf "%s%s" +- (string_of_int info.G.server_port) +- (if info.G.server_realport <> 0 +- then "(" ^ (string_of_int info.G.server_realport) ^ ")" +- else "")) +- info.G.server_nusers +- info.G.server_max_users +- info.G.server_lowid_users +- info.G.server_nfiles +- info.G.server_soft_limit +- info.G.server_hard_limit +- info.G.server_ping +- info.G.server_version +- info.G.server_name +- info.G.server_description ++ | _ -> "","" ++ in ++ ++ let server_state_string = ++ match impl.impl_server_state with ++ NotConnected _ when server_blocked s -> "IP blocked" ++ | _ -> string_of_connection_state impl.impl_server_state ++ in ++ ++ let cc,cn = Geoip.get_country (Ip.ip_of_addr info.G.server_addr) in ++ html_mods_td buf ([ ++ ("", "srb", if info.G.server_master then "M" else "-"); ++ (id_title, "sr", id_text); ++ ("", "sr", n.network_name); ++ ("", "sr", server_state_string); ++ ("", "sr br", ip_port_string); ++ ] @ (if !Geoip.active then [(cn, "sr br", cc)] else []) @ [ ++ ("", "sr ar", if info.G.server_nusers = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_nusers); ++ ("", "sr ar br", if info.G.server_max_users = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_max_users); ++ ("", "sr ar br", if info.G.server_lowid_users = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_lowid_users); ++ ("", "sr ar", if info.G.server_nfiles = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_nfiles)]); ++ ++ if info.G.server_published_files = 0 then ++ html_mods_td buf ([("", "sr br", "")]) ++ else ++ Printf.bprintf buf ++"\\<TD class=\\\"sr br\\\" title=\\\"Show published files\\\" ++onClick=\\\"location.href='submit?q=server_shares+%d'\\\"\\>%d\\</TD\\>" ++ snum info.G.server_published_files; ++ ++ html_mods_td buf ([ ++ ("", "sr ar", if info.G.server_soft_limit = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_soft_limit); ++ ("", "sr ar br", if info.G.server_hard_limit = Int64.zero then "" else Printf.sprintf "%Ld" info.G.server_hard_limit); ++ ("", "sr ar br", if info.G.server_ping = 0 then "" else Printf.sprintf "%d" info.G.server_ping); ++ ("", "sr br", info.G.server_version); ++ ("", "sr", info.G.server_name); ++ ]); + ++ Printf.bprintf buf "\\<td width=\\\"100%%\\\" class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>\n" ++ info.G.server_description; + end + else + begin +Index: src/daemon/common/commonShared.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonShared.ml,v +retrieving revision 1.33 +retrieving revision 1.37 +diff -u -r1.33 -r1.37 +--- src/daemon/common/commonShared.ml 12 Jun 2006 20:47:21 -0000 1.33 ++++ src/daemon/common/commonShared.ml 29 Oct 2006 18:58:59 -0000 1.37 +@@ -43,7 +43,8 @@ + mutable impl_shared_size : int64; + mutable impl_shared_id : Md4.t; + mutable impl_shared_requests : int; +- mutable impl_shared_magic : string option ++ mutable impl_shared_magic : string option; ++ mutable impl_shared_servers : CommonTypes.server list; + } + + and 'a shared_ops = { +@@ -214,6 +215,7 @@ + impl_shared_id = Md4.null; + impl_shared_requests = 0; + impl_shared_magic = None; ++ impl_shared_servers = [] + } + + +@@ -235,17 +237,17 @@ + + let shared_scan_directory shared_dir local_dir = + let incoming_files_inode = +- ((Unix.stat ((CommonComplexOptions.incoming_files ()).shdir_dirname)).Unix.st_ino) ++ ((Unix.stat ((CommonComplexOptions.incoming_dir false ()).shdir_dirname)).Unix.st_ino) + in + let incoming_directories_inode = +- ((Unix.stat ((CommonComplexOptions.incoming_directories ()).shdir_dirname)).Unix.st_ino) ++ ((Unix.stat ((CommonComplexOptions.incoming_dir true ()).shdir_dirname)).Unix.st_ino) + in + let temp_directory_inode = + ((Unix.stat !!temp_directory).Unix.st_ino) + in + let dirname = shared_dir.shdir_dirname in + let strategy = +- CommonComplexOptions.sharing_strategies shared_dir.shdir_strategy in ++ CommonComplexOptions.sharing_strategy shared_dir.shdir_strategy in + let dirname = + if Filename.is_relative dirname then + Filename.concat file_basedir dirname +@@ -352,6 +354,7 @@ + T.shared_requests = impl.impl_shared_requests; + T.shared_uids = []; + T.shared_sub_files = []; ++ T.shared_magic = impl.impl_shared_magic; + } + + let shared_info s = +Index: src/daemon/common/commonShared.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonShared.mli,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -r1.7 -r1.8 +--- src/daemon/common/commonShared.mli 12 Jun 2006 20:47:21 -0000 1.7 ++++ src/daemon/common/commonShared.mli 8 Oct 2006 14:20:21 -0000 1.8 +@@ -9,7 +9,8 @@ + mutable impl_shared_size : int64; + mutable impl_shared_id : Md4.Md4.t; + mutable impl_shared_requests : int; +- mutable impl_shared_magic : string option ++ mutable impl_shared_magic : string option; ++ mutable impl_shared_servers : CommonTypes.server list; + } + and 'a shared_ops = { + mutable op_shared_info : 'a -> GuiTypes.shared_info; +Index: src/daemon/common/commonSources.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonSources.ml,v +retrieving revision 1.35 +retrieving revision 1.39 +diff -u -r1.35 -r1.39 +--- src/daemon/common/commonSources.ml 8 Apr 2006 02:16:21 -0000 1.35 ++++ src/daemon/common/commonSources.ml 21 Nov 2006 22:34:33 -0000 1.39 +@@ -531,7 +531,7 @@ + Sort.list + (fun f1 f2 -> + file_best_name (f1.manager_file ()) < file_best_name (f2.manager_file ()) +- ) !file_sources_managers ++ ) (List.filter (fun m -> file_state (m.manager_file ()) = FileDownloading) !file_sources_managers) + in + (* Files *) + List.iter (fun m -> +@@ -1739,6 +1739,8 @@ + | _ -> () + ) !file_sources_managers; + ++ if !files <> [] then begin ++ + (* 'normalize' to 0 priorities*) + sum_priority := !sum_priority + (!nfiles * (-(!min_priority))); + (* update priorities to be > 0 *) +@@ -1758,7 +1760,7 @@ + + (* calc sources queue size + at least 3 sources per file*) +- let nsources = maxi (!nfiles*3) ++ let nsources = max (!nfiles*3) + (functions.function_max_connections_per_second () * 10) in + + (* calc how much sources a file can get according to its priority*) +@@ -1768,99 +1770,93 @@ + (* + iter through files to queue sources + flist_todo : next files to test +- flist_done : already tested files + assigned : number of sources already queued +- pos : position in file list +- len : length of file list + looped : number of times we allow to loop try to fill queue of sources + (how hard we try to fill queue) + *) +- let rec iter_files flist_todo flist_done assigned pos len looped = +- if pos==len || assigned>nsources then +- begin +- (* +- assigned>nsources stop! +- pos=len we are at the end of file list +- *) +- (* Cleanup some sources *) +- List.iter +- (fun m -> +- let f = m.manager_file () in +- if file_state f = FileDownloading then +- begin +- let remove_old q t = begin +- if Queue.length q > 0 then +- let (request_time, s) = Queue.head q in +- if request_time + t < last_time () then +- remove_from_queue s (find_request s m); +- end in +- remove_old m.manager_sources.(do_not_try_queue) 14400; +- remove_old m.manager_sources.(old_sources3_queue) 2400; +- remove_old m.manager_sources.(old_sources2_queue) 1200; +- end +- ) !file_sources_managers; +- (* more power to the "runaway" (most overloaded) file, pick extra sources *) +- let em = +- let q = find_throttled_queue good_sources_queue in +- if queue_period.(q) > 0 then +- let max_overloaded = List.hd (find_max_overloaded q !file_sources_managers) in +- let overhead = count_file_ready_sources max_overloaded q true in +- if overhead > 0 then +- get_sources max_consecutive max_overloaded good_sources_queue 0 +- else +- 0 +- else +- 0 +- in +- if assigned + em < nsources && looped>0 then +- (* +- if assigned < nsources restart to fill +- reorder todo files by highest priority first +- allow at most looped re-iter of list to not loop endlessly +- *) +- iter_files (List.rev flist_done) [] (assigned + em) 0 len (looped-1) +- end +- else +- begin +- (* throw in new sources at high pace and +- do not care about them in get_sources, +- this avoids "locking" a file's queue +- sources with thousands of new sources +- from SE *) +- let extr = ref 0 in +- List.iter +- (fun m -> +- let f = m.manager_file () in +- let q = m.manager_sources.(new_sources_queue) in +- if file_state f = FileDownloading && Queue.length q > 0 then +- let (request_time, s) = Queue.head q in +- source_connecting s; +- if M.direct_source s.source_uid then +- begin +- incr extr; +- Fifo.put next_direct_sources s +- end +- else +- next_indirect_sources := s :: !next_indirect_sources +- ) !file_sources_managers; ++ let rec iter_files assigned looped = + +- let fp = List.hd flist_todo in +- let file = snd fp and +- prio = fst fp in +- let tt = min (truncate (sources_per_prio *. float_of_int(prio))) +- max_consecutive in +- let to_take = max tt 1 in +- (*allow at least one source per file : +- we will overflow a bit the expected next_direct_sources length +- but it's for the good cause : not 'starving' some files +- *) +- let took = get_sources to_take file good_sources_queue 0 in +- iter_files (List.tl flist_todo) (fp::flist_done) +- (assigned + took + !extr) (pos+1) len looped +- end +- in +- iter_files !files [] 0 0 (List.length !files) 3; ++ (* throw in new sources at high pace and do not care ++ about them in get_sources, this avoids "locking" a ++ file's queue sources with thousands of new sources ++ from SE *) ++ let try_some_new_sources () = ++ let extr = ref 0 in ++ List.iter ++ (fun m -> ++ let f = m.manager_file () in ++ let q = m.manager_sources.(new_sources_queue) in ++ if file_state f = FileDownloading && Queue.length q > 0 then ++ let (request_time, s) = Queue.head q in ++ source_connecting s; ++ if M.direct_source s.source_uid then begin ++ incr extr; ++ Fifo.put next_direct_sources s ++ end ++ else ++ next_indirect_sources := s :: !next_indirect_sources ++ ) !file_sources_managers; ++ !extr in ++ ++ let cleanup_some_old_sources () = ++ (* Cleanup some sources *) ++ List.iter ++ (fun m -> ++ let f = m.manager_file () in ++ if file_state f = FileDownloading then ++ let remove_old q t = ++ if Queue.length q > 0 then ++ let (request_time, s) = Queue.head q in ++ if request_time + t < last_time () then ++ remove_from_queue s (find_request s m) in ++ ++ remove_old m.manager_sources.(do_not_try_queue) 14400; ++ remove_old m.manager_sources.(old_sources3_queue) 2400; ++ remove_old m.manager_sources.(old_sources2_queue) 1200 ++ ) !file_sources_managers in ++ ++ let rec aux flist_todo assigned = ++ if assigned >= nsources then ++ cleanup_some_old_sources () ++ else ++ match flist_todo with ++ | (prio, file) :: t -> ++ let tt = min (truncate (sources_per_prio *. (float_of_int prio))) ++ max_consecutive in ++ let to_take = max tt 1 in ++ (* allow at least one source per file : ++ we will overflow a bit the expected next_direct_sources length ++ but it's for the good cause : not 'starving' some files ++ *) ++ let took = get_sources to_take file good_sources_queue 0 in ++ aux t (assigned + took) ++ ++ | [] -> ++ cleanup_some_old_sources (); ++ ++ (* more power to the "runaway" (most overloaded) file, pick extra sources *) ++ let em = ++ let q = find_throttled_queue good_sources_queue in ++ if queue_period.(q) > 0 then ++ let max_overloaded = ++ List.hd (find_max_overloaded q !file_sources_managers) in ++ let overhead = ++ count_file_ready_sources max_overloaded q true in ++ if overhead > 0 then ++ get_sources max_consecutive max_overloaded good_sources_queue 0 ++ else 0 ++ else 0 in ++ ++ if looped > 0 then ++ (* allow at most looped re-iter of list to not ++ loop endlessly *) ++ iter_files (assigned + em) (looped - 1) ++ in ++ let extr = try_some_new_sources () in ++ aux !files (assigned + extr) + ++ in ++ iter_files 0 3; + + (* adjust queue throttling *) + let all_ready = ref 0 in +@@ -1890,6 +1886,8 @@ + end + ) [ good_sources_queue; old_sources1_queue; old_sources2_queue; old_sources3_queue ]; + ++ end; ++ + if !verbose_sources > 0 then begin + lprintf_nl "[cSrc] CommonSources.refill_sources AFTER:"; + let buf = Buffer.create 100 in +Index: src/daemon/common/commonStats.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonStats.ml,v +retrieving revision 1.3 +retrieving revision 1.4 +diff -u -r1.3 -r1.4 +--- src/daemon/common/commonStats.ml 26 Aug 2006 12:05:53 -0000 1.3 ++++ src/daemon/common/commonStats.ml 23 Sep 2006 20:29:46 -0000 1.4 +@@ -87,12 +87,16 @@ + done; + s + +-let brandlist_int_to_string l i = ++let brandlist_int_to_2string l i = + if List.length l > i then +- let (_,ls,_) = List.nth l i in +- ls ++ let (_,ls,ss) = List.nth l i in ++ ls,ss + else +- "Total" ++ "Total","Ttl" ++ ++let brandlist_int_to_string l i = ++ let (ls,_) = brandlist_int_to_2string l i in ++ ls + + let print_stats_old buf arr l tl uptime = + +@@ -234,6 +238,26 @@ + ( "1", "srh", "Total uploads:downloads ratio", "U:DL" ); + ] + ++let stats_list l arr = ++ let sl = ref [] in ++ for i = 0 to (Array.length arr) - 1 do ++ let r = arr.(i) in ++ if r.brand_seen > 0 then begin ++ let ls,ss = brandlist_int_to_2string l i in ++ let s = { ++ string_long = ls; ++ string_short = ss; ++ seen = r.brand_seen; ++ banned = r.brand_banned; ++ filerequest = r.brand_filerequest; ++ download = r.brand_download; ++ upload = r.brand_upload; ++ } in ++ sl := s :: !sl ++ end ++ done; ++ !sl ++ + let print_stats_html_mods buf arr l tl uptime = + + let stats_all = build_all arr in +Index: src/daemon/common/commonTypes.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonTypes.ml,v +retrieving revision 1.55 +retrieving revision 1.62 +diff -u -r1.55 -r1.62 +--- src/daemon/common/commonTypes.ml 5 Sep 2006 14:18:24 -0000 1.55 ++++ src/daemon/common/commonTypes.ml 13 Nov 2006 13:10:17 -0000 1.62 +@@ -88,6 +88,9 @@ + string_of_uid_sep uid "_" + + exception Illegal_urn of string ++exception Torrent_already_exists ++exception Torrent_can_not_be_used ++ + let uid_of_string s = + let s = String.lowercase s in + let urn = String2.before s 4 in +@@ -347,7 +350,7 @@ + + type shared_directory = { + shdir_dirname : string; +- shdir_priority : int; ++ mutable shdir_priority : int; + shdir_strategy : string; + shdir_networks : string list; + } +@@ -376,6 +379,8 @@ + | ByNet + | ByAvail + | ByComments ++| ByUser ++| ByGroup + | NotSorted + + type room_state = +@@ -452,7 +457,38 @@ + ExtendSearchLocally + | ExtendSearchRemotely + +-type network = { ++type network_stat_info = { ++ mutable string_long : string; ++ mutable string_short : string; ++ mutable seen : int; ++ mutable banned : int; ++ mutable filerequest : int; ++ mutable download : Int64.t; ++ mutable upload : Int64.t; ++} ++ ++type network_porttest = ++ PorttestNotAvailable ++| PorttestNotStarted ++| PorttestInProgress of int ++| PorttestResult of int * string ++ ++type groupdb = { ++ group_name : string; ++ mutable group_admin : bool; ++} ++ ++and userdb = { ++ user_name : string; ++ mutable user_pass : Md4.t; ++ mutable user_groups : groupdb list; ++ mutable user_default_group : groupdb option; ++ mutable user_mail : string; ++ mutable user_commit_dir : string; ++ mutable user_max_concurrent_downloads : int; ++} ++ ++and network = { + network_name : string; + network_num : int; + network_connection_manager : TcpBufferedSocket.connection_manager; +@@ -480,7 +516,7 @@ + mutable op_network_share : ( + string -> string -> int64 -> unit); + mutable op_network_private_message : (string -> string -> unit); +- mutable op_network_parse_url : (string -> string * bool); ++ mutable op_network_parse_url : (string -> userdb -> string * bool); + mutable op_network_connect_servers : (unit -> unit); + + mutable op_network_search : (search -> Buffer.t -> unit); +@@ -492,17 +528,20 @@ + mutable op_network_info : (unit -> network_info); + + mutable op_network_connected : (unit -> bool); +- mutable op_network_gui_message : (string -> unit); ++ mutable op_network_gui_message : (string -> userdb -> unit); + +- mutable op_network_download : (result_info -> file); ++ mutable op_network_download : (result_info -> userdb -> file); + mutable op_network_display_stats : (Buffer.t -> ui_conn -> unit); ++ mutable op_network_stat_info_list : unit -> (string * int * (network_stat_info list)) list; + mutable op_network_clean_exit : (unit -> bool); + mutable op_network_reset : (unit -> unit); + mutable op_network_ports : (unit -> (int * string) list); ++ mutable op_network_porttest_start : (unit -> unit); ++ mutable op_network_porttest_result : (unit -> network_porttest); + } + +-and ui_user = { +- ui_user_name : string; ++and ui_user = { ++ ui_user : userdb; + mutable ui_user_searches : search list; + mutable ui_last_search : search option; + mutable ui_last_results : (int * result) list; +@@ -887,3 +926,20 @@ + f : string -> string -> unit; + description : string + } ++ ++type slot_kind = ++ NoSlot ++| FriendSlot ++| ReleaseSlot ++| SmallFileSlot ++| NormalSlot ++| PrioSlot of string ++ ++let string_of_slot_kind slot_kind short = ++ match slot_kind with ++ NoSlot -> "NoSlot" ++ | FriendSlot -> "FriendSlot" ++ | ReleaseSlot -> "ReleaseSlot" ++ | SmallFileSlot -> "SmallFileSlot" ++ | NormalSlot -> if short then "" else "NormalSlot" ++ | PrioSlot dir -> Printf.sprintf "Prio %s" dir +Index: src/daemon/common/commonUploads.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonUploads.ml,v +retrieving revision 1.45 +retrieving revision 1.51 +diff -u -r1.45 -r1.51 +--- src/daemon/common/commonUploads.ml 5 Sep 2006 14:15:19 -0000 1.45 ++++ src/daemon/common/commonUploads.ml 21 Nov 2006 22:34:33 -0000 1.51 +@@ -244,7 +244,7 @@ + !!http_port, "http_port"; + !!telnet_port, "telnet_port"; + !!gui_port, "gui_port"; +- !!gift_port, "gift_port"; ++ !!gift_port, "gift_port GUI"; + ]); + network.op_network_connected_servers <- (fun _ -> []) + +@@ -279,15 +279,6 @@ + let shared_counter = ref (Int64.zero) + let shared_files = Hashtbl.create 13 + +-let _ = +- Heap.add_memstat "CommonUploads" (fun level buf -> +- Printf.bprintf buf " infos_by_name: %d\n" (Hashtbl.length infos_by_name); +- Printf.bprintf buf " shareds_by_uid: %d\n" (Hashtbl.length shareds_by_uid); +- Printf.bprintf buf " shareds_by_id: %d\n" (Hashtbl.length shareds_by_id); +- Printf.bprintf buf " shared_files: %d\n" (Hashtbl.length shared_files); +- ) +- +- + let new_shared_dir dirname = { + shared_dirname = dirname; + shared_files = []; +@@ -695,7 +686,8 @@ + impl_shared_ops = shared_ops; + impl_shared_val = sh; + impl_shared_requests = 0; +- impl_shared_magic = None ++ impl_shared_magic = None; ++ impl_shared_servers = []; + } + and sh = { + shared_info = index; +@@ -838,14 +830,7 @@ + end + else + if not (Intmap.mem (client_num c) !pending_slots_map) then +- begin +-(* This is useless since it is the goal of the pending_slots_map +- else if Fifo.mem pending_slots_fifo (client_num c) then begin +- lprintf "Avoided inserting a client twice in pending slots\n"; +- +- end else *) +- pending_slots_map := Intmap.add (client_num c) c !pending_slots_map; +- end ++ pending_slots_map := Intmap.add (client_num c) c !pending_slots_map + + let remove_pending_slot c = + if Intmap.mem (client_num c) !pending_slots_map then +@@ -853,11 +838,11 @@ + + let rec give_a_slot c = + remove_pending_slot c; +- if not (client_is_connected c) then begin +- find_pending_slot () +- end +- else begin +- set_client_has_a_slot c true; ++ if not (client_is_connected c) then ++ find_pending_slot () ++ else ++ begin ++ set_client_has_a_slot c NormalSlot; + client_enter_upload_queue c + end + +@@ -872,36 +857,47 @@ + with _ -> () + + let add_pending_slot c = +- let csh = client_upload c in ++ let client_upload c = ++ match client_upload c with ++ None -> raise Not_found ++ | Some file -> file ++ in ++ let csh = file_shared (client_upload c) in + let cdir = shared_dir csh in + let cprio = ref (shared_prio csh) in + let cfriend = ref (if is_friend c && !!friends_upload_slot then 1 else 0) in + let csmallfiles = ref (match csh with + | None -> 0 + | Some sh -> if shared_size sh <= !!small_files_slot_limit then 1 else 0) in +- (* if cdir <> "" then +- lprintf "Testing cdir %s\n" cdir; *) ++ let allowed_release_slots = ++ ref (Misc.percentage_of_ints !!max_upload_slots !!max_release_slots) in ++ ++(* check current upload slots for already used special slots *) + Intmap.iter (fun _ c -> +- let sh = client_upload c in +- if shared_dir sh = cdir then decr cprio; +- if client_has_a_friend_slot c then decr cfriend; +- match sh with +- | None -> () +- | Some sh -> +- if shared_size sh <= !!small_files_slot_limit then +- decr csmallfiles; +- ) !CommonClient.uploaders; +- (* if cdir <> "" then +- lprintf "Testing cprio %d cfriend %d csmallfiles\n" +- !cprio !cfriend !csmallfiles; *) +- if !cprio > 0 || !cfriend > 0 || !csmallfiles > 0 then begin +- remove_pending_slot c; +- if client_is_connected c then begin +- set_client_has_a_slot c true; +- client_enter_upload_queue c +- end +- end else +- add_pending_slot c ++ if shared_dir (file_shared (client_upload c)) = cdir then ++ decr cprio; ++ match client_slot c with ++ ReleaseSlot -> decr allowed_release_slots ++ | FriendSlot -> decr cfriend ++ | SmallFileSlot -> decr csmallfiles ++ | _ -> ()) !CommonClient.uploaders; ++ ++ let slot_type = ++ if file_release (client_upload c) && !allowed_release_slots > 0 then Some ReleaseSlot else ++ if !cfriend > 0 then Some FriendSlot else ++ if !csmallfiles > 0 then Some SmallFileSlot else ++ if !cprio > 0 then Some (PrioSlot cdir) else ++ None ++ in ++ match slot_type with ++ Some slot -> ++ remove_pending_slot c; ++ if client_is_connected c then ++ begin ++ set_client_has_a_slot c slot; ++ client_enter_upload_queue c ++ end ++ | None -> add_pending_slot c + + let static_refill_upload_slots () = + let len = Intmap.length !CommonClient.uploaders in +@@ -938,7 +934,7 @@ + estimated_capacity + else + (* max_hard_upload_rate lowered manually,... *) +- mini estimated_capacity (!!max_hard_upload_rate * 1024) in ++ min estimated_capacity (!!max_hard_upload_rate * 1024) in + if !verbose_upload then + lprintf_nl "usage: %d(%d) capacity: %d" + (short_delay_upload_usage ()) +@@ -966,34 +962,18 @@ + end + + let turn = ref (-1) +-let turn_h = ref (-1) + + let refill_upload_slots () = + incr turn; + if !turn = 5 then + turn := 0; +- if !turn_h = 360 then +- turn_h := 0; + if !!dynamic_slots then begin + if !turn = 0 then + (* call every 5s *) + dynamic_refill_upload_slots () + end else + (* call every 1s *) +- static_refill_upload_slots (); +- +- if !turn = 0 then begin +- (* call every 5s *) +- incr turn_h; +- update_download_history (); +- update_upload_history () +- end; +- +- if !turn_h = 0 then begin +- (* call every 720 * 5s *) +- update_h_download_history (); +- update_h_upload_history () +- end ++ static_refill_upload_slots () + + let consume_bandwidth len = + streaming_left := !streaming_left - len +@@ -1067,3 +1047,12 @@ + | l -> l + in + get_name_keywords ++ ++let _ = ++ Heap.add_memstat "CommonUploads" (fun level buf -> ++ Printf.bprintf buf " infos_by_name: %d\n" (Hashtbl.length infos_by_name); ++ Printf.bprintf buf " shareds_by_uid: %d\n" (Hashtbl.length shareds_by_uid); ++ Printf.bprintf buf " shareds_by_id: %d\n" (Hashtbl.length shareds_by_id); ++ Printf.bprintf buf " shared_files: %d\n" (Hashtbl.length shared_files); ++ Printf.bprintf buf " pending_slots: %d\n" (Intmap.length !pending_slots_map); ++ ) +Index: src/daemon/common/commonUserDb.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonUserDb.ml,v +retrieving revision 1.5 +retrieving revision 1.7 +diff -u -r1.5 -r1.7 +--- src/daemon/common/commonUserDb.ml 12 Aug 2006 20:36:14 -0000 1.5 ++++ src/daemon/common/commonUserDb.ml 9 Nov 2006 21:32:26 -0000 1.7 +@@ -31,18 +31,130 @@ + let lprintf_n fmt = + lprintf2 log_prefix fmt + +-type userdb = { +- user_name : string; +- user_pass : Md4.t; +- user_mail : string; +- } ++(*************************************************************************) ++(* DEFAULTS *) ++(*************************************************************************) + +-let admin_user = "admin" ++let users_ini = create_options_file "users.ini" ++ ++let users2_section = file_section users_ini ["Users"] "User accounts on the core (new format)" ++let users_section = file_section users_ini ["Users"] "User accounts on the core (old format)" + +-let user2_is_admin user = user = admin_user ++let dummy_group = { ++ group_name = ""; ++ group_admin = true; ++} ++ ++let default_group_name = "mldonkey" ++let system_user_default_group = { ++ dummy_group with ++ group_name = default_group_name ++} + + let blank_password = Md4.string "" + ++let dummy_user = { ++ user_name = ""; ++ user_pass = blank_password; ++ user_groups = [system_user_default_group]; ++ user_default_group = Some system_user_default_group; ++ user_mail = ""; ++ user_commit_dir = ""; ++ user_max_concurrent_downloads = 0; ++} ++ ++let admin_user_name = "admin" ++let admin_user = { ++ dummy_user with ++ user_name = admin_user_name; ++} ++ ++(*************************************************************************) ++(* GroupOption *) ++(*************************************************************************) ++ ++module GroupOption = struct ++ ++ let value_to_group v = ++ match v with ++ | Options.Module assocs -> ++ let get_value name conv = conv (List.assoc name assocs) in ++ let gname = ++ try ++ get_value "group_name" value_to_string ++ with _ -> default_group_name ++ in ++ let gadmin = ++ try ++ get_value "group_admin" value_to_bool ++ with _ -> true ++ in ++ { group_name = gname; ++ group_admin = gadmin; ++ } ++ ++ | _ -> failwith "Options: not a valid group" ++ ++ let group_to_value group = ++ Options.Module [ ++ "group_name", string_to_value group.group_name; ++ "group_admin", bool_to_value group.group_admin; ++ ] ++ ++ let t = define_option_class "Groups" value_to_group group_to_value ++ ++ end ++ ++let grouplist = define_option users2_section ["groups"] ++ "The groups that are defined on this core. ++ ++group_admin = Are members of this group MLDonkey admins? ++ Only members of this group can change settings and see uploads. ++" ++ (list_option GroupOption.t) [system_user_default_group] ++ ++(*************************************************************************) ++(* Group database functions *) ++(*************************************************************************) ++ ++let user2_groups_iter f = ++ List.iter f ((List.sort (fun g1 g2 -> compare g1.group_name g2.group_name)) !!grouplist) ++ ++let update_group name new_group = ++ let other_groups = List.filter (fun g -> g.group_name <> name) !!grouplist in ++ grouplist =:= ++ match new_group with ++ | None -> other_groups ++ | Some new_group -> new_group :: other_groups ++ ++let user2_group_add name admin = ++ let new_group = { ++ group_name = name; ++ group_admin = admin; ++ } in ++ update_group name (Some new_group) ++ ++let user2_group_remove group = ++ update_group group.group_name None ++ ++let user2_group_find group = ++ List.find (fun g -> g.group_name = group) !!grouplist ++ ++let user2_group_exists group = ++ List.exists (fun g -> g.group_name = group) !!grouplist ++ ++let user2_default_group_matches_group dgroup group = ++ match dgroup with ++ None -> false ++ | Some g -> group = g ++ ++let user2_group_admin group admin = ++ group.group_admin <- admin ++ ++(*************************************************************************) ++(* UserOption *) ++(*************************************************************************) ++ + module UserOption = struct + + let value_to_user v = +@@ -65,9 +177,49 @@ + get_value "user_mail" value_to_string + with _ -> "" + in ++ let ucdir = ++ try ++ get_value "user_commit_dir" value_to_string ++ with _ -> "" ++ in ++ let umaxdl = ++ try ++ get_value "user_max_concurrent_downloads" value_to_int ++ with _ -> 0 ++ in ++ let ugroups = ++ try ++ let ugl = get_value "user_groups" (value_to_list value_to_string) in ++ List.map user2_group_find ugl ++ with Not_found -> [system_user_default_group] ++ in ++ let udgroup = ++ try ++ match get_value "user_default_group" stringvalue_to_option with ++ None -> None ++ | Some udg -> ++ begin try ++ let g = user2_group_find udg in ++ if List.mem g ugroups then ++ Some g ++ else begin ++ lprintf_nl "User %s is not member of group %s, setting user_default_group to None" uname udg; ++ None ++ end ++ with Not_found -> ++ lprintf_nl "user_default_group %s of user %s does not exist, setting to None" udg uname; ++ None ++ end ++ with Not_found -> Some system_user_default_group ++ in + { user_name = uname; + user_pass = upass; +- user_mail = umail; } ++ user_groups = ugroups; ++ user_default_group = udgroup; ++ user_mail = umail; ++ user_commit_dir = ucdir; ++ user_max_concurrent_downloads = umaxdl; ++ } + + | _ -> failwith "Options: not a valid user" + +@@ -75,94 +227,220 @@ + Options.Module [ + "user_name", string_to_value user.user_name; + "user_pass", string_to_value (Md4.to_string user.user_pass); +- "user_mail", string_to_value user.user_mail; ] ++ "user_groups", list_to_value (fun v -> string_to_value v.group_name) user.user_groups; ++ "user_default_group", option_to_stringvalue (match user.user_default_group with Some g -> Some g.group_name | None -> None); ++ "user_mail", string_to_value user.user_mail; ++ "user_commit_dir", string_to_value user.user_commit_dir; ++ "user_max_concurrent_downloads", int_to_value user.user_max_concurrent_downloads; ++ ] + + let t = define_option_class "Users" value_to_user user_to_value + + end + +-let users_ini = create_options_file "users.ini" +- +-let users_section = file_section users_ini ["Users"] "User accounts on the core" +-let users2_section = file_section users_ini ["Users"] "User accounts on the core (new format)" +- +-let users = define_option users_section ["users"] +- "Depreciated option, kept for compatibility reasons - used by MLDonkey < 2.7.5" +- (list_option (tuple2_option (string_option, Md4.option))) +- [] +- + let userlist = define_option users2_section ["users2"] + "The users that are defined on this core. The default user is + called 'admin', and uses an empty password. To create new users, +-login as admin in mldonkey, and use the 'useradd' command." +- (list_option UserOption.t) +- [ { user_name = admin_user; +- user_pass = blank_password; +- user_mail = "" } ] ++login as admin in mldonkey, and use the 'useradd' command. + +-let users2 = Hashtbl.create 10 ++user_groups = Files belonging to one of these groups can be seen by the user. ++user_default_group = New downloads by this user will belong to this group. ++user_commit_dir = Commit files to <incoming>/<user_commit_dir> ++user_mail = Address used to sent confirmation mails after comitting a download ++user_max_concurrent_downloads = Maximum number of downloads allowed, 0 = unlimited ++" ++ (list_option UserOption.t) [admin_user] + +-let user2_iter f = +- Hashtbl.iter f users2 ++let users = define_option users_section ["users"] ++ "Depreciated option, kept for compatibility reasons - used by MLDonkey < 2.7.5" ++ (list_option (tuple2_option (string_option, Md4.option))) ++ [admin_user.user_name, blank_password] + +-let user2_add name pass mail = +- let u = { ++(*************************************************************************) ++(* User database functions *) ++(*************************************************************************) ++ ++let user2_users_iter f = ++ List.iter f ((List.sort (fun u1 u2 -> compare u1.user_name u2.user_name)) !!userlist) ++ ++let update_user name new_user = ++ let other_users = List.filter (fun u -> u.user_name <> name) !!userlist in ++ userlist =:= ++ match new_user with ++ | None -> other_users ++ | Some new_user -> new_user :: other_users ++ ++let user2_user_add name pass ?(groups = [default_group_name]) ++ ?(default_group = Some default_group_name) ++ ?(mail = "") ?(commit_dir = "") ?(max_dl = 0) () = ++ (* shouldn't we warn admin about already existing user ? *) ++ let groups = ++ let l = List.map user2_group_find (List.filter user2_group_exists groups) in ++ if l = [] then [system_user_default_group] else l ++ in ++ let default_group = ++ match default_group with ++ None -> None ++ | Some group -> if not (user2_group_exists group) then None else Some (user2_group_find group) ++ in ++ let new_user = { + user_name = name; + user_pass = pass; +- user_mail = mail ++ user_groups = groups; ++ user_default_group = default_group; ++ user_mail = mail; ++ user_commit_dir = commit_dir; ++ user_max_concurrent_downloads = max_dl; + } in +- Hashtbl.replace users2 name u; +- u ++ update_user name (Some new_user) + +-let user2_remove user = +- Hashtbl.remove users2 user ++let user2_user_remove user = ++ update_user user None + +-let user2_find user = +- try +- Hashtbl.find users2 user +- with Not_found -> failwith (Printf.sprintf "User %s does not exist" user) ++let user2_user_find user = ++ List.find (fun u -> u.user_name = user) !!userlist + +-let user2_password user = +- try +- let u = user2_find user in +- u.user_pass +- with Not_found -> failwith (Printf.sprintf "User %s does not exist" user) ++let user2_user_exists user = ++ List.exists (fun u -> u.user_name = user) !!userlist + +-let user2_mail user = +- try +- let u = user2_find user in +- u.user_mail +- with Not_found -> failwith (Printf.sprintf "User %s does not exist" user) ++(*************************************************************************) ++(* User database functions / passwords *) ++(*************************************************************************) ++ ++let user2_user_password user = ++ (user2_user_find user).user_pass ++ ++let user2_user_set_password user pass_string = ++ user.user_pass <- Md4.string pass_string + + let valid_password user pass = + try +- user2_password user = Md4.string pass +- with e -> false ++ user2_user_password user = Md4.string pass ++ with Not_found -> false + +-let empty_password user = +- try +- let p = user2_password user in +- p = blank_password +- with _ -> false ++let has_empty_password user = ++ valid_password user.user_name "" ++ ++(*************************************************************************) ++(* User database functions *) ++(*************************************************************************) ++ ++let user2_user_set_mail user mail = ++ user.user_mail <- mail ++ ++let user2_print_user_dls user = ++ let dls = user.user_max_concurrent_downloads in ++ if dls = 0 then "unlimited" else string_of_int dls ++ ++let user2_user_set_dls user dls = ++ user.user_max_concurrent_downloads <- dls ++ ++let user2_user_commit_dir user = ++ (user2_user_find user).user_commit_dir ++ ++let user2_user_set_commit_dir user dir = ++ user.user_commit_dir <- dir ++ ++(*************************************************************************) ++(* User/Group database functions *) ++(*************************************************************************) ++ ++let sort_groups_by_name gl = ++ List.sort (fun g1 g2 -> compare g1.group_name g2.group_name) gl ++ ++let user2_user_groups_iter user f = ++ List.iter f (sort_groups_by_name user.user_groups) ++ ++let user2_print_user_groups sep user = ++ String.concat sep (List.map (fun g -> g.group_name) (sort_groups_by_name user.user_groups)) ++ ++let user2_print_group group = ++ match group with ++ None -> "none" ++ | Some group -> group.group_name ++ ++let user2_print_user_default_group user = ++ user2_print_group user.user_default_group ++ ++let user2_user_set_default_group user group = ++ user.user_default_group <- group ++ ++let user2_user_add_group user group = ++ user.user_groups <- group :: user.user_groups ++ ++let user2_user_remove_group user group = ++ user.user_groups <- List.filter ((<>) group) user.user_groups ++ ++let user2_num_group_members group = ++ let counter = ref 0 in ++ user2_users_iter (fun u -> ++ user2_user_groups_iter u (fun g -> ++ if g = group then incr counter)); ++ !counter ++ ++(*************************************************************************) ++(* Access rights *) ++(*************************************************************************) ++ ++let user2_is_admin user = ++ user.user_name = admin_user.user_name || ++ List.exists (fun groupname -> ++ try ++ groupname.group_admin ++ with Not_found -> false) ++ user.user_groups ++ ++(* could be expanded later *) ++let user2_can_view_uploads user = ++ user2_is_admin user ++ ++let user2_can_view_file user file_owner file_group = ++ user2_is_admin user || user = file_owner || ++ (match file_group with ++ | None -> false ++ | Some file_group -> List.mem file_group user.user_groups) ++ ++(*************************************************************************) ++(* Hooks *) ++(*************************************************************************) + + let _ = + set_after_load_hook users_ini (fun _ -> +- List.iter (fun user -> +- ignore (user2_add user.user_name user.user_pass user.user_mail) +- ) !!userlist; +- userlist =:= []; +- if !!users <> [] then begin +- lprintf_nl "converting %d users to new format" (List.length !!users); +- List.iter (fun (user,pass) -> ignore (user2_add user pass "")) !!users; +- users =:= [] +- end ++ List.iter (fun (user,pass) -> ++ if not (user2_user_exists user) then begin ++ user2_user_add user pass (); ++ lprintf_nl "converted user %s to new format" user ++ end) !!users; ++(* clean !!users to avoid saving users more than once *) ++ users =:= []; ++(* Security and default checks ++ - user "admin" must exist, it has hard-coded admin rights independent of group membership ++ - group "mldonkey" must exist and must have admin status *) ++ if not (user2_user_exists admin_user.user_name) then ++ begin ++ user2_user_add admin_user.user_name blank_password (); ++ lprintf_nl "SECURITY INFO: user 'admin' has to be present, creating with empty password..." ++ end; ++ begin ++ try ++ let g = user2_group_find default_group_name in ++ if not g.group_admin then ++ begin ++ user2_group_admin g true; ++ lprintf_nl "SECURITY INFO: group 'mldonkey' must have admin status, updating..." ++ end ++ with Not_found -> ++ user2_group_add default_group_name true; ++ lprintf_nl "SECURITY INFO: group 'mldonkey' has to be present, creating with admin rights..." ++ end + ); ++ ++(* This code provides backward-compatibility for older MLDonkey clients *) ++(* reading new user db and copying the values into old user db !!users *) + set_before_save_hook users_ini (fun _ -> +- user2_iter (fun _ user -> +- userlist =:= (user2_find user.user_name) :: !!userlist; +- users =:= (user.user_name, (user2_password user.user_name)) :: !!users +- ) ++ user2_users_iter (fun user -> ++ users =:= (user.user_name, (user2_user_password user.user_name)) :: !!users ++ ) + ); +- set_after_save_hook users_ini (fun _ -> +- userlist =:= []; +- users =:= []) ++(* clean !!users to avoid saving users more than once *) ++ set_after_save_hook users_ini (fun _ -> users =:= []) +Index: src/daemon/common/commonWeb.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/commonWeb.ml,v +retrieving revision 1.33 +retrieving revision 1.36 +diff -u -r1.33 -r1.36 +--- src/daemon/common/commonWeb.ml 14 Jul 2006 13:24:23 -0000 1.33 ++++ src/daemon/common/commonWeb.ml 15 Nov 2006 12:37:13 -0000 1.36 +@@ -57,7 +57,8 @@ + let kind_record = { f = f; description = descr } in + file_kinds := (kind, kind_record) :: !file_kinds + +-let mldonkey_wget url f = ++ ++let mldonkey_wget_url url f = + let module H = Http_client in + let r = { + H.basic_request with +@@ -100,27 +101,21 @@ + match !date with + None -> H.wget r f + | Some date -> +- let html_time = +- begin try +- let t = Date.time_of_string date in +- r.H.req_save_to_file_time <- t; +- Unix.gmtime t +- with e -> +- let t = Unix.time () in +- r.H.req_save_to_file_time <- t; +- Unix.gmtime t +- end +- in + let file = Filename.concat "web_infos" (Filename.basename r.H.req_url.Url.short_file) in ++ r.H.req_save_to_file_time <- (begin try ++ Date.time_of_string date ++ with e -> ++ Unix.time () ++ end); + if not (Sys.file_exists file) then + H.wget r f + else + begin + let file_date = Unix.LargeFile.stat file in +- let file_time = Unix.gmtime file_date.Unix.LargeFile.st_mtime in +- if html_time <= file_time then ++ if r.H.req_save_to_file_time <= file_date.Unix.LargeFile.st_mtime then + begin +- lprintf_nl (_b "using local version of %s, HTML header (%s)") file date; ++ lprintf_nl (_b "using local version of %s (%s), HTML header (%s)") ++ file (Date.to_full_string file_date.Unix.LargeFile.st_mtime) date; + (f file : unit) + end + else +@@ -135,6 +130,19 @@ + (Printexc2.to_string e) url + end + ++let mldonkey_wget_shell url f = ++ let command_urlencoded = Str.string_after url 8 in ++ let command = Url.decode command_urlencoded in ++ let filename = Filename.temp_file "wget_" ".tmp" in ++ Sys.command (Printf.sprintf "%s > %s" command filename); ++ (f filename : unit) ++ ++let mldonkey_wget url f = ++ if Str.string_match (Str.regexp "shell://") url 0 then ++ mldonkey_wget_shell url f ++ else ++ mldonkey_wget_url url f ++ + let load_url can_fail kind url = + let f = + try +@@ -184,13 +192,47 @@ + + let rss_feeds = Hashtbl.create 10 + +- + let _ = + add_web_kind "rss" "Syndication feeds to get periodically updated data" + (fun url filename -> + lprintf_nl (_b "parsing feed %s (rss)") url; +- let c = Rss.channel_of_file filename in +- (try Sys.remove filename with _ -> ()); ++ let c = ++ (try ++ let rss_c = Rss.channel_of_file filename in ++ (try Sys.remove filename with _ -> ()); ++ rss_c ++ with Xml.Error _ -> ++ lprintf_nl (_b "found buggy feed, preprocessing with %s and trying again") !!rss_preprocessor; ++ (try ++ let pipe_out, pipe_in = Unix.pipe () in ++ let pid = Unix.create_process !!rss_preprocessor [| !!rss_preprocessor; filename |] ++ Unix.stdin pipe_in pipe_in in ++ Unix.close pipe_in; ++ let output = Buffer.create 1024 in ++ let buffersize = 1024 in ++ let buffer = String.create buffersize in ++ (try ++ while true do ++ let nread = Unix.read pipe_out buffer 0 buffersize in ++ if nread = 0 then raise End_of_file; ++ Buffer.add_substring output buffer 0 nread ++ done ++ with ++ | End_of_file -> () ++ | Unix.Unix_error (code, f, arg) -> ++ lprintf_nl "%s failed: %s" !!rss_preprocessor (Unix.error_message code)); ++ (try Unix.close pipe_out with _ -> ()); ++ (try Sys.remove filename with _ -> ()); ++ let _pid, _ = Unix.waitpid [] pid in ++ let result = Buffer.contents output in ++ if result = "" then begin ++ lprintf_nl (_b "%s produced empty content for feed %s, program missing?") !!rss_preprocessor url; ++ raise Not_found ++ end; ++ Rss.channel_of_string result ++ with Unix.Unix_error (code, f, arg) -> ++ lprintf_nl (_b "%s failed: %s") !!rss_preprocessor (Unix.error_message code); raise Not_found)) ++ in + let feed = + try Hashtbl.find rss_feeds url with + Not_found -> +Index: src/daemon/common/guiDecoding.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/guiDecoding.ml,v +retrieving revision 1.59 +retrieving revision 1.65 +diff -u -r1.59 -r1.65 +--- src/daemon/common/guiDecoding.ml 1 Sep 2006 16:22:14 -0000 1.59 ++++ src/daemon/common/guiDecoding.ml 14 Nov 2006 18:42:59 -0000 1.65 +@@ -506,13 +506,29 @@ + let get_int_pos s pos = + get_int s pos, pos + 4 + +-let get_sub_files s pos = ++let get_sub_files proto s pos = + get_list (fun s pos -> + let name, pos = get_string s pos in + let size, pos = get_int64 s pos, pos+8 in +- (name, size, Some ""), pos ++ let magic, pos = ++ if proto > 40 then ++ get_string s pos ++ else ++ "", pos ++ in ++ (name, size, Some magic), pos + ) s pos + ++let get_file_comments proto s pos = ++ get_list (fun s pos -> ++ let ip, pos = get_ip2 proto s pos in ++ let name, pos = get_string s pos in ++ let rating, pos = get_uint8 s pos, pos+1 in ++ let comment, pos = get_string s pos in ++ (ip, name, rating, comment), pos ++ ) s pos ++ ++ + let get_file proto s pos = + let num = get_int s pos in + let net = get_int s (pos+4) in +@@ -557,10 +573,31 @@ + in + let sub_files, pos = + if proto > 35 then +- get_sub_files s pos ++ get_sub_files proto s pos + else [], pos + in +- ++ let magic, pos = ++ if proto > 40 then ++ let ms, pos = get_string s pos ++ in Some ms, pos ++ else ++ Some "", pos ++ in ++ let comments, pos = ++ if proto > 40 then ++ get_file_comments proto s pos ++ else [], pos ++ in ++ let user, pos = ++ if proto > 40 ++ then get_string s pos ++ else "", pos ++ in ++ let group, pos = ++ if proto > 40 ++ then get_string s pos ++ else "", pos ++ in + (* + assert (num = file_info_test.file_num); + assert (net = file_info_test.file_network); +@@ -609,6 +646,11 @@ + file_priority = priority; + file_uids = uids; + file_sub_files = sub_files; ++ file_magic = magic; ++ file_comments = comments; ++ file_user = user; ++ file_group = group; ++ file_release = false; + }, pos + + let get_host_state proto s pos = +@@ -709,12 +751,15 @@ + server_banner = ""; + server_users = None; + server_preferred = preferred; ++ server_master = false; + server_version = ve; + server_max_users = ma; + server_lowid_users = lo; + server_soft_limit = so; + server_hard_limit = ha; + server_ping = pi; ++ server_published_files = 0; ++ server_features = None; + }, pos + + let get_client_type s pos = +@@ -768,6 +813,7 @@ + client_files = None; + client_connect_time = 0; + client_software = ""; ++ client_os = None; + client_release = ""; + client_emulemod = ""; + client_downloaded = zero; +@@ -826,6 +872,7 @@ + client_files = None; + client_connect_time = connect_time; + client_software = software; ++ client_os = None; + client_release = release; + client_emulemod = emulemod; + client_downloaded = downloaded; +@@ -940,6 +987,7 @@ + shared_requests = requests; + shared_uids = []; + shared_sub_files = []; ++ shared_magic = Some ""; + } + + let get_shared_info_version_10 proto s pos = +@@ -957,9 +1005,16 @@ + in + let sub_files, pos = + if proto > 36 then +- get_sub_files s pos ++ get_sub_files proto s pos + else [], pos + in ++ let magic, pos = ++ if proto > 40 then ++ let ms, pos = get_string s (pos) in ++ Some ms, pos ++ else ++ Some "", pos ++ in + { + shared_num = num; + shared_network = network; +@@ -969,6 +1024,7 @@ + shared_requests = requests; + shared_uids = uids; + shared_sub_files = sub_files; ++ shared_magic = magic; + } + + +@@ -998,7 +1054,7 @@ + | 5 + | 52 -> + if proto < 14 then +- let pass = fst (get_string s 2) in Password (CommonUserDb.admin_user, pass) ++ let pass = fst (get_string s 2) in Password (CommonUserDb.admin_user.CommonTypes.user_name, pass) + else + let pass,pos = get_string s 2 in + let login,pos = get_string s pos in +@@ -1262,6 +1318,10 @@ + let b = get_bool s 6 in + ServerSetPreferred (num, b) + ++ | 68 -> ++ let num = get_int s 2 in ++ GetStats num ++ + | _ -> + lprintf_nl "FROM GUI:Unknown message %d" opcode; + raise FromGuiMessageNotImplemented +Index: src/daemon/common/guiEncoding.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/guiEncoding.ml,v +retrieving revision 1.57 +retrieving revision 1.59 +diff -u -r1.57 -r1.59 +--- src/daemon/common/guiEncoding.ml 1 Sep 2006 16:22:14 -0000 1.57 ++++ src/daemon/common/guiEncoding.ml 31 Oct 2006 15:40:05 -0000 1.59 +@@ -542,10 +542,25 @@ + buf_int8 buf 18; + buf_string buf x + +-let buf_sub_files buf l = +- buf_list buf (fun buf (name, size, _) -> ++let magic_string m = ++ match m with ++ Some s -> s ++ | None -> "" ++ ++let buf_sub_files proto buf l = ++ buf_list buf (fun buf (name, size, magic) -> + buf_string buf name; +- buf_int64 buf size ++ buf_int64 buf size; ++ if proto > 40 then ++ buf_string buf (magic_string magic) ++ ) l ++ ++let buf_file_comments proto buf l = ++ buf_list buf (fun buf (ip, name, rating, comment) -> ++ buf_ip2 proto buf ip; ++ buf_string buf name; ++ buf_int8 buf rating; ++ buf_string buf comment; + ) l + + let buf_file proto buf f = +@@ -587,7 +602,13 @@ + if proto > 30 then + buf_list buf buf_uid f.file_uids; + if proto > 35 then +- buf_sub_files buf f.file_sub_files ++ buf_sub_files proto buf f.file_sub_files; ++ if proto > 40 then begin ++ buf_string buf (magic_string f.file_magic); ++ buf_file_comments proto buf f.file_comments; ++ buf_string buf f.file_user; ++ buf_string buf f.file_group; ++ end + + let buf_addr proto buf addr = + (match addr with +@@ -645,7 +666,7 @@ + buf_int buf c.client_chat_port + end else + begin +- buf_string buf c.client_software; ++ buf_string buf (client_software_short c.client_software c.client_os); + buf_int64 buf c.client_downloaded; + buf_int64 buf c.client_uploaded; + (match c.client_upload with +@@ -727,8 +748,23 @@ + else + buf_list buf buf_uid s.shared_uids; + if proto > 36 then +- buf_sub_files buf s.shared_sub_files +- ++ buf_sub_files proto buf s.shared_sub_files; ++ if proto > 40 then ++ buf_string buf (magic_string s.shared_magic) ++ ++let buf_stat_info proto buf n = ++ buf_string buf n.string_long; ++ buf_string buf n.string_short; ++ buf_int buf n.seen; ++ buf_int buf n.banned; ++ buf_int buf n.filerequest; ++ buf_int64 buf n.download; ++ buf_int64 buf n.upload ++ ++let buf_stat_info_list proto buf (s,i,l) = ++ buf_string buf s; ++ buf_int buf i; ++ buf_list2 proto buf buf_stat_info l + + (*************** + +@@ -1004,6 +1040,13 @@ + + | GiftServerAttach _ -> assert false + | GiftServerStats _ -> assert false ++ ++ | Stats (num, l) -> ++ let proto = proto.(59) in ++ buf_opcode buf 59; ++ buf_int buf num; ++ buf_list2 proto buf buf_stat_info_list l ++ + with e -> + lprintf "GuiEncoding.to_gui: Exception %s\n" + (Printexc2.to_string e) +@@ -1217,6 +1260,9 @@ + buf_opcode buf 67; + buf_int buf num; + buf_bool buf preferred ++ | GetStats n -> ++ buf_opcode buf 68; ++ buf_int buf n + + with e -> + lprintf "GuiEncoding.from_gui: Exception %s\n" +Index: src/daemon/common/guiProto.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/guiProto.ml,v +retrieving revision 1.24 +retrieving revision 1.25 +diff -u -r1.24 -r1.25 +--- src/daemon/common/guiProto.ml 16 Jan 2006 16:05:14 -0000 1.24 ++++ src/daemon/common/guiProto.ml 23 Sep 2006 20:29:46 -0000 1.25 +@@ -29,9 +29,9 @@ + + let gui_extension_poll = 1 + +-let to_gui_last_opcode = 58 +-let from_gui_last_opcode = 67 +-let best_gui_version = 40 ++let to_gui_last_opcode = 59 ++let from_gui_last_opcode = 68 ++let best_gui_version = 41 + + (* I will try to report all changes to the protocol here: send me patches + if I don't ! +@@ -166,6 +166,7 @@ + (* Understood by core protocol 32 *) + | ServerRename of (int * string) + | ServerSetPreferred of (int * bool) ++| GetStats of int + + type to_gui = + (* This message is the first message sent by the core *) +@@ -233,6 +234,7 @@ + + | GiftServerAttach of string * string + | GiftServerStats of (string * string * string * string) list ++| Stats of int * (string * int * network_stat_info list) list + + + let string_of_from_gui t = +@@ -311,6 +313,7 @@ + + | ServerRename _ -> "ServerRename" + | ServerSetPreferred _ -> "ServerSetPreferred" ++ | GetStats _ -> "GetStats" + + let string_of_to_gui t = + match t with +@@ -380,6 +383,7 @@ + + | GiftServerAttach _ -> "GiftServerAttach" + | GiftServerStats _ -> "GiftServerStats" ++ | Stats _ -> "Stats" + + type gui_record = { + mutable gui_num : int; +Index: src/daemon/common/guiTypes.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/common/guiTypes.ml,v +retrieving revision 1.29 +retrieving revision 1.34 +diff -u -r1.29 -r1.34 +--- src/daemon/common/guiTypes.ml 1 Sep 2006 16:22:14 -0000 1.29 ++++ src/daemon/common/guiTypes.ml 14 Nov 2006 18:42:59 -0000 1.34 +@@ -136,6 +136,11 @@ + mutable file_priority : int; + mutable file_uids : Uid.t list; + mutable file_sub_files : (string * int64 * string option) list; ++ mutable file_magic : string option; ++ mutable file_comments : (Ip.t * string * int * string) list; ++ mutable file_user : string; ++ mutable file_group : string; ++ mutable file_release : bool; + } + + type user_info = { +@@ -165,12 +170,15 @@ + mutable server_users : int list option; + mutable server_banner : string; + mutable server_preferred : bool; ++ mutable server_master : bool; + mutable server_version : string; + mutable server_max_users : int64; + mutable server_soft_limit : int64; + mutable server_hard_limit : int64; + mutable server_lowid_users : int64; + mutable server_ping : int; ++ mutable server_published_files : int; ++ mutable server_features : string option; + + } + +@@ -208,6 +216,7 @@ + mutable client_chat_port : int; + mutable client_connect_time : int; + mutable client_software : string; ++ mutable client_os : string option; + mutable client_release : string; + mutable client_emulemod : string; + mutable client_downloaded : int64; +@@ -240,9 +249,32 @@ + mutable shared_requests : int; + mutable shared_uids : Uid.t list; (* net file UID *) + mutable shared_sub_files : (string * int64 * string option) list; ++ mutable shared_magic : string option; + } + + ++let osinfo_short i = ++ match i with ++ Some s when ++ s = "linux" || ++ s = "netbsd" || ++ s = "macos" || ++ s = "freebsd" || ++ s = "windows" -> Some (String.sub s 0 1) ++ | _ -> i ++ ++let client_software_short software os = ++ software ^ ++ match osinfo_short os with ++ Some s -> "/" ^ s ++ | None -> "" ++ ++let client_software software os = ++ software ^ ++ match os with ++ Some s -> "/" ^ s ++ | None -> "" ++ + let add_file tree dirname r = + let path = Filename2.path_of_filename dirname in + +@@ -326,5 +358,10 @@ + file_priority = 0; + file_uids = []; + file_sub_files = []; ++ file_magic = Some ""; ++ file_comments = []; ++ file_user = ""; ++ file_group = ""; ++ file_release = false; + } + +Index: src/daemon/driver/driverCommands.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverCommands.ml,v +retrieving revision 1.175 +retrieving revision 1.199 +diff -u -r1.175 -r1.199 +--- src/daemon/driver/driverCommands.ml 16 Sep 2006 09:38:59 -0000 1.175 ++++ src/daemon/driver/driverCommands.ml 21 Nov 2006 22:34:33 -0000 1.199 +@@ -325,7 +325,7 @@ + ), ":\t\t\t\t\t$bclose telnet$n"; + + "kill", Arg_none (fun o -> +- if user2_is_admin o.conn_user.ui_user_name then ++ if user2_is_admin o.conn_user.ui_user then + begin + CommonInteractive.clean_exit 0; + _s "exit" +@@ -334,12 +334,18 @@ + _s "You are not allowed to kill MLDonkey" + ), ":\t\t\t\t\t$bsave and kill the server$n"; + +- "urladd", Arg_two (fun kind url o -> +- web_infos_add kind 1 url; ++ "urladd", Arg_multiple (fun args o -> ++ let (kind, url, period) = match args with ++ | [kind; url; period] -> kind, url, int_of_string period ++ | [kind; url] -> kind, url, 0 ++ | _ -> failwith "Bad number of arguments" ++ in ++ web_infos_add kind period url; + CommonWeb.load_url true kind url; + "url added to web_infos. downloading now" +- ), "<kind> <url> :\t\t\tload this file from the web\n" +- ^"\t\t\t\t\tkind is either server.met (if the downloaded file is a server.met)"; ++ ), "<kind> <url> [<period>]:\t\tload this file from the web\n" ++ ^"\t\t\t\t\tkind is either server.met (if the downloaded file is a server.met)\n" ++ ^"\t\t\t\t\tperiod is the period between updates (in hours, default 0 = only loaded at startup)"; + + "urlremove", Arg_one (fun url o -> + if web_infos_exists url then +@@ -400,7 +406,7 @@ + client_print c o; + if use_html_mods o then + html_mods_td buf ([ +- ("", "sr", i.client_software); ++ (client_software i.client_software i.client_os, "sr", client_software_short i.client_software i.client_os); + ("", "sr", i.client_release); + ] @ + (if !!emule_mods_count then [("", "sr", i.client_emulemod)] else [])); +@@ -736,6 +742,16 @@ + end + ) !!servers; + Printf.sprintf (_b "Removed %d blocked servers") !counter ++ | ["disc"] -> ++ Intmap.iter (fun _ s -> ++ match server_state s with ++ NotConnected _ -> ++ begin ++ server_remove s; ++ incr counter ++ end ++ | _ -> ()) !!servers; ++ Printf.sprintf (_b "Removed %d disconnected servers") !counter + | _ -> + List.iter (fun num -> + let num = int_of_string num in +@@ -743,7 +759,7 @@ + server_remove s + ) args; + Printf.sprintf (_b"%d servers removed") (List.length args) +- ), "<server numbers> :\t\t\tremove server (use 'all' for all servers, 'blocked' for all IP blocked servers)"; ++ ), "<server numbers|all|blocked|disc> :\tremove server(s) ('all'/'blocked'/'disc' = all/IP blocked/disconnected servers)"; + + "server_banner", Arg_one (fun num o -> + let num = int_of_string num in +@@ -754,6 +770,23 @@ + "" + ), "<num> :\t\t\tprint banner of connected server <num>"; + ++ "server_shares", Arg_one (fun num o -> ++ if user2_is_admin o.conn_user.ui_user then ++ let s = server_find (int_of_string num) in ++ (match server_state s with ++ Connected _ -> let list = ref [] in ++ List.iter (fun f -> ++ match file_shared f with ++ None -> () ++ | Some sh -> list := (as_shared_impl sh) :: !list) ++ (server_published s); ++ print_upstats o !list (Some s) ++ | _ -> () ++ ) ++ else print_command_result o o.conn_buf "You are not allowed to use this command"; ++ _s "" ++ ), "<num> :\t\t\tshow list of files published on server <num>"; ++ + "c", Arg_multiple (fun args o -> + let buf = o.conn_buf in + match args with +@@ -959,7 +992,7 @@ + let num = int_of_string num in + + if num > 0 then (* we want to disable upload for a short time *) +- let num = mini !CommonUploads.upload_credit num in ++ let num = min !CommonUploads.upload_credit num in + CommonUploads.has_upload := !CommonUploads.has_upload + num; + CommonUploads.upload_credit := !CommonUploads.upload_credit - num; + Printf.sprintf +@@ -970,7 +1003,7 @@ + if num < 0 && !CommonUploads.has_upload > 0 then + (* we want to restart upload probably *) + let num = - num in +- let num = mini num !CommonUploads.has_upload in ++ let num = min num !CommonUploads.has_upload in + CommonUploads.has_upload := !CommonUploads.has_upload - num; + CommonUploads.upload_credit := !CommonUploads.upload_credit + num; + Printf.sprintf +@@ -1014,13 +1047,31 @@ + Printf.bprintf buf "\\</tr\\>\\</table\\>\\</td\\>\\</tr\\>\\</table\\>\\</div\\>"; + + Printf.bprintf buf "\\<script type=\\\"text/javascript\\\"\\>window.parent.document.title='(D:%.1f) (U:%.1f) | %s | %s'\\</script\\>" +- dlkbs ulkbs o.conn_user.ui_user_name (CommonGlobals.version ()) ++ dlkbs ulkbs o.conn_user.ui_user.user_name (CommonGlobals.version ()) + end + else + DriverInteractive.print_bw_stats buf; + "" + ), ":\t\t\t\tprint current bandwidth stats"; + ++ "bw_toggle", Arg_none (fun o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then begin ++ let ul_bkp = !!max_hard_upload_rate_2 in ++ let dl_bkp = !!max_hard_download_rate_2 in ++ max_hard_upload_rate_2 =:= !!max_hard_upload_rate; ++ max_hard_download_rate_2 =:= !!max_hard_download_rate; ++ max_hard_upload_rate =:= ul_bkp; ++ max_hard_download_rate =:= dl_bkp; ++ print_command_result o buf (Printf.sprintf ++ "new upload rate: %d | new download rate: %d" ++ !!max_hard_upload_rate !!max_hard_download_rate) ++ end ++ else ++ print_command_result o buf "You are not allowed to toggle bandwidth"; ++ "" ++ ), ":\t\t\t\ttoggle between the two rate sets"; ++ + "stats", Arg_none (fun o -> + let buf = o.conn_buf in + CommonInteractive.network_display_stats buf o; +@@ -1053,16 +1104,43 @@ + "!", Arg_multiple (fun arg o -> + if !!allow_any_command then + match arg with +- c :: tail -> +- let args = String2.unsplit tail ' ' in ++ c :: args -> + let cmd = try List.assoc c !!allowed_commands with Not_found -> c in +- let tmp = Filename.temp_file "com" ".out" in +- let ret = Sys.command (Printf.sprintf "%s %s > %s" +- cmd args tmp) in +- let output = File.to_string tmp in +- Sys.remove tmp; +- Printf.sprintf (_b "%s\n---------------- Exited with code %d") output ret +- | _ -> _s "no command given" ++ (try ++ let pipe_out, pipe_in = Unix.pipe () in ++ let pid = Unix.create_process cmd ++ (Array.of_list (Filename2.basename c :: args)) ++ Unix.stdin pipe_in pipe_in in ++ Unix.close pipe_in; ++ (* can't close pipe_out in the already forked+executed process... *) ++ let output = Buffer.create 1024 in ++ let buffersize = 1024 in ++ let buffer = String.create buffersize in ++ (try ++ while true do ++ let nread = Unix.read pipe_out buffer 0 buffersize in ++ if nread = 0 then raise End_of_file; ++ Buffer.add_substring output buffer 0 nread ++ done ++ with ++ | End_of_file -> () ++ | Unix.Unix_error (code, f, arg) -> ++ lprintf_nl "%s failed%s: %s" f (if arg = "" then "" else " on " ^ arg) (Unix.error_message code)); ++ (try Unix.close pipe_out with _ -> ()); ++ let _pid, status = Unix.waitpid [] pid in ++ Printf.sprintf (_b "%s\n---------------- %s") ++ (Buffer.contents output) ++ (match status with ++ | Unix.WEXITED exitcode -> ++ Printf.sprintf "Exited with code %d" exitcode ++ | Unix.WSIGNALED signal -> ++ Printf.sprintf "Was killed by signal %d" signal ++ | Unix.WSTOPPED signal -> (* does it matter for us ? *) ++ Printf.sprintf "Was stopped by signal %d" signal) ++ ++ with Unix.Unix_error (code, f, arg) -> ++ Printf.sprintf "%s failed%s: %s" f (if arg = "" then "" else " on " ^ arg) (Unix.error_message code)) ++ | [] -> _s "no command given" + else + match arg with + [arg] -> +@@ -1100,17 +1178,44 @@ + ) , ":\t\t\t\tprint all networks"; + + "enable", Arg_one (fun num o -> +- let n = network_find_by_num (int_of_string num) in +- network_enable n; +- _s "network enabled" ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ let n = network_find_by_num (int_of_string num) in ++ network_enable n; ++ print_command_result o buf "network enabled" ++ end ++ else ++ print_command_result o buf "You are not allowed to enable networks"; ++ _s "" + ) , "<num> :\t\t\t\tenable a particular network"; + + "disable", Arg_one (fun num o -> +- let n = network_find_by_num (int_of_string num) in +- network_disable n; +- _s "network disabled" ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ let n = network_find_by_num (int_of_string num) in ++ network_disable n; ++ print_command_result o buf "network disabled" ++ end ++ else ++ print_command_result o buf "You are not allowed to disable networks"; ++ _s "" + ) , "<num> :\t\t\t\tdisable a particular network"; + ++ "porttest", Arg_none (fun o -> ++ let buf = o.conn_buf in ++ networks_iter (fun n -> ++ match network_porttest_result n with ++ PorttestNotAvailable -> () ++ | _ -> network_porttest_start n); ++ if o.conn_output = HTML then ++ Printf.bprintf buf "Click this \\<a href=\\\"porttest\\\"\\>link\\</a\\> to see results" ++ else ++ Printf.bprintf buf "Test started, you need a HTML browser to display results"; ++ "" ++ ) , ":\t\t\t\tstart network porttest"; ++ + ] + + (*************************************************************************) +@@ -1301,7 +1406,7 @@ + begin + let r = List.hd !forceable_download in + CommonNetwork.networks_iter (fun n -> +- ignore(n.op_network_download r)); ++ ignore (n.op_network_download r o.conn_user.ui_user)); + + let output = (if o.conn_output = HTML then begin + let buf = Buffer.create 100 in +@@ -1334,7 +1439,7 @@ + [ + + "set", Arg_two (fun name value o -> +- if user2_is_admin o.conn_user.ui_user_name then begin ++ if user2_is_admin o.conn_user.ui_user then begin + try + try + CommonInteractive.set_fully_qualified_options name value; +@@ -1371,7 +1476,7 @@ + if use_html_mods o then begin + + if !!html_mods_use_js_helptext then +- Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:FFFFDD;color:black;border-color:black;border-width:20;font-size:8pt; visibility:show; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; ++ Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; + + Printf.bprintf buf "\\<div class=\\\"friends\\\"\\>\\<table class=main cellspacing=0 cellpadding=0\\> + \\<tr\\>\\<td\\> +@@ -1386,7 +1491,7 @@ + \\</tr\\>\\</table\\> + \\</td\\>\\</tr\\> + \\<tr\\>\\<td\\>" +-(if (user2_is_admin o.conn_user.ui_user_name) then ++(if (user2_is_admin o.conn_user.ui_user) then + "\\<td nowrap title=\\\"Show users Tab where you can add/remove Users\\\" class=\\\"fbig fbigpad\\\"\\>\\<a onclick=\\\"javascript:window.location.href='submit?q=users'\\\"\\>Users\\</a\\>\\</td\\>" + else ""); + +@@ -1406,7 +1511,7 @@ + + Printf.bprintf buf "\\</td\\>\\</tr\\>\\<tr\\>\\<td\\>\\<table cellspacing=0 cellpadding=0 width=100%%\\>\\<tr\\>\\<td class=downloaded width=100%%\\>\\</td\\> + \\<td nowrap title=\\\"Toggle option helptext from javascript popup to html table\\\" class=\\\"fbig fbigb pr fbigpad\\\"\\> +-\\<a onclick=\\\"javascript: {parent.fstatus.location.href='submit?q=set+html_mods_use_js_helptext+%s'; setTimeout('window.location.replace(window.location.href)',1000);return true;}\\\"\\>Toggle js_helptext\\</a\\> ++\\<a onclick=\\\"javascript: {parent.fstatus.location.href='submit?q=set+html_mods_use_js_helptext+%s'; setTimeout('window.location.replace(window.location.href)',1000);return true;}\\\"\\>toggle js_helptext\\</a\\> + \\</td\\>\\</tr\\>\\</table\\>\\</td\\>\\</tr\\>\\</table\\>\\</div\\>\\</br\\>" (if !!html_mods_use_js_helptext then "false" else "true"); + + html_mods_table_one_row buf "downloaderTable" "downloaders" [ +@@ -1461,7 +1566,7 @@ + let mtabs = ref 1 in + + if !!html_mods_use_js_helptext then +- Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:FFFFDD;color:black;border-color:black;border-width:20;font-size:8pt; visibility:show; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; ++ Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; + + Printf.bprintf buf "\\<div class=\\\"vo\\\"\\>\\<table class=main cellspacing=0 cellpadding=0\\> + \\<tr\\>\\<td\\> +@@ -1531,12 +1636,13 @@ + strings_of_option global_login; + strings_of_option set_client_ip; + strings_of_option force_client_ip; +- strings_of_option run_as_user; +- strings_of_option run_as_useruid; + strings_of_option max_upload_slots; ++ strings_of_option max_release_slots; + strings_of_option dynamic_slots; + strings_of_option max_hard_upload_rate; + strings_of_option max_hard_download_rate; ++ strings_of_option max_hard_upload_rate_2; ++ strings_of_option max_hard_download_rate_2; + strings_of_option max_opened_connections; + strings_of_option max_indirect_connections; + strings_of_option max_connections_per_second; +@@ -1560,16 +1666,16 @@ + strings_of_option html_mods_use_relative_availability; + strings_of_option html_mods_human_readable; + strings_of_option html_mods_vd_network; +- strings_of_option html_mods_vd_comments; + strings_of_option html_mods_vd_active_sources; + strings_of_option html_mods_vd_age; ++ strings_of_option html_mods_vd_user; ++ strings_of_option html_mods_vd_group; + strings_of_option html_mods_vd_last; + strings_of_option html_mods_vd_prio; + strings_of_option html_mods_show_pending; + strings_of_option html_mods_load_message_file; + strings_of_option html_mods_max_messages; + strings_of_option html_mods_bw_refresh_delay; +- strings_of_option use_html_frames; + strings_of_option html_frame_border; + strings_of_option html_checkbox_vd_file_list; + strings_of_option html_checkbox_search_file_list; +@@ -1598,6 +1704,9 @@ + strings_of_option html_mods_vd_gfx_mean; + strings_of_option html_mods_vd_gfx_transparent; + strings_of_option html_mods_vd_gfx_h; ++ strings_of_option html_mods_vd_gfx_h_dynamic; ++ strings_of_option html_mods_vd_gfx_h_grid_time; ++ strings_of_option html_mods_vd_gfx_subgrid; + strings_of_option html_mods_vd_gfx_x_size; + strings_of_option html_mods_vd_gfx_y_size; + strings_of_option html_mods_vd_gfx_tag; +@@ -1702,8 +1811,10 @@ + | 8 -> + [ + strings_of_option term_ansi; +- strings_of_option enable_user_config; ++ strings_of_option run_as_user; ++ strings_of_option run_as_useruid; + strings_of_option messages_filter; ++ strings_of_option comments_filter; + strings_of_option max_displayed_results; + strings_of_option max_name_len; + strings_of_option max_filenames; +@@ -1740,7 +1851,7 @@ + \\<select id=\\\"modsStyle\\\" name=\\\"modsStyle\\\" + style=\\\"padding: 0px; font-size: 10px; font-family: verdana\\\" onchange=\\\"this.form.submit()\\\"\\> + \\<option value=\\\"0\\\"\\>style/theme\n" +-(if (user2_is_admin o.conn_user.ui_user_name) then ++(if (user2_is_admin o.conn_user.ui_user) then + "\\<td nowrap title=\\\"Show users Tab where you can add/remove Users\\\" class=\\\"fbig fbigb\\\"\\>\\<a onclick=\\\"javascript:window.location.href='submit?q=users'\\\"\\>Users\\</a\\>\\</td\\>" + else ""); + +@@ -1762,7 +1873,7 @@ + \\<td class=downloaded width=100%%\\>\\</td\\> + \\<td nowrap title=\\\"Change to simple Webinterface without html_mods\\\" class=\\\"fbig fbigb fbigpad\\\"\\>\\<a onclick=\\\"javascript:window.location.href='submit?q=html_mods'\\\"\\>toggle html_mods\\</a\\>\\</td\\> + \\<td nowrap title=\\\"Toggle option helptext from javascript popup to html table\\\" class=\\\"fbig fbigb pr fbigpad\\\"\\> +-\\<a onclick=\\\"javascript: {parent.fstatus.location.href='submit?q=set+html_mods_use_js_helptext+%s'; setTimeout('window.location.replace(window.location.href)',1000);return true;}\\\"\\>Toggle js_helptext\\</a\\> ++\\<a onclick=\\\"javascript: {parent.fstatus.location.href='submit?q=set+html_mods_use_js_helptext+%s'; setTimeout('window.location.replace(window.location.href)',1000);return true;}\\\"\\>toggle js_helptext\\</a\\> + \\</td\\>\\</tr\\>\\</table\\>\\</td\\>\\</tr\\>\\</table\\>\\</div\\>\\</br\\>" (if !!html_mods_use_js_helptext then "false" else "true"); + html_mods_table_one_row buf "downloaderTable" "downloaders" [ + ("", "srh", "!! press ENTER to send changes to core !!"); ]; +@@ -1782,7 +1893,7 @@ + \\<table cellspacing=0 cellpadding=0 width=100%%\\>\\<tr\\> + \\<td class=downloaded width=100%%\\>\\</td\\> + \\<td nowrap class=\\\"fbig pr\\\"\\>\\<a onclick=\\\"javascript: { +- var getdir = prompt('Input: <kind> <URL>','server.met URL') ++ var getdir = prompt('Input: <kind> <URL> [<period>]','server.met URL') + parent.fstatus.location.href='submit?q=urladd+' + encodeURIComponent(getdir); + setTimeout('window.location.reload()',1000); + }\\\"\\>Add URL\\</a\\> +@@ -1988,6 +2099,9 @@ + Printf.bprintf buf "st_uid %d\n" s.Unix.st_uid; + Printf.bprintf buf "st_gid %d\n" s.Unix.st_gid; + Printf.bprintf buf "st_size %d\n" s.Unix.st_size; ++ Printf.bprintf buf "st_atime %s\n" (Date.to_full_string s.Unix.st_atime); ++ Printf.bprintf buf "st_mtime %s\n" (Date.to_full_string s.Unix.st_mtime); ++ Printf.bprintf buf "st_ctime %s\n" (Date.to_full_string s.Unix.st_ctime); + let user,group = Unix32.owner arg in + Printf.bprintf buf "username %s\n" user; + Printf.bprintf buf "groupname %s\n" group; +@@ -2030,7 +2144,7 @@ + \\<table cellspacing=0 cellpadding=0 width=100%%\\>\\<tr\\> + \\<td class=downloaded width=100%%\\>\\</td\\> + \\<td nowrap class=\\\"fbig pr\\\"\\>\\<a onclick=\\\"javascript: { +- var getdir = prompt('Input: <priority#> <directory> (surround dir with quotes if necessary)','0 /home/mldonkey/share') ++ var getdir = prompt('Input: <priority#> <directory> [<strategy>] (surround dir with quotes if necessary)','0 /home/mldonkey/share') + parent.fstatus.location.href='submit?q=share+' + encodeURIComponent(getdir); + setTimeout('window.location.reload()',1000); + }\\\"\\>Add Share\\</a\\> +@@ -2049,12 +2163,10 @@ + ( "1", "srh ar", "% free", "% free" ) ; + ( "0", "srh", "Filesystem", "FS" ) ]; + +- let counter = ref 0 in +- ++ html_mods_cntr_init (); + List.iter (fun shared_dir -> + let dir = shared_dir.shdir_dirname in +- incr counter; +- Printf.bprintf buf "\\<tr class=\\\"%s\\\"\\> ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\> + \\<td title=\\\"Click to unshare this directory\\\" + onMouseOver=\\\"mOvr(this);\\\" + onMouseOut=\\\"mOut(this);\\\" +@@ -2069,7 +2181,7 @@ + \\<td class=\\\"sr ar\\\"\\>%s\\</td\\> + \\<td class=\\\"sr ar\\\"\\>%s\\</td\\> + \\<td class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>" +- (if !counter mod 2 == 0 then "dl-1" else "dl-2") ++ (html_mods_cntr ()) + (Url.encode dir) + shared_dir.shdir_priority + dir +@@ -2088,7 +2200,38 @@ + !!shared_directories; + + Printf.bprintf buf "\\</table\\>\\</td\\>\\<tr\\>\\</table\\>\\</div\\>\\<P\\>"; +- print_option_help o shared_directories ++ print_option_help o shared_directories; ++ Printf.bprintf buf "\\<P\\>"; ++ ++ html_mods_big_header_start buf "sharesTable" ["Share strategies"]; ++ html_mods_table_header buf "sharesTable" "shares" [ ++ ( "0", "srh", "Name", "Name" ) ; ++ ( "0", "srh", "Incoming", "Incoming" ) ; ++ ( "0", "srh", "Directories", "Directories" ) ; ++ ( "0", "srh", "Recursive", "Recursive" ) ; ++ ( "0", "srh", "Minsize", "Minsize" ) ; ++ ( "0", "srh", "Maxsize", "Maxsize" ) ; ++ ( "0", "srh", "Extensions", "Extensions" ) ]; ++ ++ html_mods_cntr_init (); ++ ++ let int64_print v = ++ if v = Int64.max_int then "unlimited" else Int64ops.int64_to_human_readable v in ++ ++ List.iter (fun (s,t) -> ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ html_mods_td buf [ ++ ("", "sr", s); ++ ("", "sr", string_of_bool t.sharing_incoming); ++ ("", "sr", string_of_bool t.sharing_directories); ++ ("", "sr", string_of_bool t.sharing_recursive); ++ ("", "sr", (int64_print t.sharing_minsize)); ++ ("", "sr", (int64_print t.sharing_maxsize)); ++ ("", "sr", (String.concat " " t.sharing_extensions)); ++ ]; ++ Printf.bprintf buf "\\</tr\\>\n" ++ ) !!sharing_strategies; ++ + end + else + begin +@@ -2119,23 +2262,29 @@ + } in + + if Unix2.is_directory arg then +- if not (List.mem shdir !!shared_directories) then begin ++ begin ++ try ++ let d = List.find (fun d -> d.shdir_dirname = arg) !!shared_directories in ++ let old_prio = d.shdir_priority in ++ d.shdir_priority <- prio; ++ Printf.sprintf "prio of %s changed from %d to %d" ++ d.shdir_dirname old_prio d.shdir_priority ++ with Not_found -> + shared_directories =:= shdir :: !!shared_directories; + shared_add_directory shdir; +- "directory added" +- end (* else +- if not (List.mem (arg, prio) !!shared_directories) then begin +- shared_directories =:= (arg, prio) :: List.remove_assoc arg !!shared_directories; +- shared_add_directory (arg, prio); +- "prio changed" +- end *) else +- "directory already shared" ++ Printf.sprintf "directory %s added%s" ++ shdir.shdir_dirname ++ (if shdir.shdir_priority <> 0 then ++ Printf.sprintf " with prio %d" shdir.shdir_priority ++ else "") ++ end + else + "no such directory" + ), "<priority> <dir> [<strategy>] :\tshare directory <dir> with <priority> [and sharing strategy <strategy>]"; + + "unshare", Arg_one (fun arg o -> + ++ if user2_is_admin o.conn_user.ui_user then begin + let found = ref false in + shared_directories =:= List.filter (fun sd -> + let diff = sd.shdir_dirname <> arg in +@@ -2154,118 +2303,33 @@ + _s "directory removed" + end else + _s "directory already unshared" +- +- ), "<dir> :\t\t\t\tshare directory <dir>"; ++ end ++ else ++ _s "You are not allowed to unshare directories" ++ ), "<dir> :\t\t\t\tunshare directory <dir>"; + + "upstats", Arg_none (fun o -> + let buf = o.conn_buf in +- +- if use_html_mods o then begin +- +-if !!html_mods_use_js_tooltips then Printf.bprintf buf +-"\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:FFFFDD;color:black;border-color:black;border-width:20;font-size:8pt; visibility:show; left:25px; top: +--100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; +- +- Printf.bprintf buf "\\<div class=\\\"upstats\\\"\\>"; +- html_mods_table_one_row buf "upstatsTable" "upstats" [ +- ("", "srh", Printf.sprintf "Session: %s uploaded | Shared(%d): %s\n" +- (size_of_int64 !upload_counter) !nshared_files (size_of_int64 !nshared_bytes)); ] +- end +- else begin +- Printf.bprintf buf "Upload statistics:\n"; +- Printf.bprintf buf "Session: %s uploaded | Shared(%d): %s\n" +- (size_of_int64 !upload_counter) !nshared_files (size_of_int64 !nshared_bytes) +- end; +- let list = ref [] in +- shared_iter (fun s -> +- let impl = as_shared_impl s in +- list := impl :: !list +- ); +- +- if use_html_mods o then +- html_mods_table_header buf "upstatsTable" "upstats" [ +- ( "1", "srh", "Total file requests", "Reqs" ) ; +- ( "1", "srh", "Total bytes sent", "Total" ) ; +- ( "1", "srh", "Upload Ratio", "UPRatio" ) ; +- ( "0", "srh", "Preview", "P" ) ; +- ( "0", "srh", "Filename", "Filename" ); +- ( "0", "srh", "Statistic links", "Stats" ) ] ++ if not (user2_can_view_uploads o.conn_user.ui_user) then ++ print_command_result o buf "You are not allowed to see upload statistics" + else +- begin +- Printf.bprintf buf " Requests | Bytes | Uploaded | File\n"; +- Printf.bprintf buf "----------+----------+----------+----------------------------------------------------\n"; +- end; +- +- let counter = ref 0 in +- +- let list = Sort.list (fun f1 f2 -> +- (f1.impl_shared_requests = f2.impl_shared_requests && +- f1.impl_shared_uploaded > f2.impl_shared_uploaded) || +- (f1.impl_shared_requests > f2.impl_shared_requests ) +- ) !list in +- +- List.iter (fun impl -> +- if use_html_mods o then +- begin +- incr counter; +- +- let ed2k = file_print_ed2k_link +- (Filename.basename impl.impl_shared_codedname) +- impl.impl_shared_size impl.impl_shared_id in +- +- Printf.bprintf buf "\\<tr class=\\\"%s\\\"" +- (if (!counter mod 2 == 0) then "dl-1" else "dl-2";); +- +- (if !!html_mods_use_js_tooltips then +- Printf.bprintf buf " onMouseOver=\\\"mOvr(this);setTimeout('popLayer(\\\\\'%s<br>%s\\\\\')',%d);setTimeout('hideLayer()',%d);return true;\\\" onMouseOut=\\\"mOut(this);hideLayer();setTimeout('hideLayer()',%d)\\\"\\>" +- (Http_server.html_real_escaped (Filename.basename impl.impl_shared_codedname)) +- (match impl.impl_shared_magic with +- None -> "" +- | Some magic -> "File type: " ^ (Http_server.html_real_escaped magic) ^ "<br>") +- !!html_mods_js_tooltips_wait +- !!html_mods_js_tooltips_timeout +- !!html_mods_js_tooltips_wait +- else Printf.bprintf buf " onMouseOver=\\\"mOvr(this);return true;\\\" onMouseOut=\\\"mOut(this);\\\"\\>"); +- +- let uploaded = Int64.to_float impl.impl_shared_uploaded in +- let size = Int64.to_float impl.impl_shared_size in +- +- html_mods_td buf [ +- ("", "sr ar", Printf.sprintf "%d" impl.impl_shared_requests); +- ("", "sr ar", size_of_int64 impl.impl_shared_uploaded); +- ("", "sr ar", Printf.sprintf "%5.1f" ( if size < 1.0 then 0.0 else (uploaded *. 100.) /. size)); +- ("", "sr", Printf.sprintf "\\<a href=\\\"preview_upload?q=%d\\\"\\>P\\</a\\>" +- impl.impl_shared_num); +- ("", "sr", (if impl.impl_shared_id = Md4.null then +- (Filename.basename impl.impl_shared_codedname) +- else +- Printf.sprintf "\\<a href=\\\"%s\\\"\\>%s\\</a\\>" +- ed2k (shorten (Filename.basename impl.impl_shared_codedname) !!max_name_len))); +- ("", "sr", (if impl.impl_shared_id = Md4.null then "" else +- Printf.sprintf "\\<a href=\\\"http://tothbenedek.hu/ed2kstats/ed2k?hash=%s\\\"\\>%s\\</a\\> +-\\<a href=\\\"http://ed2k.titanesel.ws/ed2k.php?hash=%s\\\"\\>%s\\</a\\> +-\\<a href=\\\"http://bitzi.com/lookup/ed2k:%s\\\"\\>%s\\</a\\>" +- (Md4.to_string impl.impl_shared_id) "T1" +- (Md4.to_string impl.impl_shared_id) "T2" +- (Md4.to_string impl.impl_shared_id) "B")) ]; +- Printf.bprintf buf "\\</tr\\>\n"; +- end +- else +- Printf.bprintf buf "%9d | %8s | %7s%% | %-50s\n" +- (impl.impl_shared_requests) +- (size_of_int64 impl.impl_shared_uploaded) +- (Printf.sprintf "%3.1f" ((Int64.to_float impl.impl_shared_uploaded *. 100.) /. Int64.to_float impl.impl_shared_size)) +- (shorten (Filename.basename impl.impl_shared_codedname) !!max_name_len); +- ) list; +- +- if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>\\</div\\>"; +- +- ++ begin ++ let list = ref [] in ++ shared_iter (fun s -> ++ let impl = as_shared_impl s in ++ list := impl :: !list ++ ); ++ print_upstats o !list None; ++ end; + _s "" + ), ":\t\t\t\tstatistics on upload"; + + "links", Arg_none (fun o -> + let buf = o.conn_buf in ++ if not (user2_can_view_uploads o.conn_user.ui_user) then ++ print_command_result o o.conn_buf "You are not allowed to see shared files list" ++ else begin ++ + let list = ref [] in + shared_iter (fun s -> + let impl = as_shared_impl s in +@@ -2285,28 +2349,26 @@ + (Filename.basename impl.impl_shared_codedname) + impl.impl_shared_size impl.impl_shared_id); + ) list; ++ end; + "Done" + ), ":\t\t\t\t\tlist links of shared files"; + + "uploaders", Arg_none (fun o -> + let buf = o.conn_buf in + +- let nuploaders = Intmap.length !uploaders in ++ if not (user2_can_view_uploads o.conn_user.ui_user) then ++ print_command_result o buf "You are not allowed to see uploaders list" ++ else begin + ++ let nuploaders = Intmap.length !uploaders in + if use_html_mods o then +- + begin +- + let counter = ref 0 in + Printf.bprintf buf "\\<div class=\\\"uploaders\\\"\\>"; + html_mods_table_one_row buf "uploadersTable" "uploaders" [ + ("", "srh", Printf.sprintf "Total upload slots: %d (%d) | Pending slots: %d\n" nuploaders + (Fifo.length CommonUploads.upload_clients) + (Intmap.length !CommonUploads.pending_slots_map)); ]; +-(* Printf.bprintf buf "\\<div class=\\\"uploaders\\\"\\>Total upload slots: %d (%d) | Pending slots: %d\n" nuploaders +- (Fifo.length CommonUploads.upload_clients) +- (Intmap.length !CommonUploads.pending_slots_map); +- *) + if nuploaders > 0 then + + begin +@@ -2327,6 +2389,7 @@ + @ [ + ( "0", "srh ar", "Total DL bytes from this client for all files", "DL" ) ; + ( "0", "srh ar", "Total UL bytes to this client for all files", "UL" ) ; ++ ( "0", "srh ar", "Slot kind", "Slot" ) ; + ( "0", "srh", "Filename", "Filename" ) ]); + + List.iter (fun c -> +@@ -2359,15 +2422,22 @@ + ("", "sr", ips); + ] @ (if !Geoip.active then [(cn, "sr", cc)] else []) @ [ + ("", "sr", Printf.sprintf "%d" (((last_time ()) - i.client_connect_time) / 60)); +- ("", "sr", i.client_software); ++ (client_software i.client_software i.client_os, "sr", client_software_short i.client_software i.client_os); + ("", "sr", i.client_release); + ] @ + (if !!emule_mods_count then [("", "sr", i.client_emulemod)] else []) + @ [ + ("", "sr ar", size_of_int64 i.client_downloaded); + ("", "sr ar", size_of_int64 i.client_uploaded); ++ (let text1, text2 = ++ match client_slot c with ++ | FriendSlot -> "Friend", "F" ++ | ReleaseSlot -> "Release", "R" ++ | SmallFileSlot -> "Small file", "S" ++ | PrioSlot dir -> "Prio dir: " ^ dir, "P" ++ | _ -> "", "" in text1, "sr ar", text2); + ("", "sr", (match i.client_upload with +- Some cu -> cu ++ Some f -> shorten f !!max_name_len + | None -> "") ) ]); + + Printf.bprintf buf "\\</tr\\>" +@@ -2388,6 +2458,9 @@ + ( "0", "srh", "Network", "Network" ) ; + ( "0", "srh", "Connection type [I]ndirect [D]irect", "C" ) ; + ( "0", "srh", "Client name", "Client name" ) ; ++ ( "0", "srh", "Secure User Identification [N]one, [P]assed, [F]ailed", "S" ) ; ++ ( "0", "srh", "IP address", "IP address" ) ; ++ ] @ (if !Geoip.active then [( "0", "srh", "Country Code/Name", "CC" )] else []) @ [ + ( "0", "srh", "Client brand", "CB" ) ; + ( "0", "srh", "Client release", "CR" ) ; + ] @ +@@ -2395,12 +2468,13 @@ + @ [ + ( "0", "srh ar", "Total DL bytes from this client for all files", "DL" ) ; + ( "0", "srh ar", "Total UL bytes to this client for all files", "UL" ) ; +- ( "0", "srh", "IP address", "IP address" ) ]); ++ ( "0", "srh", "Filename", "Filename" ) ]); + + Intmap.iter (fun cnum c -> + + try + let i = client_info c in ++ let ips,cc,cn = string_of_kind_geo i.client_kind in + incr counter; + + Printf.bprintf buf "\\<tr class=\\\"%s\\\" +@@ -2414,33 +2488,36 @@ + client_print_html c o; + + html_mods_td buf ([ +- ("", "sr", i.client_software); ++ ("", "sr", (match i.client_sui_verified with ++ | None -> "N" ++ | Some b -> if b then "P" else "F" ++ )); ++ ("", "sr", ips); ++ ] @ (if !Geoip.active then [(cn, "sr", cc)] else []) @ [ ++ (client_software i.client_software i.client_os, "sr", client_software_short i.client_software i.client_os); + ("", "sr", i.client_release); + ] @ + (if !!emule_mods_count then [("", "sr", i.client_emulemod )] else []) + @ [ + ("", "sr ar", size_of_int64 i.client_downloaded); + ("", "sr ar", size_of_int64 i.client_uploaded); +- ("", "sr", string_of_kind i.client_kind); ]); ++ ("", "sr", (match i.client_upload with ++ Some f -> shorten f !!max_name_len ++ | None -> "") ) ]); + + Printf.bprintf buf "\\</tr\\>"; + with _ -> (); + + ) !CommonUploads.pending_slots_map; + Printf.bprintf buf "\\</table\\>\\</div\\>"; +- + end; +- +- Printf.bprintf buf "\\</div\\>"; +- "" ++ Printf.bprintf buf "\\</div\\>" + end + else + begin +- + Intmap.iter (fun _ c -> + try + let i = client_info c in +- + client_print c o; + Printf.bprintf buf "client: %s downloaded: %s uploaded: %s\n" i.client_software (Int64.to_string i.client_downloaded) (Int64.to_string i.client_uploaded); + match i.client_upload with +@@ -2451,14 +2528,12 @@ + Printf.bprintf buf "no info on client %d\n" (client_num c ) + ) !uploaders; + +- Printf.sprintf "Total upload slots: %d (%d) | Pending slots: %d\n" nuploaders ++ Printf.bprintf buf "Total upload slots: %d (%d) | Pending slots: %d\n" nuploaders + (Fifo.length CommonUploads.upload_clients) + (Intmap.length !CommonUploads.pending_slots_map); +- +- + end +- +- ++ end; ++ "" + ), ":\t\t\t\tshow users currently uploading"; + + +@@ -2504,7 +2579,7 @@ + "yes" | "y" | "true" -> + List.iter (fun file -> + try +- file_cancel file ++ file_cancel file o.conn_user.ui_user + with e -> + lprintf "Exception %s in cancel file %d\n" + (Printexc2.to_string e) (file_num file) +@@ -2541,7 +2616,7 @@ + if not (List.memq num !to_cancel) then + to_cancel := num :: !to_cancel + in +- if args = ["all"] then ++ if args = ["all"] && user2_is_admin o.conn_user.ui_user then + List.iter (fun file -> + file_cancel file + ) !!files +@@ -2585,7 +2660,7 @@ + List.iter + (fun file -> + if (CommonFile.file_downloaders file o !counter) then counter := 0 else counter := 1; +- ) !!files; ++ ) (user2_filter_files !!files o.conn_user.ui_user); + + if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>"; + +@@ -2611,35 +2686,46 @@ + ), "<num> :\t\t\tverify chunks of file <num>"; + + "pause", Arg_multiple (fun args o -> +- if args = ["all"] then ++ if args = ["all"] && user2_is_admin o.conn_user.ui_user then + List.iter (fun file -> +- file_pause file; ++ file_pause file admin_user; + ) !!files + else + List.iter (fun num -> + let num = int_of_string num in + List.iter (fun file -> +- if (as_file_impl file).impl_file_num = num then begin +- file_pause file +- end ++ if (as_file_impl file).impl_file_num = num then ++ file_pause file o.conn_user.ui_user + ) !!files) args; "" + ), "<num> :\t\t\t\tpause a download (use arg 'all' for all files)"; + + "resume", Arg_multiple (fun args o -> +- if args = ["all"] then ++ if args = ["all"] && user2_is_admin o.conn_user.ui_user then + List.iter (fun file -> +- file_resume file ++ file_resume file admin_user + ) !!files + else + List.iter (fun num -> + let num = int_of_string num in + List.iter (fun file -> +- if (as_file_impl file).impl_file_num = num then begin +- file_resume file +- end ++ if (as_file_impl file).impl_file_num = num then ++ file_resume file o.conn_user.ui_user + ) !!files) args; "" + ), "<num> :\t\t\t\tresume a paused download (use arg 'all' for all files)"; + ++ "release", Arg_one (fun arg o -> ++ let num = int_of_string arg in ++ let file = file_find num in ++ let old_state = file_release file in ++ set_file_release file (not (file_release file)) o.conn_user.ui_user; ++ Printf.sprintf "%s, file: %s" ++ (match old_state, file_release file with ++ true, false -> "deactivated release state" ++ | false, true -> "activated release state" ++ | _ -> "unchanged status, enough rights?") ++ (shorten (file_best_name file) !!max_name_len) ++ ), "<num> :\t\t\t\tchange release state of a download"; ++ + "commit", Arg_none (fun o -> + List.iter (fun file -> + file_commit file +@@ -2655,20 +2741,19 @@ + + "vd", Arg_multiple (fun args o -> + let buf = o.conn_buf in ++ let list = user2_filter_files !!files o.conn_user.ui_user in ++ let filelist = List2.tail_map file_info list in + match args with + | ["queued"] -> +- let list = List2.tail_map file_info !!files in +- let list = List.filter ( fun f -> f.file_state = FileQueued ) list in ++ let list = List.filter ( fun f -> f.file_state = FileQueued ) filelist in + DriverInteractive.display_active_file_list buf o list; + "" + | ["paused"] -> +- let list = List2.tail_map file_info !!files in +- let list = List.filter ( fun f -> f.file_state = FilePaused ) list in ++ let list = List.filter ( fun f -> f.file_state = FilePaused ) filelist in + DriverInteractive.display_active_file_list buf o list; + "" + | ["downloading"] -> +- let list = List2.tail_map file_info !!files in +- let list = List.filter ( fun f -> f.file_state = FileDownloading ) list in ++ let list = List.filter ( fun f -> f.file_state = FileDownloading ) filelist in + DriverInteractive.display_file_list buf o list; + "" + | [arg] -> +@@ -2696,15 +2781,14 @@ + List.iter + (fun file -> if (as_file_impl file).impl_file_num = num then + CommonFile.file_print file o) +- !!files; ++ list; + List.iter + (fun file -> if (as_file_impl file).impl_file_num = num then + CommonFile.file_print file o) + !!done_files; + "" + | _ -> +- let list = List2.tail_map file_info !!files in +- DriverInteractive.display_file_list buf o list; ++ DriverInteractive.display_file_list buf o filelist; + "" + ), "[<num>|queued|paused|downloading] :\t$bview file info for download <num>, or lists of queued, paused or downloading files, or all downloads if no argument given$n"; + +@@ -2726,14 +2810,15 @@ + ), "<num> \"<new name>\" :\t\tchange name of download <num> to <new name>"; + + "filenames_variability", Arg_none (fun o -> +- let list = List2.tail_map file_info !!files in ++ let list = List2.tail_map file_info ++ (user2_filter_files !!files o.conn_user.ui_user) in + DriverInteractive.filenames_variability o list; + _s "done" + ), ":\t\t\ttell which files have several very different names"; + + "dllink", Arg_multiple (fun args o -> + let url = String2.unsplit args ' ' in +- dllink_parse (o.conn_output = HTML) url ++ dllink_parse (o.conn_output = HTML) url o.conn_user.ui_user + ), "<link> :\t\t\t\tdownload ed2k, sig2dat, torrent or other link"; + + "dllinks", Arg_one (fun arg o -> +@@ -2741,7 +2826,7 @@ + let file = File.to_string arg in + let lines = String2.split_simplify file '\n' in + List.iter (fun line -> +- Buffer.add_string result (dllink_parse (o.conn_output = HTML) line); ++ Buffer.add_string result (dllink_parse (o.conn_output = HTML) line o.conn_user.ui_user); + Buffer.add_string result (if o.conn_output = HTML then "\\<P\\>" else "\n") + ) lines; + (Buffer.contents result) +@@ -2758,67 +2843,272 @@ + let _ = + register_commands "Driver/Users" [ + +- "useradd", Arg_multiple (fun args o -> ++ "useradd", Arg_two (fun user pass o -> + let buf = o.conn_buf in +- let print_result o result = +- if o.conn_output = HTML then +- html_mods_table_one_row buf "serversTable" "servers" [ +- ("", "srh", result); ] +- else +- Printf.bprintf buf "%s" result +- in +- let add_new_user user pass mail = +- if o.conn_user == default_user +- || o.conn_user == (find_ui_user user) then +- try +- ignore (user2_find user); +- ignore (user2_add user (Md4.string pass) ""); +- print_result o (Printf.sprintf "Password of user %s changed" user) +- with _ -> +- ignore (user2_add user (Md4.string pass) ""); +- print_result o (Printf.sprintf "User %s added" user) ++ if user2_is_admin o.conn_user.ui_user ++ || o.conn_user.ui_user.user_name = user then ++ if user2_user_exists user then ++ begin ++ user2_user_set_password (user2_user_find user) pass; ++ print_command_result o buf (Printf.sprintf "Password of user %s changed" user) ++ end ++ else ++ begin ++ user2_user_add user (Md4.string pass) (); ++ print_command_result o buf (Printf.sprintf "User %s added" user) ++ end + else +- print_result o "Only 'admin' is allowed to add users" +- in begin +- match args with +- user :: pass :: mail :: _ -> +- add_new_user user pass mail +- | user :: pass :: _ -> +- add_new_user user pass ""; +- | _ -> print_result o "Wrong syntax: use 'useradd user pass <mail>'" +- end; ++ print_command_result o buf "You are not allowed to add users"; + _s "" +- ), "<user> <passwd> [<mail>] :\tadd new mldonkey user/change user password"; ++ ), "<user> <passwd> :\t\tadd new mldonkey user/change user password"; + + "userdel", Arg_one (fun user o -> + let buf = o.conn_buf in +- let print_result o result = +- if o.conn_output = HTML then +- html_mods_table_one_row buf "serversTable" "servers" [ +- ("", "srh", result); ] ++ if user <> o.conn_user.ui_user.user_name then ++ if user2_is_admin o.conn_user.ui_user then ++ if user = admin_user.user_name then ++ print_command_result o buf "User 'admin' can not be removed" ++ else ++ try ++ let u = user2_user_find user in ++ let n = user2_num_user_dls u in ++ if n <> 0 then print_command_result o buf ++ (Printf.sprintf "User %s has %d downloads, can not delete" user n) ++ else ++ user2_user_remove user; ++ print_command_result o buf (Printf.sprintf "User %s removed" user) ++ with ++ Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) + else +- Printf.bprintf buf "%s" result ++ print_command_result o buf "You are not allowed to remove users" ++ else ++ print_command_result o buf "You can not remove yourself"; ++ _s "" ++ ), "<user> :\t\t\tremove a mldonkey user"; ++ ++ "usergroupadd", Arg_two (fun user group o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ try ++ let u = user2_user_find user in ++ begin ++ try ++ let g = user2_group_find group in ++ user2_user_add_group u g; ++ print_command_result o buf (Printf.sprintf "Added group %s to user %s" g.group_name u.user_name) ++ with Not_found -> print_command_result o buf (Printf.sprintf "Group %s does not exist" group) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ else ++ print_command_result o buf "You are not allowed to add groups to a user"; ++ _s "" ++ ), "<user> <group> :\t\tadd a group to a mldonkey user"; ++ ++ "usergroupdel", Arg_two (fun user group o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user ++ || o.conn_user.ui_user.user_name = user then ++ begin ++ try ++ let u = user2_user_find user in ++ begin ++ try ++ let g = user2_group_find group in ++ if not (List.mem g u.user_groups) then ++ print_command_result o buf (Printf.sprintf "User %s is not member of group %s" user group) ++ else ++ if Some g = u.user_default_group then ++ print_command_result o buf (Printf.sprintf "Group %s is default group of user %s, can not remove. Use command userdgroup to change default_group." group user) ++ else ++ begin ++ let counter = ref 0 in ++ List.iter (fun f -> ++ if file_owner f = u && file_group f = Some g then ++ begin ++ incr counter; ++ set_file_group f u.user_default_group ++ end ++ ) !!files; ++ user2_user_remove_group (user2_user_find user) (user2_group_find group); ++ print_command_result o buf (Printf.sprintf "Removed group %s from user %s%s" ++ group user ++ (if !counter = 0 then "" else Printf.sprintf ", changed file_group of %d file%s to default_group %s" ++ !counter (Printf2.print_plural_s !counter) (user2_print_group u.user_default_group))) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "Group %s does not exist" group) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ ++ else ++ print_command_result o buf "You are not allowed to remove groups from a user"; ++ _s "" ++ ), "<user> <group> :\t\tremove a group from a mldonkey user"; ++ ++ "userdgroup", Arg_two (fun user group o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user ++ || o.conn_user.ui_user.user_name = user then ++ begin ++ try ++ let u = user2_user_find user in ++ begin ++ try ++ let g = if String.lowercase group = "none" then None else Some (user2_group_find group) in ++ let update_dgroup () = ++ match g with ++ None -> true ++ | Some g1 when List.mem g1 u.user_groups -> true ++ | _ -> false ++ in ++ if update_dgroup () then ++ begin ++ user2_user_set_default_group u g; ++ print_command_result o buf (Printf.sprintf "Changed default group of user %s to group %s" u.user_name (user2_print_user_default_group u)) ++ end ++ else print_command_result o buf (Printf.sprintf "User %s is not member of group %s" u.user_name group) ++ with Not_found -> print_command_result o buf (Printf.sprintf "Group %s does not exist" group) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ else ++ print_command_result o buf "You are not allowed to change default group"; ++ _s "" ++ ), "<user> <group|None> :\tchange user default group"; ++ ++ "passwd", Arg_one (fun passwd o -> ++ let buf = o.conn_buf in ++ begin ++ try ++ let u = user2_user_find o.conn_user.ui_user.user_name in ++ user2_user_set_password u passwd; ++ print_command_result o buf (Printf.sprintf "Password of user %s changed" u.user_name) ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" o.conn_user.ui_user.user_name) ++ end; ++ _s "" ++ ), "<passwd> :\t\t\tchange own password"; ++ ++ "usermail", Arg_two (fun user mail o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user ++ || o.conn_user.ui_user.user_name = user then ++ begin ++ try ++ let u = user2_user_find user in ++ user2_user_set_mail u mail; ++ print_command_result o buf (Printf.sprintf "User %s has new mail %s" user mail) ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ else print_command_result o buf "You are not allowed to change mail addresses"; ++ _s "" ++ ), "<user> <mail> :\t\tchange user mail address"; ++ ++ "userdls", Arg_two (fun user dls o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ try ++ let u = user2_user_find user in ++ user2_user_set_dls u (int_of_string dls); ++ print_command_result o buf (Printf.sprintf "User %s has now %s downloads allowed" user (user2_print_user_dls (user2_user_find user))) ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ else print_command_result o buf "You are not allowed to change this value"; ++ _s "" ++ ), "<user> <num> :\t\t\tchange number of allowed concurrent downloads"; ++ ++ "usercommit", Arg_two (fun user dir o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user ++ || o.conn_user.ui_user.user_name = user then ++ begin ++ try ++ let u = user2_user_find user in ++ user2_user_set_commit_dir u dir; ++ print_command_result o buf (Printf.sprintf "User %s has new commit dir %s" u.user_name u.user_commit_dir) ++ with Not_found -> print_command_result o buf (Printf.sprintf "User %s does not exist" user) ++ end ++ else print_command_result o buf "You are not allowed to change this value"; ++ _s "" ++ ), "<user> <dir> :\t\tchange user specific commit directory"; ++ ++ "groupadd", Arg_two (fun group admin o -> ++ let buf = o.conn_buf in ++ let g_admin = ++ try ++ bool_of_string admin ++ with _ -> false + in +- if o.conn_user == default_user then +- if user = admin_user then +- print_result o "User 'admin' can not be removed" ++ if user2_is_admin o.conn_user.ui_user then ++ if user2_group_exists group then ++ print_command_result o buf (Printf.sprintf "Group %s already exists" group) + else +- try +- ignore (user2_find user); +- ignore (user2_remove user); +- print_result o (Printf.sprintf "User %s removed" user) +- with _ -> +- print_result o (Printf.sprintf "User %s not found" user) ++ begin ++ user2_group_add group g_admin; ++ print_command_result o buf (Printf.sprintf "Group %s added" group) ++ end + else +- print_result o "Only 'admin' is allowed to remove users"; ++ print_command_result o buf "You are not allowed to add a group"; + _s "" +- ), "<user> :\t\t\tremove a mldonkey user"; ++ ), "<group> <admin: true | false>: add new mldonkey group"; + ++ "groupdel", Arg_one (fun group o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ try ++ let g = user2_group_find group in ++ let g_dls = user2_num_group_dls g in ++ let g_mem = user2_num_group_members g in ++ if g_dls <> 0 then ++ print_command_result o buf ++ (Printf.sprintf "Can not remove group %s, it has %d download%s" ++ group g_dls (Printf2.print_plural_s g_dls)) ++ else ++ if g_mem <> 0 then ++ print_command_result o buf ++ (Printf.sprintf "Can not remove group %s, it has %d member%s" ++ group g_mem (Printf2.print_plural_s g_mem)) ++ else ++ if g.group_name = system_user_default_group.group_name then ++ print_command_result o buf (Printf.sprintf "Can not remove system group %s" group) ++ else ++ begin ++ user2_group_remove g; ++ print_command_result o buf (Printf.sprintf "Removed group %s" group) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "Group %s does not exist" group) ++ end ++ else ++ print_command_result o buf "You are not allowed to remove users"; ++ _s "" ++ ), "<group> :\t\t\tremove an unused mldonkey group"; + +- "users", Arg_none (fun o -> +- if o.conn_user == default_user then ++ "groupadmin", Arg_two (fun group admin o -> ++ let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then ++ begin ++ try ++ let g = user2_group_find group in ++ if g.group_name = system_user_default_group.group_name then ++ print_command_result o buf (Printf.sprintf "Can not change state of system group %s" group) ++ else ++ begin ++ user2_group_admin g (bool_of_string admin); ++ print_command_result o buf (Printf.sprintf "Changed admin status of group %s to %b" g.group_name g.group_admin) ++ end ++ with Not_found -> print_command_result o buf (Printf.sprintf "Group %s does not exist" group) ++ end ++ else ++ print_command_result o buf "You are not allowed to change group admin status"; ++ _s "" ++ ), "<group> <true|false> :\tchange group admin status"; + ++ "users", Arg_none (fun o -> + let buf = o.conn_buf in ++ if user2_is_admin o.conn_user.ui_user then begin + + if use_html_mods o then begin + Printf.bprintf buf "\\<div class=\\\"shares\\\"\\>\\<table class=main cellspacing=0 cellpadding=0\\> +@@ -2831,55 +3121,269 @@ + var outstr = getdir.replace(reg, '+'); + parent.fstatus.location.href='submit?q=useradd+' + outstr; + setTimeout('window.location.reload()',1000); +- }\\\"\\>Add User\\</a\\> +-\\</td\\> +-\\</tr\\>\\</table\\> +-\\</td\\>\\</tr\\> +-\\<tr\\>\\<td\\>"; ++ }\\\"\\>Add user\\</a\\> ++\\</td\\>\\</tr\\>\\</table\\>\\</td\\>\\</tr\\>\\<tr\\>\\<td\\>"; + + html_mods_table_header buf "sharesTable" "shares" [ + ( "0", "srh ac", "Click to remove user", "Remove" ) ; +- ( "0", "srh", "User", "Username" ) ]; ++ ( "0", "srh", "Username", "User" ) ; ++ ( "0", "srh ac", "Only member of admin groups have admin rights", "Admin" ) ; ++ ( "0", "srh", "Member of groups", "Groups" ) ; ++ ( "0", "srh", "Default group", "Default group" ) ; ++ ( "0", "srh", "Mail address", "Email" ) ; ++ ( "0", "srh", "Commit dir", "Commit dir" ) ; ++ ( "0", "srh ar", "Download quota", "Max DLs" ) ; ++ ( "0", "srh ar", "Download count", "DLs" ) ]; + + let counter = ref 0 in +- +- user2_iter (fun name user -> ++ user2_users_iter (fun user -> + incr counter; ++ let u_dls = user2_num_user_dls user in + Printf.bprintf buf "\\<tr class=\\\"%s\\\"\\>" + (if !counter mod 2 == 0 then "dl-1" else "dl-2"); +- if user.user_name <> admin_user then Printf.bprintf buf " +- \\<td title=\\\"Click to remove user\\\" +- onMouseOver=\\\"mOvr(this);\\\" +- onMouseOut=\\\"mOut(this);\\\" +- onClick=\\\'javascript:{ +- parent.fstatus.location.href=\\\"submit?q=userdel+\\\\\\\"%s\\\\\\\"\\\"; +- setTimeout(\\\"window.location.reload()\\\",1000);}' +- class=\\\"srb\\\"\\>Remove\\</td\\>" user.user_name +- else Printf.bprintf buf " +- \\<td title=\\\"\\\" +- class=\\\"srb\\\"\\>------\\</td\\>"; +- Printf.bprintf buf +- "\\<td class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>" user.user_name ++ if user <> admin_user && (u_dls = 0) then Printf.bprintf buf ++"\\<td title=\\\"Click to remove user\\\" ++onMouseOver=\\\"mOvr(this);\\\" ++onMouseOut=\\\"mOut(this);\\\" ++onClick=\\\'javascript:{ ++parent.fstatus.location.href=\\\"submit?q=userdel+\\\\\\\"%s\\\\\\\"\\\"; ++setTimeout(\\\"window.location.reload()\\\",1000);}' ++class=\\\"srb\\\"\\>Remove\\</td\\>" user.user_name ++ else Printf.bprintf buf ++"\\<td title=\\\"%s\\\" ++class=\\\"srb\\\"\\>------\\</td\\>" ++ (if user.user_name = admin_user.user_name then "Admin user can not be removed" else ++ if u_dls <> 0 then Printf.sprintf "User has %d download%s" u_dls ++ (Printf2.print_plural_s u_dls) else ""); ++ html_mods_td buf [ ++ ("", "sr", user.user_name); ++ ("", "sr ac", Printf.sprintf "%b" (user2_is_admin user)); ++ ("Click to remove group", "sr", ++ let buf1 = Buffer.create 100 in ++ user2_user_groups_iter user (fun group -> ++ if user2_default_group_matches_group user.user_default_group group then ++ Printf.bprintf buf1 "%s " group.group_name ++ else ++ Printf.bprintf buf1 ++"\\<a onMouseOver=\\\"mOvr(this);\\\" ++onMouseOut=\\\"mOut(this);\\\" ++onClick=\\\'javascript:{ ++parent.fstatus.location.href=\\\"submit?q=usergroupdel+\\\\\\\"%s\\\\\\\"+\\\\\\\"%s\\\\\\\"\\\"; ++setTimeout(\\\"window.location.reload()\\\",1000);}' ++class=\\\"srb\\\"\\>%s\\</a\\> " user.user_name group.group_name group.group_name ++ ); ++ Buffer.contents buf1); ++ ("", "sr", user2_print_user_default_group user); ++ ("", "sr", user.user_mail); ++ ("", "sr", user.user_commit_dir); ++ ("", "sr ar", user2_print_user_dls user); ++ ("", "sr ar", string_of_int u_dls)]; + ); ++ Printf.bprintf buf "\\</table\\>\\</td\\>\\<tr\\>\\</table\\>\\</div\\>\\<P\\>"; ++ print_option_help o userlist; ++ Printf.bprintf buf "\\<P\\>"; ++ ++ Printf.bprintf buf "\\<div class=\\\"shares\\\"\\>\\<table class=main cellspacing=0 cellpadding=0\\> ++\\<tr\\>\\<td\\> ++\\<table cellspacing=0 cellpadding=0 width=100%%\\>\\<tr\\> ++\\<td class=downloaded width=100%%\\>\\</td\\> ++\\<td nowrap class=\\\"fbig pr\\\"\\>\\<a onclick=\\\"javascript: { ++ var getdir = prompt('Input: <group> <admin: true|false> [<mail>]','group true') ++ var reg = new RegExp (' ', 'gi') ; ++ var outstr = getdir.replace(reg, '+'); ++ parent.fstatus.location.href='submit?q=groupadd+' + outstr; ++ setTimeout('window.location.reload()',1000); ++ }\\\"\\>Add group\\</a\\> ++\\</td\\>\\</tr\\>\\</table\\>\\</td\\>\\</tr\\>\\<tr\\>\\<td\\>"; ++ ++ html_mods_table_header buf "sharesTable" "shares" [ ++ ( "0", "srh ac", "Click to remove group", "Remove" ); ++ ( "0", "srh", "Groupname", "Group" ); ++ ( "0", "srh ac", "Click to change status", "Admin" ); ++ ( "0", "srh ar", "Member count", "Mem" ); ++ ( "0", "srh ar", "Download count", "DLs" ) ]; ++ ++ html_mods_cntr_init (); ++ user2_groups_iter (fun group -> ++ let g_dls = user2_num_group_dls group in ++ let g_mem = user2_num_group_members group in ++ let is_sys_group = group.group_name = system_user_default_group.group_name in ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ if g_dls = 0 && g_mem = 0 && not is_sys_group then Printf.bprintf buf ++"\\<td title=\\\"Click to remove group\\\" ++onMouseOver=\\\"mOvr(this);\\\" onMouseOut=\\\"mOut(this);\\\" onClick=\\\'javascript:{ ++parent.fstatus.location.href=\\\"submit?q=groupdel+\\\\\\\"%s\\\\\\\"\\\"; ++setTimeout(\\\"window.location.reload()\\\",1000);}' ++class=\\\"srb\\\"\\>Remove\\</td\\>" group.group_name ++ else ++ Printf.bprintf buf "\\<td title=\\\"%s\\\" class=\\\"srb\\\"\\>------\\</td\\>" ++ (if g_dls <> 0 then Printf.sprintf "Group is assigned to %d download%s" ++ g_dls (Printf2.print_plural_s g_dls) else ++ if g_mem <> 0 then Printf.sprintf "Group has %d member%s" ++ g_mem (Printf2.print_plural_s g_mem) else ++ if is_sys_group then "System group can not be removed" else ""); ++ ++ html_mods_td buf [("", "sr", group.group_name)]; ++ ++ if is_sys_group then ++ html_mods_td buf [("System group, can not change state", "sr ac", Printf.sprintf "%b" group.group_admin)] ++ else Printf.bprintf buf ++"\\<td title=\\\"Change admin status\\\" ++onMouseOver=\\\"mOvr(this);\\\" onMouseOut=\\\"mOut(this);\\\" onClick=\\\'javascript:{ ++parent.fstatus.location.href=\\\"submit?q=groupadmin+\\\\\\\"%s\\\\\\\"+\\\\\\\"%s\\\\\\\"\\\"; ++setTimeout(\\\"window.location.reload()\\\",1000);}' ++class=\\\"sr ac\\\"\\>%s\\</td\\>" ++ group.group_name ++ (if group.group_admin then "false" else "true") ++ (if group.group_admin then "true" else "false"); + +- Printf.bprintf buf "\\</table\\>\\</td\\>\\<tr\\>\\</table\\>\\</div\\>"; ++ html_mods_td buf [ ++ ("", "sr ar", Printf.sprintf "%d" (user2_num_group_members group)); ++ ("", "sr ar", Printf.sprintf "%d" g_dls); ++ ]); ++ ++ Printf.bprintf buf "\\</table\\>\\</td\\>\\<tr\\>\\</table\\>\\</div\\>\\<P\\>"; ++ print_option_help o grouplist; ++ Printf.bprintf buf "\\<P\\>"; ++ ++ Buffer.add_string buf "\\<div class=\\\"cs\\\"\\>"; ++ html_mods_table_header buf "helpTable" "results" []; ++ Buffer.add_string buf "\\<tr\\>"; ++ html_mods_td buf [ ++ ("", "srh", ""); ++ ("", "srh", "Commands to manipulate user data"); ++ ("", "srh", ""); ]; ++ Buffer.add_string buf "\\</tr\\>"; ++ html_mods_cntr_init (); ++ let list = Hashtbl2.to_list2 commands_by_kind in ++ let list = List.sort (fun (s1,_) (s2,_) -> compare s1 s2) list in ++ List.iter (fun (s,list) -> ++ if s = "Driver/Users" then ++ let list = List.sort (fun (s1,_) (s2,_) -> compare s1 s2) !list in ++ List.iter (fun (cmd, help) -> ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ html_mods_td buf [ ++ ("", "sr", "\\<a href=\\\"submit?q=" ^ cmd ^ ++ "\\\"\\>" ^ cmd ^ "\\</a\\>"); ++ ("", "srw", Str.global_replace (Str.regexp "\n") "\\<br\\>" help); ++ ("", "sr", "\\<a href=\\\"http://mldonkey.sourceforge.net/" ^ (String2.upp_initial cmd) ^ ++ "\\\"\\>wiki\\</a\\>"); ]; ++ Printf.bprintf buf "\\</tr\\>\n" ++ ) list ++ ) list + end +- else +- begin +- Printf.bprintf buf "Users:\n"; +- user2_iter (fun name user -> +- Printf.bprintf buf " %s\n" +- user.user_name); +- end; +- "" +- else +- _s "Only 'admin' is allowed to list users" +- ), ":\t\t\t\t\tprint users"; ++ else begin ++ let list = ref [] in ++ user2_users_iter (fun user -> list := [| ++ user.user_name; ++ Printf.sprintf "%b" (user2_is_admin user); ++ (user2_print_user_groups " " user); ++ (user2_print_user_default_group user); ++ user.user_mail; ++ user.user_commit_dir; ++ (user2_print_user_dls user); ++ (string_of_int (user2_num_user_dls user)); ++ |] :: !list ); ++ print_table_text buf ++ [| ++ Align_Left; Align_Left; Align_Left; Align_Left; Align_Left; Align_Left; Align_Right; Align_Right |] ++ [| ++ "User"; ++ "Admin"; ++ "Groups"; ++ "Dgroup"; ++ "Email"; ++ "Commit dir"; ++ "Max dls"; ++ "Dls"; ++ |] (List.rev !list); ++ Printf.bprintf buf "\n"; ++ let list = ref [] in ++ user2_groups_iter (fun group -> list := [| ++ group.group_name; ++ Printf.sprintf "%b" group.group_admin; ++ (string_of_int (user2_num_group_members group)); ++ (string_of_int (user2_num_group_dls group)); ++ |] :: !list ); ++ print_table_text buf ++ [| ++ Align_Left; Align_Left; Align_Right; Align_Right |] ++ [| ++ "Group"; ++ "Admin"; ++ "Members"; ++ "Downloads"; ++ |] (List.rev !list); ++ end ++ end else print_command_result o buf "You are not allowed to list users"; ++ _s "" ++ ), "\t\t\t\t\tprint users"; + + "whoami", Arg_none (fun o -> +- print_command_result o o.conn_buf o.conn_user.ui_user_name; ++ print_command_result o o.conn_buf o.conn_user.ui_user.user_name; + _s "" + ), "\t\t\t\t\tprint logged-in user name"; ++ ++ "groups", Arg_none (fun o -> ++ print_command_result o o.conn_buf (user2_print_user_groups " " o.conn_user.ui_user); ++ _s "" ++ ), "\t\t\t\t\tprint groups of logged-in user"; ++ ++ "dgroup", Arg_none (fun o -> ++ print_command_result o o.conn_buf (user2_print_user_default_group o.conn_user.ui_user); ++ _s "" ++ ), "\t\t\t\t\tprint default group of logged-in user"; ++ ++ "chgrp", Arg_two (fun group filenum o -> ++ let num = int_of_string filenum in ++ begin try ++ let file = file_find num in ++ if String.lowercase group = "none" then ++ begin ++ set_file_group file None; ++ print_command_result o o.conn_buf (Printf.sprintf (_b "Changed group of download %d to %s") num group) ++ end ++ else ++ begin ++ try ++ let g = user2_group_find group in ++ if user2_allow_file_admin file o.conn_user.ui_user && ++ List.mem g (file_owner file).user_groups then ++ begin ++ set_file_group file (Some g); ++ print_command_result o o.conn_buf (Printf.sprintf (_b "Changed group of download %d to %s") num group) ++ end ++ else ++ print_command_result o o.conn_buf (Printf.sprintf (_b "You are not allowed to change group of download %d to %s") num group) ++ with Not_found -> print_command_result o o.conn_buf (Printf.sprintf (_b "Group %s not found") group) ++ end ++ with Not_found -> print_command_result o o.conn_buf (Printf.sprintf (_b "File %d not found") num) ++ end; ++ _s "" ++ ), "<group> <num> :\t\t\tchange group of download <num> to <group>, use group = none for private file"; ++ ++ "chown", Arg_two (fun user filenum o -> ++ let num = int_of_string filenum in ++ begin ++ try ++ let file = file_find num in ++ begin ++ try ++ let u = user2_user_find user in ++ if user2_allow_file_admin file o.conn_user.ui_user then ++ begin ++ set_file_owner file u; ++ print_command_result o o.conn_buf (Printf.sprintf (_b "Changed owner of download %d to %s") num user) ++ end ++ else ++ print_command_result o o.conn_buf (Printf.sprintf (_b "You are not allowed to change owner of download %d to %s") num user) ++ with Not_found -> print_command_result o o.conn_buf (Printf.sprintf (_b "User %s not found") user) ++ end ++ with Not_found -> print_command_result o o.conn_buf (Printf.sprintf (_b "File %d not found") num) ++ end; ++ _s "" ++ ), "<user> <num> :\t\t\tchange owner of download <num> to <user>"; ++ + ] + + +@@ -3021,7 +3525,6 @@ + html_mods =:= true; + html_mods_style =:= 0; + commands_frame_height =:= CommonMessages.styles.(!!html_mods_style).frame_height; +- use_html_frames =:= true; + CommonMessages.colour_changer() ; + end; + +@@ -3039,7 +3542,6 @@ + end + else begin + html_mods =:= true; +- use_html_frames =:= true; + html_mods_theme =:= ""; + let num = int_of_string (List.hd args) in + +@@ -3130,8 +3632,7 @@ + "" + end + else begin +-(* html_mods =:= true; +- use_html_frames =:= true; *) ++(* html_mods =:= true; *) + html_mods_theme =:= List.hd args; + "\\<script type=\\\"text/javascript\\\"\\>top.window.location.reload();\\</script\\>" + end +Index: src/daemon/driver/driverControlers.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverControlers.ml,v +retrieving revision 1.85 +retrieving revision 1.93 +diff -u -r1.85 -r1.93 +--- src/daemon/driver/driverControlers.ml 12 Sep 2006 22:47:11 -0000 1.85 ++++ src/daemon/driver/driverControlers.ml 19 Nov 2006 23:03:42 -0000 1.93 +@@ -310,7 +310,7 @@ + let user, pass = + match args with + [] -> failwith "Usage: auth <user> <password>" +- | [s1] -> admin_user, s1 ++ | [s1] -> admin_user.CommonTypes.user_name, s1 + | user :: pass :: _ -> user, pass + in + if valid_password user pass then begin +@@ -320,7 +320,7 @@ + let module M = CommonMessages in + Buffer.add_string buf M.full_access; + (match DriverInteractive.real_startup_message () with +- Some s -> Buffer.add_string buf s; ++ Some s -> Buffer.add_string buf ("\n" ^ s); + | None -> ()); + end else + let module M = CommonMessages in +@@ -581,7 +581,7 @@ + "telnet connection" + s in + let telnet = { +- telnet_auth = ref (empty_password admin_user); ++ telnet_auth = ref (has_empty_password admin_user); + telnet_iac = false; + telnet_wait = 0; + telnet_buffer = Buffer.create 100; +@@ -775,14 +775,6 @@ + really_input file s 0 size; + s) + +-let add_simple_commands buf = +- let this_page = "commands.html" in +- Buffer.add_string buf ( +- if !!html_mods_theme != "" && theme_page_exists this_page then +- read_theme_page this_page else +- if !!html_mods then !!CommonMessages.web_common_header_mods0 +- else !!CommonMessages.web_common_header_old) +- + let http_add_gen_header r = + add_reply_header r "Server" "MLdonkey"; + add_reply_header r "Connection" "close" +@@ -883,9 +875,7 @@ + else !!CommonMessages.html_header_old); + + Buffer.add_string buf "</head>\n"; +- if open_body then Buffer.add_string buf "<body>\n"; +- if not !!use_html_frames then add_simple_commands buf; +- () ++ if open_body then Buffer.add_string buf "<body>\n" + + let html_close_page buf close_body = + if close_body then Buffer.add_string buf "</body>\n"; +@@ -955,12 +945,18 @@ + List.iter (fun (arg, value) -> Printf.bprintf b " %s %s" arg value) r.get_url.Url.args; + if Buffer.contents b <> "" then Printf.sprintf "(%s)" (Buffer.contents b) else ""); + +- let user = if r.options.login = "" then admin_user else r.options.login in ++ let user = if r.options.login = "" then admin_user.CommonTypes.user_name else r.options.login in + if not (valid_password user r.options.passwd) then begin + clear_page buf; + http_file_type := HTM; +- Buffer.add_string buf (snd(Http_server.error_page "401" "" "" (Ip.to_string (TcpBufferedSocket.my_ip r.sock)) (string_of_int !!http_port) None)); +- need_auth r !!http_realm ++ let _, error_text_long, header = Http_server.error_page "401" "" "" ++ (Ip.to_string (TcpBufferedSocket.my_ip r.sock)) ++ (string_of_int !!http_port) None in ++ Buffer.add_string buf error_text_long; ++ r.reply_head <- header; ++ r.reply_headers <- [ ++ "Connection", "close"; ++ "WWW-Authenticate", Printf.sprintf "Basic realm=\"%s\"" !!http_realm] + end + else + begin +@@ -1000,15 +996,15 @@ + "VDC" -> + let num = int_of_string value in + let file = file_find num in +- file_cancel file ++ file_cancel file o.conn_user.ui_user + | "VDP" -> + let num = int_of_string value in + let file = file_find num in +- file_pause file ++ file_pause file o.conn_user.ui_user + | "VDR" -> + let num = int_of_string value in + let file = file_find num in +- file_resume file ++ file_resume file o.conn_user.ui_user + | _ -> () + ) r.get_url.Url.args; + +@@ -1048,7 +1044,6 @@ + if !!html_mods then !!CommonMessages.multidllink_mods0 + else !!CommonMessages.multidllink_old) + | "" | "index.html" -> +- if !!use_html_frames then begin + html_open_page buf t r false; + let this_page = "frames.html" in + if !!html_mods_theme != "" && theme_page_exists this_page then +@@ -1079,9 +1074,7 @@ + </frameset> + <frame name=\"output\" src=\"oneframe.html\"> + </frameset> +-" !!commands_frame_height; +- end else +- html_open_page buf t r true ++" !!commands_frame_height + | "complex_search.html" -> + html_open_page buf t r true; + CommonSearch.complex_search buf +@@ -1093,7 +1086,7 @@ + Buffer.add_string buf (Printf.sprintf "<br><div align=\"center\"><h3>%s %s</h3></div>" + (Printf.sprintf (_b "Welcome to MLDonkey")) Autoconf.current_version); + if !!motd_html <> "" then Buffer.add_string buf !!motd_html; +- if user2_is_admin o.conn_user.ui_user_name then ++ if user2_is_admin o.conn_user.ui_user then + (match DriverInteractive.real_startup_message () with + Some s -> Buffer.add_string buf (Printf.sprintf "<p><pre><b><h3>%s</b></h3></pre>" s); + | None -> ()) +@@ -1265,7 +1258,7 @@ + try + let num = int_of_string value in + let r = find_result num in +- let files = result_download r [] false in ++ let files = result_download r [] false o.conn_user.ui_user in + List.iter CommonInteractive.start_download files; + + let module M = CommonMessages in +@@ -1285,15 +1278,23 @@ + "cancel" -> + let num = int_of_string value in + let file = file_find num in +- file_cancel file ++ file_cancel file o.conn_user.ui_user + | "pause" -> + let num = int_of_string value in + let file = file_find num in +- file_pause file ++ file_pause file o.conn_user.ui_user + | "resume" -> + let num = int_of_string value in + let file = file_find num in +- file_resume file ++ file_resume file o.conn_user.ui_user ++ | "release" -> ++ let num = int_of_string value in ++ let file = file_find num in ++ set_file_release file true o.conn_user.ui_user ++ | "norelease" -> ++ let num = int_of_string value in ++ let file = file_find num in ++ set_file_release file false o.conn_user.ui_user + | "sortby" -> + begin + match value with +@@ -1313,13 +1314,15 @@ + | "N" -> o.conn_sortvd <- ByNet + | "Avail" -> o.conn_sortvd <- ByAvail + | "Cm" -> o.conn_sortvd <- ByComments ++ | "User" -> o.conn_sortvd <- ByUser ++ | "Group" -> o.conn_sortvd <- ByGroup + | _ -> () + end + | _ -> () + ) r.get_url.Url.args; + let b = Buffer.create 10000 in + +- let list = (List2.tail_map file_info !!files) in ++ let list = List2.tail_map file_info (user2_filter_files !!files o.conn_user.ui_user) in + DriverInteractive.display_file_list b o list; + html_open_page buf t r true; + Buffer.add_string buf (html_escaped (Buffer.contents b)) +@@ -1331,9 +1334,10 @@ + | [ "jvcmd", "multidllink" ; "links", links] -> + html_open_page buf t r true; + List.iter (fun url -> +- if url <> "\013" && url <> "" then ++ let url = fst (String2.cut_at url '\013') in ++ if url <> "" then + begin +- Buffer.add_string buf (html_escaped (dllink_parse (o.conn_output = HTML) url)); ++ Buffer.add_string buf (html_escaped (dllink_parse (o.conn_output = HTML) url o.conn_user.ui_user)); + Buffer.add_string buf (html_escaped "\\<P\\>") + end + ) (String2.split links '\n') +@@ -1388,7 +1392,7 @@ + + | [ "setoption", _ ; "option", name; "value", value ] -> + html_open_page buf t r true; +- if (o.conn_user == default_user) || !!enable_user_config then ++ if user2_is_admin o.conn_user.ui_user then + begin + CommonInteractive.set_fully_qualified_options name value; + Buffer.add_string buf "Option value changed" +@@ -1489,11 +1493,34 @@ + read_theme_page this_page else + if !!html_mods then !!CommonMessages.download_html_js_mods0 + else !!CommonMessages.download_html_js_old) +- | cmd -> +- html_open_page buf t r true; +- Printf.bprintf buf "No page named %s" (html_escaped cmd) ++ | "porttest" -> ++ html_open_page buf t r true; ++ let age time = ++ Date.time_to_string (BasicSocket.last_time () - time) "verbose" in ++ networks_iter (fun n -> ++ let result = ++ match network_porttest_result n with ++ PorttestNotAvailable -> None ++ | PorttestNotStarted -> Some "porttest not started" ++ | PorttestInProgress time -> ++ Some (Printf.sprintf "porttest started %s ago" (age time)) ++ | PorttestResult (time, s) -> ++ Some (Printf.sprintf "porttest finished %s ago, %s" (age time) s) ++ in ++ (match result with ++ None -> () ++ | Some result -> ++ Printf.bprintf buf "%s:<br> %s<br>\n" n.network_name result)); ++ Printf.bprintf buf "<br><br><a href=\"porttest\">Reload</a>" ++ | _ -> raise Not_found + with +- | Not_found -> Printf.bprintf buf "404 Not found" ++ | Not_found -> ++ let _, error_text_long, header = Http_server.error_page "404" "" "" ++ (Ip.to_string (TcpBufferedSocket.my_ip r.sock)) ++ (string_of_int !!http_port) ++ (Some (Url_not_found r.get_url.Url.full_file)) in ++ r.reply_head <- header; ++ Buffer.add_string buf error_text_long + | e -> + Printf.bprintf buf "\nException %s\n" (Printexc2.to_string e); + r.reply_stream <- None +@@ -1501,11 +1528,11 @@ + + let s = + match !http_file_type with +- HTM -> html_close_page buf false; dollar_escape o !!use_html_frames (Buffer.contents buf) +- | MLHTM -> html_close_page buf true; dollar_escape o !!use_html_frames (Buffer.contents buf) ++ HTM -> html_close_page buf false; dollar_escape o true (Buffer.contents buf) ++ | MLHTM -> html_close_page buf true; dollar_escape o true (Buffer.contents buf) + | TXT ++ | UNK + | BIN -> Buffer.contents buf +- | UNK -> "Unknown type for content :" ^ (Buffer.contents buf) + in + r.reply_content <- + if !http_file_type <> BIN && !!html_use_gzip then +Index: src/daemon/driver/driverGraphics_gd.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverGraphics_gd.ml,v +retrieving revision 1.1 +retrieving revision 1.2 +diff -u -r1.1 -r1.2 +--- src/daemon/driver/driverGraphics_gd.ml 25 Jan 2006 22:44:08 -0000 1.1 ++++ src/daemon/driver/driverGraphics_gd.ml 14 Nov 2006 11:23:11 -0000 1.2 +@@ -47,14 +47,6 @@ + + module Graphics : Graphics = struct + +-(* some thoughts +-type gfx_settings = { +- win_x : int; +- win_y : int; +- vtext : string; +- } +-*) +- + (* some defs *) + let reverse lst = + let rec reverseAux lst acc = +@@ -73,144 +65,146 @@ + | [x] -> x + | x::xs -> max x (maxlist xs) + ++(* set some vars *) + +-(* set some vars *) +-let samples_time = 5 +-let samples_h_time = 720 ++let history_time = history_size * history_step ++let history_h_time = history_h_size * history_h_step ++ ++(* define margins *) ++let left_margin = 20 ++let right_margin = 45 ++let top_margin = 16 ++let bottom_margin = 24 + + (* set _x and _y with boundaries *) +-let tag_x() = if !!html_mods_vd_gfx_tag_x_size < 130 then 130 +- else if !!html_mods_vd_gfx_tag_x_size > 3600 then 3600 +- else !!html_mods_vd_gfx_tag_x_size +-let tag_y() = if !!html_mods_vd_gfx_tag_y_size < 50 then 50 +- else if !!html_mods_vd_gfx_tag_y_size > 1200 then 1200 +- else !!html_mods_vd_gfx_tag_y_size +-let win_x() = if !!html_mods_vd_gfx_x_size < 320 then 320 +- else if !!html_mods_vd_gfx_x_size > 3600 then 3600 +- else !!html_mods_vd_gfx_x_size +-let win_y() = if !!html_mods_vd_gfx_y_size < 200 then 200 +- else if !!html_mods_vd_gfx_y_size > 1200 then 1200 +- else !!html_mods_vd_gfx_y_size +- +-let vtext = "0" +-let x_divisions() = (win_x()) / 80 +-let y_divisions() = (win_y()) / 30 +-let x_fdivisions() = float_of_int (x_divisions()) +-let y_fdivisions() = float_of_int (y_divisions()) +- +-(* todo: limit to 4 max *) +-let xmult = 1 +-let xgmult = 16 +-(* todo: limit to 4 max *) +-let ymult = 1 +-let xdivisions() = (x_divisions()) * xmult +-let fxdivisions() = float_of_int (xdivisions()) +-let xgdivisions() = (x_divisions()) * xgmult +-let fxgdivisions() = float_of_int (xgdivisions()) +-let ydivisions() = (y_divisions()) * ymult +-let fydivisions() = float_of_int (ydivisions()) +-let vmax_auto() = if detected_downlink_capacity () < detected_uplink_capacity () then +- (detected_uplink_capacity ()) ++let tag_x () = min (max 130 !!html_mods_vd_gfx_tag_x_size) 3600 ++let tag_y () = min (max 50 !!html_mods_vd_gfx_tag_y_size) 1200 ++let win_x () = min (max 365 !!html_mods_vd_gfx_x_size) 3665 ++let win_y () = min (max 200 !!html_mods_vd_gfx_y_size) 1200 ++ ++let round_down x = ++ let values = [120; 60; 30; 15; 12; 10; 6; 4; 3; 2; 1; 0] in ++ max 1 (List.find ((>=) x) values) ++ ++let round_up x = ++ let v = min x 120 in ++ let values = [0; 1; 2; 3; 4; 5; 10; 15; 30; 60; 120] in ++ List.find ((<=) v) values ++ ++let round_h_up x = ++ let v = min x 840 in ++ let values = [1; 2; 3; 4; 5; 10; 15; 30; 60; 120; 180; 240; 300; 360; 480; 720; 1440; 2880; 4320; 5760; 7200; 8640; 10080; 11520; 12960; 14400; 15840; 17280; 18720; 20160; 30240; 40320; 50400; 60480; 70560; 80640; 90720] in ++ List.find ((<=) v) values ++ ++let round_h_down x = ++ let values = [90720; 80640; 70560; 60480; 50400; 40320; 30240; 20160; 18720; 17280; 15840; 14400; 12960; 11520; 10080; 8640; 7200; 5760; 4320; 2880; 1440; 720; 480; 360; 300; 240; 180; 120; 60; 30; 15; 10; 5; 4; 3; 2; 1; 0] in ++ max 1 (List.find ((>=) x) values) ++ ++(* calculate x-values *) ++let x_time_per_grid() = 60 * round_up (history_time / (60 * ((win_x () - left_margin - right_margin) / 60))) ++let x_divisions () = history_time / (x_time_per_grid ()) ++let x_fdivisions () = float_of_int (x_divisions ()) ++ ++let x_h_time_per_grid g = max history_h_step ( ++ if !!html_mods_vd_gfx_h_dynamic then ++ begin ++ if !!html_mods_vd_gfx_h_grid_time >= 1 then ++ min (round_h_down (!!html_mods_vd_gfx_h_grid_time * 60)) ++ (min ++ ((round_h_up ((((Fifo.length g) + x_divisions () - 2) / (x_divisions())) * history_h_step/60 )) * 60) ++ ((round_h_down ( history_h_time / (60 * x_divisions()) )) * 60)) ++ else ++ min ((round_h_up ((((Fifo.length g) + x_divisions () - 2) / (x_divisions ())) * history_h_step / 60)) * 60) ++ ((round_h_down (history_h_time / (60 * x_divisions ()) )) * 60) ++ end + else +- (detected_downlink_capacity ()) ++ if !!html_mods_vd_gfx_h_grid_time >= 1 then ++ round_h_down (!!html_mods_vd_gfx_h_grid_time * 60) ++ else ++ round_h_down (history_h_time / (60 * x_divisions ()))) + + let vmax link = (detected_link_capacity link) + +-let vx() = (fxgdivisions()) /. (x_fdivisions()) +-let vy() = (((float_of_int(vmax_auto())) /. 1024.) +. 2.) /. (y_fdivisions()) +-let vy_m link = (((float_of_int(vmax link)) /. 1024.) +. 2.) /. (y_fdivisions()) ++let x_h_time g = (x_divisions() * x_h_time_per_grid g) ++let x_h_values g = ((x_h_time g) / history_h_step) + +-(* define margins *) +-let left_margin = 20 +-let right_margin = 45 +-let top_margin = 16 +-let bottom_margin = 20 ++(* calculate y-values *) ++let y_divisions () = ((win_y() - top_margin - bottom_margin)) / 50 * 2 ++let y_fdivisions () = float_of_int (y_divisions()) ++ ++let vmax_auto () = max (detected_uplink_capacity()) (detected_downlink_capacity()) + + let samples_size = win_x() - (left_margin + right_margin) + + let xbl = left_margin + let xbr() = win_x() - right_margin + let xbs() = xbr() - xbl +-let ybt = top_margin +-let ybb() = win_y() - bottom_margin ++let ybt = (top_margin / 2) * 2 ++let ybb() = ((win_y () - bottom_margin) / 2) * 2 - 1 + let ybs() = ybb() - ybt + let vdt() = float_of_int(ybs()) /. float_of_int(vmax_auto()) + let vdt_m link = float_of_int(ybs()) /. float_of_int(vmax link) +-let vdt_stack link = float_of_int(ybs() / 2) /. float_of_int(vmax link) ++let vdt_stack link = float_of_int((ybs() / 2)-1) /. float_of_int(vmax link) + + let ttl = ref "" + let vl = ref "" + let hl = ref "" + +-let graph_length g = +- if (Fifo.length g) < (xgdivisions()) then +- ((Fifo.length g) - 1) +- else +- (xgdivisions()) +- +-let datas_length g = +- ((Fifo.length g)) +- +-let l_max g = +- (List.fold_left max 0 (Fifo.to_list g)) +- +-let datas = Array.create history_size 0 +- ++let l_max g = (List.fold_left max 0 (Fifo.to_list g)) + + let draw_borders mypic gcolor = +- let my_y = (ybb()) in +- let fx x = int_of_float ((((float_of_int x) /. (fxdivisions())) *. (float_of_int(xbs()))) +. (float_of_int xbl)) in +- let my_x = (xbr()) in +- let fy x = int_of_float ((((float_of_int x) /. (fydivisions())) *. (float_of_int(ybs()))) +. (float_of_int ybt)) in +- mypic#line ~x1:(fx 0) ~y1:ybt ~x2:(fx 0) ~y2:my_y gcolor; +- mypic#line ~x1:(fx (xdivisions())) ~y1:ybt ~x2:(fx (xdivisions())) ~y2:my_y gcolor; +- mypic#line ~x1:xbl ~y1:(fy 0) ~x2:my_x ~y2:(fy 0) gcolor; +- mypic#line ~x1:xbl ~y1:(fy (ydivisions())) ~x2:my_x ~y2:(fy (ydivisions())) gcolor ++ mypic#line ~x1:(xbl-1) ~y1:(ybt-1) ~x2:(xbl-1) ~y2:(ybb()+1) gcolor; ++ mypic#line ~x1:(xbr()+1) ~y1:(ybt-1) ~x2:(xbr()+1) ~y2:(ybb()+1) gcolor; ++ mypic#line ~x1:(xbl-1) ~y1:(ybt-1) ~x2:(xbr()+1) ~y2:(ybt-1) gcolor; ++ mypic#line ~x1:(xbl-1) ~y1:(ybb()+1) ~x2:(xbr()+1) ~y2:(ybb()+1) gcolor + + let draw_stack_borders mypic gcolor = +- let my_y = (ybb()) in +- let fx x = int_of_float ((((float_of_int x) /. (fxdivisions())) *. (float_of_int(xbs()))) +. (float_of_int xbl)) in +- let my_x = (xbr()) in +- let fy x = int_of_float ((((float_of_int x) /. (fydivisions())) *. (float_of_int(ybs()))) +. (float_of_int ybt)) in +- mypic#line ~x1:(fx 0) ~y1:ybt ~x2:(fx 0) ~y2:my_y gcolor; +- mypic#line ~x1:(fx (xdivisions())) ~y1:ybt ~x2:(fx (xdivisions())) ~y2:my_y gcolor; +- mypic#line ~x1:xbl ~y1:(fy 0) ~x2:my_x ~y2:(fy 0) gcolor; +- mypic#line ~x1:xbl ~y1:(fy (ydivisions())) ~x2:my_x ~y2:(fy (ydivisions())) gcolor; +- mypic#line ~x1:xbl ~y1:(my_y - ((my_y - ybt) / 2)) ~x2:my_x ~y2:(my_y - ((my_y - ybt) / 2)) gcolor ++ draw_borders mypic gcolor; ++ mypic#line ~x1:(xbl-1) ~y1:(ybt+ybs()/2) ~x2:(xbr()+1) ~y2:(ybt+ybs()/2) gcolor + +-let draw_x_grid mypic gcolor = ++let draw_x_grid mypic gcolor gcolor2 my_xdivisons = ++ let my_sdivisions = max 1 !!html_mods_vd_gfx_subgrid in ++ let my_xsdivisons = xbs() / my_xdivisons / my_sdivisions in + let my_y = (ybb()) in +- let fx x = int_of_float ((((float_of_int x) /. (fxdivisions())) *. (float_of_int(xbs()))) +. (float_of_int xbl)) in +- for n = 1 to xdivisions() - 1 do +- mypic#dashed_line ~x1:(fx n) ~y1:ybt ~x2:(fx n) ~y2:my_y gcolor; ++ let fx x = (((x * xbs()) / my_xdivisons) + xbl + 2) in ++ let fxs x y = (fx x + my_xsdivisons * y) in ++ for n = 1 to my_xdivisons - 1 do ++ (*mypic#string ~font:Gd.Font.small ~x:(fx n) ~y:(2) ~s:(string_of_int (fx n)) gcolor;*) ++ mypic#dashed_line ~x1:(fx n) ~y1:(ybt) ~x2:(fx n) ~y2:my_y gcolor; ++ done; ++ if my_sdivisions > 1 then ++ for n = 0 to my_xdivisons - 1 do ++ for m = 1 to my_sdivisions - 1 do ++ mypic#dashed_line ~x1:(fxs n m) ~y1:(ybt) ~x2:(fxs n m) ~y2:my_y gcolor2; ++ done; + done + + let draw_y_grid mypic gcolor = +- let my_x = (xbr()) in +- let fy x = int_of_float ((((float_of_int x) /. (fydivisions())) *. (float_of_int(ybs()))) +. (float_of_int ybt)) in +- for n = 1 to ydivisions() - 1 do +- mypic#dashed_line ~x1:xbl ~y1:(fy n) ~x2:my_x ~y2:(fy n) gcolor; +- done ++ let fy x = (((x * ybs()) / y_divisions()) + ybt) in ++ for n = 1 to y_divisions() - 1 do ++ mypic#dashed_line ~x1:xbl ~y1:(fy n) ~x2:(xbr()) ~y2:(fy n) gcolor; ++ done + + let draw_arrow mypic gcolor = +- let my_x = (xbr()) in +- let my_y = (ybb()) in ++ let my_x = (xbr()+1) in ++ let my_y = (ybb()+1) in + mypic#line ~x1:(my_x - 4) ~y1:(my_y + 4) ~x2:(my_x + 4) ~y2:(my_y) gcolor; + mypic#line ~x1:(my_x - 4) ~y1:(my_y - 4) ~x2:(my_x + 4) ~y2:(my_y) gcolor; + mypic#line ~x1:(my_x - 4) ~y1:(my_y - 4) ~x2:(my_x - 4) ~y2:(my_y + 4) gcolor; + mypic#fill ~x:(my_x - 1) ~y:(my_y - 1) gcolor; ++ mypic#fill ~x:(my_x - 3) ~y:(my_y - 1) gcolor; + mypic#fill ~x:(my_x - 1) ~y:(my_y + 1) gcolor; + mypic#fill ~x:(my_x + 1) ~y:(my_y) gcolor + + let draw_tag mypic title gdown gup gcolor = + let my_sum gl = List.fold_left (+) 0 (Fifo.to_list gl) in +- let meanx gl = ((float_of_int (my_sum gl)) /. (float_of_int ((Fifo.length gl) - 1))) in ++ let meanx gl = ((float_of_int (my_sum gl)) /. (float_of_int (Fifo.length gl))) in + let down_bw = (string_of_float (float_of_int(int_of_float((meanx gdown) /. 1024. *. 100.)) /. 100.)) in + let up_bw = (string_of_float (float_of_int(int_of_float((meanx gup) /. 1024. *. 100.)) /. 100.)) in + let bw_d = "Dl: " ^ down_bw ^ "KB/s " + and bw_u = "Ul: " ^ up_bw ^ "KB/s" in + if !!html_mods_vd_gfx_tag_enable_title then +- mypic#string ~font:Gd.Font.giant ~x:!!html_mods_vd_gfx_tag_title_x_pos ~y:!!html_mods_vd_gfx_tag_title_y_pos ~s:title gcolor; ++ mypic#string ~font:Gd.Font.giant ~x:!!html_mods_vd_gfx_tag_title_x_pos ~y:!!html_mods_vd_gfx_tag_title_y_pos ~s:title gcolor; + mypic#string ~font:Gd.Font.giant ~x:!!html_mods_vd_gfx_tag_dl_x_pos ~y:!!html_mods_vd_gfx_tag_dl_y_pos ~s:bw_d gcolor; + mypic#string ~font:Gd.Font.giant ~x:!!html_mods_vd_gfx_tag_ul_x_pos ~y:!!html_mods_vd_gfx_tag_ul_y_pos ~s:bw_u gcolor + +@@ -218,16 +212,16 @@ + mypic#string_up ~font:Gd.Font.giant ~x:2 ~y:((win_y / 2) + (((String.length title) * 8) / 2)) ~s:title gcolor + + let draw_top_legend mypic title tcolor gcolor scolor win_y = +- mypic#line ~x1:(xbl + 1) ~y1:9 ~x2:(xbl + 11) ~y2:9 scolor; ++ mypic#line ~x1:xbl ~y1:9 ~x2:(xbl + 10) ~y2:9 scolor; + mypic#line ~x1:xbl ~y1:8 ~x2:(xbl + 10) ~y2:8 gcolor; + mypic#string ~font:Gd.Font.small ~x:(xbl + 16) ~y:2 ~s:title tcolor + + let draw_dual_top_legend mypic titlel tcolorl gcolorl scolorl titler tcolorr gcolorr scolorr win_y = + let my_x = (xbr()) in +- mypic#line ~x1:(xbl + 1) ~y1:9 ~x2:(xbl + 11) ~y2:9 scolorl; ++ mypic#line ~x1:xbl ~y1:9 ~x2:(xbl + 10) ~y2:9 scolorl; + mypic#line ~x1:xbl ~y1:8 ~x2:(xbl + 10) ~y2:8 gcolorl; + mypic#string ~font:Gd.Font.small ~x:(xbl + 16) ~y:2 ~s:titlel tcolorl; +- mypic#line ~x1:(my_x - 1) ~y1:9 ~x2:(my_x - 11) ~y2:9 scolorr; ++ mypic#line ~x1:my_x ~y1:9 ~x2:(my_x - 10) ~y2:9 scolorr; + mypic#line ~x1:my_x ~y1:8 ~x2:(my_x - 10) ~y2:8 gcolorr; + mypic#string ~font:Gd.Font.small ~x:(my_x - (((String.length titler) * 8)) - 2) ~y:2 ~s:titler tcolorr + +@@ -246,7 +240,7 @@ + let my_y = (ybb() - (ybs() / 2)) in + let fy x = int_of_float ((float_of_int my_y) -. (((float_of_int x) /. (y_fdivisions())) *. (float_of_int(ybs())))) in + let vtext n = (string_of_float (float_of_int(int_of_float((float_of_int (my_y - (fy n))) /. (vdt_stack g) /. 1024. *. 100.)) /. 100.)) in +- for n = 1 to ((y_divisions()) - 1) do ++ for n = 1 to ((y_divisions()) / 2) do + mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy n) - 12 + offset) ~s:(vtext n) gcolor; + done; + mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy (y_divisions())) - 5) ~s:(legend_text) lcolor +@@ -256,151 +250,145 @@ + let my_y = (ybb() - (ybs() / 2)) in + let fy x = int_of_float ((float_of_int my_y) +. (((float_of_int x) /. (y_fdivisions())) *. (float_of_int(ybs())))) in + let vtext n = (string_of_float (float_of_int(int_of_float((float_of_int ((fy n) - my_y)) /. (vdt_stack g) /. 1024. *. 100.)) /. 100.)) in +- for n = 1 to ((y_divisions()) - 1) do +- mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy n) - 12 + offset) ~s:(vtext n) gcolor; +- done; +- mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy (y_divisions())) - 5) ~s:(legend_text) lcolor ++ for n = 1 to ((y_divisions()) / 2) do ++ mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy n) - 12 + offset) ~s:(vtext n) gcolor; ++ done; ++ mypic#string ~font:Gd.Font.small ~x:(my_x + 5) ~y:((fy (y_divisions())) - 5) ~s:(legend_text) lcolor + +-let draw_h_legend mypic g legend_text gcolor my_time = ++let draw_h_legend mypic g legend_text gcolor my_time basetime show_days = + let my_x = (xbr()) in +- let my_x2 = (xbs()) in + let my_y = (ybb()) in +- let fx x = int_of_float((float_of_int my_x) -. (((float_of_int x) /. (x_fdivisions())) *. (float_of_int my_x2))) in +- (* and vtext n = (string_of_int (int_of_float((float_of_int(n))*. vx ))) in *) +- let basetime = Unix.gettimeofday () in +- let timer n = Unix.localtime (basetime -. ((float_of_int(n)) *. float_of_int(my_time) *. vx())) in ++ let fx x = my_x + 4 - (x * (xbs ()) / x_divisions ()) in + let time_string n = +- let time = timer n in +- let h0 = string_of_int(time.Unix.tm_hour ) and (* H *) +- m0 = string_of_int(time.Unix.tm_min ) and (* M *) +- s0 = string_of_int(time.Unix.tm_sec ) in (* S *) +- (if String.length h0 = 2 then h0 else "0"^h0) ^":"^ +- (if String.length m0 = 2 then m0 else "0"^m0) ^":"^ +- (if String.length s0 = 2 then s0 else "0"^s0) in +- +- for n = 1 to ((x_divisions()) - 1) do +- mypic#string ~font:Gd.Font.small ~x:((fx n) - ((String.length (time_string n) * 5) / 2)) ~y:(my_y + 5) ~s:(time_string n) gcolor; +- done; +- mypic#string ~font:Gd.Font.small ~x:((fx (x_divisions())) - ((String.length (legend_text) * 4) / 2)) ~y:(my_y + 5) ~s:(legend_text) gcolor ++ let time = Unix.localtime (basetime -. float_of_int(n * my_time / x_divisions ())) in ++ Printf.sprintf "%02d:%02d:%02d" time.Unix.tm_hour time.Unix.tm_min time.Unix.tm_sec ++ in ++ let day_string n = ++ let time = Unix.localtime (basetime -. float_of_int(n * my_time / x_divisions ())) in ++ Printf.sprintf "%02d.%02d." time.Unix.tm_mon time.Unix.tm_mday ++ in ++ if show_days then ++ begin ++ for n = 0 to (x_divisions() - 1) do ++ mypic#string ~font:Gd.Font.small ~x:((fx n) - (String.length (time_string n) * 3) ) ~y:(my_y + 1) ~s:(time_string n) gcolor; ++ mypic#string ~font:Gd.Font.small ~x:((fx n) - (String.length (day_string n) * 3) ) ~y:(my_y + 11) ~s:(day_string n) gcolor; ++ done; ++ mypic#string ~font:Gd.Font.small ~x:(4) ~y:(my_y + 2) ~s:(legend_text) gcolor; ++ mypic#string ~font:Gd.Font.small ~x:(4) ~y:(my_y + 11) ~s:("t(DD.MM.)") gcolor ++ end ++ else ++ begin ++ for n = 0 to (x_divisions() - 1) do ++ mypic#string ~font:Gd.Font.small ~x:((fx n) - (String.length (time_string n) * 3) ) ~y:(my_y + 5) ~s:(time_string n) gcolor; ++ done; ++ mypic#string ~font:Gd.Font.small ~x:(4) ~y:(my_y + 5) ~s:(legend_text) gcolor ++ end + +-let draw_load mypic g my_color shadow_color = ++let draw_load mypic g my_color shadow_color my_samples = + let my_x = (xbr()) in +- let my_x2 = (xbs()) in + let my_y = (ybb()) in ++ let my_s = min ((Fifo.length g)-1) my_samples in ++ let my_s2 = xbs() / my_samples / 4 in + let datas g n = List.nth (List.rev (Fifo.to_list g)) n in +- let fx x = int_of_float((float_of_int my_x) -. (((float_of_int x) /. (fxgdivisions())) *. (float_of_int my_x2))) ++ let fx x = my_x - (x * (xbs ()) / my_samples) + and y_c1 n = (my_y - (int_of_float(float_of_int(datas g n) *. (vdt_m g)))) + and y_c2 n = (my_y - (int_of_float(float_of_int(datas g (n+1)) *. (vdt_m g)))) in + (if !!html_mods_vd_gfx_fill then begin +- mypic#line ~x1:(fx 0) ~y1:my_y +- ~x2:(fx 0) ~y2:(if y_c1 0 >= my_y - 3 then +- ((y_c1 0) - 3) else ((y_c1 0))) shadow_color; +- for n = 0 to ((graph_length(g)) - 1) do +- (* trick to make sure filling will not fail *) +- if n = ((graph_length(g)) - 1) then +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y - 3 then +- ((y_c1 n) - 3) else ((y_c1 n))) ~x2:((fx (n+1))) ~y2:((y_c2 n)) shadow_color +- else begin +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y - 3 then +- ((y_c1 n) - 3) else ((y_c1 n))) +- ~x2:((fx (n+1))) ~y2:(if y_c2 n >= my_y - 3 then +- ((y_c2 n) - 3) else ((y_c2 n))) shadow_color +- end +- done; ++ if my_s2 = 0 then ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:(fx n) ~y1:(min (my_y-1)(y_c1 n)) ~x2:(fx(n+1)) ~y2:(min (my_y-2)(y_c2 n)) shadow_color ++ done ++ else ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1: (fx n) ~y1:(min (my_y - 1) (y_c1 n)) ~x2:((fx(n + 1)) + my_s2) ~y2:(min (my_y - 1) (y_c1 n)) shadow_color; ++ mypic#line ~x1:((fx(n + 1)) + my_s2) ~y1:(min (my_y - 1) (y_c1 n)) ~x2: (fx(n + 1)) ~y2:(min (my_y - 1) (y_c2 n)) shadow_color ++ done; ++ if (fx my_s) > (xbl + 1) then ++ mypic#line ~x1:(fx my_s) ~y1:(min (my_y - 1) (y_c1 my_s)) ~x2:(fx my_s) ~y2:(my_y) shadow_color; + mypic#fill ~x:(my_x - 1) ~y:(my_y - 1) my_color; + end + else begin +- for n = 0 to ((graph_length(g)) - 1) do +- mypic#line ~x1:((fx n) + 1) ~y1:((y_c1 n) + 1) ~x2:((fx (n+1)) + 1) ~y2:((y_c2 n) + 1) shadow_color; +- mypic#line ~x1:(fx n) ~y1:(y_c1 n) ~x2:(fx (n+1)) ~y2:(y_c2 n) my_color +- done; ++ if my_s2 = 0 then ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:((fx n)+1) ~y1:((y_c1 n)+1) ~x2:((fx (n+1))+1) ~y2:((y_c2 n)+1) shadow_color; ++ mypic#line ~x1:( fx n) ~y1:(y_c1 n) ~x2:( fx (n+1)) ~y2:(y_c2 n) my_color ++ done ++ else ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:((fx n) + 1) ~y1:((y_c1 n)+1) ~x2:((fx(n + 1)) + 1 + my_s2) ~y2:((y_c1 n) + 1) shadow_color; ++ mypic#line ~x1:((fx(n + 1)) + 1 + my_s2) ~y1:((y_c1 n)+1) ~x2:((fx(n + 1)) + 1) ~y2:((y_c2 n) + 1) shadow_color; ++ mypic#line ~x1: (fx n) ~y1: (y_c1 n) ~x2:((fx(n + 1)) + my_s2) ~y2: (y_c1 n) my_color; ++ mypic#line ~x1:((fx(n + 1)) + my_s2) ~y1: (y_c1 n) ~x2:( fx(n + 1)) ~y2: (y_c2 n) my_color ++ done; + end + ) + +-let draw_stack_download mypic g my_color shadow_color = ++let draw_stack_download mypic g my_color shadow_color my_samples = + let my_x = (xbr()) in + let my_x2 = (xbs()) in +- let my_y = (ybb() - (ybs() / 2)) in ++ let my_y = (ybt -1 + (ybs() / 2)) in ++ let my_s = min ((Fifo.length g)-1) my_samples in + let datas g n = List.nth (List.rev (Fifo.to_list g)) n in +- let fx x = int_of_float((float_of_int my_x) -. (((float_of_int x) /. (fxgdivisions())) *. (float_of_int my_x2))) +- and y_c1 n = (my_y - (int_of_float(float_of_int(datas g n) *. (vdt_stack g)))) +- and y_c2 n = (my_y - (int_of_float(float_of_int(datas g (n+1)) *. (vdt_stack g)))) in ++ let fx x = my_x - (x * my_x2 / my_samples) and ++ y_c1 n = my_y - int_of_float(float_of_int(datas g n) *. (vdt_stack g)) and ++ y_c2 n = my_y - int_of_float(float_of_int(datas g (n+1)) *. (vdt_stack g)) ++ in + (if !!html_mods_vd_gfx_fill then begin +- mypic#line ~x1:(fx 0) ~y1:my_y +- ~x2:(fx 0) ~y2:(if y_c1 0 >= my_y - 3 then +- ((y_c1 0) - 3) else ((y_c1 0))) shadow_color; +- for n = 0 to ((graph_length(g)) - 1) do +- (* trick to make sure filling will not fail *) +- if n = ((graph_length(g)) - 1) then +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y - 2 then +- ((y_c1 n) - 2) else ((y_c1 n))) ~x2:((fx (n+1))) ~y2:((y_c2 n)) shadow_color +- else begin +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y - 2 then +- ((y_c1 n) - 2) else ((y_c1 n))) +- ~x2:((fx (n+1))) ~y2:(if y_c2 n >= my_y - 2 then +- ((y_c2 n) - 2) else ((y_c2 n))) shadow_color +- end ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:(fx n) ~y1:(min (my_y-1) (y_c1 n)) ~x2:(fx(n+1)) ~y2:(min (my_y-1) (y_c2 n)) shadow_color + done; +- mypic#fill ~x:(my_x - 1) ~y:(my_y - 1) my_color; ++ if (fx my_s) > (xbl+1) then ++ mypic#line ~x1:(fx my_s) ~y1:(my_y) ~x2:(fx my_s) ~y2:(min (my_y) (y_c1 my_s)) shadow_color; ++ mypic#fill ~x:(my_x) ~y:(my_y) my_color; + end + else begin +- for n = 0 to ((graph_length(g)) - 1) do +- mypic#line ~x1:((fx n) + 1) ~y1:((y_c1 n) + 1) ~x2:((fx (n+1)) + 1) ~y2:((y_c2 n) + 1) shadow_color; ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:((fx n)+1) ~y1:((y_c1 n)+1) ~x2:((fx (n+1))+1) ~y2:((y_c2 n)+1) shadow_color; + mypic#line ~x1:(fx n) ~y1:(y_c1 n) ~x2:(fx (n+1)) ~y2:(y_c2 n) my_color + done; + end + ) + +-let draw_stack_upload mypic g my_color shadow_color = ++let draw_stack_upload mypic g my_color shadow_color my_samples = + let my_x = (xbr()) in + let my_x2 = (xbs()) in +- let my_y = (ybb() - (ybs() / 2)) in ++ let my_y = (ybt + 1 + (ybs() / 2)) in ++ let my_s = min ((Fifo.length g)-1) my_samples in + let datas g n = List.nth (List.rev (Fifo.to_list g)) n in +- let fx x = int_of_float((float_of_int my_x) -. (((float_of_int x) /. (fxgdivisions())) *. (float_of_int my_x2))) ++ let fx x = my_x - (x * my_x2 / my_samples) + and y_c1 n = (my_y + (int_of_float(float_of_int(datas g n) *. (vdt_stack g)))) + and y_c2 n = (my_y + (int_of_float(float_of_int(datas g (n+1)) *. (vdt_stack g)))) in + (if !!html_mods_vd_gfx_fill then begin +- mypic#line ~x1:(fx 0) ~y1:my_y +- ~x2:(fx 0) ~y2:(if y_c1 0 >= my_y - 3 then +- ((y_c1 0) - 3) else ((y_c1 0))) shadow_color; +- for n = 0 to ((graph_length(g)) - 1) do +- (* trick to make sure filling will not fail *) +- if n = ((graph_length(g)) - 1) then +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y + 2 then +- ((y_c1 n) + 2) else ((y_c1 n))) ~x2:((fx (n+1))) ~y2:((y_c2 n)) shadow_color +- else begin +- mypic#line ~x1:((fx n)) ~y1:(if y_c1 n >= my_y + 2 then +- ((y_c1 n) + 2) else ((y_c1 n))) +- ~x2:((fx (n+1))) ~y2:(if y_c2 n >= my_y + 2 then +- ((y_c2 n) + 2) else ((y_c2 n))) shadow_color +- end ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:(fx n) ~y1:(max (my_y+1) (y_c1 n)) ~x2:(fx(n+1)) ~y2:(max (my_y+1) (y_c2 n)) shadow_color + done; +- mypic#fill ~x:(my_x - 1) ~y:(my_y + 1) my_color; ++ if (fx my_s) > xbl+1 then ++ mypic#line ~x1:(fx my_s) ~y1:(my_y) ~x2:(fx my_s) ~y2:(max my_y (y_c1 my_s)) shadow_color; ++ mypic#fill ~x:(my_x) ~y:(my_y) my_color + end + else begin +- for n = 0 to ((graph_length(g)) - 1) do +- mypic#line ~x1:((fx n) + 1) ~y1:((y_c1 n) + 1) ~x2:((fx (n+1)) + 1) ~y2:((y_c2 n) + 1) shadow_color; +- mypic#line ~x1:(fx n) ~y1:(y_c1 n) ~x2:(fx (n+1)) ~y2:(y_c2 n) my_color ++ for n = 0 to my_s - 1 do ++ mypic#line ~x1:((fx n)+1) ~y1:(max (my_y+1)((y_c1 n)+1)) ~x2:((fx (n+1))+1) ~y2:(max (my_y+1)((y_c2 n)+1)) shadow_color; ++ mypic#line ~x1:( fx n) ~y1:(max (my_y+1)(y_c1 n)) ~x2:( fx (n+1)) ~y2:(max (my_y+1)(y_c2 n)) my_color + done; + end + ) + +- +-let draw_mean_line mypic g my_color gcolor = +- let my_x2 = (xbs()) +- and my_y = (ybb()) in ++let draw_mean_line mypic g my_color shadow_color tcolor = + let my_sum gl = List.fold_left (+) 0 (Fifo.to_list gl) in + let meanx() = ((float_of_int (my_sum g)) /. (float_of_int ((Fifo.length g)))) in +- let ypos = (my_y - (int_of_float((meanx()) *. (vdt_m g)))) in ++ let ypos = (ybb() - int_of_float(meanx() *. vdt_m g)) in + let vtext = (string_of_float (float_of_int(int_of_float(meanx() /. 1024. *. 100.)) /. 100.)) in +- mypic#line ~x1:xbl ~y1:(ypos - 1) ~x2:(xbl + my_x2 / 10) ~y2:(ypos - 1) my_color; +- mypic#line ~x1:xbl ~y1:(ypos) ~x2:(xbl + my_x2 / 10) ~y2:(ypos) gcolor; +- mypic#string ~font:Gd.Font.small ~x:(xbl + 5) ~y:((ypos) + 2) ~s:(vtext) gcolor ++ mypic#line ~x1:(xbl) ~y1:(ypos) ~x2:(xbl+1+ xbs() / x_divisions()) ~y2:(ypos) my_color; ++ if ypos+1 < ybb() then ++ mypic#line ~x1:(xbl) ~y1:(ypos+1) ~x2:(xbl+1+ xbs() / x_divisions()) ~y2:(ypos+1) shadow_color; ++ mypic#string ~font:Gd.Font.small ~x:(xbl) ~y:(ypos +2 ) ~s:(vtext) tcolor + + let draw_mygraph mypic ttl top_title vl hl g = + (* init pic *) + ( + let g_y = win_y() in ++ let mypic = Gd.create ~x:(win_x()) ~y:g_y in + (* set colors *) + let black = mypic#colors#black in + let red = mypic#colors#red in +@@ -412,11 +400,11 @@ + draw_title mypic ttl black g_y; + draw_top_legend mypic top_title black red darkgrey g_y; + draw_v_legend mypic g vl black black 0; +- draw_h_legend mypic g hl black samples_time; +- draw_load mypic g green darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic g hl black history_time !history_timeflag false; ++ draw_load mypic g green darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; +- (* draw_mean_line mypic gdown green black; *) ++ (* draw_mean_line mypic gdown green darkgrey black; *) + ) + + (* end of declarations *) +@@ -444,13 +432,13 @@ + draw_borders mypic black; + draw_title mypic ttl black win_y; + draw_v_legend mypic vl black black 0; +- draw_h_legend mypic gdown hl black samples_time; +- draw_load mypic gdown green darkgrey; +- draw_load mypic gup red darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic gdown hl black history_time !history_timeflag false; ++ draw_load mypic gdown green darkgrey history_size; ++ draw_load mypic gup red darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + +-(* draw_mean_line mypic gdown green black;*) ++ (* draw_mean_line mypic gdown green darkgrey black;*) + (* mypic#out_as_jpeg "line.jpg" ~quality:90; *) + + *) +@@ -477,16 +465,16 @@ + (* draw graph *) + draw_title mypic ttl black g_y; + draw_dual_top_legend mypic "download" black green darkgrey "upload" black red darkgrey g_y; +- draw_h_legend mypic gdown hl black samples_time; ++ draw_h_legend mypic gdown hl black history_time !history_timeflag false; + (if !!html_mods_vd_gfx_stack then begin + draw_stack_borders mypic black; + draw_stack_v_top_legend mypic gdown vl black darkgreen 5; + draw_stack_v_bottom_legend mypic gup vl black darkred 5; + (* enable filling for stack graph *) + if not !!html_mods_vd_gfx_fill then html_mods_vd_gfx_fill =:= true; +- draw_stack_download mypic gdown green darkgrey; +- draw_stack_upload mypic gup red darkgrey; +- draw_x_grid mypic black; ++ draw_stack_download mypic gdown green darkgrey history_size; ++ draw_stack_upload mypic gup red darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black + end + else begin +@@ -494,13 +482,13 @@ + draw_v_legend mypic gdown vl black darkgreen 0; + draw_v_legend mypic gup vl black darkred 10; + if !!html_mods_vd_gfx_fill then html_mods_vd_gfx_fill =:= false; +- draw_load mypic gdown green darkgrey; +- draw_load mypic gup red darkgrey; +- draw_x_grid mypic black; +- draw_y_grid mypic black; ++ draw_load mypic gdown green darkgrey history_size; ++ draw_load mypic gup red darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); ++ draw_y_grid mypic black; + (if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gdown green black; +- draw_mean_line mypic gup red black ++ draw_mean_line mypic gdown green darkgrey black; ++ draw_mean_line mypic gup red darkgrey black + ); + end); + draw_arrow mypic darkred; +@@ -514,9 +502,8 @@ + let do_draw_down_pic ttl top_title vl hl gdown = + (* init pic *) + ( +- let g_x = win_x() in + let g_y = win_y() in +- let mypic = Gd.create ~x:g_x ~y:g_y in ++ let mypic = Gd.create ~x:(win_x()) ~y:g_y in + + (* set colors *) + let white = mypic#colors#white in +@@ -532,13 +519,13 @@ + draw_title mypic ttl black g_y; + draw_top_legend mypic top_title black green darkgrey g_y; + draw_v_legend mypic gdown vl black black 6; +- draw_h_legend mypic gdown hl black samples_time; +- draw_load mypic gdown green darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic gdown hl black history_time !history_timeflag false; ++ draw_load mypic gdown green darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + draw_arrow mypic darkred; + if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gdown green black; ++ draw_mean_line mypic gdown green darkgrey black; + + (if !!html_mods_vd_gfx_png then + mypic#save_as_png "bw_download.png" +@@ -550,9 +537,8 @@ + let do_draw_up_pic ttl top_title vl hl gup = + (* init pic *) + ( +- let g_x = win_x() in + let g_y = win_y() in +- let mypic = Gd.create ~x:g_x ~y:g_y in ++ let mypic = Gd.create ~x:(win_x()) ~y:g_y in + + (* set colors *) + let white = mypic#colors#white in +@@ -568,13 +554,13 @@ + draw_title mypic ttl black g_y; + draw_top_legend mypic top_title black red darkgrey g_y; + draw_v_legend mypic gup vl black black 6; +- draw_h_legend mypic gup hl black samples_time; +- draw_load mypic gup red darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic gup hl black history_time !history_timeflag false; ++ draw_load mypic gup red darkgrey history_size; ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + draw_arrow mypic darkred; + if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gup red black; ++ draw_mean_line mypic gup red darkgrey black; + + (if !!html_mods_vd_gfx_png then + mypic#save_as_png "bw_upload.png" +@@ -588,7 +574,8 @@ + ( + let g_y = win_y() in + let mypic = Gd.create ~x:(win_x()) ~y:g_y in +- ++ let x_legend_days = (int_of_float !history_h_timeflag) mod 86400 < (x_h_time gdown) in ++ + (* set colors *) + let white = mypic#colors#white in + let black = mypic#colors#black in +@@ -606,16 +593,16 @@ + draw_dual_top_legend mypic "download" black green darkgrey "upload" black red darkgrey g_y; + draw_v_legend mypic gdown vl black darkgreen 0; + draw_v_legend mypic gup vl black darkred 10; +- draw_h_legend mypic gdown hl black samples_h_time; ++ draw_h_legend mypic gup hl black (x_h_time gdown) !history_h_timeflag x_legend_days; + if !!html_mods_vd_gfx_fill then html_mods_vd_gfx_fill =:= false; +- draw_load mypic gdown green darkgrey; +- draw_load mypic gup red darkgrey; +- draw_x_grid mypic black; ++ draw_load mypic gdown green darkgrey (x_h_values gdown); ++ draw_load mypic gup red darkgrey (x_h_values gdown); ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + draw_arrow mypic darkred; + (if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gdown green black; +- draw_mean_line mypic gup red black ++ draw_mean_line mypic gdown green darkgrey black; ++ draw_mean_line mypic gup red darkgrey black + ); + (if !!html_mods_vd_gfx_png then + mypic#save_as_png "bw_h_updown.png" +@@ -627,9 +614,9 @@ + let do_draw_down_h_pic ttl top_title vl hl gdown = + (* init pic *) + ( +- let g_x = win_x() in + let g_y = win_y() in +- let mypic = Gd.create ~x:g_x ~y:g_y in ++ let mypic = Gd.create ~x:(win_x()) ~y:g_y in ++ let x_legend_days = (int_of_float !history_h_timeflag) mod 86400 < (x_h_time gdown) in + + (* set colors *) + let white = mypic#colors#white in +@@ -645,13 +632,13 @@ + draw_title mypic ttl black g_y; + draw_top_legend mypic top_title black green darkgrey g_y; + draw_v_legend mypic gdown vl black black 6; +- draw_h_legend mypic gdown hl black samples_h_time; +- draw_load mypic gdown green darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic gdown hl black (x_h_time gdown) !history_h_timeflag x_legend_days; ++ draw_load mypic gdown green darkgrey (x_h_values gdown); ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + draw_arrow mypic darkred; + if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gdown green black; ++ draw_mean_line mypic gdown green darkgrey black; + + (if !!html_mods_vd_gfx_png then + mypic#save_as_png "bw_h_download.png" +@@ -663,9 +650,9 @@ + let do_draw_up_h_pic ttl top_title vl hl gup = + (* init pic *) + ( +- let g_x = win_x() in + let g_y = win_y() in +- let mypic = Gd.create ~x:g_x ~y:g_y in ++ let x_legend_days = (int_of_float !history_h_timeflag) mod 86400 < (x_h_time gup) in ++ let mypic = Gd.create ~x:(win_x()) ~y:g_y in + + (* set colors *) + let white = mypic#colors#white in +@@ -681,13 +668,13 @@ + draw_title mypic ttl black g_y; + draw_top_legend mypic top_title black red darkgrey g_y; + draw_v_legend mypic gup vl black black 6; +- draw_h_legend mypic gup hl black samples_h_time; +- draw_load mypic gup red darkgrey; +- draw_x_grid mypic black; ++ draw_h_legend mypic gup hl black (x_h_time gup) !history_h_timeflag x_legend_days; ++ draw_load mypic gup red darkgrey (x_h_values gup); ++ draw_x_grid mypic black darkgrey (x_divisions()); + draw_y_grid mypic black; + draw_arrow mypic darkred; + if !!html_mods_vd_gfx_mean then +- draw_mean_line mypic gup red black; ++ draw_mean_line mypic gup red darkgrey black; + + (if !!html_mods_vd_gfx_png then + mypic#save_as_png "bw_h_upload.png" +Index: src/daemon/driver/driverInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverInteractive.ml,v +retrieving revision 1.101 +retrieving revision 1.115 +diff -u -r1.101 -r1.115 +--- src/daemon/driver/driverInteractive.ml 7 Sep 2006 10:55:11 -0000 1.101 ++++ src/daemon/driver/driverInteractive.ml 21 Nov 2006 22:34:33 -0000 1.115 +@@ -21,6 +21,8 @@ + + open Printf2 + open CommonClient ++open CommonShared ++open CommonServer + open CommonNetwork + open CommonResult + open CommonFile +@@ -49,19 +51,11 @@ + lprintf2 log_prefix fmt + + let verify_user_admin () = +- let empty_pwd = ref false in +- begin try +- if user2_password admin_user = blank_password then +- empty_pwd := true +- with e -> +- lprintf_nl (_b "SECURITY INFO: user 'admin' has to be present, creating..."); +- empty_pwd := true; +- ignore (user2_add admin_user blank_password "") +- end; + let warning = + "SECURITY WARNING: user admin has an empty password, use command: useradd admin password\n" + in +- if !empty_pwd && not !!enable_user_config then ++ if has_empty_password admin_user && !!allowed_ips <> ++ [(Ip.range_of_string (strings_of_option allowed_ips).option_default)] then + begin + lprintf_n "%s" warning; + warning +@@ -83,6 +77,7 @@ + let s = + !startup_message ^ (verify_user_admin ()) ^ (check_supported_os ()) + ^ (if not !dns_works then "DNS resolution does not work\n" else "") ++ ^ (if not !Charset.conversion_enabled then "Charset conversion does not work, disabled\n" else "") + ^ (match !created_new_base_directory with + None -> "" + | Some dir -> (Printf.sprintf "MLDonkey created a new home directory in %s\n" dir)) +@@ -239,6 +234,8 @@ + then file_availability f + else string_availability f.file_availability + ++let number_of_comments f = ++ List.length f.file_comments + + (* WARNING: these computations are much more expensive as they seem. + We use the ShortLazy to avoid recomputing the result too many times, +@@ -575,7 +572,7 @@ + Printf.bprintf buf "\\</form\\>" + + +- let print_file_html_mods buf guifiles = ++let print_file_html_mods buf guifiles = + + if (List.length guifiles) > 0 then begin + let tsize = ref Int64.zero in +@@ -636,7 +633,7 @@ + \\<div class=main\\>"; + + if !!html_mods_use_js_tooltips then Printf.bprintf buf +-"\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:FFFFDD;color:black;border-color:black;border-width:20;font-size:8pt; visibility:show; left:25px; top: ++"\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top: + -100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; + + Printf.bprintf buf "\\<form id=\\\"selectForm\\\" name=\\\"selectForm\\\" action=\\\"files\\\"\\> +@@ -660,7 +657,8 @@ + + \\<td title=\\\"Pause\\\" class=\\\"dlheader np\\\"\\>P\\</td\\> + \\<td title=\\\"Resume\\\" class=\\\"dlheader np\\\"\\>R\\</td\\> +-\\<td title=\\\"Cancel\\\" class=\\\"dlheader brs\\\"\\>C\\</td\\>" ++\\<td title=\\\"Cancel\\\" class=\\\"dlheader brs\\\"\\>C\\</td\\> ++\\<td title=\\\"Click to switch release status\\\" class=\\\"dlheader brs\\\"\\>R\\</td\\>" + (if !qnum > 0 then begin + Printf.sprintf "title=\\\"Active(%d): %s/%s | Queued(%d): %s/%s\\\"" + (List.length guifiles - !qnum) (size_of_int64 (!tdl -- !qdl)) (size_of_int64 (!tsize -- !qsize)) +@@ -670,17 +668,24 @@ + (List.length guifiles) (size_of_int64 !tdl) (size_of_int64 !tsize) (!trate /. 1024.) + (let unread = ref 0 in + Fifo.iter (fun (t,i,num,n,s) -> if t > !last_message_log then incr unread) chat_message_fifo; +-if !unread > 0 then Printf.sprintf "\\<td class=downloaded title=\\\"%d unread messages\\\"\\>(+%d)\\ \\</td\\>" !unread !unread else ""); ++if !unread > 0 then Printf.sprintf "\\<td class=downloaded title=\\\"%d unread messages\\\"\\>\\<a onClick=\\\"mSub('fstatus','version');mSub('output','message')\\\"\\>(+%d)\\</a\\>\\ \\</td\\>" !unread !unread else ""); + + if !!html_mods_vd_network then Printf.bprintf buf + "\\<td title=\\\"Sort by network\\\" class=dlheader\\>\\<input style=\\\"padding-left: 0px; padding-right: 0px;\\\" class=headbutton type=submit value=N name=sortby\\>\\</td\\>"; + + Printf.bprintf buf +-"\\<td title=\\\"Sort by filename\\\" class=dlheader\\>\\<input class=headbutton type=submit value=File name=sortby\\>\\</td\\> +-\\<td title=\\\"Sort by size\\\" class=dlheader\\>\\<input class=headbutton type=submit value=Size name=sortby\\>\\</td\\> ++"\\<td title=\\\"Sort by filename\\\" class=dlheader\\>\\<input class=headbutton type=submit value=File name=sortby\\>\\</td\\>"; ++ ++if !!html_mods_vd_user then Printf.bprintf buf ++"\\<td title=\\\"Sort by user\\\" class=dlheader\\>\\<input class=headbutton type=submit value=User name=sortby\\>\\</td\\>"; ++ ++if !!html_mods_vd_group then Printf.bprintf buf ++"\\<td title=\\\"Sort by group\\\" class=dlheader\\>\\<input class=headbutton type=submit value=Group name=sortby\\>\\</td\\>"; ++ ++Printf.bprintf buf ++"\\<td title=\\\"Sort by size\\\" class=dlheader\\>\\<input class=headbutton type=submit value=Size name=sortby\\>\\</td\\> + \\<td title=\\\"Sort by size downloaded\\\" class=dlheader\\>\\<input class=\\\"headbutton ar\\\" type=submit value=DLed name=sortby\\>\\</td\\> + \\<td title=\\\"Sort by percent\\\" class=dlheader\\>\\<input class=headbutton type=submit value=%% name=sortby\\>\\</td\\>"; +- + if !!html_mods_vd_comments then Printf.bprintf buf + "\\<td title=\\\"Sort by comments\\\" class=dlheader\\>\\<input style=\\\"padding-left: 0px; padding-right: 0px;\\\" class=headbutton type=submit value=Cm name=sortby\\>\\</td\\>"; + +@@ -715,26 +720,36 @@ + [| + (if !!html_mods_use_js_tooltips then + Printf.sprintf " +- onMouseOver=\\\"mOvr(this);setTimeout('popLayer(\\\\\'%s<br>%sFile#: %d<br>Network: %s%s\\\\\')',%d);setTimeout('hideLayer()',%d);return true;\\\" onMouseOut=\\\"mOut(this);hideLayer();setTimeout('hideLayer()',%d)\\\"\\>" +- (Http_server.html_real_escaped file.file_name) ++ onMouseOver=\\\"mOvr(this);setTimeout('popLayer(\\\\\'%s<br>%sFile#: %d<br>Network: %s<br>User%s %s%s%s\\\\\')',%d);setTimeout('hideLayer()',%d);return true;\\\" onMouseOut=\\\"mOut(this);hideLayer();setTimeout('hideLayer()',%d)\\\"\\>" ++ (Http_server.html_real_escaped (Charset.to_utf8 file.file_name)) + (match file_magic (file_find file.file_num) with + None -> "" + | Some magic -> "File type: " ^ (Http_server.html_real_escaped magic) ^ "<br>") + file.file_num + (net_name file) +- (let comments = file_comment (file_find file.file_num) in +- if comments = [] then "" else ++ (if file.file_group = "none" then "" else ":Group") ++ file.file_user ++ (if file.file_group = "none" then "" else Printf.sprintf ":%s" file.file_group) ++ ++ (if file.file_comments = [] then "" else + begin +- let buf1 = Buffer.create 100 in +- Printf.bprintf buf1 "<br><br>Comments:<br>"; +- List.iter (fun s -> Printf.bprintf buf1 "%s<br>" (Http_server.html_real_escaped s)) comments; ++ let num_comments = number_of_comments file in ++ let buf1 = Buffer.create (!!max_comment_length * num_comments) in ++ Printf.bprintf buf1 "<br><br>Comments(%d):<br>" (num_comments); ++ let comments = ++ if List.length file.file_comments > 5 then ++ fst (List2.cut 5 file.file_comments) @ [Ip.null, "", 0, (_s "MLDonkey note: click file for more comments")] ++ else ++ file.file_comments ++ in ++ List.iter (fun (_,_,_,s) -> Printf.bprintf buf1 "%s<br>" (Http_server.html_real_escaped (Charset.to_utf8 s))) comments; + Buffer.contents buf1 + end) ++ + !!html_mods_js_tooltips_wait + !!html_mods_js_tooltips_timeout + !!html_mods_js_tooltips_wait +- else Printf.sprintf " +- onMouseOver=\\\"mOvr(this);return true;\\\" onMouseOut=\\\"mOut(this);\\\"\\>"); ++ else Printf.sprintf " onMouseOver=\\\"mOvr(this);return true;\\\" onMouseOut=\\\"mOut(this);\\\"\\>"); + + (if downloading file then + Printf.sprintf "\\<td class=\\\"dl al np\\\"\\>\\<input class=checkbox name=pause type=checkbox value=%d\\>\\</td\\> +@@ -749,6 +764,10 @@ + file.file_num + file.file_num); + ++ Printf.sprintf "\\<td onClick=\\\"location.href='files?%s=%d';return true;\\\" class=\\\"dl al brs\\\"\\>\\%s\\</td\\>" ++ (if file.file_release then "norelease" else "release") ++ file.file_num (if file.file_release then "R" else "-"); ++ + (if !!html_mods_vd_network then + Printf.sprintf "\\<td onClick=\\\"location.href='submit?q=vd+%d';return true;\\\" + title=\\\"%s\\\" class=\\\"dl al\\\"\\>%s\\</td\\>" +@@ -772,14 +791,14 @@ + (truncate ( (1. -. downloaded /. size) *. 100.)) + else + Printf.sprintf "\\<TD onClick=\\\"location.href='submit?q=vd+%d';return true;\\\" +- title=\\\"[File#: %d] [Net: %s]%s\\\" class=\\\"dl al\\\"\\>%s\\<br\\> ++ title=\\\"[File#: %d] [Net: %s] [Comments: %d]%s\\\" class=\\\"dl al\\\"\\>%s\\<br\\> + \\<table cellpadding=0 cellspacing=0 width=100%%\\>\\<tr\\> + \\<td class=\\\"loaded\\\" style=\\\"height:%dpx\\\" width=\\\"%d%%\\\"\\> \\</td\\> + \\<td class=\\\"remain\\\" style=\\\"height:%dpx\\\" width=\\\"%d%%\\\"\\> \\</td\\> + \\</tr\\>\\</table\\>\\</td\\>" + file.file_num + file.file_num +- (net_name file) ++ (net_name file) (number_of_comments file) + (if !!max_name_len < String.length file.file_name then " " ^ file.file_name else "") + (short_name file) + (!!html_vd_barheight) +@@ -788,14 +807,16 @@ + (truncate ( (1. -. downloaded /. size) *. 100.))); + ); + ++ (if !!html_mods_vd_user then ctd file.file_num file.file_user else ""); ++ (if !!html_mods_vd_group then ctd file.file_num file.file_group else ""); ++ + (ctd file.file_num (size_of_int64 file.file_size)); + (ctd file.file_num (size_of_int64 file.file_downloaded)); + (ctd file.file_num (Printf.sprintf "%.1f" (percent file))); + + (if !!html_mods_vd_comments then +- Printf.sprintf "\\<td onClick=\\\"location.href='submit?q=vd+%d';return true;\\\" +- class=\\\"dl al\\\"\\>%d\\</td\\>" +- file.file_num (file_comment_length (file_find file.file_num)) else ""); ++ ctd file.file_num (Printf.sprintf "%d" (number_of_comments file)) ++ else ""); + + (ctd file.file_num (Printf.sprintf "%d" (number_of_sources file))); + +@@ -936,7 +957,7 @@ + else + print_table buf + [| +- Align_Left; Align_Left; Align_Right; Align_Right; ++ Align_Left; Align_Left; Align_Right; Align_Left; Align_Left; Align_Left; + Align_Right; Align_Right; Align_Right |] + (if format.conn_output = HTML then + [| +@@ -951,6 +972,10 @@ + |] else + [| + "$nNum"; ++ "Rele"; ++ "Comm"; ++ "User"; ++ "Group"; + "File"; + " %"; + " Done"; +@@ -995,6 +1020,10 @@ + file.file_num + (if downloading file then "PAUSE" else "RESUME") + else "")); ++ (Printf.sprintf "%s" (if file.file_release then "R" else "-")); ++ (Printf.sprintf "%4d" (number_of_comments file)); ++ file.file_user; ++ file.file_group; + (short_name file); + (Printf.sprintf "%3.1f" (percent file)); + (if !!improved_telnet then (print_human_readable file file.file_downloaded) +@@ -1112,7 +1141,9 @@ + | ByLast -> (fun f1 f2 -> f1.file_last_seen >= f2.file_last_seen) + | ByNet -> (fun f1 f2 -> net_name f1 <= net_name f2) + | ByAvail -> (fun f1 f2 -> get_file_availability f1 >= get_file_availability f2) +- | ByComments -> (fun f1 f2 -> file_comment_length (file_find f1.file_num) >= file_comment_length (file_find f2.file_num)) ++ | ByComments -> (fun f1 f2 -> (number_of_comments f1) >= (number_of_comments f2)) ++ | ByUser -> (fun f1 f2 -> f1.file_user <= f2.file_user) ++ | ByGroup -> (fun f1 f2 -> f1.file_group <= f2.file_group) + | NotSorted -> raise Not_found + in + Sort.list sorter list +@@ -1158,7 +1189,7 @@ + let counter = ref 0 in + if use_html_mods o then + begin +- if !!html_mods_use_js_tooltips then Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:FFFFDD;color:black;border-color:black;border-width:20;font-size:8pt; visibility:show; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>\n"; ++ if !!html_mods_use_js_tooltips then Printf.bprintf buf "\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top:-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>\n"; + html_mods_table_header_colspan buf "resultsTable" "results" [ + ( "1", "0", "srh", "Network", "Network" ) ; + ( "1", "0", "srh", "File", "File (mouseover)" ) ; +@@ -1703,7 +1734,8 @@ + ( "0", "srh br", "Has search", "Search" ) ; + ( "0", "srh br", "Has chat", "Chat" ) ; + ( "0", "srh br", "Has rooms", "Rooms" ) ; +- ( "0", "srh", "Has multinet", "Multinet" ) ] ++ ( "0", "srh", "Has multinet", "Multinet" ) ; ++ ( "0", "srh", "Has porttest", "Porttest" ) ] + + let print_network_modules buf o = + let buf = o.conn_buf in +@@ -1718,6 +1750,10 @@ + if not (List.mem VirtualNetwork n.network_flags) then + try + let net_has e = if List.mem e n.network_flags then "yes" else "" in ++ let net_has_porttest () = ++ match network_porttest_result n with ++ PorttestNotAvailable -> "" ++ | _ -> "yes" in + Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ + ("", "sr br", n.network_name); +@@ -1728,7 +1764,8 @@ + ("", "sr br", net_has NetworkHasSearch); + ("", "sr br", net_has NetworkHasChat); + ("", "sr br", net_has NetworkHasRooms); +- ("", "sr" , net_has NetworkHasMultinet); ]; ++ ("", "sr br", net_has NetworkHasMultinet); ++ ("", "sr" , (net_has_porttest ())); ]; + Printf.bprintf buf "\\</tr\\>"; + with _ -> () + ); +@@ -1867,7 +1904,8 @@ + tack list + ( + "Features:\t", +- (if BasicSocket.has_threads () then "threads" else "no-threads") ^ ++ ("multiuser") ^ ++ (if BasicSocket.has_threads () then " threads" else " no-threads") ^ + (let s = Zlib.zlib_version_num () in + Printf.sprintf " zlib%s" (if s <> "" then "-" ^ s else "")) ^ + (if Autoconf.bzip2 then +@@ -1886,7 +1924,10 @@ + " gd(jpg)" + | _, false, false -> + " gd(neither jpg nor png ?)") ^ +- (if Autoconf.has_iconv then " iconv" else " no-iconv") ^ ++ (match Autoconf.has_iconv, !Charset.conversion_enabled with ++ | true, true -> " iconv(active)" ++ | true, false -> " iconv(inactive)" ++ | false, _ -> " no-iconv") ^ + (match Autoconf.magic, !Autoconf.magic_works with + | true, true -> " magic(active)" + | true, false -> " magic(inactive)" +@@ -1924,8 +1965,8 @@ + ( + "User:\t\t", + Printf.sprintf "%s (%s) - uptime: %s" +- o.conn_user.ui_user_name +- (if empty_password o.conn_user.ui_user_name then "Warning: empty Password" ++ o.conn_user.ui_user.user_name ++ (if has_empty_password o.conn_user.ui_user then "Warning: empty Password" + else "PW Protected") + (Date.time_to_string (last_time () - start_time) "verbose") + ); +@@ -2089,8 +2130,8 @@ + let len_dir = ref 9 in + let len_strategy = ref 29 in (* "shared (incoming_directories)" *) + List.iter ( fun (dir, strategy) -> +- len_dir := maxi !len_dir (String.length dir); +- len_strategy := maxi !len_strategy (String.length strategy) ++ len_dir := max !len_dir (String.length dir); ++ len_strategy := max !len_strategy (String.length strategy) + ) !list; + let fill_dir = String.make (!len_dir - 9) ' ' in + let fill_dir_line = String.make (!len_dir - 9) '-' in +@@ -2139,8 +2180,8 @@ + end + else + Printf.bprintf buf "%-*s|%-*s|%8s|%8s|%5s|%-s\n" +- (maxi !len_dir (!len_dir - String.length dir)) dir +- (maxi !len_strategy (!len_strategy - String.length strategy)) strategy ++ (max !len_dir (!len_dir - String.length dir)) dir ++ (max !len_strategy (!len_strategy - String.length strategy)) strategy + diskused diskfree percentfree filesystem + ) !list; + if html then +@@ -2186,11 +2227,11 @@ + if html then Printf.bprintf buf "\\</tr\\>\\</table\\>\\</div\\>\\</div\\>"; + Buffer.contents buf + +-let dllink_query_networks html url = ++let dllink_query_networks html url user = + let result = ref [] in + if not (networks_iter_until_true (fun n -> + try +- let s,r = network_parse_url n url in ++ let s,r = network_parse_url n url user in + if s = "" then + r + else +@@ -2208,7 +2249,7 @@ + else + dllink_print_result html url "Added link" !result + +-let dllink_parse html url = ++let dllink_parse html url user = + if (String2.starts_with url "http") then ( + let u = Url.of_string url in + let module H = Http_client in +@@ -2242,21 +2283,14 @@ + let concat_headers = + (List.fold_right (fun (n, c) t -> n ^ ": " ^ c ^ "\n" ^ t) headers "") + in +- ignore (dllink_query_networks html concat_headers) ++ ignore (dllink_query_networks html concat_headers user) + ); + dllink_print_result html url "Parsing HTTP url" []) + else + if (String2.starts_with url "ftp") then +- dllink_query_networks html (Printf.sprintf "Location: %s" url) ++ dllink_query_networks html (Printf.sprintf "Location: %s" url) user + else +- dllink_query_networks html url +- +-let print_command_result o buf result = +- if use_html_mods o then +- html_mods_table_one_row buf "serversTable" "servers" [ +- ("", "srh", result); ] +- else +- Printf.bprintf buf "%s" result ++ dllink_query_networks html url user + + module UnionFind = struct + type t = int array +@@ -2378,3 +2412,119 @@ + shorten fileinfo.file_name 80; + string_of_int nc |] + ) sorted_score_list) ++ ++let print_upstats o list server = ++ let buf = o.conn_buf in ++ if use_html_mods o then ++ begin ++ if !!html_mods_use_js_tooltips then Printf.bprintf buf ++"\\<div id=\\\"object1\\\" style=\\\"position:absolute; background-color:#FFFFDD;color:black;border-color:black;border-width:20px;font-size:8pt; visibility:visible; left:25px; top: ++-100px; z-index:+1\\\" onmouseover=\\\"overdiv=1;\\\" onmouseout=\\\"overdiv=0; setTimeout(\\\'hideLayer()\\\',1000)\\\"\\>\\ \\</div\\>"; ++ ++ Printf.bprintf buf "\\<div class=\\\"upstats\\\"\\>"; ++ match server with ++ None -> ++ html_mods_table_one_row buf "upstatsTable" "upstats" [ ++ ("", "srh", Printf.sprintf "Session: %s uploaded | Shared(%d): %s\n" ++ (size_of_int64 !upload_counter) !nshared_files (size_of_int64 !nshared_bytes)); ] ++ | Some s -> let info = server_info s in ++ html_mods_table_one_row buf "upstatsTable" "upstats" [ ++ ("", "srh", Printf.sprintf "%d files shared on %s (%s:%s)" ++ info.G.server_published_files info.G.server_name ++ (Ip.string_of_addr info.G.server_addr) ++ (string_of_int info.G.server_port)); ] ++ end ++ else ++ begin ++ Printf.bprintf buf "Upload statistics:\n"; ++ Printf.bprintf buf "Session: %s uploaded | Shared(%d): %s\n" ++ (size_of_int64 !upload_counter) !nshared_files (size_of_int64 !nshared_bytes) ++ end; ++ ++ if use_html_mods o then ++ html_mods_table_header buf "upstatsTable" "upstats" [ ++ ( "1", "srh", "Total file requests", "Reqs" ) ; ++ ( "1", "srh", "Total bytes sent", "Total" ) ; ++ ( "1", "srh", "Upload Ratio", "UPRatio" ) ; ++ ( "0", "srh", "Preview", "P" ) ; ++ ( "0", "srh", "Filename", "Filename" ); ++ ( "0", "srh", "Statistic links", "Stats" ); ++ ( "0", "srh", "Published on servers", "Publ" ) ] ++ else ++ begin ++ Printf.bprintf buf " Requests | Bytes | Uploaded | File\n"; ++ Printf.bprintf buf "----------+----------+----------+----------------------------------------------------\n"; ++ end; ++ ++ html_mods_cntr_init (); ++ let list = Sort.list (fun f1 f2 -> ++ (f1.impl_shared_requests = f2.impl_shared_requests && ++ f1.impl_shared_uploaded > f2.impl_shared_uploaded) || ++ (f1.impl_shared_requests > f2.impl_shared_requests ) ++ ) list in ++ ++ List.iter (fun impl -> ++ if use_html_mods o then ++ begin ++ let published = List.length impl.impl_shared_servers in ++ let ed2k = file_print_ed2k_link ++ (Filename.basename impl.impl_shared_codedname) ++ impl.impl_shared_size impl.impl_shared_id in ++ ++ Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"" (html_mods_cntr ()); ++ (if !!html_mods_use_js_tooltips then ++ Printf.bprintf buf " onMouseOver=\\\"mOvr(this);setTimeout('popLayer(\\\\\'%s<br>%s%s\\\\\')',%d);setTimeout('hideLayer()',%d);return true;\\\" onMouseOut=\\\"mOut(this);hideLayer();setTimeout('hideLayer()',%d)\\\"\\>" ++ (Http_server.html_real_escaped (Filename.basename (Charset.to_utf8 impl.impl_shared_codedname))) ++ (match impl.impl_shared_magic with ++ None -> "" ++ | Some magic -> "File type: " ^ (Http_server.html_real_escaped magic) ^ "<br>") ++ (if impl.impl_shared_servers = [] then "" else ++ Printf.sprintf "<br>Published on %d %s<br>%s" ++ published (if published = 1 then "server" else "servers") ++ (let listbuf = Buffer.create 100 in ++ List.iter (fun s -> let info = server_info s in ++ Printf.bprintf listbuf "%s (%s:%s%s)<br>" ++ info.server_name ++ (Ip.string_of_addr info.server_addr) ++ (string_of_int info.server_port) ++ (if info.server_realport <> 0 ++ then "(" ^ (string_of_int info.server_realport) ^ ")" else "") ++ ) impl.impl_shared_servers; ++ Buffer.contents listbuf)) ++ !!html_mods_js_tooltips_wait ++ !!html_mods_js_tooltips_timeout ++ !!html_mods_js_tooltips_wait ++ else Printf.bprintf buf " onMouseOver=\\\"mOvr(this);return true;\\\" onMouseOut=\\\"mOut(this);\\\"\\>"); ++ ++ let uploaded = Int64.to_float impl.impl_shared_uploaded in ++ let size = Int64.to_float impl.impl_shared_size in ++ ++ html_mods_td buf [ ++ ("", "sr ar", Printf.sprintf "%d" impl.impl_shared_requests); ++ ("", "sr ar", size_of_int64 impl.impl_shared_uploaded); ++ ("", "sr ar", Printf.sprintf "%5.1f" ( if size < 1.0 then 0.0 else (uploaded *. 100.) /. size)); ++ ("", "sr", Printf.sprintf "\\<a href=\\\"preview_upload?q=%d\\\"\\>P\\</a\\>" impl.impl_shared_num); ++ ("", "sr", (if impl.impl_shared_id = Md4.null then ++ (Filename.basename impl.impl_shared_codedname) ++ else ++ Printf.sprintf "\\<a href=\\\"%s\\\"\\>%s\\</a\\>" ++ ed2k (shorten (Filename.basename impl.impl_shared_codedname) !!max_name_len))); ++ ("", "sr", (if impl.impl_shared_id = Md4.null then "" else ++ Printf.sprintf "\\<a href=\\\"http://tothbenedek.hu/ed2kstats/ed2k?hash=%s\\\"\\>%s\\</a\\> ++\\<a href=\\\"http://ed2k.titanesel.ws/ed2k.php?hash=%s\\\"\\>%s\\</a\\> ++\\<a href=\\\"http://bitzi.com/lookup/ed2k:%s\\\"\\>%s\\</a\\>" ++ (Md4.to_string impl.impl_shared_id) "T1" ++ (Md4.to_string impl.impl_shared_id) "T2" ++ (Md4.to_string impl.impl_shared_id) "B")); ++ ("", "sr ar", Printf.sprintf "%d" published ) ]; ++ Printf.bprintf buf "\\</tr\\>\n"; ++ end ++ else ++ Printf.bprintf buf "%9d | %8s | %7s%% | %-50s\n" ++ (impl.impl_shared_requests) ++ (size_of_int64 impl.impl_shared_uploaded) ++ (Printf.sprintf "%3.1f" ((Int64.to_float impl.impl_shared_uploaded *. 100.) /. Int64.to_float impl.impl_shared_size)) ++ (shorten (Filename.basename impl.impl_shared_codedname) !!max_name_len) ++ ) list; ++ ++ if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>\\</div\\>" +Index: src/daemon/driver/driverInterface.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverInterface.ml,v +retrieving revision 1.52 +retrieving revision 1.60 +diff -u -r1.52 -r1.60 +--- src/daemon/driver/driverInterface.ml 12 Aug 2006 20:36:14 -0000 1.52 ++++ src/daemon/driver/driverInterface.ml 21 Nov 2006 22:34:33 -0000 1.60 +@@ -246,6 +246,8 @@ + + let send_update_file gui file_num update = + let file = file_find file_num in ++ if user2_can_view_file gui.gui_conn.conn_user.ui_user (file_owner file) (file_group file) then ++ begin + let impl = as_file_impl file in + let file_info = if update then + P.File_info (file_info file) +@@ -256,6 +258,7 @@ + impl.impl_file_last_seen) + in + gui_send gui file_info ++ end + + let send_update_user gui user_num update = + let user = user_find user_num in +@@ -428,7 +431,7 @@ + (File_add_source_event (file,c)) + :: gui.gui_events.gui_new_events + ) sources +- ) !!files; ++ ) (user2_filter_files !!files gui.gui_conn.conn_user.ui_user); + + List.iter (fun file -> + addevent gui.gui_events.gui_files (file_num file) true; +@@ -458,6 +461,7 @@ + end + ); + ++ if user2_can_view_uploads gui.gui_conn.conn_user.ui_user then + shared_iter (fun s -> + addevent gui.gui_events.gui_shared_files (shared_num s) true + ); +@@ -466,8 +470,11 @@ + gui.gui_events.gui_new_events <- ev :: gui.gui_events.gui_new_events + ) console_messages; + ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + gui_send gui ( + P.Options_info (simple_options "" downloads_ini)); ++ ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + networks_iter_all (fun r -> + List.iter (fun opfile -> + let prefix = r.network_shortname ^ "-" in +@@ -475,6 +482,7 @@ + gui_send gui (P.Options_info args)) r.network_config_file); + + (* Options panels defined in downloads.ini *) ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + List.iter (fun s -> + let section = section_name s in + List.iter (fun o -> +@@ -484,6 +492,7 @@ + ) (sections downloads_ini); + + (* Options panels defined in users.ini *) ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + List.iter (fun s -> + let section = section_name s in + List.iter (fun o -> +@@ -493,6 +502,7 @@ + ) (sections users_ini); + + (* Options panels defined in each plugin *) ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + networks_iter_all (fun r -> + let prefix = r.network_shortname ^ "-" in + List.iter (fun file -> +@@ -554,7 +564,7 @@ + (File_add_source_event (file,c)) + :: gui.gui_events.gui_new_events + ) (file_active_sources file) +- ) !!files; ++ ) (user2_filter_files !!files gui.gui_conn.conn_user.ui_user); + + end + +@@ -613,7 +623,7 @@ + ) list + + | P.SetOption (name, value) -> +- if user2_is_admin gui.gui_conn.conn_user.ui_user_name || !!enable_user_config then ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + CommonInteractive.set_fully_qualified_options name value + else + begin +@@ -644,6 +654,7 @@ + end + + | P.EnableNetwork (num, bool) -> ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + let n = network_find_by_num num in + if n.op_network_is_enabled () <> bool then + (try +@@ -670,7 +681,7 @@ + network_extend_search r s e) + + | P.KillServer -> +- if user2_is_admin gui.gui_conn.conn_user.ui_user_name then ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + CommonInteractive.clean_exit 0 + else + begin +@@ -689,7 +700,7 @@ + add_timer 60. (fun _ -> + gui_send gui (Search_waiting (search_num, 0)) + ); +- gui.gui_id_counter <- maxi gui.gui_id_counter search_num; ++ gui.gui_id_counter <- max gui.gui_id_counter search_num; + + let user = gui.gui_conn.conn_user in + let query = +@@ -720,7 +731,7 @@ + + | P.Download_query (filenames, num, force) -> + let r = find_result num in +- let files = result_download r filenames force in ++ let files = result_download r filenames force gui.gui_conn.conn_user.ui_user in + List.iter CommonInteractive.start_download files + + | P.ConnectMore_query -> +@@ -731,7 +742,7 @@ + if not (networks_iter_until_true + (fun n -> + try +- let s,r = network_parse_url n url in r ++ let s,r = network_parse_url n url gui.gui_conn.conn_user.ui_user in r + with e -> + lprintf "Exception %s for network %s\n" + (Printexc2.to_string e) (n.network_name); +@@ -765,11 +776,13 @@ + query_networks url + + | P.GetUploaders -> ++ if user2_can_view_uploads gui.gui_conn.conn_user.ui_user then + gui_send gui (P.Uploaders + (List2.tail_map (fun c -> client_num c) + (Intmap.to_list !uploaders))) + + | P.GetPending -> ++ if user2_can_view_uploads gui.gui_conn.conn_user.ui_user then + gui_send gui (P.Pending ( + List2.tail_map (fun c -> client_num c) + (Intmap.to_list !CommonUploads.pending_slots_map))) +@@ -778,13 +791,13 @@ + server_remove (server_find num) + + | P.SaveOptions_query list -> +- ++ if user2_is_admin gui.gui_conn.conn_user.ui_user then + List.iter (fun (name, value) -> + CommonInteractive.set_fully_qualified_options name value) list; + DriverInteractive.save_config () + + | P.RemoveDownload_query num -> +- file_cancel (file_find num) ++ file_cancel (file_find num) gui.gui_conn.conn_user.ui_user + + | P.ViewUsers num -> + let s = server_find num in +@@ -868,6 +881,7 @@ + client_connect c + + | P.DisconnectClient num -> ++ if user2_can_view_uploads gui.gui_conn.conn_user.ui_user then + let c = client_find num in + client_disconnect c + +@@ -905,9 +919,9 @@ + | P.SwitchDownload (num, resume) -> + let file = file_find num in + if resume then +- file_resume file ++ file_resume file gui.gui_conn.conn_user.ui_user + else +- file_pause file ++ file_pause file gui.gui_conn.conn_user.ui_user + + | P.FindFriend user -> + networks_iter (fun n -> +@@ -1026,7 +1040,7 @@ + + | NetworkMessage (num, s) -> + let n = network_find_by_num num in +- n.op_network_gui_message s ++ n.op_network_gui_message s gui.gui_conn.conn_user.ui_user + + | AddServer_query (num, ip, port) -> + let n = network_find_by_num num in +@@ -1036,7 +1050,7 @@ + let s = n.op_network_add_server (Ip.addr_of_ip ip) port in + server_connect s + | RefreshUploadStats -> +- ++ if user2_can_view_uploads gui.gui_conn.conn_user.ui_user then + shared_iter (fun s -> + update_shared_info s; + ) +@@ -1044,6 +1058,11 @@ + | P.GetVersion -> + gui_send gui (P.Version Autoconf.current_version) + ++ | P.GetStats num -> ++ let n = network_find_by_num num in ++ let l = n.op_network_stat_info_list () in ++ gui_send gui (P.Stats (num, l)) ++ + | P.GiftAttach (profile, version, client) -> + let user, pass = + try +@@ -1080,13 +1099,13 @@ + with + Failure s -> + gui_send gui (Console (Printf.sprintf "Failure: %s\n" s)) ++ | Torrent_can_not_be_used -> ++ gui_send gui (Console (Printf.sprintf "\nError: This torrent does not have valid tracker URLs\n")) ++ | Torrent_already_exists -> ++ gui_send gui (Console (Printf.sprintf "\nError: This torrent is already in download queue\n")) + | e -> +- let error_text = Printexc2.to_string e in +- if error_text = "BTInteractive.Torrent_can_not_be_used" then +- gui_send gui (Console (Printf.sprintf "\nError: This torrent does not have valid tracker URLs\n")) +- else +- gui_send gui (Console (Printf.sprintf "from_gui: exception %s for message %s\n" +- (Printexc2.to_string e) (GuiProto.string_of_from_gui t))) ++ gui_send gui (Console (Printf.sprintf "from_gui: exception %s for message %s\n" ++ (Printexc2.to_string e) (GuiProto.string_of_from_gui t))) + + let gui_events () = + { +Index: src/daemon/driver/driverMain.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/daemon/driver/driverMain.ml,v +retrieving revision 1.125 +retrieving revision 1.132 +diff -u -r1.125 -r1.132 +--- src/daemon/driver/driverMain.ml 5 Sep 2006 14:15:19 -0000 1.125 ++++ src/daemon/driver/driverMain.ml 19 Nov 2006 23:04:59 -0000 1.132 +@@ -84,7 +84,7 @@ + update_link_stats () + with e -> + lprintf_nl (_b "Exception %s") (Printexc2.to_string e)); +- (try ++ (try + CommonUploads.refill_upload_slots () + with e -> + lprintf_nl (_b "Exception %s") (Printexc2.to_string e)); +@@ -102,7 +102,8 @@ + | Some dir -> allowed_ips =:= !!allowed_ips + ); + +- if !!http_port <> 0 then begin try ++ if !!http_port <> 0 then begin ++ try + ignore (DriverControlers.create_http_handler ()); + with e -> + lprintf_nl (_b "Exception %s while starting HTTP interface") +@@ -218,7 +219,6 @@ + (try + Options.load downloads_ini; + Options.load users_ini; +- ignore (DriverInteractive.verify_user_admin ()); + DriverInteractive.hdd_check () + with e -> + lprintf_nl "Exception %s during options load" (Printexc2.to_string e); +@@ -341,15 +341,18 @@ + CommonGlobals.exit_properly 71 + end; + +- ( +- let hostname = "www.mldonkey.net" in +- try +- ignore(Ip.from_name hostname); +- DriverInteractive.dns_works := true +- with e -> +- lprintf (_b "\nDNS resolution does not work! Looking up %s failed with %s.") +- hostname (Printexc2.to_string e); +- lprintf " ++ ( let resolve_name hostname = ++ try ++ ignore (Ip.from_name hostname); ++ true ++ with _ -> false ++ in ++ let hostnames = ++ ["www.mldonkey.org"; "mldonkey.sf.net"; "www.mldonkey.net"; "www.google.com"] ++ in ++ DriverInteractive.dns_works := List.exists resolve_name hostnames; ++ ++ if not !DriverInteractive.dns_works then lprintf " + The core therefore is unable to get eDonkey serverlists and loading + .torrent files via dllink from websites is also impossible. + If you are using MLDonkey in a chroot environment you should +@@ -382,6 +385,9 @@ + end + end + ); ++ if not !Charset.conversion_enabled then ++ lprintf_nl (_b "Self-test failed, charset conversion disabled."); ++ + load_config (); + + add_infinite_option_timer download_sample_rate CommonFile.sample_timer; +@@ -399,7 +405,7 @@ + save_results =:= old_save_results; + end; + +- lprintf_nl (_b "Check http://www.mldonkey.net/ for updates"); ++ lprintf_nl (_b "Check http://www.mldonkey.org for updates"); + networks_iter (fun r -> network_load_complex_options r); + lprintf_nl (_b "enabling networks: "); + networks_iter (fun r -> +@@ -413,7 +419,7 @@ + lprintf_nl (_b "---- enabling interfaces ----"); + List.iter (fun (p,s) -> if p <> 0 then lprintf_nl "using port %d (%s)" p s) + (network_ports (network_find_by_name "Global Shares")); +- lprintf (_b "%sdisabled networks: ") (log_time ()); ++ lprintf (_b "%s[dMain] disabled networks: ") (log_time ()); + let found = ref false in + networks_iter_all (fun r -> + if not (network_is_enabled r) then +@@ -440,6 +446,23 @@ + add_infinite_timer 0.1 CommonUploads.upload_download_timer; + add_infinite_timer !!buffer_writes_delay (fun _ -> Unix32.flush ()); + ++ history_timeflag := (Unix.time()); ++ update_download_history (); ++ update_upload_history (); ++ history_h_timeflag := (Unix.time()); ++ update_h_download_history (); ++ update_h_upload_history (); ++ ++ add_infinite_timer (float_of_int history_step) (fun timer -> ++ history_timeflag := (Unix.time()); ++ update_download_history (); ++ update_upload_history ()); ++ ++ add_infinite_timer (float_of_int history_h_step) (fun timer -> ++ history_h_timeflag := (Unix.time()); ++ update_h_download_history (); ++ update_h_upload_history ()); ++ + if Autoconf.system = "mingw" then + add_infinite_timer 1. (fun timer -> + MlUnix.set_console_title (DriverInteractive.console_topic ())); +Index: src/gtk/gui/gui_messages.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/gui/gui_messages.ml,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -r1.7 -r1.8 +--- src/gtk/gui/gui_messages.ml 5 Aug 2005 00:56:13 -0000 1.7 ++++ src/gtk/gui/gui_messages.ml 25 Oct 2006 11:34:46 -0000 1.8 +@@ -493,7 +493,7 @@ + ======== + + Release: %s +-Authors: MLDonkey project, http://www.mldonkey.net/ ++Authors: MLDonkey project, http://www.mldonkey.org + + This documentation file is now obsolete. Read the FAQ instead (either + on the project WEB site or in the FAQ.html file). +@@ -503,9 +503,7 @@ + IRC channel: irc.freenode.net, chat #mldonkey + + Web sites: +-http://www.mldonkey.net/ Official site, bug reports +-http://www.mldonkeyworld.com/ English forum +-http://www.mldonkey.org/ German forum ++http://www.mldonkey.org Official site + + Mailing-lists: + mldonkey-users@nongnu.org +Index: src/gtk/newgui/gui_friends.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/newgui/gui_friends.ml,v +retrieving revision 1.17 +retrieving revision 1.19 +diff -u -r1.17 -r1.19 +--- src/gtk/newgui/gui_friends.ml 21 Aug 2006 10:45:51 -0000 1.17 ++++ src/gtk/newgui/gui_friends.ml 31 Oct 2006 15:40:05 -0000 1.19 +@@ -72,7 +72,7 @@ + if len > maxlen then + (String.sub s 0 (maxlen-3)) ^ "..." + else if s = "" then +- "http://www.mldonkey.net/" ++ "http://www.mldonkey.org/" + else s + + let state_pix state = +@@ -430,6 +430,7 @@ + client_chat_port = 0; + client_connect_time = c.gclient_connect_time; + client_software = c.gclient_software; ++ client_os = None; + client_release = c.gclient_release; + client_emulemod = c.gclient_emulemod; + client_downloaded = c.gclient_downloaded; +@@ -637,6 +638,7 @@ + client_chat_port = 0; + client_connect_time = c.gclient_connect_time; + client_software = c.gclient_software; ++ client_os = None; + client_release = c.gclient_release; + client_emulemod = c.gclient_emulemod; + client_downloaded = c.gclient_downloaded; +Index: src/gtk/newgui/gui_installer_base.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/newgui/gui_installer_base.ml,v +retrieving revision 1.5 +retrieving revision 1.6 +diff -u -r1.5 -r1.6 +--- src/gtk/newgui/gui_installer_base.ml 1 Nov 2004 11:22:59 -0000 1.5 ++++ src/gtk/newgui/gui_installer_base.ml 12 Nov 2006 12:42:55 -0000 1.6 +@@ -0,0 +1 @@ ++let () = () +Index: src/gtk/newgui/gui_main.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/newgui/gui_main.ml,v +retrieving revision 1.18 +retrieving revision 1.19 +diff -u -r1.18 -r1.19 +--- src/gtk/newgui/gui_main.ml 27 Jun 2006 10:38:35 -0000 1.18 ++++ src/gtk/newgui/gui_main.ml 31 Oct 2006 15:40:05 -0000 1.19 +@@ -460,6 +460,7 @@ + | GiftServerAttach _ + | GiftServerStats _ -> assert false + | Version _ ++ | Stats (_, _) + | Search _ -> () + + with e -> +Index: src/gtk/newgui/gui_messages.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/newgui/gui_messages.ml,v +retrieving revision 1.12 +retrieving revision 1.13 +diff -u -r1.12 -r1.13 +--- src/gtk/newgui/gui_messages.ml 27 Jun 2006 10:38:35 -0000 1.12 ++++ src/gtk/newgui/gui_messages.ml 25 Oct 2006 11:34:46 -0000 1.13 +@@ -1645,7 +1645,7 @@ + ======== + + Release: %s +-Authors: MLDonkey project, http://www.mldonkey.net/ ++Authors: MLDonkey project, http://www.mldonkey.org + + This documentation file is now obsolete. Read the FAQ instead (either + on the project WEB site or in the FAQ.html file). +@@ -1655,9 +1655,7 @@ + IRC channel: irc.freenode.net, chat #mldonkey + + Web sites: +-http://www.mldonkey.net/ Official site, bug reports +-http://www.mldonkeyworld.com/ English forum +-http://www.mldonkey.org/ German forum ++http://www.mldonkey.org Official site + + Mailing-lists: + mldonkey-users@nongnu.org +Index: src/gtk/newgui/gui_misc.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk/newgui/gui_misc.ml,v +retrieving revision 1.6 +retrieving revision 1.7 +diff -u -r1.6 -r1.7 +--- src/gtk/newgui/gui_misc.ml 16 Oct 2005 20:42:52 -0000 1.6 ++++ src/gtk/newgui/gui_misc.ml 25 Oct 2006 11:34:46 -0000 1.7 +@@ -44,7 +44,7 @@ + + let short_name n = + let n = if n="" then +- "http://www.mldonkey.net/" ++ "http://www.mldonkey.org" + else n + in + let len = String.length n in +Index: src/gtk2/gui/guiMessages.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk2/gui/guiMessages.ml,v +retrieving revision 1.12 +retrieving revision 1.13 +diff -u -r1.12 -r1.13 +--- src/gtk2/gui/guiMessages.ml 1 Sep 2006 16:22:14 -0000 1.12 ++++ src/gtk2/gui/guiMessages.ml 25 Oct 2006 11:34:46 -0000 1.13 +@@ -1258,7 +1258,7 @@ + ======== + + Release: %s +-Authors: MLDonkey project, http://www.mldonkey.net/ ++Authors: MLDonkey project, http://www.mldonkey.org/ + + This documentation file is now obsolete. Read the FAQ instead (either + on the project WEB site or in the FAQ.html file). +@@ -1268,9 +1268,7 @@ + IRC channel: irc.freenode.net, chat #mldonkey + + Web sites: +-http://www.mldonkey.net/ Official site, bug reports +-http://www.mldonkeyworld.com/ English forum +-http://www.mldonkey.org/ German forum ++http://www.mldonkey.org Official site + + Mailing-lists: + mldonkey-users@nongnu.org +Index: src/gtk2/gui/guiRooms.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk2/gui/guiRooms.ml,v +retrieving revision 1.3 +retrieving revision 1.4 +diff -u -r1.3 -r1.4 +--- src/gtk2/gui/guiRooms.ml 12 Nov 2005 11:16:36 -0000 1.3 ++++ src/gtk2/gui/guiRooms.ml 9 Nov 2006 21:32:26 -0000 1.4 +@@ -524,7 +524,7 @@ + let find_user_name user_num = + try + let u = Hashtbl.find G.users user_num in +- u.user_name ++ u.GuiTypes.user_name + with _ -> raise Not_found + + let message_from_server s = +Index: src/gtk2/gui/guiStarter.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk2/gui/guiStarter.ml,v +retrieving revision 1.3 +retrieving revision 1.4 +diff -u -r1.3 -r1.4 +--- src/gtk2/gui/guiStarter.ml 31 Oct 2005 18:34:02 -0000 1.3 ++++ src/gtk2/gui/guiStarter.ml 6 Nov 2006 18:06:08 -0000 1.4 +@@ -22,6 +22,17 @@ + let _s_ x = (_s x) ^ ":" + + let main () = ++ let arg_1 = ++ try ++ Sys.argv.(1) ++ with _ -> "" ++ in ++ if not (Sys.file_exists arg_1) then ++ begin ++ if arg_1 <> "" then Printf.printf "File %s not found\n%!" arg_1; ++ Printf.printf "Syntax: mlguistarter FILE\n%!"; ++ exit 0 ++ end; + ignore (GMain.Main.init ()); + let window = GWindow.window + ~title:(_s "MLdonkey GUI starter") +@@ -57,7 +68,7 @@ + ignore (wb_5#connect#clicked ~callback: + (fun () -> + window#destroy (); +- ignore (Sys.command (Printf.sprintf "%s &" Sys.argv.(1))); ++ ignore (Sys.command (Printf.sprintf "%s &" arg_1)); + )); + ignore (wb_6#connect#clicked ~callback: + (fun () -> +Index: src/gtk2/gui/guiUsers.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/gtk2/gui/guiUsers.ml,v +retrieving revision 1.3 +retrieving revision 1.4 +diff -u -r1.3 -r1.4 +--- src/gtk2/gui/guiUsers.ml 12 Nov 2005 11:16:36 -0000 1.3 ++++ src/gtk2/gui/guiUsers.ml 9 Nov 2006 21:32:26 -0000 1.4 +@@ -184,7 +184,7 @@ + (*************************************************************************) + + method from_item row (u : user_info) = +- store#set ~row ~column:user_name (U.utf8_of u.user_name); ++ store#set ~row ~column:user_name (U.utf8_of u.GuiTypes.user_name); + store#set ~row ~column:user_ip_port (Mi.ip_to_string u.user_ip u.user_port); + store#set ~row ~column:user_md4 (Md4.to_string u.user_md4); + store#set ~row ~column:user_tags (Mi.tags_to_string u.user_tags) +@@ -262,7 +262,7 @@ + let u1 = user_of_key k1 in + let u2 = user_of_key k2 in + match c with +- Col_user_name -> compare (String.lowercase u1.user_name) (String.lowercase u2.user_name) ++ Col_user_name -> compare (String.lowercase u1.GuiTypes.user_name) (String.lowercase u2.GuiTypes.user_name) + | Col_user_addr -> compare u1.user_ip u2.user_ip + | Col_user_tags -> compare u1.user_tags u2.user_tags + | Col_user_md4 -> compare u1.user_md4 u2.user_md4 +Index: src/networks/bittorrent/bTClients.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTClients.ml,v +retrieving revision 1.77 +retrieving revision 1.84 +diff -u -r1.77 -r1.84 +--- src/networks/bittorrent/bTClients.ml 16 Sep 2006 09:47:17 -0000 1.77 ++++ src/networks/bittorrent/bTClients.ml 5 Nov 2006 14:09:38 -0000 1.84 +@@ -131,14 +131,24 @@ + in + + let enabled_trackers = +- let enabled_trackers = List.filter (fun t -> t.tracker_enabled) file.file_trackers in ++ let enabled_trackers = List.filter (fun t -> tracker_is_enabled t) file.file_trackers in + if enabled_trackers <> [] then enabled_trackers + else begin + (* if there is no tracker left, do something ? *) + if !verbose_msg_servers then +- lprintf_nl "No trackers left, reenabling all of them..."; +- List.iter (fun t -> t.tracker_enabled <- can_handle_tracker t) file.file_trackers; +- file.file_trackers ++ lprintf_nl "No trackers left for %s, reenabling all of them..." (file_best_name (as_file file)); ++ List.iter (fun t -> ++ match t.tracker_status with ++ (* only re-enable after normal error *) ++ Disabled _ -> t.tracker_status <- Enabled ++ | _ -> ()) file.file_trackers; ++ let enabled_trackers = List.filter (fun t -> t.tracker_status = Enabled) file.file_trackers in ++ if enabled_trackers = [] && (file_state file) <> FilePaused then ++ begin ++ file_pause (as_file file) CommonUserDb.admin_user; ++ lprintf_file_nl (as_file file) "Paused %s, no usable trackers left" (file_best_name (as_file file)) ++ end; ++ file.file_trackers; + end in + + List.iter (fun t -> +@@ -155,13 +165,12 @@ + t.tracker_last_conn + !!min_tracker_reask_interval < last_time() )) + then + begin +- (* if we already tried to connect but failed, remove tracker *) ++ (* if we already tried to connect but failed, disable tracker, but allow re-enabling *) + if file.file_tracker_connected && t.tracker_last_clients_num = 0 && + t.tracker_last_conn < 1 then begin + if !verbose_msg_servers then + lprintf_nl "Request error from tracker: disabling %s" t.tracker_url; +- t.tracker_enabled <- false; +- (* remove_tracker t.tracker_url file *) ++ t.tracker_status <- Disabled (intern "MLDonkey: Request error from tracker") + end + (* Send request to tracker *) + else begin +@@ -205,6 +214,15 @@ + t.tracker_url (t.tracker_interval - (last_time () - t.tracker_last_conn)) file.file_name + ) enabled_trackers + ++let start_upload c = ++ set_client_upload (as_client c) (as_file c.client_file); ++ set_client_has_a_slot (as_client c) NormalSlot; ++ Rate.update_no_change c.client_downloaded_rate; ++ Rate.update_no_change c.client_upload_rate; ++ c.client_last_optimist <- last_time(); ++ client_enter_upload_queue (as_client c); ++ send_client c Unchoke ++ + (** In this function we decide which peers will be + uploaders. We send a choke message to current uploaders + that are not in the next uploaders list. We send Unchoke +@@ -216,21 +234,14 @@ + (*Send choke if a current_uploader is not in next_uploaders*) + List.iter ( fun c -> if ((List.mem c !next_uploaders)==false) then + begin +- set_client_has_a_slot (as_client c) false; ++ set_client_has_a_slot (as_client c) NoSlot; + (*we will let him finish his download and choke him on next_request*) + end + ) !current_uploaders; + + (*don't send Choke if new uploader is already an uploaders *) +- List.iter ( fun c -> if ((List.mem c !current_uploaders)==false) then +- begin +- set_client_has_a_slot (as_client c) true; +- Rate.update_no_change c.client_downloaded_rate; +- Rate.update_no_change c.client_upload_rate; +- c.client_last_optimist <- last_time(); +- client_enter_upload_queue (as_client c); +- send_client c Unchoke; +- end ++ List.iter ( fun c -> ++ if not (List.mem c !current_uploaders) then start_upload c + ) !next_uploaders; + current_uploaders := !next_uploaders + +@@ -322,7 +333,7 @@ + if not ( !must_keep && (client_has_a_slot (as_client c) || c.client_interested)) then + begin + if !verbose_msg_clients then +- lprintf_file_nl file "disconnect since download is finished"; ++ lprintf_file_nl (as_file file) "disconnect since download is finished"; + disconnect_client c Closed_by_user + end + ) file.file_clients +@@ -414,6 +425,17 @@ + + let counter = ref 0 + ++let parse_reserved rbits c = ++ let has_bit pos h = Char.code rbits.[pos] land h <> 0 in ++ ++ c.client_dht <- has_bit 7 0x01; ++ c.client_cache_extension <- has_bit 7 0x02; ++ c.client_fast_extension <- has_bit 7 0x04; ++ ++ c.client_utorrent_extension <- has_bit 5 0x10; ++ ++ c.client_azureus_messaging_protocol <- has_bit 0 0x80 ++ + + (** This function is called to parse the first message that + a client send. +@@ -428,7 +450,7 @@ + (* removed: @param peer_id The hash (sha1) of the client. (Should be checked) + *) + let rec client_parse_header counter cc init_sent gconn sock +- (proto, file_id) = ++ (proto, rbits, file_id) = + try + set_lifetime sock 600.; + if !verbose_msg_clients then +@@ -436,12 +458,12 @@ + + let file = Hashtbl.find files_by_uid file_id in + if !verbose_msg_clients then +- lprintf_file_nl file "file found"; ++ lprintf_file_nl (as_file file) "file found"; + let c = + match !cc with + None -> + let c = new_client file Sha1.null (TcpBufferedSocket.peer_addr sock) in +- if !verbose_connect then lprintf_file_nl file "Client %d: incoming connection" (client_num c); ++ if !verbose_connect then lprintf_file_nl (as_file file) "Client %d: incoming connection" (client_num c); + cc := Some c; + c + | Some c -> +@@ -482,6 +504,8 @@ + (Ip.to_string ip) port; + end; + ++ parse_reserved rbits c; ++ + (match c.client_sock with + NoConnection -> + if !verbose_msg_clients then begin +@@ -603,9 +627,8 @@ + + let num, x,y, r = + +- if !verbose_msg_clients then begin +- lprintf_file_nl file "CLIENT %d: Finding new range to send" (client_num c); +- end; ++ if !verbose_msg_clients then ++ lprintf_file_nl (as_file file) "CLIENT %d: Finding new range to send" (client_num c); + + if !verbose_swarming then begin + lprintf_n "Current download:\n Current chunks: "; +@@ -638,7 +661,7 @@ + + lprint_newline (); + +- lprintf_file_nl file "Finding Range:"; ++ lprintf_file_nl (as_file file) "Finding Range:"; + end; + + try +@@ -653,7 +676,7 @@ + + | None -> + +- if !verbose_swarming then lprintf_file_nl file "No block"; ++ if !verbose_swarming then lprintf_file_nl (as_file file) "No block"; + update_client_bitmap c; + (try CommonSwarming.verify_one_chunk swarmer with _ -> ()); + (*Find a free block in the swarmer*) +@@ -707,7 +730,7 @@ + number. Only matters with merged downloads, and even then other + clients didn't seem to care (?), so the bug remained hidden *) + if !verbose_swarming then +- lprintf_file_nl file "Asking %d For Range %Ld-%Ld" chunk x y; ++ lprintf_file_nl (as_file file) "Asking %d For Range %Ld-%Ld" chunk x y; + + chunk, x -- file.file_piece_size ** Int64.of_int chunk, y -- x, r + +@@ -742,12 +765,12 @@ + send_client c (Request (num,x,y)); + + if !verbose_msg_clients then +- lprintf_file_nl file "CLIENT %d: Asking %s For Range %Ld-%Ld" ++ lprintf_file_nl (as_file file) "CLIENT %d: Asking %s For Range %Ld-%Ld" + (client_num c) (Sha1.to_string c.client_uid) x y + + with Not_found -> + if not (CommonSwarming.check_finished swarmer) && !verbose_download then +- lprintf_file_nl file "BTClient.get_from_client ERROR: can't find a block to download and file is not yet finished for file : %s..." file.file_name ++ lprintf_file_nl (as_file file) "BTClient.get_from_client ERROR: can't find a block to download and file is not yet finished for file : %s..." file.file_name + + + (** In this function we match a message sent by a client +@@ -799,10 +822,10 @@ + + if !verbose_msg_clients then + (match c.client_ranges_sent with +- [] -> lprintf_file_nl file "EMPTY Ranges !!!" ++ [] -> lprintf_file_nl (as_file file) "EMPTY Ranges !!!" + | (p1,p2,r) :: _ -> + let (x,y) = CommonSwarming.range_range r in +- lprintf_file_nl file "Current range from %s : %Ld [%d] (asked %Ld-%Ld[%Ld-%Ld])" ++ lprintf_file_nl (as_file file) "Current range from %s : %Ld [%d] (asked %Ld-%Ld[%Ld-%Ld])" + (brand_to_string c.client_brand) position len + p1 p2 x y + ); +@@ -822,10 +845,10 @@ + Rate.update c.client_downloaded_rate (float_of_int len); + if !verbose_msg_clients then + (match c.client_ranges_sent with +- [] -> lprintf_file_nl file "EMPTY Ranges !!!" ++ [] -> lprintf_file_nl (as_file file) "EMPTY Ranges !!!" + | (p1,p2,r) :: _ -> + let (x,y) = CommonSwarming.range_range r in +- lprintf_file_nl file "Received %Ld [%d] %Ld-%Ld[%Ld-%Ld] -> %Ld" ++ lprintf_file_nl (as_file file) "Received %Ld [%d] %Ld-%Ld[%Ld-%Ld] -> %Ld" + position len + p1 p2 x y + (new_downloaded -- old_downloaded) +@@ -852,12 +875,7 @@ + *) + current_uploaders := c::(!current_uploaders); + c.client_sent_choke <- false; +- set_client_has_a_slot (as_client c) true; +- Rate.update_no_change c.client_downloaded_rate; +- Rate.update_no_change c.client_upload_rate; +- c.client_last_optimist <- last_time(); +- client_enter_upload_queue (as_client c); +- send_client c Unchoke; ++ start_upload c + end; + + (* Check if the client is still interesting for us... *) +@@ -865,6 +883,7 @@ + + | PeerID p -> + (* Disconnect if that is ourselves. *) ++ c.client_uid <- Sha1.direct_of_string p; + if not (c.client_uid = !!client_uid) then + begin + let brand, release = parse_software p in +@@ -873,7 +892,6 @@ + (* TODO : enable it + c.client_release <- (parse_release p c.client_brand); + *) +- c.client_uid <- Sha1.direct_of_string p; + + if (List.length !current_uploaders < (!!max_bt_uploaders-1)) && + (List.mem c (!current_uploaders)) == false then +@@ -882,12 +900,7 @@ + don't miss the opportunity if we can *) + current_uploaders := c::(!current_uploaders); + c.client_sent_choke <- false; +- set_client_has_a_slot (as_client c) true; +- Rate.update_no_change c.client_downloaded_rate; +- Rate.update_no_change c.client_upload_rate; +- c.client_last_optimist <- last_time(); +- client_enter_upload_queue (as_client c); +- send_client c Unchoke; ++ start_upload c + end + else + begin +@@ -911,7 +924,7 @@ + let nbits = String.length p * 8 in + + if nbits < npieces then begin +- lprintf_file_nl file "Error: expected bitfield of atleast %d but got %d" npieces nbits; ++ lprintf_file_nl (as_file file) "Error: expected bitfield of atleast %d but got %d" npieces nbits; + disconnect_client c (Closed_for_error "Wrong bitfield length") + end else begin + +@@ -934,7 +947,7 @@ + send_interested c; + + if !verbose_msg_clients then +- lprintf_file_nl file "New BitField Registered"; ++ lprintf_file_nl (as_file file) "New BitField Registered"; + + (* for i = 1 to max_range_requests - List.length c.client_ranges do + (try get_from_client sock c with _ -> ()) +@@ -1003,7 +1016,7 @@ + (* Afaik this is no protocol violation and happens if the client + didn't send a client bitmap after the handshake. *) + let (ip,port) = c.client_host in +- if !verbose_msg_clients then lprintf_file_nl file "%s:%d with software %s : Choke send, but no client bitmap" ++ if !verbose_msg_clients then lprintf_file_nl (as_file file) "%s:%d with software %s : Choke send, but no client bitmap" + (Ip.to_string ip) port (brand_to_string c.client_brand) + | Some up -> + CommonSwarming.clear_uploader_intervals up +@@ -1068,10 +1081,10 @@ + c.client_upload_requests <- List2.remove_first (n, pos, len) c.client_upload_requests + else + if !verbose_msg_clients then +- lprintf_file_nl file "Error: received cancel request but client has no slot" ++ lprintf_file_nl (as_file file) "Error: received cancel request but client has no slot" + + with e -> +- lprintf_file_nl file "Error %s while handling MESSAGE: %s" (Printexc2.to_string e) (TcpMessages.to_string msg) ++ lprintf_file_nl (as_file file) "Error %s while handling MESSAGE: %s" (Printexc2.to_string e) (TcpMessages.to_string msg) + + + (** The function used to connect to a client. +@@ -1136,7 +1149,7 @@ + let file = c.client_file in + + if !verbose_msg_clients then +- lprintf_file_nl file "READY TO DOWNLOAD FILE"; ++ lprintf_file_nl (as_file file) "READY TO DOWNLOAD FILE"; + + send_init !!client_uid file.file_id sock; + (* Fabrice: Initialize the client bitmap and uploader fields to <> None *) +@@ -1284,7 +1297,7 @@ + with _ -> ()) + with e -> + if !verbose_connect then +- lprintf_file_nl file "Exception %s in resume_clients" (Printexc2.to_string e) ++ lprintf_file_nl (as_file file) "Exception %s in resume_clients" (Printexc2.to_string e) + ) file.file_clients + + (** Check if the value replied by the tracker is correct. +@@ -1321,13 +1334,13 @@ + let tracker_reply = + try + File.to_string filename +- with e -> lprintf_file_nl file "Empty reply from tracker"; "" ++ with e -> lprintf_file_nl (as_file file) "Empty reply from tracker"; "" + in + let v = + match tracker_reply with + | "" -> + if !verbose_connect then +- lprintf_file_nl file "Empty reply from tracker"; ++ lprintf_file_nl (as_file file) "Empty reply from tracker"; + Bencode.decode "" + | _ -> Bencode.decode tracker_reply + in +@@ -1339,13 +1352,12 @@ + List.iter (fun (key,value) -> + match (key, value) with + | String "failure reason", String failure -> +- (* On failure, remove the faulty tracker from file.file_trackers list *) +- t.tracker_enabled <- false; +- (* remove_tracker t.tracker_url file; *) +- lprintf_file_nl file "Failure from Tracker %s in file: %s Reason: %s\nBT: Tracker %s disabled for failure" ++ (* On failure, disable the tracker and forbid re-enabling *) ++ t.tracker_status <- Disabled_failure (intern failure); ++ lprintf_file_nl (as_file file) "Failure from Tracker %s in file: %s Reason: %s\nBT: Tracker %s disabled for failure" + t.tracker_url file.file_name (Charset.to_utf8 failure) t.tracker_url + | String "warning message", String warning -> +- lprintf_file_nl file "Warning from Tracker %s in file: %s Reason: %s" t.tracker_url file.file_name warning ++ lprintf_file_nl (as_file file) "Warning from Tracker %s in file: %s Reason: %s" t.tracker_url file.file_name warning + | String "interval", Int n -> + t.tracker_interval <- chk_keyval (Bencode.print key) n t.tracker_url file.file_name; + (* in case we don't receive "min interval" *) +@@ -1376,11 +1388,11 @@ + | String "key", String n -> + t.tracker_key <- n; + if !verbose_msg_clients then +- lprintf_file_nl file "%s in file: %s has key: %s" t.tracker_url file.file_name n ++ lprintf_file_nl (as_file file) "%s in file: %s has key: %s" t.tracker_url file.file_name n + | String "tracker id", String n -> + t.tracker_id <- n; + if !verbose_msg_clients then +- lprintf_file_nl file "%s in file: %s has tracker id %s" t.tracker_url file.file_name n ++ lprintf_file_nl (as_file file) "%s in file: %s has tracker id %s" t.tracker_url file.file_name n + + | String "peers", List list -> + List.iter (fun v -> +@@ -1412,13 +1424,13 @@ + None -> true + | Some reason -> + if !verbose_connect then +- lprintf_file_nl file "%s:%d blocked: %s" ++ lprintf_file_nl (as_file file) "%s:%d blocked: %s" + (Ip.to_string !peer_ip) !port reason; + false) + then + let _ = new_client file !peer_id (!peer_ip,!port) + in +- if !verbose_sources > 1 then lprintf_file_nl file "Received %s:%d" (Ip.to_string !peer_ip) ++ if !verbose_sources > 1 then lprintf_file_nl (as_file file) "Received %s:%d" (Ip.to_string !peer_ip) + !port; + () + | _ -> assert false +@@ -1439,12 +1451,12 @@ + | String "private", Int n -> () + (* TODO: if set to 1, disable peer exchange *) + +- | _ -> lprintf_file_nl file "received unknown entry in answer from tracker: %s : %s" (Bencode.print key) (Bencode.print value) ++ | _ -> lprintf_file_nl (as_file file) "received unknown entry in answer from tracker: %s : %s" (Bencode.print key) (Bencode.print value) + ) list; + (*Now, that we have added new clients to a file, it's time + to connect to them*) + if !verbose_sources > 0 then +- lprintf_file_nl file "get_sources_from_tracker: got %i source(s) for file %s" ++ lprintf_file_nl (as_file file) "get_sources_from_tracker: got %i source(s) for file %s" + t.tracker_last_clients_num file.file_name; + resume_clients file + +@@ -1473,7 +1485,7 @@ + (try + connect_trackers file "" (fun _ _ -> ()) with _ -> ()) + | FilePaused -> () (*when we are paused we do nothing, not even logging this vvvv*) +- | s -> lprintf_file_nl file "Other state %s!!" (string_of_state s) ++ | s -> lprintf_file_nl (as_file file) "Other state %s!!" (string_of_state s) + ) !current_files + + let upload_buffer = String.create 100000 +@@ -1554,13 +1566,12 @@ + iter_upload sock c + ) + +- +-(** Probably useless now +-*) + let file_resume file = +- (*useless with no saving of sources +- resume_clients file; +- *) ++ List.iter (fun t -> ++ match t.tracker_status with ++ Enabled -> () ++ | _ -> t.tracker_status <- Enabled ++ ) file.file_trackers; + (try get_sources_from_tracker file with _ -> ()) + + +@@ -1573,7 +1584,7 @@ + if file.file_tracker_connected then + begin + connect_trackers file "stopped" (fun _ _ -> +- lprintf_file_nl file "Tracker return: stopped %s" file.file_name; ++ lprintf_file_nl (as_file file) "Tracker return: stopped %s" file.file_name; + file.file_tracker_connected <- false) + end + +Index: src/networks/bittorrent/bTComplexOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTComplexOptions.ml,v +retrieving revision 1.36 +retrieving revision 1.38 +diff -u -r1.36 -r1.38 +--- src/networks/bittorrent/bTComplexOptions.ml 16 Sep 2006 09:47:17 -0000 1.36 ++++ src/networks/bittorrent/bTComplexOptions.ml 1 Oct 2006 17:47:11 -0000 1.38 +@@ -183,8 +183,9 @@ + (Printf.sprintf "BT-%s" (Sha1.to_string file_id)) in + file_temp + in +- let file = new_file file_id torrent torrent_diskname file_temp file_state in +- ++ let file = new_file file_id torrent torrent_diskname ++ file_temp file_state CommonUserDb.admin_user in ++ + let file_uploaded = try + value_to_int64 (List.assoc "file_uploaded" assocs) + with _ -> zero +@@ -249,7 +250,7 @@ + CommonSwarming.frontend_to_value swarmer assocs + with + e -> +- lprintf_file_nl file "exception %s in file_to_value" ++ lprintf_file_nl (as_file file) "exception %s in file_to_value" + (Printexc2.to_string e); raise e + + +Index: src/networks/bittorrent/bTGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTGlobals.ml,v +retrieving revision 1.63 +retrieving revision 1.71 +diff -u -r1.63 -r1.71 +--- src/networks/bittorrent/bTGlobals.ml 16 Sep 2006 09:47:17 -0000 1.63 ++++ src/networks/bittorrent/bTGlobals.ml 9 Nov 2006 21:32:27 -0000 1.71 +@@ -31,6 +31,7 @@ + open CommonServer + open CommonResult + open CommonFile ++open CommonShared + open BasicSocket + open CommonGlobals + open Options +@@ -107,6 +108,45 @@ + let (client_ops : client CommonClient.client_ops) = + CommonClient.new_client_ops network + ++let must_share_file file codedname has_old_impl = ++ match file.file_shared with ++ | Some _ -> () ++ | None -> ++ begin ++ let impl = { ++ impl_shared_update = 1; ++ impl_shared_fullname = file_disk_name file; ++ impl_shared_codedname = codedname; ++ impl_shared_size = file_size file; ++ impl_shared_id = Md4.null; ++ impl_shared_num = 0; ++ impl_shared_uploaded = Int64.zero; ++ impl_shared_ops = shared_ops; ++ impl_shared_val = file; ++ impl_shared_requests = 0; ++ impl_shared_magic = None; ++ impl_shared_servers = []; ++ } in ++ file.file_shared <- Some impl; ++ incr CommonGlobals.nshared_files; ++ CommonShared.shared_calculate_total_bytes (); ++ match has_old_impl with ++ None -> update_shared_num impl ++ | Some old_impl -> replace_shared old_impl impl ++ end ++ ++let must_share_file file = must_share_file file (file_best_name (as_file file)) None ++ ++let unshare_file file = ++ match file.file_shared with ++ None -> () ++ | Some s -> ++ begin ++ file.file_shared <- None; ++ decr CommonGlobals.nshared_files; ++ CommonShared.shared_calculate_total_bytes () ++ end ++ + module DO = CommonOptions + + let current_files = ref ([] : BTTypes.file list) +@@ -126,10 +166,6 @@ + let lprintf_nl fmt = + lprintf_nl2 log_prefix fmt + +-let lprintf_file_nl file fmt = +- lprintf_nl2 (log_prefix^" [file_num "^(string_of_int (file_num file))^"]") fmt +- +- + let lprintf_n fmt = + lprintf2 log_prefix fmt + +@@ -217,13 +253,14 @@ + tracker_torrent_last_dl_req = 0; + tracker_id = ""; + tracker_key = ""; +- tracker_enabled = true ++ tracker_status = Enabled + } in +- t.tracker_enabled <- can_handle_tracker t; ++ if not (can_handle_tracker t) then ++ t.tracker_status <- Disabled_mld (intern "Tracker type not supported"); + file.file_trackers <- t :: file.file_trackers; + set_trackers file q + +-let new_file file_id t torrent_diskname file_temp file_state = ++let new_file file_id t torrent_diskname file_temp file_state user = + try + Hashtbl.find files_by_uid file_id + with Not_found -> +@@ -233,7 +270,7 @@ + file_file = file_impl; + file_piece_size = t.torrent_piece_size; + file_id = file_id; +- file_name = t.torrent_name; ++ file_name = Charset.safe_convert t.torrent_encoding t.torrent_name; + file_comment = t.torrent_comment; + file_created_by = t.torrent_created_by; + file_creation_date = t.torrent_creation_date; +@@ -252,6 +289,8 @@ + file_shared = None; + } and file_impl = { + dummy_file_impl with ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_fd = Some file_fd; + impl_file_size = t.torrent_length; + impl_file_downloaded = Int64.zero; +@@ -296,18 +335,18 @@ + current_files := file :: !current_files; + Hashtbl.add files_by_uid file_id file; + file_add file_impl file_state; +-(* lprintf "ADD FILE TO DOWNLOAD LIST\n"; *) ++ must_share_file file; + file + +-let new_download file_id t torrent_diskname = ++let new_download file_id t torrent_diskname user = + let file_temp = Filename.concat !!DO.temp_directory + (Printf.sprintf "BT-%s" (Sha1.to_string file_id)) in +- new_file file_id t torrent_diskname file_temp FileDownloading ++ new_file file_id t torrent_diskname file_temp FileDownloading user + + let ft_by_num = Hashtbl.create 13 + let ft_counter = ref 0 + +-let new_ft file_name = ++let new_ft file_name user = + incr ft_counter; + let rec ft = { + ft_file = file_impl; +@@ -316,6 +355,8 @@ + ft_retry = (fun _ -> ()); + } and file_impl = { + dummy_file_impl with ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_fd = None; + impl_file_size = zero; + impl_file_downloaded = Int64.zero; +@@ -419,6 +460,11 @@ + | "TR" -> Brand_transmission + | "HN" -> Brand_hydranode + | "RT" -> Brand_retriever ++ | "PC" -> Brand_cachelogic ++ | "ES" -> Brand_electricsheep ++ | "qB" -> Brand_qbittorrent ++ | "QT" -> Brand_qt4 ++ | "UL" -> Brand_uleecher + | _ -> Brand_unknown + in + if brand = Brand_unknown then None else +@@ -450,6 +496,7 @@ + | "A" -> Brand_abc + | "U" -> Brand_upnp + | "O" -> Brand_osprey ++ | "R" -> Brand_tribler + | _ -> Brand_unknown + in + let bv = ref None in +@@ -742,7 +789,7 @@ + client_downloaded = zero; + client_upload_rate = Rate.new_rate (); + client_downloaded_rate = Rate.new_rate (); +- client_optimist_time=0; ++ client_connect_time = last_time (); + client_blocks_sent = []; + client_new_chunks = []; + client_good = false; +@@ -753,6 +800,11 @@ + client_incoming = false; + client_registered_bitfield = false; + client_last_optimist = 0; ++ client_dht = false; ++ client_cache_extension = false; ++ client_fast_extension = false; ++ client_utorrent_extension = false; ++ client_azureus_messaging_protocol = false; + } and impl = { + dummy_client_impl with + impl_client_val = c; +@@ -789,6 +841,11 @@ + lprintf_nl "New tracker list :%s" tracker.tracker_url + ) file.file_trackers + ++let tracker_is_enabled t = ++ match t.tracker_status with ++ Enabled -> true ++ | _ -> false ++ + let torrents_directory = "torrents" + let new_torrents_directory = Filename.concat torrents_directory "incoming" + let downloads_directory = Filename.concat torrents_directory "downloads" +Index: src/networks/bittorrent/bTInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTInteractive.ml,v +retrieving revision 1.108 +retrieving revision 1.123 +diff -u -r1.108 -r1.123 +--- src/networks/bittorrent/bTInteractive.ml 16 Sep 2006 09:47:17 -0000 1.108 ++++ src/networks/bittorrent/bTInteractive.ml 12 Nov 2006 14:17:45 -0000 1.123 +@@ -52,8 +52,34 @@ + + module VB = VerificationBitmap + +-exception Already_exists +-exception Torrent_can_not_be_used ++let porttest_result = ref PorttestNotStarted ++ ++let interpret_azureus_porttest s = ++ let failure_message fmt = ++ Printf.sprintf ("Port test failure, " ^^ fmt) in ++ try ++ let value = decode s in ++ match value with ++ | Dictionary alist -> ++ (try ++ match List.assoc (String "result") alist with ++ | Int 1L -> "Port test OK!" ++ | Int 0L -> ++ (try ++ match List.assoc (String "reason") alist with ++ | String reason -> failure_message "%s" reason ++ | _ -> raise Not_found ++ with Not_found -> ++ failure_message "%s" "no reason given") ++ | Int status -> ++ failure_message "unknown status code (%Ld)" status ++ | _ -> raise Not_found ++ with Not_found -> ++ failure_message "%s" "no status given") ++ | _ -> ++ failure_message "unexpected value type %s" (Bencode.print value) ++ with _ -> ++ failure_message "%s" "broken bencoded value" + + let op_file_all_sources file = + let list = ref [] in +@@ -101,7 +127,7 @@ + set_file_state file FileShared; + + if Unix32.destroyed (file_fd file) then +- if !verbose then lprintf_file_nl file "op_file_commit: FD is destroyed... repairing"; ++ if !verbose then lprintf_file_nl (as_file file) "op_file_commit: FD is destroyed... repairing"; + + (* During the commit operation, for security, the file_fd is destroyed. So + we create it again to be able to share this file again. *) +@@ -110,7 +136,7 @@ + (create_temp_file new_name (List.map (fun (file,size,_) -> (file,size)) file.file_files) (file_state file)); + + if Unix32.destroyed (file_fd file) then +- lprintf_file_nl file "op_file_commit: FD is destroyed... could not repair!"; ++ lprintf_file_nl (as_file file) "op_file_commit: FD is destroyed... could not repair!"; + + let new_torrent_diskname = + Filename.concat seeded_directory +@@ -119,20 +145,20 @@ + (try + Unix2.rename file.file_torrent_diskname new_torrent_diskname; + with _ -> +- (lprintf_file_nl file "op_file_commit: failed to rename %s to %s" ++ (lprintf_file_nl (as_file file) "op_file_commit: failed to rename %s to %s" + file.file_torrent_diskname new_torrent_diskname)); + file.file_torrent_diskname <- new_torrent_diskname; + + end + +-let op_file_print_html file buf = +- +- html_mods_cntr_init (); ++let op_file_print file o = + ++ let buf = o.conn_buf in ++ if use_html_mods o then begin + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ + ("Filename", "sr br", "Filename"); +- ("", "sr", file.file_name) ]; ++ ("", "sr", (Charset.safe_convert file.file_encoding file.file_name)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ +@@ -143,24 +169,27 @@ + ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); +- html_mods_td buf [ +- ("Tracker(s)", "sr br", "Tracker(s)"); +- ("", "sr", +- (let enabled_tracker_string = ref "" in +- let disabled_tracker_string = ref "" in +- List.iter (fun tracker -> +- if tracker.tracker_enabled then +- enabled_tracker_string := !enabled_tracker_string ^ (shorten tracker.tracker_url !!max_name_len) ^ " " +- else +- disabled_tracker_string := !disabled_tracker_string ^ (shorten tracker.tracker_url !!max_name_len) ^ " " +- ) file.file_trackers; +- (!enabled_tracker_string ^ (if !disabled_tracker_string <> "" then " - disabled: " ^ !disabled_tracker_string else "")))) ]; +- +- Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ let tracker_header_printed = ref false in ++ List.iter (fun tracker -> ++ let tracker_text, tracker_error = ++ (match tracker.tracker_status with ++ Disabled s | Disabled_mld s | Disabled_failure s -> ++ Printf.sprintf "disabled: %s" tracker.tracker_url, s ++ | _ -> tracker.tracker_url, "") ++ in ++ html_mods_td buf [ ++ (if not !tracker_header_printed then ++ ("Tracker(s) (mouseover for errors)", "sr br", "Tracker(s)") ++ else ++ ("", "sr br", "")); ++ (tracker_error, "sr", tracker_text)]; ++ Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); ++ tracker_header_printed := true; ++ ) file.file_trackers; + + html_mods_td buf [ + ("Torrent Filename", "sr br", "Torrent Fname"); +- ("", "sr", file.file_torrent_diskname) ]; ++ ("", "sr", (Charset.safe_convert file.file_encoding file.file_torrent_diskname)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + +@@ -168,14 +197,14 @@ + ("Comment", "sr br", "Comment"); + ("", "sr", match file.file_comment with + "" -> "-" +- | _ -> file.file_comment) ]; ++ | _ -> (Charset.safe_convert file.file_encoding file.file_comment)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ + ("Created by", "sr br", "Created by"); + ("", "sr", match file.file_created_by with + "" -> "-" +- | _ -> file.file_created_by) ]; ++ | _ -> (Charset.safe_convert file.file_encoding file.file_created_by)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ +@@ -187,7 +216,7 @@ + ("Modified by", "sr br", "Modified by"); + ("", "sr", match file.file_modified_by with + "" -> "-" +- | _ -> file.file_modified_by) ]; ++ | _ -> (Charset.safe_convert file.file_encoding file.file_modified_by)) ]; + + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ +@@ -200,7 +229,7 @@ + match l with + | [] -> () + | t :: q -> +- if not t.tracker_enabled then print_first_tracker q ++ if not (tracker_is_enabled t) then print_first_tracker q + else begin + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + html_mods_td buf [ +@@ -262,24 +291,42 @@ + end in + print_first_tracker file.file_trackers; + ++ (* This is bad. Magic info should be automatically filled in when ++ the corresponding chunks complete. (see CommonSwarming) ++ ++ This code only fills in the magic info for subfiles when a user ++ manually performs a "vd #". (interfaces out of sync) ++ ++ Magic info for shared files with subfiles is missing as well? ++ *) ++ if !Autoconf.magic_works then begin + let check_magic file = + match Magic.M.magic_fileinfo file false with + None -> None +- | Some s -> Some (HashMagic.merge CommonGlobals.files_magic s) ++ | Some s -> Some (intern s) + in ++ let fdn = file_disk_name file in ++ let new_file_files = ref [] in ++ ++ List.iter (fun (f, s, m) -> ++ let subfile = Filename.concat fdn f in ++ new_file_files := (f,s, check_magic subfile) :: !new_file_files; ++ ) file.file_files; ++ ++ file.file_files <- List.rev !new_file_files; ++ file_must_update file; (* Send update to guis *) ++ ++ end; ++ (* -- End bad -- *) ++ + let cntr = ref 0 in +- List.iter (fun (filename, size, _) -> ++ List.iter (fun (filename, size, magic) -> + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()); + let fs = Printf.sprintf "File %d" !cntr in + let magic_string = +- if !Autoconf.magic_works then +- begin +- let subfile = Filename.concat (file_disk_name file) filename in +- match check_magic subfile with ++ match magic with + None -> "" +- | Some magic -> Printf.sprintf " / %s" magic +- end +- else "" ++ | Some m -> Printf.sprintf " / %s" m; + in + html_mods_td buf [ + (fs, "sr br", fs); +@@ -287,8 +334,68 @@ + ]; + incr cntr; + ) file.file_files ++ end else begin + +-let op_file_print_sources_html file buf = ++ Printf.bprintf buf "Trackers:\n"; ++ List.iter (fun tracker -> ++ match tracker.tracker_status with ++ Disabled s | Disabled_mld s | Disabled_failure s -> ++ Printf.bprintf buf "%s, disabled: %s\n" tracker.tracker_url s ++ | _ -> Printf.bprintf buf "%s\n" tracker.tracker_url ++ ) file.file_trackers; ++ let s = Charset.safe_convert file.file_encoding file.file_torrent_diskname in ++ if s <> "" then Printf.bprintf buf "Torrent diskname: %s\n" s; ++ let s = Charset.safe_convert file.file_encoding file.file_comment in ++ if s <> "" then Printf.bprintf buf "Comment: %s\n" s; ++ let s = Charset.safe_convert file.file_encoding file.file_created_by in ++ if s <> "" then Printf.bprintf buf "Created by %s\n" s; ++ let s = Date.to_string (Int64.to_float file.file_creation_date) in ++ if s <> "" then Printf.bprintf buf "Creation date: %s\n" s; ++ let s = Charset.safe_convert file.file_encoding file.file_modified_by in ++ if s <> "" then Printf.bprintf buf "Modified by %s\n" s; ++ if file.file_encoding <> "" then Printf.bprintf buf "Encoding: %s\n" file.file_encoding; ++ if file.file_files <> [] then Printf.bprintf buf "Subfiles: %d\n" (List.length file.file_files); ++ let cntr = ref 0 in ++ List.iter (fun (filename, size, magic) -> ++ incr cntr; ++ let magic_string = ++ match magic with ++ None -> "" ++ | Some m -> Printf.sprintf " / %s" m; ++ in ++ Printf.bprintf buf "File %d: %s (%Ld bytes)%s\n" !cntr filename size magic_string ++ ) file.file_files ++ end ++ ++let op_file_print_sources file o = ++ let buf = o.conn_buf in ++ ++(* redefine functions for telnet output *) ++ let html_mods_td buf l = ++ if use_html_mods o then ++ html_mods_td buf l ++ else ++ (* List *) ++ List.iter (fun (t,c,d) -> ++ (* Title Class Value *) ++ Printf.bprintf buf "%s " ++ d; ++ ) l ++ in ++ let html_mods_table_header buf n c l = ++ if use_html_mods o then ++ html_mods_table_header buf n c l ++ else ++ if List.length l > 0 then begin ++ Printf.bprintf buf "\n"; ++ List.iter (fun (w,x,y,z) -> ++ (* Sort Class Title Value *) ++ Printf.bprintf buf "%s " ++ z; ++ ) l; ++ Printf.bprintf buf "\n" ++ end ++ in + + if Hashtbl.length file.file_clients > 0 then begin + +@@ -312,10 +419,15 @@ + ( "0", "srh ar", "Incoming [T]rue, [F]alse", "I" ); + ( "0", "srh br ar", "Registered bitfield [T]rue, [F]alse", "B" ); + +- ( "0", "srh ar", "Optimist Time", "O" ); ++ ( "0", "srh ar", "Connect Time", "T" ); + ( "0", "srh ar", "Last optimist", "L.Opt" ); + ( "0", "srh br ar", "Num try", "N" ); + ++ ( "0", "srh", "DHT [T]rue, [F]alse", "D" ); ++ ( "0", "srh", "Cache extensions [T]rue, [F]alse", "C" ); ++ ( "0", "srh", "Fast extensions [T]rue, [F]alse", "F" ); ++ ( "0", "srh", "uTorrent extensions [T]rue, [F]alse", "U" ); ++ ( "0", "srh br", "Azureus messaging protocol [T]rue, [F]alse", "A" ); + (* + ( "0", "srh", "Bitmap (absent|partial|present|verified)", (colored_chunks + (Array.init (String.length info.G.file_chunks) +@@ -334,8 +446,10 @@ + html_mods_table_header buf "sourcesTable" "sources al" header_list; + + Hashtbl.iter (fun _ c -> ++ if use_html_mods o then + Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr()); + ++ let btos b = if b then "T" else "F" in + let cc,cn = Geoip.get_country (fst c.client_host) in + let td_list = [ + ("", "sr br ar", Printf.sprintf "%d" (client_num c)); +@@ -346,25 +460,31 @@ + ] @ (if !Geoip.active then [( cn, "sr br", cc)] else []) @ [ + ("", "sr ar", (size_of_int64 c.client_uploaded)); + ("", "sr ar br", (size_of_int64 c.client_downloaded)); +- ("", "sr", (if c.client_interested then "T" else "F")); +- ("", "sr", (if c.client_choked then "T" else "F")); ++ ("", "sr", (btos c.client_interested)); ++ ("", "sr", (btos c.client_choked)); + ("", "sr br ar", (Int64.to_string c.client_allowed_to_write)); + (* This is way too slow for 1000's of chunks on a page with 100's of sources + ("", "sr", (CommonFile.colored_chunks (Array.init (String.length c.client_bitmap) + (fun i -> (if c.client_bitmap.[i] = '1' then 2 else 0)) )) ); + *) +- ("", "sr", (if c.client_interesting then "T" else "F")); +- ("", "sr", (if c.client_alrd_sent_interested then "T" else "F")); +- ("", "br sr", (if c.client_alrd_sent_notinterested then "T" else "F")); +- +- ("", "sr", (if c.client_good then "T" else "F")); +- ("", "sr", (if c.client_incoming then "T" else "F")); +- ("", "br sr", (if c.client_registered_bitfield then "T" else "F")); ++ ("", "sr", (btos c.client_interesting)); ++ ("", "sr", (btos c.client_alrd_sent_interested)); ++ ("", "br sr", (btos c.client_alrd_sent_notinterested)); ++ ++ ("", "sr", (btos c.client_good)); ++ ("", "sr", (btos c.client_incoming)); ++ ("", "br sr", (btos c.client_registered_bitfield)); + +- ("", "sr", Printf.sprintf "%d" c.client_optimist_time); ++ ("", "sr", Printf.sprintf "%d" c.client_connect_time); + ("", "ar sr", string_of_date c.client_last_optimist); + ("", "br sr", Printf.sprintf "%d" c.client_num_try); + ++ ("", "sr", (btos c.client_dht)); ++ ("", "sr", (btos c.client_cache_extension)); ++ ("", "sr", (btos c.client_fast_extension)); ++ ("", "sr", (btos c.client_utorrent_extension)); ++ ("", "br sr", (btos c.client_azureus_messaging_protocol)); ++ + ("", "sr ar", (let fc = ref 0 in + (match c.client_bitmap with + None -> () +@@ -374,19 +494,21 @@ + ] in + + html_mods_td buf td_list; +- Printf.bprintf buf "\\</tr\\>"; ++ if use_html_mods o then Printf.bprintf buf "\\</tr\\>" ++ else Printf.bprintf buf "\n"; + + ) file.file_clients; + +- Printf.bprintf buf "\\</table\\>\\</div\\>\\<br\\>"; ++ if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>\\<br\\>" ++ else Printf.bprintf buf "\n"; + + end + + let op_file_check file = +- lprintf_file_nl file "Checking chunks of %s" file.file_name; ++ lprintf_file_nl (as_file file) "Checking chunks of %s" file.file_name; + match file.file_swarmer with + None -> +- lprintf_file_nl file "verify_chunks: no swarmer to verify chunks" ++ lprintf_file_nl (as_file file) "verify_chunks: no swarmer to verify chunks" + | Some swarmer -> + CommonSwarming.verify_all_chunks_immediately swarmer + +@@ -434,6 +556,7 @@ + P.file_sub_files = file.file_files; + P.file_active_sources = List.length (op_file_active_sources file); + P.file_all_sources = (Hashtbl.length file.file_clients); ++ P.file_comment = file.file_comment; + } + + let op_ft_info ft = +@@ -443,7 +566,7 @@ + { + P.file_fields = P.Fields_file_info.all; + +- P.file_comment = ""; ++ P.file_comment = file_comment (as_ft ft); + P.file_name = ft.ft_filename; + P.file_num = ft_num ft; + P.file_network = network.network_num; +@@ -465,11 +588,16 @@ + P.file_priority = 0; + P.file_uids = []; + P.file_sub_files = []; ++ P.file_magic = None; ++ P.file_comments = []; ++ P.file_user = ""; ++ P.file_group = ""; ++ P.file_release = file_release (as_ft ft); + } + + + +-let load_torrent_string s = ++let load_torrent_string s user = + let file_id, torrent = BTTorrent.decode_torrent s in + + (* Save the torrent, because we later want to put +@@ -488,20 +616,19 @@ + if Sys.file_exists torrent_diskname then + begin + if !verbose then lprintf_nl "load_torrent_string: %s already exists, ignoring" torrent_diskname; +- raise Already_exists ++ raise Torrent_already_exists + end; + File.from_string torrent_diskname s; + + if !verbose then + lprintf_nl "Starting torrent download with diskname: %s" + torrent_diskname; +- let file = new_download file_id torrent torrent_diskname in ++ let file = new_download file_id torrent torrent_diskname user in + BTClients.get_sources_from_tracker file; +- BTShare.must_share_file file; + CommonInteractive.start_download (file_find (file_num file)); + file + +-let load_torrent_file filename = ++let load_torrent_file filename user = + if !verbose then + lprintf_nl "load_torrent_file %s" filename; + let s = File.to_string filename in +@@ -510,27 +637,27 @@ + if Sys.file_exists filename + && (Filename.dirname filename) = downloads_directory then + Sys.remove filename; +- ignore (load_torrent_string s) ++ ignore (load_torrent_string s user) + + let parse_tracker_reply file t filename = + (*This is the function which will be called by the http client + for parsing the response*) + (* Interested only in interval*) +- if !verbose_msg_servers then lprintf_file_nl file "Filename %s" filename; ++ if !verbose_msg_servers then lprintf_file_nl (as_file file) "Filename %s" filename; + let tracker_reply = + try + File.to_string filename +- with e -> lprintf_file_nl file "Empty reply from tracker"; "" ++ with e -> lprintf_file_nl (as_file file) "Empty reply from tracker"; "" + in + let v = + match tracker_reply with + | "" -> + if !verbose_connect then +- lprintf_file_nl file "Empty reply from tracker"; ++ lprintf_file_nl (as_file file) "Empty reply from tracker"; + Bencode.decode "" + | _ -> Bencode.decode tracker_reply + in +- if !verbose_msg_servers then lprintf_file_nl file "Received: %s" (Bencode.print v); ++ if !verbose_msg_servers then lprintf_file_nl (as_file file) "Received: %s" (Bencode.print v); + t.tracker_interval <- 600; + match v with + Dictionary list -> +@@ -538,9 +665,9 @@ + match (key, value) with + String "interval", Int n -> + t.tracker_interval <- Int64.to_int n; +- if !verbose_msg_servers then lprintf_file_nl file ".. interval %d .." t.tracker_interval ++ if !verbose_msg_servers then lprintf_file_nl (as_file file) ".. interval %d .." t.tracker_interval + | String "failure reason", String failure -> +- lprintf_file_nl file "Failure from Tracker in file: %s Reason: %s" file.file_name failure ++ lprintf_file_nl (as_file file) "Failure from Tracker in file: %s Reason: %s" file.file_name failure + (*TODO: merge with f from get_sources_from_tracker and parse the rest of the answer, too. + also connect to the sources we receive or instruct tracker to send none, perhaps based + on an config option. firewalled people could activate the option and then seed torrents, too.*) +@@ -559,7 +686,7 @@ + match list with + [] -> raise Not_found + | sh :: tail -> +- let s = sharing_strategies sh.shdir_strategy in ++ let s = sharing_strategy sh.shdir_strategy in + if match torrent.torrent_files with + [] -> not s.sharing_directories + | _ -> s.sharing_directories then +@@ -576,9 +703,8 @@ + in + + let file = new_file file_id torrent torrent_diskname +- filename FileShared in +- BTShare.must_share_file file; +- if !verbose_share then lprintf_file_nl file "Sharing file %s" filename; ++ filename FileShared CommonUserDb.admin_user in ++ if !verbose_share then lprintf_file_nl (as_file file) "Sharing file %s" filename; + BTClients.connect_trackers file "started" + (parse_tracker_reply file) + with +@@ -636,13 +762,17 @@ + let file_basename = Filename.basename file in + if not (Unix2.is_directory file) then + try +- load_torrent_file file; ++ let user = fst (Unix32.owner file) in ++ load_torrent_file file (try CommonUserDb.user2_user_find user with Not_found -> CommonUserDb.admin_user); + (try Sys.remove file with _ -> ()) + with + Torrent_can_not_be_used -> + Unix2.rename file (Filename.concat old_directory file_basename); + lprintf_nl "Torrent %s does not have valid tracker URLs, moved to torrents/old ..." file_basename +- | e -> lprintf_nl "Error %s in scan_new_torrents_directory for %s" (Printexc2.to_string e) file_basename ++ | e -> ++ Unix2.rename file (Filename.concat old_directory file_basename); ++ lprintf_nl "Error %s in scan_new_torrents_directory for %s, moved to torrents/old ..." ++ (Printexc2.to_string e) file_basename + ) filenames + + let retry_all_ft () = +@@ -651,12 +781,12 @@ + lprintf_nl "ft_retry: exception %s" (Printexc2.to_string e) + ) ft_by_num + +-let load_torrent_from_web r ft = ++let load_torrent_from_web r user ft = + let module H = Http_client in + H.wget r (fun filename -> + if ft_state ft = FileDownloading then begin +- load_torrent_file filename; +- file_cancel (as_ft ft) ++ load_torrent_file filename user; ++ file_cancel (as_ft ft) CommonUserDb.admin_user + end) + + let valid_torrent_extension url = +@@ -669,7 +799,7 @@ + let b = Str.group_end 1 in + String.sub text a (b - a) + +-let op_network_parse_url url = ++let op_network_parse_url url user = + let location_regexp = "Location: \\(.*\\)" in + try + let real_url = get_regexp_string url (Str.regexp location_regexp) in +@@ -700,13 +830,13 @@ + ) "" cookies + ) ] + with Not_found -> []); +- H.req_max_retry = 10; ++ H.req_max_retry = 10; + } in + + let file_diskname = Filename.basename u.Url.short_file in +- let ft = new_ft file_diskname in +- ft.ft_retry <- load_torrent_from_web r ; +- load_torrent_from_web r ft; ++ let ft = new_ft file_diskname user in ++ ft.ft_retry <- (load_torrent_from_web r user); ++ load_torrent_from_web r user ft; + "started download", true + ) + else +@@ -717,10 +847,10 @@ + try + if !verbose then lprintf_nl "Not_found and trying to load %s" url; + try +- load_torrent_file url; ++ load_torrent_file url user; + "", true + with +- Already_exists -> "A torrent with this name is already in the download queue", false ++ Torrent_already_exists -> "A torrent with this name is already in the download queue", false + | Torrent_can_not_be_used -> "This torrent does not have valid tracker URLs", false + with e -> + lprintf_nl "Exception %s while 2nd loading" (Printexc2.to_string e); +@@ -753,6 +883,7 @@ + P.client_downloaded = c.client_downloaded; + P.client_uploaded = c.client_uploaded; + P.client_upload = Some (c.client_file.file_name); ++ P.client_connect_time = c.client_connect_time; + + } + +@@ -909,12 +1040,15 @@ + ), _s ":\t\t\t\tprint all .torrent files on this server"; + + "seeded_torrents", "Network/Bittorrent", Arg_none (fun o -> ++ if CommonUserDb.user2_is_admin o.conn_user.ui_user then begin + List.iter (fun file -> + if file_state file = FileShared then + Printf.bprintf o.conn_buf "%s [%s]\n" file.file_name (Int64.to_string file.file_uploaded) + ) !current_files; + _s "done" +- ++ end else ++ begin print_command_result o o.conn_buf "You are not allowed to use seeded_torrents"; ++ "" end + ), _s ":\t\t\tprint all seeded .torrent files on this server"; + + "reshare_torrents", "Network/Bittorrent", Arg_none (fun o -> +@@ -950,13 +1084,13 @@ + let buf = o.conn_buf in + if Sys.file_exists url then + begin +- load_torrent_file url; ++ load_torrent_file url o.conn_user.ui_user; + Printf.bprintf buf "loaded file %s\n" url + end + else + begin + let url = "Location: " ^ url ^ "\nContent-Type: application/x-bittorrent" in +- let result = fst (op_network_parse_url url) in ++ let result = fst (op_network_parse_url url o.conn_user.ui_user) in + Printf.bprintf buf "%s\n" result + end; + _s "" +@@ -986,7 +1120,7 @@ + Hashtbl.iter (fun _ file -> + if file_num file = num then begin + if !verbose then +- lprintf_file_nl file "adding trackers for file %i" num; ++ lprintf_file_nl (as_file file) "adding trackers for file %i" num; + set_trackers file !urls; + raise Exit + end +@@ -1031,12 +1165,18 @@ + open LittleEndian + open GuiDecoding + +-let op_gui_message s = ++let op_gui_message s user = + match get_int16 s 0 with + 0 -> + let text = String.sub s 2 (String.length s - 2) in + if !verbose then lprintf_nl "received torrent from gui..."; +- ignore (load_torrent_string text) ++ (try ++ ignore (load_torrent_string text user) ++ with e -> (match e with ++ Torrent_can_not_be_used -> lprintf_nl "Loading torrent from GUI: this torrent can not be used" ++ | Torrent_already_exists -> lprintf_nl "Loading torrent from GUI: this torrent is already in download queue" ++ | _ -> ()); ++ raise e) + | 1 -> (* 34+ *) + let n = get_int s 2 in + let a, pos = get_string s 6 in +@@ -1057,12 +1197,17 @@ + file_ops.op_file_active_sources <- op_file_active_sources; + file_ops.op_file_debug <- op_file_debug; + file_ops.op_file_commit <- op_file_commit; +- file_ops.op_file_print_html <- op_file_print_html; +- file_ops.op_file_print_sources_html <- op_file_print_sources_html; ++ file_ops.op_file_print <- op_file_print; ++ file_ops.op_file_print_sources <- op_file_print_sources; + file_ops.op_file_check <- op_file_check; + file_ops.op_file_cancel <- op_file_cancel; + file_ops.op_file_info <- op_file_info; + file_ops.op_file_save_as <- (fun file name -> ()); ++ file_ops.op_file_shared <- (fun file -> ++ match file.file_shared with ++ None -> None ++ | Some sh -> Some (as_shared sh) ++ ); + + network.op_network_gui_message <- op_gui_message; + network.op_network_connected <- op_network_connected; +@@ -1072,7 +1217,7 @@ + network.op_network_forget_search <- (fun s -> ()); + network.op_network_connect_servers <- (fun s -> ()); + network.op_network_search <- (fun ss buf -> ()); +- network.op_network_download <- (fun r -> dummy_file); ++ network.op_network_download <- (fun r user -> dummy_file); + network.op_network_recover_temp <- (fun s -> ()); + let clean_exit_started = ref false in + network.op_network_clean_exit <- (fun s -> +@@ -1090,7 +1235,25 @@ + !!client_port, "client_port TCP"; + !!BTTracker.tracker_port, "tracker_port TCP"; + ]); +- ++ network.op_network_porttest_result <- (fun _ -> !porttest_result); ++ network.op_network_porttest_start <- (fun _ -> ++ let module H = Http_client in ++ azureus_porttest_random := (Random.int 100000); ++ porttest_result := PorttestInProgress (last_time ()); ++ let r = { ++ H.basic_request with ++ H.req_url = ++ Url.of_string (Printf.sprintf ++ "http://azureus.aelitis.com/natcheck.php?port=%d&check=azureus_rand_%d" ++ !!client_port !azureus_porttest_random); ++ H.req_proxy = !CommonOptions.http_proxy; ++ H.req_user_agent = get_user_agent (); ++ } in ++ H.wget r (fun file -> ++ let result = interpret_azureus_porttest (File.to_string file) in ++ porttest_result := PorttestResult (last_time (), result) ++ ) ++ ); + client_ops.op_client_info <- op_client_info; + client_ops.op_client_connect <- op_client_connect; + client_ops.op_client_disconnect <- op_client_disconnect; +@@ -1101,8 +1264,8 @@ + CommonNetwork.register_commands commands; + + shared_ops.op_shared_unshare <- (fun file -> +- (if !verbose_share then lprintf_file_nl file "unshare file"); +- BTShare.unshare_file file); ++ (if !verbose_share then lprintf_file_nl (as_file file) "unshare file"); ++ BTGlobals.unshare_file file); + shared_ops.op_shared_info <- (fun file -> + let module T = GuiTypes in + match file.file_shared with +Index: src/networks/bittorrent/bTProtocol.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTProtocol.ml,v +retrieving revision 1.25 +retrieving revision 1.27 +diff -u -r1.25 -r1.27 +--- src/networks/bittorrent/bTProtocol.ml 15 Dec 2005 19:41:46 -0000 1.25 ++++ src/networks/bittorrent/bTProtocol.ml 1 Oct 2006 17:54:00 -0000 1.27 +@@ -231,9 +231,16 @@ + open AnyEndian + open BTTypes + ++let log_prefix = "[BT]" ++ ++let lprintf_nl fmt = ++ lprintf_nl2 log_prefix fmt ++ ++let azureus_porttest_random = ref 0 ++ + type ghandler = + BTHeader of (gconn -> TcpBufferedSocket.t -> +- (string * Sha1.t) -> unit) ++ (string * string * Sha1.t) -> unit) + | Reader of (gconn -> TcpBufferedSocket.t -> unit) + + and gconn = { +@@ -538,9 +545,13 @@ + let file_id = Sha1.direct_of_string + (String.sub b.buf (b.pos+9+slen) 20) in + let proto,pos = get_string8 b.buf b.pos in ++ let rbits = (String.sub b.buf (b.pos+pos) 8) in + buf_used b (slen+29); +- h gconn sock (proto, file_id); ++ h gconn sock (proto, rbits, file_id); + end ++ else ++ if (String.sub b.buf b.pos (min b.len 100)) = "NATCHECK_HANDSHAKE" then ++ write_string sock (Printf.sprintf "azureus_rand_%d" !azureus_porttest_random) + else if (TcpBufferedSocket.closed sock) then + let (ip,port) = (TcpBufferedSocket.peer_addr sock) in + lprintf_nl "bt-handshake: closed sock from %s:%d b.len:%i slen:%i" +Index: src/networks/bittorrent/bTShare.ml +=================================================================== +RCS file: src/networks/bittorrent/bTShare.ml +diff -N src/networks/bittorrent/bTShare.ml +--- src/networks/bittorrent/bTShare.ml 12 May 2006 21:08:30 -0000 1.3 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,67 +0,0 @@ +-(* Copyright 2001, 2002, 2005 b8_bavard, b8_fee_carabine, INRIA *) +-(* +- This file is part of mldonkey. +- +- mldonkey 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. +- +- mldonkey 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 mldonkey; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-*) +- +-open CommonGlobals +-open Printf2 +-open Md4 +-open CommonDownloads +-open CommonFile +-open CommonShared +-open CommonTypes +-open Options +-open BTTypes +-open BTGlobals +- +-let must_share_file file codedname has_old_impl = +- match file.file_shared with +- | Some _ -> () +- | None -> +- begin +- let impl = { +- impl_shared_update = 1; +- impl_shared_fullname = file_disk_name file; +- impl_shared_codedname = codedname; +- impl_shared_size = file_size file; +- impl_shared_id = Md4.null; +- impl_shared_num = 0; +- impl_shared_uploaded = Int64.zero; +- impl_shared_ops = shared_ops; +- impl_shared_val = file; +- impl_shared_requests = 0; +- impl_shared_magic = None; +- } in +- file.file_shared <- Some impl; +- incr CommonGlobals.nshared_files; +- CommonShared.shared_calculate_total_bytes (); +- match has_old_impl with +- None -> update_shared_num impl +- | Some old_impl -> replace_shared old_impl impl +- end +- +-let must_share_file file = must_share_file file (file_best_name (as_file file)) None +- +-let unshare_file file = +- match file.file_shared with +- None -> () +- | Some s -> +- begin +- file.file_shared <- None; +- decr CommonGlobals.nshared_files; +- CommonShared.shared_calculate_total_bytes () +- end +Index: src/networks/bittorrent/bTStats.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTStats.ml,v +retrieving revision 1.7 +retrieving revision 1.8 +diff -u -r1.7 -r1.8 +--- src/networks/bittorrent/bTStats.ml 26 Jan 2006 10:34:53 -0000 1.7 ++++ src/networks/bittorrent/bTStats.ml 23 Sep 2006 20:29:47 -0000 1.8 +@@ -93,6 +93,14 @@ + let _ = + network.op_network_display_stats <- (fun buf o -> print_stats o New); + ++ network.op_network_stat_info_list <- (fun _ -> ++ let l1 = stats_list brand_list stats_array in ++ let l2 = stats_list brand_list !!gstats_array in ++ let u1 = BasicSocket.last_time () - BasicSocket.start_time in ++ let u2 = (guptime() + u1) in ++ [("Session clients", u1, l1); ("Global clients", u2, l2)] ++ ); ++ + register_commands + [ + "client_stats_bt", "Network/Bittorrent",Arg_none (fun o -> +Index: src/networks/bittorrent/bTTypes.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/bittorrent/bTTypes.ml,v +retrieving revision 1.36 +retrieving revision 1.39 +diff -u -r1.36 -r1.39 +--- src/networks/bittorrent/bTTypes.ml 12 Sep 2006 22:44:52 -0000 1.36 ++++ src/networks/bittorrent/bTTypes.ml 8 Oct 2006 14:02:05 -0000 1.39 +@@ -104,6 +104,14 @@ + | Brand_retriever + | Brand_osprey + | Brand_rufus ++| Brand_tribler ++| Brand_cachelogic ++| Brand_electricsheep ++| Brand_qbittorrent ++| Brand_qt4 ++| Brand_uleecher ++ ++ + + let brand_list = [ + ( Brand_unknown , "unknown" , "unk" ) ; +@@ -163,6 +171,12 @@ + ( Brand_retriever , "Retriever" , "ret" ) ; + ( Brand_osprey , "Osprey permaseed" , "osp" ) ; + ( Brand_rufus , "Rufus" , "ruf" ) ; ++ ( Brand_tribler , "Tribler" , "trb" ) ; ++ ( Brand_cachelogic , "CacheLogic" , "cl" ) ; ++ ( Brand_electricsheep , "Electric sheep" , "els" ) ; ++ ( Brand_qbittorrent , "qBittorrent" , "qbt" ) ; ++ ( Brand_qt4 , "QT4" , "qt4" ) ; ++ ( Brand_uleecher , "uLeecher!" , "ul!" ) ; + ] + + let brand_count = List.length brand_list +@@ -176,6 +190,12 @@ + let brand_to_int brand = + find_int_of_brand brand brand_list + ++type tracker_status = ++ Enabled ++| Disabled of string ++| Disabled_mld of string ++| Disabled_failure of string ++ + type client = { + client_client : client CommonClient.client_impl; + mutable client_file : file; +@@ -207,7 +227,7 @@ + mutable client_downloaded_rate : Rate.t; + mutable client_downloaded : int64; + mutable client_uploaded : int64; +- mutable client_optimist_time : int; ++ mutable client_connect_time : int; + + mutable client_blocks_sent : int list; + mutable client_good : bool; +@@ -218,6 +238,13 @@ + mutable client_incoming : bool; + mutable client_registered_bitfield : bool; + mutable client_last_optimist : int; ++ ++ mutable client_dht : bool; ++ mutable client_cache_extension : bool; ++ mutable client_fast_extension : bool; ++ mutable client_utorrent_extension : bool; ++ mutable client_azureus_messaging_protocol : bool; ++ + } + + and tracker_info = { +@@ -233,7 +260,7 @@ + mutable tracker_torrent_last_dl_req : int; + mutable tracker_id : string; + mutable tracker_key : string; +- mutable tracker_enabled : bool; ++ mutable tracker_status : tracker_status; + } + + and file = { +Index: src/networks/direct_connect/dcClients.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/direct_connect/dcClients.ml,v +retrieving revision 1.12 +retrieving revision 1.13 +diff -u -r1.12 -r1.13 +--- src/networks/direct_connect/dcClients.ml 10 Apr 2006 19:16:36 -0000 1.12 ++++ src/networks/direct_connect/dcClients.ml 21 Nov 2006 22:34:33 -0000 1.13 +@@ -273,7 +273,7 @@ + let rec refill sock = + lprintf "FILL SOCKET"; lprint_newline (); + let len = remaining_to_write sock in +- let can = maxi (8192 - len) 0 in ++ let can = max (8192 - len) 0 in + if can > 0 then + match c.client_download with + DcUploadList list -> +@@ -281,7 +281,7 @@ + let slen = String.length list in + let pos = Int64.to_int c.client_pos in + if pos < slen then begin +- let send_len = mini (slen - pos) can in ++ let send_len = min (slen - pos) can in + lprintf "Sending %d" send_len; lprint_newline (); + TcpBufferedSocket.write sock list pos send_len; + lprintf "sent"; lprint_newline (); +Index: src/networks/direct_connect/dcInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/direct_connect/dcInteractive.ml,v +retrieving revision 1.27 +retrieving revision 1.28 +diff -u -r1.27 -r1.28 +--- src/networks/direct_connect/dcInteractive.ml 5 Sep 2006 14:15:19 -0000 1.27 ++++ src/networks/direct_connect/dcInteractive.ml 1 Oct 2006 17:54:00 -0000 1.28 +@@ -380,6 +380,7 @@ + [ + !!dc_port, "client_port"; + ]); ++ network.op_network_porttest_result <- (fun _ -> PorttestNotAvailable); + network.op_network_recover_temp <- (fun s -> ()); + network.op_network_load_complex_options <- (fun _ -> ()); + network.op_network_save_complex_options <- (fun _ -> ()); +Index: src/networks/direct_connect/dcProtocol.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/direct_connect/dcProtocol.ml,v +retrieving revision 1.4 +retrieving revision 1.5 +diff -u -r1.4 -r1.5 +--- src/networks/direct_connect/dcProtocol.ml 6 Sep 2005 11:24:59 -0000 1.4 ++++ src/networks/direct_connect/dcProtocol.ml 12 Nov 2006 12:42:55 -0000 1.5 +@@ -601,7 +601,7 @@ + module MultiConnectToMe = NickAndAddr(struct let msg = "MultiConnectToMe" end) + + module Msg = struct +- type t = () ++ type t = unit + + let parse s = () + +@@ -950,4 +950,4 @@ + in + iter 0 shared_tree; + Buffer.contents buf +- +\ No newline at end of file ++ +Index: src/networks/donkey/donkeyClient.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyClient.ml,v +retrieving revision 1.99 +retrieving revision 1.109 +diff -u -r1.99 -r1.109 +--- src/networks/donkey/donkeyClient.ml 5 Sep 2006 14:18:24 -0000 1.99 ++++ src/networks/donkey/donkeyClient.ml 21 Nov 2006 22:34:33 -0000 1.109 +@@ -80,7 +80,7 @@ + (* without server, we can't request a callback *) + let s = Hashtbl.find servers_by_key serverIP in + if serverPort = s.server_port then +- Indirect_address ( serverIP, serverPort, id_of_ip ip, port, ip ) ++ Indirect_address ( serverIP, serverPort, id_of_ip ip, 0, Ip.null ) + else + raise Not_found + with _ -> +@@ -192,9 +192,13 @@ + M.AvailableSlotReq Q.t); + + if !verbose then +- lprintf_nl "New uploader %s" +- (full_client_identifier c); +- ++ lprintf_nl "New uploader %s%s%s" ++ (full_client_identifier c) ++ (let slot_text = string_of_slot_kind (client_slot (as_client c)) true in ++ if slot_text = "" then "" else Printf.sprintf "(%s)" slot_text) ++ (match client_upload (as_client c) with ++ None -> "" ++ | Some f -> Printf.sprintf " for file %s" (CommonFile.file_best_name f)) + ) + in + client_ops.op_client_enter_upload_queue <- client_enter_upload_queue +@@ -262,7 +266,7 @@ + c.client_connect_time <- 0; + (try Hashtbl.remove connected_clients c.client_md4 with _ -> ()); + (try CommonUploads.remove_pending_slot (as_client c) with _ -> ()); +- set_client_has_a_slot (as_client c) false; ++ set_client_has_a_slot (as_client c) NoSlot; + (* connection_failed c.client_connection_control; *) + (try TcpBufferedSocket.close sock reason with _ -> ()); + +@@ -673,6 +677,11 @@ + DonkeyProtoClient.update_emule_proto_from_miscoptions1 + c.client_emule_proto i + ) ++ | Field_UNKNOWN "emule_compatoptions" -> ++ for_int_tag tag (fun i -> ++ DonkeyProtoClient.update_emule_proto_from_compatoptions ++ c.client_emule_proto i ++ ); + | Field_UNKNOWN "emule_version" -> + for_int_tag tag (fun i -> + c.client_emule_proto.emule_version <- i; +@@ -686,8 +695,9 @@ + | Field_UNKNOWN "mod_version" -> + let s = to_lowercase (string_of_tag_value tag.tag_value) in + parse_mod_version s c +- | _ -> () +- ++ | _ -> ++ if !verbose_msg_clienttags then ++ lprintf_nl "Unknown Emule tag: [%s] (update_client_from_tags)" (escaped_string_of_field tag) + ) tags + + let update_emule_proto_from_tags c tags = +@@ -727,9 +737,15 @@ + | Field_UNKNOWN "mod_version" -> + let s = to_lowercase (string_of_tag_value tag.tag_value) in + parse_mod_version s c; ++ ++ | Field_UNKNOWN "os_info" -> ++ let s = to_lowercase (string_of_tag_value tag.tag_value) in ++ (match c.client_osinfo with ++ Some _ -> () ++ | _ -> if s <> "" then c.client_osinfo <- Some s) + | _ -> +- if !verbose_msg_clients then +- lprintf_nl "Unknown Emule tag: [%s]" (escaped_string_of_field tag) ++ if !verbose_msg_clienttags then ++ lprintf_nl "Unknown Emule tag: [%s] (update_emule_proto_from_tags)" (escaped_string_of_field tag) + ) tags + + let fight_disguised_mods c = +@@ -743,6 +759,19 @@ + if c.client_brand = Brand_emuleplus && c.client_brand_mod = Brand_mod_plus then + c.client_brand_mod <- Brand_mod_unknown + ++let request_osinfo c = ++ if c.client_emule_proto.emule_osinfosupport = 1 && not c.client_osinfo_sent then ++ begin ++ let emule_osinfo = { ++ emule_info with ++ DonkeyProtoClient.EmuleClientInfo.protversion = 255; ++ DonkeyProtoClient.EmuleClientInfo.tags = [ ++ string_tag (Field_UNKNOWN "os_info") (String2.upp_initial Autoconf.system); ++ ]} in ++ client_send c (DonkeyProtoClient.EmuleClientInfoReq emule_osinfo); ++ c.client_osinfo_sent <- true ++ end ++ + let rec query_id ip port id = + let client_ip = client_ip None in + +@@ -1049,14 +1078,14 @@ + let process_mule_info c t = + update_emule_proto_from_tags c t; + update_emule_release c; +- if !!enable_sui ++ client_must_update c; ++ if sec_ident_enabled () + && (c.client_md4 <> Md4.null) + && (c.client_sent_challenge == Int64.zero) + && (c.client_emule_proto.emule_secident > 0) + then begin +- if !verbose_msg_clients then begin ++ if !verbose_msg_clients then + lprintf_nl "%s [process_mule_info] [verify_ident]" (full_client_identifier c); +- end; + verify_ident c + end + +@@ -1108,7 +1137,7 @@ + + init_client_after_first_message sock c; + +- set_client_has_a_slot (as_client c) false; ++ set_client_has_a_slot (as_client c) NoSlot; + + let module CR = M.Connect in + +@@ -1178,14 +1207,10 @@ + if !!emule_mods_count then + identify_client_brand_mod c t.CI.tags; + +- (* TODO : remove this comment +- ERR! i think the peer support eep if it send an emule client info +- PLUS this message is sent _before_ we received an md4 so +- client_brand is unknown here. +- if supports_eep c.client_brand then begin +- *) +- let module E = M.EmuleClientInfo in +- client_send c (M.EmuleClientInfoReplyReq emule_info) ++ let module E = M.EmuleClientInfo in ++ client_send c (M.EmuleClientInfoReplyReq emule_info); ++ request_osinfo c; ++ + + | M.EmuleClientInfoReplyReq t -> + +@@ -1272,9 +1297,6 @@ + (Printexc2.to_string e); + end; + +- | M.EmuleFileDescReq (rate, comment) -> +- if comment <> "" then set_file_comment c.client_last_asked_file comment +- + | M.AvailableSlotReq _ -> + set_lifetime sock active_lifetime; + set_rtimeout sock !!queued_timeout; +@@ -1367,8 +1389,11 @@ + end else *) + CommonUploads.add_pending_slot (as_client c); + if !verbose_upload then +- lprintf_nl "donkeyClient: uploader couldn't get a slot: %s" +- (full_client_identifier c); ++ lprintf_nl "added to pending slots: %s %s" ++ (full_client_identifier c) ++ (match client_upload (as_client c) with ++ None -> "" ++ | Some f -> CommonFile.file_best_name f); + (* end *) + + | M.CloseSlotReq _ -> +@@ -1424,6 +1449,28 @@ + with _ -> () + end + ++ | M.EmuleFileDescReq t -> ++ begin ++ match c.client_last_file_req_md4 with ++ Some md4 -> ++ begin ++ try ++ let file = find_file md4 in ++ let module Q = M.EmuleFileDesc in ++ let slen = String.length t.Q.comment in ++ if slen > 0 && slen <= !!max_comment_length && (!is_not_comment_spam) t.Q.comment then begin ++ (* Disallow dups from single IP, but allow comment updates *) ++ file.file_comments <- List.filter (fun (i,_,_,_) -> i <> c.client_ip) file.file_comments; ++ if List.length file.file_comments < !!max_comments_per_file then begin ++ file.file_comments <- (c.client_ip, c.client_name, t.Q.rating, (intern t.Q.comment)) :: file.file_comments; ++ file_must_update file; ++ end; ++ end ++ with _ -> () ++ end ++ | None -> () ++ end ++ + | M.QueryChunksReplyReq t -> + let module Q = M.QueryChunksReply in + begin +@@ -1675,7 +1722,7 @@ + shared_must_update_downloaded (as_shared impl); + impl.impl_shared_requests <- impl.impl_shared_requests + 1); + request_for c file sock; +- set_client_upload (as_client c) (shared_of_file file); ++ set_client_upload (as_client c) (as_file file); + client_send c ( + let module Q = M.QueryFileReply in + let filename = file_best_name file in +@@ -1708,7 +1755,7 @@ + end + + | M.EmuleSignatureReq t -> +- if !!enable_sui then ++ if sec_ident_enabled () then + begin + let module Q = M.EmuleSignatureReq in + +@@ -1763,7 +1810,7 @@ + end + + | M.EmulePublicKeyReq t -> +- if !!enable_sui then ++ if sec_ident_enabled () then + begin + let module Q = M.EmulePublicKeyReq in + (match c.client_public_key with +@@ -1788,12 +1835,11 @@ + ); + end + else +- if !verbose_msg_clients then begin +- lprintf_nl "%s [EPubKeyReq] [DISABLED]" (full_client_identifier c) ; +- end ++ if !verbose_msg_clients then ++ lprintf_nl "%s [EPubKeyReq] [DISABLED]" (full_client_identifier c); + + | M.EmuleSecIdentStateReq t -> +- if !!enable_sui then ++ if sec_ident_enabled () then + begin + let module Q = M.EmuleSecIdentStateReq in + +@@ -1816,9 +1862,8 @@ + then send_signature c; + + end else +- if !verbose_msg_clients then begin +- lprintf_nl "%s [ESecIdentStateReq] [DISABLED]" (full_client_identifier c) ; +- end ++ if !verbose_msg_clients then ++ lprintf_nl "%s [ESecIdentStateReq] [DISABLED]" (full_client_identifier c); + + | M.EmuleRequestSourcesReplyReq t -> + (* lprintf "Emule sent sources\n"; *) +@@ -1964,7 +2009,7 @@ + let module Q = M.QueryBloc in + let file = find_file t.Q.md4 in + let prio = (file_priority file) in +- let client_upload_lifetime = ref ((maxi 0 !!upload_lifetime) * 60) in ++ let client_upload_lifetime = ref ((max 0 !!upload_lifetime) * 60) in + begin + + if !!dynamic_upload_lifetime +@@ -2011,7 +2056,7 @@ + new_chunk up t.Q.start_pos2 t.Q.end_pos2; + new_chunk up t.Q.start_pos3 t.Q.end_pos3; + c.client_upload <- Some up; +- set_client_upload (as_client c) (shared_of_file file); ++ set_client_upload (as_client c) (as_file file); + if not waiting && !CommonUploads.has_upload = 0 then begin + CommonUploads.ready_for_upload (as_client c); + up.up_waiting <- true +@@ -2107,7 +2152,7 @@ + (* c.client_block <- None; *) + (* c.client_zones <- []; *) + c.client_file_queue <- []; +- set_client_has_a_slot (as_client c) false; ++ set_client_has_a_slot (as_client c) NoSlot; + c.client_upload <- None; + c.client_rank <- 0; + c.client_requests_received <- 0; +@@ -2118,7 +2163,8 @@ + let module M = DonkeyProtoClient in + + if !verbose_msg_clients then begin +- lprintf_nl "Message from incoming client"; ++ lprintf_nl "Message from incoming client %s:%d" ++ (Ip.to_string (peer_ip sock)) (peer_port sock); + M.print m; + end; + +@@ -2224,6 +2270,7 @@ + client_send c (M.EmuleClientInfoReq emule_info) + end; + ++ request_osinfo c; + client_send c ( + let module M = DonkeyProtoClient in + let module C = M.Connect in +@@ -2261,7 +2308,14 @@ + | M.NewUserIDReq _ -> + lprintf_nl "NewUserIDReq: "; M.print m; + None +- ++ ++ | M.EmulePortTestReq t -> ++ porttest_sock := Some sock; ++ set_closer sock (fun _ _ -> porttest_sock := None); ++ set_lifetime sock 30.; ++ write_string sock (client_msg_to_string (emule_proto ()) m); ++ None ++ + | _ -> + if !verbose_unknown_messages then + begin +@@ -2446,11 +2500,11 @@ + (if is_connecting_server then + ( try + let s = Hashtbl.find servers_by_key from_ip in +- Printf.sprintf " %s (%s)" s.server_name (Ip.to_string s.server_ip) ++ Printf.sprintf " %s (%s)" s.server_name (string_of_server s) + with _ -> + try + let s = Hashtbl.find servers_by_key connecting_server in +- Printf.sprintf " %s (%s)" s.server_name (Ip.to_string s.server_ip) ++ Printf.sprintf " %s (%s)" s.server_name (string_of_server s) + with _ -> "Unknown server" + ) + else "" +@@ -2510,7 +2564,6 @@ + try + let c = find_client_by_key s_uid in + let file = find_file (Md4.of_string file_uid) in +- c.client_last_asked_file <- (as_file file); + c.client_requests_sent <- c.client_requests_sent + 1; + let module M = DonkeyProtoClient in + +@@ -2538,6 +2591,7 @@ + (* TODO build the extension if needed *) + M.QueryFile.emule_extension = emule_extension; + }); ++ c.client_last_file_req_md4 <- Some file.file_md4; + let know_file_chunks = List.exists (fun (f,_,_) -> f == file) c.client_file_queue in + if not know_file_chunks then + DonkeyProtoCom.client_send c ( +@@ -2560,7 +2614,7 @@ + | Invalid_address _ -> () + | Indirect_address (server_ip, server_port, id, port, real_ip) -> + +- if low_id server_ip && Ip.reachable server_ip then ++ if Ip.reachable server_ip then + query_id server_ip server_port id; + + with e -> +@@ -2598,7 +2652,7 @@ + (CommonClient.as_client c.client_client); + + with +- Not_found -> () ++ | Not_found -> () + | e -> + if !verbose then + lprintf_nl "add_location: exception %s" (Printexc2.to_string e) +@@ -2613,9 +2667,9 @@ + (CommonClient.as_client c.client_client); + + with +- Not_found -> () ++ | Not_found -> () + | e -> + if !verbose then + lprintf_nl "remove_location for file_md4 %s: exception %s" +- file_uid (Printexc2.to_string e) ++ file_uid (Printexc2.to_string e) + ) +Index: src/networks/donkey/donkeyComplexOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyComplexOptions.ml,v +retrieving revision 1.57 +retrieving revision 1.59 +diff -u -r1.57 -r1.59 +--- src/networks/donkey/donkeyComplexOptions.ml 5 Sep 2006 14:15:19 -0000 1.57 ++++ src/networks/donkey/donkeyComplexOptions.ml 21 Nov 2006 22:34:33 -0000 1.59 +@@ -193,7 +193,7 @@ + with _ -> ()); + (try + connection_set_last_conn l.server_connection_control +- (normalize_time (mini (get_value "server_age" value_to_int) ++ (normalize_time (min (get_value "server_age" value_to_int) + (BasicSocket.last_time ()))); + with _ -> ()); + as_server l.server_server +@@ -273,7 +273,7 @@ + in + + let file = DonkeyGlobals.new_file file_diskname file_state +- (Md4.of_string file_md4) file_size "" true in ++ (Md4.of_string file_md4) file_size "" true CommonUserDb.admin_user in + + (try + set_file_best_name (as_file file) +Index: src/networks/donkey/donkeyFiles.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyFiles.ml,v +retrieving revision 1.20 +retrieving revision 1.22 +diff -u -r1.20 -r1.22 +--- src/networks/donkey/donkeyFiles.ml 19 May 2006 23:43:54 -0000 1.20 ++++ src/networks/donkey/donkeyFiles.ml 21 Nov 2006 22:34:33 -0000 1.22 +@@ -79,8 +79,8 @@ + (* let len_int = Int32.to_int len in *) + try + if !verbose_upload then +- lprintf_nl "send_small_block (%s) %Ld %d" +- (full_client_identifier c) ++ lprintf_nl "Sending %s to %s, begin %Ld len %d" ++ (file_best_name file) (full_client_identifier c) + (begin_pos) (len_int); + + let msg = +@@ -130,12 +130,12 @@ + end else + let max_len = up.up_end_chunk -- up.up_pos in + let max_len = Int64.to_int max_len in +- let msg_block_size_int = mini msg_block_size_int per_client in ++ let msg_block_size_int = min msg_block_size_int per_client in + if max_len <= msg_block_size_int then + (* last block from chunk *) + begin + if !verbose_upload then +- lprintf_nl "END OF CHUNK (%d) %Ld" max_len up.up_end_chunk; ++ lprintf_nl "End of chunk (%d) %Ld %s" max_len up.up_end_chunk (file_best_name up.up_file); + send_small_block c sock up.up_file up.up_pos max_len; + up.up_chunks <- chunks; + let per_client = per_client - max_len in +@@ -165,7 +165,7 @@ + do_if_connected c.client_source.DonkeySources.source_sock (fun sock -> + (* lprintf "upload_to_client %d connected\n" (maxi max_msg_size size); *) + +- let size = mini max_msg_size size in ++ let size = min max_msg_size size in + send_client_block c sock size; + (match c.client_upload with + None -> () +Index: src/networks/donkey/donkeyGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyGlobals.ml,v +retrieving revision 1.96 +retrieving revision 1.107 +diff -u -r1.96 -r1.107 +--- src/networks/donkey/donkeyGlobals.ml 16 Sep 2006 15:36:59 -0000 1.96 ++++ src/networks/donkey/donkeyGlobals.ml 21 Nov 2006 21:38:00 -0000 1.107 +@@ -125,8 +125,6 @@ + let tag_server = 201 + let tag_file = 202 + +-let page_size = 4096L +- + let donkey_download_counter = ref Int64.zero + let donkey_upload_counter = ref Int64.zero + +@@ -140,6 +138,8 @@ + E.tags = []; + } + ++let sec_ident_enabled () = !!enable_sui && (Autoconf.donkey_sui_works ()) ++ + let overnet_connectreply_tags = ref ([] : tag list) + let overnet_connect_tags = ref ([] : tag list) + +@@ -184,8 +184,7 @@ + let master_server = ref (None: DonkeyTypes.server option) + let udp_sock = ref (None: UdpSocket.t option) + let listen_sock = ref (None : TcpServerSocket.t option) +-let reversed_sock = ref (None : TcpServerSocket.t option) +-let new_shared = ref false ++let porttest_sock = ref (None : TcpBufferedSocket.t option) + + (*************************************************************************) + (* *) +@@ -230,7 +229,6 @@ + let shared_files_info = (Hashtbl.create 127 + : (string * int64 * float, shared_file_info) Hashtbl.t) + let shared_files = ref ([] : file_to_share list) +-let new_shared_files = ref [] + + let udp_servers_replies = (Hashtbl.create 127 : (Md4.t, server) Hashtbl.t) + +@@ -311,7 +309,7 @@ + set_file_best_name file best_name "" 0 + with Not_found -> () + +-let new_file file_diskname file_state md4 file_size filename writable = ++let new_file file_diskname file_state md4 file_size filename writable user = + + try + let file = find_file md4 in +@@ -379,8 +377,8 @@ + (try + Unix32.remove t + with e -> +- lprintf_nl "Unix32.remove %s exception %s" +- (file_diskname) (Printexc2.to_string e)); ++ lprintf_nl "Unix32.remove %s exception %s" ++ (file_diskname) (Printexc2.to_string e)); + Unix32.destroy t; + failwith (Printf.sprintf "file size %s is too big, exception: %s" + (size_of_int64 file_size) (Printexc2.to_string e)) +@@ -398,10 +396,13 @@ + file_computed_md4s = Array.of_list md4s; + file_format = FormatNotComputed 0; + file_sources = DonkeySources.create_file_sources_manager +- (Md4.to_string md4) ++ (Md4.to_string md4); ++ file_comments = []; + } + and file_impl = { + dummy_file_impl with ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_val = file; + impl_file_ops = file_ops; + impl_file_age = last_time (); +@@ -427,10 +428,7 @@ + Verification (Array.of_list (List.map (fun md4 -> Ed2k md4) md4s)) + ); + CommonSwarming.set_verified swarmer (fun nblocks num -> +- if nblocks = 1 then begin +- new_shared_files := file :: !new_shared_files; +- file_must_update file +- end) ++ if nblocks = 1 then file_must_update file) + ); + + update_best_name file; +@@ -466,7 +464,10 @@ + let is_black_address ip port = + !!black_list && not (low_id ip) && ( + (* lprintf "is black ="; *) +- not (Ip.reachable ip) || (Ip.matches ip !!server_black_list) || ++ not (Ip.reachable ip) || ++ (match Ip_set.match_ip !server_black_list_set ip with ++ | Some br -> true ++ | None -> false) || + (List.mem port !!port_black_list) || + (match !Ip.banned ip with + None -> false +@@ -513,13 +514,27 @@ + server_id_requests = Fifo.create (); + server_flags = 0; + server_has_zlib = false; ++ server_has_newtags = false; ++ server_has_unicode = false; ++ server_has_related_search = false; ++ server_has_tag_integer = false; ++ server_has_largefiles = false; ++ server_has_udp_obfuscation = false; ++ server_has_tcp_obfuscation = false; + server_version = ""; + server_lowid_users = None; + server_soft_limit = None; + server_hard_limit = None; ++ server_obfuscation_port_tcp = None; ++ server_obfuscation_port_udp = None; ++ server_udp_key = None; ++ server_udp_keyip = None; ++ server_sent_shared = []; + server_max_users = None; + server_last_ping = 0.; + server_ping = 0; ++ server_dynip = ""; ++ server_auxportslist = ""; + + } + and server_impl = +@@ -565,11 +580,11 @@ + client_file_queue = []; + client_tags = []; + client_name = ""; +- client_last_asked_file = dummy_file; + client_all_files = None; + client_rating = 0; + client_brand = Brand_unknown; + client_brand_mod = Brand_mod_unknown; ++ client_osinfo = None; + client_checked = false; + client_connected = false; + client_downloaded = Int64.zero; +@@ -592,6 +607,8 @@ + client_sent_challenge = Int64.zero; + client_public_key = None; + client_sui_verified = None; ++ client_last_file_req_md4 = None; ++ client_osinfo_sent = false; + } and + client_impl = { + dummy_client_impl with +@@ -604,7 +621,9 @@ + + let create_client key = + let module D = DonkeyProtoClient in +- let s = DonkeySources.find_source_by_uid key in ++ let s = DonkeySources.find_source_by_uid (match key with ++ Indirect_address (server_ip, server_port, id, port, real_ip) -> Indirect_address (server_ip, server_port, id, 0, Ip.null) ++ | _ -> key) in + let rec c = { + client_client = client_impl; + client_kind = key; +@@ -616,11 +635,11 @@ + client_file_queue = []; + client_tags = []; + client_name = ""; +- client_last_asked_file = dummy_file; + client_all_files = None; + client_rating = 0; + client_brand = Brand_unknown; + client_brand_mod = Brand_mod_unknown; ++ client_osinfo = None; + client_checked = false; + client_connected = false; + client_downloaded = Int64.zero; +@@ -643,6 +662,8 @@ + client_sent_challenge = Int64.zero; + client_public_key = None; + client_sui_verified = None; ++ client_last_file_req_md4 = None; ++ client_osinfo_sent = false; + } and client_impl = { + dummy_client_impl with + impl_client_val = c; +@@ -656,17 +677,27 @@ + clients_root := c :: !clients_root; + c + ++exception ClientFound of client ++let find_client_by_key key = ++ try ++ H.iter (fun c -> ++ if (match c.client_kind with ++ Indirect_address (server_ip, server_port, id, port, real_ip) -> Indirect_address (server_ip, server_port, id, 0, Ip.null) ++ | _ -> c.client_kind) = (match key with ++ Indirect_address (server_ip, server_port, id, port, real_ip) -> Indirect_address (server_ip, server_port, id, 0, Ip.null) ++ | _ -> key) then raise (ClientFound c) ++ ) clients_by_kind; ++ raise Not_found ++ with ClientFound c -> c ++ + let new_client key = + try +- H.find clients_by_kind { dummy_client with client_kind = key } ++ find_client_by_key key + with _ -> + create_client key + + let create_client = () + +-let find_client_by_key key = +- H.find clients_by_kind { dummy_client with client_kind = key } +- + let client_type c = + client_type (as_client c) + +@@ -687,6 +718,8 @@ + | Invalid_address _ -> "" + ) + ++let string_of_server s = ++ Printf.sprintf "%s:%d" (Ip.to_string s.server_ip) s.server_port + + let set_client_name c name md4 = + if name <> c.client_name || c.client_md4 <> md4 then begin +@@ -798,7 +831,6 @@ + Printf.bprintf buf " udp_servers_list: %d\n" (List.length !udp_servers_list); + Printf.bprintf buf " interesting_clients: %d\n" (List.length !interesting_clients); + Printf.bprintf buf " shared_files: %d\n" (List.length !shared_files); +- Printf.bprintf buf " new_shared_files: %d\n" (List.length !new_shared_files); + Printf.bprintf buf " servers_by_key: %d\n" (Hashtbl.length servers_by_key); + Printf.bprintf buf " banned_ips: %d\n" (Hashtbl.length banned_ips); + Printf.bprintf buf " old_requests: %d\n" (Hashtbl.length old_requests); +Index: src/networks/donkey/donkeyImport.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyImport.ml,v +retrieving revision 1.9 +retrieving revision 1.10 +diff -u -r1.9 -r1.10 +--- src/networks/donkey/donkeyImport.ml 3 Apr 2006 20:50:09 -0000 1.9 ++++ src/networks/donkey/donkeyImport.ml 14 Nov 2006 18:42:59 -0000 1.10 +@@ -54,14 +54,28 @@ + + + let names_of_tag = ++(* eMule sourcefile opcodes.h //server.met *) + [ +- "\001", Field_UNKNOWN "name"; +- "\015", Field_UNKNOWN "port"; +- "\016", Field_UNKNOWN "ip"; +- "\012", Field_UNKNOWN "ping"; +- "\014", Field_UNKNOWN "prof"; +- "\013", Field_UNKNOWN "history"; +- "\011", Field_UNKNOWN "description"; ++ "\001", Field_UNKNOWN "name"; (* 0x01 string *) ++ "\011", Field_UNKNOWN "description"; (* 0x0B string *) ++ "\012", Field_UNKNOWN "ping"; (* 0x0C uint32 *) ++ "\013", Field_UNKNOWN "history"; (* 0x0D ST_FAIL *) ++ "\014", Field_UNKNOWN "prof"; (* 0x0E ST_PREFERENCE *) ++ "\015", Field_UNKNOWN "port"; (* 0x0F uint32 *) ++ "\016", Field_UNKNOWN "ip"; (* 0x10 uint32 *) ++ "\133", Field_UNKNOWN "dynip"; (* 0x85 string *) ++ "\135", Field_UNKNOWN "maxusers"; (* 0x87 uint32 *) ++ "\136", Field_UNKNOWN "softfiles"; (* 0x88 uint32 *) ++ "\137", Field_UNKNOWN "hardfiles"; (* 0x89 uint32 *) ++ "\144", Field_UNKNOWN "lastping"; (* 0x90 uint32 *) ++ "\145", Field_UNKNOWN "version"; (* 0x91 string|uint32 *) ++ "\146", Field_UNKNOWN "udpflags"; (* 0x92 uint32 *) ++ "\147", Field_UNKNOWN "auxportslist"; (* 0x93 string *) ++ "\148", Field_UNKNOWN "lowidusers"; (* 0x94 uint32 *) ++ "\149", Field_UNKNOWN "udpkey"; (* 0x95 uint32 *) ++ "\150", Field_UNKNOWN "udpkeyip"; (* 0x96 uint32 *) ++ "\151", Field_UNKNOWN "tcpportobfuscation"; (* 0x97 uint16 *) ++ "\152", Field_UNKNOWN "udpportobfuscation"; (* 0x98 uint16 *) + ] + + +Index: src/networks/donkey/donkeyInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyInteractive.ml,v +retrieving revision 1.122 +retrieving revision 1.138 +diff -u -r1.122 -r1.138 +--- src/networks/donkey/donkeyInteractive.ml 5 Sep 2006 14:18:24 -0000 1.122 ++++ src/networks/donkey/donkeyInteractive.ml 20 Nov 2006 22:48:43 -0000 1.138 +@@ -21,6 +21,8 @@ + open Printf2 + open Md4 + open Options ++open LittleEndian ++open AnyEndian + + open BasicSocket + open TcpBufferedSocket +@@ -60,8 +62,15 @@ + let _s x = _s "DonkeyInteractive" x + let _b x = _b "DonkeyInteractive" x + ++let porttest_result = ref PorttestNotStarted ++ + module VB = VerificationBitmap + ++let log_prefix = "[EDK]" ++ ++let lprintf_nl fmt = ++ lprintf_nl2 log_prefix fmt ++ + let result_name r = + match r.result_names with + [] -> None +@@ -104,12 +113,49 @@ + let server = check_add_server r.S.ip r.S.port in + List.iter (fun tag -> + match tag with +- { tag_name = Field_UNKNOWN "name"; tag_value = String s } -> ++ | { tag_name = Field_UNKNOWN "name"; tag_value = String s } -> + server.server_name <- s; + | { tag_name = Field_UNKNOWN "description" ; tag_value = String s } -> + server.server_description <- s +- | _ -> () +- ) r.S.tags ++ | { tag_name = Field_UNKNOWN "version" ; tag_value = Uint64 s } -> ++ server.server_version <- Printf.sprintf "%d.%d" ++ ((Int64.to_int s) lsr 16) ((Int64.to_int s) land 0xFFFF) ++ | { tag_name = Field_UNKNOWN "ping" ; tag_value = Uint64 s } -> ++ server.server_ping <- (Int64.to_int s) ++ | { tag_name = Field_UNKNOWN "dynip" ; tag_value = String s } -> ++ server.server_dynip <- s ++ | { tag_name = Field_UNKNOWN "users" ; tag_value = Uint64 s } -> ++ (match server.server_nusers with ++ | None -> server.server_nusers <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "files" ; tag_value = Uint64 s } -> ++ (match server.server_nfiles with ++ | None -> server.server_nfiles <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "maxusers" ; tag_value = Uint64 s } -> ++ (match server.server_max_users with ++ | None -> server.server_max_users <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "softfiles" ; tag_value = Uint64 s } -> ++ (match server.server_soft_limit with ++ | None -> server.server_soft_limit <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "hardfiles" ; tag_value = Uint64 s } -> ++ (match server.server_hard_limit with ++ | None -> server.server_hard_limit <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "auxportslist" ; tag_value = String s } -> ++ server.server_auxportslist <- s ++ | { tag_name = Field_UNKNOWN "lowusers" ; tag_value = Uint64 s } -> ++ (match server.server_lowid_users with ++ | None -> server.server_lowid_users <- Some s | _ -> ()) ++ | { tag_name = Field_UNKNOWN "tcpportobfuscation" ; tag_value = Uint64 s } -> ++ (match server.server_obfuscation_port_tcp with ++ | None -> server.server_obfuscation_port_tcp <- Some (Int64.to_int s) | _ -> ()) ++ | { tag_name = Field_UNKNOWN "udpportobfuscation" ; tag_value = Uint64 s } -> ++ (match server.server_obfuscation_port_udp with ++ | None -> server.server_obfuscation_port_udp <- Some (Int64.to_int s) | _ -> ()) ++ | { tag_name = Field_UNKNOWN "country" ; tag_value = String s } -> () ++ | { tag_name = Field_UNKNOWN "udpflags" ; tag_value = Uint64 s } -> () ++ | { tag_name = Field_UNKNOWN "refs" ; tag_value = Uint64 s } -> () ++ | _ -> lprintf_nl "parsing server.met, unknown field %s" (string_of_tag tag) ++ ) r.S.tags; ++ server_must_update server + with _ -> () + ) ss; + List.length ss +@@ -181,14 +227,11 @@ + ) + + let already_done = Failure (Printf.sprintf (_b "File already downloaded (use 'force_download' if necessary)")) +- + let no_download_to_force = Failure (Printf.sprintf (_b "No forceable download found")) ++exception Already_downloading of string ++exception Already_shared of string + +-let already_downloading = Failure (Printf.sprintf (_b "File is already in download queue")) +- +-let already_shared = Failure (Printf.sprintf (_b "File is already shared")) +- +-let really_query_download filename size md4 location old_file absents = ++let really_query_download filename size md4 location old_file absents user = + + begin + try +@@ -226,7 +269,7 @@ + end; + + (* TODO RESULT let other_names = DonkeyIndexer.find_names md4 in *) +- let file = new_file file_diskname FileDownloading md4 size filename true in ++ let file = new_file file_diskname FileDownloading md4 size filename true user in + begin + match absents with + None -> () +@@ -295,7 +338,7 @@ + ); + as_file file + +-let query_download filename size md4 location old_file absents force = ++let query_download filename size md4 location old_file absents force user = + if force then + if !forceable_download = [] then + raise no_download_to_force +@@ -303,20 +346,23 @@ + begin + let f = List.hd !forceable_download in + forceable_download := []; +- really_query_download (List.hd f.result_names) f.result_size md4 None None None ++ really_query_download (List.hd f.result_names) f.result_size md4 None None None user + end + else + begin + try + let file = find_file md4 in + if (file_state file) = FileShared then +- raise already_shared ++ raise (Already_shared (Printf.sprintf (_b "File is already shared%s") ++ (match file.file_shared with ++ None -> "" ++ | Some sh -> (" in " ^ (Filename2.dirname sh.impl_shared_fullname))))) + else + begin + (* jave TODO: if a user currently not downloading this file is requesting the download add this user + to the list of users currently downloading this file *) + forceable_download := []; +- raise already_downloading ++ raise (Already_downloading (Printf.sprintf (_b "File is already in download queue of %s") (file_owner (as_file file)).CommonTypes.user_name)) + end + with Not_found -> + begin +@@ -336,19 +382,19 @@ + else + begin + forceable_download := []; +- really_query_download filename size md4 location old_file absents ++ really_query_download filename size md4 location old_file absents user + end + end + end + +-let result_download r filenames force = ++let result_download r filenames force user = + let rec iter uids = + match uids with + [] -> raise IgnoreNetwork + | uid :: tail -> + match Uid.to_uid uid with + Ed2k md4 -> +- query_download (List.hd filenames) r.result_size md4 None None None force ++ query_download (List.hd filenames) r.result_size md4 None None None force user + | _ -> iter tail + in + iter r.result_uids +@@ -391,7 +437,7 @@ + (match !filename_met with + None -> filename + | Some s -> s) !size f.P.md4 None +- (Some filename) (Some (List.rev f.P.absents))); ++ (Some filename) (Some (List.rev f.P.absents)) CommonUserDb.admin_user); + with _ -> () + ) list + +@@ -508,7 +554,7 @@ + + + +-let parse_donkey_url url = ++let parse_donkey_url url user = + let url = Str.global_replace (Str.regexp "|sources,") "|sources|" url in + match String2.split url '|' with + (* TODO RESULT *) +@@ -536,7 +582,7 @@ + begin + try + let file = query_download name (Int64.of_string size) +- (Md4.of_string md4) None None None false in ++ (Md4.of_string md4) None None None false user in + let new_file = find_file (Md4.of_string md4) in + CommonInteractive.start_download file; + if !new_sources <> [] then +@@ -547,7 +593,10 @@ + (Printf.sprintf (_b "added %d sources to new download") (List.length !new_sources)), true + end + else "", true +- with e -> (Printexc2.to_string e), false ++ with ++ Already_downloading (s) ++ | Already_shared (s) -> s, false ++ | e -> (Printexc2.to_string e), false + end + end + | "ed2k://" :: "file" :: name :: size :: md4 :: _ +@@ -566,12 +615,14 @@ + in + begin try + let file = query_download name (Int64.of_string size) +- (Md4.of_string md4) None None None false; ++ (Md4.of_string md4) None None None false user; + in + CommonInteractive.start_download file; + "", true +- with e -> +- (Printexc2.to_string e), false ++ with ++ Already_downloading (s) ++ | Already_shared (s) -> s, false ++ | e -> (Printexc2.to_string e), false + end + | "ed2k://" :: "server" :: ip :: port :: _ + | "server" :: ip :: port :: _ -> +@@ -713,8 +764,8 @@ + "id", Arg_none (fun o -> + let buf = o.conn_buf in + List.iter (fun s -> +- Printf.bprintf buf "For %s:%d (%s) ---> %s\n" +- (Ip.to_string s.server_ip) s.server_port s.server_name ++ Printf.bprintf buf "For %s (%s) ---> %s\n" ++ (string_of_server s) s.server_name + (match s.server_cid with + None -> "waiting" + | Some ip -> +@@ -764,11 +815,11 @@ + + "bs", Arg_multiple (fun args o -> + List.iter (fun arg -> +- let ip = Ip.of_string arg in +- server_black_list =:= ip :: !!server_black_list; ++ let range = Ip.range_of_string arg in ++ server_black_list =:= range :: !!server_black_list; + ) args; + "done" +- ), "<ip1> <ip2> ... :\t\t\tadd these IPs to the servers black list"; ++ ), "<range1> <range2> ... :\t\t\tadd these IPs to the servers black list (can be single IPs, CIDR ranges or begin-end ranges)"; + + "port", Arg_one (fun arg o -> + donkey_port =:= int_of_string arg; +@@ -776,6 +827,7 @@ + "<port> :\t\t\t\tchange connection port"; + + "scan_temp", Arg_none (fun o -> ++ if CommonUserDb.user2_is_admin o.conn_user.ui_user then begin + let buf = o.conn_buf in + let list = Unix2.list_directory !!temp_directory in + +@@ -873,24 +925,22 @@ + ) list; + + if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>"; ++ "" end ++ else begin ++ print_command_result o o.conn_buf "You are not allowed to use scan_temp"; ++ "" end + +- "" + ), ":\t\t\t\tprint temp directory content"; + + "sources", Arg_none (fun o -> +- let buf = o.conn_buf in +- DonkeySources.print buf o.conn_output; +- "" ++ if CommonUserDb.user2_is_admin o.conn_user.ui_user then begin ++ DonkeySources.print o.conn_buf o.conn_output; ++ "" end ++ else begin ++ print_command_result o o.conn_buf "You are not allowed to list sources"; ++ "" end + ), ":\t\t\t\tshow sources currently known"; + +- (* +- "update_sources", Arg_none (fun o -> +- let buf = o.conn_buf in +- DonkeySources.recompute_ready_sources (); +- "done" +- ), ":\t\t\trecompute order of connections to sources (experimental)"; +-*) +- + "xs", Arg_none (fun o -> + let buf = o.conn_buf in + if !xs_last_search >= 0 then begin +@@ -920,7 +970,7 @@ + (* TODO RESULT *) + "dd", Arg_two(fun size md4 o -> + let file = query_download md4 (Int64.of_string size) +- (Md4.of_string md4) None None None false in ++ (Md4.of_string md4) None None None false o.conn_user.ui_user in + CommonInteractive.start_download file; + "download started" + ), "<size> <md4> :\t\t\tdownload from size and md4"; +@@ -1003,8 +1053,8 @@ + with + Not_found -> () + ); +- network.op_network_download <- (fun r -> +- result_download r r.result_names r.result_force ++ network.op_network_download <- (fun r user -> ++ result_download r r.result_names r.result_force user + ) + + module P = GuiTypes +@@ -1042,6 +1092,7 @@ + P.file_format = file.file_format; + P.file_chunks_age = last_seen; + P.file_uids = [Uid.create (Ed2k file.file_md4)]; ++ P.file_comments = file.file_comments + } in + v + with e -> +@@ -1066,13 +1117,28 @@ + P.server_description = s.server_description; + P.server_banner = s.server_banner; + P.server_preferred = s.server_preferred; ++ P.server_master = s.server_master; ++ P.server_published_files = (List.length s.server_sent_shared); + P.server_version = s.server_version; + P.server_max_users = (match s.server_max_users with None -> 0L | Some v -> v); + P.server_soft_limit = (match s.server_soft_limit with None -> 0L | Some v -> v); + P.server_hard_limit = (match s.server_hard_limit with None -> 0L | Some v -> v); + P.server_lowid_users = (match s.server_lowid_users with None -> 0L | Some v -> v); + P.server_ping = s.server_ping; +- ++ P.server_features = let temp_buf = Buffer.create 100 in ++ if s.server_has_zlib then Printf.bprintf temp_buf "zlib "; ++ if s.server_has_newtags then Printf.bprintf temp_buf "newtags "; ++ if s.server_has_unicode then Printf.bprintf temp_buf "unicode "; ++ if s.server_has_related_search then Printf.bprintf temp_buf "related_search "; ++ if s.server_has_tag_integer then Printf.bprintf temp_buf "tag_integer "; ++ if s.server_has_largefiles then Printf.bprintf temp_buf "largefiles "; ++ (match s.server_obfuscation_port_tcp with ++ | Some p -> Printf.bprintf temp_buf "tcp_obfuscation(%d) " p | _ -> ()); ++ (match s.server_obfuscation_port_udp with ++ | Some p -> Printf.bprintf temp_buf "udp_obfuscation(%d) " p | _ -> ()); ++ if s.server_auxportslist <> "" then Printf.bprintf temp_buf "auxportslist %s " s.server_auxportslist; ++ if s.server_dynip <> "" then Printf.bprintf temp_buf "dynip %s " s.server_dynip; ++ if Buffer.contents temp_buf <> "" then Some (Buffer.contents temp_buf) else None; + } + else raise Not_found + ) +@@ -1125,14 +1191,15 @@ + P.client_rating = c.client_rating; + P.client_connect_time = c.client_connect_time; + P.client_software = brand_to_string_short c.client_brand; ++ P.client_os = c.client_osinfo; + P.client_release = c.client_emule_proto.emule_release; + P.client_emulemod = brand_mod_to_string_short c.client_brand_mod; + P.client_downloaded = c.client_downloaded; + P.client_uploaded = c.client_uploaded; + (* P.client_source.source_sock_addr = (); *) + P.client_upload = +- (match c.client_upload with +- Some cu -> Some (file_best_name cu.up_file) ++ (match client_upload (as_client c) with ++ Some f -> Some (CommonFile.file_best_name f) + | None -> None); + P.client_sui_verified = c.client_sui_verified; + } +@@ -1165,6 +1232,9 @@ + server_ops.op_server_users <- (fun s -> + List2.tail_map (fun u -> as_user u.user_user) s.server_users) ; + ++ server_ops.op_server_published <- (fun s -> ++ List.map (fun f -> as_file f) s.server_sent_shared); ++ + server_ops.op_server_cid <- (fun s -> ip_of_server_cid s); + + server_ops.op_server_low_id <- (fun s -> low_id (ip_of_server_cid s)); +@@ -1184,6 +1254,11 @@ + add_file_filenames (as_file file) name; + set_file_best_name (as_file file) name "" 0 + ); ++ file_ops.op_file_shared <- (fun file -> ++ match file.file_shared with ++ None -> None ++ | Some sh -> Some (as_shared sh) ++ ); + file_ops.op_file_set_format <- (fun file format -> + file.file_format <- format); + file_ops.op_file_check <- op_file_check; +@@ -1208,10 +1283,16 @@ + ) file.file_sources; + !list + ); +- file_ops.op_file_print_html <- (fun file buf -> +- +- html_mods_cntr_init (); ++ file_ops.op_file_print <- (fun file o -> + ++ let buf = o.conn_buf in ++ if not (use_html_mods o) then begin ++ let cntr = ref 0 in ++ List.iter (fun (ip, n, r, c) -> ++ incr cntr; ++ Printf.bprintf buf ++ "Comment %d: Rating(%d): %s (%s/%s)\n" !cntr r (Charset.to_utf8 c) n (Ip.to_string ip)) file.file_comments ++ end else begin + let tr () = + Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ()) + in +@@ -1223,6 +1304,16 @@ + (Md4.to_string file.file_md4) (Md4.to_string file.file_md4) + ) ]; + ++ let cntr = ref 0 in ++ List.iter (fun (ip, n, r, c) -> ++ incr cntr; ++ tr (); ++ html_mods_td buf [ ++ ("Comment", "sr br", Printf.sprintf "Comment %d" !cntr); ++ ("User rating and comment", "sr", Printf.sprintf "Rating(%d): %s (%s/%s)" r (Charset.to_utf8 c) n (Ip.to_string ip)); ++ ]; ++ ) file.file_comments; ++ + tr (); + html_mods_td buf [ + ("File History Links", "sr br", "File History"); +@@ -1257,19 +1348,22 @@ + //--\\> + \\</script\\>" (file_num file) + +- ^ "\\<table border=0 cellspacing=0 cellpadding=0\\>\\<tr\\>\\<td\\>" ++ ^ "\\<table border=0 cellspacing=0 cellpadding=0\\>\\<tr\\>" + ^ "\\<form name=\\\"renameForm1\\\" id=\\\"renameForm1\\\" action=\\\"javascript:submitRenameForm(1);\\\"\\>" ++ ^ "\\<td\\>" + ^ "\\<select name=\\\"newName\\\" id=\\\"newName\\\" onchange=\\\"javascript:renameForm2.newName.value=renameForm1.newName.options[renameForm1.newName.selectedIndex].value;this.form.submit();\\\"\\>" + ^ Printf.sprintf "\\<option value=\\\"%s\\\" selected\\>%s\n" (file_best_name file) (file_best_name file) + ^ !optionlist +- ^ "\\</select\\>\\</td\\>\\</tr\\>\\</form\\>\\<tr\\>\\<td\\>\\<form name=\\\"renameForm2\\\" id=\\\"renameForm2\\\" action=\\\"javascript:submitRenameForm(2);\\\"\\>" +- ^ "\\<input name=\\\"newName\\\" type=text size=" ^ input_size ^ " value=\\\"" ^ (file_best_name file) ^ "\\\"\\>\\</input\\>\\</td\\>\\</tr\\>\\</form\\>\\</table\\>" ++ ^ "\\</select\\>\\</td\\>\\</form\\>\\</tr\\>\\<tr\\>\\<form name=\\\"renameForm2\\\" id=\\\"renameForm2\\\" action=\\\"javascript:submitRenameForm(2);\\\"\\>" ++ ^ "\\<td\\>" ++ ^ "\\<input name=\\\"newName\\\" type=text size=" ^ input_size ^ " value=\\\"" ^ (file_best_name file) ^ "\\\"\\>\\</input\\>\\</td\\>\\</form\\>\\</tr\\>\\</table\\>" + ) ]; +- +- ++ end + ); +- file_ops.op_file_print_sources_html <- (fun file buf -> ++ file_ops.op_file_print_sources <- (fun file o -> + ++ if not (use_html_mods o) then raise Not_found; ++ let buf = o.conn_buf in + let sources_list = ref [] in + DonkeySources.iter_relevant_sources (fun s -> + let s_uid = s.DonkeySources.source_uid in +@@ -1362,7 +1456,8 @@ + ((string_of_connection_state (client_state c)), "sr", + (short_string_of_connection_state (client_state c)) ); + (String.escaped c.client_name, "sr", client_short_name c.client_name); +- (brand_to_string c.client_brand, "sr", brand_to_string_short c.client_brand); ++ (client_software (brand_to_string c.client_brand) c.client_osinfo, "sr", ++ client_software_short (brand_to_string_short c.client_brand) c.client_osinfo); + ("", "sr", c.client_emule_proto.emule_release); + + ] @ +@@ -1456,7 +1551,7 @@ + let size = Unix32.getsize file_diskname in + if size <> zero then + begin +- ignore (really_query_download (Md4.to_string md4) size md4 None (Some file_diskname) None); ++ ignore (really_query_download (Md4.to_string md4) size md4 None (Some file_diskname) None CommonUserDb.admin_user); + recover_md4s md4 + end + +@@ -1508,6 +1603,28 @@ + network.op_network_reset <- (fun _ -> ()); + + network.op_network_close_search <- (fun s -> ()); ++ network.op_network_porttest_start <- (fun _ -> ++ porttest_result := PorttestInProgress (last_time ()); ++ let module H = Http_client in ++ let r = { H.basic_request with ++ H.req_url = Url.of_string ++ (Printf.sprintf "http://porttest.emule-project.net:81/ct_noframe.php?lang=&tcpport=%d&udpport=%d" ++ !!donkey_port (!!donkey_port + 4)); ++ H.req_proxy = !CommonOptions.http_proxy; ++ H.req_max_retry = 10; ++ H.req_user_agent = get_user_agent () } in ++ H.wget r (fun file -> ++ Unix2.tryopen_read file (fun cin -> ++ try ++ while true do ++ let line = input_line cin in ++ try ++ if Str.string_match (Str.regexp "^<P>Testing IP") line 0 then ++ porttest_result := PorttestResult (last_time (), line) ++ with _ -> () ++ done ++ with End_of_file -> ()) ++ )); + network.op_network_forget_search <- forget_search + + (* emule<->mldonkey disconnects during chat, and this doesn't seem to auto reconnect +@@ -1685,18 +1802,17 @@ + (fun url filename -> + if !!enable_donkey && !!update_server_list_server_met then + begin +- lprintf_n "server.met loaded from %s" url; ++ lprintf_nl "server.met loaded from %s" url; + begin + try + let s = unpack_server_met filename url in + let nservers = List.length (Hashtbl2.to_list servers_by_key) in + let n = load_server_met s in + if s <> filename then Sys.remove s; +- lprintf ", %d servers found, %d new ones inserted" ++ lprintf_nl "%d servers found, %d new ones inserted" + n ((List.length (Hashtbl2.to_list servers_by_key)) - nservers) + with _ -> () + end; +- lprint_newline () + end + else + if not !!enable_donkey then +Index: src/networks/donkey/donkeyMain.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyMain.ml,v +retrieving revision 1.57 +retrieving revision 1.61 +diff -u -r1.57 -r1.61 +--- src/networks/donkey/donkeyMain.ml 5 Sep 2006 14:15:20 -0000 1.57 ++++ src/networks/donkey/donkeyMain.ml 21 Nov 2006 21:38:00 -0000 1.61 +@@ -69,7 +69,6 @@ + clean_join_queue_tables () + + let fivemin_timer timer = +- DonkeyShare.send_new_shared (); + clients_root := [] + + let second_timer timer = +@@ -95,10 +94,7 @@ + (try + DonkeyServers.query_locations_timer (); + with _ -> ()); +- (List.iter (fun file -> DonkeyShare.must_share_file file) !new_shared_files; +- new_shared_files := []) +-(* DonkeyIndexer.add_to_local_index_timer () *) +- ++ DonkeyShare.send_new_shared () + + let local_login () = + if !!login = "" then !!global_login else !!login +@@ -118,10 +114,6 @@ + | Some sock -> + listen_sock := None; + TcpServerSocket.close sock Closed_by_user); +- (match !reversed_sock with None -> () +- | Some sock -> +- reversed_sock := None; +- TcpServerSocket.close sock Closed_by_user); + (match !udp_sock with None -> () + | Some sock -> + udp_sock := None; +@@ -136,11 +128,12 @@ + let module D = DonkeyProtoClient in + let m = D.mldonkey_emule_proto in + +- let secident = if !!enable_sui then 3 else 0 in ++ let secident = if sec_ident_enabled () then 3 else 0 in + m.emule_secident <- secident; + m.emule_features <- secident; + + let emule_miscoptions1 = D.emule_miscoptions1 m in ++ let emule_compatoptions = D.emule_compatoptions m in + client_to_client_tags := + [ + string_tag (Field_UNKNOWN "name") (local_login ()); +@@ -149,6 +142,7 @@ + int_tag (Field_UNKNOWN "emule_udpports") (!!donkey_port+4); + int_tag (Field_UNKNOWN "emule_version") m.emule_version; + int64_tag (Field_UNKNOWN "emule_miscoptions1") emule_miscoptions1; ++ int_tag (Field_UNKNOWN "emule_compatoptions") emule_compatoptions; + ]; + + let extended = ref 0x04 in (* support of auxport *) +@@ -302,9 +296,7 @@ + + Options.option_hook global_login reset_tags; + Options.option_hook login reset_tags; +- Options.option_hook enable_sui ( fun _ -> +- if not (Autoconf.donkey_sui_works ()) && !!enable_sui then enable_sui =:= false; +- reset_tags ()); ++ Options.option_hook enable_sui reset_tags; + + (**** START TIMERS ****) + add_session_option_timer enabler check_client_connections_delay +@@ -392,6 +384,8 @@ + !overnet_port_info, "overnet_port TCP+UDP"; + !kademlia_port_info, "kademlia_port UDP"; + ]); ++ network.op_network_porttest_result <- ++ (fun _ -> !DonkeyInteractive.porttest_result); + CommonInteractive.register_gui_options_panel "eDonkey" + gui_donkey_options_panel; + CommonInteractive.register_gui_options_panel "Overnet" +Index: src/networks/donkey/donkeyOneFile.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyOneFile.ml,v +retrieving revision 1.42 +retrieving revision 1.46 +diff -u -r1.42 -r1.46 +--- src/networks/donkey/donkeyOneFile.ml 8 Aug 2006 23:55:28 -0000 1.42 ++++ src/networks/donkey/donkeyOneFile.ml 21 Nov 2006 22:34:34 -0000 1.46 +@@ -100,7 +100,7 @@ + end + + let remove_client_slot c = +- set_client_has_a_slot (as_client c) false; ++ set_client_has_a_slot (as_client c) NoSlot; + client_send c ( + let module M = DonkeyProtoClient in + let module Q = M.CloseSlot in +@@ -111,8 +111,11 @@ + match file.file_shared with + None -> () + | Some s -> +- if !verbose_share then +- lprintf_nl "unshare_file %s" file.file_diskname; ++ if !verbose_share || !verbose then ++ lprintf_nl "Unsharing file %s" (file_best_name file); ++ List.iter (fun s -> ++ s.server_sent_shared <- List2.removeq file s.server_sent_shared; ++ ) (connected_servers ()); + file.file_shared <- None; + decr nshared_files; + CommonShared.shared_calculate_total_bytes (); +@@ -269,7 +272,7 @@ + | _ -> assert false + in + let msg = M.QueryBlocReq msg in +- set_read_power sock (!!upload_power + maxi 0 (file_priority file)); ++ set_read_power sock (!!upload_power + max 0 (file_priority file)); + (* lprintf "QUEUE DOWNLOAD REQUEST\n"; *) + (* CommonUploads.queue_download_request (fun _ -> *) + client_send c msg +@@ -435,7 +438,7 @@ + (file_best_name file) (Printexc2.to_string e) in + Printf2.lprint_string m; + CommonEvent.add_event (Console_message_event m); +- file_pause (as_file file); ++ file_pause (as_file file) CommonUserDb.admin_user; + raise e + end + +Index: src/networks/donkey/donkeyOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyOptions.ml,v +retrieving revision 1.52 +retrieving revision 1.56 +diff -u -r1.52 -r1.56 +--- src/networks/donkey/donkeyOptions.ml 4 Sep 2006 21:30:27 -0000 1.52 ++++ src/networks/donkey/donkeyOptions.ml 21 Nov 2006 22:34:34 -0000 1.56 +@@ -45,6 +45,10 @@ + "The number of servers you want to stay connected to" + int_option 3 + ++let max_allowed_connected_servers () = ++ min (int_of_string ((strings_of_option max_connected_servers).option_default)) ++ !!max_connected_servers ++ + let reliable_sources = define_option donkey_section ["reliable_sources"] + "Should mldonkey try to detect sources responsible for corruption and ban them, currently disabled" + bool_option true +@@ -53,13 +57,16 @@ + "Should mldonkey try to detect sources masquerading as others and ban them" + bool_option true + +-let max_allowed_connected_servers () = +- BasicSocket.mini 5 !!max_connected_servers +- + let server_black_list = define_option donkey_section ["server_black_list"] +- "A list of server IP to remove from server list. ++ "A list of server IP to remove from server list. Can contain single IPs, CIDR ranges, or begin-end ranges. + Servers on this list can't be added, and will eventually be removed" +- (list_option Ip.option) [] ++ CommonOptions.ip_range_list_option [] ++ ++let server_black_list_set = ref Ip_set.BL_Empty ++ ++let () = ++ option_hook server_black_list (fun _ -> ++ server_black_list_set := Ip_set.of_list !!server_black_list) + + let force_high_id = define_option donkey_section ["force_high_id"] + "immediately close connection to servers that don't grant a High ID" +@@ -100,8 +107,7 @@ + int_option 1 + + let walker_server_lifetime = define_expert_option donkey_section ["walker_server_lifetime"] +- "The maximal delay a connection with a server should last when walking +- through the list (should be greater than become_master_delay)" ++ "The maximal delay a connection with a server should last when walking through the list" + int_option 300 + + let log_clients_on_console = define_expert_option donkey_section ["log_clients_on_console"] +@@ -247,6 +253,10 @@ + "min connected users for each server" + int_option 0 + ++let max_published_files = define_option donkey_section ["max_published_files"] ++ "maximum number of files published to servers per minute, eMule default 200" ++ int_option 200 ++ + let login = define_option donkey_section ["login"] + "login of client on eDonkey network (nothing default to global one)" + string_option "" +@@ -260,10 +270,6 @@ + "port for overnet" + int_option (2000 + Random.int 20000) + +-let become_master_delay = define_expert_option donkey_section ["become_master_delay"] +- "(only for development tests)" +- int_option 120 +- + let options_version = define_expert_option donkey_section ["options_version"] + "(internal option)" + int_option 3 +Index: src/networks/donkey/donkeyOvernet.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyOvernet.ml,v +retrieving revision 1.68 +retrieving revision 1.74 +diff -u -r1.68 -r1.74 +--- src/networks/donkey/donkeyOvernet.ml 5 Sep 2006 14:15:20 -0000 1.68 ++++ src/networks/donkey/donkeyOvernet.ml 14 Nov 2006 16:35:53 -0000 1.74 +@@ -270,6 +270,13 @@ + end + ) + ++module KnownPeers = Weak.Make(struct ++ type t = peer ++ let hash c = Hashtbl.hash (c.peer_ip, c.peer_port) ++ let equal x y = x.peer_port = y.peer_port ++ && x.peer_ip = y.peer_ip ++ end) ++ + type search_for = + | FileSearch of file + | KeywordSearch of CommonTypes.search +@@ -293,14 +300,22 @@ + search_md4 : Md4.t; + mutable search_kind : search_for; + +- search_waiting_peers : peer Fifo.t array; +- search_asked_peers : peer Fifo.t array; +- search_ok_peers : peer Fifo.t array; ++(* Peer queues of the search *) ++(* Stage 1: This peers we know for this search *) ++ search_peers : peer Fifo.t array; ++(* Stage 2: We send a OvernetSearch *) ++ search_asked_peers : KnownPeers.t; ++(* Stage 3: We received from this peer a OvernetSearchReply *) ++ search_ok_peers : KnownPeers.t; ++(* Stage 4: We picked peers from search_ok_peers and send them a ++ OvernetSearch.*Results *) ++ search_result_asked_peers : KnownPeers.t; + + mutable search_queries : int; + mutable search_requests : int; + mutable search_start : int; +- mutable search_last_query : int; ++ mutable search_lifetime : int; ++ mutable search_last_recv : int; + mutable search_results : (Md4.t, tag list) Hashtbl.t; + mutable search_nresults : int; (* number of results messages *) + mutable search_hits : int; (* number of diff results *) +@@ -347,11 +362,10 @@ + *********************************************************************) + + let max_peers_per_bucket = 20 +-let max_peers_per_prebucket = 40 +-(* how many peers a search may ask for more search peers *) +-let max_search_queries = 50 +-(* how many peers we ask for results *) +-let max_search_requests = 20 ++let max_peers_per_prebucket = 20 ++(* number of peers a search start with *) ++(* get_any_peers and get_closest_peers change the order of the peers in the bucket fifo *) ++let init_peers_per_search = ((max_peers_per_bucket*2)/3) + let max_boot_peers = 200 + + let is_enabled = ref false +@@ -451,7 +465,16 @@ + let pre_connected_peers = ref 0 + + let is_overnet_ip ip = +- Ip.usable ip && Ip.of_string "1.0.0.0" <> ip ++ let is_not_banned ip = ++ match !Ip.banned ip with ++ None -> true ++ | Some reason -> ++ if !verbose_overnet then ++ lprintf_nl "%s blocked: %s" ++ (Ip.to_string ip) reason; ++ false ++ in ++ Ip.usable ip && Ip.of_string "1.0.0.0" <> ip && is_not_banned ip + + module LimitedList = struct + +@@ -557,13 +580,6 @@ + let prebuckets = Array.init 129 (fun _ -> Fifo.create ()) + + let to_ping = ref [] +-module KnownPeers = Weak.Make(struct +- type t = peer +- let hash c = Hashtbl.hash (c.peer_ip, c.peer_port) +- let equal x y = x.peer_port = y.peer_port +- && x.peer_ip = y.peer_ip +- end) +- + let known_peers = KnownPeers.create 1023 + + (* +@@ -607,17 +623,11 @@ + let bucket_number md4 = + common_bits md4 !!overnet_md4 + +- (* +-(* TODO: this structure MUST disappear. It is not Kademlia ! *) +-let global_peers : (Md4.t, peer) Hashtbl.t array Options.option_record = +- raise Not_found +- *) +- +-(*let firewalled_overnet_peers = Hashtbl.create 13*) +- + let search_hits = ref 0 + let source_hits = ref 0 + ++(* when we created the searches for all files last time*) ++let last_check_current_downloads = ref 0 + + let udp_sock = ref None + +@@ -756,12 +766,16 @@ + match !udp_sock with + None -> () + | Some sock -> ++(* Why check this? Because it may have been blocked since it was added *) ++ if ip <> Ip.localhost && is_overnet_ip ip && port <> 0 then + Proto.udp_send sock ip port false msg + + let udp_send_ping ip port msg = + match !udp_sock with + None -> () + | Some sock -> ++(* Why check this? Because it may have been blocked since it was added *) ++ if ip <> Ip.localhost && is_overnet_ip ip && port <> 0 then + Proto.udp_send sock ip port true msg + + let udp_send p msg = +@@ -802,7 +816,12 @@ + if bucket < !n_used_buckets && bucket <> 128 && + p.peer_md4 <> Md4.null then begin + +- if Fifo.length prebuckets.(bucket) = max_peers_per_prebucket then ++(* If this is a good peer add it directly to the bucket. Helps at startup *) ++ if p.peer_kind < 3 && Fifo.length buckets.(bucket) < max_peers_per_bucket then begin ++ Fifo.put buckets.(bucket) p; ++ incr connected_peers; ++ end ++ else if Fifo.length prebuckets.(bucket) >= max_peers_per_prebucket then + begin + let pp = Fifo.take prebuckets.(bucket) in + Fifo.put prebuckets.(bucket) +@@ -847,11 +866,10 @@ + if n > 0 then + let p = Fifo.take fifo in + Fifo.put fifo p; +- (* kind < 4 so we do not send too much requests and avoid dead contacts not ++ (* kind < 3 so we do not send too much requests and avoid dead contacts not + yet removed because of timeouts *) + (* TODO: Keep order? Then we need a in_use flag? *) +- if p.peer_kind < 4 && p.peer_expire > last_time () && +- p.peer_last_send <> 0 then begin ++ if p.peer_kind < 3 && p.peer_expire > last_time () then begin + if !verbose_overnet then begin + lprintf_nl "Adding good search peer %s:%d" + (Ip.to_string p.peer_ip) p.peer_port; +@@ -928,50 +946,59 @@ + if p.peer_ip <> Ip.localhost && is_overnet_ip p.peer_ip && + p.peer_port <> 0 && p.peer_created <> 0 then begin + let nbits = common_bits p.peer_md4 s.search_md4 in +- begin ++(* Don't add ourself *) ++ if not (nbits = 128 && s.search_kind == FillBuckets) then begin + try +- Fifo.iter (fun pp -> ++ let is_in pp = + if pp.peer_ip = p.peer_ip && + pp.peer_port = p.peer_port then + raise Exit +- ) s.search_waiting_peers.(nbits); +- Fifo.put s.search_waiting_peers.(nbits) p; ++ in ++ Fifo.iter is_in s.search_peers.(nbits); ++ Fifo.put s.search_peers.(nbits) p; + with Exit -> () + end + end + + let create_search kind md4 = + if !verbose_overnet then lprintf_nl "create_search"; +- let starttime = last_time () + (2 * List.length !overnet_searches) in ++ let starttime = last_time () + (3 * List.length !overnet_searches) in + let s = ref { + search_md4 = md4; + search_kind = kind; + search_queries = 0; + search_requests = 0; +- search_waiting_peers = Array.init 129 (fun _ -> Fifo.create ()); +- search_asked_peers = Array.init 129 (fun _ -> Fifo.create ()); +- search_ok_peers = Array.init 129 (fun _ -> Fifo.create ()); ++ search_peers = Array.init 129 (fun _ -> Fifo.create ()); ++ search_asked_peers = KnownPeers.create 129; ++ search_ok_peers = KnownPeers.create 129; ++ search_result_asked_peers = KnownPeers.create 129; ++(* taken from eMule0.47c-Sources/srchybrid/kademlia/kademlia/Defines.h *) ++ search_lifetime = (match kind with ++ _ -> 45 );(* SEARCH_LIFETIME *) + search_start = (match kind with + KeywordSearch s -> last_time () + | FillBuckets -> last_time () + | FileSearch s -> starttime); +- search_last_query = (match kind with +- KeywordSearch s -> last_time () +- | FillBuckets -> last_time () +- | FileSearch s -> starttime); ++ search_last_recv = last_time (); + search_hits = 0; + search_nresults = 0; + search_results = Hashtbl.create 64; + } in +- List.iter (fun ss -> +- if ss.search_md4 = !s.search_md4 && (search_for_equals ss.search_kind !s.search_kind) then begin +- ss.search_start <- !s.search_start; +- s := ss; +- end +- ) !overnet_searches; +- List.iter (add_search_peer !s) (get_closest_peers md4 max_search_queries); +- if !verbose_overnet then lprintf_nl "create_search done"; +- overnet_searches := !s :: !overnet_searches; ++ begin try ++ List.iter (fun ss -> ++ if ss.search_md4 = !s.search_md4 && (search_for_equals ss.search_kind !s.search_kind) then begin ++(* ss.search_start <- !s.search_start; *) ++ s := ss; ++ raise Exit; ++ end ++ ) !overnet_searches; ++ begin match kind with ++ FillBuckets -> List.iter (add_search_peer !s) (get_any_peers init_peers_per_search) ++ | _ -> List.iter (add_search_peer !s) (get_closest_peers md4 init_peers_per_search) ++ end; ++ if !verbose_overnet then lprintf_nl "create_search done"; ++ overnet_searches := !s :: !overnet_searches; ++ with Exit -> () end; + !s + + let create_keyword_search w s = +@@ -979,6 +1006,54 @@ + let search = create_search (KeywordSearch s) md4 in + search + ++(* query the best todo peers of a search s or ask it for results *) ++let rec overnet_search_iter s nbits todo = ++ let nresults = match s.search_kind with ++ FillBuckets -> 10 ++ | _ -> 2 ++ in ++ if nbits >= 0 then ++ let len = Fifo.length s.search_peers.(nbits) in ++ if len > 0 then ++ let process_peer p = ++ if not (KnownPeers.mem s.search_asked_peers p) then begin ++(* Not asked: send a OvernetSearch *) ++ checking_kind p; ++ udp_send p (OvernetSearch (nresults, s.search_md4, Some p.peer_md4)); ++ s.search_queries <- s.search_queries + 1; ++ KnownPeers.add s.search_asked_peers p; ++ raise Exit; ++ end ++ else if (KnownPeers.mem s.search_ok_peers p) && ++ not (KnownPeers.mem s.search_result_asked_peers p) then begin ++(* Is ok but we did not request a result yet *) ++ let kind = match s.search_kind with ++ FillBuckets -> Search_for_file ++ | FileSearch _ -> Search_for_file ++ | _ -> Search_for_keyword None ++ in ++ checking_kind p; ++ udp_send p ( OvernetGetSearchResults (s.search_md4, kind, 0, 100)); ++ s.search_requests <- s.search_requests + 1; ++ KnownPeers.add s.search_result_asked_peers p; ++ raise Exit; ++ end ++ (* raised an Exit if we send something to the peer *) ++ in ++ try ++ Fifo.iter process_peer s.search_peers.(nbits); ++ overnet_search_iter s (nbits-1) todo; ++ with Exit -> ++ (if todo > 1 then overnet_search_iter s nbits (todo - 1)) ++ else ++ overnet_search_iter s (nbits-1) todo ++ else ++ begin ++ if !verbose_overnet then ++ lprintf_nl "overnet_search_iter: call add_search_peer"; ++ List.iter (add_search_peer s) (get_closest_peers s.search_md4 init_peers_per_search); ++ end ++ + let ip_of_udp_packet p = + match p.UdpSocket.udp_addr with + Unix.ADDR_INET (inet, port) -> +@@ -1005,7 +1080,7 @@ + + | OvernetConnect p -> + if is_overnet_ip sender.peer_ip && sender.peer_port <> 0 then +- let sender = new_peer { p with peer_ip = other_ip } in ++ let sender = new_peer { p with peer_ip = other_ip; peer_kind = 2 } in + (* let sender = new_peer { p with peer_port = other_port ; peer_ip = other_ip } in *) + new_peer_message sender; + udp_send sender (OvernetConnectReply (get_any_peers 20)) +@@ -1014,7 +1089,6 @@ + if !verbose_overnet then + lprintf_nl "Connect: invalid IP %s:%d received from %s:%d" + (Ip.to_string p.peer_ip) p.peer_port (Ip.to_string other_ip) other_port; +- failwith "Message not understood" + end + + | OvernetConnectReply ps -> +@@ -1023,7 +1097,8 @@ + match list with + [] -> () + | [p] -> +- let sender = new_peer { p with peer_ip = other_ip } in ++ (* last one is always the peer itself *) ++ let sender = new_peer { p with peer_ip = other_ip; peer_kind = 2 } in + new_peer_message sender + | p :: tail -> + let _ = new_peer p in +@@ -1032,7 +1107,7 @@ + iter ps; + + | OvernetPublicize p -> +- let sender = new_peer { p with peer_ip = other_ip } in ++ let sender = new_peer { p with peer_ip = other_ip; peer_kind = 2 } in + new_peer_message sender; + if is_overnet_ip sender.peer_ip && sender.peer_port <> 0 then + udp_send sender (OvernetPublicized (Some (my_peer ()))) +@@ -1040,7 +1115,6 @@ + if !verbose_overnet then + lprintf_nl "Publicize: invalid IP %s:%d received from %s:%d" + (Ip.to_string p.peer_ip) p.peer_port (Ip.to_string other_ip) other_port; +- failwith "Message not understood" + end + + | OvernetPublicized None -> +@@ -1049,6 +1123,9 @@ + | OvernetPublicized (Some p) -> + () + ++ | OvernetNoResult (md4) -> ++ () ++ + | OvernetSearch (nresults, md4, from_who) -> + let peers = get_closest_peers md4 nresults in + udp_send sender (OvernetSearchReply (md4,peers)) +@@ -1059,8 +1136,10 @@ + List.iter (fun s -> + if s.search_md4 = md4 then begin + List.iter (add_search_peer s) peers; +- let nbits = common_bits sender.peer_md4 s.search_md4 in +- Fifo.put s.search_ok_peers.(nbits) sender; ++ KnownPeers.add s.search_ok_peers sender; ++ s.search_last_recv <- last_time (); ++ (* Now ask the next best peer *) ++ overnet_search_iter s 128 1; + end + ) !overnet_searches; + +@@ -1113,6 +1192,9 @@ + if is_overnet_ip ip && port <> 0 then + let s = DonkeySources.find_source_by_uid + (Direct_address (ip, port)) in ++ if !verbose_overnet then ++ lprintf_nl "added new source %s:%d for file %s" ++ (Ip.to_string ip) port (Md4.to_string md4); + incr source_hits; + DonkeySources.set_request_result s + file.file_sources File_new_source; +@@ -1167,18 +1249,18 @@ + (* send the answer *) + | OvernetGetMyIP other_port -> + if !verbose_overnet && debug_client other_ip then +- lprintf_nl "GET MY IP (port=%d)\n" other_port; ++ lprintf_nl "GET MY IP (port=%d)" other_port; + (* FIXME : should be able to flush the UDP buffer*) + udp_send sender (OvernetGetMyIPResult other_ip); + udp_send sender OvernetGetMyIPDone + + | OvernetGetMyIPResult(ip) -> + if !verbose_overnet && debug_client other_ip then +- lprintf_nl "GET MY IP RESULT (%s)\n" (Ip.to_string ip) ++ lprintf_nl "GET MY IP RESULT (%s)" (Ip.to_string ip) + + | OvernetGetMyIPDone -> + if !verbose_overnet && debug_client other_ip then +- lprintf_nl "GET MY IP DONE\n" ++ lprintf_nl "GET MY IP DONE" + + | OvernetPeerNotFound peer -> + begin +@@ -1186,24 +1268,34 @@ + lprintf_nl "Peer NOT FOUND %s (%s:%d) kind: %d (msg 33)" + (Md4.to_string peer.peer_md4) (Ip.to_string peer.peer_ip) + peer.peer_port peer.peer_kind; ++(* We ignore OvernetPeerNotFound, the original client sends this message very often (even if the peer is alive) + let dp = { dummy_peer with peer_port = peer.peer_port ; peer_ip = peer.peer_ip } in + if KnownPeers.mem known_peers dp + then begin +-(* remove it from the prebuckets and known_peers only *) ++(* remove it from the buckets, put it into prebuckets and set kind to 3 *) + try + for i = 0 to !n_used_buckets do +- let b = prebuckets.(i) in ++ let b = buckets.(i) in ++ let pb = prebuckets.(i) in + for j = 1 to Fifo.length b do + let p = Fifo.take b in + if p.peer_ip = peer.peer_ip && + p.peer_port = peer.peer_port then begin +- decr pre_connected_peers; +- KnownPeers.remove known_peers dp; ++ decr connected_peers; ++ if !pre_connected_peers < max_peers_per_prebucket then begin ++ incr pre_connected_peers; ++ Fifo.put pb p; ++ p.peer_kind <- 3; ++ end ++ else begin if !verbose_overnet then lprintf_nl "peernotfound: removing %s:%d" (Ip.to_string p.peer_ip) p.peer_port; ++ KnownPeers.remove known_peers p; ++ end + end else Fifo.put b p + done; + done; + with Exit -> (); + end; ++*) + end + + | OvernetUnknown21 peer -> +@@ -1215,77 +1307,19 @@ + + | _ -> failwith "Message not understood" + +-let query_next_peers () = +- let overnet_query_peer_period = int_of_float !!overnet_query_peer_period in +- let timeout = last_time () - overnet_query_peer_period in ++(* Start unstarted searches, retry stalled searches *) ++let overnet_search_tick () = + List.iter (fun s -> +- let nresults = match s.search_kind with +- FillBuckets -> 10 +- | _ -> 2 +- in +- +- let rec iter nbits todo = +- if nbits >= 0 then +- let len = Fifo.length s.search_waiting_peers.(nbits) in +- if len > 0 then +- let p = Fifo.take s.search_waiting_peers.(nbits) in +- +- if p.peer_last_send < timeout then begin +- checking_kind p; +- udp_send p (OvernetSearch (nresults, s.search_md4, Some p.peer_md4)); +- s.search_queries <- s.search_queries + 1; +- Fifo.put s.search_asked_peers.(nbits) p; +- +- (if todo > 1 then iter nbits (todo - 1)) +- end else +- iter nbits todo +- else +- iter (nbits-1) todo +- else +- if s.search_queries < max_search_queries then +- List.iter (fun p -> +- add_search_peer s p +- ) (get_closest_peers s.search_md4 max_search_queries) +- in +- if s.search_last_query < timeout then begin +- s.search_last_query <- s.search_last_query + overnet_query_peer_period; +-(* Query next search peers *) +- if s.search_queries < max_search_queries then +- (if s.search_queries = 0 then +- iter 128 3 +- else +- iter 128 2); +-(* Request next results *) +- if s.search_requests < max_search_requests then begin +- let nrequests = +- match s.search_kind with +- FillBuckets -> 0 +- | FileSearch _ -> 1 +- | KeywordSearch _ -> 5 +- in +- for i = 1 to nrequests do +- try +- for j = 128 downto 5 do +- if Fifo.length s.search_ok_peers.(j) > 0 then +- let p = Fifo.take s.search_ok_peers.(j) in +- if p.peer_last_send < timeout then begin +- checking_kind p; +- udp_send p ( +- OvernetGetSearchResults (s.search_md4, +- (match s.search_kind with +- FillBuckets -> Search_for_file +- | FileSearch _ -> Search_for_file +- | _ -> Search_for_keyword None +- ), 0, 100)); +- s.search_requests <- s.search_requests + 1; +- raise Exit +- end else +- Fifo.put s.search_ok_peers.(j) p +- done +- with Exit -> () +- done +- end +- end; ++(* Start a search, if search_start is reached *) ++ if (s.search_lifetime + s.search_start - 20) > last_time () && ++ s.search_start < last_time () then begin ++ (if s.search_queries = 0 then ++ overnet_search_iter s 128 3 ++ else if (s.search_last_recv + 3) < last_time () then ++(* The search has stalled. We do what eMule call a JumpStart *) ++(* FIXME: with implemented firewalling this should be 1 peer not 2*) ++ overnet_search_iter s 128 2); ++ end + ) !overnet_searches + + let recover_file file = +@@ -1305,7 +1339,8 @@ + for j = 1 to Fifo.length b do + let p = Fifo.take b in + (* bad peers have kind = 4 and did not respond within peer_expire *) +- if not (p.peer_kind = 4 && p.peer_expire <= last_time ()) then ++ (* Why check is_overnet_ip? Because it may have been blocked since it was added *) ++ if not (p.peer_kind = 4 && p.peer_expire <= last_time ()) && is_overnet_ip p.peer_ip then + Fifo.put b p + else + begin +@@ -1335,7 +1370,9 @@ + incr connected_peers; + decr pre_connected_peers; + (* bad peers are removed *) +- end else if p.peer_kind = 4 && p.peer_expire <= last_time () then begin ++ (* Why check is_overnet_ip? Because it may have been blocked since it was added *) ++ end else if (p.peer_kind = 4 && p.peer_expire <= last_time ()) || ++ not (is_overnet_ip p.peer_ip) then begin + decr pre_connected_peers; + KnownPeers.remove known_peers p; + if !verbose_overnet then lprintf_nl "update_bucket2: removing %s:%d" (Ip.to_string p.peer_ip) p.peer_port; +@@ -1418,14 +1455,13 @@ + + (* copy all boot_peers to unknown_peers *) + LimitedList.iter (fun (ip, port) -> +- LimitedList.add unknown_peers (ip, port) ++ if ip <> Ip.localhost && is_overnet_ip ip && port <> 0 then ++ LimitedList.add unknown_peers (ip, port) + ) !!boot_peers; + + add_session_timer enabler 1. (fun _ -> + if !!enable_overnet then begin +-(* Searches have a search_last_query controlled by !!overnet_query_peer_period *) +-(* Here also the result request a done *) +- query_next_peers (); ++ overnet_search_tick (); + + let my_peer = my_peer () in + +@@ -1437,7 +1473,7 @@ + | p :: tail -> + (* do not hammer a peer, we could have send already a search reqeust + since to_ping is rebuild at least every 60 seconds *) +- if (last_time () - p.peer_last_send) > 60 then begin ++ if (last_time () - p.peer_last_send) > 61 then begin + checking_kind p; + udp_send p (OvernetPublicize my_peer); + end; +@@ -1445,12 +1481,20 @@ + end; + in + process_to_ping (); ++(* When we start with 50 searches and no peers, we have to update the buckets ++ fast, otherwise we cannot add search peers (rest: sanity check) *) ++ begin ++ if !connected_peers < 20 && !pre_connected_peers <> 0 && ++ LimitedList.length unknown_peers <> 0 then ++ update_buckets (); ++ compute_to_ping (); ++ end; + (* Send OvernetConnects and ping more peers *) + (* TODO: How does eMule it? are 50 ok? *) + begin + try +- if !connected_peers < 50 then +- for i = 1 to 4 do ++ if !connected_peers < 10 then ++ for i = 1 to 2 do + process_to_ping (); + done; + (* Do not send too much OvernetConnect, there is no use *) +@@ -1476,12 +1520,25 @@ + + compute_to_ping (); + +- let l = last_time () - 180 in +-(* remove searches that are older than 3 minutes *) ++(* remove searches that are older than their lifetime *) + overnet_searches := List.filter (fun s -> +- s.search_requests < max_search_requests && +- s.search_start > l ++ (* s.search_lifetime + s.search_start > last_time () *) ++(*DEBUG:show longer *) 360 + s.search_start > last_time () + ) !overnet_searches; ++ ++ (* FIXE: Dump latencies to logfile *) ++ if !verbose_overnet then ignore (UdpSocket.get_latencies (ref true)); ++ ++ if !verbose_overnet then begin ++ lprintf_nl "%d peers (prebucket: %d peers)" !connected_peers !pre_connected_peers; ++ for i = 0 to !n_used_buckets do ++ if (Fifo.length buckets.(i)) <> 0 || (Fifo.length prebuckets.(i)) <> 0 then ++ lprintf_nl "bucket[%d] : %d peers (prebucket %d)" ++ i (Fifo.length buckets.(i)) (Fifo.length prebuckets.(i)); ++ done; ++ lprintf_nl "unknown_peers: %d" (LimitedList.length unknown_peers); ++ lprintf_nl "boot_peers: %d" (LimitedList.length !!boot_peers); ++ end; + ); + + (* every 15min for light operations *) +@@ -1489,12 +1546,18 @@ + if !!enable_overnet then begin + let _ = create_search FillBuckets !!overnet_md4 in + check_current_downloads (); ++ last_check_current_downloads := last_time (); + end + ); + + begin +- let _ = create_search FillBuckets !!overnet_md4 in + check_current_downloads (); ++(* Delay the first normal searches, so we can fill the buckets first *) ++ List.iter (fun s -> ++ s.search_start <- s.search_start + 30; ++ ) !overnet_searches; ++ ignore (create_search FillBuckets !!overnet_md4); ++ last_check_current_downloads := last_time (); + end; + + add_infinite_timer 1800. (fun _ -> +@@ -1571,6 +1634,28 @@ + (List.map (fun (command, args, help) -> + command_prefix ^ command, args, help) + [ ++ "dump_searches", Arg_none (fun o -> ++ let buf = o.conn_buf in ++ List.iter ( fun s -> ++ Printf.bprintf buf "Search %s for %s\nrequests:%d queries:%d seconds alive:%d lifetime:%d\n" ++ (match s.search_kind with ++ KeywordSearch _ -> "keyword" ++ | FileSearch _ -> "file" ++ | FillBuckets -> "fillbuckets" ) ++ (Md4.to_string s.search_md4) s.search_requests s.search_queries (last_time ()-s.search_start) s.search_lifetime; ++ let pp p = print_peer buf p in ++ Printf.bprintf buf "search_peers\n"; ++ Array.iter (fun a -> Fifo.iter (fun p -> pp p) a) s.search_peers; ++ Printf.bprintf buf "search_asked_peers\n"; ++ KnownPeers.iter pp s.search_asked_peers; ++ Printf.bprintf buf "search_ok_peers\n"; ++ KnownPeers.iter pp s.search_ok_peers; ++ Printf.bprintf buf "search_result_asked_peers\n"; ++ KnownPeers.iter pp s.search_result_asked_peers; ++ Printf.bprintf buf "\n"; ++ ) !overnet_searches; ++ "" ++ ), ("<bucket_nr> :\t\tdumps a search (Devel)"); + "dump_bucket", Arg_one (fun i o -> + let i = int_of_string i in + let i = min i !n_used_buckets in +@@ -1593,7 +1678,7 @@ + Fifo.put pb p; + done; + "" +- ), ("<bucket_nr> :\t\tdumps a bucket"); ++ ), ("<bucket_nr> :\t\tdumps a bucket (Devel)"); + + "dump_known_peers", Arg_none (fun o -> + let buf = o.conn_buf in +@@ -1604,7 +1689,7 @@ + print_peer buf p; + ) known_peers; + "" +- ), (":\t\t\tdumps known_peers"); ++ ), (":\t\t\tdumps known_peers (Devel)"); + + "boot", Arg_two (fun ip port o -> + let ip = Ip.from_name ip in +@@ -1648,6 +1733,8 @@ + Printf.bprintf buf "\\<tr\\>"; + html_mods_td buf [ + ("", "srh", Printf.sprintf "%s statistics" command_prefix_to_net); ++ ("", "srh", Printf.sprintf "Last file search started %d seconds ago\n" ++ (last_time () - !last_check_current_downloads)); + ("", "srh", Printf.sprintf "Search hits: %d\n" !search_hits); + ("", "srh", Printf.sprintf "Source hits: %d\n" !source_hits); ]; + Printf.bprintf buf "\\</tr\\>\\</table\\>\\</div\\>\n"; +@@ -1656,6 +1743,8 @@ + begin + Printf.bprintf buf "%s statistics:\n" + (command_prefix_to_net); ++ Printf.bprintf buf " Last file search started %d seconds ago\n" ++ (last_time () - !last_check_current_downloads); + Printf.bprintf buf " Search hits: %d\n" !search_hits; + Printf.bprintf buf " Source hits: %d\n" !source_hits; + end; +@@ -1675,35 +1764,44 @@ + Printf.bprintf buf "\\</tr\\>"; + Printf.bprintf buf "\\<tr class=\\\"dl-1\\\"\\>"; + html_mods_td buf [ +- ("", "sr", Printf.sprintf "requests:%d queries:%d search_last_query:%d\n" s.search_requests s.search_queries s.search_last_query); ]; ++ ("", "sr", Printf.sprintf "requests:%d queries:%d seconds alive:%d lifetime:%d\n" ++ s.search_requests s.search_queries (last_time ()-s.search_start) s.search_lifetime); ]; + Printf.bprintf buf "\\</tr\\>"; + end + else +- Printf.bprintf buf "Search %s for %s\nrequests:%d queries:%d search_last_query:%d\n" ++ Printf.bprintf buf "Search %s for %s\nrequests:%d queries:%d seconds alive:%d lifetime:%d\n" + (match s.search_kind with + KeywordSearch _ -> "keyword" + | FileSearch _ -> "file" + | FillBuckets -> "fillbuckets" ) +- (Md4.to_string s.search_md4) s.search_requests s.search_queries s.search_last_query; ++ (Md4.to_string s.search_md4) s.search_requests s.search_queries (last_time ()-s.search_start) s.search_lifetime; + for i = 128 downto 0 do +- let npeers = Fifo.length s.search_waiting_peers.(i) in +- let nasked = Fifo.length s.search_asked_peers.(i) in +- let nok = Fifo.length s.search_ok_peers.(i) in ++ let npeers = Fifo.length s.search_peers.(i) in ++ let count = ref 0 in ++ let cp p = if (common_bits p.peer_md4 s.search_md4) = i then count := !count + 1 in ++ KnownPeers.iter cp s.search_asked_peers; ++ let nasked = !count in ++ count := 0; ++ KnownPeers.iter cp s.search_ok_peers; ++ let nok = !count in ++ count := 0; ++ KnownPeers.iter cp s.search_result_asked_peers; ++ let nres = !count in + if npeers > 0 || nasked > 0 then + if o.conn_output = HTML then + begin + Printf.bprintf buf "\\<tr class=\\\"dl-1\\\"\\>"; + html_mods_td buf [ + ("", "sr", +- Printf.sprintf "nbits[%d] = %d peer(s) not asked, %d peer(s) asked, %d peer(s) ok" +- i npeers nasked nok); ]; ++ Printf.sprintf "nbits[%d] = %d peer(s) total, %d peer(s) asked, %d peer(s) ok, %d peer(s) result asked" ++ i npeers nasked nok nres); ]; + Printf.bprintf buf "\\</tr\\>"; + + end + else + Printf.bprintf buf +- " nbits[%d] = %d peers not asked, %d peers asked\n" +- i npeers nasked ++ " nbits[%d] = %d peer(s) total, %d peer(s) asked, %d peer(s) ok, %d peer(s) result asked\n" ++ i npeers nasked nok nres + done; + if o.conn_output = HTML then + Printf.bprintf buf "\\</table\\>\\</div\\>\n"; +@@ -1864,18 +1962,6 @@ + + let forget_search ss = + begin +-(* reset the Hashtbls and Fifos *) +- List.iter ( fun s -> +- match s.search_kind with +- KeywordSearch sss when ss == sss -> +- begin +- Array.iter (fun a -> Fifo.clear a) s.search_waiting_peers; +- Array.iter (fun a -> Fifo.clear a) s.search_asked_peers; +- Array.iter (fun a -> Fifo.clear a) s.search_ok_peers; +- Hashtbl.clear s.search_results; +- end +- | _ -> () +- ) !overnet_searches; + (* Remove from overnet_searches *) + overnet_searches := List.filter (fun s -> + match s.search_kind with +@@ -1885,18 +1971,6 @@ + + let cancel_recover_file file = + begin +-(* reset the Hashtbls and Fifos *) +- List.iter ( fun s -> +- match s.search_kind with +- FileSearch f when f == file -> +- begin +- Array.iter (fun a -> Fifo.clear a) s.search_waiting_peers; +- Array.iter (fun a -> Fifo.clear a) s.search_asked_peers; +- Array.iter (fun a -> Fifo.clear a) s.search_ok_peers; +- Hashtbl.clear s.search_results; +- end +- | _ -> () +- ) !overnet_searches; + (* Remove from overnet_searches *) + overnet_searches := List.filter (fun s -> + match s.search_kind with +@@ -1954,8 +2028,8 @@ + Printf.bprintf buf " unknown_peers: %d\n" (LimitedList.length unknown_peers); + update_buckets (); + Printf.bprintf buf " boot_peers: %d\n" (LimitedList.length !!boot_peers); +- Printf.bprintf buf " %d buckets with %d peers and %d prebucket peers\n" +- !n_used_buckets !connected_peers !pre_connected_peers; ++ Printf.bprintf buf " %d peers and %d prebucket peers\n" ++ !connected_peers !pre_connected_peers; + + Printf.bprintf buf " Search hits: %d\n" !search_hits; + Printf.bprintf buf " Source hits: %d\n" !source_hits; +@@ -1970,27 +2044,19 @@ + + + Printf.bprintf buf " to_ping: %d\n" (List.length !to_ping); +- let n_search_waiting_peers = ref 0 in +- let n_search_asked_peers = ref 0 in +- let n_search_ok_peers = ref 0 in ++ let n_search_peers = ref 0 in + let n_search_results = ref 0 in + let n_overnet_searches = ref 0 in + List.iter ( fun s -> + for i = 128 downto 0 do +- n_search_waiting_peers := +- !n_search_waiting_peers + (Fifo.length s.search_waiting_peers.(i)); +- n_search_asked_peers := +- !n_search_asked_peers + (Fifo.length s.search_asked_peers.(i)); +- n_search_ok_peers := +- !n_search_ok_peers + (Fifo.length s.search_ok_peers.(i)); ++ n_search_peers := ++ !n_search_peers + (Fifo.length s.search_peers.(i)); + n_search_results := + !n_search_results + (Hashtbl.length s.search_results); + done; + incr n_overnet_searches + ) !overnet_searches; +- Printf.bprintf buf " n_search_waiting_peers: %d\n" !n_search_waiting_peers; +- Printf.bprintf buf " n_search_asked_peers: %d\n" !n_search_asked_peers; +- Printf.bprintf buf " n_search_ok_peers: %d\n" !n_search_ok_peers; ++ Printf.bprintf buf " n_search_peers: %d\n" !n_search_peers; + Printf.bprintf buf " n_search_results: %d\n" !n_search_results; + Printf.bprintf buf " n_overnet_searches: %d\n" !n_overnet_searches; + ); +Index: src/networks/donkey/donkeyProtoClient.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoClient.ml,v +retrieving revision 1.36 +retrieving revision 1.39 +diff -u -r1.36 -r1.39 +--- src/networks/donkey/donkeyProtoClient.ml 5 Sep 2006 14:18:24 -0000 1.36 ++++ src/networks/donkey/donkeyProtoClient.ml 31 Oct 2006 15:40:06 -0000 1.39 +@@ -51,6 +51,7 @@ + emule_secident = 3; (* Emule uses v1 if advertising both, v2 if only advertising 2 *) + emule_noviewshared = 0; + emule_supportpreview = 0; ++ emule_osinfosupport = 1; + emule_compression = 1; (* 1 *) + emule_sourceexchange = 2; (* 2 : +client_md4 3 : +IdHybrid (emule Kademlia?)*) + emule_multipacket = 0; (* 1 *) +@@ -85,6 +86,12 @@ + m.emule_multipacket <- (o lsr 1) land 0x1; + m.emule_supportpreview <- (o lsr 0) land 0x1 + ++let emule_compatoptions m = ++ (m.emule_osinfosupport lsl 0) ++ ++let update_emule_proto_from_compatoptions m o = ++ m.emule_osinfosupport <- (o lsr 0) land 0x1 ++ + let extendedrequest e = + min e.emule_extendedrequest mldonkey_emule_proto.emule_extendedrequest + +@@ -133,6 +140,7 @@ + "\060", Field_UNKNOWN "downloadtime"; + "\061", Field_UNKNOWN "incompleteparts"; + "\085", Field_UNKNOWN "mod_version"; ++ "\239", Field_UNKNOWN "emule_compatoptions"; + "\249", Field_UNKNOWN "emule_udpports"; + "\250", Field_UNKNOWN "emule_miscoptions1"; + "\251", Field_UNKNOWN "emule_version"; +@@ -799,7 +807,7 @@ + "\135", "mod_lsd"; + "\136", "mod_lsd_version"; + "\144", "mod_lovelace_version"; +- "\148", "mod_oxy"; ++ "\148", "os_info"; (* reused by aMule to transfer client OS type *) + "\153", "mod_plus"; + "\160", "mod_wombat"; + "\161", "dev_wombat"; +@@ -1044,6 +1052,43 @@ + done + + end ++module EmuleFileDesc = struct ++ ++ type t = { ++ rating : int; ++ comment : string; ++ } ++ ++ let parse len s = ++ let rating = get_uint8 s 1 in ++ let (comment, _) = get_string32 s 2 in ++ { ++ rating = rating; ++ comment = comment; ++ } ++ ++ let print t = ++ lprintf_nl "EmuleFileDesc [%d][%s]" t.rating t.comment ++ ++ let write buf t = ++ buf_int8 buf t.rating; ++ buf_string buf t.comment ++ end ++ ++ ++module EmulePortTestReq = struct ++ ++ type t = string ++ ++ let print s = ++ lprintf_nl "Emule porttest request %s" (String.escaped s) ++ ++ let parse s = s ++ ++ let write buf = ++ buf_int8 buf 0x12 ++ ++ end + + type t = + | ConnectReq of Connect.t +@@ -1081,13 +1126,14 @@ + | EmuleQueueRankingReq of EmuleQueueRanking.t + | EmuleRequestSourcesReq of EmuleRequestSources.t + | EmuleRequestSourcesReplyReq of EmuleRequestSourcesReply.t +-| EmuleFileDescReq of int * string ++| EmuleFileDescReq of EmuleFileDesc.t + | EmulePublicKeyReq of EmulePublicKeyReq.t + | EmuleSignatureReq of EmuleSignatureReq.t + | EmuleSecIdentStateReq of EmuleSecIdentStateReq.t + | EmuleMultiPacketReq of Md4.t * t list + | EmuleMultiPacketAnswerReq of Md4.t * t list + | EmuleCompressedPart of Md4.t * int64 * int64 * string ++| EmulePortTestReq of EmulePortTestReq.t + + let rec print t = + begin +@@ -1133,8 +1179,8 @@ + | EmuleRequestSourcesReplyReq t -> + EmuleRequestSourcesReply.print t + +- | EmuleFileDescReq (rating, comment) -> +- lprintf "EMULE FILE DESC %s" comment ++ | EmuleFileDescReq t -> ++ EmuleFileDesc.print t + + | EmuleMultiPacketReq (md4, list) -> + lprintf_nl "EmuleMultiPacket for %s:" (Md4.to_string md4); +@@ -1155,11 +1201,11 @@ + EmuleSignatureReq.print t + | EmulePublicKeyReq t -> + EmulePublicKeyReq.print t +- + | EmuleCompressedPart (md4, statpos, newsize, bloc) -> + lprintf_nl "EmuleCompressedPart for %s %Ld %Ld len %d" + (Md4.to_string md4) statpos newsize (String.length bloc) +- ++ | EmulePortTestReq t -> ++ EmulePortTestReq.print t + | UnknownReq (opcode, s) -> + let len = String.length s in + lprintf_nl "UnknownReq: magic (%d), opcode (%d) len (%d)" opcode +@@ -1195,10 +1241,7 @@ + + | 0x60 (* 96 *) -> EmuleQueueRankingReq (EmuleQueueRanking.parse len s) + +- | 0x61 (* 97 *) -> +- let rating = get_uint8 s 1 in +- let (comment,_) = get_string32 s 2 in +- EmuleFileDescReq (rating, comment) ++ | 0x61 (* 97 *) -> EmuleFileDescReq (EmuleFileDesc.parse len s) + + | 0x81 (* 129 *) -> EmuleRequestSourcesReq (EmuleRequestSources.parse len s) + | 0x82 (* 130 *) -> +@@ -1294,6 +1337,9 @@ + in + EmuleMultiPacketAnswerReq (md4, iter s 17 len) + ++ | 0xfe (* 254 *) -> ++ EmulePortTestReq s ++ + | code -> + if !CommonOptions.verbose_unknown_messages then + lprintf_nl "EDK: unknown eMule message %d" code; +@@ -1514,11 +1560,9 @@ + | EmuleRequestSourcesReplyReq t -> + buf_int8 buf 0x82; + EmuleRequestSourcesReply.write emule buf t +- | EmuleFileDescReq (rating, comment) -> ++ | EmuleFileDescReq t -> + buf_int8 buf 0x61; +- buf_int8 buf rating; +- buf_string buf comment +- ++ EmuleFileDesc.write buf t + | EmuleCompressedPart (md4, statpos, newsize, bloc) -> + buf_int8 buf 0x40; + buf_md4 buf md4; +@@ -1578,6 +1622,10 @@ + buf_int8 buf 0x85; + EmulePublicKeyReq.write buf t + ++ | EmulePortTestReq t -> ++ buf_int8 buf 0xfe; ++ EmulePortTestReq.write buf; ++ + | UnknownReq (opcode, s) -> + Buffer.add_string buf s + +Index: src/networks/donkey/donkeyProtoCom.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoCom.ml,v +retrieving revision 1.32 +retrieving revision 1.33 +diff -u -r1.32 -r1.33 +--- src/networks/donkey/donkeyProtoCom.ml 19 May 2006 23:43:54 -0000 1.32 ++++ src/networks/donkey/donkeyProtoCom.ml 8 Oct 2006 14:20:22 -0000 1.33 +@@ -341,7 +341,7 @@ + if !verbose_share || !verbose then + lprintf_nl "Sending %d share(s) to server %s:%d%s" + nfiles (Ip.to_string (peer_ip sock)) (peer_port sock) +- (if compressed then "(zlib)" else ""); ++ (if compressed then " (zlib)" else ""); + Buffer.reset buf; + let s_c = + if compressed then +Index: src/networks/donkey/donkeyProtoKademlia.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoKademlia.ml,v +retrieving revision 1.20 +retrieving revision 1.21 +diff -u -r1.20 -r1.21 +--- src/networks/donkey/donkeyProtoKademlia.ml 19 May 2006 23:43:54 -0000 1.20 ++++ src/networks/donkey/donkeyProtoKademlia.ml 31 Oct 2006 15:42:48 -0000 1.21 +@@ -45,6 +45,7 @@ + + let names_of_tag = + [ ++ "\243", Field_UNKNOWN "encryption"; (* 0xF3 *) + "\248", Field_UNKNOWN "buddyhash"; (* 0xF8 *) + "\249", Field_UNKNOWN "clientlowid"; (* 0xF9 *) + "\250", Field_UNKNOWN "serverport"; (* 0xFA *) +Index: src/networks/donkey/donkeyProtoOvernet.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoOvernet.ml,v +retrieving revision 1.30 +retrieving revision 1.31 +diff -u -r1.30 -r1.31 +--- src/networks/donkey/donkeyProtoOvernet.ml 19 May 2006 23:43:54 -0000 1.30 ++++ src/networks/donkey/donkeyProtoOvernet.ml 5 Nov 2006 14:13:51 -0000 1.31 +@@ -189,15 +189,21 @@ + match tag.tag_name with + Field_UNKNOWN "loc" -> + for_string_tag tag (fun bcp -> +- if !verbose_overnet then +- lprintf_nl "loc tag : [%s]" bcp; ++ if !verbose_overnet then lprintf_nl "loc tag : [%s]" bcp; + if String2.starts_with bcp "bcp://" then + let bcp2 = String.sub bcp 6 (String.length bcp - 6) + in + match String2.split_simplify bcp2 ':' with ++ | [_;ip;udpport;tcpport] -> ++ if !verbose_overnet then ++ lprintf_nl "Received BCP type 3 %s" bcp; ++ peer_ip := Ip.of_string ip; ++ peer_udpport := int_of_string udpport; ++ peer_tcpport := int_of_string tcpport; ++ + | [_;ip;port] -> + if !verbose_overnet then +- lprintf_nl "Received BCP type 2 %s" ++ lprintf_nl "Received BCP type 2 %s, ignoring" + bcp; + + (* FIXME: A firewalled peer... +Index: src/networks/donkey/donkeyProtoServer.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoServer.ml,v +retrieving revision 1.21 +retrieving revision 1.22 +diff -u -r1.21 -r1.22 +--- src/networks/donkey/donkeyProtoServer.ml 6 Oct 2005 10:21:09 -0000 1.21 ++++ src/networks/donkey/donkeyProtoServer.ml 14 Nov 2006 18:42:59 -0000 1.22 +@@ -154,22 +154,30 @@ + module SetID = struct + type t = { + ip : Ip.t; ++ port : int option; + zlib : bool; +- port : int option ++ newtags : bool; ++ unicode : bool; ++ related_search : bool; ++ tag_integer : bool; ++ largefiles : bool; ++ udp_obfuscation : bool; ++ tcp_obfuscation : bool; + } + + let parse len s = +- let ip = get_ip s 1 in +- let zlib = (0x01 land get_int s 5) = 0x01 in +- let port = +- if len <= 9 then +- None +- else +- Some (get_int s 9) in ++ let flags = get_int s 5 in + { +- ip = ip; +- zlib = zlib; +- port = port; ++ ip = get_ip s 1; ++ port = if len <= 9 then None else Some (get_int s 9); ++ zlib = 0x01 land flags = 0x01; ++ newtags = 0x08 land flags = 0x08; ++ unicode = 0x10 land flags = 0x10; ++ related_search = 0x40 land flags = 0x40; ++ tag_integer = 0x80 land flags = 0x80; ++ largefiles = 0x100 land flags = 0x100; ++ udp_obfuscation = 0x200 land flags = 0x200; ++ tcp_obfuscation = 0x400 land flags = 0x400; + } + + let print t = +Index: src/networks/donkey/donkeyProtoUdp.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyProtoUdp.ml,v +retrieving revision 1.14 +retrieving revision 1.15 +diff -u -r1.14 -r1.15 +--- src/networks/donkey/donkeyProtoUdp.ml 16 Jan 2006 16:05:14 -0000 1.14 ++++ src/networks/donkey/donkeyProtoUdp.ml 1 Oct 2006 17:54:00 -0000 1.15 +@@ -455,6 +455,7 @@ + | EmuleReaskAckUdpReq of Md4.t + | EmuleFileNotFoundUdpReq + | EmuleQueueFullUdpReq ++| EmulePortTestReq + + | UnknownUdpReq of int * string + +@@ -484,6 +485,7 @@ + | 145 -> EmuleReaskAckUdpReq (get_md4 s 1) + (* | 146 -> EmuleFileNotFoundUdpReq *) + | 147 -> EmuleQueueFullUdpReq ++ | 254 -> EmulePortTestReq + + | _ -> raise Exit + with +@@ -524,6 +526,8 @@ + Printf.bprintf b "EmuleFileNotFoundUdpReq" + | EmuleQueueFullUdpReq -> + Printf.bprintf b "EmuleQueueFullUdpReq" ++ | EmulePortTestReq -> ++ Printf.bprintf b "EmulePortTestReq" + + | UnknownUdpReq (magic, s) -> + Printf.bprintf b "UnknownReq magic %d\n" magic; +@@ -613,6 +617,14 @@ + buf_int8 buf 53; + QueryIDReplyUdp.write buf t + ++ | EmulePortTestReq -> ++ buf_int8 buf 2; ++ buf_int8 buf 0; ++ buf_int8 buf 0; ++ buf_int8 buf 0; ++ buf_int8 buf 0xfe; ++ buf_int8 buf 0x31 ++ + | EmuleQueueFullUdpReq + | EmuleFileNotFoundUdpReq + | EmuleReaskAckUdpReq _ +Index: src/networks/donkey/donkeyServers.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyServers.ml,v +retrieving revision 1.59 +retrieving revision 1.66 +diff -u -r1.59 -r1.66 +--- src/networks/donkey/donkeyServers.ml 1 Sep 2006 16:25:15 -0000 1.59 ++++ src/networks/donkey/donkeyServers.ml 21 Nov 2006 22:34:34 -0000 1.66 +@@ -34,6 +34,7 @@ + open CommonOptions + open CommonGlobals + open CommonSources ++open CommonShared + + open DonkeyMftp + open DonkeyImport +@@ -210,6 +211,35 @@ + + + let disconnect_server s reason = ++ let choose_new_master_server s = ++ match !DonkeyGlobals.master_server with ++ | Some ss when s == ss -> ++ DonkeyGlobals.master_server := None; ++ (try ++ DonkeyGlobals.master_server := ++ Some (List.find (fun s -> s.server_master) !servers_list); ++ if !verbose_location then begin ++ match !DonkeyGlobals.master_server with ++ | Some ns -> ++ lprintf_nl "changed main master server from %s (%s) to %s (%s)" ++ ss.server_name (string_of_server ss) ns.server_name (string_of_server ns) ++ | _ -> () ++ end ++ with Not_found -> ()) ++ | _ -> (); ++ in ++(* remove this server from the list of servers a file was published to *) ++ let remove_server_from_shared_files s = ++ List.iter (fun file -> ++ shared_iter (fun sh -> ++ let impl = as_shared_impl sh in ++ impl.impl_shared_servers <- ++ List2.removeq (as_server s.server_server) impl.impl_shared_servers) ++ ) s.server_sent_shared; ++ s.server_sent_shared <- [] ++ in ++ choose_new_master_server s; ++ remove_server_from_shared_files s; + match s.server_sock with + NoConnection -> () + | ConnectionWaiting token -> +@@ -225,16 +255,11 @@ + s.server_users <- []; + set_server_state s (NotConnected (reason, -1)); + s.server_master <- false; +- (match !DonkeyGlobals.master_server with +- | Some ss when s == ss -> +- DonkeyGlobals.master_server := None; +- | _ -> ()); + s.server_banner <- ""; + s.server_sent_all_queries <- false; + remove_connecting_server s; + remove_connected_server s + +- + let server_handler s sock event = + match event with + BASIC_EVENT (CLOSED r) -> +@@ -259,6 +284,13 @@ + match t with + M.SetIDReq t -> + s.server_has_zlib <- t.M.SetID.zlib; ++ s.server_has_newtags <- t.M.SetID.newtags; ++ s.server_has_unicode <- t.M.SetID.unicode; ++ s.server_has_related_search <- t.M.SetID.related_search; ++ s.server_has_tag_integer <- t.M.SetID.tag_integer; ++ s.server_has_largefiles <- t.M.SetID.largefiles; ++ s.server_has_udp_obfuscation <- t.M.SetID.udp_obfuscation; ++ s.server_has_tcp_obfuscation <- t.M.SetID.tcp_obfuscation; + if low_id t.M.SetID.ip && !!force_high_id then + disconnect_server s (Closed_for_error "Low ID") + else begin +@@ -281,23 +313,24 @@ + end + + | M.MessageReq msg -> ++ if msg <> "" then begin + if !last_message_sender <> server_num s then begin +- let server_header = Printf.sprintf "\n+-- From server %s [%s:%d] ------" +- s.server_name (Ip.to_string s.server_ip) s.server_port in ++ let server_header = Printf.sprintf "\n+-- From server %s [%s] ------" ++ s.server_name (string_of_server s) in + CommonEvent.add_event (Console_message_event (Printf.sprintf "%s\n" server_header)); + if !CommonOptions.verbose_msg_servers then +- lprintf_nl "%s" server_header; ++ lprintf_nl "%s" server_header; + last_message_sender := server_num s + end; + s.server_banner <- s.server_banner ^ Printf.sprintf "%s\n" msg; + let msg = Printf.sprintf "| %s" msg in + CommonEvent.add_event (Console_message_event (Printf.sprintf "%s\n" msg)); +- if !CommonOptions.verbose_msg_servers then +- lprintf_nl "%s" msg ++ if !CommonOptions.verbose_msg_servers then lprintf_nl "%s" msg ++ end + + | M.ServerListReq l -> + if !!update_server_list_server then begin +- if !verbose_msg_servers then lprintf_nl "Received serverlist from server"; ++ if !verbose_msg_servers then lprintf_nl "Received %d servers from server" (List.length l); + let module Q = M.ServerList in + List.iter (fun s -> + safe_add_server s.Q.ip s.Q.port +@@ -318,7 +351,7 @@ + s.server_name <- name + | { tag_name = Field_UNKNOWN "description"; tag_value = String desc } -> + s.server_description <- desc +- | _ -> () ++ | _ -> lprintf_nl "parsing donkeyServers.ServerInfo, unknown field %s" (string_of_tag tag) + ) s.server_tags; + + (* nice and ugly, but it doesn't require any new fields *) +@@ -339,8 +372,8 @@ + s.server_nfiles <- Some (Int64.of_int files); + if (users < !!min_users_on_server && not s.server_preferred) then + begin +- lprintf_nl "%s:%d remove server min_users_on_server limit hit!" +- (Ip.to_string s.server_ip) s.server_port; ++ lprintf_nl "%s remove server min_users_on_server limit hit!" ++ (string_of_server s); + + disconnect_server s Closed_for_timeout; + server_remove (as_server s.server_server); +@@ -618,8 +651,7 @@ + if ls < min_last_conn && s.server_sock = NoConnection + && not s.server_preferred then begin + if !verbose then +- lprintf_nl "old servers: Server too old: %s:%d" +- (Ip.to_string s.server_ip) s.server_port; ++ lprintf_nl "old servers: Server too old: %s" (string_of_server s); + + to_remove := s :: !to_remove + end +@@ -674,16 +706,14 @@ + if connection_can_try s.server_connection_control then + begin + if !verbose then +- lprintf_nl "WALKER: try connect %s" +- (Ip.to_string s.server_ip); ++ lprintf_nl "WALKER: try connect %s" (string_of_server s); + connect_server s + end + else + begin + delayed_list := s :: !delayed_list; + if !verbose then +- lprintf_nl "WALKER: connect %s delayed" +- (Ip.to_string s.server_ip); ++ lprintf_nl "WALKER: connect %s delayed" (string_of_server s); + end + | _ -> () + +@@ -749,13 +779,13 @@ + match s.server_sock with + | Connection _ -> + if !verbose_location then begin +- if !tag2 then begin ++ if !tag2 then begin + lprintf_n "master servers (old):"; +- tag1 := false; +- tag2 := false +- end; +- lprintf " %s" (Ip.to_string s.server_ip) +- end; ++ tag1 := false; ++ tag2 := false ++ end; ++ lprintf " %s" (string_of_server s) ++ end; + masters := s :: !masters + | _ -> s.server_master <- false + ) server_list; +@@ -772,8 +802,7 @@ + masters := s :: !masters; + masters := List.rev (List.sort compare_servers !masters); + +- server_send_share s.server_has_zlib sock +- (DonkeyShare.all_shared ()) ++ DonkeyGlobals.master_server := Some s; + ) + in + +@@ -786,7 +815,7 @@ + if !nconnected_servers > max_allowed_connected_servers then + begin + if !verbose_location then +- lprintf_nl "master servers: disconnect %s" (Ip.to_string s.server_ip); ++ lprintf_nl "master servers: disconnect %s" (string_of_server s); + nconnected_servers := !nconnected_servers - 3; + do_if_connected s.server_sock (fun sock -> + (* We will disconnect from this server. +@@ -805,23 +834,17 @@ + - connection_last_conn s.server_connection_control + in + if !verbose_location then +- lprintf_nl "master servers: Checking ip:%s, users: %Ld, ct:%d" +- (Ip.to_string s.server_ip) +- (match s.server_nusers with None -> 0L | Some v -> v) +- connection_time; +- if not s.server_master +- && (s.server_preferred +- || connection_time > !!become_master_delay +- ) +- then ++ lprintf_nl "master servers: Checking %s, users: %Ld, ct:%d" ++ (string_of_server s) ++ (match s.server_nusers with None -> 0L | Some v -> v) ++ connection_time; ++ if not s.server_master then + begin +- if (!nmasters < max_allowed_connected_servers) then +- begin +- if !verbose_location then +- lprintf_nl "master servers: raising %s" +- (Ip.to_string s.server_ip); ++ if (!nmasters < max_allowed_connected_servers) then begin ++ if !verbose_location then ++ lprintf_nl "master servers: raising %s" (string_of_server s); + make_master s +- end ++ end + else if s.server_sent_all_queries then + match !masters with + [] -> disconnect_old_server s +@@ -838,7 +861,7 @@ + or is a preferred one *) + if (s.server_preferred && not ss.server_preferred) + || (!!keep_best_server +- && mini ((Int64.to_int ss_nusers) + 1000) ++ && min ((Int64.to_int ss_nusers) + 1000) + ((Int64.to_int ss_nusers) * 5) + < (Int64.to_int s_nusers) + ) +@@ -847,7 +870,7 @@ + if !verbose_location then + lprintf_nl + "master servers: raising %s, disconnected %s" +- (Ip.to_string s.server_ip) (Ip.to_string ss.server_ip); ++ (string_of_server s) (string_of_server ss); + ss.server_master <- false; + masters := tail; + make_master s +Index: src/networks/donkey/donkeyShare.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyShare.ml,v +retrieving revision 1.50 +retrieving revision 1.55 +diff -u -r1.50 -r1.55 +--- src/networks/donkey/donkeyShare.ml 1 Sep 2006 16:22:15 -0000 1.50 ++++ src/networks/donkey/donkeyShare.ml 9 Oct 2006 16:17:19 -0000 1.55 +@@ -39,18 +39,15 @@ + + module VB = VerificationBitmap + +-let new_shared_files = ref [] +- + let must_share_file file codedname has_old_impl = + match file.file_shared with + | Some _ -> () + | None -> +- new_shared := true; + let full_name = file_disk_name file in + let magic = + match Magic.M.magic_fileinfo full_name false with + None -> None +- | Some magic -> Some (HashMagic.merge files_magic magic) ++ | Some magic -> Some (intern magic) + in + + let impl = { +@@ -65,9 +62,9 @@ + impl_shared_val = file; + impl_shared_requests = 0; + impl_shared_magic = magic; ++ impl_shared_servers = [] + } in + file.file_shared <- Some impl; +- new_shared_files := file :: !new_shared_files; + incr CommonGlobals.nshared_files; + CommonShared.shared_calculate_total_bytes (); + match has_old_impl with +@@ -99,7 +96,7 @@ + lprintf_nl "Sharing file with MD4: %s" (Md4.to_string md4); + + let file = new_file sh.sh_name FileShared md4 sh.sh_size +- "" false in ++ "" false CommonUserDb.admin_user in + must_share_file file codedname old_impl; + file.file_computed_md4s <- md4s; + add_file_filenames (as_file file) (Filename.basename sh.sh_name); +@@ -162,41 +159,69 @@ + None -> () + | Some _ -> shared_files := file :: !shared_files + ) files_by_md4; +- if !verbose_share then lprintf_nl "%d files shared" (List.length !shared_files); ++ if !verbose_share then lprintf_nl "scanned shared files, %d files found" (List.length !shared_files); + !shared_files + +-(* Check whether new files are shared, and send them to connected servers. +- Do it only once per 5 minutes to prevent sending to many times all files. +- Change: Just send *new* shared files to servers, they never forget a +- clients files until disconnection. +- Should I only do it for master servers, no ? +-*) ++(* publish shared files to servers, called once per minute *) + let send_new_shared () = +- let tag = ref false in +- if !new_shared then +- begin +- new_shared := false; +- if !new_shared_files <> [] then +- begin +- List.iter (fun s -> +- if s.server_master then +- begin +- if !verbose_share || !verbose then +- lprintf_nl "send_new_shared: found master server %s:%d" +- (Ip.to_string s.server_ip) s.server_port; +- tag := true; +- do_if_connected s.server_sock (fun sock -> +- server_send_share s.server_has_zlib sock !new_shared_files) +- end +- ) (connected_servers ()); +- if !tag && (!verbose_share || !verbose) then +- lprintf_nl "send_new_shared: Sent %d new shared files to servers" +- (List.length !new_shared_files); +- new_shared_files := [] +- end +- else +- lprintf_nl "donkey send_new_share: No new shared files to send to servers" +- end ++(* sort list to publish least published files first *) ++ let ( |> ) x f = f x in ++ let all_shared = ++ all_shared () ++ |> List.map (fun e -> ++ (match e.file_shared with ++ Some s -> List.length s.impl_shared_servers ++ | _ -> 0) , e) ++ |> List.sort (fun (a,_) (b,_) -> compare a b) ++ |> List.map snd ++ in ++ ++(* iter through connected servers *) ++ List.iter (fun s -> ++ ++(* publish files only on master servers and do not publish more files than hard limit allows *) ++ if s.server_master && ++ (match s.server_hard_limit with ++ Some v when (Int64.to_int v) < List.length s.server_sent_shared -> false ++ | _ -> true) then ++ ++(* iter through all shared files and check if the file is already published on the current server ++ build a list of files_to_send with yet unpublished files *) ++ begin ++ let files_to_send = ref [] in ++ List.iter (fun f -> ++ match f.file_shared with ++ Some impl -> ++ if not (List.mem (CommonServer.as_server s.server_server) impl.impl_shared_servers) ++ && List.length !files_to_send < !!max_published_files then ++ files_to_send := f :: !files_to_send ++ | _ -> () (* this case never happens *) ++ ) all_shared; ++ ++ if !files_to_send <> [] then ++ begin ++ if !verbose_share || !verbose then ++ lprintf_nl "publishing %d new files to %s (holds %d files)" ++ (List.length !files_to_send) (string_of_server s) ++ (List.length s.server_sent_shared); ++ ++(* publish files on server *) ++ do_if_connected s.server_sock (fun sock -> ++ server_send_share s.server_has_zlib sock !files_to_send); ++ ++(* append new published files to server structure *) ++ s.server_sent_shared <- !files_to_send @ s.server_sent_shared; ++ ++(* iter through published files and append current server *) ++ List.iter (fun file -> ++ match file.file_shared with ++ Some impl -> impl.impl_shared_servers <- ++ impl.impl_shared_servers @ [(CommonServer.as_server s.server_server)] ++ | _ -> ()) !files_to_send ++ ++ end ++ end ++ ) (connected_servers ()); + + (* + The problem: sh.shared_fd might be closed during the execution of the +@@ -209,7 +234,7 @@ + let computing = ref false + + (* Compute (at most) one MD4 chunk if needed. *) +-let check_shared_files () = ++let rec check_shared_files () = + let module M = CommonHasher in + if not !computing then + match !shared_files with +@@ -259,7 +284,11 @@ + new_file_to_share s sh.shared_shared.impl_shared_codedname (Some sh.shared_shared); + end + else +- job_creater () ++ job_creater (); ++ (* only try back-to-back hashing if hashing is ++ handled by a separate thread *) ++ if BasicSocket.has_threads () then ++ check_shared_files () + ) + with + Wrong_file_size (real,computed) -> +@@ -307,7 +336,7 @@ + let magic = + match Magic.M.magic_fileinfo fullname false with + None -> None +- | Some magic -> Some (HashMagic.merge files_magic magic) ++ | Some magic -> Some (intern magic) + in + + let rec impl = { +@@ -322,6 +351,7 @@ + impl_shared_val = pre_shared; + impl_shared_requests = 0; + impl_shared_magic = magic; ++ impl_shared_servers = []; + } and + pre_shared = { + shared_shared = impl; +Index: src/networks/donkey/donkeyStats.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyStats.ml,v +retrieving revision 1.24 +retrieving revision 1.25 +diff -u -r1.24 -r1.25 +--- src/networks/donkey/donkeyStats.ml 26 Jan 2006 10:34:53 -0000 1.24 ++++ src/networks/donkey/donkeyStats.ml 23 Sep 2006 20:29:47 -0000 1.25 +@@ -170,6 +170,21 @@ + let _ = + network.op_network_display_stats <- (fun buf o -> print_stats o New false); + ++ network.op_network_stat_info_list <- (fun _ -> ++ let r = ref [] in ++ let l1 = stats_list brand_list stats_array in ++ let l2 = stats_list brand_list !!gstats_array in ++ let u1 = BasicSocket.last_time () - !start_session in ++ let u2 = (guptime() + u1) in ++ r := [("Session clients", u1, l1); ("Global clients", u2, l2)]; ++ if !!emule_mods_count then begin ++ let l3 = stats_list brand_mod_list stats_mod_array in ++ let l4 = stats_list brand_mod_list !!gstats_mod_array in ++ r := !r @ [ ("Session mods", u1, l3); ("Global mods", u2, l4)] ++ end; ++ !r ++ ); ++ + register_commands + [ + "client_stats", "Network/Donkey",Arg_none (fun o -> +Index: src/networks/donkey/donkeySui1.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeySui1.ml,v +retrieving revision 1.1 +retrieving revision 1.2 +diff -u -r1.1 -r1.2 +--- src/networks/donkey/donkeySui1.ml 26 Jan 2006 00:25:25 -0000 1.1 ++++ src/networks/donkey/donkeySui1.ml 21 Oct 2006 20:01:23 -0000 1.2 +@@ -30,3 +30,8 @@ + external create_signature : string -> int -> int64 -> int -> int64 -> string = "ml_createSignature" + external verify_signature : string -> int -> string -> int -> int64 -> int -> int64 -> bool = "ml_verifySignature_bytecode" "ml_verifySignature" + end ++ ++let ext_lprintf_nl msg = ++ if !CommonOptions.verbose_unexpected_messages then Printf2.lprintf_nl ("%s") msg ++ ++let _ = Callback.register "ml_lprintf_nl" ext_lprintf_nl +Index: src/networks/donkey/donkeyTypes.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyTypes.ml,v +retrieving revision 1.47 +retrieving revision 1.51 +diff -u -r1.47 -r1.51 +--- src/networks/donkey/donkeyTypes.ml 5 Sep 2006 14:18:24 -0000 1.47 ++++ src/networks/donkey/donkeyTypes.ml 14 Nov 2006 18:42:59 -0000 1.51 +@@ -39,6 +39,7 @@ + mutable emule_secident : int; + mutable emule_noviewshared : int; + mutable emule_supportpreview : int; ++ mutable emule_osinfosupport : int; + + mutable emule_compression : int; + mutable emule_sourceexchange : int; +@@ -430,7 +431,21 @@ + | Invalid_address _ -> failwith "Invalid address" + end) + +-type server = (*[]*){ ++type file = { ++ file_file : file CommonFile.file_impl; ++ file_md4 : Md4.t; ++ mutable file_swarmer : CommonSwarming.t option; ++ mutable file_nchunks : int; ++ mutable file_nchunk_hashes : int; ++ mutable file_diskname : string; ++ mutable file_computed_md4s : Md4.t array; ++ mutable file_format : format; ++ mutable file_shared : file CommonShared.shared_impl option; ++ mutable file_sources : DonkeySources.file_sources_manager; ++ mutable file_comments : (Ip.t * string * int * string) list; ++ } ++ ++and server = (*[]*){ + mutable server_server : server CommonServer.server_impl; + mutable server_ip : Ip.t; + mutable server_cid : Ip.t option; +@@ -463,6 +478,19 @@ + mutable server_waiting_queries : file list; + mutable server_sent_all_queries : bool; + mutable server_has_zlib : bool; ++ mutable server_has_newtags : bool; ++ mutable server_has_unicode : bool; ++ mutable server_has_related_search : bool; ++ mutable server_has_tag_integer : bool; ++ mutable server_has_largefiles : bool; ++ mutable server_has_udp_obfuscation : bool; ++ mutable server_has_tcp_obfuscation : bool; ++ mutable server_obfuscation_port_tcp : int option; ++ mutable server_obfuscation_port_udp : int option; ++ mutable server_udp_key : int option; ++ mutable server_udp_keyip : Ip.t option; ++ mutable server_dynip : string; ++ mutable server_auxportslist : string; + + mutable server_flags : int; + mutable server_version : string; +@@ -470,6 +498,7 @@ + mutable server_soft_limit : int64 option; + mutable server_hard_limit : int64 option; + mutable server_max_users : int64 option; ++ mutable server_sent_shared : file list + } + + +@@ -519,7 +548,6 @@ + CommonSwarming.uploader + ) list; + mutable client_all_files : result list option; +- mutable client_last_asked_file : CommonTypes.file; + mutable client_tags: CommonTypes.tag list; + mutable client_name : string; + mutable client_rating : int ; +@@ -531,6 +559,8 @@ + mutable client_uploaded : Int64.t; + mutable client_brand : brand; + mutable client_brand_mod : brand_mod; ++ mutable client_osinfo_sent : bool; ++ mutable client_osinfo : string option; + mutable client_banned : bool; + mutable client_score : int; + mutable client_next_queue : int; +@@ -549,6 +579,7 @@ + mutable client_req_challenge : Int64.t; + mutable client_public_key : string option; + mutable client_sui_verified : bool option; ++ mutable client_last_file_req_md4 : Md4.t option; + } + + and compressed_parts = { +@@ -579,19 +610,6 @@ + int * (* last connection attempt *) + int (* booked client num *) + +-and file = { +- file_file : file CommonFile.file_impl; +- file_md4 : Md4.t; +- mutable file_swarmer : CommonSwarming.t option; +- mutable file_nchunks : int; +- mutable file_nchunk_hashes : int; +- mutable file_diskname : string; +- mutable file_computed_md4s : Md4.t array; +- mutable file_format : format; +- mutable file_shared : file CommonShared.shared_impl option; +- mutable file_sources : DonkeySources.file_sources_manager; +- } +- + and file_to_share = { + shared_name : string; + shared_size : int64; +@@ -674,6 +692,7 @@ + emule_secident = 0; + emule_noviewshared = 0; + emule_supportpreview = 0; ++ emule_osinfosupport = 0; + + emule_compression = 0; (* 1 *) + emule_sourceexchange = 0; (* 3 *) +Index: src/networks/donkey/donkeyUdp.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/donkey/donkeyUdp.ml,v +retrieving revision 1.23 +retrieving revision 1.25 +diff -u -r1.23 -r1.25 +--- src/networks/donkey/donkeyUdp.ml 1 Sep 2006 16:25:15 -0000 1.23 ++++ src/networks/donkey/donkeyUdp.ml 15 Nov 2006 12:34:30 -0000 1.25 +@@ -223,7 +223,7 @@ + + let udp_client_handler t p = + if !verbose_udp then +- lprintf "Received UDP message:\n%s\n" (Udp.print t); ++ lprintf_nl "Received UDP message:\n%s" (Udp.print t); + + let udp_from_server p = + match p.UdpSocket.udp_addr with +@@ -296,7 +296,11 @@ + s.server_name <- name + | { tag_name = Field_UNKNOWN "description"; tag_value = String desc } -> + s.server_description <- desc +- | _ -> () ++ | { tag_name = Field_UNKNOWN "auxportslist" ; tag_value = String aux } -> ++ s.server_auxportslist <- aux ++ | { tag_name = Field_UNKNOWN "dynip" ; tag_value = String dynip } -> ++ s.server_dynip <- dynip ++ | _ -> lprintf_nl "parsing Udp.ServerDescReplyUdp, unknown field %s" (string_of_tag tag) + ) t.M.tags; + + if s.server_tags = [] then +@@ -306,6 +310,15 @@ + + | Udp.EmuleReaskFilePingUdpReq t -> () + ++ | Udp.EmulePortTestReq -> ++ (match !porttest_sock with ++ None -> () ++ | Some sock -> ++ let s = Buffer.create 10 in ++ DonkeyProtoUdp.write s Udp.EmulePortTestReq; ++ TcpBufferedSocket.write_string sock (Buffer.contents s); ++ porttest_sock := None) ++ + | _ -> + if !verbose_unexpected_messages then + lprintf "Unexpected UDP message: %s\n" +Index: src/networks/fasttrack/fasttrackGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fasttrack/fasttrackGlobals.ml,v +retrieving revision 1.41 +retrieving revision 1.43 +diff -u -r1.41 -r1.43 +--- src/networks/fasttrack/fasttrackGlobals.ml 1 Sep 2006 16:22:15 -0000 1.41 ++++ src/networks/fasttrack/fasttrackGlobals.ml 9 Nov 2006 21:32:27 -0000 1.43 +@@ -267,7 +267,7 @@ + + let min_range_size = megabyte + +-let new_file file_temporary file_name file_size file_hash = ++let new_file file_temporary file_name file_size file_hash user = + let file_temp = Filename.concat !!temp_directory file_temporary in + (* (Printf.sprintf "FT-%s" (Md4.to_string file_id)) in *) + let t = Unix32.create_rw file_temp in +@@ -293,6 +293,8 @@ + impl_file_fd = Some t; + impl_file_size = file_size; + impl_file_downloaded = Int64.zero; ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_val = file; + impl_file_ops = file_ops; + impl_file_age = last_time (); +@@ -335,7 +337,7 @@ + + exception FileFound of file + +-let new_file file_id file_name file_size file_uids = ++let new_file file_id file_name file_size file_uids user = + let file = ref None in + List.iter (fun uid -> + match Uid.to_uid uid with +@@ -343,7 +345,7 @@ + file := Some (try + Hashtbl.find files_by_uid file_hash + with _ -> +- let file = new_file file_id file_name file_size file_hash in ++ let file = new_file file_id file_name file_size file_hash user in + Hashtbl.add files_by_uid file_hash file; + file) + | _ -> () +Index: src/networks/fasttrack/fasttrackPandora.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fasttrack/fasttrackPandora.ml,v +retrieving revision 1.9 +retrieving revision 1.10 +diff -u -r1.9 -r1.10 +--- src/networks/fasttrack/fasttrackPandora.ml 14 Dec 2005 21:17:46 -0000 1.9 ++++ src/networks/fasttrack/fasttrackPandora.ml 21 Nov 2006 22:34:34 -0000 1.10 +@@ -223,7 +223,7 @@ + let check1 = check_xinu s (pos + 5 + len1) len (depth+1) in + let check2 = check_xinu s (pos + 5 + len2) len (depth+1) in + +- maxi check0 (maxi check1 check2) ++ max check0 (max check1 check2) + else depth + | _ -> -10 + +Index: src/networks/fasttrack/fasttrackServers.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fasttrack/fasttrackServers.ml,v +retrieving revision 1.29 +retrieving revision 1.30 +diff -u -r1.29 -r1.30 +--- src/networks/fasttrack/fasttrackServers.ml 19 May 2006 23:43:54 -0000 1.29 ++++ src/networks/fasttrack/fasttrackServers.ml 19 Sep 2006 17:07:43 -0000 1.30 +@@ -322,7 +322,7 @@ + ) file.file_searches + ) !connected_servers + +-let really_download_file (r : CommonTypes.result_info) = ++let really_download_file (r : CommonTypes.result_info) user = + let rec iter uids = + match uids with + uid :: tail -> +@@ -334,7 +334,7 @@ + let hash,file_temp = iter r.result_uids in + + let file = new_file file_temp (List.hd r.result_names) +- r.result_size [Uid.create (Md5Ext hash)] in ++ r.result_size [Uid.create (Md5Ext hash)] user in + if !verbose then + lprintf "DOWNLOAD FILE %s\n" file.file_name; + if not (List.memq file !current_files) then begin +Index: src/networks/fileTP/fileTPClients.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPClients.ml,v +retrieving revision 1.22 +retrieving revision 1.23 +diff -u -r1.22 -r1.23 +--- src/networks/fileTP/fileTPClients.ml 8 Aug 2006 23:55:28 -0000 1.22 ++++ src/networks/fileTP/fileTPClients.ml 19 Sep 2006 17:07:43 -0000 1.23 +@@ -63,9 +63,9 @@ + if (filesize = 0L || !!chunk_size = 0) then 1 + else Int64.to_int ((filesize) // (min_range_size file)) + 5 + +-let pause_for_cause f r = ++let pause_for_cause f r user = + lprintf_nl "Pausing file %s (%s)" (file_best_name f) r; +- file_pause (as_file f) ++ file_pause (as_file f) user + + let disconnect_client c r = + match c.client_sock with +Index: src/networks/fileTP/fileTPComplexOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPComplexOptions.ml,v +retrieving revision 1.16 +retrieving revision 1.17 +diff -u -r1.16 -r1.17 +--- src/networks/fileTP/fileTPComplexOptions.ml 25 May 2006 19:47:25 -0000 1.16 ++++ src/networks/fileTP/fileTPComplexOptions.ml 19 Sep 2006 17:07:43 -0000 1.17 +@@ -78,8 +78,7 @@ + Md4.of_string (get_value "file_id" value_to_string) + with _ -> failwith "Bad file_id" + in +- +- let file = new_file file_id file_name file_size in ++ let file = new_file file_id file_name file_size CommonUserDb.admin_user in + + (match file.file_swarmer with + None -> () +Index: src/networks/fileTP/fileTPFTP.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPFTP.ml,v +retrieving revision 1.13 +retrieving revision 1.14 +diff -u -r1.13 -r1.14 +--- src/networks/fileTP/fileTPFTP.ml 30 May 2006 10:54:14 -0000 1.13 ++++ src/networks/fileTP/fileTPFTP.ml 19 Sep 2006 17:07:43 -0000 1.14 +@@ -278,13 +278,13 @@ + | "530 " -> + let reason = String.sub line 4 (slen - 4) in + if not (retry_530 reason) then begin +- pause_for_cause d.download_file "530"; ++ pause_for_cause d.download_file "530" CommonUserDb.admin_user; + end else begin + c.client_reconnect <- true; + end; + disconnect_client c Closed_by_user; + | "550 " -> +- pause_for_cause d.download_file "550"; ++ pause_for_cause d.download_file "550" CommonUserDb.admin_user; + disconnect_client c Closed_by_user; + | _ -> + if !verbose then lprintf_nl "Unexpected line %s" line; +@@ -443,11 +443,11 @@ + | "530 " -> + let reason = String.sub line 4 (slen - 4) in + if not (retry_530 reason) then begin +- pause_for_cause file "530"; ++ pause_for_cause file "530" CommonUserDb.admin_user; + end; + close sock Closed_by_user; + | "550 " -> +- pause_for_cause file "550"; ++ pause_for_cause file "550" CommonUserDb.admin_user; + close sock Closed_by_user; + | _ -> + if !verbose then lprintf_nl "Unexpected line %s" line; +Index: src/networks/fileTP/fileTPGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPGlobals.ml,v +retrieving revision 1.28 +retrieving revision 1.30 +diff -u -r1.28 -r1.30 +--- src/networks/fileTP/fileTPGlobals.ml 1 Sep 2006 16:22:15 -0000 1.28 ++++ src/networks/fileTP/fileTPGlobals.ml 9 Nov 2006 21:32:27 -0000 1.30 +@@ -138,7 +138,7 @@ + file_must_update (as_file file); + end + +-let new_file file_id file_name file_size = ++let new_file file_id file_name file_size user = + let file_temp = Filename.concat !!temp_directory + (Printf.sprintf "FileTP-%s" (Md4.to_string file_id)) in + let t = Unix32.create_rw file_temp in +@@ -152,6 +152,8 @@ + file_nconnected_clients = 0; + } and file_impl = { + dummy_file_impl with ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_fd = Some t; + impl_file_size = zero; + impl_file_downloaded = zero; +@@ -168,11 +170,11 @@ + (* lprintf "ADD FILE TO DOWNLOAD LIST\n"; *) + file + +-let new_file file_id file_name file_size = ++let new_file file_id file_name file_size users = + try + Hashtbl.find files_by_uid file_id + with _ -> +- let file = new_file file_id file_name file_size in ++ let file = new_file file_id file_name file_size users in + Hashtbl.add files_by_uid file_id file; + file + +Index: src/networks/fileTP/fileTPHTTP.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPHTTP.ml,v +retrieving revision 1.25 +retrieving revision 1.26 +diff -u -r1.25 -r1.26 +--- src/networks/fileTP/fileTPHTTP.ml 10 Aug 2006 17:41:20 -0000 1.25 ++++ src/networks/fileTP/fileTPHTTP.ml 19 Sep 2006 17:07:43 -0000 1.26 +@@ -174,7 +174,7 @@ + end; + + if code < 200 || code > 299 then begin +- pause_for_cause file (Printf.sprintf "%d" code); ++ pause_for_cause file (Printf.sprintf "%d" code) CommonUserDb.admin_user; + failwith "Bad HTTP code"; + end; + +@@ -386,7 +386,7 @@ + (fun c -> + match c with + x when x < 200 || x > 299 -> +- pause_for_cause file (string_of_int x); ++ pause_for_cause file (string_of_int x) CommonUserDb.admin_user; + | _ -> () + ) + +Index: src/networks/fileTP/fileTPInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/fileTP/fileTPInteractive.ml,v +retrieving revision 1.46 +retrieving revision 1.52 +diff -u -r1.46 -r1.52 +--- src/networks/fileTP/fileTPInteractive.ml 5 Sep 2006 15:32:17 -0000 1.46 ++++ src/networks/fileTP/fileTPInteractive.ml 12 Nov 2006 12:44:24 -0000 1.52 +@@ -234,7 +234,7 @@ + + let previous_url = ref "" + +-let download_file url referer = ++let download_file url referer user = + let u = Url.of_string url in + + if List.mem u !!old_files && !previous_url <> url then begin +@@ -242,7 +242,7 @@ + failwith "URL already downloaded: repeat command again to force"; + end; + +- let file = new_file (Md4.random ()) u.Url.full_file zero in ++ let file = new_file (Md4.random ()) u.Url.full_file zero user in + + if !verbose then + lprintf_nl "Started new download: %s from %s" (file_best_name file) url; +@@ -250,6 +250,7 @@ + current_files := file :: !current_files; + end; + ++ CommonInteractive.start_download (as_file file); + download_file_from_mirror file u referer; + find_mirrors file u + +@@ -276,7 +277,7 @@ + It returns true if this file can be handled by fileTP, + and false otherwise. + *) +-let op_network_parse_url url = ++let op_network_parse_url url user = + let location_regexp = "Location: \\(.*\\)" in + let real_url = get_regexp_string url (Str.regexp location_regexp) in + if (is_http_torrent url real_url) && !!enable_bittorrent then +@@ -286,7 +287,7 @@ + let length_regexp = "Content-Length: \\(.*\\)" in + try let length = get_regexp_int url (Str.regexp length_regexp) in + if (length > 0) then begin +- download_file real_url ""; "started FileTP download", true ++ download_file real_url "" user; "started FileTP download", true + end + else "can not parse Content-Length", false + with Not_found -> +@@ -295,7 +296,7 @@ + else + if (String2.check_prefix real_url "ftp://") || + (String2.check_prefix real_url "ssh://") then ( +- download_file real_url ""; ++ download_file url "" user; + "started FileTP download", true) + else + "invalid URL", false +@@ -310,8 +311,8 @@ + "http", "Network/FileTP", Arg_multiple (fun args o -> + try + (match args with +- url :: [referer] -> download_file url referer +- | [url] -> download_file url "" ++ url :: [referer] -> download_file url referer o.conn_user.ui_user ++ | [url] -> download_file url "" o.conn_user.ui_user + | _ -> raise Not_found); + let buf = o.conn_buf in + if o.conn_output = HTML then +@@ -405,7 +406,7 @@ + CommonNetwork.register_commands commands; + network.op_network_share <- (fun fullname codedname size -> ()); + network.op_network_search <- (fun ss buf -> ()); +- network.op_network_download <- (fun r -> dummy_file); ++ network.op_network_download <- (fun r user -> dummy_file); + file_ops.op_file_commit <- (fun file new_name -> clean_stop file); + file_ops.op_file_pause <- (fun file -> + List.iter (fun c -> +@@ -416,9 +417,10 @@ + ) file.file_clients + ); + file_ops.op_file_resume <- (fun file -> ()); +- file_ops.op_file_print_html <- (fun file buf -> ()); ++ file_ops.op_file_print <- (fun file buf -> ()); + network.op_network_close_search <- (fun s -> ()); + network.op_network_forget_search <- (fun s -> ()); + network.op_network_connect_servers <- (fun s -> ()); + network.op_network_reset <- (fun _ -> ()); ++ network.op_network_porttest_result <- (fun _ -> PorttestNotAvailable); + network.op_network_recover_temp <- (fun s -> ()) +Index: src/networks/gnutella/gnutellaClients.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaClients.ml,v +retrieving revision 1.43 +retrieving revision 1.45 +diff -u -r1.43 -r1.45 +--- src/networks/gnutella/gnutellaClients.ml 8 Aug 2006 23:55:28 -0000 1.43 ++++ src/networks/gnutella/gnutellaClients.ml 21 Nov 2006 22:34:34 -0000 1.45 +@@ -997,7 +997,7 @@ + if get_request then + let pos = uc.uc_chunk_pos in + let to_write = uc.uc_chunk_end -- pos in +- let rlen = mini (max_refill sock) (Int64.to_int to_write) in ++ let rlen = min (max_refill sock) (Int64.to_int to_write) in + if !verbose_msg_clients then + lprintf "[GUP] to_write: %d/%Ld/%d\n" rlen to_write + (remaining_to_write sock); +@@ -1041,7 +1041,7 @@ + let listen () = + try + let sock = TcpServerSocket.create "gnutella client server" +- Unix.inet_addr_any ++ (Ip.to_inet_addr !!client_bind_addr) + !!client_port + (fun sock event -> + match event with +Index: src/networks/gnutella/gnutellaComplexOptions.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaComplexOptions.ml,v +retrieving revision 1.28 +retrieving revision 1.29 +diff -u -r1.28 -r1.29 +--- src/networks/gnutella/gnutellaComplexOptions.ml 25 May 2006 19:47:25 -0000 1.28 ++++ src/networks/gnutella/gnutellaComplexOptions.ml 19 Sep 2006 17:07:43 -0000 1.29 +@@ -130,7 +130,7 @@ + file_uids := hash :: !file_uids; + with _ -> ()); + +- let file = new_file file_temp file_name file_size !file_uids in ++ let file = new_file file_temp file_name file_size !file_uids CommonUserDb.admin_user in + + (try + file.file_ttr <- Some (get_value "file_ttr" (value_to_array +Index: src/networks/gnutella/gnutellaGlobals.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaGlobals.ml,v +retrieving revision 1.42 +retrieving revision 1.44 +diff -u -r1.42 -r1.44 +--- src/networks/gnutella/gnutellaGlobals.ml 1 Sep 2006 16:22:15 -0000 1.42 ++++ src/networks/gnutella/gnutellaGlobals.ml 9 Nov 2006 21:32:27 -0000 1.44 +@@ -310,7 +310,7 @@ + let megabyte = Int64.of_int (1024 * 1024) + let megabytes10 = Int64.of_int (10 * 1024 * 1024) + +-let new_file file_temporary file_name file_size file_uids = ++let new_file file_temporary file_name file_size file_uids user = + let file_temp = Filename.concat !!temp_directory file_temporary in + let t = Unix32.create_rw file_temp in + let rec file = { +@@ -329,6 +329,8 @@ + impl_file_fd = Some t; + impl_file_size = file_size; + impl_file_downloaded = Int64.zero; ++ impl_file_owner = user; ++ impl_file_group = user.user_default_group; + impl_file_val = file; + impl_file_ops = file_ops; + impl_file_age = last_time (); +@@ -357,7 +359,7 @@ + + exception FileFound of file + +-let new_file file_id file_name file_size file_uids = ++let new_file file_id file_name file_size file_uids user = + (* if file_uids = [] then + try Hashtbl.find files_by_key (file_name, file_size) with + _ -> +@@ -370,7 +372,7 @@ + try raise (FileFound (Hashtbl.find files_by_uid uid)) + with Not_found -> () + ) file_uids; +- let file = new_file file_id file_name file_size file_uids in ++ let file = new_file file_id file_name file_size file_uids user in + List.iter (fun uid -> + if !verbose then + lprintf "Adding file %s\n" (Uid.to_string uid); +Index: src/networks/gnutella/gnutellaHandler.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaHandler.ml,v +retrieving revision 1.15 +retrieving revision 1.16 +diff -u -r1.15 -r1.16 +--- src/networks/gnutella/gnutellaHandler.ml 28 Aug 2006 18:19:16 -0000 1.15 ++++ src/networks/gnutella/gnutellaHandler.ml 21 Nov 2006 22:34:34 -0000 1.16 +@@ -180,7 +180,7 @@ + let module M = QueryReply in + let module C = CommonUploads in + let replies = ref [] in +- for i = 0 to mini (Array.length files - 1) 9 do ++ for i = 0 to min (Array.length files - 1) 9 do + let sh, info = files.(i) in + let infos = ref [] in + List.iter (fun uid -> +Index: src/networks/gnutella/gnutellaInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaInteractive.ml,v +retrieving revision 1.63 +retrieving revision 1.67 +diff -u -r1.63 -r1.67 +--- src/networks/gnutella/gnutellaInteractive.ml 5 Sep 2006 15:32:17 -0000 1.63 ++++ src/networks/gnutella/gnutellaInteractive.ml 12 Nov 2006 12:44:24 -0000 1.67 +@@ -77,8 +77,8 @@ + if file_state file = FileDownloading then + GnutellaServers.really_recover_file file + +-let download_file r = +- let file = GnutellaServers.really_download_file r in ++let download_file r user = ++ let file = GnutellaServers.really_download_file r user in + recover_file file; + as_file file + +@@ -219,6 +219,7 @@ + [ + !!client_port, "client_port TCP+UDP"; + ]); ++ network.op_network_porttest_result <- (fun _ -> PorttestNotAvailable); + network.op_network_share <- (fun fullname codedname size -> + (* + lprintf "*************** op_network_share %s\n +@@ -241,8 +242,8 @@ + end + ); + (* TODO RESULT *) +- network.op_network_download <- (fun r -> +- result_download r ++ network.op_network_download <- (fun r user -> ++ result_download r user + ) + + let file_num file = +@@ -288,7 +289,7 @@ + module P = GuiTypes + + let _ = +- file_ops.op_file_print_html <- (fun file buf -> ()); ++ file_ops.op_file_print <- (fun file buf -> ()); + file_ops.op_file_cancel <- (fun file -> + CommonSwarming.remove_swarmer file.file_swarmer; + file.file_swarmer <- None; +@@ -370,7 +371,7 @@ + List2.tail_map (fun s -> as_server s.server_server) + !connected_servers + ); +- network.op_network_parse_url <- (fun url -> ++ network.op_network_parse_url <- (fun url user -> + match String2.split (String.escaped url) '|' with + | "gnut://" :: "server" :: ip :: port :: _ -> + let ip = Ip.addr_of_string ip in +@@ -402,7 +403,7 @@ + (* Start a download for this file *) + let rs = new_result name size [] uids [] in + let r = IndexedResults.get_result rs in +- let file = download_file r in ++ let file = download_file r user in + CommonInteractive.start_download file; + "started Gnutella download", true + end +Index: src/networks/gnutella/gnutellaServers.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/gnutella/gnutellaServers.ml,v +retrieving revision 1.28 +retrieving revision 1.29 +diff -u -r1.28 -r1.29 +--- src/networks/gnutella/gnutellaServers.ml 19 May 2006 23:43:55 -0000 1.28 ++++ src/networks/gnutella/gnutellaServers.ml 19 Sep 2006 17:07:43 -0000 1.29 +@@ -678,14 +678,14 @@ + (* *) + (*************************************************************************) + +-let really_download_file (r : result_info) = ++let really_download_file (r : result_info) user = + if !verbose then + lprintf "download_file\n"; + let file_temp = match r.result_uids with + [] -> assert false + | uid :: _ -> Uid.to_file_string uid in + let file = new_file file_temp +- (List.hd r.result_names) r.result_size r.result_uids in ++ (List.hd r.result_names) r.result_size r.result_uids user in + if !verbose then + lprintf "DOWNLOAD FILE %s\n" file.file_name; + if not (List.memq file !current_files) then begin +Index: src/networks/opennap/opennapClients.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/opennap/opennapClients.ml,v +retrieving revision 1.10 +retrieving revision 1.11 +diff -u -r1.10 -r1.11 +--- src/networks/opennap/opennapClients.ml 10 Apr 2006 19:16:36 -0000 1.10 ++++ src/networks/opennap/opennapClients.ml 12 Nov 2006 12:36:14 -0000 1.11 +@@ -69,8 +69,8 @@ + (try file_completed (as_file file.file_file) + with e -> + lprintf "Exception %s in file completed" +- (Printexc2.to_string e) +- ; lprint_newline ()); ++ (Printexc2.to_string e); ++ lprint_newline ()); + current_files := List2.removeq file !current_files; + old_files =:= (file.file_name, file_size file) :: !!old_files; + List.iter (fun c -> +@@ -357,4 +357,4 @@ + lprintf "Exception %s while init limewire server" + (Printexc2.to_string e); + lprint_newline () +- +\ No newline at end of file ++ +Index: src/networks/opennap/opennapInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/opennap/opennapInteractive.ml,v +retrieving revision 1.25 +retrieving revision 1.26 +diff -u -r1.25 -r1.26 +--- src/networks/opennap/opennapInteractive.ml 5 Sep 2006 14:15:20 -0000 1.25 ++++ src/networks/opennap/opennapInteractive.ml 1 Oct 2006 17:54:00 -0000 1.26 +@@ -241,6 +241,7 @@ + [ + !!client_port, "client_port TCP"; + ]); ++ network.op_network_porttest_result <- (fun _ -> PorttestNotAvailable); + + network.op_network_recover_temp <- (fun s -> ()); + +Index: src/networks/opennap/opennapProtocol.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/opennap/opennapProtocol.ml,v +retrieving revision 1.4 +retrieving revision 1.5 +diff -u -r1.4 -r1.5 +--- src/networks/opennap/opennapProtocol.ml 16 Oct 2005 20:42:54 -0000 1.4 ++++ src/networks/opennap/opennapProtocol.ml 12 Nov 2006 12:42:55 -0000 1.5 +@@ -771,7 +771,7 @@ + end + + module Msg = struct +- type t = () ++ type t = unit + + let parse s = () + +Index: src/networks/soulseek/slskInteractive.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/networks/soulseek/slskInteractive.ml,v +retrieving revision 1.23 +retrieving revision 1.24 +diff -u -r1.23 -r1.24 +--- src/networks/soulseek/slskInteractive.ml 5 Sep 2006 14:15:20 -0000 1.23 ++++ src/networks/soulseek/slskInteractive.ml 1 Oct 2006 17:54:00 -0000 1.24 +@@ -182,6 +182,7 @@ + + let _ = + network.op_network_ports <- (fun _ -> []); ++ network.op_network_porttest_result <- (fun _ -> PorttestNotAvailable); + network.op_network_recover_temp <- (fun s -> ()); + network.op_network_load_complex_options <- (fun _ -> ()); + network.op_network_download <- (fun r -> +Index: src/utils/cdk/filename2.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/cdk/filename2.ml,v +retrieving revision 1.5 +retrieving revision 1.6 +diff -u -r1.5 -r1.6 +--- src/utils/cdk/filename2.ml 20 Jul 2006 15:30:21 -0000 1.5 ++++ src/utils/cdk/filename2.ml 31 Oct 2006 15:40:06 -0000 1.6 +@@ -196,7 +196,7 @@ + let sys_checked_name = + if Autoconf.windows then + windows_compliant name +- else if Autoconf.system = "macosx" then ++ else if Autoconf.system = "macos" then + macosx_compliant name + else + posix_compliant name in +Index: src/utils/cdk/printf2.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/cdk/printf2.ml,v +retrieving revision 1.20 +retrieving revision 1.21 +diff -u -r1.20 -r1.21 +--- src/utils/cdk/printf2.ml 24 Jul 2006 20:15:16 -0000 1.20 ++++ src/utils/cdk/printf2.ml 9 Nov 2006 21:32:27 -0000 1.21 +@@ -395,3 +395,6 @@ + + let html_mods_cntr_init () = + html_mods_counter := true ++ ++let print_plural_s v = ++ if v > 1 then "s" else "" +Index: src/utils/cdk/printf2.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/cdk/printf2.mli,v +retrieving revision 1.8 +retrieving revision 1.9 +diff -u -r1.8 -r1.9 +--- src/utils/cdk/printf2.mli 24 Jul 2006 20:15:16 -0000 1.8 ++++ src/utils/cdk/printf2.mli 9 Nov 2006 21:32:27 -0000 1.9 +@@ -56,4 +56,4 @@ + val html_mods_td : Buffer.t -> (string * string * string) list -> unit + val html_mods_cntr_init : unit -> unit + val html_mods_cntr : unit -> int +- ++val print_plural_s : int -> string +Index: src/utils/lib/CryptoPP.cc +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/CryptoPP.cc,v +retrieving revision 1.5 +retrieving revision 1.6 +diff -u -r1.5 -r1.6 +--- src/utils/lib/CryptoPP.cc 2 Dec 2005 12:01:44 -0000 1.5 ++++ src/utils/lib/CryptoPP.cc 21 Oct 2006 20:01:23 -0000 1.6 +@@ -9515,6 +9515,8 @@ + static byte m_publicKey[MAXPUBKEYSIZE+1]; + static unsigned long m_publicKeyLen = 0; + ++void cc_lprintf_nl(const char * msg); ++ + void crypto_exit () { + if (s_signer) { + delete (Signer*) s_signer; +@@ -9539,7 +9541,9 @@ + buf[myString.size()] = 0; + + } catch(const CryptoPP::Exception& e) { +- std::cerr << "createKey: " << e.what() << std::endl; ++ char buf[256]="[CryptoPP] createKey: "; ++ strcat(buf, e.what()); ++ cc_lprintf_nl(buf); + } + + } +@@ -9570,7 +9574,9 @@ + result = m_publicKeyLen; + + } catch(const CryptoPP::Exception& e) { +- std::cerr << "loadKey: " << e.what() << std::endl; ++ char buf[256]="[CryptoPP] loadKey: "; ++ strcat(buf, e.what()); ++ cc_lprintf_nl(buf); + } + + return result; +@@ -9585,7 +9591,7 @@ + + + if (s_signer == NULL) { +- std::cerr << "createSignature: No signer" << std::endl; ++ cc_lprintf_nl("createSignature: No signer"); + return result; + } + +@@ -9612,7 +9618,9 @@ + result = aSink.TotalPutLength(); + + } catch(const CryptoPP::Exception& e) { +- std::cerr << "createSignature: " << e.what() << std::endl; ++ char buf[256]="[CryptoPP] createSignature: "; ++ strcat(buf, e.what()); ++ cc_lprintf_nl(buf); + } + + return result; +@@ -9640,11 +9648,12 @@ + PokeUInt32(bArray+m_publicKeyLen+4,ip); + PokeUInt8(bArray+m_publicKeyLen+4+4,ipType); + } +- + result = pubKey.VerifyMessage(bArray, m_publicKeyLen+4+extra, sig, sigLen); + + } catch(const CryptoPP::Exception& e) { +- std::cerr << "verifySignature: " << e.what() << std::endl; ++ char buf[256]="[CryptoPP] verifySignature: "; ++ strcat(buf, e.what()); ++ cc_lprintf_nl(buf); + } + + return result ? 1 : 0; +Index: src/utils/lib/CryptoPP_stubs.c +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/CryptoPP_stubs.c,v +retrieving revision 1.2 +retrieving revision 1.3 +diff -u -r1.2 -r1.3 +--- src/utils/lib/CryptoPP_stubs.c 29 Jan 2006 19:50:33 -0000 1.2 ++++ src/utils/lib/CryptoPP_stubs.c 21 Oct 2006 20:01:23 -0000 1.3 +@@ -17,10 +17,6 @@ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-#include <string.h> +-#include <caml/mlvalues.h> +-#include <caml/alloc.h> +- + #include "CryptoPP_stubs.h" + + +@@ -87,3 +83,11 @@ + ml_verifySignature_bytecode(value *argv, int argn) { + return ml_verifySignature(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + } ++ ++void cc_lprintf_nl(const char * msg) ++{ ++ static value * caml_func = NULL; ++ if (caml_func == NULL) caml_func = caml_named_value("ml_lprintf_nl"); ++ caml_callback(*caml_func, caml_copy_string(msg)); ++} ++ +Index: src/utils/lib/CryptoPP_stubs.h +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/CryptoPP_stubs.h,v +retrieving revision 1.2 +retrieving revision 1.3 +diff -u -r1.2 -r1.3 +--- src/utils/lib/CryptoPP_stubs.h 29 Jan 2006 19:50:33 -0000 1.2 ++++ src/utils/lib/CryptoPP_stubs.h 21 Oct 2006 20:01:23 -0000 1.3 +@@ -20,6 +20,11 @@ + + #include "../../../config/config.h" + ++#include <string.h> ++#include <caml/mlvalues.h> ++#include <caml/alloc.h> ++#include <caml/callback.h> ++ + #if defined (HAVE_STDINT_H) + #include <stdint.h> + #endif +Index: src/utils/lib/autoconf.ml.new.in +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/autoconf.ml.new.in,v +retrieving revision 1.25 +retrieving revision 1.26 +diff -u -r1.25 -r1.26 +--- src/utils/lib/autoconf.ml.new.in 10 Aug 2006 17:41:20 -0000 1.25 ++++ src/utils/lib/autoconf.ml.new.in 3 Oct 2006 15:23:08 -0000 1.26 +@@ -52,5 +52,3 @@ + let zlib__uncompress_string2 s = Zlib.uncompress_string2 s + let zlib__compress_string s = Zlib.compress_string s + let zlib__gzip_string s = Zlib.gzip_string s +- +-@TYPE_FORMAT@ +Index: src/utils/lib/charset.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/charset.ml,v +retrieving revision 1.7 +retrieving revision 1.9 +diff -u -r1.7 -r1.9 +--- src/utils/lib/charset.ml 14 Sep 2006 17:40:29 -0000 1.7 ++++ src/utils/lib/charset.ml 8 Oct 2006 14:12:13 -0000 1.9 +@@ -172,7 +172,7 @@ + (**********************************************************************************) + + (* taken from camomile *) +-(* $Id: charset.ml,v 1.7 2006/09/14 17:40:29 spiralvoice Exp $ *) ++(* $Id: charset.ml,v 1.9 2006/10/08 14:12:13 spiralvoice Exp $ *) + (* Copyright 2002, 2003 Yamagata Yoriyuki. distributed with LGPL *) + + let utf8_look s i = +@@ -254,7 +254,7 @@ + (**********************************************************************************) + + (* taken from camomile *) +-(* $Id: charset.ml,v 1.7 2006/09/14 17:40:29 spiralvoice Exp $ *) ++(* $Id: charset.ml,v 1.9 2006/10/08 14:12:13 spiralvoice Exp $ *) + (* Copyright 2002, 2003 Yamagata Yoriyuki. distributed with LGPL *) + + let rec length_aux s c i = +@@ -281,7 +281,7 @@ + + + (* taken from camomile *) +-(* $Id: charset.ml,v 1.7 2006/09/14 17:40:29 spiralvoice Exp $ *) ++(* $Id: charset.ml,v 1.9 2006/10/08 14:12:13 spiralvoice Exp $ *) + (* Copyright 2002, 2003 Yamagata Yoriyuki. distributed with LGPL *) + + external uint_code : uchar -> int = "%identity" +@@ -1810,6 +1810,8 @@ + (* *) + (**********************************************************************************) + ++let conversion_enabled = ref true ++ + let convert ~from_charset ~to_charset s = + if s <> "" then begin + let t = charset_to_string to_charset in +@@ -1817,6 +1819,17 @@ + convert_string s t f + end else s + ++let safe_convert enc s = ++ match enc with ++ "" -> s ++ | enc -> ++ try ++ convert ++ ~from_charset: (charset_from_string enc) ++ ~to_charset: (charset_from_string "UTF-8") ++ s ++ with _ -> s ++ + (**********************************************************************************) + (* *) + (* slow_encode_from_utf8 *) +@@ -1898,7 +1911,7 @@ + (**********************************************************************************) + + let to_locale s = +- if s = "" ++ if s = "" || not !conversion_enabled + then s + else begin + let s = to_utf8 s in +Index: src/utils/lib/charset.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/charset.mli,v +retrieving revision 1.3 +retrieving revision 1.5 +diff -u -r1.3 -r1.5 +--- src/utils/lib/charset.mli 1 Aug 2005 20:09:13 -0000 1.3 ++++ src/utils/lib/charset.mli 8 Oct 2006 14:12:14 -0000 1.5 +@@ -141,6 +141,7 @@ + (** [convert ~from_charset ~to_charset s] + raise CharsetError if the string s is not entirely convertible. *) + val convert : from_charset : charset -> to_charset : charset -> string -> string ++val safe_convert: string -> string -> string + + (** [is_utf8 s] + returns TRUE if s is a valid UTF-8, otherwise returns FALSE. +@@ -171,3 +172,4 @@ + + val default_language : string + val locstr : string ++val conversion_enabled : bool ref +Index: src/utils/lib/gettext.ml4 +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/gettext.ml4,v +retrieving revision 1.8 +retrieving revision 1.9 +diff -u -r1.8 -r1.9 +--- src/utils/lib/gettext.ml4 24 Jul 2006 20:12:32 -0000 1.8 ++++ src/utils/lib/gettext.ml4 19 Nov 2006 23:04:59 -0000 1.9 +@@ -20,6 +20,14 @@ + open Printf2 + open Autoconf + ++let log_prefix = "[Gettext]" ++ ++let lprintf_nl fmt = ++ lprintf_nl2 log_prefix fmt ++ ++let lprintf_n fmt = ++ lprintf2 log_prefix fmt ++ + type expected_types = + Type_int + | Type_char +@@ -291,7 +299,7 @@ + !verified.(index) <- true; + true + end else begin +- lprintf "Bad format for %s\n" translated; ++ lprintf_nl "Bad format for %s\n" translated; + save_strings_file := true; + !translation.(index) <- no_translation; + false +@@ -359,7 +367,7 @@ + + save_strings_file := false) + with e -> +- lprintf "Gettext.save_strings: Error %s\n\n" ++ lprintf_nl "save_strings: Error %s" + (Printexc2.to_string e) + open Genlex2 + +@@ -404,7 +412,7 @@ + parse_file stream + with e -> + strings_file_error := true; +- lprintf "Gettext.set_strings_file: Exception %s in %s at pos %d\n" ++ lprintf_nl "set_strings_file: Exception %s in %s at pos %d" + (Printexc2.to_string e) filename (Stream.count s)) + with e -> + save_strings_file := true); +@@ -420,7 +428,7 @@ + let strings = Hashtbl.create 111 in + + let translate1 s0 s1 = +- lprintf "translate0 %s\n" s0; ++ lprintf_nl "translate0 %s" s0; + Hashtbl.add strings s0 s1 + in + +@@ -444,18 +452,18 @@ + parse_file stream + with e -> + strings_file_error := true; +- lprintf "Gettext.set_strings_file: Exception %s in %s at pos %d\n" ++ lprintf_nl "set_strings_file: Exception %s in %s at pos %d" + (Printexc2.to_string e) f1 (Stream.count s)) + with e -> + save_strings_file := true; +- lprintf "Gettext.set_strings_file: no message file found. Creating one\n"); ++ lprintf_nl "set_strings_file: no message file found. Creating one"); + + let translate2 s0 s1 = + try +- lprintf "translate2 %s\n" s0; ++ lprintf_nl "translate2 %s" s0; + let s0 = Hashtbl.find strings s0 in + translate "Former Translation" s0 s1 +- with _ -> lprintf "No translation for %s\n" s0 ++ with _ -> lprintf_nl "No translation for %s" s0 + in + + let rec parse_file = (parser +@@ -477,11 +485,11 @@ + parse_file stream + with e -> + strings_file_error := true; +- lprintf "Gettext.set_strings_file: Exception %s in %s at pos %d\n" ++ lprintf_nl "set_strings_file: Exception %s in %s at pos %d" + (Printexc2.to_string e) f2 (Stream.count s)) + with e -> + save_strings_file := true; +- lprintf "Gettext.set_strings_file: no message file found. Creating one\n" ++ lprintf_nl "set_strings_file: no message file found. Creating one" + + + with _ -> () +Index: src/utils/lib/int64ops.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/int64ops.ml,v +retrieving revision 1.3 +retrieving revision 1.4 +diff -u -r1.3 -r1.4 +--- src/utils/lib/int64ops.ml 15 Jul 2006 11:52:54 -0000 1.3 ++++ src/utils/lib/int64ops.ml 25 Oct 2006 11:14:02 -0000 1.4 +@@ -50,3 +50,12 @@ + let round_up64 x y = + ((Int64.pred (x ++ y)) // y) ** y + ++let int64_to_human_readable size = ++ if Int64.to_float size >= 1024. && Int64.to_float size < 1048576. then ++ (Printf.sprintf "%5.1f%s" (Int64.to_float size /. 1024.) ("kb") ) ++ else if size >= Int64.of_float 1048576. && Int64.to_float size < 1073741824. then ++ (Printf.sprintf "%5.1f%s" (Int64.to_float size /. 1048576.) ("mb") ) ++ else if size >= Int64.of_float 1073741824. then ++ (Printf.sprintf "%5.1f%s" (Int64.to_float size /. 1073741824.) ("gb") ) ++ else ++ (Printf.sprintf "%8s%s" (Int64.to_string size) ("b") ) +Index: src/utils/lib/misc.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/misc.ml,v +retrieving revision 1.6 +retrieving revision 1.7 +diff -u -r1.6 -r1.7 +--- src/utils/lib/misc.ml 3 Apr 2006 20:50:09 -0000 1.6 ++++ src/utils/lib/misc.ml 25 Oct 2006 11:12:38 -0000 1.7 +@@ -49,6 +49,10 @@ + let s = string_of_int num in + int_of_string ("0b" ^ s) + ++let percentage_of_ints v percent = ++ int_of_float ( ++ (float_of_int v *. float_of_int percent /. 100.0) +. 0.5) ++ + let zip_extract_entry ifile e = + if e.Zip.is_directory then begin + try +Index: src/utils/lib/options.ml4 +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/options.ml4,v +retrieving revision 1.21 +retrieving revision 1.22 +diff -u -r1.21 -r1.22 +--- src/utils/lib/options.ml4 31 May 2006 22:26:05 -0000 1.21 ++++ src/utils/lib/options.ml4 21 Oct 2006 19:35:54 -0000 1.22 +@@ -514,6 +514,13 @@ + let int_to_value i = IntValue (Int64.of_int i) + let int64_to_value i = IntValue i + ++let percent_to_value i = IntValue (Int64.of_int i) ++let value_to_percent v = ++ match Int64.to_int (value_to_int64 v) with ++ v when v < 0 -> 0 ++ | v when v > 100 -> 100 ++ | v -> v ++ + (* The Pervasives version is too restrictive *) + let bool_of_string s = + match String.lowercase s with +@@ -729,6 +736,7 @@ + + let int_option = define_option_class "Int" value_to_int int_to_value + let int64_option = define_option_class "Int64" value_to_int64 int64_to_value ++let percent_option = define_option_class "Int" value_to_percent percent_to_value + + + let bool_option = define_option_class "Bool" value_to_bool bool_to_value +Index: src/utils/lib/options.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/lib/options.mli,v +retrieving revision 1.9 +retrieving revision 1.10 +diff -u -r1.9 -r1.10 +--- src/utils/lib/options.mli 31 May 2006 22:26:05 -0000 1.9 ++++ src/utils/lib/options.mli 21 Oct 2006 19:35:54 -0000 1.10 +@@ -90,6 +90,7 @@ + val font_option : string option_class + val int_option : int option_class + val int64_option : int64 option_class ++val percent_option : int option_class + val bool_option : bool option_class + val float_option : float option_class + val path_option : string list option_class +@@ -157,6 +158,8 @@ + val int_to_value : int -> option_value + val value_to_int64 : option_value -> int64 + val int64_to_value : int64 -> option_value ++val value_to_percent : option_value -> int ++val percent_to_value : int -> option_value + val bool_of_string : string -> bool + val value_to_bool : option_value -> bool + val bool_to_value : bool -> option_value +Index: src/utils/net/basicSocket.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/basicSocket.ml,v +retrieving revision 1.30 +retrieving revision 1.31 +diff -u -r1.30 -r1.31 +--- src/utils/net/basicSocket.ml 17 May 2006 08:52:44 -0000 1.30 ++++ src/utils/net/basicSocket.ml 21 Nov 2006 22:34:34 -0000 1.31 +@@ -204,15 +204,9 @@ + let minf (x: float) (y: float) = + if x > y then y else x + +-let mini (x: int) (y: int) = +- if x > y then y else x +- + let maxf (x: float) (y: float) = + if x < y then y else x + +-let maxi (x: int) (y: int) = +- if x < y then y else x +- + (*************************************************************************) + (* *) + (* Some functions *) +Index: src/utils/net/basicSocket.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/basicSocket.mli,v +retrieving revision 1.15 +retrieving revision 1.16 +diff -u -r1.15 -r1.16 +--- src/utils/net/basicSocket.mli 28 Oct 2005 08:21:49 -0000 1.15 ++++ src/utils/net/basicSocket.mli 21 Nov 2006 22:34:34 -0000 1.16 +@@ -75,9 +75,7 @@ + + val stats : Buffer.t -> t -> unit + +-val mini : int -> int -> int + val minf : float -> float -> float +-val maxi : int -> int -> int + val maxf : float -> float -> float + + val set_allow_write : t -> bool ref -> unit +Index: src/utils/net/http_client.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/http_client.ml,v +retrieving revision 1.33 +retrieving revision 1.35 +diff -u -r1.33 -r1.35 +--- src/utils/net/http_client.ml 30 May 2006 10:54:14 -0000 1.33 ++++ src/utils/net/http_client.ml 21 Nov 2006 22:34:34 -0000 1.35 +@@ -62,7 +62,7 @@ + lprintf_nl2 log_prefix fmt + + let basic_request = { +- req_url = Url.of_string "http://www.mldonkey.net/"; ++ req_url = Url.of_string "http://www.mldonkey.org/"; + req_referer = None; + req_save_to_file_time = 0.; + req_request = GET; +@@ -170,7 +170,7 @@ + let b = TcpBufferedSocket.buf sock in + let end_pos = b.pos + b.len in + let new_pos = end_pos - nread in +- let new_pos = maxi 0 (new_pos - 1) in ++ let new_pos = max 0 (new_pos - 1) in + (* + lprintf "received [%s]" (String.escaped + (String.sub b.buf new_pos nread)); +@@ -380,7 +380,7 @@ + if nread > 0 then begin + let left = + if maxlen >= 0 then +- mini (maxlen - !file_size) nread ++ min (maxlen - !file_size) nread + else nread + in + Buffer.add_string file_buf (String.sub buf.buf buf.pos left); +@@ -452,7 +452,7 @@ + if nread > 0 then begin + let left = + if maxlen >= 0 then +- mini (maxlen - !file_size) nread ++ min (maxlen - !file_size) nread + else nread + in + Buffer.add_string file_buf (String.sub buf.buf buf.pos left); +Index: src/utils/net/http_server.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/http_server.ml,v +retrieving revision 1.33 +retrieving revision 1.36 +diff -u -r1.33 -r1.36 +--- src/utils/net/http_server.ml 27 Jul 2006 21:45:06 -0000 1.33 ++++ src/utils/net/http_server.ml 21 Nov 2006 22:34:34 -0000 1.36 +@@ -95,6 +95,7 @@ + type error_reason = + | Blocked + | Not_allowed ++| Url_not_found of string + + type header = + Unknown of string * string +@@ -234,11 +235,13 @@ + | "403" -> "Forbidden", + (match reason with + Some Not_allowed -> Printf.sprintf +-"<p>Connection from %s rejected (see downloads.ini, <a href=\"http://mldonkey.sourceforge.net/Allowed_ips\">allowed_ips</a>)</p>\n" ++"<p>Connection from %s rejected (see downloads.ini, <a href=\"http://mldonkey.sourceforge.net/Allowed_ips\">allowed_ips</a>)</p>" + from_ip + | Some Blocked -> Printf.sprintf "IP %s is blocked, its part of the used IP blocklist " from_ip +- | None -> "") +- | _ -> Printf.sprintf "Unknown %s" code, "" ++ | _ -> "") ++ | "404" -> "Not found", Printf.sprintf "The requested URL %swas not found on this server." ++ (match reason with Some (Url_not_found url) -> url ^ " " | _ -> "") ++ | _ -> Printf.sprintf "Unknown error %s" code, "" + in + let reject_message = Printf.sprintf + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html> +@@ -250,7 +253,8 @@ + Printf.sprintf + "HTTP/1.1 %s %s\nMLDonkey/%s\nConnection: close + Content-Type: text/html; charset=iso-8859-1\nContent-length: %d\r\n" +- code error_text Autoconf.current_version (String.length reject_message), reject_message ++ code error_text Autoconf.current_version (String.length reject_message), reject_message, ++ Printf.sprintf "%s %s" code error_text + + let parse_head sock s = + let h = split_head s in +@@ -681,13 +685,6 @@ + at_write_end buf.fd_task shutdown + *) + +-let need_auth r name = +- r.reply_head <- "401 Unauthorized"; +- r.reply_headers <- [ +- "Connection", "close"; +- "WWW-Authenticate", Printf.sprintf "Basic realm=\"%s\"" name +- ] +- + (* + let simple_give_auth psread pswrite request = + try +@@ -788,7 +785,7 @@ + let b = TcpBufferedSocket.buf sock in + let end_pos = b.pos + b.len in + let new_pos = end_pos - nread in +- let new_pos = maxi 0 (new_pos - 1) in ++ let new_pos = max 0 (new_pos - 1) in + (* lprintf "received [%s]\n" (String.escaped + (String.sub b.buf new_pos nread)); - log commented out *) + let rec iter i = +@@ -860,7 +857,7 @@ + (if ip_is_blocked from_ip then "IP is blocked" else "see allowed_ips setting"); + let token = create_token unlimited_connection_manager in + let sock = TcpBufferedSocket.create_simple token "http connection" s in +- let s1,s2 = error_page "403" ++ let s1,s2,_ = error_page "403" + (Ip.to_string from_ip) + (string_of_int from_port) + (Ip.to_string (TcpBufferedSocket.my_ip sock)) +Index: src/utils/net/http_server.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/http_server.mli,v +retrieving revision 1.9 +retrieving revision 1.10 +diff -u -r1.9 -r1.10 +--- src/utils/net/http_server.mli 27 Jul 2006 21:45:06 -0000 1.9 ++++ src/utils/net/http_server.mli 21 Oct 2006 19:35:09 -0000 1.10 +@@ -17,7 +17,7 @@ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *) + type auth = No_auth | Read_auth | Write_auth +-type error_reason = Blocked | Not_allowed ++type error_reason = Blocked | Not_allowed | Url_not_found of string + and header = + Unknown of string * string + | Referer of Url.url +@@ -67,7 +67,6 @@ + } + + val create : config -> TcpServerSocket.t +-val need_auth : request -> string -> unit + val html_escaped : string -> string + val html_real_escaped : string -> string + +@@ -79,4 +78,4 @@ + + val request_range : request -> int64 * (int64 option) + val parse_range : string -> int64 * int64 option * int64 option +-val error_page : string -> string -> string -> string -> string -> error_reason option -> string * string ++val error_page : string -> string -> string -> string -> string -> error_reason option -> string * string * string +Index: src/utils/net/ip_set.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/ip_set.ml,v +retrieving revision 1.29 +retrieving revision 1.31 +diff -u -r1.29 -r1.31 +--- src/utils/net/ip_set.ml 27 Jul 2006 21:45:06 -0000 1.29 ++++ src/utils/net/ip_set.ml 20 Nov 2006 22:34:40 -0000 1.31 +@@ -259,14 +259,14 @@ + find_in_zip q in + find_in_zip filenames_list + with e -> +- lprintf_nl "Exception %s while extracting %s from %s" ++ lprintf_nl "Exception %s while extracting %s from %s" + (Printexc2.to_string e) + (String.concat "/" filenames_list) + filename; +- lprintf_nl "One of the mentioned files has to be present in the zip file"; ++ lprintf_nl "One of the mentioned files has to be present in the zip file"; + bl_empty) + with e -> +- lprintf_nl "Exception %s while opening %s" ++ lprintf_nl "Exception %s while opening %s" + (Printexc2.to_string e) + filename; + bl_empty) +@@ -281,12 +281,12 @@ + let s = Misc.archive_extract filename filetype in + load_merge bl_empty s true + with e -> +- lprintf_nl "Exception %s while extracting from %s" ++ lprintf_nl "Exception %s while extracting from %s" + (Printexc2.to_string e) filename; + bl_empty) + | ".tar.bz2" | ".p2p.tar.bz2" | ".dat.tar.bz2" + | ".tar.gz" | ".p2p.tar.gz" | ".dat.tar.gz" -> +- lprintf_nl "tar files are not (yet) supported, please untar %s" filename; ++ lprintf_nl "tar files are not (yet) supported, please untar %s" filename; + bl_empty + | _ -> load_merge bl_empty filename false + else +@@ -296,6 +296,7 @@ + end + + let of_list l = ++ bl_optimize ( + List.fold_left (fun acc r -> + let range = + match r with +@@ -314,7 +315,7 @@ + blocking_end = Ip.broadcast_address ip mask } + in + add_range acc range +- ) BL_Empty l ++ ) BL_Empty l) + + let print_list buf bl = + let rec print_list_aux bl = +Index: src/utils/net/tcpBufferedSocket.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/tcpBufferedSocket.ml,v +retrieving revision 1.43 +retrieving revision 1.45 +diff -u -r1.43 -r1.45 +--- src/utils/net/tcpBufferedSocket.ml 3 Apr 2006 20:50:09 -0000 1.43 ++++ src/utils/net/tcpBufferedSocket.ml 21 Nov 2006 22:34:34 -0000 1.45 +@@ -319,7 +319,7 @@ + forecast_download t 0 + + let best_packet_size nbytes = +- let nbytes = maxi nbytes !minimal_packet_size in ++ let nbytes = max nbytes !minimal_packet_size in + let nip_packets = 1 + nbytes / !mtu_packet_size in + let headers = nip_packets * !ip_packet_size in + let nframes = 1 + (nbytes + headers) / packet_frame_size in +@@ -411,7 +411,7 @@ + lprintf "[TCP_BS]: BUFFER OVERFLOW %d+%d> %d " b.len len b.max_buf_size ; + + lprintf "MESSAGE: ["; +- for i = pos1 to pos1 + (mini len 20) - 1 do ++ for i = pos1 to pos1 + (min len 20) - 1 do + lprintf "(%d)" (int_of_char s.[i]); + done; + if len > 20 then lprintf "..."; +@@ -422,7 +422,7 @@ + socket !!! *) + end + else +- let new_len = mini (maxi (2 * max_len) (b.len + len)) b.max_buf_size in ++ let new_len = min (max (2 * max_len) (b.len + len)) b.max_buf_size in + (* if t.monitored then + (lprintf "Allocate new for %d\n" len; ); *) + let new_buf = String.create new_len in +@@ -631,8 +631,8 @@ + buf_len - b.len + ) + else +- let new_len = mini +- (maxi ++ let new_len = min ++ (max + (2 * buf_len) (b.len + min_read_size)) b.max_buf_size + in + let new_buf = String.create new_len in +@@ -645,7 +645,7 @@ + in + b.buf, b.pos+b.len, can_write_in_buffer + in +- let can_read = mini max_len buffer_len in ++ let can_read = min max_len buffer_len in + if can_read > 0 then + let old_len = b.len in + let nread = try +@@ -684,8 +684,8 @@ + end else + b.len <- b.len + nread; + (* lprintf " %d\n" nread; *) +- b.min_buf_size <- mini b.max_buf_size ( +- maxi (nread + nread / 2) min_read_size); ++ b.min_buf_size <- min b.max_buf_size ( ++ max (nread + nread / 2) min_read_size); + + (* + if nread = can_read then begin +@@ -804,12 +804,9 @@ + | Some bc -> + if bc.total_bytes = 0 then + can_write_handler t sock t.wbuf.len +- else begin +-(* lprintf "DELAYED\n"; *) +- if bc.remaining_bytes > 0 then begin +- bc.connections <- t :: bc.connections; +- bc.nconnections <- t.write_power + bc.nconnections +- end ++ else begin ++ bc.connections <- t :: bc.connections; ++ bc.nconnections <- t.write_power + bc.nconnections + end + end + +@@ -1680,9 +1677,9 @@ + + List.iter (fun t -> + if bc.remaining_bytes > 0 then +- let nconnections = maxi bc.nconnections 1 in +- let can_read = maxi 1 (bc.remaining_bytes / nconnections) in +- let can_read = maxi !ip_packet_size (can_read * t.read_power) in ++ let nconnections = max bc.nconnections 1 in ++ let can_read = max 1 (bc.remaining_bytes / nconnections) in ++ let can_read = max !ip_packet_size (can_read * t.read_power) in + (try + (* lprintf "allow to read %d\n" can_read; *) + can_read_handler t t.sock_in can_read +@@ -1731,14 +1728,14 @@ + end; + List.iter (fun t -> + if bc.remaining_bytes > 0 then +- let nconnections = maxi bc.nconnections 1 in +- let can_write = maxi 1 (bc.remaining_bytes / nconnections) in ++ let nconnections = max bc.nconnections 1 in ++ let can_write = max 1 (bc.remaining_bytes / nconnections) in + let can_write = best_packet_size (can_write * t.write_power) + in + let old_nwrite = t.nwrite in + (try + (* lprintf "WRITE\n"; *) +- can_write_handler t t.sock_out (mini can_write t.wbuf.len) ++ can_write_handler t t.sock_out (min can_write t.wbuf.len) + with _ -> ()); + bc.remaining_bytes <- bc.remaining_bytes - + t.nwrite + old_nwrite; +@@ -1749,7 +1746,7 @@ + t.name (sock_num t.sock_out) (remaining_to_write t) + end + ) bc.connections; +-(* if bc.remaining_bytes > 0 then bc.allow_io := false; *) ++(* if bc.remaining_bytes > 0 then bc.allow_io := false; *) + end; + if !verbose_bandwidth > 2 then begin + +Index: src/utils/net/tcpBufferedSocket.mli +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/tcpBufferedSocket.mli,v +retrieving revision 1.15 +retrieving revision 1.16 +diff -u -r1.15 -r1.16 +--- src/utils/net/tcpBufferedSocket.mli 28 Jun 2005 22:45:57 -0000 1.15 ++++ src/utils/net/tcpBufferedSocket.mli 31 Oct 2006 15:41:55 -0000 1.16 +@@ -79,6 +79,9 @@ + val set_max_output_buffer : t -> int -> unit + val can_write : t -> bool + val can_write_len : t -> int -> bool ++val register_download : t -> int -> unit ++val register_upload : t -> int -> unit ++val register_bytes : bandwidth_controler option -> int -> unit + + val set_monitored : t -> bool -> unit + val monitored : t -> bool +Index: src/utils/net/tcpClientSocket.ml +=================================================================== +RCS file: src/utils/net/tcpClientSocket.ml +diff -N src/utils/net/tcpClientSocket.ml +--- src/utils/net/tcpClientSocket.ml 6 Dec 2005 20:26:40 -0000 1.12 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,789 +0,0 @@ +-(* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *) +-(* +- This file is part of mldonkey. +- +- mldonkey 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. +- +- mldonkey 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 mldonkey; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-*) +- +-open Printf2 +-open BasicSocket +- +-(* let _ = Unix2.init () *) +- +-type event = +- WRITE_DONE +-| CAN_REFILL +-| BUFFER_OVERFLOW +-| READ_DONE of int +-| BASIC_EVENT of BasicSocket.event +- +-type buf = { +- mutable buf : string; +- mutable pos : int; +- mutable len : int; +- mutable max_buf_size:int; +- } +- +-type t = { +- mutable sock : BasicSocket.t; +- mutable rbuf : buf; +- mutable wfifo : string Fifo.t; +- mutable wlen : int; +- mutable wfbuf : buf; +- mutable event_handler : handler; +- mutable error : string; +- mutable nread : int; +- mutable nwrite : int; +- mutable monitored : bool; +- +- mutable read_control : bandwidth_controler option; +- mutable write_control : bandwidth_controler option; +- mutable write_power : int; +- mutable read_power : int; +- } +- +-and handler = t -> event -> unit +- +-and bandwidth_controler = { +- mutable remaining_bytes : int; +- mutable total_bytes : int; +- mutable nconnections : int; +- mutable connections : t list; +- allow_io : bool ref; +- mutable last_remaining : int; +- mutable moved_bytes : int64; +- } +- +- +-let tcp_uploaded_bytes = ref Int64.zero +-let tcp_downloaded_bytes = ref Int64.zero +- +-let nread t = t.nread +- +-let min_buffer_read = 500 +-let min_read_size = min_buffer_read - 100 +- +-let old_strings_size = 20 +-let old_strings = Array.create old_strings_size "" +-let old_strings_len = ref 0 +- +-let new_string () = +- if !old_strings_len > 0 then begin +- decr old_strings_len; +- let s = old_strings.(!old_strings_len) in +- old_strings.(!old_strings_len) <- ""; +- s +- end else +- String.create min_buffer_read +- +-let delete_string s = +- if !old_strings_len < old_strings_size && +- String.length s = min_buffer_read then begin +- old_strings.(!old_strings_len) <- s; +- incr old_strings_len; +- end +- +-let close t s = +-(* +- if t.monitored then begin +- lprintf "close with %s %s" t.error s; lprint_newline (); +-end; +-*) +- begin +- try +- delete_string t.rbuf.buf; +- t.rbuf.buf <- ""; +- t.wfbuf.buf <- ""; +- close t.sock (Printf.sprintf "%s after %d/%d" s t.nread t.nwrite) +- with e -> +- lprintf "Exception %s in TcpBufferedSocket.close\n" +- (Printexc2.to_string e); +- raise e +- end +- +-let shutdown t s = +- (* +- if t.monitored then begin +- lprintf "shutdown"; lprint_newline (); +-end; +- *) +- (try BasicSocket.shutdown t.sock s with e -> +- lprintf "exception %s in shutdown\n" (Printexc2.to_string e)); +- (try close t s with e -> +- lprintf "exception %s in shutdown\n" (Printexc2.to_string e)) +- +-let buf_create max = +- { +- buf = ""; +- pos = 0; +- len = 0; +- max_buf_size = max; +- } +- +-let error t = t.error +- +- +-let set_reader t f = +- let old_handler = t.event_handler in +- let handler t ev = +-(* if t.monitored then (lprintf "set_reader handler"; lprint_newline ()); *) +- match ev with +- READ_DONE nread -> +-(* lprintf "READ_DONE %d" nread; lprint_newline (); *) +- f t nread +- |_ -> old_handler t ev +- in +- t.event_handler <- handler +- +-let set_closer t f = +- let old_handler = t.event_handler in +- let handler t ev = +-(* if t.monitored then (lprintf "set_closer handler"; lprint_newline ()); *) +- match ev with +- BASIC_EVENT (CLOSED s) -> +-(* lprintf "READ_DONE %d" nread; lprint_newline (); *) +- f t s +- |_ -> old_handler t ev +- in +- t.event_handler <- handler +- +- +-let buf_used t nused = +- let b = t.rbuf in +- if nused = b.len then +- ( b.len <- 0; +- b.pos <- 0; +- delete_string b.buf; +- b.buf <- ""; +- ) +- else +- (b.len <- b.len - nused; b.pos <- b.pos + nused) +- +-let set_handler t event handler = +- let old_handler = t.event_handler in +- let handler t ev = +-(* if t.monitored then (lprintf "set_handler handler"; lprint_newline ()); *) +- if ev = event then +- handler t +- else +- old_handler t ev +- in +- t.event_handler <- handler +- +-let set_refill t f = +- set_handler t CAN_REFILL f; +- if t.wlen = 0 then (try f t with _ -> ()) +- +-let buf t = t.rbuf +-let sock t = t.sock +- +-let closed t = closed t.sock +- +-let buf_add t b s pos1 len = +- let curpos = b.pos + b.len in +- let max_len = +- if b.buf = "" then +- begin +- b.buf <- new_string (); +- min_buffer_read +- end else +- String.length b.buf in +- if max_len - curpos < len then (* resize before blit *) +- if b.len + len < max_len then (* just move to 0 *) +- begin +- String.blit b.buf b.pos b.buf 0 b.len; +- String.blit s pos1 b.buf b.len len; +- b.len <- b.len + len; +- b.pos <- 0; +- end +- else +- if b.len + len > b.max_buf_size then begin +- lprintf "BUFFER OVERFLOW %d+%d> %d\n" b.len len b.max_buf_size ; +- +- lprintf "MESSAGE ["; +- for i = pos1 to pos1 + (mini len 20) - 1 do +- lprintf "(%d)" (int_of_char s.[i]); +- done; +- if len > 20 then lprintf "..."; +- lprintf "]\n"; +- +- t.event_handler t BUFFER_OVERFLOW; +- end +- else +- let new_len = mini (maxi (2 * max_len) (b.len + len)) b.max_buf_size in +-(* if t.monitored then +- (lprintf "Allocate new for %d" len; lprint_newline ()); *) +- let new_buf = String.create new_len in +- String.blit b.buf b.pos new_buf 0 b.len; +- String.blit s pos1 new_buf b.len len; +- b.len <- b.len + len; +- b.pos <- 0; +- if max_len = min_buffer_read then delete_string b.buf; +-(* if t.monitored then +- (lprintf "new buffer allocated"; lprint_newline ()); *) +- b.buf <- new_buf +- else begin +- String.blit s pos1 b.buf curpos len; +- b.len <- b.len + len +- end +- +-let write t s = () +-let write_uniq t s = () +- +- (* +-let write t s pos1 len = +-(* lprintf "want_write %d" len; lprint_newline (); *) +- if len > 0 && not (closed t) then +- let pos2 = pos1 + len in +- let b = t.wbuf in +- let pos1 = +- if b.len = 0 && (match t.write_control with +- None -> +-(* lprintf "NO CONTROL"; lprint_newline (); *) +- true +- | Some bc -> +-(* lprintf "LIMIT %d" bc.total_bytes; lprint_newline (); *) +- bc.total_bytes = 0) +- then +- try +-(* lprintf "try write %d" len; lprint_newline (); *) +- let fd = fd t.sock in +- let nw = Unix.write fd s pos1 len in +-(* if t.monitored then begin +- lprintf "write: direct written %d" nw; lprint_newline (); +-end; *) +- tcp_uploaded_bytes := !tcp_uploaded_bytes ++ (Int64.of_int nw); +- (match t.write_control with +- None -> () +- | Some bc -> +- bc.moved_bytes <- bc.moved_bytes ++ (Int64.of_int nw)); +- t.nwrite <- t.nwrite + nw; +- if nw = 0 then (close t "closed on write"; pos2) else +- pos1 + nw +- with +- Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.EAGAIN | Unix.ENOTCONN), _, _) -> pos1 +- | e -> +- t.error <- Printf.sprintf "Write Error: %s" (Printexc2.to_string e); +- close t t.error; +- +-(* lprintf "exce %s in read" (Printexc2.to_string e); lprint_newline (); *) +- raise e +- +- else pos1 +- in +- if pos2 > pos1 then +- let sock = t.sock in +- must_write sock true; +- buf_add t b s pos1 (pos2 - pos1) +- +-let write_string t s = write t s 0 (String.length s) +- *) +- +-let dummy_sock = Obj.magic 0 +- +-let exn_exit = Exit +- +-let can_read_handler t sock max_len = +- let b = t.rbuf in +- let curpos = b.pos + b.len in +- let can_read = +- if b.buf = "" then begin +- b.buf <- new_string (); +- min_buffer_read +- end +- else +- if max_len - curpos < min_read_size then +- if b.len + min_read_size > b.max_buf_size then +- ( +- t.event_handler t BUFFER_OVERFLOW; +- lprintf "[OVERFLOW] in %s" (info sock); +- close t "buffer overflow"; +- raise exn_exit; +- 0 +- ) +- else +- if b.len + min_read_size < max_len then +- ( +- String.blit b.buf b.pos b.buf 0 b.len; +- b.pos <- 0; +- max_len - b.len +- ) +- else +- let new_len = mini +- (maxi +- (2 * max_len) (b.len + min_read_size)) b.max_buf_size +- in +- let new_buf = String.create new_len in +- String.blit b.buf b.pos new_buf 0 b.len; +- b.pos <- 0; +- b.buf <- new_buf; +- new_len - b.len +- else +- max_len - curpos +- in +- let can_read = mini max_len can_read in +- if can_read > 0 then +- let nread = try +-(* lprintf "try read %d" can_read; lprint_newline ();*) +- Unix.read (fd sock) b.buf (b.pos + b.len) can_read +- with +- Unix.Unix_error((Unix.EWOULDBLOCK | Unix.EAGAIN), _,_) as e -> raise e +- | e -> +- t.error <- Printf.sprintf "Can Read Error: %s" (Printexc2.to_string e); +- close t t.error; +- +-(* lprintf "exce %s in read" (Printexc2.to_string e); lprint_newline (); *) +- raise e +- +- in +- tcp_downloaded_bytes := !tcp_downloaded_bytes ++ (Int64.of_int nread); +- (match t.read_control with +- None -> () | Some bc -> +- bc.moved_bytes <- bc.moved_bytes ++ (Int64.of_int nread)); +- +- t.nread <- t.nread + nread; +- if nread = 0 then begin +- close t "closed on read"; +- end else begin +- let curpos = b.pos in +- b.len <- b.len + nread; +- try +-(* if t.monitored then +- (lprintf "event handler READ DONE"; lprint_newline ()); *) +- t.event_handler t (READ_DONE nread); +- with +- | e -> +-(* if t.monitored then +- (lprintf "Exception in READ DONE"; lprint_newline ()); *) +- t.error <- Printf.sprintf "READ_DONE Error: %s" (Printexc2.to_string e); +- close t t.error; +- +-(* lprintf "exce %s in read" (Printexc2.to_string e); lprint_newline (); *) +- raise e +- end +- +-let can_write_handler t sock max_len = +- (* +-(* if t.monitored then ( +- lprintf "CAN_WRITE (%d)" t.wbuf.len; lprint_newline (); +- ); *) +- let b = t.wbuf in +- if b.len > 0 then +- begin +- try +-(* lprintf "try write %d/%d" max_len t.wbuf.len; lprint_newline (); *) +- let fd = fd sock in +- let nw = Unix.write fd b.buf b.pos max_len in +- +-(* if t.monitored then +-(lprintf "written %d" nw; lprint_newline ()); *) +- tcp_uploaded_bytes := !tcp_uploaded_bytes ++ (Int64.of_int nw); +- (match t.write_control with +- None -> () +- | Some bc -> +- bc.moved_bytes <- bc.moved_bytes ++ (Int64.of_int nw)); +- t.nwrite <- t.nwrite + nw; +- b.len <- b.len - nw; +- b.pos <- b.pos + nw; +- if nw = 0 then close t "closed on write" else +- if b.len = 0 then begin +- b.pos <- 0; +- delete_string b.buf; +- b.buf <- ""; +- end +- with +- Unix.Unix_error((Unix.EWOULDBLOCK | Unix.EAGAIN), _,_) as e -> raise e +- | e -> +- t.error <- Printf.sprintf "Can Write Error: %s" (Printexc2.to_string e); +- close t t.error; +- +-(* lprintf "exce %s in read" (Printexc2.to_string e); lprint_newline (); *) +- raise e +- +- end; +- if not (closed t) then begin +- t.event_handler t CAN_REFILL; +- if b.len = 0 then begin +- delete_string b.buf; +- b.pos <- 0; +- must_write t.sock false; +- t.event_handler t WRITE_DONE +- end +- end +-*) +- () +- +-let remaining_to_write t = t.wlen +- +-let tcp_handler t sock event = +- match event with +- | CAN_READ -> +-(* lprintf "CAN_READ"; lprint_newline (); *) +- begin +- match t.read_control with +- None -> +- can_read_handler t sock (String.length t.rbuf.buf) +- | Some bc -> +- if bc.total_bytes = 0 then +- can_read_handler t sock (String.length t.rbuf.buf) +- else begin +-(* lprintf "DELAYED"; lprint_newline (); *) +- if bc.remaining_bytes > 0 then +- begin +- bc.connections <- t :: bc.connections; +- bc.nconnections <- t.read_power + bc.nconnections +- end +- end +- end +- | CAN_WRITE -> +-(* lprintf "CAN_WRITE"; lprint_newline (); *) +- begin +- match t.write_control with +- None -> +- can_write_handler t sock t.wlen +- | Some bc -> +- if bc.total_bytes = 0 then +- can_write_handler t sock t.wlen +- else begin +-(* lprintf "DELAYED"; lprint_newline (); *) +- if bc.remaining_bytes > 0 then begin +- bc.connections <- t :: bc.connections; +- bc.nconnections <- t.write_power + bc.nconnections +- end +- end +- end +- | _ -> t.event_handler t (BASIC_EVENT event) +- +-let read_bandwidth_controlers = ref [] +-let write_bandwidth_controlers = ref [] +- +-let _ = +- Heap.add_memstat "tcpClientSocket" (fun level buf -> +- Printf.bprintf buf " read_bandwidth_controlers: %d\n" (List.length !read_bandwidth_controlers); +- Printf.bprintf buf " write_bandwidth_controlers: %d\n" (List.length !write_bandwidth_controlers); +- ) +- +-let create_read_bandwidth_controler rate = +- let bc = { +- remaining_bytes = rate; +- total_bytes = rate; +- nconnections = 0; +- connections = []; +- allow_io = ref true; +- last_remaining = 0; +- moved_bytes = Int64.zero; +- } in +- read_bandwidth_controlers := bc :: !read_bandwidth_controlers; +- bc +- +-let create_write_bandwidth_controler rate = +- let bc = { +- remaining_bytes = rate; +- total_bytes = rate; +- nconnections = 0; +- connections = []; +- allow_io = ref true; +- last_remaining = 0; +- moved_bytes = Int64.zero; +- } in +- write_bandwidth_controlers := bc :: !write_bandwidth_controlers; +- bc +- +-let change_rate bc rate = +- bc.total_bytes <- rate +- +-let bandwidth_controler t sock = +- (match t.read_control with +- None -> () +- | Some bc -> +- must_read sock (bc.total_bytes = 0 || bc.remaining_bytes > 0)); +- (match t.write_control with +- None -> () +- | Some bc -> +- must_write sock ((bc.total_bytes = 0 || bc.remaining_bytes > 0) +- && t.wlen > 0)) +- +-let set_read_controler t bc = +- t.read_control <- Some bc; +-(* set_before_select t.sock (bandwidth_controler t); *) +- set_allow_read t.sock bc.allow_io; +- bandwidth_controler t t.sock +- +-let set_write_controler t bc = +- t.write_control <- Some bc; +-(* set_before_select t.sock (bandwidth_controler t); *) +- set_allow_write t.sock bc.allow_io; +- bandwidth_controler t t.sock +- +-let max_buffer_size = ref 1000000 +- +-let dump_socket t buf = +- print_socket buf t.sock; +- Printf.bprintf buf "rbuf: %d/%d wbuf: %d/%d\n" t.rbuf.len (String.length t.rbuf.buf) t.wlen (String.length t.wfbuf.buf) +- +-let create name fd handler = +- if !debug then begin +- lprintf_nl "[fd %d %s]\n" (Obj.magic fd) name; +- end; +- MlUnix.set_close_on_exec fd; +- let t = { +- sock = dummy_sock; +- rbuf = buf_create !max_buffer_size; +- wfbuf = buf_create !max_buffer_size; +- event_handler = handler; +- error = ""; +- nread = 0; +- nwrite = 0; +- monitored = false; +- read_control = None; +- write_control = None; +- write_power = 1; +- read_power = 1; +- wlen = 0; +- wfifo = Fifo.create (); +- } in +- let sock = BasicSocket.create name fd (tcp_handler t) in +- let name = (fun () -> +- Printf.sprintf "%s (nread: %d nwritten: %d) [U %s,D %s]" name t.nread t.nwrite +- (string_of_bool (t.read_control <> None)) +- (string_of_bool (t.write_control <> None)); +- ; +- ) in +- set_printer sock name; +- set_dump_info sock (dump_socket t); +- t.sock <- sock; +- t +- +-let create_blocking name fd handler = +- MlUnix.set_close_on_exec fd; +- let t = { +- sock = dummy_sock; +- rbuf = buf_create !max_buffer_size; +- wfbuf = buf_create !max_buffer_size; +- event_handler = handler; +- error = ""; +- nread = 0; +- nwrite = 0; +- monitored = false; +- read_control = None; +- write_control = None; +- write_power = 1; +- read_power = 1; +- wlen = 0; +- wfifo = Fifo.create (); +- } in +- let sock = create_blocking name fd (tcp_handler t) in +- t.sock <- sock; +- set_dump_info sock (dump_socket t); +- t +- +-let create_simple name fd = +- create name fd (fun _ _ -> ()) +- +-let connect name host port handler = +- try +-(* lprintf "CONNECT tcpClientSocket\n"; *) +- let s = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in +- let t = create name s handler in +- must_write (sock t) true; +- try +- Unix.connect s (Unix.ADDR_INET(host,port)); +- t +- with +- Unix.Unix_error((Unix.EINPROGRESS|Unix.EINTR),_,_) -> t +- | Unix.Unix_error (Unix.ENETUNREACH,_,_) as e -> +- close t "connect failed"; +- raise e +- | e -> +- lprintf "For host %s port %d\n" +- (Unix.string_of_inet_addr host) port; +- close t "connect failed"; +- raise e +- with e -> +- lprintf "+++ Exception BEFORE CONNECT %s\n" (Printexc2.to_string e); +- raise e +- +- +- +-let set_max_write_buffer t len = +- t.wfbuf.max_buf_size <- len; +- t.rbuf.max_buf_size <- len +- +-let can_write t = +- t.wlen = 0 +- +-let can_write_len t len = +-(* lprintf "CAN WRITE %d > %d + %d" t.wbuf.max_buf_size t.wbuf.len len; lprint_newline (); *) +- t.wfbuf.max_buf_size > t.wlen + len +- +-let close_after_write t = +- if t.wlen = 0 then begin +- shutdown t "close after write" +- end +- else +- set_handler t WRITE_DONE (fun t -> +- shutdown t "close after write") +- +-let set_monitored t = +- t.monitored <- true +- +- +-let _ = +- add_infinite_timer 1.0 (fun timer -> +- List.iter (fun bc -> +- bc.last_remaining <- bc.remaining_bytes; +- bc.remaining_bytes <- bc.total_bytes; +- if bc.remaining_bytes > 0 then bc.allow_io := true +-(* lprintf "READ remaining_bytes: %d" bc.remaining_bytes; *) +- ) !read_bandwidth_controlers; +- List.iter (fun bc -> +- bc.last_remaining <- bc.remaining_bytes; +- bc.remaining_bytes <- bc.total_bytes; +- if bc.remaining_bytes > 0 then bc.allow_io := true; +- (* +- lprintf "WRITE remaining_bytes: %d" bc.remaining_bytes; +- lprint_newline (); *) +- ) !write_bandwidth_controlers +- ); +- +- set_before_select_hook (fun _ -> +- List.iter (fun bc -> +- bc.allow_io := (bc.total_bytes = 0 || bc.remaining_bytes > 0); +- ) !read_bandwidth_controlers; +- List.iter (fun bc -> +- bc.allow_io := (bc.total_bytes = 0 || bc.remaining_bytes > 0); +- ) !write_bandwidth_controlers; +- ); +- +- set_after_select_hook (fun _ -> +- List.iter (fun bc -> +- List.iter (fun t -> +- if bc.remaining_bytes > 0 then +- let can_read = maxi 1 (bc.remaining_bytes / bc.nconnections) in +- let can_read = can_read * t.read_power in +- let old_nread = t.nread in +- (try +- can_read_handler t t.sock (mini can_read +- (String.length t.rbuf.buf)) +- with _ -> ()); +- bc.remaining_bytes <- bc.remaining_bytes - +- t.nread + old_nread; +- bc.nconnections <- bc.nconnections - t.read_power; +- ) bc.connections; +- if bc.remaining_bytes > 0 then bc.allow_io := false; +- bc.connections <- []; +- bc.nconnections <- 0; +- ) !read_bandwidth_controlers; +- List.iter (fun bc -> +- List.iter (fun t -> +- if bc.remaining_bytes > 0 then +- let can_write = maxi 1 (bc.remaining_bytes / bc.nconnections) in +- let can_write = can_write * t.write_power in +- let old_nwrite = t.nwrite in +- (try +-(* lprintf "WRITE"; lprint_newline (); *) +- can_write_handler t t.sock (mini can_write t.wlen) +- with _ -> ()); +- bc.remaining_bytes <- bc.remaining_bytes - +- t.nwrite + old_nwrite; +- bc.nconnections <- bc.nconnections - t.write_power; +- ) bc.connections; +- if bc.remaining_bytes > 0 then bc.allow_io := false; +- bc.connections <- []; +- bc.nconnections <- 0; +- ) !write_bandwidth_controlers +- ) +- +- +-let my_ip t = +- let fd = fd t.sock in +- match Unix.getsockname fd with +- Unix.ADDR_INET (ip, port) -> Ip.of_inet_addr ip +- | _ -> raise Not_found +- +-let stats buf t = +- BasicSocket.stats buf t.sock; +- Printf.bprintf buf " rbuf size: %d/%d\n" (String.length t.rbuf.buf) +- t.rbuf.max_buf_size; +- Printf.bprintf buf " wbuf size: %d/%d\n" (t.wlen) +- t.wfbuf.max_buf_size +- +-let buf_size t = +- (String.length t.rbuf.buf), +- (String.length t.wfbuf.buf) +- +-let can_fill t = +- t.wlen < (t.wfbuf.max_buf_size / 2) +- +-let if_possible bc len = +- bc.total_bytes = 0 || +- if bc.last_remaining >= len then begin +- bc.last_remaining <- bc.last_remaining - len; +- true; +- end else false +- +-let set_rtimeout s t = set_rtimeout (sock s) t +-let set_wtimeout s t = set_wtimeout (sock s) t +- +-open LittleEndian +- +-let internal_buf = Buffer.create 17000 +- +-let simple_send_buf buf sock = () +- (* +- let s = Buffer.contents buf in +- Buffer.reset buf; +- buf_int8 buf 228; +- let len = String.length s in +- buf_int buf len; +- write sock (Buffer.contents buf) 0 5; +- write sock s 0 len +-*) +- +-let value_send sock m = () +- (* +- Buffer.reset internal_buf; +- Buffer.add_string internal_buf (Marshal.to_string m []); +- simple_send_buf internal_buf sock +-*) +- +-let value_handler f sock nread = () +- (* +- let b = buf sock in +- try +- while b.len >= 5 do +- let msg_len = get_int b.buf (b.pos+1) in +- if b.len >= 5 + msg_len then +- begin +- let s = String.sub b.buf (b.pos+5) msg_len in +- let t = Marshal.from_string s 0 in +- buf_used sock (msg_len + 5); +- f t sock; +- () +- end +- else raise Not_found +- done +- with Not_found -> () +- *) +- +- +- +-let set_write_power t p = t.write_power <- p +-let set_read_power t p = t.read_power <- p +- +-let set_lifetime s = set_lifetime (sock s) +- +-let moved_bytes bc = bc.moved_bytes +- +Index: src/utils/net/tcpClientSocket.mli +=================================================================== +RCS file: src/utils/net/tcpClientSocket.mli +diff -N src/utils/net/tcpClientSocket.mli +--- src/utils/net/tcpClientSocket.mli 22 Apr 2003 22:33:40 -0000 1.1 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,108 +0,0 @@ +-(* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *) +-(* +- This file is part of mldonkey. +- +- mldonkey 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. +- +- mldonkey 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 mldonkey; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-*) +-type event = +- WRITE_DONE +- | CAN_REFILL +- | BUFFER_OVERFLOW +- | READ_DONE of int +- | BASIC_EVENT of BasicSocket.event +- +-and buf = { +- mutable buf : string; +- mutable pos : int; +- mutable len : int; +- mutable max_buf_size : int; +- } +- +-type t +- +-type bandwidth_controler = { +- mutable remaining_bytes : int; +- mutable total_bytes : int; +- mutable nconnections : int; +- mutable connections : t list; +- allow_io : bool ref; +- mutable last_remaining : int; +- mutable moved_bytes : int64; +- } +- +- +-and handler = t -> event -> unit +- +-val max_buffer_size : int ref +- +-val sock: t -> BasicSocket.t +-val create : string -> Unix.file_descr -> handler -> t +-val create_simple : string -> Unix.file_descr -> t +-val create_blocking : string -> Unix.file_descr -> handler -> t +-val buf : t -> buf +-val set_reader : t -> (t -> int -> unit) -> unit +-val buf_used : t -> int -> unit +-val set_handler : t -> event -> (t -> unit) -> unit +-val set_refill : t -> (t -> unit) -> unit +-val write: t -> string -> unit +-val write_uniq: t -> string -> unit +-val connect: string -> Unix.inet_addr -> int -> handler -> t +-val close : t -> string -> unit +-val closed : t -> bool +-val shutdown : t -> string -> unit +-val error: t -> string +-val tcp_handler: t -> BasicSocket.t -> BasicSocket.event -> unit +-val set_closer : t -> (t -> string -> unit) -> unit +-val nread : t -> int +-val set_max_write_buffer : t -> int -> unit +-val can_write : t -> bool +-val can_write_len : t -> int -> bool +-val set_monitored : t -> unit +- +-val close_after_write : t -> unit +- +-val create_read_bandwidth_controler : int -> bandwidth_controler +-val create_write_bandwidth_controler : int -> bandwidth_controler +-val set_read_controler : t -> bandwidth_controler -> unit +-val set_write_controler : t -> bandwidth_controler -> unit +-val change_rate : bandwidth_controler -> int -> unit +- +- +-val my_ip : t -> Ip.t +- +-val stats : Buffer.t -> t -> unit +-val buf_size : t -> int * int +-val can_fill : t -> bool +- +-val if_possible : bandwidth_controler -> int -> bool +- +-val set_rtimeout : t -> float -> unit +-val set_wtimeout : t -> float -> unit +- +-val internal_buf : Buffer.t +-val value_send : t -> 'a -> unit +-val value_handler : ('a -> t -> unit) -> t -> int -> unit +- +-val set_write_power : t -> int -> unit +-val set_read_power : t -> int -> unit +- +-val remaining_to_write : t -> int +- +-val set_lifetime : t -> float -> unit +- +-val tcp_uploaded_bytes : int64 ref +-val tcp_downloaded_bytes : int64 ref +-val moved_bytes : bandwidth_controler -> int64 +- +Index: src/utils/net/tcpSocket.mli +=================================================================== +RCS file: src/utils/net/tcpSocket.mli +diff -N src/utils/net/tcpSocket.mli +--- src/utils/net/tcpSocket.mli 22 Apr 2003 22:33:40 -0000 1.1 ++++ /dev/null 1 Jan 1970 00:00:00 -0000 +@@ -1,44 +0,0 @@ +-(* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *) +-(* +- This file is part of mldonkey. +- +- mldonkey 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. +- +- mldonkey 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 mldonkey; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-*) +-type event = +- WRITE_DONE +- | CAN_REFILL +- | BUFFER_OVERFLOW +- | READ_DONE of int +- | BASIC_EVENT of BasicSocket.event +- +-and buf = { +- mutable buf : string; +- mutable pos : int; +- mutable len : int; +- mutable max_buf_size : int; +- } +- +-type t +- +-and handler = t -> event -> unit +- +-val sock: t -> BasicSocket.t +-val create : Unix.file_descr -> handler -> t +-val buf : t -> buf +-val set_reader : t -> (t -> int -> unit) -> unit +-val buf_used : t -> int -> unit +-val set_handler : t -> event -> (t -> unit) -> unit +-val set_refill : t -> (t -> unit) -> unit +-val write: t -> string -> int -> int -> unit +\ No newline at end of file +Index: src/utils/net/udpSocket.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/src/utils/net/udpSocket.ml,v +retrieving revision 1.20 +retrieving revision 1.22 +diff -u -r1.20 -r1.22 +--- src/utils/net/udpSocket.ml 14 Dec 2005 21:17:47 -0000 1.20 ++++ src/utils/net/udpSocket.ml 21 Nov 2006 22:34:34 -0000 1.22 +@@ -23,6 +23,14 @@ + open AnyEndian + open LittleEndian + ++let log_prefix = "[udpSock]" ++ ++let lprintf_nl fmt = ++ lprintf_nl2 log_prefix fmt ++ ++let lprintf_n fmt = ++ lprintf2 log_prefix fmt ++ + type event = + WRITE_DONE + | CAN_REFILL +@@ -131,6 +139,7 @@ + mutable allow_io : bool ref; + mutable count : int; + mutable base_time : int; ++ mutable tcp_bc : TcpBufferedSocket.bandwidth_controler; + } + + and handler = t -> event -> unit +@@ -190,10 +199,10 @@ + lprintf_nl "ADDR_UNIX (%s)" s; + end + +-let max_delayed_send = 30 ++let max_delayed_send = 1 + + let write t ping s ip port = +-(* lprintf "UDP write to %s:%d\n" (Ip.to_string ip) port; *) ++(* lprintf_nl "UDP write to %s:%d" (Ip.to_string ip) port; *) + if not (closed t) && t.wlist_size < !max_wlist_size then + let s, addr = match t.socks_local with + None -> s, Unix.ADDR_INET(Ip.to_inet_addr ip, port) +@@ -219,16 +228,19 @@ + let _ = + try + if ping then declare_ping ip; +- ignore(Unix.sendto (fd sock) s 0 len [] addr) ++ ignore(Unix.sendto (fd sock) s 0 len [] addr); ++ if !verbose_bandwidth > 1 then begin ++ lprintf_nl "[BW2] direct send udp %d bytes (write)" len; ++ end; + with e -> +- lprintf "Exception in sendto %s:%d\n" (Ip.to_string ip) port; ++ lprintf_nl "Exception in sendto %s:%d" (Ip.to_string ip) port; + raise e + in + udp_uploaded_bytes := !udp_uploaded_bytes ++ (Int64.of_int len); + () + (* +-lprintf "UDP sent [%s]" (String.escaped +-(String.sub s pos len)); lprint_newline (); ++lprintf_nl "UDP sent [%s]" (String.escaped ++(String.sub s pos len)); + *) + with + Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.ENOBUFS), _, _) -> +@@ -240,7 +252,7 @@ + t.wlist_size <- t.wlist_size + String.length s; + must_write sock true; + | e -> +- lprintf "Exception %s in sendto\n" ++ lprintf_nl "Exception %s in sendto" + (Printexc2.to_string e); + print_addr addr; + raise e +@@ -265,6 +277,10 @@ + t.wlist_size <- t.wlist_size + String.length s; + must_write t.sock true; + end ++ else ++ if !debug then begin ++ lprintf_nl "UDP DROPPED in write"; ++ end + + let dummy_sock = Obj.magic 0 + +@@ -278,11 +294,16 @@ + begin try + ignore (local_sendto (fd sock) p); + udp_uploaded_bytes := !udp_uploaded_bytes ++ (Int64.of_int len); ++ if !verbose_bandwidth > 1 then begin ++ lprintf_nl "[BW2] direct send udp %d bytes (iter_write_no_bc)" len; ++ end + with +- Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.ENOBUFS), _, _) as e -> raise e ++ Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.ENOBUFS), _, _) as e -> ++ lprintf_nl "Exception %s in sendto next" (Printexc2.to_string e); ++ raise e + | e -> + if !debug then +- lprintf "Exception %s in sendto next\n" ++ lprintf_nl "Exception %s in sendto next" + (Printexc2.to_string e) + end; + iter_write_no_bc t sock +@@ -302,23 +323,27 @@ + t.wlist_size <- t.wlist_size - String.length p.udp_content; + if time < bc.base_time then begin + if !debug then begin +- lprintf "[UDP DROPPED]"; ++ lprintf_nl "UDP DROPPED in iter_write"; + end; + iter_write t sock bc + end else + let len = String.length p.udp_content in + begin try +- +- + ignore (local_sendto (fd sock) p); + udp_uploaded_bytes := !udp_uploaded_bytes ++ (Int64.of_int len); + bc.remaining_bytes <- bc.remaining_bytes - (len + + !TcpBufferedSocket.ip_packet_size) ; ++ TcpBufferedSocket.register_bytes (Some bc.tcp_bc) len; ++ if !verbose_bandwidth > 1 then begin ++ lprintf_nl "[BW2] bc send udp %d bytes" len; ++ end; + with +- Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.ENOBUFS), _, _) as e -> raise e ++ Unix.Unix_error ((Unix.EWOULDBLOCK | Unix.ENOBUFS), _, _) as e -> ++ lprintf_nl "Exception %s in sendto next" (Printexc2.to_string e); ++ raise e + | e -> + if !debug then +- lprintf "Exception %s in sendto next\n" ++ lprintf_nl "Exception %s in sendto next" + (Printexc2.to_string e) + end; + iter_write t sock bc +@@ -418,26 +443,28 @@ + allow_io = ref false; + count = 0; + base_time = 0; ++ tcp_bc = tcp_bc; + } in + let udp_user total n = +-(* +- if !BasicSocket.debug then begin +- lprintf "udp_user %d/%d" n total; lprint_newline (); +- end; *) +- let n = if total = 0 then 100000 else n in ++ if !verbose_bandwidth > 0 then ++ lprintf_nl "udp_user %d/%d" n total; ++(* let n = if total = 0 then 100000 else n in *) + udp_bc.base_time <- udp_bc.base_time + 1; + if udp_bc.count = 0 then begin + udp_bc.count <- 10; + TcpBufferedSocket.set_lost_bytes tcp_bc udp_bc.remaining_bytes + udp_bc.base_time; +- udp_bc.remaining_bytes <- 0; + end; + udp_bc.count <- udp_bc.count - 1; + udp_bc.total_bytes <- total; +- udp_bc.remaining_bytes <- udp_bc.remaining_bytes + n; ++ udp_bc.remaining_bytes <- total / 2; ++(* udp_bc.remaining_bytes <- udp_bc.remaining_bytes + n; *) + if total <> 0 && udp_bc.remaining_bytes > total then + udp_bc.remaining_bytes <- total; + udp_bc.allow_io := udp_bc.remaining_bytes > 0; ++ if !verbose_bandwidth > 0 then ++ lprintf_nl "udp_bc count:%d total_bytes:%d remaining_bytes:%d" ++ udp_bc.count udp_bc.total_bytes udp_bc.remaining_bytes; + in + TcpBufferedSocket.set_remaining_bytes_user tcp_bc udp_user; + udp_bc +@@ -510,8 +537,8 @@ + + MlUnix.set_nonblock fd; + with e -> +- lprintf "[SOCKS] proxy error prevent creation of UDP socket: %s" +- (Printexc2.to_string e); lprint_newline (); ++ lprintf_nl "[SOCKS] proxy error prevent creation of UDP socket: %s" ++ (Printexc2.to_string e); + close t "socks proxy error"; raise e + *) + +@@ -524,7 +551,7 @@ + ) latencies; + LittleEndian.buf_int b !counter; + Hashtbl.iter (fun ip (latency, samples) -> +- if !verbose then lprintf " Latency UDP: %s -> %d (%d samples)\n" (Ip.to_string ip) !latency !samples; ++ if !verbose then lprintf_nl " Latency UDP: %s -> %d (%d samples)" (Ip.to_string ip) !latency !samples; + LittleEndian.buf_ip b ip; + LittleEndian.buf_int16 b !latency; + LittleEndian.buf_int16 b !samples; +Index: tools/make_torrent.ml +=================================================================== +RCS file: /sources/mldonkey/mldonkey/tools/make_torrent.ml,v +retrieving revision 1.8 +retrieving revision 1.9 +diff -u -r1.8 -r1.9 +--- tools/make_torrent.ml 9 Jan 2006 00:25:59 -0000 1.8 ++++ tools/make_torrent.ml 25 Oct 2006 11:34:46 -0000 1.9 +@@ -203,7 +203,7 @@ + "Quick Howto:\n" ^ + "- create a new torrent:\n" ^ + "make_torrent -tracker http://ip:port/announce -torrent file.torrent " ^ +- "-comment \"www.mldonkey.net\" -create file\n\n" ^ ++ "-comment \"www.mldonkey.org\" -create file\n\n" ^ + "- change the tracker of a torrent file:\n" ^ + "make_torrent -tracker http://ip:port/tracker -torrent myfile.torrent -change\n\n" ^ + "- print the infos of a torrent file:\n" ^ diff --git a/net-p2p/mldonkey-devel/files/patch-src__daemon__driver__driverMain.ml b/net-p2p/mldonkey-devel/files/patch-src__daemon__driver__driverMain.ml deleted file mode 100644 index 8a114a8296fa..000000000000 --- a/net-p2p/mldonkey-devel/files/patch-src__daemon__driver__driverMain.ml +++ /dev/null @@ -1,11 +0,0 @@ ---- src/daemon/driver/driverMain.ml.orig Tue Nov 21 10:55:31 2006 -+++ src/daemon/driver/driverMain.ml Tue Nov 21 10:55:54 2006 -@@ -342,7 +342,7 @@ - end; - - ( -- let hostname = "www.mldonkey.net" in -+ let hostname = "savannah.nongnu.org" in - try - ignore(Ip.from_name hostname); - DriverInteractive.dns_works := true |