Start the printer's admin login at the root and default to /home/status.html: this firmware has no /general/status.html behind the login (issue 1514)
This commit is contained in:
@@ -19,8 +19,10 @@ type Admin struct {
|
||||
HTTP *http.Client
|
||||
}
|
||||
|
||||
// loginPath is a page that needs a login: asking for it is answered by the login page.
|
||||
const loginPath = "/general/status.html"
|
||||
// loginStart is where a login starts: the root, which the printer answers with a redirect to the page it serves
|
||||
// its login form on (/home/status.html). Asking for a page behind the login instead is answered by a redirect
|
||||
// to an error page, and a loginurl naming a page this firmware lacks is a 404 after the login (issue 1514).
|
||||
const loginStart = "/"
|
||||
|
||||
// maxPage bounds a page of the admin site.
|
||||
const maxPage = 4 << 20
|
||||
@@ -93,17 +95,26 @@ func passwordForm(p Page) (*Form, *Field) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Login asks for a page that needs one, reads the login form it is answered with, and posts the form's own
|
||||
// fields with the password in the field the form names. A login is failed when the answer is a login page
|
||||
// again.
|
||||
func (a *Admin) Login(ctx context.Context) error {
|
||||
body, at, err := a.fetch(ctx, http.MethodGet, a.url(loginPath), nil)
|
||||
func hasField(f *Form, name string) bool {
|
||||
for _, fl := range f.Fields {
|
||||
if fl.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Login asks for the printer's login page, reads its login form, and posts the form's own fields with the
|
||||
// password in the field the form names and the form's loginurl set to the page that will be asked for. A login
|
||||
// is failed when the answer is a login page again.
|
||||
func (a *Admin) Login(ctx context.Context, page string) error {
|
||||
body, at, err := a.fetch(ctx, http.MethodGet, a.url(loginStart), nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the printer's admin page cannot be asked for its login: %w", err)
|
||||
}
|
||||
form, pw := passwordForm(ParsePage(body))
|
||||
if form == nil {
|
||||
return fmt.Errorf("the printer's answer to %s holds no login form with a password field", loginPath)
|
||||
return fmt.Errorf("the printer's answer to %s holds no login form with a password field", at.Path)
|
||||
}
|
||||
target, err := at.Parse(form.Action)
|
||||
if err != nil {
|
||||
@@ -118,12 +129,17 @@ func (a *Admin) Login(ctx context.Context) error {
|
||||
vals.Set(f.Name, f.Value)
|
||||
}
|
||||
}
|
||||
if _, ok := vals["loginurl"]; ok {
|
||||
vals.Set("loginurl", page)
|
||||
}
|
||||
vals.Set(pw.Name, a.Password)
|
||||
answer, _, err := a.fetch(ctx, http.MethodPost, target.String(), vals)
|
||||
if err != nil {
|
||||
return fmt.Errorf("the login was not answered: %w", err)
|
||||
}
|
||||
if again, _ := passwordForm(ParsePage(answer)); again != nil {
|
||||
// A login page again is a form with a password field and the loginurl field; a page that was asked for may
|
||||
// hold a password field of its own.
|
||||
if again, _ := passwordForm(ParsePage(answer)); again != nil && hasField(again, "loginurl") {
|
||||
return fmt.Errorf("the login failed: the printer showed its login page again, so the secret admin-password is not its password")
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -44,8 +44,13 @@ func newFakePrinter(t *testing.T, field, password string) *fakePrinter {
|
||||
t.Helper()
|
||||
f := &fakePrinter{field: field, password: password, loggedIn: map[string]bool{}, pages: map[string]string{}}
|
||||
login := strings.ReplaceAll(adminFixture(t, "login-page.html"), "B1891", field)
|
||||
// The login page the printer serves at /home/status.html carries that page as its loginurl (captured live
|
||||
// 2026-10-20, testdata/admin/live/home_status_html.body); the page it serves at /etc/loginerror.html carries
|
||||
// the page that was asked for.
|
||||
homeLogin := strings.ReplaceAll(login, `value="/general/status.html"`, `value="/home/status.html"`)
|
||||
synthetic := adminFixture(t, "synthetic-status.html")
|
||||
f.pages["/general/status.html"] = synthetic
|
||||
// This firmware has no /general/status.html behind the login (issue 1514): asking for it is a 404.
|
||||
f.pages["/home/status.html"] = synthetic
|
||||
f.pages["/general/other.html"] = adminFixture(t, "synthetic-settings.html")
|
||||
authed := func(r *http.Request) bool {
|
||||
c, err := r.Cookie("session")
|
||||
@@ -64,7 +69,7 @@ func newFakePrinter(t *testing.T, field, password string) *fakePrinter {
|
||||
w.Write([]byte(synthetic))
|
||||
return
|
||||
}
|
||||
w.Write([]byte(login))
|
||||
w.Write([]byte(homeLogin))
|
||||
return
|
||||
}
|
||||
r.ParseForm()
|
||||
@@ -76,7 +81,8 @@ func newFakePrinter(t *testing.T, field, password string) *fakePrinter {
|
||||
f.session = "s" + field
|
||||
f.loggedIn[f.session] = true
|
||||
http.SetCookie(w, &http.Cookie{Name: "session", Value: f.session, Path: "/"})
|
||||
w.Write([]byte(synthetic))
|
||||
// Like Brother's form, a good login goes on to the page named in loginurl.
|
||||
http.Redirect(w, r, r.PostForm.Get("loginurl"), http.StatusFound)
|
||||
})
|
||||
mux.HandleFunc("/home/logout.html", func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
@@ -89,16 +95,21 @@ func newFakePrinter(t *testing.T, field, password string) *fakePrinter {
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/home/status.html", http.StatusMovedPermanently)
|
||||
return
|
||||
}
|
||||
f.requests = append(f.requests, r.Method+" "+r.URL.Path)
|
||||
// Captured: a page behind the login is answered by a redirect to the login page whether it exists or not.
|
||||
if !authed(r) {
|
||||
http.Redirect(w, r, "/etc/loginerror.html?url="+url.QueryEscape(r.URL.Path), http.StatusFound)
|
||||
return
|
||||
}
|
||||
body, ok := f.pages[r.URL.Path]
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if !authed(r) {
|
||||
http.Redirect(w, r, "/etc/loginerror.html?url="+url.QueryEscape(r.URL.Path), http.StatusFound)
|
||||
return
|
||||
}
|
||||
w.Write([]byte(body))
|
||||
})
|
||||
f.Server = httptest.NewUnstartedServer(mux)
|
||||
@@ -140,7 +151,7 @@ func TestACertificateOtherThanThePinnedOneIsRefusedBeforeAnythingIsSent(t *testi
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = a.Login(context.Background())
|
||||
err = a.Login(context.Background(), DefaultPage)
|
||||
if err == nil {
|
||||
t.Fatal("a certificate that is not the pinned one was accepted")
|
||||
}
|
||||
@@ -158,7 +169,7 @@ func TestACertificateOtherThanThePinnedOneIsRefusedBeforeAnythingIsSent(t *testi
|
||||
func TestThePinnedCertificateIsAccepted(t *testing.T) {
|
||||
f := newFakePrinter(t, "B1891", "hunter2")
|
||||
a, _ := NewAdmin(f.host(), strings.ToLower(f.print()), "hunter2")
|
||||
if err := a.Login(context.Background()); err != nil {
|
||||
if err := a.Login(context.Background(), DefaultPage); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -170,7 +181,7 @@ func TestLoginUsesThePasswordFieldNameTheFormGives(t *testing.T) {
|
||||
f := newFakePrinter(t, name, "s3cret pass")
|
||||
a, _ := NewAdmin(f.host(), f.print(), "s3cret pass")
|
||||
ctx := context.Background()
|
||||
if err := a.Login(ctx); err != nil {
|
||||
if err := a.Login(ctx, "/general/other.html"); err != nil {
|
||||
t.Fatalf("field %s: %v", name, err)
|
||||
}
|
||||
if len(f.posted) != 1 {
|
||||
@@ -179,7 +190,7 @@ func TestLoginUsesThePasswordFieldNameTheFormGives(t *testing.T) {
|
||||
if got := f.posted[0].Get(name); got != "s3cret pass" {
|
||||
t.Errorf("field %s: the password field holds %q", name, got)
|
||||
}
|
||||
if f.posted[0].Get("loginurl") != "/general/status.html" {
|
||||
if f.posted[0].Get("loginurl") != "/general/other.html" {
|
||||
t.Errorf("field %s: the form's hidden loginurl was %q", name, f.posted[0].Get("loginurl"))
|
||||
}
|
||||
if len(f.posted[0]) != 2 {
|
||||
@@ -196,7 +207,7 @@ func TestLoginUsesThePasswordFieldNameTheFormGives(t *testing.T) {
|
||||
func TestAWrongPasswordIsAFailedLogin(t *testing.T) {
|
||||
f := newFakePrinter(t, "B1891", "right")
|
||||
a, _ := NewAdmin(f.host(), f.print(), "wrong")
|
||||
err := a.Login(context.Background())
|
||||
err := a.Login(context.Background(), DefaultPage)
|
||||
if err == nil || !strings.Contains(err.Error(), "login failed") {
|
||||
t.Fatalf("error = %v, want a failed login", err)
|
||||
}
|
||||
@@ -210,10 +221,10 @@ func TestLogoutEndsTheSessionAndAPageWithoutOneSaysSo(t *testing.T) {
|
||||
f := newFakePrinter(t, "B1891", "pw")
|
||||
a, _ := NewAdmin(f.host(), f.print(), "pw")
|
||||
ctx := context.Background()
|
||||
if err := a.Login(ctx); err != nil {
|
||||
if err := a.Login(ctx, DefaultPage); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, at, err := a.Get(ctx, "/general/status.html")
|
||||
body, at, err := a.Get(ctx, DefaultPage)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -239,7 +250,7 @@ func TestThePasswordIsNotPostedToAnotherHost(t *testing.T) {
|
||||
defer srv.Close()
|
||||
host := strings.TrimPrefix(srv.URL, "https://")
|
||||
a, _ := NewAdmin(host, Fingerprint(srv.TLS.Certificates[0].Certificate[0]), "pw")
|
||||
err := a.Login(context.Background())
|
||||
err := a.Login(context.Background(), DefaultPage)
|
||||
if err == nil || !strings.Contains(err.Error(), "not sent") {
|
||||
t.Fatalf("error = %v", err)
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ func (w *deviceWorker) read(ctx context.Context, page string) (map[string]any, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := a.Login(ctx); err != nil {
|
||||
if err := a.Login(ctx, page); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Log out with a context of its own: the ask's may be what ended this.
|
||||
|
||||
@@ -165,7 +165,7 @@ func tools(settingsFile string, now func() time.Time) []stdio.Tool {
|
||||
Input: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"page": map[string]any{"type": "string", "description": "a page of the admin site: a path starting with / and ending in .html (default /general/status.html)"},
|
||||
"page": map[string]any{"type": "string", "description": "a page of the admin site: a path starting with / and ending in .html (default /home/status.html)"},
|
||||
},
|
||||
},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestAPinListingTwoFingerprintsAcceptsTheCertificateTheServerPresents(t *tes
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := a.Login(context.Background()); err != nil {
|
||||
if err := a.Login(context.Background(), DefaultPage); err != nil {
|
||||
t.Fatalf("a pin listing the served certificate was refused: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func TestAPinListingTwoFingerprintsAcceptsTheCertificateTheServerPresents(t *tes
|
||||
func TestAPinListingOnlyOtherCertificatesStillRefuses(t *testing.T) {
|
||||
f := newFakePrinter(t, "B1891", "hunter2")
|
||||
a, _ := NewAdmin(f.host(), printerECDSAPrint+" , "+printerRSAPrint, "hunter2")
|
||||
err := a.Login(context.Background())
|
||||
err := a.Login(context.Background(), DefaultPage)
|
||||
if err == nil {
|
||||
t.Fatal("a certificate that is in no pinned fingerprint was accepted")
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
var pagePattern = regexp.MustCompile(`^/[A-Za-z0-9_./-]*\.html$`)
|
||||
|
||||
// DefaultPage is the page printer.settings reads when none is asked for.
|
||||
const DefaultPage = "/general/status.html"
|
||||
const DefaultPage = "/home/status.html"
|
||||
|
||||
// The asks the verbs put to the device process (ADR 0468 clause 1; the shape of ADR 0436's mail-sends): the tools
|
||||
// never hold a credential, so each verb queues an ask on the work-queue stream device-asks, and the device process,
|
||||
|
||||
@@ -211,3 +211,31 @@ func TestTheAdminWorkerSettlesEachAsk(t *testing.T) {
|
||||
t.Errorf("an answer that cannot be written: action %v delay %v, want retry", act, delay)
|
||||
}
|
||||
}
|
||||
|
||||
// 1514: printer.settings with no page, and with /home/status.html, logs in from the page the printer serves its
|
||||
// login form on (the root redirects to it) and reads that page. The printer has no /general/status.html behind
|
||||
// the login: the fake, like the captured printer, answers it a 404 once logged in, and a redirect to the login
|
||||
// page before.
|
||||
func TestPrinterSettingsDefaultPageAndHomeStatusAreReadAfterTheLogin(t *testing.T) {
|
||||
for name, args := range map[string]map[string]any{"default": {}, "explicit": {"page": "/home/status.html"}} {
|
||||
f := newFakePrinter(t, "B1891", "pw")
|
||||
_, run := printerTools(t, f, "pw")
|
||||
got, err := run(args)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
raw, _ := json.Marshal(got)
|
||||
if !strings.Contains(string(raw), "Synthetic status") || !strings.Contains(string(raw), "/home/status.html") {
|
||||
t.Errorf("%s: answer %.200s", name, raw)
|
||||
}
|
||||
if len(f.posted) != 1 || f.posted[0].Get("loginurl") != "/home/status.html" {
|
||||
t.Errorf("%s: posted %v, want loginurl /home/status.html", name, f.posted)
|
||||
}
|
||||
if f.loggedInNow() != 0 {
|
||||
t.Errorf("%s: the session is still open", name)
|
||||
}
|
||||
}
|
||||
if DefaultPage != "/home/status.html" {
|
||||
t.Errorf("DefaultPage = %s", DefaultPage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
Cache-Control: no-store
|
||||
X-Frame-Options: DENY
|
||||
Content-Length: 0
|
||||
Connection: close
|
||||
Pragma: no-cache
|
||||
Location: /home/status.html
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html lang="en-gb" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb"><head><meta http-equiv="Content-Script-Type" content="text/javascript" /><meta http-equiv="content-style-type" content="text/css" /><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><meta http-equiv="X-UA-Compatible" content="IE=edge"><script type="text/javascript" src="/common/js/ews.js"></script><link rel="stylesheet" type="text/css" href="../common/css/mobile_common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../common/css/mobile_ews.css" />
|
||||
<title>Brother MFC-L8390CDW series</title><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no" /></head><body><div id="headerFrame"><div id="menuButton" layout="pc" onclick="toggleMenu();"><img src="/common/images/menu_icon.gif" alt="Menu"></div><div id="modelName" layout="pc">MFC-L8390CDW series</div><div id="corporateLogo" layout="pc"><img src="/common/images/logo.gif" alt="Brother" /></div></div></div><div id="Menu" layout="pc"><div id="navigationContainer"><div id="navMenu"><div id="LayoutButton"><img id="PCButton" layout="pc" src="/common/images/chevron_left.gif" onclick="changeLayout('user_layout','mobile');"></img><img id="MobileButton" layout="pc" src="/common/images/pin.gif" onclick="changeLayout('user_layout','pc');"></img></div><hr><div id="LogInOutBox"><form method="post" action="/home/status.html"><div><input type="password" id="LogBox" name="B1891" /><input type="hidden" name="loginurl" value="/general/status.html"/><input id="login" type="submit" value="Login" /></div></form></div><div class="links"><a href="https://update.brother.co.jp/device/redirect.aspx?redirecttype=6&country=w&lang=en&device=MFC-L8390CDW series&alias=aqfs" target="_blank">Trouble logging in?</a></div><hr><div id="menu_tree_root"><div class="menuLv1"><a href="#" onclick="expandMenu('home_sub');">Home</a><div id="home_sub" class="open"><div class="menuLv2"><a href="/home/status.html">Status</a></div><div class="menuLv2"><a href="/home/reflesh.html" class="subMenu">Auto Refresh Interval</a></div></div></div><hr></div></div></div></div><div id="Page" layout="pc"><div id="rightFrameContainer"><div id="rightFrame"><div id="mainContent"><div id="pageTitle"><h2>Please Login</h2></div><div id="pageContents"><div class="contentsGroup"><p class="noteMessage">To Access this page you are required to Login, please Login.</p></div></div></div></div></div></div></div><div id="footerFrame">Copyright(C) 2000-2023 Brother Industries, Ltd. All Rights Reserved.</div><script type="text/javascript" src="/common/js/mobilemenucontrl.js"></script><script>window.onpageshow = function(event) {if (event.persisted) { window.location.reload(); }};</script></body></html>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
HTTP/1.1 200 OK
|
||||
Cache-Control: no-store
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: DENY
|
||||
Content-Length: 2974
|
||||
Content-Type: text/html
|
||||
Content-Language: en-gb
|
||||
Connection: close
|
||||
X-XSS-Protection: 1; mode=block
|
||||
Pragma: no-cache
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
HTTP/1.1 302 Moved Temporarily
|
||||
Cache-Control: no-store
|
||||
X-Frame-Options: DENY
|
||||
Content-Length: 0
|
||||
Connection: close
|
||||
Pragma: no-cache
|
||||
Location: /etc/loginerror.html?url=%2Fgeneral%2Fstatus%2Ehtml
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html lang="en-gb" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb"><head><meta http-equiv="Content-Script-Type" content="text/javascript" /><meta http-equiv="content-style-type" content="text/css" /><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><meta http-equiv="X-UA-Compatible" content="IE=edge"><script type="text/javascript" src="/common/js/ews.js"></script><link rel="stylesheet" type="text/css" href="../common/css/mobile_common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../common/css/mobile_ews.css" />
|
||||
<script type="text/javascript" src="../common/js/cookie.js"></script>
|
||||
<script type="text/javascript" src="../common/js/language.js"></script>
|
||||
<script type="text/javascript" src="../common/js/lcddisplay.js"></script>
|
||||
<title>Brother MFC-L8390CDW series</title><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no" /></head><body><div id="headerFrame"><div id="menuButton" layout="pc" onclick="toggleMenu();"><img src="/common/images/menu_icon.gif" alt="Menu"></div><div id="modelName" layout="pc">MFC-L8390CDW series</div><div id="corporateLogo" layout="pc"><img src="/common/images/logo.gif" alt="Brother" /></div></div></div><div id="Menu" layout="pc"><div id="navigationContainer"><div id="navMenu"><div id="LayoutButton"><img id="PCButton" layout="pc" src="/common/images/chevron_left.gif" onclick="changeLayout('user_layout','mobile');"></img><img id="MobileButton" layout="pc" src="/common/images/pin.gif" onclick="changeLayout('user_layout','pc');"></img></div><hr><div id="LogInOutBox"><form method="post" action="/home/status.html"><div><input type="password" id="LogBox" name="B1891" /><input type="hidden" name="loginurl" value="/home/status.html"/><input id="login" type="submit" value="Login" /></div></form></div><div class="links"><a href="https://update.brother.co.jp/device/redirect.aspx?redirecttype=6&country=w&lang=en&device=MFC-L8390CDW series&alias=aqfs" target="_blank">Trouble logging in?</a></div><hr><div id="menu_tree_root"><div class="menuLv1"><a href="#" onclick="expandMenu('home_sub');">Home</a><div id="home_sub" class="open"><div class="menuLv2 selected"><a href="../home/status.html">Status</a></div><div class="menuLv2"><a href="/home/reflesh.html" class="subMenu">Auto Refresh Interval</a></div></div></div><hr></div></div></div></div><div id="Page" layout="pc"><div id="rightFrameContainer"><div id="rightFrame"><div id="mainContent"><div id="pageTitle"><h2>Status</h2></div><div id="pageContents"><form method="post" action="/home/status.html"><div><input type="hidden" id="pageid" name="pageid" value="1" /></div><div class="contentsGroup"><dl class="items"><dt>Device Status</dt><dd><div id="moni_data"><span class="moni moniError"></span></div></dd><dt class="subhead"><span>Automatic Refresh</span></dt><dd><div><label class="radioSimple"><input type="radio" name="Refresh" value="0" onclick="refreshLCD(0);" checked="checked" />
|
||||
Off</label><label class="radioSimple"><input type="radio" name="Refresh" value="1" onclick="refreshLCD(60000);" />
|
||||
On</label></div></dd><dt>Toner Level</dt><dd><div id="ink_level"><table id="inkLevel" summary="ink level"><tr><th></th><th><img src="../common/images/low.gif" alt="Low" /></th><th><img src="../common/images/replace.gif" alt="Replace" /></th><th></th></tr><tr><td><img src="../common/images/black.gif" alt="Black" class="tonerremain" height="22" /></td><td><img src="../common/images/cyan.gif" alt="Cyan" class="tonerremain" height="5" /></td><td><img src="../common/images/magenta.gif" alt="Magenta" class="tonerremain" height="0" /></td><td><img src="../common/images/yellow.gif" alt="Yellow" class="tonerremain" height="56" /></td></tr><tr><th>BK</th><th>C</th><th>M</th><th>Y</th></tr></table></div></dd></dl></div><div class="contentsGroup"><dl class="items"><dt>Web Language</dt><dd><select id="Language" name="Language" onchange="changeLanguage('Language', 'WebLang');"><option value="0" selected="selected">Auto</option><option value="2">English</option><option value="3">French</option><option value="4">German</option><option value="5">Dutch</option><option value="6">Spanish</option><option value="7">Italian</option><option value="10">Russian</option><option value="11">Portuguese</option><option value="12">Polish</option></select></dd></dl></div><div class="contentsGroup"><dl class="items"><dt>Device Location</dt><dd><ul class="itemList"><li class="contact">Contact<span class="spacer">:</span></li><li class="location">Location<span class="spacer">:</span></li></ul></dd></dl></div></form></div></div></div></div><script type="text/javascript">
|
||||
<!--
|
||||
judge_refresh(60000);
|
||||
//-->
|
||||
</script>
|
||||
</div></div><div id="footerFrame">Copyright(C) 2000-2023 Brother Industries, Ltd. All Rights Reserved.</div><script type="text/javascript" src="/common/js/mobilemenucontrl.js"></script><script>window.onpageshow = function(event) {if (event.persisted) { window.location.reload(); }};</script></body></html>
|
||||
@@ -0,0 +1,11 @@
|
||||
HTTP/1.1 200 OK
|
||||
Cache-Control: no-store
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: DENY
|
||||
Content-Length: 5180
|
||||
Content-Type: text/html
|
||||
Content-Language: en-gb
|
||||
Connection: close
|
||||
X-XSS-Protection: 1; mode=block
|
||||
Pragma: no-cache
|
||||
|
||||
Reference in New Issue
Block a user