public void checkServerTrusted(X509Certificate[] chain, String authType)
      throws CertificateException {
    calledCheckServerTrusted = true;
    if (!authType.equals("RSA") && !authType.equals("DHE_RSA"))
      throw new CertificateException("Only RSA and DHE_RSA supported, not " + authType);

    if (chain.length != 3) throw new CertificateException("Need three certificates");

    X509Certificate cert0 = chain[0];
    X509Certificate cert1 = chain[1];
    X509Certificate cert2 = chain[2];
    int failedCert = 0;
    try {
      // NOTE: cert0 is a self-signed key used only for SSL
      failedCert = 0;
      cert0.verify(cert0.getPublicKey());
      // NOTE: cert1 is the SSL key signed by the server's Martus key
      failedCert = 1;
      cert1.verify(cert2.getPublicKey());
      // NOTE: cert2 is a self-signed key of and by the server's Martus key
      failedCert = 2;
      cert2.verify(cert2.getPublicKey());
      failedCert = -1;

      PublicKey tryPublicKey = expectedPublicKey;
      if (tryPublicKey == null) {
        if (expectedPublicCode == null) throw new CertificateException("No key or code is trusted");
        String certPublicKeyString = SimpleX509TrustManager.getKeyString(cert2.getPublicKey());
        String certPublicCode = MartusCrypto.computePublicCode(certPublicKeyString);
        if (expectedPublicCode.equals(certPublicCode)) {
          tryPublicKey = cert2.getPublicKey();
        }
      }

      if (tryPublicKey == null) throw new CertificateException("Key is not trusted");
      cert1.verify(tryPublicKey);
      String keyString = SimpleX509TrustManager.getKeyString(tryPublicKey);
      setExpectedPublicKey(keyString);
    } catch (SignatureException e) {
      MartusLogger.logException(e);
      MartusLogger.log("Failed cert: " + failedCert);
      String key0 = SimpleX509TrustManager.getKeyString(cert0.getPublicKey());
      String key1 = SimpleX509TrustManager.getKeyString(cert1.getPublicKey());
      String key2 = SimpleX509TrustManager.getKeyString(cert2.getPublicKey());
      MartusLogger.log("Cert0 public: " + key0);
      if (!key0.equals(key1)) MartusLogger.log("Cert1 public: " + key1);
      MartusLogger.log("Cert2 public: " + key2);
      MartusLogger.log("Cert2 public code: " + MartusCrypto.formatAccountIdForLog(key2));
      String expectedKeyString = SimpleX509TrustManager.getKeyString(expectedPublicKey);
      MartusLogger.log(
          "Expected public code: " + MartusCrypto.formatAccountIdForLog(expectedKeyString));

      throw new CertificateException(e.toString());
    } catch (Exception e) {
      // Tests will cause this to fire
      MartusLogger.logException(e);
      throw new CertificateException(e.toString());
    }
  }
  public void loadFrom(File emailNotificationsFile) throws Exception {
    if (!emailNotificationsFile.exists()) {
      MartusLogger.logWarning("Missing: " + emailNotificationsFile);
      return;
    }

    UnicodeReader reader = new UnicodeReader(emailNotificationsFile);
    try {
      String line = reader.readLine();
      while (line != null) {
        processLine(line);
        line = reader.readLine();
      }
    } finally {
      reader.close();
    }

    MartusLogger.log("Will send email notifications to " + recipientWithHosts.toString());
  }
  protected BulletinMirroringInformation getNextItemToRetrieve() {
    try {
      while (itemsToRetrieve.size() == 0) {
        String nextAccountId = getNextAccountToRetrieve();
        if (nextAccountId == null) return null;

        int totalIdsReturned = 0;
        String mirroringCallUsed = "listAvailableIdsForMirroring";
        NetworkResponse response =
            gateway.listAvailableIdsForMirroring(getSecurity(), nextAccountId);
        if (networkResponseOk(response)) {
          Vector listwithBulletinMirroringInfo = response.getResultVector();
          totalIdsReturned = listwithBulletinMirroringInfo.size();
          itemsToRetrieve =
              listOnlyPacketsThatWeWantUsingBulletinMirroringInformation(
                  nextAccountId, listwithBulletinMirroringInfo);
        } else {
          mirroringCallUsed = "OLD MIRRORING CALL(listBulletinsForMirroring)";
          response = gateway.listBulletinsForMirroring(getSecurity(), nextAccountId);
          if (networkResponseOk(response)) {
            Vector listWithLocalIds = response.getResultVector();
            totalIdsReturned = listWithLocalIds.size();
            itemsToRetrieve =
                listOnlyPacketsThatWeWantUsingLocalIds(nextAccountId, listWithLocalIds);
          }
        }

        if (networkResponseOk(response)) {
          String publicCode = MartusCrypto.getFormattedPublicCode(nextAccountId);
          if (totalIdsReturned > 0 || itemsToRetrieve.size() > 0)
            logInfo(
                mirroringCallUsed
                    + ": "
                    + publicCode
                    + " -> "
                    + totalIdsReturned
                    + " -> "
                    + itemsToRetrieve.size());
        } else {
          logWarning(
              "MirroringRetriever.getNextItemToRetrieve: Returned NetworkResponse: "
                  + response.getResultCode());
        }
      }

      if (itemsToRetrieve.size() == 0) return null;

      return (BulletinMirroringInformation) itemsToRetrieve.remove(0);

    } catch (Exception e) {
      logError("MirroringRetriever.getNextUidToRetrieve: ", e);
      MartusLogger.logException(e);
      return null;
    }
  }
  private void createAccountMap(String[] args) throws Exception {
    processArgs(args);
    if (prompt) System.out.println("CreateAccountMap");

    File packetsDirectory = new File(packetDirName);
    if (noisy) MartusLogger.log("Packets directory: " + packetsDirectory);

    security = MartusServerUtilities.loadKeyPair(keyPairFileName, prompt);
    db = new ServerFileDatabase(packetsDirectory, security);
    File mapFile = db.getAccountMapFile();

    if (noisy) MartusLogger.log("Account map file: " + mapFile.getAbsolutePath());
    if (mapFile.exists()) {
      System.err.println("Cannot create account map because it already exists");
      System.exit(1);
    }

    processPacketsDirectory(packetsDirectory);
    if (prompt) MartusLogger.log("Finished");
  }
 protected Vector listOnlyPacketsThatWeWantUsingBulletinMirroringInformation(
     String accountId, Vector listWithMirroringInfo) {
   Vector dataToRetrieve = new Vector();
   for (int i = 0; i < listWithMirroringInfo.size(); ++i) {
     if (listWithMirroringInfo.get(i) instanceof Vector)
       MartusLogger.log("Found vector instead of array");
     Object[] infoArray = (Object[]) listWithMirroringInfo.get(i);
     Vector info = new Vector(Arrays.asList(infoArray));
     BulletinMirroringInformation mirroringInfo =
         new BulletinMirroringInformation(accountId, info);
     if (doWeWantThis(mirroringInfo)) dataToRetrieve.add(mirroringInfo);
   }
   return dataToRetrieve;
 }
  private String getAccountIdFromAccountDirectory(File accountDirectory) throws Exception {
    FileSet packetBucketDirectories = getAllPacketBucketDirectories(accountDirectory);

    for (File packetBucketDirectory : packetBucketDirectories) {
      FileSet headerPackets = getFiles(packetBucketDirectory, getHeaderPacketFilter());
      if (headerPackets.size() > 0) {
        File firstHeaderPacket = headerPackets.iterator().next();
        String accountId = extractAccountId(firstHeaderPacket);
        return accountId;
      }
    }

    MartusLogger.logWarning("No packets in " + accountDirectory.getAbsolutePath());
    return null;
  }
  private void processPacketsDirectory(File packetsDirectory) throws Exception {
    FileSet accountBuckets = getFiles(packetsDirectory, getAccountBucketFilter());

    for (File accountBucket : accountBuckets) {
      File[] accountDirectories = accountBucket.listFiles(getAccountDirectoryFilter());
      if (noisy)
        MartusLogger.log(
            "Processing " + accountBucket.getName() + ": " + accountDirectories.length);

      for (File accountDirectory : accountDirectories) {
        String accountId = getAccountIdFromAccountDirectory(accountDirectory);
        if (accountId != null) {
          writeAccountDirectoryIdentificationFile(accountDirectory, accountId);
          db.appendAccountToMapFile(accountId, accountDirectory.getAbsolutePath());
        }
      }
    }
  }
  private void processLine(String line) throws Exception {
    int commentAt = line.indexOf('#');
    if (commentAt >= 0) line = line.substring(0, commentAt);

    if (line.trim().length() == 0) return;

    String[] parts = line.split(",");
    if (parts.length < 2) {
      MartusLogger.logError("Ignoring illegal line in email notifications: " + line);
      return;
    }

    String recipient = parts[0].trim();
    String hosts[] = new String[parts.length - 1];
    System.arraycopy(parts, 1, hosts, 0, hosts.length);
    for (int i = 0; i < hosts.length; ++i) hosts[i] = hosts[i].trim();
    recipientWithHosts = new RecipientWithSmtpHosts(recipient, hosts);
  }
  @Override
  public String toString(ContactKey contactKey) {
    if (contactKey == null) return "";

    String storableLabel = contactKey.getLabel();
    String displayableLabel =
        new UiFontEncodingHelper(FontHandler.isDoZawgyiConversion()).getDisplayable(storableLabel);
    try {
      String contactsCompleteName;
      String publicCode = contactKey.getFormattedPublicCode40();
      if (displayableLabel.length() > 0)
        contactsCompleteName = String.format("%s (%s)", displayableLabel, publicCode);
      else contactsCompleteName = publicCode;
      return contactsCompleteName;
    } catch (Exception e) {
      MartusLogger.logException(e);
      return "[Error]";
    }
  }
  private FieldSpec convertToFieldSpec(FormEntryPrompt questionPrompt) {
    QuestionDef question = questionPrompt.getQuestion();
    final int dataType = questionPrompt.getDataType();
    TreeReference reference = (TreeReference) question.getBind().getReference();
    String tag = reference.getNameLast();
    String questionLabel = questionPrompt.getQuestion().getLabelInnerText();

    if (questionPrompt.isReadOnly())
      return FieldSpec.createCustomField(tag, questionLabel, new FieldTypeMessage());

    if (isNormalFieldType(dataType))
      return FieldSpec.createCustomField(tag, questionLabel, new FieldTypeNormal());

    if (dataType == Constants.DATATYPE_DATE)
      return FieldSpec.createCustomField(tag, questionLabel, new FieldTypeDate());

    if (shouldTreatSingleItemChoiceListAsBooleanField(dataType, question))
      return FieldSpec.createCustomField(tag, questionLabel, new FieldTypeBoolean());

    if (dataType == Constants.DATATYPE_CHOICE) {
      Vector<ChoiceItem> convertedChoices = new Vector<ChoiceItem>();
      List<SelectChoice> choicesToConvert = question.getChoices();
      for (SelectChoice choiceToConvert : choicesToConvert) {
        // String choiceItemCode = choiceToConvert.getValue();
        String choiceItemLabel = choiceToConvert.getLabelInnerText();
        // Martus doesn't keep Code's when exporting so use Label twice instead
        convertedChoices.add(new ChoiceItem(choiceItemLabel, choiceItemLabel));
      }

      FieldSpec fieldSpec = new CustomDropDownFieldSpec(convertedChoices);
      fieldSpec.setTag(tag);
      fieldSpec.setLabel(questionLabel);
      return fieldSpec;
    }
    MartusLogger.log(
        "Warning: BulletinFromXFormsLoader.convertToFieldSpec unknown Field Type:"
            + String.valueOf(dataType));
    return null;
  }