Beispiel #1
0
  @Override
  HTTPResponse makeWebPageGet(URI uri, HTTPRequest req, ToadletContext ctx, PageNode page) {
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    HTMLNode container = contentNode.addChild("div", "class", "container");

    // Add the list of folders
    HTMLNode folderList = container.addChild("div", "class", "folderlist");

    String identity = loginManager.getSession(ctx).getUserID();
    FreemailAccount account = accountManager.getAccount(identity);
    MessageBank topLevelMessageBank = account.getMessageBank();
    addMessageBank(folderList, topLevelMessageBank, "inbox");

    // Add the message
    String folderName = req.getParam("folder", "inbox");
    MessageBank messageBank = MessageBankTools.getMessageBank(account, folderName);

    int messageUid;
    try {
      messageUid = Integer.parseInt(req.getParam("uid"));
    } catch (NumberFormatException e) {
      Logger.error(this, "Got invalid uid: " + req.getParam("uid"));
      messageUid = 0;
    }
    MailMessage msg = MessageBankTools.getMessage(messageBank, messageUid);

    if (msg == null) {
      /* FIXME: L10n */
      HTMLNode infobox = addErrorbox(container, "Message doesn't exist");
      infobox.addChild("p", "The message you requested doesn't exist");
      return new GenericHTMLResponse(ctx, 200, "OK", pageNode.generate());
    }

    HTMLNode messageNode = container.addChild("div", "class", "message");

    addMessageButtons(ctx, messageNode, folderName, messageUid);
    addMessageHeaders(messageNode, msg);
    addMessageContents(messageNode, msg);

    // Mark message as read
    if (!msg.flags.get("\\seen")) {
      msg.flags.set("\\seen", true);
      msg.storeFlags();
    }

    return new GenericHTMLResponse(ctx, 200, "OK", pageNode.generate());
  }
Beispiel #2
0
  public void handleMethodGET(URI uri, HTTPRequest req, ToadletContext ctx)
      throws ToadletContextClosedException, IOException, RedirectException {
    // If we don't disconnect we will have pipelining issues
    ctx.forceDisconnect();

    String path = uri.getPath();
    if (path.startsWith(StaticToadlet.ROOT_URL) && staticToadlet != null)
      staticToadlet.handleMethodGET(uri, req, ctx);
    else {
      String desc = NodeL10n.getBase().getString("StartupToadlet.title");
      PageNode page = ctx.getPageMaker().getPageNode(desc, false, ctx);
      HTMLNode pageNode = page.outer;
      HTMLNode headNode = page.headNode;
      headNode.addChild(
          "meta", new String[] {"http-equiv", "content"}, new String[] {"refresh", "20; url="});
      HTMLNode contentNode = page.content;

      if (!isPRNGReady) {
        HTMLNode prngInfoboxContent =
            ctx.getPageMaker()
                .getInfobox(
                    "infobox-error",
                    NodeL10n.getBase().getString("StartupToadlet.entropyErrorTitle"),
                    contentNode,
                    null,
                    true);
        prngInfoboxContent.addChild(
            "#", NodeL10n.getBase().getString("StartupToadlet.entropyErrorContent"));
      }

      HTMLNode infoboxContent =
          ctx.getPageMaker().getInfobox("infobox-error", desc, contentNode, null, true);
      infoboxContent.addChild("#", NodeL10n.getBase().getString("StartupToadlet.isStartingUp"));

      WelcomeToadlet.maybeDisplayWrapperLogfile(ctx, contentNode);

      // TODO: send a Retry-After header ?
      writeHTMLReply(ctx, 503, desc, pageNode.generate());
    }
  }
  public void handleMethodGET(URI uri, HTTPRequest request, ToadletContext ctx)
      throws ToadletContextClosedException, IOException {

    PageNode page = ctx.getPageMaker().getPageNode("Redirect to Decoded link", ctx);
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    if (ctx.isAllowedFullAccess()) contentNode.addChild(ctx.getAlertManager().createSummary());

    final String requestPath = request.getPath().substring(path().length());

    // Without this it'll try to look in the current directory which will be /decode and won't work.
    String keyToFetch = "/" + requestPath;

    // This is for when a browser can't handle 301s, should very rarely (never?) be seen.
    ctx.getPageMaker()
        .getInfobox("infobox-warning", "Decode Link", contentNode, "decode-not-redirected", true)
        .addChild("a", "href", keyToFetch, "Click Here to be re-directed");

    this.writeHTMLReply(
        ctx, 301, "Moved Permanently\nLocation: " + keyToFetch, pageNode.generate());
  }
Beispiel #4
0
  private void makeMainPage(
      ToadletContext ctx,
      List<String> errors,
      String key,
      int hexWidth,
      boolean automf,
      boolean deep,
      boolean ml)
      throws ToadletContextClosedException, IOException, RedirectException, URISyntaxException {
    PageNode page = pluginContext.pageMaker.getPageNode(i18n("KeyExplorer.PageTitle"), ctx);
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    byte[] data = null;
    GetResult getresult = null;
    String extraParams = "&hexwidth=" + hexWidth;
    if (automf) {
      extraParams += "&automf=checked";
    }
    if (deep) {
      extraParams += "&deep=checked";
    }
    if (ml) {
      extraParams += "&ml=checked";
    }
    FreenetURI furi = null;
    FreenetURI retryUri = null;

    try {
      if (key != null && (key.trim().length() > 0)) {
        furi =
            URISanitizer.sanitizeURI(
                errors,
                key,
                false,
                URISanitizer.Options.NOMETASTRINGS,
                URISanitizer.Options.SSKFORUSK);
        retryUri = furi;
        if (ml) { // multilevel is requestet
          Metadata tempMD =
              KeyExplorerUtils.simpleManifestGet(pluginContext.pluginRespirator, furi);
          FetchResult tempResult =
              KeyExplorerUtils.splitGet(pluginContext.pluginRespirator, tempMD);
          getresult = new GetResult(tempResult.asBucket(), true);
          data = tempResult.asByteArray();
        } else { // normal get
          getresult = KeyExplorerUtils.simpleGet(pluginContext.pluginRespirator, furi);
          data = BucketTools.toByteArray(getresult.getData());
        }
      }
    } catch (MalformedURLException e) {
      errors.add("MalformedURL: " + key);
    } catch (IOException e) {
      Logger.error(this, "500", e);
      errors.add("IO Error: " + e.getMessage());
    } catch (MetadataParseException e) {
      errors.add("Metadata Parse Error: " + e.getMessage());
    } catch (FetchException e) {
      errors.add("Get failed (" + e.mode + "): " + e.getMessage());
    } catch (KeyListenerConstructionException e) {
      Logger.error(this, "Hu?", e);
      errors.add("Internal Error: " + e.getMessage());
    } finally {
      if (getresult != null) getresult.free();
    }

    HTMLNode uriBox =
        createUriBox(
            pluginContext,
            ((furi == null) ? null : furi.toString(false, false)),
            hexWidth,
            automf,
            deep,
            errors);

    if (errors.size() > 0) {
      contentNode.addChild(createErrorBox(errors, path(), retryUri, extraParams));
      errors.clear();
    }

    contentNode.addChild(uriBox);

    if (data != null) {
      Metadata md = null;

      if (getresult.isMetaData()) {
        try {
          md = Metadata.construct(data);
        } catch (MetadataParseException e) {
          errors.add("Metadata parse error: " + e.getMessage());
        }
        if (md != null) {
          if (automf && md.isArchiveManifest()) {
            if (md.getArchiveType() == ARCHIVE_TYPE.TAR) {
              writeTemporaryRedirect(
                  ctx,
                  "",
                  KeyUtilsPlugin.PLUGIN_URI
                      + "/Site/?mftype=TARmanifest&key="
                      + furi
                      + extraParams);
              return;
            } else if (md.getArchiveType() == ARCHIVE_TYPE.ZIP) {
              writeTemporaryRedirect(
                  ctx,
                  "",
                  KeyUtilsPlugin.PLUGIN_URI
                      + "/Site/?mftype=ZIPmanifest&key="
                      + furi
                      + extraParams);
              return;
            } else {
              errors.add("Unknown Archive Type: " + md.getArchiveType().name());
            }
          }
          if (automf && md.isSimpleManifest()) {
            writeTemporaryRedirect(
                ctx,
                "",
                KeyUtilsPlugin.PLUGIN_URI
                    + "/Site/?mftype=simplemanifest&key="
                    + furi
                    + extraParams);
            return;
          }
        }
      }

      String title = "Key: " + furi.toString(false, false);
      if (getresult.isMetaData()) title = title + "\u00a0(MetaData)";
      HTMLNode dataBox2 = pluginContext.pageMaker.getInfobox("#", title, contentNode);

      dataBox2.addChild("%", "<pre lang=\"en\" style=\"font-family: monospace;\">\n");
      dataBox2.addChild("#", hexDump(data, hexWidth));
      dataBox2.addChild("%", "\n</pre>");

      if (getresult.isMetaData()) {
        if (md != null) {
          HTMLNode metaBox =
              pluginContext.pageMaker.getInfobox("#", "Decomposed metadata", contentNode);

          metaBox.addChild("#", "Metadata version " + Short.toString(md.getParsedVersion()));
          metaBox.addChild("br");
          metaBox.addChild("#", "Document type:\u00a0");
          if (md.isSimpleRedirect()) {
            metaBox.addChild("#", "SimpleRedirect");
          } else if (md.isSimpleManifest()) {
            metaBox.addChild("#", "SimpleManifest");
          } else if (md.isArchiveInternalRedirect()) {
            metaBox.addChild("#", "ArchiveInternalRedirect");
          } else if (md.isArchiveMetadataRedirect()) {
            metaBox.addChild("#", "ArchiveMetadataRedirect");
          } else if (md.isArchiveManifest()) {
            metaBox.addChild("#", "ArchiveManifest");
          } else if (md.isMultiLevelMetadata()) {
            metaBox.addChild("#", "MultiLevelMetadata");
          } else if (md.isSymbolicShortlink()) {
            metaBox.addChild("#", "SymbolicShortlink");
          } else {
            metaBox.addChild("#", "<Unknown document type>");
          }
          metaBox.addChild("br");

          final String MIMEType = md.getMIMEType();
          if (MIMEType != null) {
            metaBox.addChild("#", "MIME Type: " + MIMEType);
            metaBox.addChild("br");
          }

          if (md.haveFlags()) {
            metaBox.addChild("#", "Flags:\u00a0");
            boolean isFirst = true;

            if (md.isSplitfile()) {
              metaBox.addChild("#", "SplitFile");
              isFirst = false;
            }
            if (md.isCompressed()) {
              if (isFirst) isFirst = false;
              else metaBox.addChild("#", "\u00a0");
              metaBox.addChild("#", "Compressed (" + md.getCompressionCodec().name + ")");
            }
            if (md.hasTopData()) {
              if (isFirst) isFirst = false;
              else metaBox.addChild("#", "\u00a0");
              metaBox.addChild("#", "HasTopData");
            }
            if (isFirst) metaBox.addChild("#", "<No flag set>");
          }
          metaBox.addChild("br");

          if (md.isCompressed()) {
            metaBox.addChild("#", "Decompressed size: " + md.uncompressedDataLength() + " bytes.");
          } else {
            metaBox.addChild("#", "Uncompressed");
          }

          metaBox.addChild("br");

          if (md.topCompatibilityMode != 0) {
            metaBox.addChild("#", "Compatibility mode: " + md.getTopCompatibilityMode().toString());
            metaBox.addChild("br");
          }

          if (md.hasTopData()) {
            metaBox.addChild("#", "Top Block Data:");
            metaBox.addChild("br");
            metaBox.addChild(
                "#", "\u00a0\u00a0DontCompress: " + Boolean.toString(md.topDontCompress));
            metaBox.addChild("br");
            metaBox.addChild(
                "#",
                "\u00a0\u00a0Compressed size: " + Long.toString(md.topCompressedSize) + " bytes.");
            metaBox.addChild("br");
            metaBox.addChild(
                "#", "\u00a0\u00a0Decompressed Size: " + Long.toString(md.topSize) + " bytes.");
            metaBox.addChild("br");
            metaBox.addChild(
                "#",
                "\u00a0\u00a0Blocks: "
                    + Integer.toString(md.topBlocksRequired)
                    + " required, "
                    + Integer.toString(md.topBlocksTotal)
                    + " total.");
            metaBox.addChild("br");
          }
          final HashResult[] hashes = md.getHashes();
          if (hashes != null && hashes.length > 0) {
            metaBox.addChild("#", "Hashes:");
            metaBox.addChild("br");
            for (final HashResult hash : hashes) {
              metaBox.addChild(
                  "#", "\u00a0\u00a0" + hash.type.name() + ": " + HexUtil.bytesToHex(hash.result));
              metaBox.addChild("br");
            }
          }

          if (md.isSplitfile()) {
            metaBox.addChild("#", "Splitfile size\u00a0=\u00a0" + md.dataLength() + " bytes.");
            metaBox.addChild("br");

            byte[] splitfileCryptoKey = md.getCustomSplitfileKey();
            if (splitfileCryptoKey != null) {
              metaBox.addChild(
                  "#", "Splitfile CryptoKey\u00a0=\u00a0" + HexUtil.bytesToHex(splitfileCryptoKey));
              metaBox.addChild("br");
            }
          }

          metaBox.addChild("#", "Options:");
          metaBox.addChild("br");

          if (md.isSimpleManifest()) {
            metaBox.addChild(
                new HTMLNode(
                    "a",
                    "href",
                    KeyUtilsPlugin.PLUGIN_URI
                        + "/Site/?mftype=simplemanifest&key="
                        + furi
                        + extraParams,
                    "reopen as manifest"));
            metaBox.addChild("br");
          }
          if (md.isArchiveManifest()) {
            metaBox.addChild(
                new HTMLNode(
                    "a",
                    "href",
                    KeyUtilsPlugin.PLUGIN_URI
                        + "/Site/?mftype="
                        + md.getArchiveType().name()
                        + "manifest&key="
                        + furi
                        + extraParams,
                    "reopen as manifest"));
            metaBox.addChild("br");
          }
          if (md.isMultiLevelMetadata()) {
            if (ml)
              metaBox.addChild(
                  new HTMLNode(
                      "a",
                      "href",
                      KeyUtilsPlugin.PLUGIN_URI + "/?key=" + furi + extraParams,
                      "explore multilevel"));
            else
              metaBox.addChild(
                  new HTMLNode(
                      "a",
                      "href",
                      KeyUtilsPlugin.PLUGIN_URI + "/?ml=checked&key=" + furi + extraParams,
                      "explore multilevel"));
            metaBox.addChild("br");
          }

          FreenetURI uri = md.getSingleTarget();
          if (uri != null) {
            String sfrUri = uri.toString(false, false);
            metaBox.addChild("#", sfrUri);
            metaBox.addChild("#", "\u00a0");
            metaBox.addChild(new HTMLNode("a", "href", "/?key=" + sfrUri, "open"));
            metaBox.addChild("#", "\u00a0");
            metaBox.addChild(
                new HTMLNode(
                    "a",
                    "href",
                    KeyUtilsPlugin.PLUGIN_URI + "/?key=" + sfrUri + extraParams,
                    "explore"));
          } else {
            metaBox.addChild(new HTMLNode("a", "href", "/?key=" + furi, "reopen normal"));
          }
          metaBox.addChild("br");

          if ((uri == null) && md.isSplitfile()) {
            metaBox.addChild(
                new HTMLNode(
                    "a",
                    "href",
                    KeyUtilsPlugin.PLUGIN_URI + "/Split?key=" + furi.toString(false, false),
                    "reopen as splitfile"));
            metaBox.addChild("br");
            metaBox.addChild(
                new HTMLNode(
                    "a",
                    "href",
                    KeyUtilsPlugin.PLUGIN_URI
                        + "/Download?action=splitdownload&key="
                        + furi.toString(false, false),
                    "split-download"));
            metaBox.addChild("br");
          }
        }
      }
      if (errors.size() > 0) contentNode.addChild(createErrorBox(errors));
    }
    contentNode.addChild(Utils.makeDonateFooter(_intl));
    writeHTMLReply(ctx, 200, "OK", pageNode.generate());
  }
  public void handleMethodGET(URI uri, HTTPRequest req, ToadletContext ctx)
      throws ToadletContextClosedException, IOException {

    if (!ctx.checkFullAccess(this)) return;

    boolean advancedModeEnabled = ctx.isAdvancedModeEnabled();

    PageNode page =
        ctx.getPageMaker()
            .getPageNode(NodeL10n.getBase().getString("ConfigToadlet.fullTitle"), ctx);
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    contentNode.addChild(ctx.getAlertManager().createSummary());

    HTMLNode infobox = contentNode.addChild("div", "class", "infobox infobox-normal");
    infobox.addChild("div", "class", "infobox-header", l10n("title"));
    HTMLNode configNode = infobox.addChild("div", "class", "infobox-content");
    HTMLNode formNode = ctx.addFormChild(configNode, path(), "configForm");

    // Invisible apply button at the top so that an enter keypress will
    // apply settings instead of
    // going to a directory browser if present.
    formNode.addChild(
        "input",
        new String[] {"type", "value", "class"},
        new String[] {"submit", l10n("apply"), "invisible"});

    /*
     * Special case: present an option for the wrapper's maximum memory
     * under Core configuration, provided the maximum memory property is
     * defined. (the wrapper is being used)
     */
    if (subConfig.getPrefix().equals("node") && WrapperConfig.canChangeProperties()) {
      String configName = "wrapper.java.maxmemory";
      String curValue = WrapperConfig.getWrapperProperty(configName);
      // If persisted from directory browser, override. This is a POST
      // HTTPRequest.
      if (req.isPartSet(configName)) {
        curValue = req.getPartAsStringFailsafe(configName, MAX_PARAM_VALUE_SIZE);
      }
      if (curValue != null) {
        formNode.addChild("div", "class", "configprefix", l10n("wrapper"));
        HTMLNode list = formNode.addChild("ul", "class", "config");
        HTMLNode item = list.addChild("li", "class", OptionType.TEXT.cssClass);
        // FIXME how to get the real default???
        String defaultValue = "256";
        item.addChild(
                "span",
                new String[] {"class", "title", "style"},
                new String[] {
                  "configshortdesc",
                  NodeL10n.getBase()
                      .getString(
                          "ConfigToadlet.defaultIs",
                          new String[] {"default"},
                          new String[] {defaultValue}),
                  "cursor: help;"
                })
            .addChild(NodeL10n.getBase().getHTMLNode("WrapperConfig." + configName + ".short"));
        item.addChild("span", "class", "config")
            .addChild(
                "input",
                new String[] {"type", "class", "name", "value"},
                new String[] {"text", "config", configName, curValue});
        item.addChild("span", "class", "configlongdesc")
            .addChild(NodeL10n.getBase().getHTMLNode("WrapperConfig." + configName + ".long"));
      }
    }

    short displayedConfigElements = 0;
    HTMLNode configGroupUlNode = new HTMLNode("ul", "class", "config");

    String overriddenOption = null;
    String overriddenValue = null;

    // A value changed by the directory selector takes precedence.
    if (req.isPartSet("select-for") && req.isPartSet(LocalFileBrowserToadlet.selectDir)) {
      overriddenOption = req.getPartAsStringFailsafe("select-for", MAX_PARAM_VALUE_SIZE);
      overriddenValue = req.getPartAsStringFailsafe("filename", MAX_PARAM_VALUE_SIZE);
    }

    /*
     * Present all other options for this subconfig.
     */
    for (Option<?> o : subConfig.getOptions()) {
      if (!((!advancedModeEnabled) && o.isExpert())) {
        displayedConfigElements++;
        String configName = o.getName();
        String fullName = subConfig.getPrefix() + '.' + configName;
        String value = o.getValueDisplayString();

        if (value == null) {
          Logger.error(this, fullName + "has returned null from config!);");
          continue;
        }

        ConfigCallback<?> callback = o.getCallback();

        final OptionType optionType;
        if (callback instanceof EnumerableOptionCallback) {
          optionType = OptionType.DROP_DOWN;
        } else if (callback instanceof BooleanCallback) {
          optionType = OptionType.BOOLEAN;
        } else if (callback instanceof ProgramDirectory.DirectoryCallback
            && !callback.isReadOnly()) {
          optionType = OptionType.DIRECTORY;
        } else if (!callback.isReadOnly()) {
          optionType = OptionType.TEXT;
        } else /* if (callback.isReadOnly()) */ {
          optionType = OptionType.TEXT_READ_ONLY;
        }

        // If ConfigToadlet is serving a plugin, ask the plugin to
        // translate the
        // config descriptions, otherwise use the node's BaseL10n
        // instance like
        // normal.
        HTMLNode shortDesc = o.getShortDescNode(plugin);
        HTMLNode longDesc = o.getLongDescNode(plugin);

        HTMLNode configItemNode = configGroupUlNode.addChild("li");
        String defaultValue;
        if (callback instanceof BooleanCallback) {
          // Only case where values are localised.
          defaultValue = l10n(Boolean.toString(Boolean.valueOf(value)));
        } else {
          defaultValue = o.getDefault();
        }

        configItemNode.addAttribute("class", optionType.cssClass);
        configItemNode
            .addChild("a", new String[] {"name", "id"}, new String[] {configName, configName})
            .addChild(
                "span",
                new String[] {"class", "title", "style"},
                new String[] {
                  "configshortdesc",
                  NodeL10n.getBase()
                          .getString(
                              "ConfigToadlet.defaultIs",
                              new String[] {"default"},
                              new String[] {defaultValue})
                      + (advancedModeEnabled ? " [" + fullName + ']' : ""),
                  "cursor: help;"
                })
            .addChild(shortDesc);
        HTMLNode configItemValueNode = configItemNode.addChild("span", "class", "config");

        // Values persisted through browser or backing down from
        // resetting to defaults
        // override the currently applied ones.
        if (req.isPartSet(fullName)) {
          value = req.getPartAsStringFailsafe(fullName, MAX_PARAM_VALUE_SIZE);
        }
        if (overriddenOption != null && overriddenOption.equals(fullName)) value = overriddenValue;
        switch (optionType) {
          case DROP_DOWN:
            configItemValueNode.addChild(
                addComboBox(
                    value, (EnumerableOptionCallback) callback, fullName, callback.isReadOnly()));
            break;
          case BOOLEAN:
            configItemValueNode.addChild(
                addBooleanComboBox(Boolean.valueOf(value), fullName, callback.isReadOnly()));
            break;
          case DIRECTORY:
            configItemValueNode.addChild(addTextBox(value, fullName, o, false));
            configItemValueNode.addChild(
                "input",
                new String[] {"type", "name", "value"},
                new String[] {
                  "submit",
                  "select-directory." + fullName,
                  NodeL10n.getBase().getString("QueueToadlet.browseToChange")
                });
            break;
          case TEXT_READ_ONLY:
            configItemValueNode.addChild(addTextBox(value, fullName, o, true));
            break;
          case TEXT:
            configItemValueNode.addChild(addTextBox(value, fullName, o, false));
            break;
        }

        configItemNode.addChild("span", "class", "configlongdesc").addChild(longDesc);
      }
    }

    if (displayedConfigElements > 0) {
      formNode.addChild(
          "div",
          "class",
          "configprefix",
          (plugin == null) ? l10n(subConfig.getPrefix()) : plugin.getString(subConfig.getPrefix()));
      formNode.addChild("a", "id", subConfig.getPrefix());
      formNode.addChild(configGroupUlNode);
    }

    formNode.addChild(
        "input", new String[] {"type", "value"}, new String[] {"submit", l10n("apply")});
    formNode.addChild(
        "input", new String[] {"type", "value"}, new String[] {"reset", l10n("undo")});
    formNode.addChild(
        "input",
        new String[] {"type", "name", "value"},
        new String[] {"hidden", "subconfig", subConfig.getPrefix()});
    // 'Node' prefix options should not be reset to defaults as it is a,
    // quoting Toad, "very bad idea".
    // Options whose defaults are not wise to apply include the location of
    // the master keys file,
    // the Darknet port number, and the datastore size.
    if (!subConfig.getPrefix().equals("node")) {
      formNode.addChild(
          "input",
          new String[] {"type", "name", "value"},
          new String[] {"submit", "confirm-reset-to-defaults", l10n("resetToDefaults")});
    }

    this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
  }
  public void handleMethodPOST(URI uri, HTTPRequest request, ToadletContext ctx)
      throws ToadletContextClosedException, IOException, RedirectException {

    if (!ctx.checkFullAccess(this)) return;

    // User requested reset to defaults, so present confirmation page.
    if (request.isPartSet("confirm-reset-to-defaults")) {
      PageNode page = ctx.getPageMaker().getPageNode(l10n("confirmResetTitle"), ctx);
      HTMLNode pageNode = page.outer;
      HTMLNode contentNode = page.content;

      HTMLNode content =
          ctx.getPageMaker()
              .getInfobox(
                  "infobox-warning", l10n("confirmResetTitle"), contentNode, "reset-confirm", true);
      content.addChild("#", l10n("confirmReset"));

      HTMLNode formNode = ctx.addFormChild(content, path(), "yes-button");
      String subconfig = request.getPartAsStringFailsafe("subconfig", MAX_PARAM_VALUE_SIZE);
      formNode.addChild(
          "input",
          new String[] {"type", "name", "value"},
          new String[] {"hidden", "subconfig", subconfig});

      // Persist visible fields so that they are reset to default or
      // unsaved changes are persisted.
      for (String part : request.getParts()) {
        if (part.startsWith(subconfig)) {
          formNode.addChild(
              "input",
              new String[] {"type", "name", "value"},
              new String[] {
                "hidden", part, request.getPartAsStringFailsafe(part, MAX_PARAM_VALUE_SIZE)
              });
        }
      }

      formNode.addChild(
          "input",
          new String[] {"type", "name", "value"},
          new String[] {
            "submit", "reset-to-defaults", NodeL10n.getBase().getString("Toadlet.yes")
          });

      formNode.addChild(
          "input",
          new String[] {"type", "name", "value"},
          new String[] {
            "submit", "decline-default-reset", NodeL10n.getBase().getString("Toadlet.no")
          });
      writeHTMLReply(ctx, 200, "OK", pageNode.generate());
      return;
    }

    // Returning from directory selector with a selection or declining
    // resetting settings to defaults.
    // Re-render config page with any changes made in the selector and/or
    // persisting values changed but
    // not applied.
    if (request.isPartSet(LocalFileBrowserToadlet.selectDir)
        || request.isPartSet("decline-default-reset")) {
      handleMethodGET(uri, request, ctx);
      return;
    }

    // Entering directory selector from config page.
    // This would be two loops if it checked for a redirect
    // (key.startsWith("select-directory.")) before
    // constructing params string. It always constructs it, then redirects
    // if it turns out to be needed.
    boolean directorySelector = false;
    StringBuilder paramsBuilder = new StringBuilder();
    paramsBuilder.append('?');
    String value;
    for (String key : request.getParts()) {
      // Prepare parts for page selection redirect:
      // Extract option and put into "select-for"; preserve others.
      value = request.getPartAsStringFailsafe(key, MAX_PARAM_VALUE_SIZE);
      if (key.startsWith("select-directory.")) {
        paramsBuilder
            .append("select-for=")
            .append(URLEncoder.encode(key.substring("select-directory.".length()), true))
            .append('&');
        directorySelector = true;
      } else {
        paramsBuilder
            .append(URLEncoder.encode(key, true))
            .append('=')
            .append(URLEncoder.encode(value, true))
            .append('&');
      }
    }
    String params = paramsBuilder.toString();
    if (directorySelector) {
      MultiValueTable<String, String> headers = new MultiValueTable<String, String>(1);
      // params ends in &. Download directory browser starts in default
      // download directory.
      headers.put(
          "Location",
          directoryBrowserPath + params + "path=" + core.getDownloadsDir().getAbsolutePath());
      ctx.sendReplyHeaders(302, "Found", headers, null, 0);
      return;
    }

    StringBuilder errbuf = new StringBuilder();
    boolean logMINOR = Logger.shouldLog(LogLevel.MINOR, this);

    String prefix = request.getPartAsStringFailsafe("subconfig", MAX_PARAM_VALUE_SIZE);
    if (logMINOR) {
      Logger.minor(this, "Current config prefix is " + prefix);
    }
    boolean resetToDefault = request.isPartSet("reset-to-defaults");
    if (resetToDefault && logMINOR) {
      Logger.minor(this, "Resetting to defaults");
    }

    for (Option<?> o : config.get(prefix).getOptions()) {
      String configName = o.getName();
      if (logMINOR) {
        Logger.minor(this, "Checking option " + prefix + '.' + configName);
      }

      // This ignores unrecognized parameters.
      if (request.isPartSet(prefix + '.' + configName)) {
        // Current subconfig is to be reset to default.
        if (resetToDefault) {
          // Disallow resetting fproxy port number to default as it
          // might break the link to start fproxy on the system tray,
          // shortcuts etc.
          if (prefix.equals("fproxy") && configName.equals("port")) continue;
          value = o.getDefault();
        } else {
          value = request.getPartAsStringFailsafe(prefix + '.' + configName, MAX_PARAM_VALUE_SIZE);
        }

        if (!(o.getValueDisplayString().equals(value))) {

          if (logMINOR) {
            Logger.minor(this, "Changing " + prefix + '.' + configName + " to " + value);
          }

          try {
            o.setValue(value);
          } catch (InvalidConfigValueException e) {
            errbuf.append(o.getName()).append(' ').append(e.getMessage()).append('\n');
          } catch (NodeNeedRestartException e) {
            needRestart = true;
          } catch (Exception e) {
            errbuf.append(o.getName()).append(' ').append(e).append('\n');
            Logger.error(this, "Caught " + e, e);
          }
        } else if (logMINOR) {
          Logger.minor(this, prefix + '.' + configName + " not changed");
        }
      }
    }

    // Wrapper params
    String wrapperConfigName = "wrapper.java.maxmemory";
    if (request.isPartSet(wrapperConfigName)) {
      value = request.getPartAsStringFailsafe(wrapperConfigName, MAX_PARAM_VALUE_SIZE);
      if (!WrapperConfig.getWrapperProperty(wrapperConfigName).equals(value)) {
        if (logMINOR) {
          Logger.minor(this, "Setting " + wrapperConfigName + " to " + value);
        }
        WrapperConfig.setWrapperProperty(wrapperConfigName, value);
      }
    }

    config.store();

    PageNode page = ctx.getPageMaker().getPageNode(l10n("appliedTitle"), ctx);
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    if (errbuf.length() == 0) {
      HTMLNode content =
          ctx.getPageMaker()
              .getInfobox(
                  "infobox-success",
                  l10n("appliedTitle"),
                  contentNode,
                  "configuration-applied",
                  true);
      content.addChild("#", l10n("appliedSuccess"));

      if (needRestart) {
        content.addChild("br");
        content.addChild("#", l10n("needRestart"));

        if (node.isUsingWrapper()) {
          content.addChild("br");
          HTMLNode restartForm = ctx.addFormChild(content, "/", "restartForm");
          restartForm.addChild(
              "input", //
              new String[] {"type", "name"}, //
              new String[] {"hidden", "restart"});
          restartForm.addChild(
              "input", //
              new String[] {"type", "name", "value"}, //
              new String[] {
                "submit",
                "restart2", //
                l10n("restartNode")
              });
        }

        if (needRestartUserAlert == null) {
          needRestartUserAlert = new NeedRestartUserAlert(ctx.getFormPassword());
          ctx.getAlertManager().register(needRestartUserAlert);
        }
      }
    } else {
      HTMLNode content =
          ctx.getPageMaker()
              .getInfobox(
                  "infobox-error",
                  l10n("appliedFailureTitle"),
                  contentNode,
                  "configuration-error",
                  true)
              .addChild("div", "class", "infobox-content");
      content.addChild("#", l10n("appliedFailureExceptions"));
      content.addChild("br");
      content.addChild("#", errbuf.toString());
    }

    HTMLNode content =
        ctx.getPageMaker()
            .getInfobox(
                "infobox-normal",
                l10n("possibilitiesTitle"),
                contentNode,
                "configuration-possibilities",
                false);
    content.addChild(
        "a",
        new String[] {"href", "title"},
        new String[] {path(), l10n("shortTitle")},
        l10n("returnToNodeConfig"));
    content.addChild("br");
    addHomepageLink(content);

    writeHTMLReply(ctx, 200, "OK", pageNode.generate());
  }
  public void handleMethodGET(URI uri, HTTPRequest req, ToadletContext ctx)
      throws ToadletContextClosedException, IOException {
    PageNode page = ctx.getPageMaker().getPageNode(l10n("title"), ctx);
    HTMLNode pageNode = page.outer;
    HTMLNode contentNode = page.content;

    contentNode.addChild(ctx.getAlertManager().createSummary());

    HTMLNode contentBox =
        ctx.getPageMaker()
            .getInfobox("infobox-information", l10n("title"), contentNode, "freesite-insert", true);

    contentBox.addChild("p", l10n("content1"));

    NodeL10n.getBase()
        .addL10nSubstitution(
            contentBox.addChild("p"),
            "InsertFreesiteToadlet.contentFlogHelper",
            new String[] {"plugins"},
            new HTMLNode[] {HTMLNode.link(PproxyToadlet.PATH)});

    NodeL10n.getBase()
        .addL10nSubstitution(
            contentBox.addChild("p"),
            "InsertFreesiteToadlet.content2",
            new String[] {"jsite-http", "jsite-freenet", "jsite-freenet-version", "jsite-info"},
            new HTMLNode[] {
              HTMLNode.link(
                  ExternalLinkToadlet.escape("http://downloads.freenetproject.org/alpha/jSite/")),
              HTMLNode.link(
                  "/CHK@1bWi9EOtVyY77cJfioG7BvWzSlU-2j7dNG55c6KlpNk,5EsWJj4BjJa51K6kLTLWb64DEJQ3qHEEmFBITZMtcIw,AAMC--8/jSite-0.11.1-jar-with-dependencies.jar"),
              HTMLNode.text("0.11.1"),
              HTMLNode.link(
                  "/SSK@1waTsw46L9-JEQ8yX1khjkfHcn--g0MlMsTlYHax9zQ,oYyxr5jyFnaTsVGDQWk9e3ddOWGKnqEASxAk08MHT2Y,AQACAAE/jSite-6/"),
            });
    contentBox.addChild("p", l10n("content3"));
    HTMLNode ul = contentBox.addChild("ul");
    HTMLNode li = ul.addChild("li");
    li.addChild(
        "a",
        "href",
        "/SSK@940RYvj1-aowEHGsb5HeMTigq8gnV14pbKNsIvUO~-0,FdTbR3gIz21QNfDtnK~MiWgAf2kfwHe-cpyJXuLHdOE,AQACAAE/publish-3/",
        "Publish!");
    li.addChild("#", " - " + l10n("publishExplanation"));
    li = ul.addChild("li");
    li.addChild(
        "a",
        "href",
        "/SSK@8r-uSRcJPkAr-3v3YJR16OCx~lyV2XOKsiG4MOQQBMM,P42IgNemestUdaI7T6z3Og6P-Hi7g9U~e37R3kWGVj8,AQACAAE/freesite-HOWTO-4/",
        "Freesite HOWTO");
    li.addChild("#", " - " + l10n("freesiteHowtoExplanation"));

    NodeL10n.getBase()
        .addL10nSubstitution(
            contentBox.addChild("p"),
            "InsertFreesiteToadlet.contentThingamablog",
            new String[] {"thingamablog", "thingamablog-freenet"},
            new HTMLNode[] {
              HTMLNode.link(
                  ExternalLinkToadlet.escape(
                      "http://downloads.freenetproject.org/alpha/thingamablog/thingamablog.zip")),
              HTMLNode.link(
                  "/CHK@o8j9T2Ghc9cfKMLvv9aLrHbvW5XiAMEGwGDqH2UANTk,sVxLdxoNL-UAsvrlXRZtI5KyKlp0zv3Ysk4EcO627V0,AAIC--8/thingamablog.zip")
            });

    this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
  }