예제 #1
0
  /**
   * ATTENTION: This function is duplicated in the Web Of Trust plugin, please backport any changes.
   */
  private synchronized void restoreDatabaseBackup(File databaseFile, File backupFile)
      throws IOException {
    Logger.warning(this, "Trying to restore database backup: " + backupFile.getAbsolutePath());

    if (db != null) throw new RuntimeException("Database is opened already!");

    if (backupFile.exists()) {
      try {
        FileUtil.secureDelete(databaseFile);
      } catch (IOException e) {
        Logger.warning(this, "Deleting of the database failed: " + databaseFile.getAbsolutePath());
      }

      if (backupFile.renameTo(databaseFile)) {
        Logger.warning(this, "Backup restored!");
      } else {
        throw new IOException(
            "Unable to rename backup file back to database file: "
                + databaseFile.getAbsolutePath());
      }

    } else {
      throw new IOException("Cannot restore backup, it does not exist!");
    }
  }
예제 #2
0
 /**
  * Adds the node reference in the resources as a friend to the node this plugin is loaded.
  *
  * @return the corresponding PeerAdditionReturnCode indicating whether the bridge was added
  *     successfully as a friend
  */
 private PeerAdditionReturnCodes addFriendBridge() {
   SimpleFieldSet bridgeNodeFS;
   try {
     bridgeNodeFS = nodeRefHelper.getBridgeNodeRefFS();
   } catch (IOException e) {
     Logger.error(this, "IO Exception while parsing bridge reference resource file");
     return PeerAdditionReturnCodes.INTERNAL_ERROR;
   }
   PeerNode pn;
   try {
     pn = node.createNewDarknetNode(bridgeNodeFS, FRIEND_TRUST.HIGH, FRIEND_VISIBILITY.NO);
     ((DarknetPeerNode) pn).setPrivateDarknetCommentNote("Master Bridge");
   } catch (FSParseException e) {
     return PeerAdditionReturnCodes.CANT_PARSE;
   } catch (PeerParseException e) {
     return PeerAdditionReturnCodes.CANT_PARSE;
   } catch (ReferenceSignatureVerificationException e) {
     return PeerAdditionReturnCodes.INVALID_SIGNATURE;
   } catch (Throwable t) {
     Logger.error(this, "Internal error adding reference :" + t.getMessage(), t);
     return PeerAdditionReturnCodes.INTERNAL_ERROR;
   }
   if (Arrays.equals(pn.getPubKeyHash(), node.getDarknetPubKeyHash())) {
     Logger.warning(this, "The bridge  node reference file belongs to this node.");
     return PeerAdditionReturnCodes.TRY_TO_ADD_SELF;
   }
   if (!node.addPeerConnection(pn)) {
     return PeerAdditionReturnCodes.ALREADY_IN_REFERENCE;
   }
   return PeerAdditionReturnCodes.OK;
 }
예제 #3
0
 public void setLanguage(LANGUAGE newLanguage) {
   try {
     CENOL10n.l10n = new PluginL10n(this, newLanguage);
   } catch (Exception e) {
     Logger.warning(this, "Could not set language to " + newLanguage);
   }
 }
  @Override
  public synchronized void onFailure(
      InsertException e, BaseClientPutter state, ObjectContainer container) {
    try {
      if (e.getMode() == InsertException.COLLISION) {
        Logger.warning(
            this, "WoTOwnMessageList insert collided, trying to insert with higher index ...");
        try {
          synchronized (mMessageManager) {
            // We must call getOwnMessageList() before calling onMessageListInsertFailed() because
            // the latter will increment the message list's
            // index, resulting in the ID of the message list changing - getIDFromURI would fail
            // with the old state.getURI() if we called it after
            // onMessageListInsertFailed()
            WoTOwnMessageList list =
                (WoTOwnMessageList)
                    mMessageManager.getOwnMessageList(
                        MessageListID.construct(state.getURI()).toString());
            mMessageManager.onMessageListInsertFailed(state.getURI(), true);
            insertMessageList(list);
          }
        } catch (Exception ex) {
          Logger.error(this, "Inserting WoTOwnMessageList with higher index failed", ex);
        }
      } else {
        if (e.isFatal()) Logger.error(this, "WoTOwnMessageList insert failed", e);
        else Logger.warning(this, "WoTOwnMessageList insert failed non-fatally", e);

        mMessageManager.onMessageListInsertFailed(state.getURI(), false);
      }
    } catch (Exception ex) {
      Logger.error(this, "WoTOwnMessageList insert failed and failure handling threw", ex);
    } finally {
      removeInsert(state);
      Closer.close(((ClientPutter) state).getData());
    }
  }
예제 #5
0
  /**
   * ATTENTION: This function is duplicated in the Web Of Trust plugin, please backport any changes.
   */
  private void closeDatabase() {
    if (db == null) {
      Logger.warning(this, "Terminated already.");
      return;
    }

    synchronized (Persistent.transactionLock(db)) {
      try {
        System.gc();
        db.rollback();
        System.gc();
        db.close();
        db = null;
      } catch (RuntimeException e) {
        Logger.error(this, "Error while closing database", e);
      }
    }
  }
예제 #6
0
  public static NPFPacket create(byte[] plaintext, BasePeerNode pn) {
    NPFPacket packet = new NPFPacket();
    if (pn == null)
      throw new IllegalArgumentException("Can't estimate an ack type of received packet");
    int offset = 0;

    if (plaintext.length < (offset + 5)) { // Sequence number + the number of acks
      packet.error = true;
      return packet;
    }

    packet.sequenceNumber =
        ((plaintext[offset] & 0xFF) << 24)
            | ((plaintext[offset + 1] & 0xFF) << 16)
            | ((plaintext[offset + 2] & 0xFF) << 8)
            | (plaintext[offset + 3] & 0xFF);
    offset += 4;

    // Process received acks

    int numAckRanges = plaintext[offset++] & 0xFF;
    if (numAckRanges > 0) {
      try {
        int ack, prevAck = 0;

        for (int i = 0; i < numAckRanges; i++) {
          if (i == 0) {
            ack =
                ((plaintext[offset] & 0xFF) << 24)
                    | ((plaintext[offset + 1] & 0xFF) << 16)
                    | ((plaintext[offset + 2] & 0xFF) << 8)
                    | (plaintext[offset + 3] & 0xFF);
            offset += 4;
          } else {
            int distanceFromPrevious = (plaintext[offset++] & 0xFF);
            if (distanceFromPrevious != 0) {
              ack = prevAck + distanceFromPrevious;
            } else {
              // Far offset
              ack =
                  ((plaintext[offset] & 0xFF) << 24)
                      | ((plaintext[offset + 1] & 0xFF) << 16)
                      | ((plaintext[offset + 2] & 0xFF) << 8)
                      | (plaintext[offset + 3] & 0xFF);
              offset += 4;
            }
          }

          int rangeSize = (plaintext[offset++] & 0xFF);
          for (int j = 1; j <= rangeSize; j++) {
            packet.acks.add(ack++);
          }

          prevAck = ack - 1;
        }
      } catch (ArrayIndexOutOfBoundsException e) {
        // The packet's length is not big enough
        packet.error = true;
        return packet;
      }
    }

    // Handle received message fragments
    int prevFragmentID = -1;
    while (offset < plaintext.length) {
      boolean shortMessage = (plaintext[offset] & 0x80) != 0;
      boolean isFragmented = (plaintext[offset] & 0x40) != 0;
      boolean firstFragment = (plaintext[offset] & 0x20) != 0;

      if (!isFragmented && !firstFragment) {
        // Padding or lossy messages.
        offset = tryParseLossyMessages(packet, plaintext, offset);
        break;
      }

      int messageID = -1;
      if ((plaintext[offset] & 0x10) != 0) {
        if (plaintext.length < (offset + 4)) {
          packet.error = true;
          return packet;
        }

        messageID =
            ((plaintext[offset] & 0x0F) << 24)
                | ((plaintext[offset + 1] & 0xFF) << 16)
                | ((plaintext[offset + 2] & 0xFF) << 8)
                | (plaintext[offset + 3] & 0xFF);
        offset += 4;
      } else {
        if (plaintext.length < (offset + 2)) {
          packet.error = true;
          return packet;
        }

        if (prevFragmentID == -1) {
          Logger.warning(NPFPacket.class, "First fragment doesn't have full message id");
          packet.error = true;
          return packet;
        }
        messageID =
            prevFragmentID + (((plaintext[offset] & 0x0F) << 8) | (plaintext[offset + 1] & 0xFF));
        offset += 2;
      }
      prevFragmentID = messageID;

      int requiredLength =
          offset + (shortMessage ? 1 : 2) + (isFragmented ? (shortMessage ? 1 : 3) : 0);
      if (plaintext.length < requiredLength) {
        packet.error = true;
        return packet;
      }

      int fragmentLength;
      if (shortMessage) {
        fragmentLength = plaintext[offset++] & 0xFF;
      } else {
        fragmentLength = ((plaintext[offset] & 0xFF) << 8) | (plaintext[offset + 1] & 0xFF);
        offset += 2;
      }

      int messageLength = -1;
      int fragmentOffset = 0;
      if (isFragmented) {
        int value;
        if (shortMessage) {
          value = plaintext[offset++] & 0xFF;
        } else {
          value = ((plaintext[offset] & 0xFF) << 8) | (plaintext[offset + 1] & 0xFF);
          offset += 2;
        }

        if (firstFragment) {
          messageLength = value;
          if (messageLength == fragmentLength) {
            Logger.warning(
                NPFPacket.class,
                "Received fragmented message, but fragment contains the entire message");
          }
        } else {
          fragmentOffset = value;
        }
      } else {
        messageLength = fragmentLength;
      }
      if ((offset + fragmentLength) > plaintext.length) {
        Logger.error(
            NPFPacket.class,
            "Fragment doesn't fit in the received packet: offset is "
                + offset
                + " fragment length is "
                + fragmentLength
                + " plaintext length is "
                + plaintext.length
                + " message length "
                + messageLength
                + " message ID "
                + messageID
                + (pn == null ? "" : (" from " + pn.shortToString())));
        packet.error = true;
        break;
      }
      byte[] fragmentData = Arrays.copyOfRange(plaintext, offset, offset + fragmentLength);
      offset += fragmentLength;

      packet.fragments.add(
          new MessageFragment(
              shortMessage,
              isFragmented,
              firstFragment,
              messageID,
              fragmentLength,
              messageLength,
              fragmentOffset,
              fragmentData,
              null));
    }

    packet.length = offset;

    return packet;
  }
예제 #7
0
파일: NPFPacket.java 프로젝트: saces/fred
  public static NPFPacket create(byte[] plaintext, BasePeerNode pn) {
    NPFPacket packet = new NPFPacket();
    int offset = 0;

    if (plaintext.length < (offset + 5)) { // Sequence number + the number of acks
      packet.error = true;
      return packet;
    }

    packet.sequenceNumber =
        ((plaintext[offset] & 0xFF) << 24)
            | ((plaintext[offset + 1] & 0xFF) << 16)
            | ((plaintext[offset + 2] & 0xFF) << 8)
            | (plaintext[offset + 3] & 0xFF);
    offset += 4;

    // Process received acks
    int numAcks = plaintext[offset++] & 0xFF;
    if (plaintext.length < (offset + numAcks + (numAcks > 0 ? 3 : 0))) {
      packet.error = true;
      return packet;
    }

    int prevAck = 0;
    for (int i = 0; i < numAcks; i++) {
      int ack = 0;
      if (i == 0) {
        ack =
            ((plaintext[offset] & 0xFF) << 24)
                | ((plaintext[offset + 1] & 0xFF) << 16)
                | ((plaintext[offset + 2] & 0xFF) << 8)
                | (plaintext[offset + 3] & 0xFF);
        offset += 4;
      } else {
        ack = prevAck + (plaintext[offset++] & 0xFF);
      }
      packet.acks.add(ack);
      prevAck = ack;
    }

    // Handle received message fragments
    int prevFragmentID = -1;
    while (offset < plaintext.length) {
      boolean shortMessage = (plaintext[offset] & 0x80) != 0;
      boolean isFragmented = (plaintext[offset] & 0x40) != 0;
      boolean firstFragment = (plaintext[offset] & 0x20) != 0;

      if (!isFragmented && !firstFragment) {
        // Padding or lossy messages.
        offset = tryParseLossyMessages(packet, plaintext, offset);
        break;
      }

      int messageID = -1;
      if ((plaintext[offset] & 0x10) != 0) {
        if (plaintext.length < (offset + 4)) {
          packet.error = true;
          return packet;
        }

        messageID =
            ((plaintext[offset] & 0x0F) << 24)
                | ((plaintext[offset + 1] & 0xFF) << 16)
                | ((plaintext[offset + 2] & 0xFF) << 8)
                | (plaintext[offset + 3] & 0xFF);
        offset += 4;
      } else {
        if (plaintext.length < (offset + 2)) {
          packet.error = true;
          return packet;
        }

        if (prevFragmentID == -1) {
          Logger.warning(NPFPacket.class, "First fragment doesn't have full message id");
          packet.error = true;
          return packet;
        }
        messageID =
            prevFragmentID + (((plaintext[offset] & 0x0F) << 8) | (plaintext[offset + 1] & 0xFF));
        offset += 2;
      }
      prevFragmentID = messageID;

      int requiredLength =
          offset + (shortMessage ? 1 : 2) + (isFragmented ? (shortMessage ? 1 : 3) : 0);
      if (plaintext.length < requiredLength) {
        packet.error = true;
        return packet;
      }

      int fragmentLength;
      if (shortMessage) {
        fragmentLength = plaintext[offset++] & 0xFF;
      } else {
        fragmentLength = ((plaintext[offset] & 0xFF) << 8) | (plaintext[offset + 1] & 0xFF);
        offset += 2;
      }

      int messageLength = -1;
      int fragmentOffset = 0;
      if (isFragmented) {
        int value;
        if (shortMessage) {
          value = plaintext[offset++] & 0xFF;
        } else {
          value = ((plaintext[offset] & 0xFF) << 8) | (plaintext[offset + 1] & 0xFF);
          offset += 2;
        }

        if (firstFragment) {
          messageLength = value;
          if (messageLength == fragmentLength) {
            Logger.warning(
                NPFPacket.class,
                "Received fragmented message, but fragment contains the entire message");
          }
        } else {
          fragmentOffset = value;
        }
      } else {
        messageLength = fragmentLength;
      }
      byte[] fragmentData = new byte[fragmentLength];
      if ((offset + fragmentLength) > plaintext.length) {
        Logger.error(
            NPFPacket.class,
            "Fragment doesn't fit in the received packet: offset is "
                + offset
                + " fragment length is "
                + fragmentLength
                + " plaintext length is "
                + plaintext.length
                + " message length "
                + messageLength
                + " message ID "
                + messageID
                + (pn == null ? "" : (" from " + pn.shortToString())));
        packet.error = true;
        break;
      }
      System.arraycopy(plaintext, offset, fragmentData, 0, fragmentLength);
      offset += fragmentLength;

      packet.fragments.add(
          new MessageFragment(
              shortMessage,
              isFragmented,
              firstFragment,
              messageID,
              fragmentLength,
              messageLength,
              fragmentOffset,
              fragmentData,
              null));
    }

    packet.length = offset;

    return packet;
  }
 /**
  * Compare a recently registered SendableRequest to what is already on the starter queue. If it is
  * better, kick out stuff from the queue until we are just over the limit.
  *
  * @param req
  * @param container
  */
 public void maybeAddToStarterQueue(
     SendableRequest req, ObjectContainer container, SendableRequest[] mightBeActive) {
   short prio = req.getPriorityClass(container);
   if (logMINOR) Logger.minor(this, "Maybe adding to starter queue: prio=" + prio);
   synchronized (starterQueue) {
     boolean betterThanSome = false;
     int size = 0;
     PersistentChosenRequest prev = null;
     for (PersistentChosenRequest old : starterQueue) {
       if (old.request == req) {
         // Wait for a reselect. Otherwise we can starve other
         // requests. Note that this happens with persistent SBI's:
         // they are added at the new retry count before being
         // removed at the old retry count.
         if (logMINOR) Logger.minor(this, "Already on starter queue: " + old + " for " + req);
         return;
       }
       if (prev == old)
         Logger.error(this, "ON STARTER QUEUE TWICE: " + prev + " for " + prev.request);
       if (prev != null && prev.request == old.request)
         Logger.error(
             this,
             "REQUEST ON STARTER QUEUE TWICE: "
                 + prev
                 + " for "
                 + prev.request
                 + " vs "
                 + old
                 + " for "
                 + old.request);
       boolean ignoreActive = false;
       if (mightBeActive != null) {
         for (SendableRequest tmp : mightBeActive) if (tmp == old.request) ignoreActive = true;
       }
       if (!ignoreActive) {
         if (container.ext().isActive(old.request))
           Logger.warning(
               this,
               "REQUEST ALREADY ACTIVATED: "
                   + old.request
                   + " for "
                   + old
                   + " while checking request queue in maybeAddToStarterQueue for "
                   + req);
         else if (logDEBUG)
           Logger.debug(
               this,
               "Not already activated for "
                   + old
                   + " in while checking request queue in maybeAddToStarterQueue for "
                   + req);
       } else if (logMINOR)
         Logger.minor(
             this,
             "Ignoring active because just registered: "
                 + old.request
                 + " in maybeAddToStarterQueue for "
                 + req);
       size += old.sizeNotStarted();
       if (old.prio > prio) betterThanSome = true;
       if (old.request == req) return;
       prev = old;
     }
     if (size >= MAX_STARTER_QUEUE_SIZE && !betterThanSome) {
       if (logMINOR)
         Logger.minor(
             this,
             "Not adding to starter queue: over limit and req not better than any queued requests");
       return;
     }
   }
   addToStarterQueue(req, container);
   trimStarterQueue(container);
 }
  private void fillRequestStarterQueue(ObjectContainer container, ClientContext context) {
    synchronized (this) {
      if (fillingRequestStarterQueue) return;
      fillingRequestStarterQueue = true;
    }
    long now = System.currentTimeMillis();
    try {
      if (logMINOR)
        Logger.minor(
            this,
            "Filling request queue... (SSK=" + isSSKScheduler + " insert=" + isInsertScheduler);
      long noLaterThan = Long.MAX_VALUE;
      boolean checkCooldownQueue = now > nextQueueFillRequestStarterQueue;
      if ((!isInsertScheduler) && checkCooldownQueue) {
        if (persistentCooldownQueue != null)
          noLaterThan = moveKeysFromCooldownQueue(persistentCooldownQueue, true, container);
        noLaterThan =
            Math.min(
                noLaterThan, moveKeysFromCooldownQueue(transientCooldownQueue, false, container));
      }
      // If anything has been re-added, the request starter will have been woken up.
      short fuzz = -1;
      if (PRIORITY_SOFT.equals(choosenPriorityScheduler)) fuzz = -1;
      else if (PRIORITY_HARD.equals(choosenPriorityScheduler)) fuzz = 0;
      boolean added = false;
      synchronized (starterQueue) {
        if (logMINOR && (!isSSKScheduler) && (!isInsertScheduler)) {
          Logger.minor(this, "Scheduling CHK fetches...");
          for (SendableRequest req : runningPersistentRequests) {
            boolean wasActive = container.ext().isActive(req);
            if (!wasActive) container.activate(req, 1);
            Logger.minor(this, "Running persistent request: " + req);
            if (!wasActive) container.deactivate(req, 1);
          }
        }
        // Recompute starterQueueLength
        int length = 0;
        PersistentChosenRequest old = null;
        for (PersistentChosenRequest req : starterQueue) {
          if (old == req) Logger.error(this, "DUPLICATE CHOSEN REQUESTS ON QUEUE: " + req);
          if (old != null && old.request == req.request)
            Logger.error(
                this, "DUPLICATE REQUEST ON QUEUE: " + old + " vs " + req + " both " + req.request);
          boolean ignoreActive = false;
          if (!ignoreActive) {
            if (container.ext().isActive(req.request))
              Logger.warning(
                  this,
                  "REQUEST ALREADY ACTIVATED: "
                      + req.request
                      + " for "
                      + req
                      + " while checking request queue in filling request queue");
            else if (logMINOR)
              Logger.minor(
                  this,
                  "Not already activated for "
                      + req
                      + " in while checking request queue in filling request queue");
          } else if (logMINOR)
            Logger.minor(this, "Ignoring active because just registered: " + req.request);
          req.pruneDuplicates(ClientRequestScheduler.this);
          old = req;
          length += req.sizeNotStarted();
        }
        if (logMINOR)
          Logger.minor(
              this,
              "Queue size: " + length + " SSK=" + isSSKScheduler + " insert=" + isInsertScheduler);
        if (length > MAX_STARTER_QUEUE_SIZE * 3 / 4) {
          if (length >= WARNING_STARTER_QUEUE_SIZE)
            Logger.error(this, "Queue already full: " + length);
          return;
        }
      }

      if ((!isSSKScheduler) && (!isInsertScheduler)) {
        Logger.minor(this, "Scheduling CHK fetches...");
      }
      boolean addedMore = false;
      while (true) {
        SelectorReturn r;
        // Must synchronize on scheduler to avoid problems with cooldown queue. See notes on
        // CooldownTracker.clearCachedWakeup, which also applies to other cooldown operations.
        synchronized (this) {
          r =
              selector.removeFirstInner(
                  fuzz,
                  random,
                  offeredKeys,
                  starter,
                  schedCore,
                  schedTransient,
                  false,
                  true,
                  Short.MAX_VALUE,
                  isRTScheduler,
                  context,
                  container,
                  now);
        }
        SendableRequest request = null;
        if (r != null && r.req != null) request = r.req;
        else {
          if (r != null && r.wakeupTime > 0 && noLaterThan > r.wakeupTime) {
            noLaterThan = r.wakeupTime;
            if (logMINOR)
              Logger.minor(
                  this,
                  "Waking up in " + TimeUtil.formatTime(noLaterThan - now) + " for cooldowns");
          }
        }
        if (request == null) {
          synchronized (ClientRequestScheduler.this) {
            // Don't wake up for a while, but no later than the time we expect the next item to come
            // off the cooldown queue
            if (checkCooldownQueue && !added) {
              nextQueueFillRequestStarterQueue =
                  System.currentTimeMillis() + WAIT_AFTER_NOTHING_TO_START;
              if (nextQueueFillRequestStarterQueue > noLaterThan)
                nextQueueFillRequestStarterQueue = noLaterThan + 1;
            }
          }
          if (addedMore) starter.wakeUp();
          return;
        }
        boolean full = addToStarterQueue(request, container);
        container.deactivate(request, 1);
        if (!added) starter.wakeUp();
        else addedMore = true;
        added = true;
        if (full) {
          if (addedMore) starter.wakeUp();
          return;
        }
      }
    } finally {
      synchronized (this) {
        fillingRequestStarterQueue = false;
      }
    }
  }
예제 #10
0
  /**
   * Imports a identity XML file into the given web of trust. This includes: - The identity itself
   * and its attributes - The trust list of the identity, if it has published one in the XML.
   *
   * @param xmlInputStream The input stream containing the XML.
   */
  public void importIdentity(FreenetURI identityURI, InputStream xmlInputStream) {
    try { // Catch import problems so we can mark the edition as parsing failed
      // We first parse the XML without synchronization, then do the synchronized import into the
      // WebOfTrust
      final ParsedIdentityXML xmlData = parseIdentityXML(xmlInputStream);

      synchronized (mWoT) {
        synchronized (mWoT.getIdentityFetcher()) {
          final Identity identity = mWoT.getIdentityByURI(identityURI);

          Logger.normal(this, "Importing parsed XML for " + identity);

          long newEdition = identityURI.getEdition();
          if (identity.getEdition() > newEdition) {
            if (logDEBUG)
              Logger.debug(
                  this,
                  "Fetched an older edition: current == "
                      + identity.getEdition()
                      + "; fetched == "
                      + identityURI.getEdition());
            return;
          } else if (identity.getEdition() == newEdition) {
            if (identity.getCurrentEditionFetchState() == FetchState.Fetched) {
              if (logDEBUG)
                Logger.debug(
                    this,
                    "Fetched current edition which is marked as fetched already, not importing: "
                        + identityURI);
              return;
            } else if (identity.getCurrentEditionFetchState() == FetchState.ParsingFailed) {
              Logger.normal(
                  this,
                  "Re-fetched current-edition which was marked as parsing failed: " + identityURI);
            }
          }

          // We throw parse errors AFTER checking the edition number: If this XML was outdated
          // anyway, we don't have to throw.
          if (xmlData.parseError != null) throw xmlData.parseError;

          synchronized (Persistent.transactionLock(mDB)) {
            try { // Transaction rollback block
              identity.setEdition(
                  newEdition); // The identity constructor only takes the edition number as a hint,
                               // so we must store it explicitly.
              boolean didPublishTrustListPreviously = identity.doesPublishTrustList();
              identity.setPublishTrustList(xmlData.identityPublishesTrustList);

              try {
                identity.setNickname(xmlData.identityName);
              } catch (Exception e) {
                /* Nickname changes are not allowed, ignore them... */
                Logger.warning(this, "setNickname() failed.", e);
              }

              try {
                  /* Failure of context importing should not make an identity disappear, therefore we catch exceptions. */
                identity.setContexts(xmlData.identityContexts);
              } catch (Exception e) {
                Logger.warning(this, "setContexts() failed.", e);
              }

              try {
                  /* Failure of property importing should not make an identity disappear, therefore we catch exceptions. */
                identity.setProperties(xmlData.identityProperties);
              } catch (Exception e) {
                Logger.warning(this, "setProperties() failed", e);
              }

              mWoT
                  .beginTrustListImport(); // We delete the old list if !identityPublishesTrustList
                                           // and it did publish one earlier => we always call this.

              if (xmlData.identityPublishesTrustList) {
                // We import the trust list of an identity if it's score is equal to 0, but we only
                // create new identities or import edition hints
                // if the score is greater than 0. Solving a captcha therefore only allows you to
                // create one single identity.
                boolean positiveScore = false;
                boolean hasCapacity = false;

                // TODO: getBestScore/getBestCapacity should always yield a positive result because
                // we store a positive score object for an OwnIdentity
                // upon creation. The only case where it could not exist might be
                // restoreOwnIdentity() ... check that. If it is created there as well,
                // remove the additional check here.
                if (identity instanceof OwnIdentity) {
                  // Importing of OwnIdentities is always allowed
                  positiveScore = true;
                  hasCapacity = true;
                } else {
                  try {
                    positiveScore = mWoT.getBestScore(identity) > 0;
                    hasCapacity = mWoT.getBestCapacity(identity) > 0;
                  } catch (NotInTrustTreeException e) {
                  }
                }

                HashSet<String> identitiesWithUpdatedEditionHint = null;

                if (positiveScore) {
                  identitiesWithUpdatedEditionHint =
                      new HashSet<String>(xmlData.identityTrustList.size() * 2);
                }

                for (final ParsedIdentityXML.TrustListEntry trustListEntry :
                    xmlData.identityTrustList) {
                  final FreenetURI trusteeURI = trustListEntry.mTrusteeURI;
                  final byte trustValue = trustListEntry.mTrustValue;
                  final String trustComment = trustListEntry.mTrustComment;

                  Identity trustee = null;
                  try {
                    trustee = mWoT.getIdentityByURI(trusteeURI);
                    if (positiveScore) {
                      if (trustee.setNewEditionHint(trusteeURI.getEdition())) {
                        identitiesWithUpdatedEditionHint.add(trustee.getID());
                        trustee.storeWithoutCommit();
                      }
                    }
                  } catch (UnknownIdentityException e) {
                    if (hasCapacity) {
                        /* We only create trustees if the truster has capacity to rate them. */
                      try {
                        trustee = new Identity(mWoT, trusteeURI, null, false);
                        trustee.storeWithoutCommit();
                      } catch (MalformedURLException urlEx) {
                        // Logging the exception does NOT log the actual malformed URL so we do it
                        // manually.
                        Logger.warning(
                            this, "Received malformed identity URL: " + trusteeURI, urlEx);
                        throw urlEx;
                      }
                    }
                  }

                  if (trustee != null)
                    mWoT.setTrustWithoutCommit(identity, trustee, trustValue, trustComment);
                }

                for (Trust trust :
                    mWoT.getGivenTrustsOfDifferentEdition(identity, identityURI.getEdition())) {
                  mWoT.removeTrustWithoutCommit(trust);
                }

                IdentityFetcher identityFetcher = mWoT.getIdentityFetcher();
                if (positiveScore) {
                  for (String id : identitiesWithUpdatedEditionHint)
                    identityFetcher.storeUpdateEditionHintCommandWithoutCommit(id);

                  // We do not have to store fetch commands for new identities here,
                  // setTrustWithoutCommit does it.
                }
              } else if (!xmlData.identityPublishesTrustList
                  && didPublishTrustListPreviously
                  && !(identity instanceof OwnIdentity)) {
                // If it does not publish a trust list anymore, we delete all trust values it has
                // given.
                for (Trust trust : mWoT.getGivenTrusts(identity))
                  mWoT.removeTrustWithoutCommit(trust);
              }

              mWoT.finishTrustListImport();
              identity.onFetched(); // Marks the identity as parsed successfully
              identity.storeAndCommit();
            } catch (Exception e) {
              mWoT.abortTrustListImport(e, Logger.LogLevel.WARNING); // Does the rollback
              throw e;
            } // try
          } // synchronized(Persistent.transactionLock(db))

          Logger.normal(this, "Finished XML import for " + identity);
        } // synchronized(mWoT)
      } // synchronized(mWoT.getIdentityFetcher())
    } // try
    catch (Exception e) {
      synchronized (mWoT) {
        synchronized (mWoT.getIdentityFetcher()) {
          try {
            final Identity identity = mWoT.getIdentityByURI(identityURI);
            final long newEdition = identityURI.getEdition();
            if (identity.getEdition() <= newEdition) {
              Logger.normal(this, "Marking edition as parsing failed: " + identityURI);
              try {
                identity.setEdition(newEdition);
              } catch (InvalidParameterException e1) {
                // Would only happen if newEdition < current edition.
                // We have validated the opposite.
                throw new RuntimeException(e1);
              }
              identity.onParsingFailed();
              identity.storeAndCommit();
            } else {
              Logger.normal(
                  this,
                  "Not marking edition as parsing failed, we have already fetched a new one ("
                      + identity.getEdition()
                      + "):"
                      + identityURI);
            }
            Logger.normal(this, "Parsing identity XML failed gracefully for " + identityURI, e);
          } catch (UnknownIdentityException uie) {
            Logger.error(this, "Fetched an unknown identity: " + identityURI);
          }
        }
      }
    }
  }