From 006d1e1e210718868b5d6ec8ec7f1a81709e4803 Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Wed, 4 Nov 2026 00:24:14 +0100 Subject: [PATCH 1/5] Keep the cookies of the sites a login went through A login that ends on another site lost the cookies of the site the user typed. The engine names the bundle after the address the login landed on. That address alone set the bundle's scope, so every record outside it was dropped before the file was written. Xavier measured it: entering httrack.com and reaching ffii.org through the login wrote a bundle holding only the ffii.org cookies. The crawl needs those records. With --near or a filter it fetches the first site's images and stylesheets through pages on the second. An out-of-scope cookie is dropped when the bundle loads, not when the request goes out. Such a file comes back as a login page or a 403, and lands in the mirror as that. So the window now names the hosts that answered with a cookie, and the engine keeps their records behind a scope line each. A named host never becomes a crawl root. The user asked to copy one site, not the sign-in page it passed through. The engine runs out of room when its jar fills, and it now reports how many records it left out. That count is shown apart from the cookies this app drops for length. The causes differ, and only one of them is something the user can act on. Engine pin moves to 483d5d47 for hts_bundle_from_paste_hosts, engine master. HTTRACK_VERSIONID is still 3.51.0, so versionName stays. Signed-off-by: Xavier Roche Co-Authored-By: Claude Opus 5 (1M context) --- .../httrack/android/BrowserLoginActivity.java | 6 +- .../httrack/android/BrowserLoginPolicy.java | 22 + .../com/httrack/android/HTTrackActivity.java | 14 +- .../com/httrack/android/jni/HTTrackLib.java | 12 +- app/src/main/jni/htslibjni.c | 31 +- app/src/main/jni/httrack | 2 +- app/src/main/res/values/strings.xml | 607 +++++++++--------- .../android/BrowserLoginEngineLimitsTest.java | 14 +- .../android/BrowserLoginPolicyTest.java | 24 + .../android/BrowserLoginWiringTest.java | 39 +- 10 files changed, 445 insertions(+), 326 deletions(-) diff --git a/app/src/main/java/com/httrack/android/BrowserLoginActivity.java b/app/src/main/java/com/httrack/android/BrowserLoginActivity.java index 7ab5f03..e1e8311 100644 --- a/app/src/main/java/com/httrack/android/BrowserLoginActivity.java +++ b/app/src/main/java/com/httrack/android/BrowserLoginActivity.java @@ -76,6 +76,7 @@ public class BrowserLoginActivity extends FragmentActivity { static final String EXTRA_COOKIE_COUNT = "com.httrack.android.cookieCount"; static final String EXTRA_HOST_COUNT = "com.httrack.android.hostCount"; static final String EXTRA_DROPPED_COUNT = "com.httrack.android.droppedCount"; + static final String EXTRA_NO_ROOM_COUNT = "com.httrack.android.noRoomCount"; private WebView web; @@ -348,7 +349,8 @@ public void onClickLoginDone(final View view) { HTTrackLib.LoginBundle bundle = null; for (final String url : BrowserLoginPolicy.addressesToTry(web.getUrl(), openedUrl)) { bundle = HTTrackLib.writeLoginBundle(result.text, bundleDir.getAbsolutePath(), - web.getSettings().getUserAgentString(), url); + web.getSettings().getUserAgentString(), url, + BrowserLoginPolicy.keptHosts(byHost.keySet())); if (bundle != null) { break; } @@ -365,6 +367,8 @@ public void onClickLoginDone(final View view) { data.putExtra(EXTRA_HOST_COUNT, bundle.hosts); // Ours, and only the over-long ones, which is what the message the user reads says. data.putExtra(EXTRA_DROPPED_COUNT, result.dropped); + // The engine's, which are the ones its jar had no room for. + data.putExtra(EXTRA_NO_ROOM_COUNT, bundle.dropped); setResult(RESULT_OK, data); finish(); } diff --git a/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java b/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java index cf1829c..2829b5a 100644 --- a/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java +++ b/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java @@ -4,6 +4,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; @@ -96,6 +97,27 @@ static String hostOf(final String url) { return host.toLowerCase(Locale.ROOT); } + /** + * The hosts whose cookies the login keeps, as the engine's comma-separated list. A host + * gets a scope line for its registrable domain and never becomes a crawl root, so the + * crawl can authenticate on a site the login passed through without copying it. + * + * @param hosts + * the hosts that answered with a cookie, in the order they were asked + * @return the list, or null when no host answered, which keeps the engine's older + * behaviour of taking the address's own cookies alone + */ + static String keptHosts(final Collection hosts) { + final StringBuilder list = new StringBuilder(); + + for (final String host : hosts) { + if (host != null && host.length() != 0) { + list.append(list.length() == 0 ? "" : ",").append(host); + } + } + return list.length() == 0 ? null : list.toString(); + } + /** * The addresses to offer the engine for this login, best first. The login stops on the page * the user wants copied, so that address leads. The engine refuses an address it cannot diff --git a/app/src/main/java/com/httrack/android/HTTrackActivity.java b/app/src/main/java/com/httrack/android/HTTrackActivity.java index 9697ce6..dcbb6cf 100755 --- a/app/src/main/java/com/httrack/android/HTTrackActivity.java +++ b/app/src/main/java/com/httrack/android/HTTrackActivity.java @@ -2955,13 +2955,21 @@ private void applyBrowserLogin(final Intent data) { BrowserLoginPolicy.sockets(getMap(R.id.editNumberOfConnections))); setMap(R.id.editRules, BrowserLoginPolicy.rules(getMap(R.id.editRules))); final int droppedCookies = data.getIntExtra(BrowserLoginActivity.EXTRA_DROPPED_COUNT, 0); + final int noRoomCookies = data.getIntExtra(BrowserLoginActivity.EXTRA_NO_ROOM_COUNT, 0); final String saved = getString(R.string.browser_login_saved, data.getIntExtra(BrowserLoginActivity.EXTRA_COOKIE_COUNT, 0), data.getIntExtra(BrowserLoginActivity.EXTRA_HOST_COUNT, 0), BrowserLoginPolicy.MAX_SOCKETS); - // The engine drops a cookie its jar refuses, so saying only how many it kept is a lie. - final String message = droppedCookies == 0 ? saved - : saved + "\n\n" + getString(R.string.browser_login_dropped, droppedCookies); + /* The two counts have different causes and different answers, so they are never summed: + ours is a cookie too long to send, the engine's is a jar with no room left. */ + String message = saved; + + if (droppedCookies != 0) { + message += "\n\n" + getString(R.string.browser_login_dropped, droppedCookies); + } + if (noRoomCookies != 0) { + message += "\n\n" + getString(R.string.browser_login_no_room, noRoomCookies); + } new AlertDialog.Builder(this).setMessage(message) .setPositiveButton(android.R.string.ok, null).show(); } diff --git a/app/src/main/java/com/httrack/android/jni/HTTrackLib.java b/app/src/main/java/com/httrack/android/jni/HTTrackLib.java index 6ff498b..9cc79a3 100755 --- a/app/src/main/java/com/httrack/android/jni/HTTrackLib.java +++ b/app/src/main/java/com/httrack/android/jni/HTTrackLib.java @@ -133,10 +133,15 @@ public static final class LoginBundle { /** Hosts those cookies cover, which is not every host the login visited. */ public final int hosts; - public LoginBundle(final String url, final int cookies, final int hosts) { + /** Cookies of the named hosts the engine had no room for, so the crawl will not send them. */ + public final int dropped; + + public LoginBundle(final String url, final int cookies, final int hosts, + final int dropped) { this.url = url; this.cookies = cookies; this.hosts = hosts; + this.dropped = dropped; } } @@ -154,10 +159,13 @@ public LoginBundle(final String url, final int cookies, final int hosts) { * the browser's User-Agent, sent to the bundle's sites alone * @param url * the address the login ended on + * @param hosts + * the hosts whose cookies to keep besides the address's own, comma-separated, or + * null to keep the address's own alone * @return what it wrote, or null when the engine refused the login */ public static native LoginBundle writeLoginBundle(String paste, String folder, String agent, - String url); + String url, String hosts); /** * Has a native fault been recovered in this process ? diff --git a/app/src/main/jni/htslibjni.c b/app/src/main/jni/htslibjni.c index ce5b5e6..0362758 100755 --- a/app/src/main/jni/htslibjni.c +++ b/app/src/main/jni/htslibjni.c @@ -660,15 +660,16 @@ JNICALL jint Java_com_httrack_android_jni_HTTrackLib_getCustomHeaderVerdict(JNIE /* A new HTTrackLib.LoginBundle, or NULL with the exception pending. Looked up * here rather than cached, because one login writes one bundle. */ -static jobject newLoginBundle(JNIEnv* env, jstring url, jint cookies, jint hosts) { +static jobject newLoginBundle(JNIEnv* env, jstring url, jint cookies, jint hosts, + jint dropped) { jclass cls = (*env)->FindClass(env, "com/httrack/android/jni/HTTrackLib$LoginBundle"); jmethodID cons = cls != NULL - ? (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;II)V") + ? (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;III)V") : NULL; return cons != NULL - ? (*env)->NewObject(env, cls, cons, url, cookies, hosts) + ? (*env)->NewObject(env, cls, cons, url, cookies, hosts, dropped) : NULL; } @@ -696,8 +697,13 @@ JNICALL jobject Java_com_httrack_android_jni_HTTrackLib_writeLoginBundle(JNIEnv* jstring opaste, jstring ofolder, jstring oagent, - jstring ourl) { + jstring ourl, + jstring ohosts) { const char *paste, *folder, *agent, *url; + /* NULL is a value here, not a mistake: it asks the engine for the address's + own cookies alone, as the bundle writer did before the hosts list. */ + const char* hosts = NULL; + hts_boolean got_hosts; jobject bundle = NULL; UNUSED(clazz); @@ -716,23 +722,34 @@ JNICALL jobject Java_com_httrack_android_jni_HTTrackLib_writeLoginBundle(JNIEnv* folder = (*env)->GetStringUTFChars(env, ofolder, NULL); agent = (*env)->GetStringUTFChars(env, oagent, NULL); url = (*env)->GetStringUTFChars(env, ourl, NULL); - if (paste != NULL && folder != NULL && agent != NULL && url != NULL) { + if (ohosts != NULL) { + hosts = (*env)->GetStringUTFChars(env, ohosts, NULL); + } + got_hosts = ohosts == NULL || hosts != NULL; + if (paste != NULL && folder != NULL && agent != NULL && url != NULL && got_hosts) { hts_paste_result result; memset(&result, 0, sizeof(result)); result.size = sizeof(result); - if (hts_bundle_from_paste(paste, folder, agent, url, HTS_FALSE, &result)) { + /* The hosts keep their cookies through a scope line each, and none of them + becomes a start URL. */ + if (hts_bundle_from_paste_hosts(paste, folder, agent, url, hosts, 0u, + &result)) { jstring written = (*env)->NewStringUTF(env, result.url); if (written != NULL) { bundle = newLoginBundle(env, written, (jint) result.cookies, - countBundleHosts(result.hosts)); + countBundleHosts(result.hosts), + (jint) result.dropped); } } else { /* The reason is unlocalized English, so the user gets our own message. */ debug("the engine refused the login: %s", result.error); } } + if (hosts != NULL) { + (*env)->ReleaseStringUTFChars(env, ohosts, hosts); + } if (url != NULL) { (*env)->ReleaseStringUTFChars(env, ourl, url); } diff --git a/app/src/main/jni/httrack b/app/src/main/jni/httrack index b097228..483d5d4 160000 --- a/app/src/main/jni/httrack +++ b/app/src/main/jni/httrack @@ -1 +1 @@ -Subproject commit b0972289468f41652f88708300d6fa0e33472b3f +Subproject commit 483d5d47a6243cf57d1495e473a09a09519f3dea diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d6a7b4..79e8c45 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,304 +1,305 @@ - - - - HTTrack Website Copier - HTTrack Website Copier - Delete - Select project - Options - Welcome to HTTrack Website Copier!\n\nPlease click on the NEXT button to\n\n- start a new project\n- or resume a partial download - Unfinished download: %s. Open the project and select \"Continue interrupted download\". - and %s more - Next - New project - Project name - My Website - Project category - Funny Sites - URL and Settings - Previous - Start - Web Addresses: (URL) - http://www.example.com/funny/ - Options - Cancel - Exit - Exit - View log - Earlier - Browse Mirrored Website - Website Copier/Offline Browser. Copy remote websites to your computer. Free. - Browse sites - New project… - Delete… - Delete - Options - Exclude links - Include link(s) - Use wildcards to exclude or include URLs or links.\nYou can put several scan strings on the same line.\nUse spaces as separators.\n\nExample: +*.zip -www.*.com -www.*.edu/cgi-bin/*.cgi - Maximum link scanning depth - Maximum external depth - Max size of any HTML file (B) - Max size of any non-HTML file - Max site size - Max time - Max transfer rate - Max connections / seconds - Maximum number of links - Try to catch all URLs (even in unknown tags/code) - Get non-HTML files related to a link, eg external .ZIP or pictures - Test all links (even forbidden ones) - Get HTML files first! - Structure type (how links are saved) - Site-structure (default) - Html in web/, images/other files in web/images/ - Html in web/html, images/other in web/images - Html in web/, images/other in web/ - Html in web/, images/other in web/xxx, where xxx is the file extension - Html in web/html, images/other in web/xxx - Site-structure, without www.domain.xxx/ - Html in site_name/, images/other files in site_name/images/ - Html in site_name/html, images/other in site_name/images - Html in site_name/, images/other in site_name/ - Html in site_name/, images/other in site_name/xxx - Html in site_name/html, images/other in site_name/xxx - All files in web/, with random names (gadget !) - All files in site_name/, with random names (gadget !) - User-defined structure.. - DOS names (8+3) - ISO9660 names (CDROM) - No error pages - No external pages - Hide passwords - Hide query strings - Do not purge old files - Inline assets as data: URIs (self-contained pages) - Max size of an inlined asset (bytes) - 10485760 - Write WARC archive - WARC file name - mysite.warc.gz - Report what changed since the previous mirror - Seed the crawl from the site\'s sitemap - Sitemap URL - http://www.example.com/sitemap.xml - Max simultaneous connections - Persistent connections (Keep-Alive) - File timeout - Cancel all links from host if timeout occurs - Retries - Longest wait a server may ask for (seconds) - Minimum admissible transfer rate - Cancel all links from host if too slow - Browser identity - HTML footer - Accept cookies - Check document type - Parse java files - Spider - Update hack (limit re-transfers) - URL hacks (join similar URLs) - Tolerant requests (for servers) - Force old HTTP/1.0 requests (no 1.1) - Never - If unknown (except /) - If unknown - no robots.txt rules - robots.txt except wizard - follow robots.txt rules - Proxy address - Proxy port - Proxy protocol - HTTP - SOCKS5 - HTTP (CONNECT tunnel) - proxy.example.com - 8080 - Cookies file - cookies.txt (Netscape format) - Pause between downloads (sec, MIN:MAX) - 2:5 - Strip query keys - sid,utm_source - Other hostnames of this same site (one per line) - www2.example.com,m.example.com=example.com - Keep www. prefix (www.x != x) - Keep double slashes (//) - Keep query-string order (?b&a) - Mozilla/5.0 (compatible; HTTrack; +https://www.httrack.com/) - <!-- Mirrored from {url} by HTTrack Website Copier/3.x [XR&CO], {date} --> - Use proxy for ftp transfers - Store ALL files in cache - Do not re-download locally erased files - Log files - Make an index - Log file type (if generated) - normal - extended - debug - Use a cache for updates - Primary Scan Rule - Just scan - Store html files - Store non html files - Store all files (default) - Store html files first - Travel mode - Stay in the same directory - Can go down (default) - Can go up - Can both go up & down - Global travel mode - Stay on the same address (default) - Stay on the same domain - Stay on the same top level domain - Go everywhere on the web - Rewrite links: internal / external - Relative URI / Absolute URL (default) - Absolute URL / Absolute URL - Absolute URI / Absolute URL - Original URL / Original URL - Activate Debugging Mode (winhttrack.log) - -* +www.example.com/*.html +*.png +*.webp +*.woff2 - Download web site(s) - Continue interrupted download - Update existing download - User-defined structure - %h%p/%n%q.%t - Help - View Documentation - About HTTrack - Import existing mirrors - Pick the folder that holds your older HTTrack mirrors (for example HTTrack on the internal storage). Their contents will be copied into this app\'s storage; the originals are left untouched. - Importing mirrors… - Building index… - Imported %1$d files (%2$d already present). - Imported %1$d files; %2$d could not be copied. You can run the import again. - Nothing to import from that folder. - Not enough free space to import these mirrors. - Mirrors from an older version can no longer be reached directly. Import them into this app now? - Import - Not now - Don\'t ask again - Go To HTTrack Website - Go To HTTrack Forum - View License - Scan Rules - Limits - Flow Control - Links - Build - Browser ID - Log, Index, Cache - Experts Only - Bytes saved - Links scanned - Time - Files written - Transfer rate - Files updated - Connections - Errors - connect - ready - Finishing pending transfers - Select [Cancel] to stop now! - ABORT - Site mirroring finished! - Beware: you local browser might be unable to browse files with embedded filenames - No \'index.html\' file in %s! - Recreated HTTrack internal cached resources - Could not create internal cached resources - \n(Please notify us of any bug or problem)\n\nDevelopment:\nEngine and interfaces: Xavier Roche\nCode, documentation and packaging: many contributors\n\n© Xavier Roche and other contributors\nMANY THANKS for English translations to:\nXavier Roche, Robert Lagadec - Could not get the system external storage directory - Could not write to: - Read-only media (SDCARD) - No storage media (SDCARD) - Invalid project name: it must not contain \'/\', \'\\\' or \'..\' - HTTrack may not be able to download websites until this problem is fixed - HTTrack: mirror \'%s\' stopped! - Click on this notification to restart the interrupted mirror - HTTrack: could not save profile for \'%s\'! - Languages - en,fr,ja - Additional HTTP Headers - X-Magic: 42 - The copy will not send some of these extra headers as you typed them. - Line %1$d: %2$s - the line is not a header - HTTrack sets this header itself - a later line sets the same header, and only the last one is sent - an update or a resume can send its own value instead - doc,docx - application/msword - ↔ - Type/MIME associations - File types - MIME identity - Default referer URL - http://www.example.com/ - Accept headers - The Accept header sent for each kind of link. Automatic sends what the chosen browser sends. - Automatic - Default - Pages - Inline frames - Frames - Images - Style sheets - Scripts - Fonts - Audio - Video - Objects - Embedded content - Chrome default - Firefox default - Old HTTrack value - Make a word database - Build a single-file MHTML archive (.mht) - Base path - Open folder - Grant all-files access - Without all-files access, mirrors are saved in a private folder that other apps and file managers can\'t open. Android also erases that folder if you uninstall HTTrack or clear its storage. Grant access to keep mirrors in a public HTTrack folder instead. - Allow HTTrack all-files access so your mirrors are saved in a public HTTrack folder other apps can open. Without it, mirrors stay in a private folder that Android erases if you uninstall HTTrack or clear its storage. - Mirrors used to be saved in %1$s. Older projects are still there. - Mirrors used to be saved in %1$s. Older projects are still there, and Android erases that folder if you uninstall HTTrack. - Tap to switch back. - Folder path copied to clipboard: %s - FileChooserActivity - Save prefs - … - 0123456789 - Starting worker thread… - Waiting for a network connection… - Creating the project… - Starting the mirror… - The mirror cannot make a self-contained page two ways at once. Untick either \"Build a single-file MHTML archive\" (Log, index, cache) or \"Inline assets as data: URIs\" (Build). - A mirror is already in progress. Wait for it to finish, or stop it, before you start another one. - A copy is running. Wait for it to finish, or stop it, before you delete projects. - The copier engine crashed and cannot be used again until HTTrack restarts. The app closes when you leave this screen; open it again to start a new mirror. - Copy in progress - Shows how far a website copy has got, while it runs. - This copy has stopped and will not start again on its own. Open HTTrack to continue it. - Keep the screen on while copying. On Android 13 and earlier, copying stops when you leave HTTrack. - Load default options - Save default options - Reset to default options - Select default storage - Directory does not exist - Log in with a browser - Log in with a browser - Log in to the site here, then tap \"Use this login\". Google accounts refuse this window. - Use this login - Forget the saved login - The saved login is deleted. This also signs you out of every site in the browser inside this app. - Type a web address first. - Give the project a name first. - This window opens https addresses only. - This site set no cookies, so there is no login to save. - The login could not be saved on this device. - The login could not be saved for this address. - %1$d cookies were too long for the copier and were left out. - The login is saved: %1$d cookies from %2$d hosts.\n\nThe copy sends them with this browser\'s User-Agent. It opens %3$d connections and skips logout links.\n\nA site that uses bot protection can still refuse the copy. - + + + + HTTrack Website Copier + HTTrack Website Copier + Delete + Select project + Options + Welcome to HTTrack Website Copier!\n\nPlease click on the NEXT button to\n\n- start a new project\n- or resume a partial download + Unfinished download: %s. Open the project and select \"Continue interrupted download\". + and %s more + Next + New project + Project name + My Website + Project category + Funny Sites + URL and Settings + Previous + Start + Web Addresses: (URL) + http://www.example.com/funny/ + Options + Cancel + Exit + Exit + View log + Earlier + Browse Mirrored Website + Website Copier/Offline Browser. Copy remote websites to your computer. Free. + Browse sites + New project… + Delete… + Delete + Options + Exclude links + Include link(s) + Use wildcards to exclude or include URLs or links.\nYou can put several scan strings on the same line.\nUse spaces as separators.\n\nExample: +*.zip -www.*.com -www.*.edu/cgi-bin/*.cgi + Maximum link scanning depth + Maximum external depth + Max size of any HTML file (B) + Max size of any non-HTML file + Max site size + Max time + Max transfer rate + Max connections / seconds + Maximum number of links + Try to catch all URLs (even in unknown tags/code) + Get non-HTML files related to a link, eg external .ZIP or pictures + Test all links (even forbidden ones) + Get HTML files first! + Structure type (how links are saved) + Site-structure (default) + Html in web/, images/other files in web/images/ + Html in web/html, images/other in web/images + Html in web/, images/other in web/ + Html in web/, images/other in web/xxx, where xxx is the file extension + Html in web/html, images/other in web/xxx + Site-structure, without www.domain.xxx/ + Html in site_name/, images/other files in site_name/images/ + Html in site_name/html, images/other in site_name/images + Html in site_name/, images/other in site_name/ + Html in site_name/, images/other in site_name/xxx + Html in site_name/html, images/other in site_name/xxx + All files in web/, with random names (gadget !) + All files in site_name/, with random names (gadget !) + User-defined structure.. + DOS names (8+3) + ISO9660 names (CDROM) + No error pages + No external pages + Hide passwords + Hide query strings + Do not purge old files + Inline assets as data: URIs (self-contained pages) + Max size of an inlined asset (bytes) + 10485760 + Write WARC archive + WARC file name + mysite.warc.gz + Report what changed since the previous mirror + Seed the crawl from the site\'s sitemap + Sitemap URL + http://www.example.com/sitemap.xml + Max simultaneous connections + Persistent connections (Keep-Alive) + File timeout + Cancel all links from host if timeout occurs + Retries + Longest wait a server may ask for (seconds) + Minimum admissible transfer rate + Cancel all links from host if too slow + Browser identity + HTML footer + Accept cookies + Check document type + Parse java files + Spider + Update hack (limit re-transfers) + URL hacks (join similar URLs) + Tolerant requests (for servers) + Force old HTTP/1.0 requests (no 1.1) + Never + If unknown (except /) + If unknown + no robots.txt rules + robots.txt except wizard + follow robots.txt rules + Proxy address + Proxy port + Proxy protocol + HTTP + SOCKS5 + HTTP (CONNECT tunnel) + proxy.example.com + 8080 + Cookies file + cookies.txt (Netscape format) + Pause between downloads (sec, MIN:MAX) + 2:5 + Strip query keys + sid,utm_source + Other hostnames of this same site (one per line) + www2.example.com,m.example.com=example.com + Keep www. prefix (www.x != x) + Keep double slashes (//) + Keep query-string order (?b&a) + Mozilla/5.0 (compatible; HTTrack; +https://www.httrack.com/) + <!-- Mirrored from {url} by HTTrack Website Copier/3.x [XR&CO], {date} --> + Use proxy for ftp transfers + Store ALL files in cache + Do not re-download locally erased files + Log files + Make an index + Log file type (if generated) + normal + extended + debug + Use a cache for updates + Primary Scan Rule + Just scan + Store html files + Store non html files + Store all files (default) + Store html files first + Travel mode + Stay in the same directory + Can go down (default) + Can go up + Can both go up & down + Global travel mode + Stay on the same address (default) + Stay on the same domain + Stay on the same top level domain + Go everywhere on the web + Rewrite links: internal / external + Relative URI / Absolute URL (default) + Absolute URL / Absolute URL + Absolute URI / Absolute URL + Original URL / Original URL + Activate Debugging Mode (winhttrack.log) + -* +www.example.com/*.html +*.png +*.webp +*.woff2 + Download web site(s) + Continue interrupted download + Update existing download + User-defined structure + %h%p/%n%q.%t + Help + View Documentation + About HTTrack + Import existing mirrors + Pick the folder that holds your older HTTrack mirrors (for example HTTrack on the internal storage). Their contents will be copied into this app\'s storage; the originals are left untouched. + Importing mirrors… + Building index… + Imported %1$d files (%2$d already present). + Imported %1$d files; %2$d could not be copied. You can run the import again. + Nothing to import from that folder. + Not enough free space to import these mirrors. + Mirrors from an older version can no longer be reached directly. Import them into this app now? + Import + Not now + Don\'t ask again + Go To HTTrack Website + Go To HTTrack Forum + View License + Scan Rules + Limits + Flow Control + Links + Build + Browser ID + Log, Index, Cache + Experts Only + Bytes saved + Links scanned + Time + Files written + Transfer rate + Files updated + Connections + Errors + connect + ready + Finishing pending transfers - Select [Cancel] to stop now! + ABORT + Site mirroring finished! + Beware: you local browser might be unable to browse files with embedded filenames + No \'index.html\' file in %s! + Recreated HTTrack internal cached resources + Could not create internal cached resources + \n(Please notify us of any bug or problem)\n\nDevelopment:\nEngine and interfaces: Xavier Roche\nCode, documentation and packaging: many contributors\n\n© Xavier Roche and other contributors\nMANY THANKS for English translations to:\nXavier Roche, Robert Lagadec + Could not get the system external storage directory + Could not write to: + Read-only media (SDCARD) + No storage media (SDCARD) + Invalid project name: it must not contain \'/\', \'\\\' or \'..\' + HTTrack may not be able to download websites until this problem is fixed + HTTrack: mirror \'%s\' stopped! + Click on this notification to restart the interrupted mirror + HTTrack: could not save profile for \'%s\'! + Languages + en,fr,ja + Additional HTTP Headers + X-Magic: 42 + The copy will not send some of these extra headers as you typed them. + Line %1$d: %2$s + the line is not a header + HTTrack sets this header itself + a later line sets the same header, and only the last one is sent + an update or a resume can send its own value instead + doc,docx + application/msword + ↔ + Type/MIME associations + File types + MIME identity + Default referer URL + http://www.example.com/ + Accept headers + The Accept header sent for each kind of link. Automatic sends what the chosen browser sends. + Automatic + Default + Pages + Inline frames + Frames + Images + Style sheets + Scripts + Fonts + Audio + Video + Objects + Embedded content + Chrome default + Firefox default + Old HTTrack value + Make a word database + Build a single-file MHTML archive (.mht) + Base path + Open folder + Grant all-files access + Without all-files access, mirrors are saved in a private folder that other apps and file managers can\'t open. Android also erases that folder if you uninstall HTTrack or clear its storage. Grant access to keep mirrors in a public HTTrack folder instead. + Allow HTTrack all-files access so your mirrors are saved in a public HTTrack folder other apps can open. Without it, mirrors stay in a private folder that Android erases if you uninstall HTTrack or clear its storage. + Mirrors used to be saved in %1$s. Older projects are still there. + Mirrors used to be saved in %1$s. Older projects are still there, and Android erases that folder if you uninstall HTTrack. + Tap to switch back. + Folder path copied to clipboard: %s + FileChooserActivity + Save prefs + … + 0123456789 + Starting worker thread… + Waiting for a network connection… + Creating the project… + Starting the mirror… + The mirror cannot make a self-contained page two ways at once. Untick either \"Build a single-file MHTML archive\" (Log, index, cache) or \"Inline assets as data: URIs\" (Build). + A mirror is already in progress. Wait for it to finish, or stop it, before you start another one. + A copy is running. Wait for it to finish, or stop it, before you delete projects. + The copier engine crashed and cannot be used again until HTTrack restarts. The app closes when you leave this screen; open it again to start a new mirror. + Copy in progress + Shows how far a website copy has got, while it runs. + This copy has stopped and will not start again on its own. Open HTTrack to continue it. + Keep the screen on while copying. On Android 13 and earlier, copying stops when you leave HTTrack. + Load default options + Save default options + Reset to default options + Select default storage + Directory does not exist + Log in with a browser + Log in with a browser + Log in to the site here, then tap \"Use this login\". Google accounts refuse this window. + Use this login + Forget the saved login + The saved login is deleted. This also signs you out of every site in the browser inside this app. + Type a web address first. + Give the project a name first. + This window opens https addresses only. + This site set no cookies, so there is no login to save. + The login could not be saved on this device. + The login could not be saved for this address. + %1$d cookies were too long for the copier and were left out. + %1$d cookies of the other sites the login went through did not fit and were left out. Sign out of the sites you do not need, then log in again. + The login is saved: %1$d cookies from %2$d hosts.\n\nThe copy sends them with this browser\'s User-Agent. It opens %3$d connections and skips logout links.\n\nA site that uses bot protection can still refuse the copy. + \ No newline at end of file diff --git a/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java b/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java index abe1fa4..6b8b05c 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java @@ -105,17 +105,15 @@ public void theCapsKeepEveryLineUnderTheEnginesLineMax() throws IOException { */ @Test public void theBundleWriterIsAnExportedEngineCallThatReadsItsOwnOutputBack() throws IOException { - assertTrue("the JNI calls the flags entry point now, so this test reads the wrong function", + assertTrue("the JNI calls another entry point now, so this test reads the wrong function", TestSources.read(TestSources.mainFile("jni/htslibjni.c")).contains( - "hts_bundle_from_paste(paste, folder, agent, url")); - assertTrue("hts_bundle_from_paste is no longer exported", - source("httrack-library.h").contains("HTSEXT_API hts_boolean hts_bundle_from_paste(")); + "hts_bundle_from_paste_hosts(paste, folder, agent, url")); + assertTrue("hts_bundle_from_paste_hosts is no longer exported", + source("httrack-library.h").contains( + "HTSEXT_API hts_boolean hts_bundle_from_paste_hosts(")); final String paste = source("htsbundle.c"); - final String wrapper = TestSources.balancedBlock(paste, - TestSources.indexOf(paste, "HTSEXT_API hts_boolean hts_bundle_from_paste(")); - assertTrue(wrapper, wrapper.contains("return hts_bundle_from_paste_flags(")); final String body = TestSources.balancedBlock(paste, - TestSources.indexOf(paste, "HTSEXT_API hts_boolean hts_bundle_from_paste_flags(")); + TestSources.indexOf(paste, "HTSEXT_API hts_boolean hts_bundle_from_paste_hosts(")); // Failing has to be the guard's whole statement, or a bundle the read-back rejected still // reaches the folder. final int read = TestSources.indexOf(body, "!bundle_load_src("); diff --git a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java index f41bdac..07a3725 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java @@ -204,6 +204,30 @@ public void theTypedCredentialsDoNotTravelToAnotherHost() { assertEquals(cleartext, named("https://user:pw@a.example/", cleartext)); } + /** + * The login keeps the cookies of every host that answered with one, so a crawl can + * authenticate on a site the login passed through. + */ + @Test + public void everyHostThatAnsweredWithACookieIsNamed() { + assertEquals("a.example,idp.example", + BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", "idp.example"))); + // The engine's separator is a comma, and nothing else. + assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("a.example"))); + // The order the hosts were asked in, which puts the login's first host first. + assertEquals("b.example,a.example", + BrowserLoginPolicy.keptHosts(Arrays.asList("b.example", "a.example"))); + } + + /** Naming no host asks the engine for the address's own cookies alone, as it did before. */ + @Test + public void namingNoHostKeepsTheEnginesOlderBehaviour() { + assertNull(BrowserLoginPolicy.keptHosts(new ArrayList())); + // A host the cookie query answered for with an empty name carries nothing to keep. + assertNull(BrowserLoginPolicy.keptHosts(Arrays.asList("", null))); + assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("", "a.example"))); + } + /** * The landed address leads, because that is the page to copy. The opened one follows, so a * landed address the engine refuses still leaves a login to save. diff --git a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java index d22fc00..c282dd1 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java @@ -210,6 +210,38 @@ public void deletingAProjectForgetsItsLogin() throws IOException { assertTrue(forget, forget.contains("StoragePaths.savedLoginFiles(")); } + /** + * The JNI call keeps the cookies of the named hosts, which the older entry point could + * not, and carries back the count the engine had no room for. + */ + @Test + public void theJniCallKeepsTheNamedHostsCookies() throws IOException { + final String source = TestSources.jniSource("htslibjni.c"); + + assertTrue(source, source.contains("hts_bundle_from_paste_hosts(paste, folder, agent, url")); + // The older entry point takes no hosts, so a login through a second site lost them. + assertFalse("the entry point without a hosts list keeps no intermediate cookies", + source.contains("hts_bundle_from_paste(paste,")); + assertTrue(source, source.contains("(jint) result.dropped")); + } + + /** + * The two drop counts are never summed. One is a cookie too long to send, the other a jar + * with no room, and the user can act on the second alone. + */ + @Test + public void theTwoDropCountsAreReportedApart() throws IOException { + final String apply = TestSources.balancedBlock( + TestSources.withoutCommentsAndStrings(TestSources.javaSource("HTTrackActivity")), + TestSources.indexOf(TestSources.withoutCommentsAndStrings( + TestSources.javaSource("HTTrackActivity")), "private void applyBrowserLogin(")); + + assertTrue(apply, apply.contains("R.string.browser_login_dropped, droppedCookies")); + assertTrue(apply, apply.contains("R.string.browser_login_no_room, noRoomCookies")); + assertFalse("the two causes are different, so one count cannot stand for both", + apply.contains("droppedCookies + noRoomCookies")); + } + /** * The engine is offered the address the login landed on, and the window keeps trying until * one is accepted. BrowserLoginPolicyTest owns which addresses those are. @@ -222,12 +254,17 @@ public void theBundleIsNamedForTheAddressTheLoginLandedOn() throws IOException { TestSources.indexOf(source, "public void onClickLoginDone(")); assertTrue(done, done.contains("BrowserLoginPolicy.addressesToTry(web.getUrl(), openedUrl)")); // The loop's own address, or every attempt would name the one the window was opened for. - assertTrue(done, done.contains("getUserAgentString(), url);")); + assertTrue(done, done.contains("getUserAgentString(), url,")); // The loop stops at the first address the engine accepts, rather than at the last. assertTrue(done, done.contains("if (bundle != null) {\n break;")); final String create = TestSources.balancedBlock(source, TestSources.indexOf(source, "protected void onCreate(")); assertTrue(create, create.contains("openedUrl = BrowserLoginPolicy.withoutUserInfo(url)")); + /* The hosts that answered, not the hosts asked: a host with no cookie carries nothing + to keep, and naming it would widen the bundle's scope for nothing. */ + assertTrue(done, done.contains("BrowserLoginPolicy.keptHosts(byHost.keySet())")); + // The engine's own count of what it had no room for, which the dialog reads. + assertTrue(done, done.contains("EXTRA_NO_ROOM_COUNT, bundle.dropped")); final String caller = TestSources .withoutCommentsAndStrings(TestSources.javaSource("HTTrackActivity")); final String apply = TestSources.balancedBlock(caller, From 06154bfa48faf673ab238c63a821b54666b1872f Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Wed, 4 Nov 2026 00:24:46 +0100 Subject: [PATCH 2/5] Pin the whole argument list of the bundle call The test matched the function name alone, so passing a NULL hosts list read as the fix and did nothing. That mutant survived. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- .../java/com/httrack/android/BrowserLoginWiringTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java index c282dd1..63ed21e 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java @@ -218,7 +218,10 @@ public void deletingAProjectForgetsItsLogin() throws IOException { public void theJniCallKeepsTheNamedHostsCookies() throws IOException { final String source = TestSources.jniSource("htslibjni.c"); - assertTrue(source, source.contains("hts_bundle_from_paste_hosts(paste, folder, agent, url")); + /* The whole argument list: calling the hosts entry point with a NULL hosts keeps the + older behaviour, and reads as the fix while doing nothing. */ + assertTrue(source, + source.contains("hts_bundle_from_paste_hosts(paste, folder, agent, url, hosts, 0u,")); // The older entry point takes no hosts, so a login through a second site lost them. assertFalse("the entry point without a hosts list keeps no intermediate cookies", source.contains("hts_bundle_from_paste(paste,")); From f1b2d11107bdab2a35833f506ba5bdac4495366f Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Wed, 4 Nov 2026 00:26:03 +0100 Subject: [PATCH 3/5] Cover the host skip where it changes the list Every empty-host case was leading, where skipping it and keeping it build the same string. A trailing one is what leaves a stray comma, and the mutant that drops the skip survived without it. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- .../test/java/com/httrack/android/BrowserLoginPolicyTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java index 07a3725..8bdf456 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java @@ -226,6 +226,10 @@ public void namingNoHostKeepsTheEnginesOlderBehaviour() { // A host the cookie query answered for with an empty name carries nothing to keep. assertNull(BrowserLoginPolicy.keptHosts(Arrays.asList("", null))); assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("", "a.example"))); + // Last, where skipping it is the difference between a clean list and a stray comma. + assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", ""))); + assertEquals("a.example,b.example", + BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", null, "b.example"))); } /** From 0579cfa0c2fcd8aefffd0c6f0f951b54e5bfaa60 Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Wed, 4 Nov 2026 00:26:45 +0100 Subject: [PATCH 4/5] Keep the CRLF line endings of strings.xml The file uses CRLF, and a text-mode rewrite turned all 303 lines over to LF. Only the added string should show in the diff. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- app/src/main/res/values/strings.xml | 608 ++++++++++++++-------------- 1 file changed, 304 insertions(+), 304 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 79e8c45..c95a463 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,305 +1,305 @@ - - - - HTTrack Website Copier - HTTrack Website Copier - Delete - Select project - Options - Welcome to HTTrack Website Copier!\n\nPlease click on the NEXT button to\n\n- start a new project\n- or resume a partial download - Unfinished download: %s. Open the project and select \"Continue interrupted download\". - and %s more - Next - New project - Project name - My Website - Project category - Funny Sites - URL and Settings - Previous - Start - Web Addresses: (URL) - http://www.example.com/funny/ - Options - Cancel - Exit - Exit - View log - Earlier - Browse Mirrored Website - Website Copier/Offline Browser. Copy remote websites to your computer. Free. - Browse sites - New project… - Delete… - Delete - Options - Exclude links - Include link(s) - Use wildcards to exclude or include URLs or links.\nYou can put several scan strings on the same line.\nUse spaces as separators.\n\nExample: +*.zip -www.*.com -www.*.edu/cgi-bin/*.cgi - Maximum link scanning depth - Maximum external depth - Max size of any HTML file (B) - Max size of any non-HTML file - Max site size - Max time - Max transfer rate - Max connections / seconds - Maximum number of links - Try to catch all URLs (even in unknown tags/code) - Get non-HTML files related to a link, eg external .ZIP or pictures - Test all links (even forbidden ones) - Get HTML files first! - Structure type (how links are saved) - Site-structure (default) - Html in web/, images/other files in web/images/ - Html in web/html, images/other in web/images - Html in web/, images/other in web/ - Html in web/, images/other in web/xxx, where xxx is the file extension - Html in web/html, images/other in web/xxx - Site-structure, without www.domain.xxx/ - Html in site_name/, images/other files in site_name/images/ - Html in site_name/html, images/other in site_name/images - Html in site_name/, images/other in site_name/ - Html in site_name/, images/other in site_name/xxx - Html in site_name/html, images/other in site_name/xxx - All files in web/, with random names (gadget !) - All files in site_name/, with random names (gadget !) - User-defined structure.. - DOS names (8+3) - ISO9660 names (CDROM) - No error pages - No external pages - Hide passwords - Hide query strings - Do not purge old files - Inline assets as data: URIs (self-contained pages) - Max size of an inlined asset (bytes) - 10485760 - Write WARC archive - WARC file name - mysite.warc.gz - Report what changed since the previous mirror - Seed the crawl from the site\'s sitemap - Sitemap URL - http://www.example.com/sitemap.xml - Max simultaneous connections - Persistent connections (Keep-Alive) - File timeout - Cancel all links from host if timeout occurs - Retries - Longest wait a server may ask for (seconds) - Minimum admissible transfer rate - Cancel all links from host if too slow - Browser identity - HTML footer - Accept cookies - Check document type - Parse java files - Spider - Update hack (limit re-transfers) - URL hacks (join similar URLs) - Tolerant requests (for servers) - Force old HTTP/1.0 requests (no 1.1) - Never - If unknown (except /) - If unknown - no robots.txt rules - robots.txt except wizard - follow robots.txt rules - Proxy address - Proxy port - Proxy protocol - HTTP - SOCKS5 - HTTP (CONNECT tunnel) - proxy.example.com - 8080 - Cookies file - cookies.txt (Netscape format) - Pause between downloads (sec, MIN:MAX) - 2:5 - Strip query keys - sid,utm_source - Other hostnames of this same site (one per line) - www2.example.com,m.example.com=example.com - Keep www. prefix (www.x != x) - Keep double slashes (//) - Keep query-string order (?b&a) - Mozilla/5.0 (compatible; HTTrack; +https://www.httrack.com/) - <!-- Mirrored from {url} by HTTrack Website Copier/3.x [XR&CO], {date} --> - Use proxy for ftp transfers - Store ALL files in cache - Do not re-download locally erased files - Log files - Make an index - Log file type (if generated) - normal - extended - debug - Use a cache for updates - Primary Scan Rule - Just scan - Store html files - Store non html files - Store all files (default) - Store html files first - Travel mode - Stay in the same directory - Can go down (default) - Can go up - Can both go up & down - Global travel mode - Stay on the same address (default) - Stay on the same domain - Stay on the same top level domain - Go everywhere on the web - Rewrite links: internal / external - Relative URI / Absolute URL (default) - Absolute URL / Absolute URL - Absolute URI / Absolute URL - Original URL / Original URL - Activate Debugging Mode (winhttrack.log) - -* +www.example.com/*.html +*.png +*.webp +*.woff2 - Download web site(s) - Continue interrupted download - Update existing download - User-defined structure - %h%p/%n%q.%t - Help - View Documentation - About HTTrack - Import existing mirrors - Pick the folder that holds your older HTTrack mirrors (for example HTTrack on the internal storage). Their contents will be copied into this app\'s storage; the originals are left untouched. - Importing mirrors… - Building index… - Imported %1$d files (%2$d already present). - Imported %1$d files; %2$d could not be copied. You can run the import again. - Nothing to import from that folder. - Not enough free space to import these mirrors. - Mirrors from an older version can no longer be reached directly. Import them into this app now? - Import - Not now - Don\'t ask again - Go To HTTrack Website - Go To HTTrack Forum - View License - Scan Rules - Limits - Flow Control - Links - Build - Browser ID - Log, Index, Cache - Experts Only - Bytes saved - Links scanned - Time - Files written - Transfer rate - Files updated - Connections - Errors - connect - ready - Finishing pending transfers - Select [Cancel] to stop now! - ABORT - Site mirroring finished! - Beware: you local browser might be unable to browse files with embedded filenames - No \'index.html\' file in %s! - Recreated HTTrack internal cached resources - Could not create internal cached resources - \n(Please notify us of any bug or problem)\n\nDevelopment:\nEngine and interfaces: Xavier Roche\nCode, documentation and packaging: many contributors\n\n© Xavier Roche and other contributors\nMANY THANKS for English translations to:\nXavier Roche, Robert Lagadec - Could not get the system external storage directory - Could not write to: - Read-only media (SDCARD) - No storage media (SDCARD) - Invalid project name: it must not contain \'/\', \'\\\' or \'..\' - HTTrack may not be able to download websites until this problem is fixed - HTTrack: mirror \'%s\' stopped! - Click on this notification to restart the interrupted mirror - HTTrack: could not save profile for \'%s\'! - Languages - en,fr,ja - Additional HTTP Headers - X-Magic: 42 - The copy will not send some of these extra headers as you typed them. - Line %1$d: %2$s - the line is not a header - HTTrack sets this header itself - a later line sets the same header, and only the last one is sent - an update or a resume can send its own value instead - doc,docx - application/msword - ↔ - Type/MIME associations - File types - MIME identity - Default referer URL - http://www.example.com/ - Accept headers - The Accept header sent for each kind of link. Automatic sends what the chosen browser sends. - Automatic - Default - Pages - Inline frames - Frames - Images - Style sheets - Scripts - Fonts - Audio - Video - Objects - Embedded content - Chrome default - Firefox default - Old HTTrack value - Make a word database - Build a single-file MHTML archive (.mht) - Base path - Open folder - Grant all-files access - Without all-files access, mirrors are saved in a private folder that other apps and file managers can\'t open. Android also erases that folder if you uninstall HTTrack or clear its storage. Grant access to keep mirrors in a public HTTrack folder instead. - Allow HTTrack all-files access so your mirrors are saved in a public HTTrack folder other apps can open. Without it, mirrors stay in a private folder that Android erases if you uninstall HTTrack or clear its storage. - Mirrors used to be saved in %1$s. Older projects are still there. - Mirrors used to be saved in %1$s. Older projects are still there, and Android erases that folder if you uninstall HTTrack. - Tap to switch back. - Folder path copied to clipboard: %s - FileChooserActivity - Save prefs - … - 0123456789 - Starting worker thread… - Waiting for a network connection… - Creating the project… - Starting the mirror… - The mirror cannot make a self-contained page two ways at once. Untick either \"Build a single-file MHTML archive\" (Log, index, cache) or \"Inline assets as data: URIs\" (Build). - A mirror is already in progress. Wait for it to finish, or stop it, before you start another one. - A copy is running. Wait for it to finish, or stop it, before you delete projects. - The copier engine crashed and cannot be used again until HTTrack restarts. The app closes when you leave this screen; open it again to start a new mirror. - Copy in progress - Shows how far a website copy has got, while it runs. - This copy has stopped and will not start again on its own. Open HTTrack to continue it. - Keep the screen on while copying. On Android 13 and earlier, copying stops when you leave HTTrack. - Load default options - Save default options - Reset to default options - Select default storage - Directory does not exist - Log in with a browser - Log in with a browser - Log in to the site here, then tap \"Use this login\". Google accounts refuse this window. - Use this login - Forget the saved login - The saved login is deleted. This also signs you out of every site in the browser inside this app. - Type a web address first. - Give the project a name first. - This window opens https addresses only. - This site set no cookies, so there is no login to save. - The login could not be saved on this device. - The login could not be saved for this address. - %1$d cookies were too long for the copier and were left out. - %1$d cookies of the other sites the login went through did not fit and were left out. Sign out of the sites you do not need, then log in again. - The login is saved: %1$d cookies from %2$d hosts.\n\nThe copy sends them with this browser\'s User-Agent. It opens %3$d connections and skips logout links.\n\nA site that uses bot protection can still refuse the copy. - + + + + HTTrack Website Copier + HTTrack Website Copier + Delete + Select project + Options + Welcome to HTTrack Website Copier!\n\nPlease click on the NEXT button to\n\n- start a new project\n- or resume a partial download + Unfinished download: %s. Open the project and select \"Continue interrupted download\". + and %s more + Next + New project + Project name + My Website + Project category + Funny Sites + URL and Settings + Previous + Start + Web Addresses: (URL) + http://www.example.com/funny/ + Options + Cancel + Exit + Exit + View log + Earlier + Browse Mirrored Website + Website Copier/Offline Browser. Copy remote websites to your computer. Free. + Browse sites + New project… + Delete… + Delete + Options + Exclude links + Include link(s) + Use wildcards to exclude or include URLs or links.\nYou can put several scan strings on the same line.\nUse spaces as separators.\n\nExample: +*.zip -www.*.com -www.*.edu/cgi-bin/*.cgi + Maximum link scanning depth + Maximum external depth + Max size of any HTML file (B) + Max size of any non-HTML file + Max site size + Max time + Max transfer rate + Max connections / seconds + Maximum number of links + Try to catch all URLs (even in unknown tags/code) + Get non-HTML files related to a link, eg external .ZIP or pictures + Test all links (even forbidden ones) + Get HTML files first! + Structure type (how links are saved) + Site-structure (default) + Html in web/, images/other files in web/images/ + Html in web/html, images/other in web/images + Html in web/, images/other in web/ + Html in web/, images/other in web/xxx, where xxx is the file extension + Html in web/html, images/other in web/xxx + Site-structure, without www.domain.xxx/ + Html in site_name/, images/other files in site_name/images/ + Html in site_name/html, images/other in site_name/images + Html in site_name/, images/other in site_name/ + Html in site_name/, images/other in site_name/xxx + Html in site_name/html, images/other in site_name/xxx + All files in web/, with random names (gadget !) + All files in site_name/, with random names (gadget !) + User-defined structure.. + DOS names (8+3) + ISO9660 names (CDROM) + No error pages + No external pages + Hide passwords + Hide query strings + Do not purge old files + Inline assets as data: URIs (self-contained pages) + Max size of an inlined asset (bytes) + 10485760 + Write WARC archive + WARC file name + mysite.warc.gz + Report what changed since the previous mirror + Seed the crawl from the site\'s sitemap + Sitemap URL + http://www.example.com/sitemap.xml + Max simultaneous connections + Persistent connections (Keep-Alive) + File timeout + Cancel all links from host if timeout occurs + Retries + Longest wait a server may ask for (seconds) + Minimum admissible transfer rate + Cancel all links from host if too slow + Browser identity + HTML footer + Accept cookies + Check document type + Parse java files + Spider + Update hack (limit re-transfers) + URL hacks (join similar URLs) + Tolerant requests (for servers) + Force old HTTP/1.0 requests (no 1.1) + Never + If unknown (except /) + If unknown + no robots.txt rules + robots.txt except wizard + follow robots.txt rules + Proxy address + Proxy port + Proxy protocol + HTTP + SOCKS5 + HTTP (CONNECT tunnel) + proxy.example.com + 8080 + Cookies file + cookies.txt (Netscape format) + Pause between downloads (sec, MIN:MAX) + 2:5 + Strip query keys + sid,utm_source + Other hostnames of this same site (one per line) + www2.example.com,m.example.com=example.com + Keep www. prefix (www.x != x) + Keep double slashes (//) + Keep query-string order (?b&a) + Mozilla/5.0 (compatible; HTTrack; +https://www.httrack.com/) + <!-- Mirrored from {url} by HTTrack Website Copier/3.x [XR&CO], {date} --> + Use proxy for ftp transfers + Store ALL files in cache + Do not re-download locally erased files + Log files + Make an index + Log file type (if generated) + normal + extended + debug + Use a cache for updates + Primary Scan Rule + Just scan + Store html files + Store non html files + Store all files (default) + Store html files first + Travel mode + Stay in the same directory + Can go down (default) + Can go up + Can both go up & down + Global travel mode + Stay on the same address (default) + Stay on the same domain + Stay on the same top level domain + Go everywhere on the web + Rewrite links: internal / external + Relative URI / Absolute URL (default) + Absolute URL / Absolute URL + Absolute URI / Absolute URL + Original URL / Original URL + Activate Debugging Mode (winhttrack.log) + -* +www.example.com/*.html +*.png +*.webp +*.woff2 + Download web site(s) + Continue interrupted download + Update existing download + User-defined structure + %h%p/%n%q.%t + Help + View Documentation + About HTTrack + Import existing mirrors + Pick the folder that holds your older HTTrack mirrors (for example HTTrack on the internal storage). Their contents will be copied into this app\'s storage; the originals are left untouched. + Importing mirrors… + Building index… + Imported %1$d files (%2$d already present). + Imported %1$d files; %2$d could not be copied. You can run the import again. + Nothing to import from that folder. + Not enough free space to import these mirrors. + Mirrors from an older version can no longer be reached directly. Import them into this app now? + Import + Not now + Don\'t ask again + Go To HTTrack Website + Go To HTTrack Forum + View License + Scan Rules + Limits + Flow Control + Links + Build + Browser ID + Log, Index, Cache + Experts Only + Bytes saved + Links scanned + Time + Files written + Transfer rate + Files updated + Connections + Errors + connect + ready + Finishing pending transfers - Select [Cancel] to stop now! + ABORT + Site mirroring finished! + Beware: you local browser might be unable to browse files with embedded filenames + No \'index.html\' file in %s! + Recreated HTTrack internal cached resources + Could not create internal cached resources + \n(Please notify us of any bug or problem)\n\nDevelopment:\nEngine and interfaces: Xavier Roche\nCode, documentation and packaging: many contributors\n\n© Xavier Roche and other contributors\nMANY THANKS for English translations to:\nXavier Roche, Robert Lagadec + Could not get the system external storage directory + Could not write to: + Read-only media (SDCARD) + No storage media (SDCARD) + Invalid project name: it must not contain \'/\', \'\\\' or \'..\' + HTTrack may not be able to download websites until this problem is fixed + HTTrack: mirror \'%s\' stopped! + Click on this notification to restart the interrupted mirror + HTTrack: could not save profile for \'%s\'! + Languages + en,fr,ja + Additional HTTP Headers + X-Magic: 42 + The copy will not send some of these extra headers as you typed them. + Line %1$d: %2$s + the line is not a header + HTTrack sets this header itself + a later line sets the same header, and only the last one is sent + an update or a resume can send its own value instead + doc,docx + application/msword + ↔ + Type/MIME associations + File types + MIME identity + Default referer URL + http://www.example.com/ + Accept headers + The Accept header sent for each kind of link. Automatic sends what the chosen browser sends. + Automatic + Default + Pages + Inline frames + Frames + Images + Style sheets + Scripts + Fonts + Audio + Video + Objects + Embedded content + Chrome default + Firefox default + Old HTTrack value + Make a word database + Build a single-file MHTML archive (.mht) + Base path + Open folder + Grant all-files access + Without all-files access, mirrors are saved in a private folder that other apps and file managers can\'t open. Android also erases that folder if you uninstall HTTrack or clear its storage. Grant access to keep mirrors in a public HTTrack folder instead. + Allow HTTrack all-files access so your mirrors are saved in a public HTTrack folder other apps can open. Without it, mirrors stay in a private folder that Android erases if you uninstall HTTrack or clear its storage. + Mirrors used to be saved in %1$s. Older projects are still there. + Mirrors used to be saved in %1$s. Older projects are still there, and Android erases that folder if you uninstall HTTrack. + Tap to switch back. + Folder path copied to clipboard: %s + FileChooserActivity + Save prefs + … + 0123456789 + Starting worker thread… + Waiting for a network connection… + Creating the project… + Starting the mirror… + The mirror cannot make a self-contained page two ways at once. Untick either \"Build a single-file MHTML archive\" (Log, index, cache) or \"Inline assets as data: URIs\" (Build). + A mirror is already in progress. Wait for it to finish, or stop it, before you start another one. + A copy is running. Wait for it to finish, or stop it, before you delete projects. + The copier engine crashed and cannot be used again until HTTrack restarts. The app closes when you leave this screen; open it again to start a new mirror. + Copy in progress + Shows how far a website copy has got, while it runs. + This copy has stopped and will not start again on its own. Open HTTrack to continue it. + Keep the screen on while copying. On Android 13 and earlier, copying stops when you leave HTTrack. + Load default options + Save default options + Reset to default options + Select default storage + Directory does not exist + Log in with a browser + Log in with a browser + Log in to the site here, then tap \"Use this login\". Google accounts refuse this window. + Use this login + Forget the saved login + The saved login is deleted. This also signs you out of every site in the browser inside this app. + Type a web address first. + Give the project a name first. + This window opens https addresses only. + This site set no cookies, so there is no login to save. + The login could not be saved on this device. + The login could not be saved for this address. + %1$d cookies were too long for the copier and were left out. + %1$d cookies of the other sites the login went through did not fit and were left out. Sign out of the sites you do not need, then log in again. + The login is saved: %1$d cookies from %2$d hosts.\n\nThe copy sends them with this browser\'s User-Agent. It opens %3$d connections and skips logout links.\n\nA site that uses bot protection can still refuse the copy. + \ No newline at end of file From b465aae2ac93f7e574e57945ab18a5b2059a948a Mon Sep 17 00:00:00 2001 From: Xavier Roche Date: Wed, 4 Nov 2026 00:36:34 +0100 Subject: [PATCH 5/5] Drop the no-room count the engine cannot report here The engine leaves a named host out when its jar fills or a cookie is too long. Neither can happen from this app, and two existing tests already pin why: MAX_PAIR equals the engine COOKIE_MAX_PAIR and tests the same joint guard, and MAX_BYTES is below COOKIE_MAX_JAR. So the count is always zero and the message was unreachable. That removes a string nobody can see, its branch, the extra, the LoginBundle field and the JNI pass-through. The engine keeps the field for WinHTTrack, whose capture is a whole browser profile. Also drop the empty-host skip in keptHosts, which the one caller cannot reach, and an asserted host order the engine ignores. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Xavier Roche --- .../httrack/android/BrowserLoginActivity.java | 3 -- .../httrack/android/BrowserLoginPolicy.java | 11 ++---- .../com/httrack/android/HTTrackActivity.java | 14 ++----- .../com/httrack/android/jni/HTTrackLib.java | 7 +--- app/src/main/jni/htslibjni.c | 19 ++++------ app/src/main/res/values/strings.xml | 1 - .../android/BrowserLoginEngineLimitsTest.java | 13 +++++-- .../android/BrowserLoginPolicyTest.java | 10 ----- .../android/BrowserLoginWiringTest.java | 37 ------------------- 9 files changed, 25 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/com/httrack/android/BrowserLoginActivity.java b/app/src/main/java/com/httrack/android/BrowserLoginActivity.java index e1e8311..8b087a1 100644 --- a/app/src/main/java/com/httrack/android/BrowserLoginActivity.java +++ b/app/src/main/java/com/httrack/android/BrowserLoginActivity.java @@ -76,7 +76,6 @@ public class BrowserLoginActivity extends FragmentActivity { static final String EXTRA_COOKIE_COUNT = "com.httrack.android.cookieCount"; static final String EXTRA_HOST_COUNT = "com.httrack.android.hostCount"; static final String EXTRA_DROPPED_COUNT = "com.httrack.android.droppedCount"; - static final String EXTRA_NO_ROOM_COUNT = "com.httrack.android.noRoomCount"; private WebView web; @@ -367,8 +366,6 @@ public void onClickLoginDone(final View view) { data.putExtra(EXTRA_HOST_COUNT, bundle.hosts); // Ours, and only the over-long ones, which is what the message the user reads says. data.putExtra(EXTRA_DROPPED_COUNT, result.dropped); - // The engine's, which are the ones its jar had no room for. - data.putExtra(EXTRA_NO_ROOM_COUNT, bundle.dropped); setResult(RESULT_OK, data); finish(); } diff --git a/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java b/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java index 2829b5a..68b68c8 100644 --- a/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java +++ b/app/src/main/java/com/httrack/android/BrowserLoginPolicy.java @@ -98,22 +98,17 @@ static String hostOf(final String url) { } /** - * The hosts whose cookies the login keeps, as the engine's comma-separated list. A host - * gets a scope line for its registrable domain and never becomes a crawl root, so the - * crawl can authenticate on a site the login passed through without copying it. + * Joins HOSTS into the comma-separated list the engine keeps cookies for. * * @param hosts * the hosts that answered with a cookie, in the order they were asked - * @return the list, or null when no host answered, which keeps the engine's older - * behaviour of taking the address's own cookies alone + * @return the list, or null for none, which leaves the address's own cookies alone */ static String keptHosts(final Collection hosts) { final StringBuilder list = new StringBuilder(); for (final String host : hosts) { - if (host != null && host.length() != 0) { - list.append(list.length() == 0 ? "" : ",").append(host); - } + list.append(list.length() == 0 ? "" : ",").append(host); } return list.length() == 0 ? null : list.toString(); } diff --git a/app/src/main/java/com/httrack/android/HTTrackActivity.java b/app/src/main/java/com/httrack/android/HTTrackActivity.java index dcbb6cf..9697ce6 100755 --- a/app/src/main/java/com/httrack/android/HTTrackActivity.java +++ b/app/src/main/java/com/httrack/android/HTTrackActivity.java @@ -2955,21 +2955,13 @@ private void applyBrowserLogin(final Intent data) { BrowserLoginPolicy.sockets(getMap(R.id.editNumberOfConnections))); setMap(R.id.editRules, BrowserLoginPolicy.rules(getMap(R.id.editRules))); final int droppedCookies = data.getIntExtra(BrowserLoginActivity.EXTRA_DROPPED_COUNT, 0); - final int noRoomCookies = data.getIntExtra(BrowserLoginActivity.EXTRA_NO_ROOM_COUNT, 0); final String saved = getString(R.string.browser_login_saved, data.getIntExtra(BrowserLoginActivity.EXTRA_COOKIE_COUNT, 0), data.getIntExtra(BrowserLoginActivity.EXTRA_HOST_COUNT, 0), BrowserLoginPolicy.MAX_SOCKETS); - /* The two counts have different causes and different answers, so they are never summed: - ours is a cookie too long to send, the engine's is a jar with no room left. */ - String message = saved; - - if (droppedCookies != 0) { - message += "\n\n" + getString(R.string.browser_login_dropped, droppedCookies); - } - if (noRoomCookies != 0) { - message += "\n\n" + getString(R.string.browser_login_no_room, noRoomCookies); - } + // The engine drops a cookie its jar refuses, so saying only how many it kept is a lie. + final String message = droppedCookies == 0 ? saved + : saved + "\n\n" + getString(R.string.browser_login_dropped, droppedCookies); new AlertDialog.Builder(this).setMessage(message) .setPositiveButton(android.R.string.ok, null).show(); } diff --git a/app/src/main/java/com/httrack/android/jni/HTTrackLib.java b/app/src/main/java/com/httrack/android/jni/HTTrackLib.java index 9cc79a3..d7515bf 100755 --- a/app/src/main/java/com/httrack/android/jni/HTTrackLib.java +++ b/app/src/main/java/com/httrack/android/jni/HTTrackLib.java @@ -133,15 +133,10 @@ public static final class LoginBundle { /** Hosts those cookies cover, which is not every host the login visited. */ public final int hosts; - /** Cookies of the named hosts the engine had no room for, so the crawl will not send them. */ - public final int dropped; - - public LoginBundle(final String url, final int cookies, final int hosts, - final int dropped) { + public LoginBundle(final String url, final int cookies, final int hosts) { this.url = url; this.cookies = cookies; this.hosts = hosts; - this.dropped = dropped; } } diff --git a/app/src/main/jni/htslibjni.c b/app/src/main/jni/htslibjni.c index 0362758..f127f53 100755 --- a/app/src/main/jni/htslibjni.c +++ b/app/src/main/jni/htslibjni.c @@ -660,16 +660,15 @@ JNICALL jint Java_com_httrack_android_jni_HTTrackLib_getCustomHeaderVerdict(JNIE /* A new HTTrackLib.LoginBundle, or NULL with the exception pending. Looked up * here rather than cached, because one login writes one bundle. */ -static jobject newLoginBundle(JNIEnv* env, jstring url, jint cookies, jint hosts, - jint dropped) { +static jobject newLoginBundle(JNIEnv* env, jstring url, jint cookies, jint hosts) { jclass cls = (*env)->FindClass(env, "com/httrack/android/jni/HTTrackLib$LoginBundle"); jmethodID cons = cls != NULL - ? (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;III)V") + ? (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;II)V") : NULL; return cons != NULL - ? (*env)->NewObject(env, cls, cons, url, cookies, hosts, dropped) + ? (*env)->NewObject(env, cls, cons, url, cookies, hosts) : NULL; } @@ -700,10 +699,10 @@ JNICALL jobject Java_com_httrack_android_jni_HTTrackLib_writeLoginBundle(JNIEnv* jstring ourl, jstring ohosts) { const char *paste, *folder, *agent, *url; - /* NULL is a value here, not a mistake: it asks the engine for the address's - own cookies alone, as the bundle writer did before the hosts list. */ + /* NULL is a value, not a mistake. It asks for the address's own cookies + alone. */ const char* hosts = NULL; - hts_boolean got_hosts; + int got_hosts; jobject bundle = NULL; UNUSED(clazz); @@ -731,16 +730,14 @@ JNICALL jobject Java_com_httrack_android_jni_HTTrackLib_writeLoginBundle(JNIEnv* memset(&result, 0, sizeof(result)); result.size = sizeof(result); - /* The hosts keep their cookies through a scope line each, and none of them - becomes a start URL. */ + /* 0u: no HTS_PASTE_* flag. */ if (hts_bundle_from_paste_hosts(paste, folder, agent, url, hosts, 0u, &result)) { jstring written = (*env)->NewStringUTF(env, result.url); if (written != NULL) { bundle = newLoginBundle(env, written, (jint) result.cookies, - countBundleHosts(result.hosts), - (jint) result.dropped); + countBundleHosts(result.hosts)); } } else { /* The reason is unlocalized English, so the user gets our own message. */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c95a463..1d6a7b4 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -299,7 +299,6 @@ The login could not be saved on this device. The login could not be saved for this address. %1$d cookies were too long for the copier and were left out. - %1$d cookies of the other sites the login went through did not fit and were left out. Sign out of the sites you do not need, then log in again. The login is saved: %1$d cookies from %2$d hosts.\n\nThe copy sends them with this browser\'s User-Agent. It opens %3$d connections and skips logout links.\n\nA site that uses bot protection can still refuse the copy. \ No newline at end of file diff --git a/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java b/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java index 6b8b05c..deeafb7 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginEngineLimitsTest.java @@ -1,6 +1,7 @@ package com.httrack.android; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; @@ -105,9 +106,15 @@ public void theCapsKeepEveryLineUnderTheEnginesLineMax() throws IOException { */ @Test public void theBundleWriterIsAnExportedEngineCallThatReadsItsOwnOutputBack() throws IOException { - assertTrue("the JNI calls another entry point now, so this test reads the wrong function", - TestSources.read(TestSources.mainFile("jni/htslibjni.c")).contains( - "hts_bundle_from_paste_hosts(paste, folder, agent, url")); + /* Comments and spacing stripped, so reflowing the call cannot fail this. The argument + list is pinned whole, because the same call with a NULL hosts keeps the older + behaviour and reads as the fix. */ + final String jni = TestSources.withoutCommentsAndStrings( + TestSources.read(TestSources.mainFile("jni/htslibjni.c"))).replaceAll("\\s+", " "); + assertTrue(jni, jni.contains( + "hts_bundle_from_paste_hosts(paste, folder, agent, url, hosts, 0u, &result)")); + assertFalse("the entry point without a hosts list keeps no intermediate cookies", + jni.contains("hts_bundle_from_paste(paste,")); assertTrue("hts_bundle_from_paste_hosts is no longer exported", source("httrack-library.h").contains( "HTSEXT_API hts_boolean hts_bundle_from_paste_hosts(")); diff --git a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java index 8bdf456..6e69997 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginPolicyTest.java @@ -214,22 +214,12 @@ public void everyHostThatAnsweredWithACookieIsNamed() { BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", "idp.example"))); // The engine's separator is a comma, and nothing else. assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("a.example"))); - // The order the hosts were asked in, which puts the login's first host first. - assertEquals("b.example,a.example", - BrowserLoginPolicy.keptHosts(Arrays.asList("b.example", "a.example"))); } /** Naming no host asks the engine for the address's own cookies alone, as it did before. */ @Test public void namingNoHostKeepsTheEnginesOlderBehaviour() { assertNull(BrowserLoginPolicy.keptHosts(new ArrayList())); - // A host the cookie query answered for with an empty name carries nothing to keep. - assertNull(BrowserLoginPolicy.keptHosts(Arrays.asList("", null))); - assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("", "a.example"))); - // Last, where skipping it is the difference between a clean list and a stray comma. - assertEquals("a.example", BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", ""))); - assertEquals("a.example,b.example", - BrowserLoginPolicy.keptHosts(Arrays.asList("a.example", null, "b.example"))); } /** diff --git a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java index 63ed21e..8c59ae0 100644 --- a/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java +++ b/app/src/test/java/com/httrack/android/BrowserLoginWiringTest.java @@ -210,41 +210,6 @@ public void deletingAProjectForgetsItsLogin() throws IOException { assertTrue(forget, forget.contains("StoragePaths.savedLoginFiles(")); } - /** - * The JNI call keeps the cookies of the named hosts, which the older entry point could - * not, and carries back the count the engine had no room for. - */ - @Test - public void theJniCallKeepsTheNamedHostsCookies() throws IOException { - final String source = TestSources.jniSource("htslibjni.c"); - - /* The whole argument list: calling the hosts entry point with a NULL hosts keeps the - older behaviour, and reads as the fix while doing nothing. */ - assertTrue(source, - source.contains("hts_bundle_from_paste_hosts(paste, folder, agent, url, hosts, 0u,")); - // The older entry point takes no hosts, so a login through a second site lost them. - assertFalse("the entry point without a hosts list keeps no intermediate cookies", - source.contains("hts_bundle_from_paste(paste,")); - assertTrue(source, source.contains("(jint) result.dropped")); - } - - /** - * The two drop counts are never summed. One is a cookie too long to send, the other a jar - * with no room, and the user can act on the second alone. - */ - @Test - public void theTwoDropCountsAreReportedApart() throws IOException { - final String apply = TestSources.balancedBlock( - TestSources.withoutCommentsAndStrings(TestSources.javaSource("HTTrackActivity")), - TestSources.indexOf(TestSources.withoutCommentsAndStrings( - TestSources.javaSource("HTTrackActivity")), "private void applyBrowserLogin(")); - - assertTrue(apply, apply.contains("R.string.browser_login_dropped, droppedCookies")); - assertTrue(apply, apply.contains("R.string.browser_login_no_room, noRoomCookies")); - assertFalse("the two causes are different, so one count cannot stand for both", - apply.contains("droppedCookies + noRoomCookies")); - } - /** * The engine is offered the address the login landed on, and the window keeps trying until * one is accepted. BrowserLoginPolicyTest owns which addresses those are. @@ -266,8 +231,6 @@ public void theBundleIsNamedForTheAddressTheLoginLandedOn() throws IOException { /* The hosts that answered, not the hosts asked: a host with no cookie carries nothing to keep, and naming it would widen the bundle's scope for nothing. */ assertTrue(done, done.contains("BrowserLoginPolicy.keptHosts(byHost.keySet())")); - // The engine's own count of what it had no room for, which the dialog reads. - assertTrue(done, done.contains("EXTRA_NO_ROOM_COUNT, bundle.dropped")); final String caller = TestSources .withoutCommentsAndStrings(TestSources.javaSource("HTTrackActivity")); final String apply = TestSources.balancedBlock(caller,