diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index 878b1831ce..466cccd09a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -275,8 +275,11 @@ public class TrackerClient extends I2PThread tr.stop = true; } } - if (++tr.consecutiveFails == MAX_CONSEC_FAILS && tr.interval < LONG_SLEEP) - tr.interval = LONG_SLEEP; // slow down + if (++tr.consecutiveFails == MAX_CONSEC_FAILS) { + tr.seenPeers = 0; + if (tr.interval < LONG_SLEEP) + tr.interval = LONG_SLEEP; // slow down + } } } if ((!tr.stop) && maxSeenPeers < tr.seenPeers) diff --git a/checklist.txt b/checklist.txt index ad277e24fb..66b52a659c 100644 --- a/checklist.txt +++ b/checklist.txt @@ -14,6 +14,10 @@ Change revision in: router/java/src/net/i2p/router/RouterVersion.java core/java/src/net/i2p/CoreVersion.java +Review the complete diff from the last release: + mtn diff -r t:i2p-0.6.1.(xx-1) > out.diff + vi out.diff + Build and tag: ant pkg mtn ci diff --git a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java index 696fe1479b..c07c1a7711 100644 --- a/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java +++ b/core/java/src/net/i2p/client/naming/HostsTxtNamingService.java @@ -9,8 +9,10 @@ package net.i2p.client.naming; import java.io.File; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Properties; +import java.util.Set; import java.util.StringTokenizer; import net.i2p.I2PAppContext; @@ -87,6 +89,28 @@ public class HostsTxtNamingService extends NamingService { } public String reverseLookup(Destination dest) { + String destkey = dest.toBase64(); + List filenames = getFilenames(); + for (int i = 0; i < filenames.size(); i++) { + String hostsfile = (String)filenames.get(i); + Properties hosts = new Properties(); + try { + File f = new File(hostsfile); + if ( (f.exists()) && (f.canRead()) ) { + DataHelper.loadProps(hosts, f, true); + Set keyset = hosts.keySet(); + Iterator iter = keyset.iterator(); + while (iter.hasNext()) { + String host = (String)iter.next(); + String key = hosts.getProperty(host); + if (destkey.equals(key)) + return host; + } + } + } catch (Exception ioe) { + _log.error("Error loading hosts file " + hostsfile, ioe); + } + } return null; } } diff --git a/history.txt b/history.txt index 748a712369..f87795944e 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,14 @@ +2008-03-30 zzz + * ExploratoryPeerSelector: Try NonFailing even more + * HostsTxtNamingService: Add reverse lookup support + * Outbound message: Minor cleanup + * i2psnark TrackerCLient: Minor cleanup + * checklist.txt: Minor edit + * hosts.txt: Add perv.i2p, false.i2p, mtn.i2p2.i2p + * i2ptunnel.config: Change CVS client to mtn + * netdb.jsp: Show leaseSet destinations using reverse lookup + * profiles.jsp: First cut at showing floodfill data + 2008-03-27 zzz * Send messages for the same destination to the same inbound lease to reduce out-of-order delivery. @@ -16,6 +27,7 @@ - Don't have eepget retry announces - Slow down tracker contacts if they've failed for a while - Add some debug support showing connections (?p=2) + * hosts.txt: Add nickyb.i2p, tracker.welterde.i2p 2008-03-22 zzz * NewsFetcher: Fix bug causing fetch every 10m diff --git a/hosts.txt b/hosts.txt index 440d94c7e4..23dd1dbb94 100644 --- a/hosts.txt +++ b/hosts.txt @@ -302,3 +302,6 @@ www.i2p2.i2p=-KR6qyfPWXoN~F3UzzYSMIsaRy4udcRkHu2Dx9syXSzUQXQdi2Af1TV2UMH3PpPuNu- i2p-projekt.i2p=8ZAW~KzGFMUEj0pdchy6GQOOZbuzbqpWtiApEj8LHy2~O~58XKxRrA43cA23a9oDpNZDqWhRWEtehSnX5NoCwJcXWWdO1ksKEUim6cQLP-VpQyuZTIIqwSADwgoe6ikxZG0NGvy5FijgxF4EW9zg39nhUNKRejYNHhOBZKIX38qYyXoB8XCVJybKg89aMMPsCT884F0CLBKbHeYhpYGmhE4YW~aV21c5pebivvxeJPWuTBAOmYxAIgJE3fFU-fucQn9YyGUFa8F3t-0Vco-9qVNSEWfgrdXOdKT6orr3sfssiKo3ybRWdTpxycZ6wB4qHWgTSU5A-gOA3ACTCMZBsASN3W5cz6GRZCspQ0HNu~R~nJ8V06Mmw~iVYOu5lDvipmG6-dJky6XRxCedczxMM1GWFoieQ8Ysfuxq-j8keEtaYmyUQme6TcviCEvQsxyVirr~dTC-F8aZ~y2AlG5IJz5KD02nO6TRkI2fgjHhv9OZ9nskh-I2jxAzFP6Is1kyAAAA nickyb.i2p=9On6d3cZ27JjwYCtyJJbowe054d5tFnfMjv4PHsYs-EQn4Y4mk2zRixatvuAyXz2MmRfXG-NAUfhKr0KCxRNZbvHmlckYfT-WBzwwpiMAl0wDFY~Pl8cqXuhfikSG5WrqdPfDNNIBuuznS0dqaczf~OyVaoEOpvuP3qV6wKqbSSLpjOwwAaQPHjlRtNIW8-EtUZp-I0LT45HSoowp~6b7zYmpIyoATvIP~sT0g0MTrczWhbVTUZnEkZeLhOR0Duw1-IRXI2KHPbA24wLO9LdpKKUXed05RTz0QklW5ROgR6TYv7aXFufX8kC0-DaKvQ5JKG~h8lcoHvm1RCzNqVE-2aiZnO2xH08H-iCWoLNJE-Td2kT-Tsc~3QdQcnEUcL5BF-VT~QYRld2--9r0gfGl-yDrJZrlrihHGr5J7ImahelNn9PpkVp6eIyABRmJHf2iicrk3CtjeG1j9OgTSwaNmEpUpn4aN7Kx0zNLdH7z6uTgCGD9Kmh1MFYrsoNlTp4AAAA tracker.welterde.i2p=BGKmlDOoH3RzFbPRfRpZV2FjpVj8~3moFftw5-dZfDf2070TOe8Tf2~DAVeaM6ZRLdmFEt~9wyFL8YMLMoLoiwGEH6IGW6rc45tstN68KsBDWZqkTohV1q9XFgK9JnCwE~Oi89xLBHsLMTHOabowWM6dkC8nI6QqJC2JODqLPIRfOVrDdkjLwtCrsckzLybNdFmgfoqF05UITDyczPsFVaHtpF1sRggOVmdvCM66otyonlzNcJbn59PA-R808vUrCPMGU~O9Wys0i-NoqtIbtWfOKnjCRFMNw5ex4n9m5Sxm9e20UkpKG6qzEuvKZWi8vTLe1NW~CBrj~vG7I3Ok4wybUFflBFOaBabxYJLlx4xTE1zJIVxlsekmAjckB4v-cQwulFeikR4LxPQ6mCQknW2HZ4JQIq6hL9AMabxjOlYnzh7kjOfRGkck8YgeozcyTvcDUcUsOuSTk06L4kdrv8h2Cozjbloi5zl6KTbj5ZTciKCxi73Pn9grICn-HQqEAAAA +perv.i2p=HazSt3kSqVjpMO9Ol9HPjXCfzTLurOPjSeN47UxMLLs4lm5SJJHP-p3vA4sTfxrR3z7Cqq5Nd0t1gog~bV0PCbnKNsTiF~pSPBIFXOPVe5RuX7keDRJ-Mp5-quPIe2FFNEOz7HNkbuz6aHYN74T5Cs2VcMDEvL9Wz~oxV7FPSijD1qs1hi~f3byvSvswRkt4w7A10iJvunq43r1jOi6JwS~lAChP1q7t16sfhtWp4XEaEvfES0bIbnTsRv-aDVy0Kp4zYYPuZstit-S8GHkS7ceDRHSoid1O6V1WgogeDoTRzfxfHqQb5db5xWt4DXmnQbb7xysXub-2S3mbog-WnoawDRM5ZJDItCDuMsg0edIn5RJYkANh0Qox-JMVUZdU8tcx--DX9gONJ4cm4a-MLxY9dme~Zq4hqJJRNRvrRcA11ZuLH-ZN3aTXL70Yod-YyNPcechGrpugKEU38g5sDyj77pX~IrdaTVTFpJE6zlClRatTnl4Enhr4pQ7gWFpcAAAA +false.i2p=V5EeX1UrVanCQhE9q-Nj5UFRyQiH6b~lSO7qe8hgxGZkymrpHFZ5j5D9rVOjsogokon1bZF5NPlPdzI~d4Ap7UyqBR~90vhFxsIKednRZLdf46JgjYTZq~ct-Bi3sdpBXdg04L8i4dStE4jkdvl6NF96MfQKdt6mtYFeaXb0XhPx~04NECG5~y~mTYcMjcvrftt5uulLbT6TcGmJ0KV5Xu2UVnkN3zOaQEObIIDZT4wkz9jOqaiJzMHNJqWc4GU4ocIfwxeMkSn9qvA0Q1AXuM~z11~wgHYLVEFoN5k0O4aB8b1r1WtpZTojZ9ADThW89q~AcD981nIYnRp59cmyqBdN7X-71cBrKC7QfnCRJpbwoVrn7kePw1dCu6Bobnd4~a74abxjFeCxVzQxSTbkey6f~wJXE2JPAqfsM1EKXsdqUZQJOP0ngQu16srSpqMSxkqHzlr3US~Vn9EgMcHuDCkdctwo7stwn0UQ34sSF9sLhtmlHIGqatDhfmYEGmSiAAAA +mtn.i2p2.i2p=G6VmsrLYbdcxBq585OUcQn7wbwC7J5jfXDWWL6lPBw5iq68VxqxibraiPwwF6NM2aHV8BkqyCKYSL9fUuYWoeUc1zL~2l1DX2x~LfyItGJKDIUGImWQivXF1w7EGYMhjq4UCmPKTsnl4G86oKW8PGaaF8mzjjUKW1R7G7941my~mnbeTrhjlLgaMK-tauVodgTPIYkxfMJaq3zWuirztuUgDcXXIbkpzaA2Iben0VqbjbMJisj4fFh0EvqNkYAG54YBc26~W6SPWyBgZilXvFlcizF90Q5NkIGMMHXTq46qEYHkpQC3CoaH6PMNVDetDPmFc3QXmc68cNcj~VPh4XVsn3qiKhXuRdXggEC3RoTcxqaeassfIG5xhRdnJzGSVhYUE3At~8wI-AuRV~AglV1Q-AZTWT~9VxBzcxfI1PpfzeA-5z5T4542bh1e-RM9tzXEx5ErPCt6M~zJ2~4-tz-aBsZEhBkn0iDi8pazshg6lTl1~hCnueZBxYICqPrlBAAAA diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index eff3ae7485..2cbe84b502 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -29,14 +29,14 @@ tunnel.1.option.i2p.streaming.connectDelay=1000 tunnel.1.option.i2p.streaming.maxWindowSize=1 tunnel.1.startOnLoad=true -# I2P's cvs server -tunnel.2.name=cvs.i2p -tunnel.2.description=I2P CVS pserver +# I2P's mtn server +tunnel.2.name=mtn.i2p2.i2p +tunnel.2.description=I2P Monotone Server tunnel.2.type=client tunnel.2.sharedClient=true tunnel.2.interface=127.0.0.1 -tunnel.2.listenPort=2401 -tunnel.2.targetDestination=cvs.i2p +tunnel.2.listenPort=8998 +tunnel.2.targetDestination=mtn.i2p2.i2p tunnel.2.i2cpHost=127.0.0.1 tunnel.2.i2cpPort=7654 tunnel.2.option.inbound.nickname=shared clients diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 750dae67cd..54461d6815 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-02-10 15:00:00 $"; public final static String VERSION = "0.6.1.32"; - public final static long BUILD = 13; + public final static long BUILD = 14; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index d4c599e257..f250c784b9 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -228,6 +228,9 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * simultaneously talking to the same dest is probably rare enough * to not bother separating out. * + * We're going to use the lease until it expires, not even looking for a newer lease. + * So if the inbound tunnel fails and the dest publishes a new lease, we won't know about it. + * * If not found, * fetch the next lease that we should try sending through, randomly chosen * from within the sorted leaseSet (NOT sorted by # of failures through each @@ -244,8 +247,9 @@ public class OutboundClientMessageOneShotJob extends JobImpl { return false; } long now = getContext().clock().now(); - + // Use the same lease if it's still good + // Even if _leaseSet changed, _leaseSet.getEncryptionKey() didn't... synchronized (_leaseCache) { if (now - _cleanTime > 5*60*1000) { // clean out periodically cleanLeaseCache(_leaseCache); @@ -254,12 +258,12 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _lease = (Lease) _leaseCache.get(_to); if (_lease != null) { if (!_lease.isExpired()) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Found in cache - lease for dest " + _to.calculateHash().toBase64()); + if (_log.shouldLog(Log.INFO)) + _log.info("Found in cache - lease for " + _toString); return true; } else { if (_log.shouldLog(Log.WARN)) - _log.warn("Expired from cache - lease for dest " + _to.calculateHash().toBase64()); + _log.warn("Expired from cache - lease for " + _toString); _leaseCache.remove(_to); } } @@ -288,6 +292,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { // sort are randomly ordered) Collections.shuffle(leases); +/**** if (false) { // ordered by lease number of failures TreeMap orderedLeases = new TreeMap(); @@ -303,13 +308,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl { _lease = (Lease)orderedLeases.get(orderedLeases.firstKey()); } else { +****/ _lease = (Lease)leases.get(0); - } +// } synchronized (_leaseCache) { _leaseCache.put(_to, _lease); } if (_log.shouldLog(Log.WARN)) - _log.warn("Added to cache - lease for dest " + _to.calculateHash().toBase64()); + _log.warn("Added to cache - lease for " + _toString); return true; } @@ -538,7 +544,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel)) { if (!getContext().commSystem().isBacklogged(tunnel.getPeer(1))) { if (_log.shouldLog(Log.WARN)) - _log.warn("Switching back to tunnel " + tunnel + " for dest " + to.calculateHash().toBase64()); + _log.warn("Switching back to tunnel " + tunnel + " for " + _toString); _backloggedTunnelCache.remove(to); _tunnelCache.put(to, tunnel); return tunnel; @@ -554,7 +560,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { return tunnel; // backlogged if (_log.shouldLog(Log.WARN)) - _log.warn("Switching from backlogged " + tunnel + " for dest " + to.calculateHash().toBase64()); + _log.warn("Switching from backlogged " + tunnel + " for " + _toString); _backloggedTunnelCache.put(to, tunnel); } // else no longer valid _tunnelCache.remove(to); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index a9d93691bc..c4a57965fd 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -21,9 +21,11 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import net.i2p.data.Base64; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.DataStructure; +import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.Lease; import net.i2p.data.LeaseSet; @@ -39,6 +41,7 @@ import net.i2p.router.networkdb.DatabaseLookupMessageHandler; import net.i2p.router.networkdb.DatabaseStoreMessageHandler; import net.i2p.router.networkdb.PublishLocalRouterInfoJob; import net.i2p.router.peermanager.PeerProfile; +import net.i2p.router.TunnelPoolSettings; import net.i2p.util.Log; /** @@ -934,8 +937,28 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { long now = _context.clock().now(); for (Iterator iter = leases.iterator(); iter.hasNext(); ) { LeaseSet ls = (LeaseSet)iter.next(); - Hash key = ls.getDestination().calculateHash(); - buf.append("LeaseSet: ").append(key.toBase64()).append("
\n"); + Destination dest = ls.getDestination(); + Hash key = dest.calculateHash(); + buf.append("LeaseSet: ").append(key.toBase64()); + if (_context.clientManager().isLocal(dest)) { + buf.append(" (Local "); + if (! _context.clientManager().shouldPublishLeaseSet(key)) + buf.append("Unpublished "); + buf.append("Destination "); + TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(key); + if (in != null && in.getDestinationNickname() != null) + buf.append(in.getDestinationNickname()); + else + buf.append(dest.toBase64().substring(0, 6)); + } else { + buf.append(" (Destination "); + String host = _context.namingService().reverseLookup(dest); + if (host != null) + buf.append(host); + else + buf.append(dest.toBase64().substring(0, 6)); + } + buf.append(")
\n"); long exp = ls.getEarliestLeaseDate()-now; if (exp > 0) buf.append("Earliest expiration date in: ").append(DataHelper.formatDuration(exp)).append("
\n"); diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java index bbceb5c7c0..80ba9e7be4 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java @@ -3,6 +3,8 @@ package net.i2p.router.peermanager; import java.io.IOException; import java.io.Writer; +import java.lang.Math; + import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -12,9 +14,13 @@ import java.util.Locale; import java.util.Set; import java.util.TreeSet; +import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.RouterInfo; import net.i2p.router.RouterContext; +import net.i2p.router.peermanager.DBHistory; +import net.i2p.stat.Rate; +import net.i2p.stat.RateStat; /** * Helper class to refactor the HTML rendering from out of the ProfileOrganizer @@ -33,13 +39,22 @@ class ProfileOrganizerRenderer { public void renderStatusHTML(Writer out) throws IOException { Set peers = _organizer.selectAllPeers(); - long hideBefore = _context.clock().now() - 3*60*60*1000; + long now = _context.clock().now(); + long hideBefore = now - 3*60*60*1000; TreeSet order = new TreeSet(_comparator); + TreeSet integratedPeers = new TreeSet(_comparator); for (Iterator iter = peers.iterator(); iter.hasNext();) { Hash peer = (Hash)iter.next(); if (_organizer.getUs().equals(peer)) continue; PeerProfile prof = _organizer.getProfile(peer); + if (_organizer.isWellIntegrated(peer)) { + integratedPeers.add(prof); + } else { + RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); + if (info != null && info.getCapabilities().indexOf("f") >= 0) + integratedPeers.add(prof); + } if (prof.getLastSendSuccessful() <= hideBefore) continue; order.add(prof); } @@ -127,6 +142,75 @@ class ProfileOrganizerRenderer { buf.append(""); } buf.append(""); + + buf.append("

Floodfill and Integrated Peers

\n"); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + for (Iterator iter = integratedPeers.iterator(); iter.hasNext();) { + PeerProfile prof = (PeerProfile)iter.next(); + Hash peer = prof.getPeer(); + + buf.append(""); + buf.append(""); + else + buf.append(""); + buf.append(""); + buf.append(""); + long time; + time = now - prof.getLastHeardAbout(); + buf.append(""); + time = now - prof.getLastHeardFrom(); + buf.append(""); + time = now - prof.getLastSendSuccessful(); + buf.append(""); + time = now - prof.getLastSendFailed(); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + DBHistory dbh = prof.getDBHistory(); + if (dbh != null) { + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + buf.append(""); + } + } + buf.append("
PeerCapsInteg. ValueLast Heard AboutLast Heard FromLast Successful SendLast Failed Send10m Resp. Time1h Resp. Time1d Resp. TimeSuccessful LookupsFailed LookupsNew StoresOld Stores1m Fail Rate1h Fail Rate1d Fail Rate
"); + if (prof.getIsFailing()) { + buf.append("-- ").append(peer.toBase64().substring(0,6)).append(""); + } else { + if (prof.getIsActive()) { + buf.append("++ ").append(peer.toBase64().substring(0,6)).append(""); + } else { + buf.append("   ").append(peer.toBase64().substring(0,6)); + } + } + RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer); + if (info != null) + buf.append("" + info.getCapabilities() + " ").append(num(prof.getIntegrationValue())).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(DataHelper.formatDuration(time)).append("").append(avg(prof, 10*60*1000l)).append("").append(avg(prof, 60*60*1000l)).append("").append(avg(prof, 24*60*60*1000l)).append("").append(dbh.getSuccessfulLookups()).append("").append(dbh.getFailedLookups()).append("").append(dbh.getUnpromptedDbStoreNew()).append("").append(dbh.getUnpromptedDbStoreOld()).append("").append(davg(dbh, 60*1000l)).append("").append(davg(dbh, 60*60*1000l)).append("").append(davg(dbh, 24*60*60*1000l)).append("
"); + buf.append("

Definitions: