Commit message: security fixes

https://hhvm.com/blog/2021/02/25/security-update.html
CVE: CVE-2020-1918
CWE: CWE-190
Vulnerability description: In-memory file operations (ie: using fopen on a data URI) did not properly restrict negative seeking, allowing for the reading of memory prior to the in-memory buffer. This issue affects HHVM versions prior to 4.56.3, all versions between 4.57.0 and 4.80.1, all versions between 4.81.0 and 4.93.1, and versions 4.94.0, 4.95.0, 4.96.0, 4.97.0, 4.98.0.

Project: hhvm
File: ext_openssl.cpp

static inline bool php_openssl_config_check_syntax
(const char *section_label, const char *config_filename, const char *section,
 LHASH_OF(CONF_VALUE) *config) {
#else
static inline bool php_openssl_config_check_syntax
(const char *section_label, const char *config_filename, const char *section,
 LHASH *config) {
#endif

  X509V3_CTX ctx;
  X509V3_set_ctx_test(&ctx);
  X509V3_set_conf_lhash(&ctx, config);
  if (!X509V3_EXT_add_conf(config, &ctx, (char*)section, nullptr)) {
    raise_warning("Error loading %s section %s of %s",
                    section_label, section, config_filename);
    return false;
  }
  return true;
}

const StaticString
  s_config("config"),
  s_config_section_name("config_section_name"),
  s_digest_alg("digest_alg"),
  s_x509_extensions("x509_extensions"),
  s_req_extensions("req_extensions"),
  s_private_key_bits("private_key_bits"),
  s_private_key_type("private_key_type"),
  s_encrypt_key("encrypt_key"),
  s_curve_name("curve_name");

static bool php_openssl_parse_config(struct php_x509_request *req,
                                     const Array& args,
                                     std::vector<String> &strings) {
  req->config_filename =
    read_string(args, s_config, default_ssl_conf_filename, strings);
  req->section_name =
    read_string(args, s_config_section_name, "req", strings);
  req->global_config = CONF_load(nullptr, default_ssl_conf_filename, nullptr);
  req->req_config = CONF_load(nullptr, req->config_filename, nullptr);
  if (req->req_config == nullptr) {
    return false;
  }

  /* read in the oids */
  char *str = CONF_get_string(req->req_config, nullptr, "oid_file");
  if (str) {
    BIO *oid_bio = BIO_new_file(str, "r");
    if (oid_bio) {
      OBJ_create_objects(oid_bio);
      BIO_free(oid_bio);
    }
  }
  if (!add_oid_section(req)) {
    return false;
  }

  req->digest_name =
    read_string(args, s_digest_alg,
                CONF_get_string(req->req_config, req->section_name,
                                "default_md"),
                strings);

  req->extensions_section =
    read_string(args, s_x509_extensions,
                CONF_get_string(req->req_config, req->section_name,
                                "x509_extensions"),
                strings);

  req->request_extensions_section =
    read_string(args, s_req_extensions,
                CONF_get_string(req->req_config, req->section_name,
                                "req_extensions"),
                strings);

  req->priv_key_bits =
    read_integer(args, s_private_key_bits,
                 CONF_get_number(req->req_config, req->section_name,
                                 "default_bits"));

  req->priv_key_type =
    read_integer(args, s_private_key_type, OPENSSL_KEYTYPE_DEFAULT);

  if (args.exists(s_encrypt_key)) {
    bool value = args[s_encrypt_key].toBoolean();
    req->priv_key_encrypt = value ? 1 : 0;
  } else {
    str = CONF_get_string(req->req_config, req->section_name,
                          "encrypt_rsa_key");
    if (str == nullptr) {
      str = CONF_get_string(req->req_config, req->section_name, "encrypt_key");
    }
    if (str && strcmp(str, "no") == 0) {
      req->priv_key_encrypt = 0;
    } else {
      req->priv_key_encrypt = 1;
    }
  }

  /* digest alg */
  if (req->digest_name == nullptr) {
    req->digest_name = CONF_get_string(req->req_config, req->section_name,
                                       "default_md");
  }
  if (req->digest_name) {
    req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name);
  }
  if (req->md_alg == nullptr) {
    req->md_alg = req->digest = EVP_sha256();
  }

#ifdef HAVE_EVP_PKEY_EC
  /* set the ec group curve name */
  req->curve_name = NID_undef;
  if (args.exists(s_curve_name)) {
    auto const curve_name = args[s_curve_name].toString();
    req->curve_name = OBJ_sn2nid(curve_name.data());
    if (req->curve_name == NID_undef) {
      raise_warning(
        "Unknown elliptic curve (short) name %s",
        curve_name.data()
      );
      return false;
    }
  }
#endif

  if (req->extensions_section &&
      !php_openssl_config_check_syntax
      ("extensions_section", req->config_filename, req->extensions_section,
       req->req_config)) {
    return false;
  }

  /* set the string mask */
  str = CONF_get_string(req->req_config, req->section_name, "string_mask");
  if (str && !ASN1_STRING_set_default_mask_asc(str)) {
    raise_warning("Invalid global string mask setting %s", str);
    return false;
  }

  if (req->request_extensions_section &&
      !php_openssl_config_check_syntax
      ("request_extensions_section", req->config_filename,
       req->request_extensions_section, req->req_config)) {
    return false;
  }

  return true;
}

static void php_openssl_dispose_config(struct php_x509_request *req) {
  if (req->global_config) {
    CONF_free(req->global_config);
    req->global_config = nullptr;
  }
  if (req->req_config) {
    CONF_free(req->req_config);
    req->req_config = nullptr;
  }
}

static STACK_OF(X509) *load_all_certs_from_file(const char *certfile) {
  STACK_OF(X509_INFO) *sk = nullptr;
  STACK_OF(X509) *stack = nullptr, *ret = nullptr;
  BIO *in = nullptr;
  X509_INFO *xi;

  if (!(stack = sk_X509_new_null())) {
    raise_warning("memory allocation failure");
    goto end;
  }

  if (!(in = BIO_new_file(certfile, "r"))) {
    raise_warning("error opening the file, %s", certfile);
    sk_X509_free(stack);
    goto end;
  }

  /* This loads from a file, a stack of x509/crl/pkey sets */
  if (!(sk = PEM_X509_INFO_read_bio(in, nullptr, nullptr, nullptr))) {
    raise_warning("error reading the file, %s", certfile);
    sk_X509_free(stack);
    goto end;
  }

  /* scan over it and pull out the certs */
  while (sk_X509_INFO_num(sk)) {
    xi = sk_X509_INFO_shift(sk);
    if (xi->x509 != nullptr) {
      sk_X509_push(stack, xi->x509);
      xi->x509 = nullptr;
    }
    X509_INFO_free(xi);
  }
  if (!sk_X509_num(stack)) {
    raise_warning("no certificates in file, %s", certfile);
    sk_X509_free(stack);
    goto end;
  }
  ret = stack;

end:
  BIO_free(in);
  sk_X509_INFO_free(sk);

  return ret;
}

/**
 * calist is an array containing file and directory names.  create a
 * certificate store and add those certs to it for use in verification.
 */
static X509_STORE *setup_verify(const Array& calist) {
  X509_STORE *store = X509_STORE_new();
  if (store == nullptr) {
    return nullptr;
  }

  X509_LOOKUP *dir_lookup, *file_lookup;
  int ndirs = 0, nfiles = 0;
  for (ArrayIter iter(calist); iter; ++iter) {
    String item = iter.second().toString();

    struct stat sb;
    if (stat(item.data(), &sb) == -1) {
      raise_warning("unable to stat %s", item.data());
      continue;
    }

    if ((sb.st_mode & S_IFREG) == S_IFREG) {
      file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
      if (file_lookup == nullptr ||
          !X509_LOOKUP_load_file(file_lookup, item.data(),
                                 X509_FILETYPE_PEM)) {
        raise_warning("error loading file %s", item.data());
      } else {
        nfiles++;
      }
      file_lookup = nullptr;
    } else {
      dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
      if (dir_lookup == nullptr ||
          !X509_LOOKUP_add_dir(dir_lookup, item.data(), X509_FILETYPE_PEM)) {
        raise_warning("error loading directory %s", item.data());
      } else {
        ndirs++;
      }
      dir_lookup = nullptr;
    }
  }
  if (nfiles == 0) {
    file_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
    if (file_lookup) {
      X509_LOOKUP_load_file(file_lookup, nullptr, X509_FILETYPE_DEFAULT);
    }
  }
  if (ndirs == 0) {
    dir_lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
    if (dir_lookup) {
      X509_LOOKUP_add_dir(dir_lookup, nullptr, X509_FILETYPE_DEFAULT);
    }
  }
  return store;
}

///////////////////////////////////////////////////////////////////////////////

static bool add_entries(X509_NAME *subj, const Array& items) {
  for (ArrayIter iter(items); iter; ++iter) {
    auto const index = iter.first().toString();
    auto const item = iter.second().toString();
    int nid = OBJ_txt2nid(index.data());
    if (nid != NID_undef) {
      if (!X509_NAME_add_entry_by_NID(subj, nid, MBSTRING_ASC,
                                      (unsigned char*)item.data(),
                                      -1, -1, 0)) {
        raise_warning("dn: add_entry_by_NID %d -> %s (failed)",
                        nid, item.data());
        return false;
      }
    } else {
      raise_warning("dn: %s is not a recognized name", index.data());
    }
  }
  return true;
}

static bool php_openssl_make_REQ(struct php_x509_request *req, X509_REQ *csr,
                                 const Array& dn, const Array& attribs) {
  char *dn_sect = CONF_get_string(req->req_config, req->section_name,
                                  "distinguished_name");
  if (dn_sect == nullptr) return false;

  STACK_OF(CONF_VALUE) *dn_sk = CONF_get_section(req->req_config, dn_sect);
  if (dn_sk == nullptr) return false;

  char *attr_sect = CONF_get_string(req->req_config, req->section_name,
                                    "attributes");
  STACK_OF(CONF_VALUE) *attr_sk = nullptr;
  if (attr_sect) {
    attr_sk = CONF_get_section(req->req_config, attr_sect);
    if (attr_sk == nullptr) {
      return false;
    }
  }

  /* setup the version number: version 1 */
  if (X509_REQ_set_version(csr, 0L)) {
    X509_NAME *subj = X509_REQ_get_subject_name(csr);
    if (!add_entries(subj, dn)) return false;

    /* Finally apply defaults from config file */
    for (int i = 0; i < sk_CONF_VALUE_num(dn_sk); i++) {
      CONF_VALUE *v = sk_CONF_VALUE_value(dn_sk, i);
      char *type = v->name;

      int len = strlen(type);
      if (len < (int)sizeof("_default")) {
        continue;
      }
      len -= sizeof("_default") - 1;
      if (strcmp("_default", type + len) != 0) {
        continue;
      }
      if (len > 200) {
        len = 200;
      }
      char buffer[200 + 1]; /*200 + \0 !*/
      memcpy(buffer, type, len);
      buffer[len] = '\0';
      type = buffer;

      /* Skip past any leading X. X: X, etc to allow for multiple instances */
      for (char *str = type; *str; str++) {
        if (*str == ':' || *str == ',' || *str == '.') {
          str++;
          if (*str) {
            type = str;
          }
          break;
        }
      }
      /* if it is already set, skip this */
      int nid = OBJ_txt2nid(type);
      if (X509_NAME_get_index_by_NID(subj, nid, -1) >= 0) {
        continue;
      }
      if (!X509_NAME_add_entry_by_txt(subj, type, MBSTRING_ASC,
                                      (unsigned char*)v->value, -1, -1, 0)) {
        raise_warning("add_entry_by_txt %s -> %s (failed)", type, v->value);
        return false;
      }
      if (!X509_NAME_entry_count(subj)) {
        raise_warning("no objects specified in config file");
        return false;
      }
    }

    if (!add_entries(subj, attribs)) return false;

    if (attr_sk) {
      for (int i = 0; i < sk_CONF_VALUE_num(attr_sk); i++) {
        CONF_VALUE *v = sk_CONF_VALUE_value(attr_sk, i);
        /* if it is already set, skip this */
        int nid = OBJ_txt2nid(v->name);
        if (X509_REQ_get_attr_by_NID(csr, nid, -1) >= 0) {
          continue;
        }
        if (!X509_REQ_add1_attr_by_txt(csr, v->name, MBSTRING_ASC,
                                       (unsigned char*)v->value, -1)) {
          /**
           * hzhao: mismatched version of conf file may have attributes that
           * are not recognizable, and I don't think it should be treated as
           * fatal errors.
           */
          Logger::Verbose("add1_attr_by_txt %s -> %s (failed)", v->name,
                          v->value);
          // return false;
        }
      }
    }
  }

  X509_REQ_set_pubkey(csr, req->priv_key);
  return true;
}

bool HHVM_FUNCTION(openssl_csr_export_to_file, const Variant& csr,
                                               const String& outfilename,
                                               bool notext /* = true */) {
  auto pcsr = CSRequest::Get(csr);
  if (!pcsr) return false;

  BIO *bio_out = BIO_new_file((char*)outfilename.data(), "w");
  if (bio_out == nullptr) {
    raise_warning("error opening file %s", outfilename.data());
    return false;
  }

  if (!notext) {
    X509_REQ_print(bio_out, pcsr->csr());
  }
  PEM_write_bio_X509_REQ(bio_out, pcsr->csr());
  BIO_free(bio_out);
  return true;
}

bool HHVM_FUNCTION(openssl_csr_export, const Variant& csr, Variant& out,
                                       bool notext /* = true */) {
  auto pcsr = CSRequest::Get(csr);
  if (!pcsr) return false;

  BIO *bio_out = BIO_new(BIO_s_mem());
  if (!notext) {
    X509_REQ_print(bio_out, pcsr->csr());
  }

  if (PEM_write_bio_X509_REQ(bio_out, pcsr->csr())) {
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    out = String((char*)bio_buf->data, bio_buf->length, CopyString);
    BIO_free(bio_out);
    return true;
  }

  BIO_free(bio_out);
  return false;
}

Variant HHVM_FUNCTION(openssl_csr_get_public_key, const Variant& csr) {
  auto pcsr = CSRequest::Get(csr);
  if (!pcsr) return false;

  auto input_csr = pcsr->csr();

#if OPENSSL_VERSION_NUMBER >= 0x10100000
  /* Due to changes in OpenSSL 1.1 related to locking when decoding CSR,
   * the pub key is not changed after assigning. It means if we pass
   * a private key, it will be returned including the private part.
   * If we duplicate it, then we get just the public part which is
   * the same behavior as for OpenSSL 1.0 */
  input_csr = X509_REQ_dup(input_csr);
  /* We need to free the CSR as it was duplicated */
  SCOPE_EXIT { X509_REQ_free(input_csr); };
#endif
  auto pubkey = X509_REQ_get_pubkey(input_csr);
  if (!pubkey) return false;
  return Variant(req::make<Key>(pubkey));
}

Variant HHVM_FUNCTION(openssl_csr_get_subject, const Variant& csr,
                      bool use_shortnames /* = true */) {
  auto pcsr = CSRequest::Get(csr);
  if (!pcsr) return false;

  X509_NAME *subject = X509_REQ_get_subject_name(pcsr->csr());
  Array ret = Array::CreateDArray();
  add_assoc_name_entry(ret, nullptr, subject, use_shortnames);
  return ret;
}

Variant HHVM_FUNCTION(openssl_csr_new,
                      const Variant& dn, Variant& privkey,
                      const Variant& configargs /* = uninit_variant */,
                      const Variant& extraattribs /* = uninit_variant */) {
  Variant ret = false;
  struct php_x509_request req;
  memset(&req, 0, sizeof(req));

  req::ptr<Key> okey;
  X509_REQ *csr = nullptr;
  std::vector<String> strings;
  if (php_openssl_parse_config(&req, configargs.toArray(), strings)) {
    /* Generate or use a private key */
    if (!privkey.isNull()) {
      okey = Key::Get(privkey, false);
      if (okey) {
        req.priv_key = okey->m_key;
      }
    }
    if (req.priv_key == nullptr) {
      req.generatePrivateKey();
      if (req.priv_key) {
        okey = req::make<Key>(req.priv_key);
      }
    }
    if (req.priv_key == nullptr) {
      raise_warning("Unable to generate a private key");
    } else {
      csr = X509_REQ_new();
      if (csr && php_openssl_make_REQ(&req, csr, dn.toArray(),
                                      extraattribs.toArray())) {
        X509V3_CTX ext_ctx;
        X509V3_set_ctx(&ext_ctx, nullptr, nullptr, csr, nullptr, 0);
        X509V3_set_conf_lhash(&ext_ctx, req.req_config);

        /* Add extensions */
        if (req.request_extensions_section &&
            !X509V3_EXT_REQ_add_conf(req.req_config, &ext_ctx,
                                     (char*)req.request_extensions_section,
                                     csr)) {
          raise_warning("Error loading extension section %s",
                          req.request_extensions_section);
        } else {
          ret = true;
          if (X509_REQ_sign(csr, req.priv_key, req.digest)) {
            ret = req::make<CSRequest>(csr);
            csr = nullptr;
          } else {
            raise_warning("Error signing request");
          }

          privkey = Variant(okey);
        }
      }
    }
  }
  if (csr) {
    X509_REQ_free(csr);
  }
  php_openssl_dispose_config(&req);
  return ret;
}

Variant HHVM_FUNCTION(openssl_csr_sign, const Variant& csr,
                                        const Variant& cacert,
                                        const Variant& priv_key, int days,
                                        const Variant& configargs /* = null */,
                                        int serial /* = 0 */) {
  auto pcsr = CSRequest::Get(csr);
  if (!pcsr) return false;

  req::ptr<Certificate> ocert;
  if (!cacert.isNull()) {
    ocert = Certificate::Get(cacert);
    if (!ocert) {
      raise_warning("cannot get cert from parameter 2");
      return false;
    }
  }
  auto okey = Key::Get(priv_key, false);
  if (!okey) {
    raise_warning("cannot get private key from parameter 3");
    return false;
  }
  X509 *cert = nullptr;
  if (ocert) {
    cert = ocert->m_cert;
  }
  EVP_PKEY *pkey = okey->m_key;
  if (cert && !X509_check_private_key(cert, pkey)) {
    raise_warning("private key does not correspond to signing cert");
    return false;
  }

  req::ptr<Certificate> onewcert;
  struct php_x509_request req;
  memset(&req, 0, sizeof(req));
  Variant ret = false;
  std::vector<String> strings;
  if (!php_openssl_parse_config(&req, configargs.toArray(), strings)) {
    goto cleanup;
  }

  /* Check that the request matches the signature */
  EVP_PKEY *key;
  key = X509_REQ_get_pubkey(pcsr->csr());
  if (key == nullptr) {
    raise_warning("error unpacking public key");
    goto cleanup;
  }
  int i;
  i = X509_REQ_verify(pcsr->csr(), key);
  if (i < 0) {
    raise_warning("Signature verification problems");
    goto cleanup;
  }
  if (i == 0) {
    raise_warning("Signature did not match the certificate request");
    goto cleanup;
  }

  /* Now we can get on with it */
  X509 *new_cert;
  new_cert = X509_new();
  if (new_cert == nullptr) {
    raise_warning("No memory");
    goto cleanup;
  }
  onewcert = req::make<Certificate>(new_cert);
  /* Version 3 cert */
  if (!X509_set_version(new_cert, 2)) {
    goto cleanup;
  }
  ASN1_INTEGER_set(X509_get_serialNumber(new_cert), serial);
  X509_set_subject_name(new_cert, X509_REQ_get_subject_name(pcsr->csr()));

  if (cert == nullptr) {
    cert = new_cert;
  }
  if (!X509_set_issuer_name(new_cert, X509_get_subject_name(cert))) {
    goto cleanup;
  }
  X509_gmtime_adj(X509_get_notBefore(new_cert), 0);
  X509_gmtime_adj(X509_get_notAfter(new_cert), (long)60 * 60 * 24 * days);
  i = X509_set_pubkey(new_cert, key);
  if (!i) {
    goto cleanup;
  }
  if (req.extensions_section) {
    X509V3_CTX ctx;
    X509V3_set_ctx(&ctx, cert, new_cert, pcsr->csr(), nullptr, 0);
    X509V3_set_conf_lhash(&ctx, req.req_config);
    if (!X509V3_EXT_add_conf(req.req_config, &ctx, (char*)req.extensions_section,
                             new_cert)) {
      goto cleanup;
    }
  }

  /* Now sign it */
  if (!X509_sign(new_cert, pkey, req.digest)) {
    raise_warning("failed to sign it");
    goto cleanup;
  }

  /* Succeeded; lets return the cert */
  ret = onewcert;

 cleanup:
  php_openssl_dispose_config(&req);
  return ret;
}

Variant HHVM_FUNCTION(openssl_error_string) {
  char buf[512];
  unsigned long val = ERR_get_error();
  if (val) {
    return String(ERR_error_string(val, buf), CopyString);
  }
  return false;
}


bool HHVM_FUNCTION(openssl_open, const String& sealed_data, Variant& open_data,
                                 const String& env_key,
                                 const Variant& priv_key_id,
                                 const String& method, /* = null_string */
                                 const String& iv /* = null_string */) {
  const EVP_CIPHER *cipher_type;
  if (method.empty()) {
    cipher_type = EVP_rc4();
  } else {
    cipher_type = EVP_get_cipherbyname(method.c_str());
    if (!cipher_type) {
      raise_warning("Unknown cipher algorithm");
      return false;
    }
  }

  auto okey = Key::Get(priv_key_id, false);
  if (!okey) {
    raise_warning("unable to coerce parameter 4 into a private key");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;

  const unsigned char *iv_buf = nullptr;
  int iv_len = EVP_CIPHER_iv_length(cipher_type);
  if (iv_len > 0) {
    if (iv.empty()) {
      raise_warning(
        "Cipher algorithm requires an IV to be supplied as a sixth parameter");
      return false;
    }
    if (iv.length() != iv_len) {
      raise_warning("IV length is invalid");
      return false;
    }
    iv_buf = reinterpret_cast<const unsigned char*>(iv.c_str());
  }

  String s = String(sealed_data.size(), ReserveString);
  unsigned char *buf = (unsigned char *)s.mutableData();

  EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
  if (ctx == nullptr) {
    raise_warning("Failed to allocate an EVP_CIPHER_CTX object");
    return false;
  }
  SCOPE_EXIT {
    EVP_CIPHER_CTX_free(ctx);
  };
  int len1, len2;
  if (!EVP_OpenInit(
          ctx,
          cipher_type,
          (unsigned char*)env_key.data(),
          env_key.size(),
          iv_buf,
          pkey) ||
      !EVP_OpenUpdate(
          ctx,
          buf,
          &len1,
          (unsigned char*)sealed_data.data(),
          sealed_data.size()) ||
      !EVP_OpenFinal(ctx, buf + len1, &len2) || len1 + len2 == 0) {
    return false;
  }
  open_data = s.setSize(len1 + len2);
  return true;
}

static STACK_OF(X509) *php_array_to_X509_sk(const Variant& certs) {
  STACK_OF(X509) *pcerts = sk_X509_new_null();
  Array arrCerts;
  if (certs.isArray()) {
    arrCerts = certs.toArray();
  } else {
    arrCerts.append(certs);
  }
  for (ArrayIter iter(arrCerts); iter; ++iter) {
    auto ocert = Certificate::Get(iter.second());
    if (!ocert) {
      break;
    }
    sk_X509_push(pcerts, ocert->m_cert);
  }
  return pcerts;
}

const StaticString
  s_friendly_name("friendly_name"),
  s_extracerts("extracerts");

static bool
openssl_pkcs12_export_impl(const Variant& x509, BIO *bio_out,
                           const Variant& priv_key, const String& pass,
                           const Variant& args /* = uninit_variant */) {
  auto ocert = Certificate::Get(x509);
  if (!ocert) {
    raise_warning("cannot get cert from parameter 1");
    return false;
  }
  auto okey = Key::Get(priv_key, false);
  if (!okey) {
    raise_warning("cannot get private key from parameter 3");
    return false;
  }
  X509 *cert = ocert->m_cert;
  EVP_PKEY *key = okey->m_key;
  if (cert && !X509_check_private_key(cert, key)) {
    raise_warning("private key does not correspond to cert");
    return false;
  }

  Array arrArgs = args.toArray();

  String friendly_name;
  if (arrArgs.exists(s_friendly_name)) {
    friendly_name = arrArgs[s_friendly_name].toString();
  }

  STACK_OF(X509) *ca = nullptr;
  if (arrArgs.exists(s_extracerts)) {
    ca = php_array_to_X509_sk(arrArgs[s_extracerts]);
  }

  PKCS12 *p12 = PKCS12_create
    ((char*)pass.data(),
     (char*)(friendly_name.empty() ? nullptr : friendly_name.data()),
     key, cert, ca, 0, 0, 0, 0, 0);

  assertx(bio_out);
  bool ret = i2d_PKCS12_bio(bio_out, p12);
  PKCS12_free(p12);
  sk_X509_free(ca);
  return ret;
}

bool HHVM_FUNCTION(openssl_pkcs12_export_to_file, const Variant& x509,
                                                  const String& filename,
                                                  const Variant& priv_key,
                                                  const String& pass,
                                  const Variant& args /* = uninit_variant */) {
  BIO *bio_out = BIO_new_file(filename.data(), "w");
  if (bio_out == nullptr) {
    raise_warning("error opening file %s", filename.data());
    return false;
  }
  bool ret = openssl_pkcs12_export_impl(x509, bio_out, priv_key, pass, args);
  BIO_free(bio_out);
  return ret;
}

bool HHVM_FUNCTION(openssl_pkcs12_export, const Variant& x509, Variant& out,
                                          const Variant& priv_key,
                                          const String& pass,
                                  const Variant& args /* = uninit_variant */) {
  BIO *bio_out = BIO_new(BIO_s_mem());
  bool ret = openssl_pkcs12_export_impl(x509, bio_out, priv_key, pass, args);
  if (ret) {
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    out = String((char*)bio_buf->data, bio_buf->length, CopyString);
  }
  BIO_free(bio_out);
  return ret;
}

const StaticString
  s_cert("cert"),
  s_pkey("pkey");

bool HHVM_FUNCTION(openssl_pkcs12_read, const String& pkcs12, Variant& certs,
                                        const String& pass) {
  bool ret = false;
  PKCS12 *p12 = nullptr;

  BIO *bio_in = BIO_new(BIO_s_mem());
  if (!BIO_write(bio_in, pkcs12.data(), pkcs12.size())) {
    goto cleanup;
  }

  if (d2i_PKCS12_bio(bio_in, &p12)) {
    EVP_PKEY *pkey = nullptr;
    X509 *cert = nullptr;
    STACK_OF(X509) *ca = nullptr;
    if (PKCS12_parse(p12, pass.data(), &pkey, &cert, &ca)) {
      Variant vcerts = Array::CreateDArray();
      SCOPE_EXIT {
        certs = vcerts;
      };
      BIO *bio_out = nullptr;
      if (cert) {
        bio_out = BIO_new(BIO_s_mem());
        if (PEM_write_bio_X509(bio_out, cert)) {
          BUF_MEM *bio_buf;
          BIO_get_mem_ptr(bio_out, &bio_buf);
          vcerts.asArrRef().set(s_cert,
            String((char*)bio_buf->data, bio_buf->length, CopyString));
        }
        BIO_free(bio_out);
      }

      if (pkey) {
        bio_out = BIO_new(BIO_s_mem());
        if (PEM_write_bio_PrivateKey(bio_out, pkey, nullptr, nullptr, 0, 0, nullptr)) {
          BUF_MEM *bio_buf;
          BIO_get_mem_ptr(bio_out, &bio_buf);
          vcerts.asArrRef().set(s_pkey,
            String((char*)bio_buf->data, bio_buf->length, CopyString));
        }
        BIO_free(bio_out);
      }

      if (ca) {
        Array extracerts;
        for (X509 *aCA = sk_X509_pop(ca); aCA; aCA = sk_X509_pop(ca)) {
          bio_out = BIO_new(BIO_s_mem());
          if (PEM_write_bio_X509(bio_out, aCA)) {
            BUF_MEM *bio_buf;
            BIO_get_mem_ptr(bio_out, &bio_buf);
            extracerts.append(String((char*)bio_buf->data, bio_buf->length,
                                     CopyString));
          }
          BIO_free(bio_out);
          X509_free(aCA);
        }
        sk_X509_free(ca);
        vcerts.asArrRef().set(s_extracerts, extracerts);
      }
      ret = true;
      PKCS12_free(p12);
    }
  }

 cleanup:
  if (bio_in) {
    BIO_free(bio_in);
  }

  return ret;
}

bool HHVM_FUNCTION(openssl_pkcs7_decrypt, const String& infilename,
                                          const String& outfilename,
                                          const Variant& recipcert,
                              const Variant& recipkey /* = uninit_variant */) {
  bool ret = false;
  BIO *in = nullptr, *out = nullptr, *datain = nullptr;
  PKCS7 *p7 = nullptr;
  req::ptr<Key> okey;

  auto ocert = Certificate::Get(recipcert);
  if (!ocert) {
    raise_warning("unable to coerce parameter 3 to x509 cert");
    goto clean_exit;
  }

  okey = Key::Get(recipkey.isNull() ? recipcert : recipkey, false);
  if (!okey) {
    raise_warning("unable to get private key");
    goto clean_exit;
  }

  in = BIO_new_file(infilename.data(), "r");
  if (in == nullptr) {
    raise_warning("error opening the file, %s", infilename.data());
    goto clean_exit;
  }
  out = BIO_new_file(outfilename.data(), "w");
  if (out == nullptr) {
    raise_warning("error opening the file, %s", outfilename.data());
    goto clean_exit;
  }

  p7 = SMIME_read_PKCS7(in, &datain);
  if (p7 == nullptr) {
    goto clean_exit;
  }
  assertx(okey->m_key);
  assertx(ocert->m_cert);
  if (PKCS7_decrypt(p7, okey->m_key, ocert->m_cert, out, PKCS7_DETACHED)) {
    ret = true;
  }

 clean_exit:
  PKCS7_free(p7);
  BIO_free(datain);
  BIO_free(in);
  BIO_free(out);

  return ret;
}

static void print_headers(BIO *outfile, const Array& headers) {
  if (!headers.isNull()) {
    if (headers->isVectorData()) {
      for (ArrayIter iter(headers); iter; ++iter) {
        BIO_printf(outfile, "%s\n", iter.second().toString().data());
      }
    } else {
      for (ArrayIter iter(headers); iter; ++iter) {
        BIO_printf(outfile, "%s: %s\n", iter.first().toString().data(),
                   iter.second().toString().data());
      }
    }
  }
}

bool HHVM_FUNCTION(openssl_pkcs7_encrypt, const String& infilename,
                                          const String& outfilename,
                                          const Variant& recipcerts,
                                          const Array& headers,
                                          int flags /* = 0 */,
                                int cipherid /* = k_OPENSSL_CIPHER_RC2_40 */) {
  bool ret = false;
  BIO *infile = nullptr, *outfile = nullptr;
  STACK_OF(X509) *precipcerts = nullptr;
  PKCS7 *p7 = nullptr;
  const EVP_CIPHER *cipher = nullptr;

  infile = BIO_new_file(infilename.data(), (flags & PKCS7_BINARY) ? "rb" : "r");
  if (infile == nullptr) {
    raise_warning("error opening the file, %s", infilename.data());
    goto clean_exit;
  }
  outfile = BIO_new_file(outfilename.data(), "w");
  if (outfile == nullptr) {
    raise_warning("error opening the file, %s", outfilename.data());
    goto clean_exit;
  }

  precipcerts = php_array_to_X509_sk(recipcerts);

  /* sanity check the cipher */
  switch (cipherid) {
#ifndef OPENSSL_NO_RC2
  case PHP_OPENSSL_CIPHER_RC2_40:  cipher = EVP_rc2_40_cbc();   break;
  case PHP_OPENSSL_CIPHER_RC2_64:  cipher = EVP_rc2_64_cbc();   break;
  case PHP_OPENSSL_CIPHER_RC2_128: cipher = EVP_rc2_cbc();      break;
#endif
#ifndef OPENSSL_NO_DES
  case PHP_OPENSSL_CIPHER_DES:     cipher = EVP_des_cbc();      break;
  case PHP_OPENSSL_CIPHER_3DES:    cipher = EVP_des_ede3_cbc(); break;
#endif
  default:
    raise_warning("Invalid cipher type `%d'", cipherid);
    goto clean_exit;
  }
  if (cipher == nullptr) {
    raise_warning("Failed to get cipher");
    goto clean_exit;
  }

  p7 = PKCS7_encrypt(precipcerts, infile, (EVP_CIPHER*)cipher, flags);
  if (p7 == nullptr) goto clean_exit;

  print_headers(outfile, headers);
  (void)BIO_reset(infile);
  SMIME_write_PKCS7(outfile, p7, infile, flags);
  ret = true;

 clean_exit:
  PKCS7_free(p7);
  BIO_free(infile);
  BIO_free(outfile);
  sk_X509_free(precipcerts);
  return ret;
}

bool HHVM_FUNCTION(openssl_pkcs7_sign, const String& infilename,
                                       const String& outfilename,
                                       const Variant& signcert,
                                       const Variant& privkey,
                                       const Variant& headers,
                                       int flags /* = k_PKCS7_DETACHED */,
                                const String& extracerts /* = null_string */) {
  bool ret = false;
  STACK_OF(X509) *others = nullptr;
  BIO *infile = nullptr, *outfile = nullptr;
  PKCS7 *p7 = nullptr;
  req::ptr<Key> okey;
  req::ptr<Certificate> ocert;

  if (!extracerts.empty()) {
    others = load_all_certs_from_file(extracerts.data());
    if (others == nullptr) {
      goto clean_exit;
    }
  }

  okey = Key::Get(privkey, false);
  if (!okey) {
    raise_warning("error getting private key");
    goto clean_exit;
  }
  EVP_PKEY *key;
  key = okey->m_key;

  ocert = Certificate::Get(signcert);
  if (!ocert) {
    raise_warning("error getting cert");
    goto clean_exit;
  }
  X509 *cert;
  cert = ocert->m_cert;

  infile = BIO_new_file(infilename.data(), (flags & PKCS7_BINARY) ? "rb" : "r");
  if (infile == nullptr) {
    raise_warning("error opening input file %s!", infilename.data());
    goto clean_exit;
  }

  outfile = BIO_new_file(outfilename.data(), "w");
  if (outfile == nullptr) {
    raise_warning("error opening output file %s!", outfilename.data());
    goto clean_exit;
  }

  p7 = PKCS7_sign(cert, key, others, infile, flags);
  if (p7 == nullptr) {
    raise_warning("error creating PKCS7 structure!");
    goto clean_exit;
  }

  print_headers(outfile, headers.toArray());
  (void)BIO_reset(infile);
  SMIME_write_PKCS7(outfile, p7, infile, flags);
  ret = true;

 clean_exit:
  PKCS7_free(p7);
  BIO_free(infile);
  BIO_free(outfile);
  if (others) {
    sk_X509_pop_free(others, X509_free);
  }

  return ret;
}

static int pkcs7_ignore_expiration(int ok, X509_STORE_CTX *ctx) {
  if (ok) {
    return ok;
  }
  int error = X509_STORE_CTX_get_error(ctx);
  if (error == X509_V_ERR_CERT_HAS_EXPIRED) {
    // ignore cert expirations
    Logger::Verbose("Ignoring cert expiration");
    return 1;
  }
  return ok;
}

/**
 *  NOTE: when ignore_cert_expiration is true, a custom certificate validation
 *  callback is set up. Please be aware of this if you modify the function to
 *  allow other certificate validation behaviors
 */
Variant openssl_pkcs7_verify_core(
  const String& filename,
  int flags,
  const Variant& voutfilename /* = null_string */,
  const Variant& vcainfo /* = null_array */,
  const Variant& vextracerts /* = null_string */,
  const Variant& vcontent /* = null_string */,
  bool ignore_cert_expiration
) {
  Variant ret = -1;
  X509_STORE *store = nullptr;
  BIO *in = nullptr;
  PKCS7 *p7 = nullptr;
  BIO *datain = nullptr;
  BIO *dataout = nullptr;

  auto cainfo = vcainfo.toArray();
  auto extracerts = vextracerts.toString();
  auto content = vcontent.toString();

  STACK_OF(X509) *others = nullptr;
  if (!extracerts.empty()) {
    others = load_all_certs_from_file(extracerts.data());
    if (others == nullptr) {
      goto clean_exit;
    }
  }

  flags = flags & ~PKCS7_DETACHED;

  store = setup_verify(cainfo);
  if (!store) {
    goto clean_exit;
  }
  if (ignore_cert_expiration) {
#if (OPENSSL_VERSION_NUMBER >= 0x10000000)
    // make sure no other callback is specified
  #if OPENSSL_VERSION_NUMBER >= 0x10100000L
    assertx(!X509_STORE_get_verify_cb(store));
  #else
    assertx(!store->verify_cb);
  #endif
    // ignore expired certs
    X509_STORE_set_verify_cb(store, pkcs7_ignore_expiration);
#else
    always_assert(false);
#endif
  }
  in = BIO_new_file(filename.data(), (flags & PKCS7_BINARY) ? "rb" : "r");
  if (in == nullptr) {
    raise_warning("error opening the file, %s", filename.data());
    goto clean_exit;
  }

  p7 = SMIME_read_PKCS7(in, &datain);
  if (p7 == nullptr) {
    goto clean_exit;
  }

  if (!content.empty()) {
    dataout = BIO_new_file(content.data(), "w");
    if (dataout == nullptr) {
      raise_warning("error opening the file, %s", content.data());
      goto clean_exit;
    }
  }

  if (PKCS7_verify(p7, others, store, datain, dataout, flags)) {
    ret = true;
    auto outfilename = voutfilename.toString();
    if (!outfilename.empty()) {
      BIO *certout = BIO_new_file(outfilename.data(), "w");
      if (certout) {
        STACK_OF(X509) *signers = PKCS7_get0_signers(p7, nullptr, flags);
        for (int i = 0; i < sk_X509_num(signers); i++) {
          PEM_write_bio_X509(certout, sk_X509_value(signers, i));
        }
        BIO_free(certout);
        sk_X509_free(signers);
      } else {
        raise_warning("signature OK, but cannot open %s for writing",
                        outfilename.data());
        ret = -1;
      }
    }
    goto clean_exit;
  } else {
    ret = false;
  }

 clean_exit:
  X509_STORE_free(store);
  BIO_free(datain);
  BIO_free(in);
  BIO_free(dataout);
  PKCS7_free(p7);
  sk_X509_pop_free(others, X509_free);

  return ret;
}

Variant HHVM_FUNCTION(openssl_pkcs7_verify, const String& filename, int flags,
                               const Variant& voutfilename /* = null_string */,
                               const Variant& vcainfo /* = null_array */,
                               const Variant& vextracerts /* = null_string */,
                               const Variant& vcontent /* = null_string */) {
  return openssl_pkcs7_verify_core(filename, flags, voutfilename, vcainfo,
                                   vextracerts, vcontent, false);
}

static bool
openssl_pkey_export_impl(const Variant& key, BIO *bio_out,
                         const String& passphrase /* = null_string */,
                         const Variant& configargs /* = uninit_variant */) {
  auto okey = Key::Get(key, false, passphrase.data());
  if (!okey) {
    raise_warning("cannot get key from parameter 1");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;

  struct php_x509_request req;
  memset(&req, 0, sizeof(req));
  std::vector<String> strings;
  bool ret = false;
  if (php_openssl_parse_config(&req, configargs.toArray(), strings)) {
    const EVP_CIPHER *cipher;
    if (!passphrase.empty() && req.priv_key_encrypt) {
      cipher = (EVP_CIPHER *)EVP_des_ede3_cbc();
    } else {
      cipher = nullptr;
    }
    assertx(bio_out);

    switch (EVP_PKEY_id(pkey)) {
#ifdef HAVE_EVP_PKEY_EC
      case EVP_PKEY_EC:
        ret = PEM_write_bio_ECPrivateKey(bio_out, EVP_PKEY_get0_EC_KEY(pkey),
                                         cipher,
                                         (unsigned char *)passphrase.data(),
                                         passphrase.size(),
                                         nullptr,
                                         nullptr);
        break;
#endif
      default:
        ret = PEM_write_bio_PrivateKey(bio_out, pkey, cipher,
                                       (unsigned char *)passphrase.data(),
                                       passphrase.size(), nullptr, nullptr);
        break;
    }
  }
  php_openssl_dispose_config(&req);
  return ret;
}

bool HHVM_FUNCTION(openssl_pkey_export_to_file, const Variant& key,
                                                const String& outfilename,
                                   const String& passphrase /* = null_string */,
                             const Variant& configargs /* = uninit_variant */) {
  BIO *bio_out = BIO_new_file(outfilename.data(), "w");
  if (bio_out == nullptr) {
    raise_warning("error opening the file, %s", outfilename.data());
    return false;
  }
  bool ret = openssl_pkey_export_impl(key, bio_out, passphrase, configargs);
  BIO_free(bio_out);
  return ret;
}

bool HHVM_FUNCTION(openssl_pkey_export, const Variant& key, Variant& out,
                                   const String& passphrase /* = null_string */,
                            const Variant& configargs /* = uninit_variant */) {
  BIO *bio_out = BIO_new(BIO_s_mem());
  bool ret = openssl_pkey_export_impl(key, bio_out, passphrase, configargs);
  if (ret) {
    char *bio_mem_ptr;
    long bio_mem_len = BIO_get_mem_data(bio_out, &bio_mem_ptr);
    out = String(bio_mem_ptr, bio_mem_len, CopyString);
  }
  BIO_free(bio_out);
  return ret;
}

const StaticString
  s_bits("bits"),
  s_key("key"),
  s_type("type"),
  s_name("name"),
  s_hash("hash"),
  s_version("version"),
  s_serialNumber("serialNumber"),
  s_signatureAlgorithm("signatureAlgorithm"),
  s_validFrom("validFrom"),
  s_validTo("validTo"),
  s_validFrom_time_t("validFrom_time_t"),
  s_validTo_time_t("validTo_time_t"),
  s_alias("alias"),
  s_purposes("purposes"),
  s_extensions("extensions"),
  s_rsa("rsa"),
  s_dsa("dsa"),
  s_dh("dh"),
  s_ec("ec"),
  s_n("n"),
  s_e("e"),
  s_d("d"),
  s_p("p"),
  s_q("q"),
  s_g("g"),
  s_x("x"),
  s_y("y"),
  s_dmp1("dmp1"),
  s_dmq1("dmq1"),
  s_iqmp("iqmp"),
  s_priv_key("priv_key"),
  s_pub_key("pub_key"),
  s_curve_oid("curve_oid");

static void add_bignum_as_string(Array &arr,
                                 StaticString key,
                                 const BIGNUM *bn) {
  if (!bn) {
    return;
  }
  int num_bytes = BN_num_bytes(bn);
  String str{size_t(num_bytes), ReserveString};
  BN_bn2bin(bn, (unsigned char*)str.mutableData());
  str.setSize(num_bytes);
  arr.set(key, std::move(str));
}

Array HHVM_FUNCTION(openssl_pkey_get_details, const Resource& key) {
  EVP_PKEY *pkey = cast<Key>(key)->m_key;
  BIO *out = BIO_new(BIO_s_mem());
  PEM_write_bio_PUBKEY(out, pkey);
  char *pbio;
  unsigned int pbio_len = BIO_get_mem_data(out, &pbio);

  auto ret = make_darray(
    s_bits, EVP_PKEY_bits(pkey),
    s_key, String(pbio, pbio_len, CopyString)
  );
  long ktype = -1;

  auto details = Array::CreateDArray();
  switch (EVP_PKEY_id(pkey)) {
  case EVP_PKEY_RSA:
  case EVP_PKEY_RSA2:
    {
      ktype = OPENSSL_KEYTYPE_RSA;
      RSA *rsa = EVP_PKEY_get0_RSA(pkey);
      assertx(rsa);
      const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
      RSA_get0_key(rsa, &n, &e, &d);
      RSA_get0_factors(rsa, &p, &q);
      RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
      add_bignum_as_string(details, s_n, n);
      add_bignum_as_string(details, s_e, e);
      add_bignum_as_string(details, s_d, d);
      add_bignum_as_string(details, s_p, p);
      add_bignum_as_string(details, s_q, q);
      add_bignum_as_string(details, s_dmp1, dmp1);
      add_bignum_as_string(details, s_dmq1, dmq1);
      add_bignum_as_string(details, s_iqmp, iqmp);
      ret.set(s_rsa, details);
      break;
  }
  case EVP_PKEY_DSA:
  case EVP_PKEY_DSA2:
  case EVP_PKEY_DSA3:
  case EVP_PKEY_DSA4:
    {
      ktype = OPENSSL_KEYTYPE_DSA;
      DSA *dsa = EVP_PKEY_get0_DSA(pkey);
      assertx(dsa);
      const BIGNUM *p, *q, *g, *pub_key, *priv_key;
      DSA_get0_pqg(dsa, &p, &q, &g);
      DSA_get0_key(dsa, &pub_key, &priv_key);
      add_bignum_as_string(details, s_p, p);
      add_bignum_as_string(details, s_q, q);
      add_bignum_as_string(details, s_g, g);
      add_bignum_as_string(details, s_priv_key, priv_key);
      add_bignum_as_string(details, s_pub_key, pub_key);
      ret.set(s_dsa, details);
      break;
    }
  case EVP_PKEY_DH:
    {
      ktype = OPENSSL_KEYTYPE_DH;
      DH *dh = EVP_PKEY_get0_DH(pkey);
      assertx(dh);
      const BIGNUM *p, *q, *g, *pub_key, *priv_key;
      DH_get0_pqg(dh, &p, &q, &g);
      DH_get0_key(dh, &pub_key, &priv_key);
      add_bignum_as_string(details, s_p, p);
      add_bignum_as_string(details, s_g, g);
      add_bignum_as_string(details, s_priv_key, priv_key);
      add_bignum_as_string(details, s_pub_key, pub_key);
      ret.set(s_dh, details);
      break;
    }
#ifdef HAVE_EVP_PKEY_EC
  case EVP_PKEY_EC:
    {
      ktype = OPENSSL_KEYTYPE_EC;
      auto const ec = EVP_PKEY_get0_EC_KEY(pkey);
      assertx(ec);

      auto const ec_group = EC_KEY_get0_group(ec);
      auto const nid = EC_GROUP_get_curve_name(ec_group);
      if (nid == NID_undef) {
        break;
      }

      auto const crv_sn = OBJ_nid2sn(nid);
      if (crv_sn != nullptr) {
        details.set(s_curve_name, String(crv_sn, CopyString));
      }

      auto const obj = OBJ_nid2obj(nid);
      if (obj != nullptr) {
        SCOPE_EXIT {
          ASN1_OBJECT_free(obj);
        };
        char oir_buf[256];
        OBJ_obj2txt(oir_buf, sizeof(oir_buf) - 1, obj, 1);
        details.set(s_curve_oid, String(oir_buf, CopyString));
      }

      auto x = BN_new();
      auto y = BN_new();
      SCOPE_EXIT {
        BN_free(x);
        BN_free(y);
      };
      auto const pub = EC_KEY_get0_public_key(ec);
      if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, nullptr)) {
        add_bignum_as_string(details, s_x, x);
        add_bignum_as_string(details, s_y, y);
      }

      auto d = BN_dup(EC_KEY_get0_private_key(ec));
      SCOPE_EXIT {
        BN_free(d);
      };
      if (d != nullptr) {
        add_bignum_as_string(details, s_d, d);
      }

      ret.set(s_ec, details);
    }
    break;
#endif
  }
  ret.set(s_type, ktype);
  BIO_free(out);
  return ret;
}

Variant HHVM_FUNCTION(openssl_pkey_get_private, const Variant& key,
                                 const String& passphrase /* = null_string */) {
  return toVariant(Key::Get(key, false, passphrase.data()));
}

Variant HHVM_FUNCTION(openssl_pkey_get_public, const Variant& certificate) {
  return toVariant(Key::Get(certificate, true));
}

Variant HHVM_FUNCTION(openssl_pkey_new,
                       const Variant& configargs /* = uninit_variant */) {
  struct php_x509_request req;
  memset(&req, 0, sizeof(req));
  SCOPE_EXIT {
    php_openssl_dispose_config(&req);
  };

  std::vector<String> strings;
  if (php_openssl_parse_config(&req, configargs.toArray(), strings) &&
      req.generatePrivateKey()) {
    return Resource(req::make<Key>(req.priv_key));
  } else {
    return false;
  }
}

bool HHVM_FUNCTION(openssl_private_decrypt, const String& data,
                                            Variant& decrypted,
                                            const Variant& key,
                                  int padding /* = k_OPENSSL_PKCS1_PADDING */) {
  auto okey = Key::Get(key, false);
  if (!okey) {
    raise_warning("key parameter is not a valid private key");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;
  int cryptedlen = EVP_PKEY_size(pkey);
  String s = String(cryptedlen, ReserveString);
  unsigned char *cryptedbuf = (unsigned char *)s.mutableData();

  int successful = 0;
  switch (EVP_PKEY_id(pkey)) {
  case EVP_PKEY_RSA:
  case EVP_PKEY_RSA2:
    cryptedlen = RSA_private_decrypt(data.size(),
                                     (unsigned char *)data.data(),
                                     cryptedbuf,
                                     EVP_PKEY_get0_RSA(pkey),
                                     padding);
    if (cryptedlen != -1) {
      successful = 1;
    }
    break;

  default:
    raise_warning("key type not supported");
  }

  if (successful) {
    decrypted = s.setSize(cryptedlen);
    return true;
  }

  return false;
}

bool HHVM_FUNCTION(openssl_private_encrypt, const String& data,
                                            Variant& crypted,
                                            const Variant& key,
                                  int padding /* = k_OPENSSL_PKCS1_PADDING */) {
  auto okey = Key::Get(key, false);
  if (!okey) {
    raise_warning("key param is not a valid private key");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;
  int cryptedlen = EVP_PKEY_size(pkey);
  String s = String(cryptedlen, ReserveString);
  unsigned char *cryptedbuf = (unsigned char *)s.mutableData();

  int successful = 0;
  switch (EVP_PKEY_id(pkey)) {
  case EVP_PKEY_RSA:
  case EVP_PKEY_RSA2:
    successful = (RSA_private_encrypt(data.size(),
                                      (unsigned char *)data.data(),
                                      cryptedbuf,
                                      EVP_PKEY_get0_RSA(pkey),
                                      padding) == cryptedlen);
    break;
  default:
    raise_warning("key type not supported");
  }

  if (successful) {
    crypted = s.setSize(cryptedlen);
    return true;
  }

  return false;
}

bool HHVM_FUNCTION(openssl_public_decrypt, const String& data,
                                           Variant& decrypted,
                                           const Variant& key,
                                  int padding /* = k_OPENSSL_PKCS1_PADDING */) {
  auto okey = Key::Get(key, true);
  if (!okey) {
    raise_warning("key parameter is not a valid public key");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;
  int cryptedlen = EVP_PKEY_size(pkey);
  String s = String(cryptedlen, ReserveString);
  unsigned char *cryptedbuf = (unsigned char *)s.mutableData();

  int successful = 0;
  switch (EVP_PKEY_id(pkey)) {
  case EVP_PKEY_RSA:
  case EVP_PKEY_RSA2:
    cryptedlen = RSA_public_decrypt(data.size(),
                                    (unsigned char *)data.data(),
                                    cryptedbuf,
                                    EVP_PKEY_get0_RSA(pkey),
                                    padding);
    if (cryptedlen != -1) {
      successful = 1;
    }
    break;

  default:
    raise_warning("key type not supported");
  }

  if (successful) {
    decrypted = s.setSize(cryptedlen);
    return true;
  }

  return false;
}

bool HHVM_FUNCTION(openssl_public_encrypt, const String& data,
                                           Variant& crypted,
                                           const Variant& key,
                                  int padding /* = k_OPENSSL_PKCS1_PADDING */) {
  auto okey = Key::Get(key, true);
  if (!okey) {
    raise_warning("key parameter is not a valid public key");
    return false;
  }
  EVP_PKEY *pkey = okey->m_key;
  int cryptedlen = EVP_PKEY_size(pkey);
  String s = String(cryptedlen, ReserveString);
  unsigned char *cryptedbuf = (unsigned char *)s.mutableData();

  int successful = 0;
  switch (EVP_PKEY_id(pkey)) {
  case EVP_PKEY_RSA:
  case EVP_PKEY_RSA2:
    successful = (RSA_public_encrypt(data.size(),
                                     (unsigned char *)data.data(),
                                     cryptedbuf,
                                     EVP_PKEY_get0_RSA(pkey),
                                     padding) == cryptedlen);
    break;
  default:
    raise_warning("key type not supported");
  }

  if (successful) {
    crypted = s.setSize(cryptedlen);
    return true;
  }

  return false;
}

Variant HHVM_FUNCTION(openssl_seal, const String& data, Variant& sealed_data,
                                    Variant& env_keys,
                                    const Array& pub_key_ids,
                                    const String& method,
                                    Variant& iv) {
  int nkeys = pub_key_ids.size();
  if (nkeys == 0) {
    raise_warning("Fourth argument to openssl_seal() must be "
                    "a non-empty array");
    return false;
  }

  const EVP_CIPHER *cipher_type;
  if (method.empty()) {
    cipher_type = EVP_rc4();
  } else {
    cipher_type = EVP_get_cipherbyname(method.c_str());
    if (!cipher_type) {
      raise_warning("Unknown cipher algorithm");
      return false;
    }
  }

  int iv_len = EVP_CIPHER_iv_length(cipher_type);
  unsigned char *iv_buf = nullptr;
  String iv_s;
  if (iv_len > 0) {
    iv_s = String(iv_len, ReserveString);
    iv_buf = (unsigned char*)iv_s.mutableData();

    if (!RAND_bytes(iv_buf, iv_len)) {
      raise_warning("Could not generate an IV.");
      return false;
    }
  }

  EVP_PKEY **pkeys = (EVP_PKEY**)malloc(nkeys * sizeof(*pkeys));
  int *eksl = (int*)malloc(nkeys * sizeof(*eksl));
  unsigned char **eks = (unsigned char **)malloc(nkeys * sizeof(*eks));
  memset(eks, 0, sizeof(*eks) * nkeys);

  // holder is needed to make sure none of the Keys get deleted prematurely.
  // The pkeys array points to elements inside of Keys returned from Key::Get()
  // which may be newly allocated and have no other owners.
  std::vector<req::ptr<Key>> holder;

  /* get the public keys we are using to seal this data */
  bool ret = true;
  int i = 0;
  String s;
  unsigned char* buf = nullptr;
  EVP_CIPHER_CTX* ctx = nullptr;
  for (ArrayIter iter(pub_key_ids); iter; ++iter, ++i) {
    auto okey = Key::Get(iter.second(), true);
    if (!okey) {
      raise_warning("not a public key (%dth member of pubkeys)", i + 1);
      ret = false;
      goto clean_exit;
    }
    holder.push_back(okey);
    pkeys[i] = okey->m_key;
    eks[i] = (unsigned char *)malloc(EVP_PKEY_size(pkeys[i]) + 1);
  }

  ctx = EVP_CIPHER_CTX_new();
  if (ctx == nullptr) {
    raise_warning("Failed to allocate an EVP_CIPHER_CTX object");
    ret = false;
    goto clean_exit;
  }
  if (!EVP_EncryptInit_ex(ctx, cipher_type, nullptr, nullptr, nullptr)) {
    ret = false;
    goto clean_exit;
  }

  int len1, len2;

  s = String(data.size() + EVP_CIPHER_CTX_block_size(ctx), ReserveString);
  buf = (unsigned char *)s.mutableData();
  if (EVP_SealInit(ctx, cipher_type, eks, eksl, iv_buf, pkeys, nkeys) <= 0 ||
      !EVP_SealUpdate(ctx, buf, &len1, (unsigned char*)data.data(), data.size()) ||
      !EVP_SealFinal(ctx, buf + len1, &len2)) {
    ret = false;
    goto clean_exit;
  }

  if (len1 + len2 > 0) {
    sealed_data = s.setSize(len1 + len2);

    auto ekeys = Array::CreateVArray();
    for (i = 0; i < nkeys; i++) {
      eks[i][eksl[i]] = '\0';
      ekeys.append(String((char*)eks[i], eksl[i], AttachString));
      eks[i] = nullptr;
    }
    env_keys = ekeys;
  }

 clean_exit:
  for (i = 0; i < nkeys; i++) {
    if (eks[i]) free(eks[i]);
  }
  free(eks);
  free(eksl);
  free(pkeys);

  if (iv_buf != nullptr) {
    if (ret) {
      iv = iv_s.setSize(iv_len);
    }
  }
  if (ctx != nullptr) {
    EVP_CIPHER_CTX_free(ctx);
  }

  if (ret) return len1 + len2;
  return false;
}

static const EVP_MD *php_openssl_get_evp_md_from_algo(long algo) {
  switch (algo) {
  case OPENSSL_ALGO_SHA1: return EVP_sha1();
  case OPENSSL_ALGO_MD5:  return EVP_md5();
  case OPENSSL_ALGO_MD4:  return EVP_md4();
#ifdef HAVE_OPENSSL_MD2_H
  case OPENSSL_ALGO_MD2:  return EVP_md2();
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
  case OPENSSL_ALGO_DSS1: return EVP_dss1();
#endif
#if OPENSSL_VERSION_NUMBER >= 0x0090708fL
  case OPENSSL_ALGO_SHA224: return EVP_sha224();
  case OPENSSL_ALGO_SHA256: return EVP_sha256();
  case OPENSSL_ALGO_SHA384: return EVP_sha384();
  case OPENSSL_ALGO_SHA512: return EVP_sha512();
  case OPENSSL_ALGO_RMD160: return EVP_ripemd160();
#endif
  }
  return nullptr;
}

bool HHVM_FUNCTION(openssl_sign, const String& data, Variant& signature,
                                 const Variant& priv_key_id,
                     const Variant& signature_alg /* = k_OPENSSL_ALGO_SHA1 */) {
  auto okey = Key::Get(priv_key_id, false);
  if (!okey) {
    raise_warning("supplied key param cannot be coerced into a private key");
    return false;
  }

  const EVP_MD *mdtype = nullptr;
  if (signature_alg.isInteger()) {
    mdtype = php_openssl_get_evp_md_from_algo(signature_alg.toInt64Val());
  } else if (signature_alg.isString()) {
    mdtype = EVP_get_digestbyname(signature_alg.toString().data());
  }

  if (!mdtype) {
    raise_warning("Unknown signature algorithm.");
    return false;
  }

  EVP_PKEY *pkey = okey->m_key;
  int siglen = EVP_PKEY_size(pkey);
  String s = String(siglen, ReserveString);
  unsigned char *sigbuf = (unsigned char *)s.mutableData();

  EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
  SCOPE_EXIT {
    EVP_MD_CTX_free(md_ctx);
  };
  EVP_SignInit(md_ctx, mdtype);
  EVP_SignUpdate(md_ctx, (unsigned char *)data.data(), data.size());
  if (EVP_SignFinal(md_ctx, sigbuf, (unsigned int *)&siglen, pkey)) {
    signature = s.setSize(siglen);
    return true;
  }
  return false;
}

Variant HHVM_FUNCTION(openssl_verify, const String& data,
                                      const String& signature,
                                      const Variant& pub_key_id,
                     const Variant& signature_alg /* = k_OPENSSL_ALGO_SHA1 */) {
  int err;
  const EVP_MD *mdtype = nullptr;

  if (signature_alg.isInteger()) {
    mdtype = php_openssl_get_evp_md_from_algo(signature_alg.toInt64Val());
  } else if (signature_alg.isString()) {
    mdtype = EVP_get_digestbyname(signature_alg.toString().data());
  }

  if (!mdtype) {
    raise_warning("Unknown signature algorithm.");
    return false;
  }

  auto okey = Key::Get(pub_key_id, true);
  if (!okey) {
    raise_warning("supplied key param cannot be coerced into a public key");
    return false;
  }

  EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
  SCOPE_EXIT {
    EVP_MD_CTX_free(md_ctx);
  };
  EVP_VerifyInit(md_ctx, mdtype);
  EVP_VerifyUpdate(md_ctx, (unsigned char*)data.data(), data.size());
  err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature.data(),
                         signature.size(), okey->m_key);
  return err;
}

bool HHVM_FUNCTION(openssl_x509_check_private_key, const Variant& cert,
                                                   const Variant& key) {
  auto ocert = Certificate::Get(cert);
  if (!ocert) {
    return false;
  }
  auto okey = Key::Get(key, false);
  if (!okey) {
    return false;
  }
  return X509_check_private_key(ocert->m_cert, okey->m_key);
}

static int check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain,
                      int purpose) {
  X509_STORE_CTX *csc = X509_STORE_CTX_new();
  if (csc == nullptr) {
    raise_warning("memory allocation failure");
    return 0;
  }
  X509_STORE_CTX_init(csc, ctx, x, untrustedchain);

  if (purpose >= 0) {
    X509_STORE_CTX_set_purpose(csc, purpose);
  }

  int ret = X509_verify_cert(csc);
  X509_STORE_CTX_free(csc);
  return ret;
}

Variant HHVM_FUNCTION(openssl_x509_checkpurpose, const Variant& x509cert,
                      int purpose,
                      const Array& cainfo /* = null_array */,
                      const String& untrustedfile /* = null_string */) {
  int ret = -1;
  STACK_OF(X509) *untrustedchain = nullptr;
  X509_STORE *pcainfo = nullptr;
  req::ptr<Certificate> ocert;

  if (!untrustedfile.empty()) {
    untrustedchain = load_all_certs_from_file(untrustedfile.data());
    if (untrustedchain == nullptr) {
      goto clean_exit;
    }
  }

  pcainfo = setup_verify(cainfo);
  if (pcainfo == nullptr) {
    goto clean_exit;
  }

  ocert = Certificate::Get(x509cert);
  if (!ocert) {
    raise_warning("cannot get cert from parameter 1");
    return false;
  }
  X509 *cert;
  cert = ocert->m_cert;
  assertx(cert);

  ret = check_cert(pcainfo, cert, untrustedchain, purpose);

 clean_exit:
  if (pcainfo) {
    X509_STORE_free(pcainfo);
  }
  if (untrustedchain) {
    sk_X509_pop_free(untrustedchain, X509_free);
  }
  return ret == 1 ? true : ret == 0 ? false : -1;
}

static bool openssl_x509_export_impl(const Variant& x509, BIO *bio_out,
                                     bool notext /* = true */) {
  auto ocert = Certificate::Get(x509);
  if (!ocert) {
    raise_warning("cannot get cert from parameter 1");
    return false;
  }
  X509 *cert = ocert->m_cert;
  assertx(cert);

  assertx(bio_out);
  if (!notext) {
    X509_print(bio_out, cert);
  }
  return PEM_write_bio_X509(bio_out, cert);
}

bool HHVM_FUNCTION(openssl_x509_export_to_file, const Variant& x509,
                                                const String& outfilename,
                                                bool notext /* = true */) {
  BIO *bio_out = BIO_new_file((char*)outfilename.data(), "w");
  if (bio_out == nullptr) {
    raise_warning("error opening file %s", outfilename.data());
    return false;
  }
  bool ret = openssl_x509_export_impl(x509, bio_out, notext);
  BIO_free(bio_out);
  return ret;
}

bool HHVM_FUNCTION(openssl_x509_export, const Variant& x509, Variant& output,
                                        bool notext /* = true */) {
  BIO *bio_out = BIO_new(BIO_s_mem());
  bool ret = openssl_x509_export_impl(x509, bio_out, notext);
  if (ret) {
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    output = String(bio_buf->data, bio_buf->length, CopyString);
  }
  BIO_free(bio_out);
  return ret;
}

/**
 * This is how the time string is formatted:
 *
 * snprintf(p, sizeof(p), "%02d%02d%02d%02d%02d%02dZ",ts->tm_year%100,
 *          ts->tm_mon+1,ts->tm_mday,ts->tm_hour,ts->tm_min,ts->tm_sec);
 */
static time_t asn1_time_to_time_t(ASN1_UTCTIME *timestr) {

  auto const timestr_type = ASN1_STRING_type(timestr);

  if (timestr_type != V_ASN1_UTCTIME && timestr_type != V_ASN1_GENERALIZEDTIME) {
    raise_warning("illegal ASN1 data type for timestamp");
    return (time_t)-1;
  }

  auto const timestr_len = (size_t)ASN1_STRING_length(timestr);

  // Binary safety
  if (timestr_len != strlen((char*)ASN1_STRING_data(timestr))) {
    raise_warning("illegal length in timestamp");
    return (time_t)-1;
  }

  if (timestr_len < 13 && timestr_len != 11) {
    raise_warning("unable to parse time string %s correctly",
                    timestr->data);
    return (time_t)-1;
  }

  if (timestr_type == V_ASN1_GENERALIZEDTIME && timestr_len < 15) {
    raise_warning("unable to parse time string %s correctly", timestr->data);
    return (time_t)-1;
  }

  char *strbuf = strdup((char*)timestr->data);

  struct tm thetime;
  memset(&thetime, 0, sizeof(thetime));

  /* we work backwards so that we can use atoi more easily */
  char *thestr = strbuf + ASN1_STRING_length(timestr) - 3;
  if (ASN1_STRING_length(timestr) == 11) {
    thetime.tm_sec = 0;
  } else {
    thetime.tm_sec  = atoi(thestr);   *thestr = '\0';  thestr -= 2;
  }
  thetime.tm_min  = atoi(thestr);   *thestr = '\0';  thestr -= 2;
  thetime.tm_hour = atoi(thestr);   *thestr = '\0';  thestr -= 2;
  thetime.tm_mday = atoi(thestr);   *thestr = '\0';  thestr -= 2;
  thetime.tm_mon  = atoi(thestr)-1; *thestr = '\0';

  if (ASN1_STRING_type(timestr) == V_ASN1_UTCTIME) {
    thestr -= 2;
    thetime.tm_year = atoi(thestr);
    if (thetime.tm_year < 68) {
      thetime.tm_year += 100;
    }
  } else if (ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME) {
    thestr -= 4;
    thetime.tm_year = atoi(thestr) - 1900;
  }

  thetime.tm_isdst = -1;
  time_t ret = mktime(&thetime);

  long gmadjust = 0;
#if HAVE_TM_GMTOFF
  gmadjust = thetime.tm_gmtoff;
#elif defined(_MSC_VER)
  TIME_ZONE_INFORMATION inf;
  GetTimeZoneInformation(&inf);
  gmadjust = thetime.tm_isdst ? inf.DaylightBias : inf.StandardBias;
#else
  /**
   * If correcting for daylight savings time, we set the adjustment to
   * the value of timezone - 3600 seconds. Otherwise, we need to overcorrect
   * and set the adjustment to the main timezone + 3600 seconds.
   */
  gmadjust = -(thetime.tm_isdst ?
               (long)timezone - 3600 : (long)timezone);
#endif
  /* no adjustment for UTC */
  if (timezone) ret += gmadjust;
  free(strbuf);
  return ret;
}

/* Special handling of subjectAltName, see CVE-2013-4073
 * Christian Heimes
 */

static int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension)
{
  GENERAL_NAMES *names;
  const X509V3_EXT_METHOD *method = nullptr;
  long i, length, num;
  const unsigned char *p;

  method = X509V3_EXT_get(extension);
  if (method == nullptr) {
    return -1;
  }

  const auto data = X509_EXTENSION_get_data(extension);
  p = data->data;
  length = data->length;
  if (method->it) {
    names = (GENERAL_NAMES*)(ASN1_item_d2i(nullptr, &p, length,
                                          ASN1_ITEM_ptr(method->it)));
  } else {
    names = (GENERAL_NAMES*)(method->d2i(nullptr, &p, length));
  }
  if (names == nullptr) {
    return -1;
  }

  num = sk_GENERAL_NAME_num(names);
  for (i = 0; i < num; i++) {
    GENERAL_NAME *name;
    ASN1_STRING *as;
    name = sk_GENERAL_NAME_value(names, i);
    switch (name->type) {
      case GEN_EMAIL:
             BIO_puts(bio, "email:");
             as = name->d.rfc822Name;
             BIO_write(bio, ASN1_STRING_data(as),
                       ASN1_STRING_length(as));
             break;
      case GEN_DNS:
             BIO_puts(bio, "DNS:");
             as = name->d.dNSName;
             BIO_write(bio, ASN1_STRING_data(as),
                       ASN1_STRING_length(as));
             break;
      case GEN_URI:
             BIO_puts(bio, "URI:");
             as = name->d.uniformResourceIdentifier;
             BIO_write(bio, ASN1_STRING_data(as),
                       ASN1_STRING_length(as));
             break;
      default:
             /* use builtin print for GEN_OTHERNAME, GEN_X400,
              * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID
              */
             GENERAL_NAME_print(bio, name);
    }
    /* trailing ', ' except for last element */
    if (i < (num - 1)) {
           BIO_puts(bio, ", ");
    }
  }
  sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);

  return 0;
}
Variant HHVM_FUNCTION(openssl_x509_parse, const Variant& x509cert,
                                          bool shortnames /* = true */) {
  auto ocert = Certificate::Get(x509cert);
  if (!ocert) {
    return false;
  }
  X509 *cert = ocert->m_cert;
  assertx(cert);

  auto ret = Array::CreateDArray();
  const auto sn = X509_get_subject_name(cert);
  if (sn) {
    ret.set(s_name, String(X509_NAME_oneline(sn, nullptr, 0), CopyString));
  }
  add_assoc_name_entry(ret, "subject", sn, shortnames);
  /* hash as used in CA directories to lookup cert by subject name */
  {
    char buf[32];
    snprintf(buf, sizeof(buf), "%08lx", X509_subject_name_hash(cert));
    ret.set(s_hash, String(buf, CopyString));
  }

  add_assoc_name_entry(ret, "issuer", X509_get_issuer_name(cert), shortnames);
  ret.set(s_version, X509_get_version(cert));

  ret.set(s_serialNumber, String
          (i2s_ASN1_INTEGER(nullptr, X509_get_serialNumber(cert)), AttachString));
  // Adding Signature Algorithm
  BIO *bio_out = BIO_new(BIO_s_mem());
  SCOPE_EXIT { BIO_free(bio_out); };
  if (i2a_ASN1_OBJECT(bio_out, X509_get0_tbs_sigalg(cert)->algorithm) > 0) {
    BUF_MEM *bio_buf;
    BIO_get_mem_ptr(bio_out, &bio_buf);
    ret.set(s_signatureAlgorithm,
            String((char*)bio_buf->data, bio_buf->length, CopyString));
  }

  ASN1_STRING *str = X509_get_notBefore(cert);
  ret.set(s_validFrom, String((char*)str->data, str->length, CopyString));
  str = X509_get_notAfter(cert);
  ret.set(s_validTo, String((char*)str->data, str->length, CopyString));
  ret.set(s_validFrom_time_t, asn1_time_to_time_t(X509_get_notBefore(cert)));
  ret.set(s_validTo_time_t, asn1_time_to_time_t(X509_get_notAfter(cert)));

  char *tmpstr = (char *)X509_alias_get0(cert, nullptr);
  if (tmpstr) {
    ret.set(s_alias, String(tmpstr, CopyString));
  }

  /* NOTE: the purposes are added as integer keys - the keys match up to
     the X509_PURPOSE_SSL_XXX defines in x509v3.h */
  {
    Array subitem;
    for (int i = 0; i < X509_PURPOSE_get_count(); i++) {
      X509_PURPOSE *purp = X509_PURPOSE_get0(i);
      int id = X509_PURPOSE_get_id(purp);
      char * pname = shortnames ? X509_PURPOSE_get0_sname(purp) :
        X509_PURPOSE_get0_name(purp);
      auto subsub = make_varray(
        (bool)X509_check_purpose(cert, id, 0),
        (bool)X509_check_purpose(cert, id, 1),
        String(pname, CopyString)
      );
      subitem.set(id, std::move(subsub));
    }
    ret.set(s_purposes, subitem);
  }
  {
    auto subitem = Array::CreateDArray();
    for (int i = 0; i < X509_get_ext_count(cert); i++) {
      int nid;
      X509_EXTENSION *extension = X509_get_ext(cert, i);
      char *extname;
      char buf[256];
      nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
      if (nid != NID_undef) {
        extname = (char*)OBJ_nid2sn(OBJ_obj2nid
                                    (X509_EXTENSION_get_object(extension)));
      } else {
        OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension),
                    1);
        extname = buf;
      }
      BIO *bio_out = BIO_new(BIO_s_mem());
      if (nid == NID_subject_alt_name) {
        if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
          BUF_MEM *bio_buf;
          BIO_get_mem_ptr(bio_out, &bio_buf);
          subitem.set(String(extname, CopyString),
                      String((char*)bio_buf->data,
                             bio_buf->length,
                             CopyString));
        } else {
          BIO_free(bio_out);
          return false;
        }
      } else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
        BUF_MEM *bio_buf;
        BIO_get_mem_ptr(bio_out, &bio_buf);
        subitem.set(String(extname, CopyString),
                    String((char*)bio_buf->data, bio_buf->length, CopyString));
      } else {
        str = X509_EXTENSION_get_data(extension);
        subitem.set(String(extname, CopyString),
                    String((char*)str->data, str->length, CopyString));
      }
      BIO_free(bio_out);
    }
    ret.set(s_extensions, subitem);
  }

  return ret;
}

Variant HHVM_FUNCTION(openssl_x509_read, const Variant& x509certdata) {
  auto ocert = Certificate::Get(x509certdata);
  if (!ocert) {
    raise_warning("supplied parameter cannot be coerced into "
                  "an X509 certificate!");
    return false;
  }
  return Variant(ocert);
}

Variant HHVM_FUNCTION(openssl_random_pseudo_bytes, int length,
                      bool& crypto_strong) {
  if (length <= 0) {
    return false;
  }

  unsigned char *buffer = nullptr;

  String s = String(length, ReserveString);
  buffer = (unsigned char *)s.mutableData();

  if (RAND_bytes(buffer, length) <= 0) {
    crypto_strong = false;
    return false;
  } else {
    crypto_strong = true;
    s.setSize(length);
    return s;
  }
}

Variant HHVM_FUNCTION(openssl_cipher_iv_length, const String& method) {
  if (method.empty()) {
    raise_warning("Unknown cipher algorithm");
    return false;
  }

  const EVP_CIPHER *cipher_type = EVP_get_cipherbyname(method.c_str());
  if (!cipher_type) {
    raise_warning("Unknown cipher algorithm");
    return false;
  }

  return EVP_CIPHER_iv_length(cipher_type);
}

/* Cipher mode info */
struct php_openssl_cipher_mode {
  /* Whether this mode uses authenticated encryption. True, for example, with
     the GCM and CCM modes */
  bool is_aead;
  /* Whether this mode is a 'single run aead', meaning that DecryptFinal doesn't
     get called. For example, CCM mode is a single run aead mode. */
  bool is_single_run_aead;
  /* The OpenSSL flag to get the computed tag, if this mode is aead. */
  int aead_get_tag_flag;
  /* The OpenSSL flag to set the computed tag, if this mode is aead. */
  int aead_set_tag_flag;
  /* The OpenSSL flag to set the IV length, if this mode is aead */
  int aead_ivlen_flag;
};

// initialize a php_openssl_cipher_mode corresponding to an EVP_CIPHER.
static php_openssl_cipher_mode php_openssl_load_cipher_mode(
    const EVP_CIPHER* cipher_type) {
  php_openssl_cipher_mode mode = {};
  switch (EVP_CIPHER_mode(cipher_type)) {
#ifdef EVP_CIPH_GCM_MODE
    case EVP_CIPH_GCM_MODE:
      mode.is_aead = true;
      mode.is_single_run_aead = false;
      mode.aead_get_tag_flag = EVP_CTRL_GCM_GET_TAG;
      mode.aead_set_tag_flag = EVP_CTRL_GCM_SET_TAG;
      mode.aead_ivlen_flag = EVP_CTRL_GCM_SET_IVLEN;
      break;
#endif
#ifdef EVP_CIPH_CCM_MODE
    case EVP_CIPH_CCM_MODE:
      mode.is_aead = true;
      mode.is_single_run_aead = true;
      mode.aead_get_tag_flag = EVP_CTRL_CCM_GET_TAG;
      mode.aead_set_tag_flag = EVP_CTRL_CCM_SET_TAG;
      mode.aead_ivlen_flag = EVP_CTRL_CCM_SET_IVLEN;
      break;
#endif
    default:
      break;
  }
  return mode;
}

static bool php_openssl_validate_iv(
    String piv,
    int iv_required_len,
    String& out,
    EVP_CIPHER_CTX* cipher_ctx,
    const php_openssl_cipher_mode* mode) {
  if (cipher_ctx == nullptr || mode == nullptr) {
    return false;
  }

  /* Best case scenario, user behaved */
  if (piv.size() == iv_required_len) {
    out = std::move(piv);
    return true;
  }

  if (mode->is_aead) {
    if (EVP_CIPHER_CTX_ctrl(
            cipher_ctx, mode->aead_ivlen_flag, piv.size(), nullptr) != 1) {
      raise_warning(
          "Setting of IV length for AEAD mode failed, the expected length is "
          "%d bytes",
          iv_required_len);
      return false;
    }
    out = std::move(piv);
    return true;
  }

  String s = String(iv_required_len, ReserveString);
  char* iv_new = s.mutableData();
  memset(iv_new, 0, iv_required_len);

  if (piv.size() <= 0) {
    /* BC behavior */
    s.setSize(iv_required_len);
    out = std::move(s);
    return true;
  }

  if (piv.size() < iv_required_len) {
    raise_warning("IV passed is only %ld bytes long, cipher "
                  "expects an IV of precisely %d bytes, padding with \\0",
                  piv.size(), iv_required_len);
    memcpy(iv_new, piv.data(), piv.size());
    s.setSize(iv_required_len);
    out = std::move(s);
    return true;
  }

  raise_warning("IV passed is %ld bytes long which is longer than the %d "
                "expected by selected cipher, truncating", piv.size(),
                iv_required_len);
  memcpy(iv_new, piv.data(), iv_required_len);
  s.setSize(iv_required_len);
  out = std::move(s);
  return true;
}

namespace {

Variant openssl_encrypt_impl(const String& data,
                                    const String& method,
                                    const String& password,
                                    int options,
                                    const String& iv,
                                    Variant* tag_out,
                                    const String& aad,
                                    int tag_length) {
  const EVP_CIPHER *cipher_type = EVP_get_cipherbyname(method.c_str());
  if (!cipher_type) {
    raise_warning("Unknown cipher algorithm");
    return false;
  }

  EVP_CIPHER_CTX* cipher_ctx = EVP_CIPHER_CTX_new();
  if (!cipher_ctx) {
    raise_warning("Failed to create cipher context");
    return false;
  }

  SCOPE_EXIT {
    EVP_CIPHER_CTX_free(cipher_ctx);
  };

  php_openssl_cipher_mode mode = php_openssl_load_cipher_mode(cipher_type);

  if (mode.is_aead && !tag_out) {
    raise_warning("Must call openssl_encrypt_with_tag when using an AEAD cipher");
    return false;
  }

  int keylen = EVP_CIPHER_key_length(cipher_type);
  String key = password;

  /*
   * older openssl libraries can assert if the passed in password length is
   * less than keylen
   */
  if (keylen > password.size()) {
    String s = String(keylen, ReserveString);
    char *keybuf = s.mutableData();
    memset(keybuf, 0, keylen);
    memcpy(keybuf, password.data(), password.size());
    key = s.setSize(keylen);
  }

  int max_iv_len = EVP_CIPHER_iv_length(cipher_type);
  if (iv.size() <= 0 && max_iv_len > 0 && !mode.is_aead) {
    raise_warning("Using an empty Initialization Vector (iv) is potentially "
                  "insecure and not recommended");
  }

  int result_len = 0;
  int outlen = data.size() + EVP_CIPHER_block_size(cipher_type);
  String rv = String(outlen, ReserveString);
  unsigned char *outbuf = (unsigned char*)rv.mutableData();

  EVP_EncryptInit_ex(cipher_ctx, cipher_type, nullptr, nullptr, nullptr);

  String new_iv;
  // we do this after EncryptInit because validate_iv changes cipher_ctx for
  // aead modes (must be initialized first).
  if (!php_openssl_validate_iv(
          std::move(iv), max_iv_len, new_iv, cipher_ctx, &mode)) {
    return false;
  }

  // set the tag length for CCM mode/other modes that require tag lengths to
  // be set.
  if (mode.is_single_run_aead &&
      !EVP_CIPHER_CTX_ctrl(
          cipher_ctx, mode.aead_set_tag_flag, tag_length, nullptr)) {
    raise_warning("Setting tag length failed");
    return false;
  }
  if (password.size() > keylen) {
    EVP_CIPHER_CTX_set_key_length(cipher_ctx, password.size());
  }
  EVP_EncryptInit_ex(
      cipher_ctx,
      nullptr,
      nullptr,
      (unsigned char*)key.data(),
      (unsigned char*)new_iv.data());
  if (options & k_OPENSSL_ZERO_PADDING) {
    EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
  }

  // for single run aeads like CCM, we need to provide the length of the
  // plaintext before providing AAD or ciphertext.
  if (mode.is_single_run_aead &&
      !EVP_EncryptUpdate(
          cipher_ctx, nullptr, &result_len, nullptr, data.size())) {
    raise_warning("Setting of data length failed");
    return false;
  }

  // set up aad:
  if (mode.is_aead &&
      !EVP_EncryptUpdate(
          cipher_ctx,
          nullptr,
          &result_len,
          (unsigned char*)aad.data(),
          aad.size())) {
    raise_warning("Setting of additional application data failed");
    return false;
  }

  // OpenSSL before 0.9.8i asserts with size < 0
  if (data.size() >= 0) {
    EVP_EncryptUpdate(cipher_ctx, outbuf, &result_len,
                      (unsigned char *)data.data(), data.size());
  }

  outlen = result_len;

  if (EVP_EncryptFinal_ex(
          cipher_ctx, (unsigned char*)outbuf + result_len, &result_len)) {
    outlen += result_len;
    rv.setSize(outlen);
    // Get tag if possible
    if (mode.is_aead) {
      String tagrv = String(tag_length, ReserveString);
      if (EVP_CIPHER_CTX_ctrl(
              cipher_ctx,
              mode.aead_get_tag_flag,
              tag_length,
              tagrv.mutableData()) == 1) {
        tagrv.setSize(tag_length);
        assertx(tag_out);
        *tag_out = tagrv;
      } else {
        raise_warning("Retrieving authentication tag failed");
        return false;
      }
    } else if (tag_out) {
      raise_warning(
          "The authenticated tag cannot be provided for cipher that does not"
          " support AEAD");
    }
    // Return encrypted data
    if (options & k_OPENSSL_RAW_DATA) {
      return rv;
    } else {
      return StringUtil::Base64Encode(rv);
    }
  }
  return false;
}

} // anonymous namespace

Variant HHVM_FUNCTION(openssl_encrypt,
                      const String& data,
                      const String& method,
                      const String& password,
                      int options /* = 0 */,
                      const String& iv /* = null_string */,
                      const String& aad /* = null_string */,
                      int tag_length /* = 16 */) {
  return openssl_encrypt_impl(data, method, password, options, iv,
                              nullptr, aad, tag_length);
}

Variant HHVM_FUNCTION(openssl_encrypt_with_tag,
                      const String& data,
                      const String& method,
                      const String& password,
                      int options,
                      const String& iv,
                      Variant& tag_out,
                      const String& aad /* = null_string */,
                      int tag_length /* = 16 */) {
  return openssl_encrypt_impl(data, method, password, options, iv,
                              &tag_out, aad, tag_length);
}

Variant HHVM_FUNCTION(openssl_decrypt, const String& data, const String& method,
                                       const String& password,
                                       int options /* = 0 */,
                                       const String& iv /* = null_string */,
                                       const String& tag /* = null_string */,
                                       const String& aad /* = null_string */) {
  const EVP_CIPHER *cipher_type = EVP_get_cipherbyname(method.c_str());
  if (!cipher_type) {
    raise_warning("Unknown cipher algorithm");
    return false;
  }

  EVP_CIPHER_CTX* cipher_ctx = EVP_CIPHER_CTX_new();
  if (!cipher_ctx) {
    raise_warning("Failed to create cipher context");
    return false;
  }

  SCOPE_EXIT {
    EVP_CIPHER_CTX_free(cipher_ctx);
  };

  php_openssl_cipher_mode mode = php_openssl_load_cipher_mode(cipher_type);

  String decoded_data = data;

  if (!(options & k_OPENSSL_RAW_DATA)) {
    decoded_data = StringUtil::Base64Decode(data);
  }

  int keylen = EVP_CIPHER_key_length(cipher_type);
  String key = password;

  /*
   * older openssl libraries can assert if the passed in password length is
   * less than keylen
   */
   if (keylen > password.size()) {
    String s = String(keylen, ReserveString);
    char *keybuf = s.mutableData();
    memset(keybuf, 0, keylen);
    memcpy(keybuf, password.data(), password.size());
    key = s.setSize(keylen);
  }

  int result_len = 0;
  int outlen = decoded_data.size() + EVP_CIPHER_block_size(cipher_type);
  String rv = String(outlen, ReserveString);
  unsigned char *outbuf = (unsigned char*)rv.mutableData();

  EVP_DecryptInit_ex(cipher_ctx, cipher_type, nullptr, nullptr, nullptr);

  String new_iv;
  // we do this after DecryptInit because validate_iv changes cipher_ctx for
  // aead modes (must be initialized first).
  if (!php_openssl_validate_iv(
          std::move(iv),
          EVP_CIPHER_iv_length(cipher_type),
          new_iv,
          cipher_ctx,
          &mode)) {
    return false;
  }

  // set the tag if required:
  if (tag.size() > 0) {
    if (!mode.is_aead) {
      raise_warning(
          "The tag is being ignored because the cipher method does not"
          " support AEAD");
    } else if (!EVP_CIPHER_CTX_ctrl(
                   cipher_ctx,
                   mode.aead_set_tag_flag,
                   tag.size(),
                   (unsigned char*)tag.data())) {
      raise_warning("Setting tag for AEAD cipher decryption failed");
      return false;
    }
  } else {
    if (mode.is_aead) {
      raise_warning("A tag should be provided when using AEAD mode");
      return false;
    }
  }
  if (password.size() > keylen) {
    EVP_CIPHER_CTX_set_key_length(cipher_ctx, password.size());
  }
  EVP_DecryptInit_ex(
      cipher_ctx,
      nullptr,
      nullptr,
      (unsigned char*)key.data(),
      (unsigned char*)new_iv.data());
  if (options & k_OPENSSL_ZERO_PADDING) {
    EVP_CIPHER_CTX_set_padding(cipher_ctx, 0);
  }

  // for single run aeads like CCM, we need to provide the length of the
  // ciphertext before providing AAD or ciphertext.
  if (mode.is_single_run_aead &&
      !EVP_DecryptUpdate(
          cipher_ctx, nullptr, &result_len, nullptr, decoded_data.size())) {
    raise_warning("Setting of data length failed");
    return false;
  }

  // set up aad:
  if (mode.is_aead &&
      !EVP_DecryptUpdate(
          cipher_ctx,
          nullptr,
          &result_len,
          (unsigned char*)aad.data(),
          aad.size())) {
    raise_warning("Setting of additional application data failed");
    return false;
  }

  if (!EVP_DecryptUpdate(
          cipher_ctx,
          outbuf,
          &result_len,
          (unsigned char*)decoded_data.data(),
          decoded_data.size())) {
    return false;
  }
  outlen = result_len;

  // if is_single_run_aead is enabled, DecryptFinal shouldn't be called.
  // if something went wrong in this case, we would've caught it at
  // DecryptUpdate.
  if (mode.is_single_run_aead ||
      EVP_DecryptFinal_ex(
          cipher_ctx, (unsigned char*)outbuf + result_len, &result_len)) {
    // don't want to do this if is_single_run_aead was enabled, since we didn't
    // make a call to EVP_DecryptFinal.
    if (!mode.is_single_run_aead) {
      outlen += result_len;
    }
    rv.setSize(outlen);
    return rv;
  } else {
    return false;
  }
}

Variant HHVM_FUNCTION(openssl_digest, const String& data, const String& method,
                                      bool raw_output /* = false */) {
  const EVP_MD *mdtype = EVP_get_digestbyname(method.c_str());

  if (!mdtype) {
    raise_warning("Unknown signature algorithm");
    return false;
  }
  int siglen = EVP_MD_size(mdtype);
  String rv = String(siglen, ReserveString);
  unsigned char *sigbuf = (unsigned char *)rv.mutableData();
  EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
  SCOPE_EXIT {
    EVP_MD_CTX_free(md_ctx);
  };

  EVP_DigestInit(md_ctx, mdtype);

  EVP_DigestUpdate(md_ctx, (unsigned char *)data.data(), data.size());
  if (EVP_DigestFinal(md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) {
    if (raw_output) {
      rv.setSize(siglen);
      return rv;
    } else {
      char* digest_str = string_bin2hex((char*)sigbuf, siglen);
      return String(digest_str, AttachString);
    }
  } else {
    return false;
  }
}

static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg)
{
  Array *ret = (Array*)arg;
  ret->append(String((char *)name->name, CopyString));
}

static void openssl_add_method(const OBJ_NAME *name, void *arg)
{
  if (name->alias == 0) {
    Array *ret = (Array*)arg;
    ret->append(String((char *)name->name, CopyString));
  }
}

Array HHVM_FUNCTION(openssl_get_cipher_methods, bool aliases /* = false */) {
  Array ret = Array::CreateVArray();
  OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
    aliases ? openssl_add_method_or_alias: openssl_add_method,
    &ret);
  return ret;
}

Variant HHVM_FUNCTION(openssl_get_curve_names) {
#ifdef HAVE_EVP_PKEY_EC
  const size_t len = EC_get_builtin_curves(nullptr, 0);
  std::unique_ptr<EC_builtin_curve[]> curves(new EC_builtin_curve[len]);
  if (!EC_get_builtin_curves(curves.get(), len)) {
    return false;
  }

  VArrayInit ret(len);
  for (size_t i = 0; i < len; ++i) {
    auto const sname = OBJ_nid2sn(curves[i].nid);
    if (sname != nullptr) {
      ret.append(String(sname, CopyString));
    }
  }

  return ret.toArray();
#else
  return false;
#endif
}

Array HHVM_FUNCTION(openssl_get_md_methods, bool aliases /* = false */) {
  Array ret = Array::CreateVArray();
  OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
    aliases ? openssl_add_method_or_alias: openssl_add_method,
    &ret);
  return ret;
}

/////////////////////////////////////////////////////////////////////////////

const StaticString s_OPENSSL_VERSION_TEXT("OPENSSL_VERSION_TEXT");

struct opensslExtension final : Extension {
  opensslExtension() : Extension("openssl") {}
  void moduleInit() override {
    HHVM_RC_INT(OPENSSL_RAW_DATA, k_OPENSSL_RAW_DATA);
    HHVM_RC_INT(OPENSSL_ZERO_PADDING, k_OPENSSL_ZERO_PADDING);
    HHVM_RC_INT(OPENSSL_NO_PADDING, k_OPENSSL_NO_PADDING);
    HHVM_RC_INT(OPENSSL_PKCS1_OAEP_PADDING, k_OPENSSL_PKCS1_OAEP_PADDING);
    HHVM_RC_INT(OPENSSL_SSLV23_PADDING, k_OPENSSL_SSLV23_PADDING);
    HHVM_RC_INT(OPENSSL_PKCS1_PADDING, k_OPENSSL_PKCS1_PADDING);

    HHVM_RC_INT_SAME(OPENSSL_ALGO_SHA1);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_MD5);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_MD4);
#ifdef HAVE_OPENSSL_MD2_H
    HHVM_RC_INT_SAME(OPENSSL_ALGO_MD2);
#endif
    HHVM_RC_INT_SAME(OPENSSL_ALGO_DSS1);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_SHA224);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_SHA256);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_SHA384);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_SHA512);
    HHVM_RC_INT_SAME(OPENSSL_ALGO_RMD160);

    HHVM_RC_INT(OPENSSL_CIPHER_RC2_40, PHP_OPENSSL_CIPHER_RC2_40);
    HHVM_RC_INT(OPENSSL_CIPHER_RC2_128, PHP_OPENSSL_CIPHER_RC2_128);
    HHVM_RC_INT(OPENSSL_CIPHER_RC2_64, PHP_OPENSSL_CIPHER_RC2_64);
    HHVM_RC_INT(OPENSSL_CIPHER_DES, PHP_OPENSSL_CIPHER_DES);
    HHVM_RC_INT(OPENSSL_CIPHER_3DES, PHP_OPENSSL_CIPHER_3DES);

    HHVM_RC_INT_SAME(OPENSSL_KEYTYPE_RSA);
    HHVM_RC_INT_SAME(OPENSSL_KEYTYPE_DSA);
    HHVM_RC_INT_SAME(OPENSSL_KEYTYPE_DH);
#ifdef HAVE_EVP_PKEY_EC
    HHVM_RC_INT_SAME(OPENSSL_KEYTYPE_EC);
#endif

    HHVM_RC_INT_SAME(OPENSSL_VERSION_NUMBER);

    HHVM_RC_INT_SAME(PKCS7_TEXT);
    HHVM_RC_INT_SAME(PKCS7_NOCERTS);
    HHVM_RC_INT_SAME(PKCS7_NOSIGS);
    HHVM_RC_INT_SAME(PKCS7_NOCHAIN);
    HHVM_RC_INT_SAME(PKCS7_NOINTERN);
    HHVM_RC_INT_SAME(PKCS7_NOVERIFY);
    HHVM_RC_INT_SAME(PKCS7_DETACHED);
    HHVM_RC_INT_SAME(PKCS7_BINARY);
    HHVM_RC_INT_SAME(PKCS7_NOATTR);

    HHVM_RC_STR_SAME(OPENSSL_VERSION_TEXT);

    HHVM_RC_INT_SAME(X509_PURPOSE_SSL_CLIENT);
    HHVM_RC_INT_SAME(X509_PURPOSE_SSL_SERVER);
    HHVM_RC_INT_SAME(X509_PURPOSE_NS_SSL_SERVER);
    HHVM_RC_INT_SAME(X509_PURPOSE_SMIME_SIGN);
    HHVM_RC_INT_SAME(X509_PURPOSE_SMIME_ENCRYPT);
    HHVM_RC_INT_SAME(X509_PURPOSE_CRL_SIGN);
#ifdef X509_PURPOSE_ANY
    HHVM_RC_INT_SAME(X509_PURPOSE_ANY);
#endif

    HHVM_FE(openssl_csr_export_to_file);
    HHVM_FE(openssl_csr_export);
    HHVM_FE(openssl_csr_get_public_key);
    HHVM_FE(openssl_csr_get_subject);
    HHVM_FE(openssl_csr_new);
    HHVM_FE(openssl_csr_sign);
    HHVM_FE(openssl_error_string);
    HHVM_FE(openssl_open);
    HHVM_FE(openssl_pkcs12_export_to_file);
    HHVM_FE(openssl_pkcs12_export);
    HHVM_FE(openssl_pkcs12_read);
    HHVM_FE(openssl_pkcs7_decrypt);
    HHVM_FE(openssl_pkcs7_encrypt);
    HHVM_FE(openssl_pkcs7_sign);
    HHVM_FE(openssl_pkcs7_verify);
    HHVM_FE(openssl_pkey_export_to_file);
    HHVM_FE(openssl_pkey_export);
    HHVM_FE(openssl_pkey_get_details);
    HHVM_FE(openssl_pkey_get_private);
    HHVM_FE(openssl_pkey_get_public);
    HHVM_FE(openssl_pkey_new);
    HHVM_FE(openssl_private_decrypt);
    HHVM_FE(openssl_private_encrypt);
    HHVM_FE(openssl_public_decrypt);
    HHVM_FE(openssl_public_encrypt);
    HHVM_FE(openssl_seal);
    HHVM_FE(openssl_sign);
    HHVM_FE(openssl_verify);
    HHVM_FE(openssl_x509_check_private_key);
    HHVM_FE(openssl_x509_checkpurpose);
    HHVM_FE(openssl_x509_export_to_file);
    HHVM_FE(openssl_x509_export);
    HHVM_FE(openssl_x509_parse);
    HHVM_FE(openssl_x509_read);
    HHVM_FE(openssl_random_pseudo_bytes);
    HHVM_FE(openssl_cipher_iv_length);
    HHVM_FE(openssl_encrypt);
    HHVM_FE(openssl_encrypt_with_tag);
    HHVM_FE(openssl_decrypt);
    HHVM_FE(openssl_digest);
    HHVM_FE(openssl_get_cipher_methods);
    HHVM_FE(openssl_get_curve_names);
    HHVM_FE(openssl_get_md_methods);

    loadSystemlib();
  }
} s_openssl_extension;

///////////////////////////////////////////////////////////////////////////////
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(ucwords,
                     const String& str,
                     const String& delimiters /* = " \t\r\n\f\v"*/) {
  if (str.empty()) {
    return str;
  }

  char mask[257];
  string_charmask(delimiters.c_str(), delimiters.size(), mask);
  mask[256] = 1; // special 'start of string' character

  int last = 256;
  return stringForEach<false>(str.size(), str, [&] (char c) {
      char ret = mask[last] ? toupper(c) : c;
      last = (uint8_t)c;
      return ret;
    });
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imageconvolution, const Resource& image,
    const Array& matrix, double div, double offset) {
  gdImagePtr im_src = cast<Image>(image)->get();
  if (!im_src) return false;
  int nelem = matrix.size();
  int i, j;
  float mtx[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}};
  Variant v;
  Array row;

  if (nelem != 3) {
    raise_warning("You must have 3x3 array");
    return false;
  }
  for (i=0; i<3; i++) {
    if (matrix.exists(i) && (v = matrix[i]).isArray()) {
      if ((row = v.toArray()).size() != 3) {
        raise_warning("You must have 3x3 array");
        return false;
      }

      for (j=0; j<3; j++) {
        if (row.exists(j)) {
          mtx[i][j] = row[j].toDouble();
        } else {
          raise_warning("You must have a 3x3 matrix");
          return false;
        }
      }
    }
  }
  if (hphp_gdImageConvolution(im_src, mtx, div, offset)) {
    return true;
  } else {
    return false;
  }
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecreatetruecolor, int64_t width, int64_t height) {
  gdImagePtr im;

  if (width <= 0 || height <= 0 || width >= INT_MAX || height >= INT_MAX) {
    raise_warning("Invalid image dimensions");
    return false;
  }

  im = gdImageCreateTrueColor(width, height);

  if (!im) {
    return false;
  }
  return Variant(req::make<Image>(im));
}

Project: hhvm
File: ext_gd.cpp

php_image_filter_edgedetect(gdImagePtr im, int /*arg1*/ /* = 0 */,
                            int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                            int /*arg4*/ /* = 0 */) {
  return gdImageEdgeDetectQuick(im) == 1;
}

Project: hhvm
File: ext_gd.cpp

_php_image_output(const Resource& image, const String& filename, int quality,
                  int type, int image_type, char* /*tn*/, void (*func_p)()) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  req::ptr<File> file;
  FILE *fp;
  int q = quality, i, t = type;

  /* The quality parameter for Wbmp stands for the threshold when
     called from image2wbmp() */
  /* When called from imagewbmp() the quality parameter stands
     for the foreground color. Default: black. */
  /* The quality parameter for gd2 stands for chunk size */

  if (!filename.empty()) {
    file = php_open_plain_file(filename, "wb", &fp);
    if (!file) {
      raise_warning("Unable to open '%s' for writing", filename.c_str());
      return false;
    }

    switch (image_type) {
    case PHP_GDIMG_CONVERT_WBM:
      if (q == -1) {
        q = 0;
      } else if (q < 0 || q > 255) {
        raise_warning("Invalid threshold value '%d'. "
                        "It must be between 0 and 255", q);
        q = 0;
      }
      gdImageWBMP(im, q, fp);
      break;
    case PHP_GDIMG_TYPE_JPG: {
      // gdImageJpeg
      ((void(*)(gdImagePtr, FILE *, int))(func_p))(im, fp, q);
      break;
    }
    case PHP_GDIMG_TYPE_WBM:
      for (i = 0; i < gdImageColorsTotal(im); i++) {
        if (gdImageRed(im, i) == 0) break;
      }
      // gdImageWBMP
      ((void(*)(gdImagePtr, int, FILE *))(func_p))(im, i, fp);
      break;
    case PHP_GDIMG_TYPE_GD:
      if (im->trueColor) {
        gdImageTrueColorToPalette(im,1,256);
      }
      // gdImageGd
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, fp);
      break;
    case PHP_GDIMG_TYPE_GD2:
      if (q == -1) {
        q = 128;
      }
      // gdImageGd2
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, fp, q, t);
      break;
    default:
      if (q == -1) {
        q = 128;
      }
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, fp, q, t);
      break;
    }
    fflush(fp);
    file->close();
  } else {
    int   b;
    FILE *tmp;
    char  buf[4096];
    char path[PATH_MAX];

    // open a temporary file
    snprintf(path, sizeof(path), "/tmp/XXXXXX");
    int fd = mkstemp(path);
    if (fd == -1 || (tmp = fdopen(fd, "r+b")) == nullptr) {
      if (fd != -1) close(fd);
      raise_warning("Unable to open temporary file");
      return false;
    }

    switch (image_type) {
    case PHP_GDIMG_CONVERT_WBM:
       if (q == -1) {
          q = 0;
        } else if (q < 0 || q > 255) {
          raise_warning("Invalid threshold value '%d'. "
                          "It must be between 0 and 255", q);
         q = 0;
        }
      gdImageWBMP(im, q, tmp);
      break;
    case PHP_GDIMG_TYPE_JPG:
      ((void(*)(gdImagePtr, FILE *, int))(func_p))(im, tmp, q);
      break;
    case PHP_GDIMG_TYPE_WBM:
      for (i = 0; i < gdImageColorsTotal(im); i++) {
        if (gdImageRed(im, i) == 0) {
          break;
        }
      }
      ((void(*)(gdImagePtr, int, FILE *))(func_p))(im, q, tmp);
      break;
    case PHP_GDIMG_TYPE_GD:
      if (im->trueColor) {
        gdImageTrueColorToPalette(im,1,256);
      }
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, tmp);
      break;
    case PHP_GDIMG_TYPE_GD2:
      if (q == -1) {
        q = 128;
      }
      ((void(*)(gdImagePtr, FILE *, int, int))(func_p))(im, tmp, q, t);
      break;
    default:
      ((void(*)(gdImagePtr, FILE *))(func_p))(im, tmp);
      break;
    }

    fseek(tmp, 0, SEEK_SET);

    while ((b = fread(buf, 1, sizeof(buf), tmp)) > 0) {
      g_context->write(buf, b);
    }

    fclose(tmp);
    /* make sure that the temporary file is removed */
    unlink((const char *)path);
  }
  return true;
}

Project: hhvm
File: ext_gd.cpp

static char *php_strdup_impl(const char* s
#ifdef IM_MEMORY_CHECK
, int ln
#endif
  ) {
#ifdef IM_MEMORY_CHECK
  return php_strndup_impl(s, strlen(s), ln);
#else
  return php_strndup_impl(s, strlen(s));
#endif
}

Project: hhvm
File: string-data-inl.h

inline bool StringData::isImmutable() const {
  return !isRefCounted() || isProxy();
}

Project: hhvm
File: ext_string.cpp

static Variant substr_replace(const Variant& str, const Variant& replacement,
                              const Variant& start, const Variant& length) {
  if (!str.isArray()) {
    String repl;
    if (replacement.isArray()) {
      repl = replacement.asCArrRef()[0].toString();
    } else {
      repl = replacement.toString();
    }
    if (start.isArray()) {
      if (!length.isArray()) {
        raise_invalid_argument_warning("start and length should be of same type - "
                               "numerical or array");
        return str;
      }
      Array startArr = start.toArray();
      Array lengthArr = length.toArray();
      if (startArr.size() != lengthArr.size()) {
        raise_invalid_argument_warning("start and length: (different item count)");
        return str;
      }
      raise_invalid_argument_warning("start and length as arrays not implemented");
      return str;
    }
    return string_replace(str.toString(), start.toInt32(), length.toInt32(),
                          repl);
  }

  // 'start' and 'length' can be arrays (in which case we step through them in
  // sync with stepping through 'str'), or not arrays, in which case we convert
  // them to ints and always use those.
  Array ret;
  Array strArr = str.toArray();
  folly::Optional<int> opStart;
  folly::Optional<int> opLength;
  if (!start.isArray()) {
    opStart = start.toInt32();
  }
  if (!length.isArray()) {
    opLength = length.toInt32();
  }

  Array startArr = start.toArray();
  Array lengthArr = length.toArray();
  ArrayIter startIter(startArr);
  ArrayIter lengthIter(lengthArr);

  if (replacement.isArray()) {
    Array replArr = replacement.toArray();
    ArrayIter replIter(replArr);
    for (ArrayIter iter(strArr); iter; ++iter) {
      auto str = iter.second().toString();
      // If 'start' or 'length' are arrays and we've gone past the end, default
      // to 0 for start and the length of the input string for length.
      int nStart =
        (opStart.has_value()
         ? opStart.value()
         : (startIter ? startIter.second().toInt32() : 0));
      int nLength =
        (opLength.has_value()
         ? opLength.value()
         : (lengthIter ? lengthIter.second().toInt32() : str.length()));
      if (startIter) ++startIter;
      if (lengthIter) ++lengthIter;

      String repl;
      if (replIter) {
        repl = replIter.second().toString();
        ++replIter;
      } else {
        repl = empty_string();
      }
      auto s2 = string_replace(str, nStart, nLength, repl);
      ret.append(s2);
    }
  } else {
    String repl = replacement.toString();
    for (ArrayIter iter(strArr); iter; ++iter) {
      auto str = iter.second().toString();
      int nStart =
        (opStart.has_value()
         ? opStart.value()
         : (startIter ? startIter.second().toInt32() : 0));
      int nLength =
        (opLength.has_value()
         ? opLength.value()
         : (lengthIter ? lengthIter.second().toInt32() : str.length()));
      if (startIter) ++startIter;
      if (lengthIter) ++lengthIter;

      auto s2 = string_replace(str, nStart, nLength, repl);
      ret.append(s2);
    }
  }
  return ret;
}

Project: hhvm
File: zend-string.cpp

char* php_crypt_r(const char* key, const char* salt) {
  if (salt[0] == '$' && salt[1] == '1' && salt[2] == '$') {
    char output[MD5_HASH_MAX_LEN], *out;

    out = php_md5_crypt_r(key, salt, output);
    return out ? strdup(out) : nullptr;
  } else if (salt[0] == '$' && salt[1] == '6' && salt[2] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    char* crypt_res = php_sha512_crypt_r(key, salt, output, PHP_MAX_SALT_LEN);
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (salt[0] == '$' && salt[1] == '5' && salt[2] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    char* crypt_res = php_sha256_crypt_r(key, salt, output, PHP_MAX_SALT_LEN);
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (
    salt[0] == '$' &&
    salt[1] == '2' &&
    salt[3] == '$') {
    char output[PHP_MAX_SALT_LEN + 1];

    memset(output, 0, PHP_MAX_SALT_LEN + 1);

    char* crypt_res = php_crypt_blowfish_rn(key, salt, output, sizeof(output));
    if (!crypt_res) {
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return nullptr;
    } else {
      char* result = strdup(output);
      SECURE_ZERO(output, PHP_MAX_SALT_LEN + 1);
      return result;
    }
  } else if (salt[0] == '*' && (salt[1] == '0' || salt[1] == '1')) {
    return nullptr;
  } else {
    struct php_crypt_extended_data buffer;
    /* DES Fallback */
    memset(&buffer, 0, sizeof(buffer));
    _crypt_extended_init_r();

    char* crypt_res = _crypt_extended_r(key, salt, &buffer);
    if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) {
      return nullptr;
    } else {
      return strdup(crypt_res);
    }
  }
}

Project: hhvm
File: preg.cpp

static String preg_do_repl_func(const Variant& function, const String& subject,
                                int* offsets, const char* const* subpat_names,
                                int count) {
  Array subpats = Array::CreateDArray();
  for (int i = 0; i < count; i++) {
    auto off1 = offsets[i<<1];
    auto off2 = offsets[(i<<1)+1];
    auto sub = subject.substr(off1, off2 - off1);

    if (subpat_names[i]) {
      subpats.set(String(subpat_names[i]), sub);
    }
    subpats.set(i, sub);
  }

  return vm_call_user_func(function, make_varray(subpats)).toString();
}

Project: hhvm
File: zend-string.cpp

void string_translate(char *str, int len, const char *str_from,
                      const char *str_to, int trlen) {
  int i;
  unsigned char xlat[256];

  if ((trlen < 1) || (len < 1)) {
    return;
  }

  for (i = 0; i < 256; xlat[i] = i, i++);
  for (i = 0; i < trlen; i++) {
    xlat[(unsigned char) str_from[i]] = str_to[i];
  }

  for (i = 0; i < len; i++) {
    str[i] = xlat[(unsigned char) str[i]];
  }
}

Project: hhvm
File: ext_std_variable.cpp

bool HHVM_FUNCTION(is_numeric, const Variant& v) {
  return v.isNumeric(true);
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(str_replace,
                         const Variant& search, const Variant& replace,
                         const Variant& subject) {
  int64_t count;
  return tvReturn(str_replace(search, replace, subject, count));
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(str_split, const String& str, int64_t split_length) {
  return tvReturn(StringUtil::Split(str, split_length));
}

Project: hhvm
File: ext_gd.cpp

static struct gfxinfo *php_handle_iff(const req::ptr<File>& stream) {
  struct gfxinfo * result;
  char *a;
  int chunkId;
  int size;
  short width, height, bits;

  String str = stream->read(8);
  if (str.length() != 8) return nullptr;
  a = (char *)str.c_str();
  if (strncmp(a+4, "ILBM", 4) && strncmp(a+4, "PBM ", 4)) {
    return nullptr;
  }

  /* loop chunks to find BMHD chunk */
  do {
    str = stream->read(8);
    if (str.length() != 8) return nullptr;
    a = (char *)str.c_str();
    chunkId = php_ifd_get32s(a+0, 1);
    size = php_ifd_get32s(a+4, 1);
    if (size < 0) return nullptr;
    if ((size & 1) == 1) {
      size++;
    }
    if (chunkId == 0x424d4844) { /* BMHD chunk */
      if (size < 9) return nullptr;
      str = stream->read(9);
      if (str.length() != 9) return nullptr;
      a = (char *)str.c_str();
      width = php_ifd_get16s(a+0, 1);
      height = php_ifd_get16s(a+2, 1);
      bits = a[8] & 0xff;
      if (width > 0 && height > 0 && bits > 0 && bits < 33) {
        result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
        CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
        result->width = width;
        result->height = height;
        result->bits = bits;
        result->channels = 0;
        return result;
      }
    } else {
      if (!stream->seek(size, SEEK_CUR)) return nullptr;
    }
  } while (1);
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imageftbbox, double size, double angle,
    const String& font_file, const String& text,
    const Array& extrainfo /*=[] */) {
  return php_imagettftext_common(TTFTEXT_BBOX, 1,
                                 size, angle, font_file, text, extrainfo);
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imageline, const Resource& image,
    int64_t x1, int64_t y1, int64_t x2, int64_t y2, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  color = SetupAntiAliasedColor(im, color);
  gdImageLine(im, x1, y1, x2, y2, color);
  return true;
}

Project: hhvm
File: ext_std_variable.cpp

Variant HHVM_FUNCTION(HH_first_key, const Variant& v) {
  // 1. array, vec, dict, keyset
  if (v.isArray() || v.isClsMeth()) {
    auto arr = v.toArray();
    if (arr->empty()) {
      return init_null();
    }
    return arr->getKey(arr->iter_begin());
  }

  if (v.isObject()) {
    auto obj = v.asCObjRef();
    // 2. collection
    if (obj->isCollection()) {
      // Pair
      if (obj->collectionType() == CollectionType::Pair) {
        return Variant::wrap(make_tv<KindOfInt64>(0));
      }

      // Vector, Map, Set, and Imm variants
      auto arr = collections::asArray(obj.get());
      if (arr->empty()) {
        return init_null();
      }
      return arr->getKey(arr->iter_begin());
    }
  }
  SystemLib::throwInvalidArgumentExceptionObject(
    "Argument 1 passed to HH\\Lib\\_Private\\Native\\first_key() "
    "must be a Container");
}

Project: hhvm
File: ext_string.cpp

String stringForEach(uint32_t len, const String& str, Op action) {
  String ret = mutate ? str : String(len, ReserveString);

  auto srcSlice = str.slice();

  const char* src = srcSlice.begin();
  const char* end = srcSlice.end();

  char* dst = ret.mutableData();

  for (; src != end; ++src, ++dst) {
    *dst = action(*src);
  }

  if (!mutate) ret.setSize(len);
  return ret;
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecolorallocatealpha,
    const Resource& image,
    int64_t red, int64_t green, int64_t blue, int64_t alpha) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  int ct = gdImageColorAllocateAlpha(im, red, green, blue, alpha);
  if (ct < 0) {
    return false;
  }
  return ct;
}

Project: hhvm
File: ext_gd.cpp

static int exif_scan_JPEG_header(image_info_type *ImageInfo) {
  int section, sn;
  int marker = 0, last_marker = M_PSEUDO, comment_correction=1;
  int ll, lh;
  unsigned char *Data;
  size_t fpos, size, got, itemlen;
  jpeg_sof_info  sof_info;

  for(section=0;;section++) {
    // get marker byte, swallowing possible padding
    // some software does not count the length bytes of COM section
    // one company doing so is very much envolved in JPEG...
    // so we accept too
    if (last_marker==M_COM && comment_correction) {
      comment_correction = 2;
    }
    do {
      if ((marker = ImageInfo->infile->getc()) == EOF) {
        raise_warning("File structure corrupted");
        return 0;
      }
      if (last_marker==M_COM && comment_correction>0) {
        if (marker!=0xFF) {
          marker = 0xff;
          comment_correction--;
        } else  {
          last_marker = M_PSEUDO; /* stop skipping 0 for M_COM */
        }
      }
    } while (marker == 0xff);
    if (last_marker==M_COM && !comment_correction) {
      raise_notice("Image has corrupt COM section: some software set "
                   "wrong length information");
    }
    if (last_marker==M_COM && comment_correction)
      return M_EOI; /* ah illegal: char after COM section not 0xFF */

    fpos = ImageInfo->infile->tell();

    if (marker == 0xff) {
      // 0xff is legal padding, but if we get that many, something's wrong.
      raise_warning("To many padding bytes");
      return 0;
    }

    /* Read the length of the section. */

    if ((lh = ImageInfo->infile->getc()) == EOF) {
      raise_warning("File structure corrupted");
      return 0;
    }

    if ((ll = ImageInfo->infile->getc()) == EOF) {
      raise_warning("File structure corrupted");
      return 0;
    }

    itemlen = (lh << 8) | ll;

    if (itemlen < 2) {
      raise_warning("File structure corrupted");
      return 0;
    }

    sn = exif_file_sections_add(ImageInfo, marker, itemlen+1, nullptr);
    if (sn == -1) return 0;
    Data = ImageInfo->file.list[sn].data;

    /* Store first two pre-read bytes. */
    Data[0] = (unsigned char)lh;
    Data[1] = (unsigned char)ll;

    String str = ImageInfo->infile->read(itemlen-2);
    got = str.length();
    if (got != itemlen-2) {
      raise_warning("Error reading from file: "
                      "got=x%04lX(=%lu) != itemlen-2=x%04lX(=%lu)",
                      got, got, itemlen-2, itemlen-2);
      return 0;
    }
    memcpy(Data+2, str.c_str(), got);
    switch(marker) {
      case M_SOS:   /* stop before hitting compressed data  */
        // If reading entire image is requested, read the rest of the data.
        if (ImageInfo->read_all) {
          /* Determine how much file is left. */
          fpos = ImageInfo->infile->tell();
          size = ImageInfo->FileSize - fpos;
          sn = exif_file_sections_add(ImageInfo, M_PSEUDO, size, nullptr);
          if (sn == -1) return 0;
          Data = ImageInfo->file.list[sn].data;
          str = ImageInfo->infile->read(size);
          got = str.length();
          if (got != size) {
            raise_warning("Unexpected end of file reached");
            return 0;
          }
          memcpy(Data, str.c_str(), got);
        }
        return 1;

      case M_EOI:   /* in case it's a tables-only JPEG stream */
        raise_warning("No image in jpeg!");
        return (ImageInfo->sections_found&(~FOUND_COMPUTED)) ? 1 : 0;

      case M_COM: /* Comment section */
        exif_process_COM(ImageInfo, (char *)Data, itemlen);
        break;

      case M_EXIF:
        if (!(ImageInfo->sections_found&FOUND_IFD0)) {
          /*ImageInfo->sections_found |= FOUND_EXIF;*/
          /* Seen files from some 'U-lead' software with Vivitar scanner
             that uses marker 31 later in the file (no clue what for!) */
          exif_process_APP1(ImageInfo, (char *)Data, itemlen, fpos);
        }
        break;

      case M_APP12:
        exif_process_APP12(ImageInfo, (char *)Data, itemlen);
        break;


      case M_SOF0:
      case M_SOF1:
      case M_SOF2:
      case M_SOF3:
      case M_SOF5:
      case M_SOF6:
      case M_SOF7:
      case M_SOF9:
      case M_SOF10:
      case M_SOF11:
      case M_SOF13:
      case M_SOF14:
      case M_SOF15:
        if ((itemlen - 2) < 6) {
          return 0;
        }

        exif_process_SOFn(Data, marker, &sof_info);
        ImageInfo->Width  = sof_info.width;
        ImageInfo->Height = sof_info.height;
        if (sof_info.num_components == 3) {
          ImageInfo->IsColor = 1;
        } else {
          ImageInfo->IsColor = 0;
        }
        break;
      default:
        /* skip any other marker silently. */
        break;
    }

    /* keep track of last marker */
    last_marker = marker;
  }
  return 1;
}

Project: hhvm
File: ext_string.cpp

Variant HHVM_FUNCTION(strtr,
                      const String& str,
                      const Variant& from,
                      const Variant& to /* = uninit_variant */) {
  if (str.empty()) {
    return str;
  }

  if (!to.isNull()) {
    return StringUtil::Translate(str, from.toString(), to.toString());
  }

  if (!from.isArray()) {
    raise_invalid_argument_warning("2nd argument: (not array)");
    return false;
  }

  int maxlen = 0;
  int minlen = -1;
  Array arr = from.toArray();

  if (arr.empty()) {
    // Nothing to translate
    return str;
  }

  for (ArrayIter iter(arr); iter; ++iter) {
    auto const search = iter.first().toString();
    auto const len = search.size();
    if (len < 1) return false;
    if (maxlen < len) maxlen = len;
    if (minlen == -1 || minlen > len) minlen = len;
  }

  if (arr.size() <= kBitsPerQword && maxlen <= 16) {
    return strtr_fast(str, arr, minlen, maxlen);
  }

  if (arr.size() < 1000) {
    const WuManberReplacement replacer(arr, minlen);
    return replacer.translate(str);
  }

  // wu manber cost is mostly in preprocessing the patterns into
  // tables.  this hash is much faster than initializing and most
  // codebases likely only have a few constant sets with more than
  // a thousand patterns.
  int64_t hash = 0;
  for (ArrayIter iter(arr); iter; ++iter) {
    String pattern = iter.first().toString();
    String replacement = iter.second().toString();
    hash = murmur_hash_64A(pattern.data(), pattern.size(), hash);
    hash = murmur_hash_64A(replacement.data(), replacement.size(), hash);
  }
  WuManberCache::ConstAccessor got;
  WuManberPtr replacer;
  if (wuManberCache.find(got, hash)) {
    replacer = *got;
  } else {
    replacer.reset(new WuManberReplacement(arr, minlen));
    wuManberCache.insert(hash, replacer);
  }
  return replacer->translate(str);
}

Project: hhvm
File: type-string.h

  uint32_t capacity() const {
    return m_px->capacity(); // intentionally skip nullptr check
  }

Project: hhvm
File: ext_gd.cpp

static void exif_process_TIFF_in_JPEG(image_info_type *ImageInfo,
                                      char *CharBuf, size_t length,
                                      size_t displacement) {
  char *end = CharBuf + length;
  unsigned exif_value_2a, offset_of_ifd;

  /* set the thumbnail stuff to nothing so we can test to see if
     they get set up */
  CHECK_BUFFER(CharBuf, end, 2);
  if (memcmp(CharBuf, "II", 2) == 0) {
    ImageInfo->motorola_intel = 0;
  } else if (memcmp(CharBuf, "MM", 2) == 0) {
    ImageInfo->motorola_intel = 1;
  } else {
    raise_warning("Invalid TIFF a lignment marker");
    return;
  }

  /* Check the next two values for correctness. */
  CHECK_BUFFER(CharBuf+4, end, 4);
  exif_value_2a = php_ifd_get16u(CharBuf+2, ImageInfo->motorola_intel);
  offset_of_ifd = php_ifd_get32u(CharBuf+4, ImageInfo->motorola_intel);
  if ( exif_value_2a != 0x2a || offset_of_ifd < 0x08) {
    raise_warning("Invalid TIFF start (1)");
    return;
  }

  if (offset_of_ifd > length) {
    raise_warning("Invalid IFD start");
    return;
  }

  ImageInfo->sections_found |= FOUND_IFD0;
  /* First directory starts at offset 8. Offsets starts at 0. */
  exif_process_IFD_in_JPEG(ImageInfo, CharBuf+offset_of_ifd,
                           CharBuf, end, length/* -14*/, displacement,
                           SECTION_IFD0);

  /* Compute the CCD width, in milimeters. */
  if (ImageInfo->FocalplaneXRes != 0) {
    ImageInfo->CCDWidth = (float)(ImageInfo->ExifImageWidth *
      ImageInfo->FocalplaneUnits / ImageInfo->FocalplaneXRes);
  }
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(strncmp,
                         const String& str1,
                         const String& str2,
                         int len) {
  if (len < 0) {
    raise_warning("Length must be greater than or equal to 0");
    return make_tv<KindOfBoolean>(false);
  }
  return tvReturn(string_strncmp(
    str1.data(), str1.size(),
    str2.data(), str2.size(), len
  ));
}

Project: hhvm
File: preg.cpp

static void php_reg_eprint(int err, regex_t* re) {
  char *buf = nullptr, *message = nullptr;
  size_t len;
  size_t buf_len;

#ifdef REG_ITOA
  /* get the length of the message */
  buf_len = regerror(REG_ITOA | err, re, nullptr, 0);
  if (buf_len) {
    buf = (char *)req::malloc_noptrs(buf_len);
    if (!buf) return; /* fail silently */
    /* finally, get the error message */
    regerror(REG_ITOA | err, re, buf, buf_len);
  }
#else
  buf_len = 0;
#endif
  len = regerror(err, re, nullptr, 0);
  if (len) {
    message = (char *)req::malloc_noptrs(buf_len + len + 2);
    if (!message) {
      return; /* fail silently */
    }
    if (buf_len) {
      snprintf(message, buf_len, "%s: ", buf);
      buf_len += 1; /* so pointer math below works */
    }
    /* drop the message into place */
    regerror(err, re, message + buf_len, len);
    raise_warning("%s", message);
  }
  req::free(buf);
  req::free(message);
}

Project: hhvm
File: ext_hotprofiler.cpp

void end_profiler_frame(Profiler *p,
                        const TypedValue *retval,
                        const char *symbol) {
  p->endFrame(retval, symbol);
}

Project: hhvm
File: type-string.h

  static String attach(StringData* sd) {
    return String(sd, NoIncRef{});
  }

Project: hhvm
File: ext_string.cpp

int64_t HHVM_FUNCTION(crc32,
                      StringArg str) {
  return (uint32_t)string_crc32(str.get()->data(), str.get()->size());
}

Project: hhvm
File: ext_hotprofiler.cpp

bool Profiler::extractStats(phpret& ret, StatsMap& stats, int flags,
                            int64_t MHz)
{
    for (typename StatsMap::const_iterator iter = stats.begin();
         iter != stats.end(); ++iter) {
      returnVals(ret, iter->first, iter->second, flags, MHz);
    }
    return true;
}

Project: hhvm
File: ext_gd.cpp

php_image_filter_grayscale(gdImagePtr im, int /*arg1*/ /* = 0 */,
                           int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                           int /*arg4*/ /* = 0 */) {
  return gdImageGrayScale(im) == 1;
}

Project: hhvm
File: type-string.h

  String(const String& str) : m_str(str.m_str) { }

Project: hhvm
File: preg.cpp

    Accessor& operator=(EntryPtr&& ep) {
      switch (m_kind) {
        case Kind::AccessorKind:
          m_u.accessor.~ConstAccessor();
        case Kind::Empty:
        case Kind::Ptr:
          m_kind = Kind::SmartPtr;
          new (&m_u.smart_ptr) EntryPtr(std::move(ep));
          break;
        case Kind::SmartPtr:
          m_u.smart_ptr = std::move(ep);
          break;
      }
      return *this;
    }

Project: hhvm
File: ext_gd.cpp

static int exif_process_undefined(char **result, char *value,
                                  size_t byte_count) {
  /* we cannot use strlcpy - here the problem is that we have to copy NUL
   * chars up to byte_count, we also have to add a single NUL character to
   * force end of string.
   */
  if (byte_count) {
    PHP_STRNDUP((*result), value, byte_count); /* NULL @ byte_count!!! */
    if (*result) return byte_count+1;
  }
  return 0;
}

Project: hhvm
File: ext_string.cpp

Variant HHVM_FUNCTION(soundex,
                      const String& str) {
  if (str.empty()) return false;
  return string_soundex(str);
}

Project: hhvm
File: ext_gd.cpp

static void _php_image_output_ctxfree(struct gdIOCtx *ctx) {
  if (ctx) {
    IM_FREE(ctx);
  }
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(md5,
                     const String& str,
                     bool raw_output /* = false */) {
  return StringUtil::MD5(str, raw_output);
}

Project: hhvm
File: zend-string.cpp

static void ito64(char *s, long v, int n) {
  while (--n >= 0) {
    *s++ = itoa64[v&0x3f];
    v >>= 6;
  }
}

Project: hhvm
File: ext_sockets.cpp

bool HHVM_FUNCTION(socket_create_pair,
                   int domain,
                   int type,
                   int protocol,
                   Variant& fd) {
  return socket_create_pair_impl(domain, type, protocol, fd, false);
}

Project: hhvm
File: ext_std_variable.cpp

Variant HHVM_FUNCTION(HH_last_key, const Variant& v) {
  // 1. array, vec, dict, keyset
  if (v.isArray() || v.isClsMeth()) {
    auto arr = v.toArray();
    if (arr->empty()) {
      return init_null();
    }
    return arr->getKey(arr->iter_last());
  }

  if (v.isObject()) {
    auto obj = v.asCObjRef();
    // 2. collection
    if (obj->isCollection()) {
      // Pair
      if (obj->collectionType() == CollectionType::Pair) {
        return Variant::wrap(make_tv<KindOfInt64>(1));
      }

      // Vector, Map, Set, and Imm variants
      auto arr = collections::asArray(obj.get());
      if (arr->empty()) {
        return init_null();
      }
      return arr->getKey(arr->iter_last());
    }
  }
  SystemLib::throwInvalidArgumentExceptionObject(
    "Argument 1 passed to HH\\Lib\\_Private\\Native\\last_key() "
    "must be a Container");
}

Project: hhvm
File: type-string.h

  template<size_t N> explicit StaticString(const char(&s)[N]) {
    construct(s, N - 1);
  }

Project: hhvm
File: type-string.h

  StringData* detach() { return m_str.detach(); }

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagecopymergegray, const Resource& dst_im,
    const Resource& src_im,
    int64_t dst_x, int64_t dst_y,
    int64_t src_x, int64_t src_y,
    int64_t src_w, int64_t src_h, int64_t pct) {
  gdImagePtr im_src = cast<Image>(src_im)->get();
  if (!im_src) return false;
  gdImagePtr im_dst = cast<Image>(dst_im)->get();
  if (!im_dst) return false;
  gdImageCopyMergeGray(im_dst, im_src, dst_x, dst_y,
                       src_x, src_y, src_w, src_h, pct);
  return true;
}

Project: hhvm
File: type-string.h

  folly::MutableStringPiece reserve(size_t size) {
    if (!m_str) return folly::MutableStringPiece();
    auto const tmp = m_str->reserve(size);
    if (UNLIKELY(tmp != m_str)) {
      m_str = req::ptr<StringData>::attach(tmp);
    }
    return m_str->bufferSlice();
  }

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(str_shuffle,
                     const String& str) {
  if (str.size() <= 1) {
    return str;
  }

  String ret(str, CopyString);
  char* buf  = ret.get()->mutableData();
  int left   = ret.size();

  while (--left) {
    int idx = HHVM_FN(rand)(0, left);
    if (idx != left) {
      char temp = buf[left];
      buf[left] = buf[idx];
      buf[idx] = temp;
    }
  }
  return ret;
}

Project: hhvm
File: string-data-inl.h

inline strhash_t StringData::hash() const {
  strhash_t h = m_hash & STRHASH_MASK;
  return h ? h : hashHelper();
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagecopyresampled,
    const Resource& dst_im, const Resource& src_im,
    int64_t dst_x, int64_t dst_y, int64_t src_x, int64_t src_y,
    int64_t dst_w, int64_t dst_h, int64_t src_w, int64_t src_h) {
  gdImagePtr im_src = get_valid_image_resource(src_im);
  if (!im_src) return false;
  gdImagePtr im_dst = get_valid_image_resource(dst_im);
  if (!im_dst) return false;
  gdImageCopyResampled(im_dst, im_src, dst_x, dst_y, src_x, src_y,
                       dst_w, dst_h, src_w, src_h);
  return true;
}

Project: hhvm
File: ext_openssl.cpp

  static bool write_rand_file(const char *file, int egdsocket, int seeded) {
    char buffer[PATH_MAX];

    if (egdsocket || !seeded) {
      /* if we did not manage to read the seed file, we should not write
       * a low-entropy seed file back */
      return false;
    }
    if (file == nullptr) {
      file = RAND_file_name(buffer, sizeof(buffer));
    }
    if (file == nullptr || !RAND_write_file(file)) {
      raise_warning("unable to write random state");
      return false;
    }
    return true;
  }

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagettftext, const Resource& image,
    const Variant& size, const Variant& angle,
    int64_t x, int64_t y, int64_t color,
    const String& fontfile, const String& text) {
  return php_imagettftext_common(TTFTEXT_DRAW, 0,
                                 image, size.toDouble(), angle.toDouble(),
                                 x, y, color, fontfile, text);
}

Project: hhvm
File: ext_std_variable.cpp

bool HHVM_FUNCTION(is_null, const Variant& v) {
  return is_null(v.asTypedValue());
}

Project: hhvm
File: type-string.h

  StrNR() : m_px(nullptr) {}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_enc_get_block_size, const Resource& td) {
  auto pm = get_valid_mcrypt_resource(td);
  if (!pm) {
    return false;
  }

  return mcrypt_enc_get_block_size(pm->m_td);
}

Project: hhvm
File: ext_string.cpp

int64_t HHVM_FUNCTION(similar_text,
                      const String& first,
                      const String& second,
                      double& percent) {
  int ret = string_similar_text(first.data(), first.size(),
                                second.data(), second.size(), &percent);
  return ret;
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecolorclosest,
    const Resource& image, int64_t red, int64_t green, int64_t blue) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return gdImageColorClosest(im, red, green, blue);
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagecharup, const Resource& image,
    int64_t font, int64_t x, int64_t y,
    const String& c, int64_t color) {
  return php_imagechar(image, font, x, y, c, color, 1);
}

Project: hhvm
File: ext_openssl.cpp

  bool generatePrivateKey() {
    assertx(priv_key == nullptr);

    if (priv_key_bits < MIN_KEY_LENGTH) {
      raise_warning("private key length is too short; it needs to be "
                      "at least %d bits, not %d",
                      MIN_KEY_LENGTH, priv_key_bits);
      return false;
    }

    char *randfile = CONF_get_string(req_config, section_name, "RANDFILE");
    int egdsocket, seeded;
    load_rand_file(randfile, &egdsocket, &seeded);

    bool ret = false;
    if ((priv_key = EVP_PKEY_new()) != nullptr) {
      switch (priv_key_type) {
      case OPENSSL_KEYTYPE_RSA:
        if (EVP_PKEY_assign_RSA
            (priv_key, RSA_generate_key(priv_key_bits, 0x10001, nullptr, nullptr))) {
          ret = true;
        }
        break;
#if !defined(NO_DSA) && defined(HAVE_DSA_DEFAULT_METHOD)
      case OPENSSL_KEYTYPE_DSA:
        {
          DSA *dsapar = DSA_generate_parameters(priv_key_bits, nullptr, 0, nullptr,
                                                nullptr, nullptr, nullptr);
          if (dsapar) {
            DSA_set_method(dsapar, DSA_get_default_method());
            if (DSA_generate_key(dsapar)) {
              if (EVP_PKEY_assign_DSA(priv_key, dsapar)) {
                ret = true;
              }
            } else {
              DSA_free(dsapar);
            }
          }
        }
        break;
#endif
#ifdef HAVE_EVP_PKEY_EC
      case OPENSSL_KEYTYPE_EC:
        {
          if (curve_name == NID_undef) {
            raise_warning("Missing configuration value: 'curve_name' not set");
            return false;
          }
          if (auto const eckey = EC_KEY_new_by_curve_name(curve_name)) {
            EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
            if (EC_KEY_generate_key(eckey) &&
                EVP_PKEY_assign_EC_KEY(priv_key, eckey)) {
              ret = true;
            } else {
              EC_KEY_free(eckey);
            }
          }
        }
        break;
#endif
      default:
        raise_warning("Unsupported private key type");
      }
    }

    write_rand_file(randfile, egdsocket, seeded);
    return ret;
  }

Project: hhvm
File: ext_string.cpp

int64_t HHVM_FUNCTION(strnatcmp,
                      const String& str1,
                      const String& str2) {
  return string_natural_cmp(str1.data(), str1.size(), str2.data(), str2.size(),
                            false);
}

Project: hhvm
File: ext_string.cpp

int64_t HHVM_FUNCTION(strcasecmp,
                      const String& str1,
                      const String& str2) {
  return bstrcasecmp(str1.data(), str1.size(), str2.data(), str2.size());
}

Project: hhvm
File: preg.cpp

  PCRECache()
    : m_kind(CacheKind::Static), m_staticCache(nullptr)
  {
    reinit(CacheKind::Static);
  }

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imageantialias, const Resource& image, bool on) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  SetAntiAliased(im, on);
  return true;
}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_ofb, const String& cipher, const String& key,
                                  const String& data, const Variant& mode,
                                  const Variant& viv /* = null_string */) {
  raise_deprecated("Function mcrypt_ofb() is deprecated");
  String iv = viv.toString();
  return php_mcrypt_do_crypt(cipher, key, data, "ofb", iv, mode.toInt32(),
                             "mcrypt_ofb");
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(strrchr,
                         const String& haystack,
                         const Variant& needle) {
  if (haystack.size() == 0) {
    return make_tv<KindOfBoolean>(false);
  }

  int pos;
  if (needle.isString() && needle.toString().size() > 0) {
    pos = haystack.rfind(needle.toString().data()[0], false);
  } else {
    pos = haystack.rfind(needle.toByte(), false);
  }
  if (pos < 0) return make_tv<KindOfBoolean>(false);
  return tvReturn(haystack.substr(pos));
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(htmlspecialchars_decode,
                     const String& str,
                     int flags /* = k_ENT_HTML_QUOTE_DOUBLE */) {
  return StringUtil::HtmlDecode(str, StringUtil::toQuoteStyle(flags),
                                "UTF-8", false);
}

Project: hhvm
File: ext_gd.cpp

static struct gfxinfo *php_handle_jp2(const req::ptr<File>& stream) {
  struct gfxinfo *result = nullptr;
  unsigned int box_length;
  unsigned int box_type;
  char jp2c_box_id[] = {(char)0x6a, (char)0x70, (char)0x32, (char)0x63};

  /* JP2 is a wrapper format for JPEG 2000. Data is contained within "boxes".
     Boxes themselves can be contained within "super-boxes". Super-Boxes can
     contain super-boxes which provides us with a hierarchical storage system.

     It is valid for a JP2 file to contain multiple individual codestreams.
     We'll just look for the first codestream at the root of the box structure
     and handle that.
  */

  for (;;)
  {
    box_length = php_read4(stream); /* LBox */
    /* TBox */
    String str = stream->read(sizeof(box_type));
    if (str.length() != sizeof(box_type)) {
      /* Use this as a general "out of stream" error */
      break;
    }
    memcpy(&box_type, str.c_str(), sizeof(box_type));

    if (box_length == 1) {
      /* We won't handle XLBoxes */
      return nullptr;
    }

    if (!memcmp(&box_type, jp2c_box_id, 4))
    {
      /* Skip the first 3 bytes to emulate the file type examination */
      stream->seek(3, SEEK_CUR);

      result = php_handle_jpc(stream);
      break;
    }

    /* Stop if this was the last box */
    if ((int)box_length <= 0) {
      break;
    }

    /* Skip over LBox (Which includes both TBox and LBox itself */
    if (!stream->seek(box_length - 8, SEEK_CUR)) {
      break;
    }
  }

  if (result == nullptr) {
    raise_warning("JP2 file has no codestreams at root level");
  }

  return result;
}

Project: hhvm
File: ext_hotprofiler.cpp

  SampleProfiler() : Profiler(true) {
    struct timeval  now;
    uint64_t truncated_us;
    uint64_t truncated_tsc;

    // Init the last_sample in tsc
    m_last_sample_tsc = cpuCycles();

    // Find the microseconds that need to be truncated
    gettimeofday(&m_last_sample_time, 0);
    now = m_last_sample_time;
    hp_trunc_time(&m_last_sample_time, SAMPLING_INTERVAL);

    // Subtract truncated time from last_sample_tsc
    truncated_us  = get_us_interval(&m_last_sample_time, &now);
    truncated_tsc = truncated_us * m_MHz;
    if (m_last_sample_tsc > truncated_tsc) {
      // just to be safe while subtracting unsigned ints
      m_last_sample_tsc -= truncated_tsc;
    }

    // Convert sampling interval to ticks
    m_sampling_interval_tsc = SAMPLING_INTERVAL * m_MHz;
  }

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(htmlentities,
                     const String& str,
                     int flags /* = k_ENT_HTML_QUOTE_DOUBLE */,
                     const String& charset /* = "UTF-8" */,
                     bool double_encode /* = true */) {
  // dropping double_encode parameters and see runtime/base/zend-html.h
  const char *scharset = charset.data();
  if (!*scharset) scharset = "ISO-8859-1";
  return StringUtil::HtmlEncode(str, StringUtil::toQuoteStyleBitmask(flags),
                                scharset, double_encode, true);
}

Project: hhvm
File: string-data-inl.h

inline const void* StringData::payload() const { return this + 1; }

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imageloadfont, const String& /*file*/) {
  throw_not_supported(__func__, "NYI");
  // If you decide to implement this, be careful to avoid the crash from
  // https://github.com/php/php-src/commit/9a60aed6d1925c98b1b40c19b40f5b4b65baa
}

Project: hhvm
File: zend-printf.cpp

char * ap_php_conv_p2(register uint64_t num, register int nbits,
                      char format, char *buf_end, register int *len)
{
  register int mask = (1 << nbits) - 1;
  register char *p = buf_end;
  static char low_digits[] = "0123456789abcdef";
  static char upper_digits[] = "0123456789ABCDEF";
  register char *digits = (format == 'X') ? upper_digits : low_digits;

  do {
    *--p = digits[num & mask];
    num >>= nbits;
  }
  while (num);

  *len = buf_end - p;
  return (p);
}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_get_key_size, const String& cipher,
                                           const String& module) {
  MCRYPT td = mcrypt_module_open((char*)cipher.data(),
                                 (char*)MCG(algorithms_dir).data(),
                                 (char*)module.data(),
                                 (char*)MCG(modes_dir).data());
  if (td == MCRYPT_FAILED) {
    MCRYPT_OPEN_MODULE_FAILED("mcrypt_get_key_size");
    return false;
  }

  int64_t ret = mcrypt_enc_get_key_size(td);
  mcrypt_module_close(td);
  return ret;
}

Project: hhvm
File: preg.cpp

    ~Accessor() {
      switch (m_kind) {
        case Kind::Empty:
        case Kind::Ptr:
          break;
        case Kind::SmartPtr:
          m_u.smart_ptr.~EntryPtr();
          break;
        case Kind::AccessorKind:
          m_u.accessor.~ConstAccessor();
          break;
      }
    }

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(substr_count,
                         const String& haystack,
                         const String& needle,
                         int offset /* = 0 */,
                         int length /* = 0x7FFFFFFF */) {
  int lenNeedle = needle.size();
  if (lenNeedle == 0) {
    raise_invalid_argument_warning("needle: (empty)");
    return make_tv<KindOfBoolean>(false);
  }

  if (offset < 0 || offset > haystack.size()) {
    raise_invalid_argument_warning("offset: (out of range)");
    return make_tv<KindOfBoolean>(false);
  }
  if (length == 0x7FFFFFFF) {
    length = haystack.size() - offset;
  } else if (length <= 0 || length > haystack.size() - offset) {
    raise_invalid_argument_warning("length: (out of range)");
    return make_tv<KindOfBoolean>(false);
  }

  int count = 0;
  int posMax = offset + length - lenNeedle;
  for (int pos = haystack.find(needle, offset);
       pos != -1 && pos <= posMax;
       pos = haystack.find(needle, pos + lenNeedle)) {
    ++count;
  }
  return make_tv<KindOfInt64>(count);
}

Project: hhvm
File: ext_gd.cpp

static unsigned long int php_swf_get_bits(unsigned char* buffer,
                                          unsigned int pos,
                                          unsigned int count) {
  unsigned int loop;
  unsigned long int result = 0;

  for (loop = pos; loop < pos + count; loop++)
  {
    result = result +
      ((((buffer[loop / 8]) >> (7 - (loop % 8))) & 0x01) <<
      (count - (loop - pos) - 1));
  }
  return result;
}

Project: hhvm
File: ext_hotprofiler.cpp

  void collectStats(const char *symbol, bool isFuncExit, TraceEntry& te) {
    te.symbol = symbol;
    te.is_func_exit = isFuncExit;
    collectStats(te);
  }

Project: hhvm
File: ext_sockets.cpp

bool HHVM_FUNCTION(socket_bind,
                   const Resource& socket,
                   const String& address,
                   int port /* = 0 */) {
  auto sock = cast<Socket>(socket);

  sockaddr_storage sa_storage;
  struct sockaddr *sa_ptr;
  size_t sa_size;
  if (!set_sockaddr(sa_storage, sock, address, port, sa_ptr, sa_size)) {
    return false;
  }

  long retval = ::bind(sock->fd(), sa_ptr, sa_size);
  if (retval != 0) {
    std::string msg = "unable to bind address";
    msg += address.data();
    msg += ":";
    msg += folly::to<std::string>(port);
    SOCKET_ERROR(sock, msg.c_str(), errno);
    return false;
  }

  return true;
}

Project: hhvm
File: type-string.h

  String rval(double  key) const { return rvalImpl((int64_t)key);}

Project: hhvm
File: ext_gd.cpp

int php_iptc_next_marker(const req::ptr<File>& file,
                         int spool,
                         unsigned char **spoolbuf) {
  int c;

  /* skip unimportant stuff */

  c = php_iptc_get1(file, spool, spoolbuf);

  if (c == EOF) return M_EOI;

  while (c != 0xff) {
    if ((c = php_iptc_get1(file, spool, spoolbuf)) == EOF) {
      return M_EOI; /* we hit EOF */
    }
  }

  /* get marker byte, swallowing possible padding */
  do {
    c = php_iptc_get1(file, 0, 0);
    if (c == EOF)
      return M_EOI;       /* we hit EOF */
    else if (c == 0xff)
      php_iptc_put1(file, spool, (unsigned char)c, spoolbuf);
  } while (c == 0xff);

  return (unsigned int) c;
}

Project: hhvm
File: ext_gd.cpp

static int exif_process_string_raw(char **result, char *value,
                                   size_t byte_count) {
  /* we cannot use strlcpy - here the problem is that we have to copy NUL
   * chars up to byte_count, we also have to add a single NUL character to
   * force end of string.
   */
  *result = 0;
  if (byte_count) {
    (*result) = (char*)IM_MALLOC(byte_count + 1);
    CHECK_ALLOC_R((*result), byte_count + 1, 0);
    memcpy(*result, value, byte_count);
    (*result)[byte_count] = '\0';
    return byte_count+1;
  }
  return 0;
}

Project: hhvm
File: string-data-inl.h

  bool operator()(const StringData *s1, const StringData *s2) const {
    return bstrcasecmp(s1->data(), s1->size(), s2->data(), s2->size()) < 0;
  }

Project: hhvm
File: ext_hotprofiler.cpp

  ~MachineInfo() {
    delete[] m_cpu_frequencies;
  }

Project: hhvm
File: ext_string.cpp

  const std::string getRepl() const {
    return repl;
  }

Project: hhvm
File: preg.cpp

preg_return_pcre_error(int pcre_code, Variant&& return_value) {
  pcre_handle_exec_error(pcre_code);
  return std::move(return_value);
}

Project: hhvm
File: ext_string.cpp

String stringForEachBuffered(uint32_t bufLen, const String& str, Op action) {
  StringBuffer sb(bufLen);
  auto sl = str.slice();
  const char* src = sl.begin();
  const char* end = sl.end();

  for (; src < end; ++src) {
    action(sb, src, end);
  }

  return sb.detach();
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagefilltoborder, const Resource& image,
    int64_t x, int64_t y, int64_t border, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageFillToBorder(im, x, y, border, color);
  return true;
}

Project: hhvm
File: ext_gd.cpp

static unsigned short php_read4(const req::ptr<File>& stream) {
  unsigned char *a;
  String str = stream->read(4);
  /* just return 0 if we hit the end-of-file */
  if (str.length() != 4) return 0;
  a = (unsigned char *)str.c_str();
  return (((unsigned int)a[0]) << 24)
       + (((unsigned int)a[1]) << 16)
       + (((unsigned int)a[2]) <<  8)
       + (((unsigned int)a[3]));
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(str_pad,
                     const String& input,
                     int pad_length,
                     const String& pad_string /* = " " */,
                     int pad_type /* = k_STR_PAD_RIGHT */) {
  return StringUtil::Pad(input, pad_length, pad_string,
                         (StringUtil::PadType)pad_type);
}

Project: hhvm
File: zend-printf.cpp

char * php_conv_fp(register char format, register double num,
                   bool add_dp, int precision, char dec_point,
                   int *is_negative, char *buf, int *len) {
  register char *s = buf;
  register char *p, *p_orig;
  int decimal_point;

  if (precision >= NDIG - 1) {
    precision = NDIG - 2;
  }

  if (format == 'F') {
    p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
  } else { // either e or E format
    p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
  }

  // Check for Infinity and NaN
  if (isalpha((int)*p)) {
    *len = strlen(p);
    memcpy(buf, p, *len + 1);
    *is_negative = 0;
    free(p_orig);
    return (buf);
  }
  if (format == 'F') {
    if (decimal_point <= 0) {
      if (num != 0 || precision > 0) {
        *s++ = '0';
        if (precision > 0) {
          *s++ = dec_point;
          while (decimal_point++ < 0) {
            *s++ = '0';
          }
        } else if (add_dp) {
          *s++ = dec_point;
        }
      }
    } else {
      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
      decimal_point -= addz;
      while (decimal_point-- > 0) {
        *s++ = *p++;
      }
      while (addz-- > 0) {
        *s++ = '0';
      }
      if (precision > 0 || add_dp) {
        *s++ = dec_point;
      }
    }
  } else {
    *s++ = *p++;
    if (precision > 0 || add_dp) {
      *s++ = '.';
    }
  }

  // copy the rest of p, the NUL is NOT copied
  while (*p) {
    *s++ = *p++;
  }

  if (format != 'F') {
    char temp[EXPONENT_LENGTH]; // for exponent conversion
    int t_len;
    int exponent_is_negative;

    *s++ = format; // either e or E
    decimal_point--;
    if (decimal_point != 0) {
      p = ap_php_conv_10((int64_t) decimal_point, false,
                         &exponent_is_negative, &temp[EXPONENT_LENGTH],
                         &t_len);
      *s++ = exponent_is_negative ? '-' : '+';

      // Make sure the exponent has at least 2 digits
      while (t_len--) {
        *s++ = *p++;
      }
    } else {
      *s++ = '+';
      *s++ = '0';
    }
  }
  *len = s - buf;
  free(p_orig);
  return (buf);
}

Project: hhvm
File: ext_string.cpp

Variant HHVM_FUNCTION(count_chars,
                      const String& str,
                      int64_t mode /* = 0 */) {
  int chars[256];
  memset((void*)chars, 0, sizeof(chars));
  const unsigned char *buf = (const unsigned char *)str.data();
  for (int len = str.size(); len > 0; len--) {
    chars[*buf++]++;
  }

  Array retarr = Array::CreateDArray();
  char retstr[256];
  int retlen = 0;
  switch (mode) {
  case 0:
    for (int inx = 0; inx < 256; inx++) {
      retarr.set(inx, chars[inx]);
    }
    return retarr;
  case 1:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] != 0) {
        retarr.set(inx, chars[inx]);
      }
    }
    return retarr;
  case 2:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] == 0) {
        retarr.set(inx, chars[inx]);
      }
    }
    return retarr;
  case 3:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] != 0) {
        retstr[retlen++] = inx;
      }
    }
    return String(retstr, retlen, CopyString);
  case 4:
    for (int inx = 0; inx < 256; inx++) {
      if (chars[inx] == 0) {
        retstr[retlen++] = inx;
      }
    }
    return String(retstr, retlen, CopyString);
  }

  raise_invalid_argument_warning("mode: %" PRId64, mode);
  return false;
}

Project: hhvm
File: ext_hotprofiler.cpp

    CountedTraceData() : count(0)  { clear(); }

Project: hhvm
File: ext_gd.cpp

static int exif_file_sections_add(image_info_type *ImageInfo, int type,
                                  size_t size, unsigned char *data) {
  file_section *tmp;
  int count = ImageInfo->file.count;
  size_t realloc_size = (count+1) * sizeof(file_section);
  tmp = (file_section *)IM_REALLOC(ImageInfo->file.list, realloc_size);
  CHECK_ALLOC_R(tmp, realloc_size, -1);
  ImageInfo->file.list = tmp;
  ImageInfo->file.list[count].type = 0xFFFF;
  ImageInfo->file.list[count].data = nullptr;
  ImageInfo->file.list[count].size = 0;
  ImageInfo->file.count = count+1;
  if (!size) {
    data = nullptr;
  } else if (data == nullptr) {
    data = (unsigned char *)IM_MALLOC(size);
    if (data == nullptr) IM_FREE(tmp);
    CHECK_ALLOC_R(data, size, -1);
  }
  ImageInfo->file.list[count].type = type;
  ImageInfo->file.list[count].data = data;
  ImageInfo->file.list[count].size = size;
  return count;
}

Project: hhvm
File: type-string.h

  bool empty() const {
    return m_str ? m_str->empty() : true;
  }

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(getimagesizefromstring, const String& imagedata,
                      Array& imageinfo) {
  String data = "data://text/plain;base64,";
  data += StringUtil::Base64Encode(imagedata);
  if (auto stream = File::Open(data, "r")) {
    return getImageSize(stream, imageinfo);
  }
  return false;
}

Project: hhvm
File: type-string.h

  String rval(int     key) const { return rvalImpl(key);}

Project: hhvm
File: preg.cpp

PCREglobals::~PCREglobals() {
  pcre_jit_stack_free(jit_stack);
}

Project: hhvm
File: string-data-inl.h

  bool operator()(const StringData *s1, const StringData *s2) const {
    int len1 = s1->size();
    int len2 = s2->size();
    if (len1 < len2) {
      return (len1 == 0) || (memcmp(s1->data(), s2->data(), len1) <= 0);
    } else if (len1 == len2) {
      return (len1 != 0) && (memcmp(s1->data(), s2->data(), len1) < 0);
    } else /* len1 > len2 */ {
      return ((len2 != 0) && (memcmp(s1->data(), s2->data(), len2) < 0));
    }
  }

Project: hhvm
File: preg.cpp

  explicit FreeHelperImpl(void* p) : p(p) {}

Project: hhvm
File: ext_hotprofiler.cpp

  explicit HierarchicalProfiler(int flags) : Profiler(true), m_flags(flags) {
  }

Project: hhvm
File: ext_std_variable.cpp

bool HHVM_FUNCTION(HH_is_late_init_sprop_init,
                   const String& clsName,
                   const String& name) {
  auto const cls = Class::load(clsName.get());
  if (!cls) {
    SystemLib::throwInvalidArgumentExceptionObject(
      folly::sformat("Unknown class {}", clsName)
    );
  }
  auto const ctx = fromCaller(
    [] (const ActRec* fp, Offset) { return fp->func()->cls(); }
  );
  auto const lookup = cls->getSPropIgnoreLateInit(ctx, name.get());
  if (!lookup.val || !lookup.accessible) {
    SystemLib::throwInvalidArgumentExceptionObject(
      folly::sformat(
       "Unknown or inaccessible static property '{}' on class {}",
       name.get(),
       clsName.get()
      )
    );
  }
  return type(lookup.val) != KindOfUninit;
}

Project: hhvm
File: string-data-inl.h

inline StringData* StringData::Make(const StringData* s1,
                                    folly::StringPiece s2) {
  return Make(s1->slice(), s2);
}

Project: hhvm
File: preg.cpp

Variant preg_split(const String& pattern, const String& subject,
                   int limit /* = -1 */, int flags /* = 0 */) {
  PCRECache::Accessor accessor;
  if (!pcre_get_compiled_regex_cache(accessor, pattern.get())) {
    return preg_return_bad_regex_error(false);
  }
  const pcre_cache_entry* pce = accessor.get();

  int no_empty = flags & PREG_SPLIT_NO_EMPTY;
  bool delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE;
  bool offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE;

  if (limit == 0) {
    limit = -1;
  }

  int size_offsets = 0;
  int* offsets = create_offset_array(pce, size_offsets);
  SmartFreeHelper offsetsFreer(offsets);
  if (offsets == nullptr) {
    return preg_return_internal_error(false);
  }

  /* Start at the beginning of the string */
  int start_offset = 0;
  int next_offset = 0;
  const char* last_match = subject.data();
  pcre_extra extra;
  init_local_extra(&extra, pce->extra);

  const bool hackArrOutput = flags & PREG_FB_HACK_ARRAYS;

  // Get next piece if no limit or limit not yet reached and something matched
  Array result = hackArrOutput ? Array::CreateDict() : Array::CreateDArray();
  int g_notempty = 0;   /* If the match should not be empty */
  int utf8_check = 0;
  PCRECache::Accessor bump_accessor;
  const pcre_cache_entry* bump_pce = nullptr; /* instance for empty matches */
  while ((limit == -1 || limit > 1)) {
    int count = pcre_exec(pce->re, &extra, subject.data(), subject.size(),
                          start_offset, g_notempty | utf8_check,
                          offsets, size_offsets);

    /* Subsequent calls to pcre_exec don't need to bother with the
     * utf8 validity check: if the subject isn't valid, the first
     * call to pcre_exec will have failed, and as long as we only
     * set start_offset to known character boundaries we won't
     * supply an invalid offset. */
    utf8_check = PCRE_NO_UTF8_CHECK;

    /* Check for too many substrings condition. */
    if (count == 0) {
      raise_warning("Matched, but too many substrings");
      count = size_offsets / 3;
    }

    /* If something matched */
    if (count > 0 && offsets[1] >= offsets[0]) {
      if (!no_empty || subject.data() + offsets[0] != last_match) {
        auto const length = subject.data() + offsets[0] - last_match;
        auto const match = String(last_match, length, CopyString);
        auto const value = offset_capture
          ? Variant(str_offset_pair(match, next_offset, hackArrOutput))
          : Variant(match);
        assertx(result->isVectorData());
        result.set(safe_cast<int64_t>(result.size()), value);

        /* One less left to do */
        if (limit != -1) limit--;
      }

      last_match = subject.data() + offsets[1];
      next_offset = offsets[1];

      if (delim_capture) {
        int i, match_len;
        for (i = 1; i < count; i++) {
          match_len = offsets[(i<<1)+1] - offsets[i<<1];
          /* If we have matched a delimiter */
          if (!no_empty || match_len > 0) {
            auto const match = subject.substr(offsets[i<<1], match_len);
            auto const value = offset_capture
              ? Variant(str_offset_pair(match, offsets[i<<1], hackArrOutput))
              : Variant(match);
            assertx(result->isVectorData());
            result.set(safe_cast<int64_t>(result.size()), value);
          }
        }
      }
    } else if (count == PCRE_ERROR_NOMATCH) {
      /* If we previously set PCRE_NOTEMPTY after a null match,
         this is not necessarily the end. We need to advance
         the start offset, and continue. Fudge the offset values
         to achieve this, unless we're already at the end of the string. */
      if (g_notempty != 0 && start_offset < subject.size()) {
        if (pce->compile_options & PCRE_UTF8) {
          if (bump_pce == nullptr) {
            auto const DEBUG_ONLY ok = pcre_get_compiled_regex_cache(
              bump_accessor, s_OneUnicodeCharPattern.get());
            assertx(ok);
            bump_pce = bump_accessor.get();
          }
          pcre_extra bump_extra;
          init_local_extra(&bump_extra, bump_pce->extra);
          count = pcre_exec(bump_pce->re, &bump_extra, subject.data(),
                            subject.size(), start_offset,
                            utf8_check, offsets, size_offsets);
          if (count < 1) {
            raise_warning("Unknown error");
            offsets[0] = start_offset;
            offsets[1] = start_offset + 1;
            if (pcre_need_log_error(count)) {
              pcre_log_error(__FUNCTION__, __LINE__, count,
                             pattern.data(), pattern.size(),
                             subject.data(), subject.size(),
                             "", 0,
                             limit, flags, start_offset);
            }
          }
        } else {
          offsets[0] = start_offset;
          offsets[1] = start_offset + 1;
        }
      } else
        break;
    } else {
      if (pcre_need_log_error(count)) {
        pcre_log_error(__FUNCTION__, __LINE__, count,
                       pattern.data(), pattern.size(),
                       subject.data(), subject.size(),
                       "", 0,
                       limit, flags, start_offset, g_notempty);
      }
      // NOTE: this returns an error together with a partial result :-(
      start_offset = last_match - subject.data(); /* offset might have
                                                   * been incremented,
                                                   * but without further
                                                   * successful matches */
      if (!no_empty || start_offset < subject.size()) {
        auto const match = subject.substr(start_offset);
        auto const value = offset_capture
          ? Variant(str_offset_pair(match, start_offset, hackArrOutput))
          : Variant(match);
        assertx(result->isVectorData());
        result.set(safe_cast<int64_t>(result.size()), value);
      }
      return preg_return_pcre_error(count, std::move(result));
    }

    /* If we have matched an empty string, mimic what Perl's /g options does.
       This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try
       the match again at the same point. If this fails (picked up above) we
       advance to the next character. */
    g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0;

    /* Advance to the position right after the last full match */
    start_offset = offsets[1];
  }

  start_offset = last_match - subject.data(); /* offset might have
                                                * been incremented,
                                                * but without further
                                                * successful matches */
  if (!no_empty || start_offset < subject.size()) {
    auto const match = subject.substr(start_offset);
    auto const value = offset_capture
      ? Variant(str_offset_pair(match, start_offset, hackArrOutput))
      : Variant(match);
    assertx(result->isVectorData());
    result.set(safe_cast<int64_t>(result.size()), value);
  }

  return preg_return_no_error(std::move(result));
}

Project: hhvm
File: ext_hotprofiler.cpp

  ~TraceProfiler() override {
    if (m_successful) {
      free(m_traceBuffer);
      IniSetting::Unbind("profiler.max_trace_buffer");
      pthread_mutex_unlock(&s_inUse);
    }
  }

Project: hhvm
File: ext_string.cpp

  StringExtension() : Extension("string") {}

Project: hhvm
File: type-string.h

  void reset(StringData* str = nullptr) { m_str.reset(str); }

Project: hhvm
File: type-string.h

  StringData* get() const { return m_px; }

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagegd2, const Resource& image,
    const String& filename /* = null_string */,
    int64_t chunk_size /* = 0 */, int64_t type /* = 0 */) {
  return _php_image_output(image, filename, chunk_size, type,
                           PHP_GDIMG_TYPE_GD2, "GD2",
                           (void (*)())gdImageGd2);
}

Project: hhvm
File: ext_hotprofiler.cpp

Profiler::Profiler(bool needCPUAffinity) : m_successful(true),
                                           m_stack(nullptr),
                                           m_frame_free_list(nullptr),
                                           m_has_affinity(needCPUAffinity) {
    if (!s_rand_initialized) {
      s_rand_initialized = true;
      srand(math_generate_seed());
    }

    if (m_has_affinity) {
      //
      // Bind to a random cpu so that we can use rdtsc instruction.
      //
      int cur_cpu_id = rand() % s_machine.m_cpu_num;
      GET_AFFINITY(0, sizeof(cpu_set_t), &m_prev_mask);
      MachineInfo::BindToCPU(cur_cpu_id);
      m_MHz = s_machine.m_cpu_frequencies[cur_cpu_id];
    } else {
      //
      // Take cpu0's speed as a proxy for all cpus.
      //
      m_MHz = s_machine.m_cpu_frequencies[0];
    }

    memset(m_func_hash_counters, 0, sizeof(m_func_hash_counters));
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagesetinterpolation,
    const Resource& image, int64_t method /*=GD_BILINEAR_FIXED*/) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (method == -1) method = GD_BILINEAR_FIXED;
  return gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method);
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecolorexact, const Resource& image,
    int64_t red, int64_t green, int64_t blue) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  return gdImageColorExact(im, red, green, blue);
}

Project: hhvm
File: ext_hotprofiler.cpp

  void endFrameEx(const TypedValue* /*retval*/,
                  const char* /*given_symbol*/) override {
    char symbol[512];
    HierarchicalProfilerFrame *frame =
      dynamic_cast<HierarchicalProfilerFrame *>(m_stack);
    frame->getStack(2, symbol, sizeof(symbol));
    CountMap &counts = m_stats[symbol];
    counts.count++;
    counts.wall_time += cpuCycles() - frame->m_tsc_start;

    if (m_flags & TrackCPU) {
      counts.cpu += cpuTime(m_MHz) - frame->m_vtsc_start;
    }

    if (m_flags & TrackMemory) {
      auto const stats = tl_heap->getStats();
      int64_t mu_end = stats.usage();
      int64_t pmu_end = stats.peakUsage;
      counts.memory += mu_end - frame->m_mu_start;
      counts.peak_memory += pmu_end - frame->m_pmu_start;
    } else if (m_flags & TrackMalloc) {
      counts.memory += get_allocs() - frame->m_mu_start;
      counts.peak_memory += get_frees() - frame->m_pmu_start;
    }
  }

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecolorsforindex, const Resource& image,
                                           int64_t index) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (index >= 0 &&
      (gdImageTrueColor(im) || index < gdImageColorsTotal(im))) {
    return make_darray(
      s_red,  gdImageRed(im,index),
      s_green, gdImageGreen(im,index),
      s_blue, gdImageBlue(im,index),
      s_alpha, gdImageAlpha(im,index)
    );
  }
  raise_warning("Color index %" PRId64 " out of range", index);
  return false;
}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_enc_self_test, const Resource& td) {
  auto pm = get_valid_mcrypt_resource(td);
  if (!pm) {
    return false;
  }

  return mcrypt_enc_self_test(pm->m_td);
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagesavealpha, const Resource& image, bool saveflag) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageSaveAlpha(im, saveflag);
  return true;
}

Project: hhvm
File: type-string.h

  static bool HasConverted(int n) {
    return HasConverted((int64_t)n);
  }

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(strripos,
                         const String& haystack,
                         const Variant& needle,
                         int offset /* = 0 */) {
  if (!is_valid_strrpos_args(haystack, needle, offset)) {
    return make_tv<KindOfBoolean>(false);
  }
  int pos;
  if (needle.isString()) {
    pos = haystack.rfind(needle.toString(), offset, false);
  } else {
    pos = haystack.rfind(needle.toByte(), offset, false);
  }
  if (pos >= 0) return make_tv<KindOfInt64>(pos);
  return make_tv<KindOfBoolean>(false);
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagefill, const Resource& image,
    int64_t x, int64_t y, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  gdImageFill(im, x, y, color);
  return true;
}

Project: hhvm
File: ext_gd.cpp

static void exif_process_APP12(image_info_type *ImageInfo,
                               char *buffer, size_t length) {
  size_t l1, l2=0;
  if ((l1 = php_strnlen(buffer+2, length-2)) > 0) {
    exif_iif_add_tag(ImageInfo, SECTION_APP12, "Company",
                     TAG_NONE, TAG_FMT_STRING, l1, buffer+2);
    if (length > 2+l1+1) {
      l2 = php_strnlen(buffer+2+l1+1, length-2-l1-1);
      exif_iif_add_tag(ImageInfo, SECTION_APP12, "Info",
                       TAG_NONE, TAG_FMT_STRING, l2, buffer+2+l1+1);
    }
  }
}

Project: hhvm
File: ext_string.cpp

Array HHVM_FUNCTION(get_html_translation_table,
                    int table /* = 0 */,
                    int flags /* = k_ENT_HTML_QUOTE_DOUBLE */,
                    const String& encoding /* = "UTF-8" */) {
  using namespace entity_charset_enum;
  auto charset = determine_charset(encoding.data());
  if (charset == cs_unknown) {
    charset = cs_utf_8;
    if (!encoding.empty()) {
      raise_warning("get_html_translation_table(): charset `%s' not supported"
                    ", assuming utf-8", encoding.data());
    }
  }
  auto doctype = determine_doctype(flags);

  bool all = (table == k_HTML_ENTITIES);

  Array ret = Array::CreateDArray();
  switch (table) {
  case k_HTML_ENTITIES: {
    if (charset == cs_utf_8) {
      auto entity_map = get_doctype_entity_table(doctype);
      for (const auto& item : *entity_map) {
        auto key = encode_as_utf8(item.first);

        char buffer[32];
        snprintf(buffer, sizeof(buffer), "&%s;", item.second.c_str());
        ret.set(key, String(buffer, CopyString));
      }
      if (doctype == entity_doctype::html5) {
        for (const auto& item: *get_multicode_table()) {
          auto codes = item.first;
          String key = encode_as_utf8(codes.first);
          key += encode_as_utf8(codes.second);

          char buffer[32];
          snprintf(buffer, sizeof(buffer), "&%s", item.second.c_str());
          ret.set(key, String(buffer, CopyString));
        }
      }
    } else {
      const auto& entity_map = get_doctype_entity_table(doctype);
      auto charset_table = get_charset_table(charset);
      for (const auto& item : *charset_table) {
        const auto iter = entity_map->find(item.second);
        if (iter != entity_map->end()) {
          char buffer[16];
          snprintf(buffer, sizeof(buffer), "&%s;", iter->second.c_str());

          auto key = String::FromChar(item.first);
          ret.set(key, String(buffer, CopyString));
        }
      }
    }
    /* fall thru */
  }
  case k_HTML_SPECIALCHARS:
    const auto& basic_table = get_basic_table(all, doctype);
    for (int j = 0; basic_table[j].charcode != 0; j++) {
      const auto& item = basic_table[j];
      if (item.flags && (flags & item.flags) == 0)
        continue;

      ret.set(String::FromChar(item.charcode), item.entity);
    }
    ret.set(s_amp, s_ampsemi);
    break;
  }
  if (ret.empty()) {
    return Array();
  }

  return ret;
}

Project: hhvm
File: type-string.h

  const String& shrink(size_t len) {
    assertx(m_str && !m_str->isImmutable());
    if (m_str->capacity() - len > kMinShrinkThreshold) {
      m_str = req::ptr<StringData>::attach(m_str->shrinkImpl(len));
    } else {
      assertx(len < StringData::MaxSize);
      m_str->setSize(len);
    }
    return *this;
  }

Project: hhvm
File: type-string.h

ALWAYS_INLINE String empty_string() {
  return String::attach(staticEmptyString());
}

Project: hhvm
File: ext_string.cpp

Variant WuManberReplacement::translate(String source) const {
  size_t  pos      = 0,
          nextwpos = 0,
          lastpos  = source.size() - m;

  if (!valid) {
    return false;
  }

  // all patterns are longer than the source
  if (m > source.size()) {
    return source;
  }

  StringBuffer  result(source.size());
  while (pos <= lastpos) {
    uint16_t h = strtr_hash(source.data() + pos + m - B, B) & SHIFT_TAB_MASK;
    size_t shift_pos = shift[h];

    if (shift_pos > 0) {
      pos += shift_pos;
    } else {
      uint16_t  h2        = h & HASH_TAB_MASK,
                prefix_h  = strtr_hash(source.data() + pos, Bp);
      int offset_start  = hash[h2],
          offset_end    = hash[h2 + 1], // exclusive
          i             = 0;

      for (i = offset_start; i < offset_end; i++) {
        if (prefix[i] != prefix_h) {
          continue;
        }

        const PatAndRepl *pnr = &patterns[i];
        if (pnr->getPat().size() > source.size() - pos ||
            memcmp(pnr->getPat().data(), source.data() + pos,
                   pnr->getPat().size()) != 0) {
          continue;
        }

        result.append(source.data() + nextwpos, pos - nextwpos);
        result.append(pnr->getRepl());
        pos += pnr->getPat().size();
        nextwpos = pos;
        goto end_outer_loop;
      }

      pos++;
end_outer_loop: ;
    }
  }

  result.append(source.data() + nextwpos, source.size() - nextwpos );

  return result.detach();
}

Project: hhvm
File: ext_strobelight.cpp

bool Strobelight::isXenonActive() {
  if (RuntimeOption::XenonForceAlwaysOn) {
    return true;
  }

  bool xenonProfiled = Xenon::getInstance().getIsProfiledRequest();
  if (xenonProfiled) {
    return true;
  }

  return false;
}

Project: hhvm
File: ext_mcrypt.cpp

int64_t HHVM_FUNCTION(mcrypt_module_get_algo_key_size, const String& algorithm,
                                   const String& lib_dir /* = null_string */) {
  String dir = lib_dir.empty() ? String(MCG(algorithms_dir)) : lib_dir;
  return mcrypt_module_get_algo_key_size((char*)algorithm.data(),
                                         (char*)dir.data());
}

Project: hhvm
File: ext_gd.cpp

void Image::reset() {
  if (m_gdImage) {
    gdImageDestroy(m_gdImage);
    m_gdImage = nullptr;
  }
}

Project: hhvm
File: ext_strobelight.cpp

bool logToUSDT(const Array& bt) {
  std::lock_guard<std::mutex> lock(usdt_mutex);

  memset(&bt_slab, 0, sizeof(bt_slab));

  int i = 0;
  IterateVNoInc(
    bt.get(),
    [&](TypedValue tv) -> bool {

      if (i >= strobelight::kMaxStackframes) {
        return true;
      }

      assertx(isArrayLikeType(type(tv)));
      ArrayData* bt_frame = val(tv).parr;
      strobelight::backtrace_frame_t* frame = &bt_slab.frames[i];

      auto const line = bt_frame->get(s_line.get());
      if (line.is_init()) {
        assertx(isIntType(type(line)));
        frame->line = val(line).num;
      }

      auto const file_name = bt_frame->get(s_file.get());
      if (file_name.is_init()) {
        assertx(isStringType(type(file_name)));
        strncpy(frame->file_name,
                val(file_name).pstr->data(),
                std::min<int64_t>(
                  val(file_name).pstr->size(),
                  strobelight::kFileNameMax
                ));
        frame->file_name[strobelight::kFileNameMax - 1] = '\0';
      }

      auto const class_name = bt_frame->get(s_class.get());
      if (class_name.is_init()) {
        assertx(isStringType(type(class_name)));
        strncpy(frame->class_name,
                val(class_name).pstr->data(),
                std::min<int64_t>(
                  val(class_name).pstr->size(),
                  strobelight::kClassNameMax
                ));
        frame->class_name[strobelight::kClassNameMax - 1] = '\0';
      }

      auto const function_name = bt_frame->get(s_function.get());
      if (function_name.is_init()) {
        assertx(isStringType(type(function_name)));
        strncpy(frame->function,
                val(function_name).pstr->data(),
                std::min<int64_t>(
                  val(function_name).pstr->size(),
                  strobelight::kFunctionMax
                ));
        frame->function[strobelight::kFunctionMax - 1] = '\0';
      }

      i++;
      return false;
    }
  );
  bt_slab.len = i;

  // Allow BPF to read the now-formatted stacktrace
  FOLLY_SDT_WITH_SEMAPHORE(hhvm, hhvm_stack, &bt_slab);

  return true;
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(stristr,
                         const String& haystack,
                         const Variant& needle,
                         bool before_needle /* = false */) {
  auto const tv = HHVM_FN(stripos)(haystack, needle);
  auto const& ret = tvAsCVarRef(&tv);
  assertx(!isRefcountedType(tv.m_type));

  if (same(ret, false)) {
    return make_tv<KindOfBoolean>(false);
  }
  if (before_needle) {
    return tvReturn(haystack.substr(0, ret.toInt32()));
  }
  return tvReturn(haystack.substr(ret.toInt32()));
}

Project: hhvm
File: mem-file.cpp

int64_t MemFile::writeImpl(const char* /*buffer*/, int64_t /*length*/) {
  raise_fatal_error((std::string("cannot write a mem stream: ") +
                             getName()).c_str());
}

Project: hhvm
File: ext_string.cpp

Variant HHVM_FUNCTION(convert_uudecode,
                      const String& data) {
  String ret = StringUtil::UUDecode(data);
  if (ret.isNull()) {
    raise_warning(
      "convert_uudecode(): "
      "The given parameter is not a valid uuencoded string");
    return false; // bad format
  }
  return ret;
}

Project: hhvm
File: ext_std_variable.cpp

String HHVM_FUNCTION(get_resource_type, const Resource& handle) {
  return handle->o_getResourceName();
}

Project: hhvm
File: ext_gd.cpp

static char * exif_get_tagname(int tag_num, char *ret, int len,
                               tag_table_type tag_table) {
  int i, t;
  char tmp[32];

  for (i = 0; (t = tag_table[i].Tag) != TAG_END_OF_LIST; i++) {
    if (t == tag_num) {
      if (ret && len)  {
        string_copy(ret, tag_table[i].Desc, abs(len));
        if (len < 0) {
          memset(ret + strlen(ret), ' ', -len - strlen(ret) - 1);
          ret[-len - 1] = '\0';
        }
        return ret;
      }
      return tag_table[i].Desc;
    }
  }

  if (ret && len) {
    snprintf(tmp, sizeof(tmp), "UndefinedTag:0x%04X", tag_num);
    string_copy(ret, tmp, abs(len));
    if (len < 0) {
      memset(ret + strlen(ret), ' ', -len - strlen(ret) - 1);
      ret[-len - 1] = '\0';
    }
    return ret;
  }
  return "";
}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_get_block_size, const String& cipher,
                                             const String& mode) {
  MCRYPT td = mcrypt_module_open((char*)cipher.data(),
                                 (char*)MCG(algorithms_dir).data(),
                                 (char*)mode.data(),
                                 (char*)MCG(modes_dir).data());
  if (td == MCRYPT_FAILED) {
    MCRYPT_OPEN_MODULE_FAILED("mcrypt_get_block_size");
    return false;
  }

  int64_t ret = mcrypt_enc_get_block_size(td);
  mcrypt_module_close(td);
  return ret;
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagecopyresized, const Resource& dst_im,
    const Resource& src_im,
    int64_t dst_x, int64_t dst_y, int64_t src_x, int64_t src_y,
    int64_t dst_w, int64_t dst_h, int64_t src_w, int64_t src_h) {
  gdImagePtr im_src = cast<Image>(src_im)->get();
  if (!im_src) return false;
  gdImagePtr im_dst = cast<Image>(dst_im)->get();
  if (!im_dst) return false;
  if (dst_w <= 0 || dst_h <= 0 || src_w <= 0 || src_h <= 0) {
    raise_warning("Invalid image dimensions");
    return false;
  }
  gdImageCopyResized(im_dst, im_src,
                     dst_x, dst_y, src_x, src_y,
                     dst_w, dst_h, src_w, src_h);
  return true;
}

Project: hhvm
File: type-string.h

  double toDouble () const { return m_str ? m_str->toDouble () : 0;}

Project: hhvm
File: ext_gd.cpp

int php_get_wbmp(const req::ptr<File>& file,
                 struct gfxinfo **result,
                 int check) {
  int i, width = 0, height = 0;

  if (!file->rewind()) {
    return 0;
  }

  /* get type */
  if (file->getc() != 0) {
    return 0;
  }

  /* skip header */
  do {
    i = file->getc();
    if (i < 0) {
      return 0;
    }
  } while (i & 0x80);

  /* get width */
  do {
    i = file->getc();
    if (i < 0) {
      return 0;
    }
    width = (width << 7) | (i & 0x7f);
  } while (i & 0x80);

  /* get height */
  do {
    i = file->getc();
    if (i < 0) {
      return 0;
    }
    height = (height << 7) | (i & 0x7f);
  } while (i & 0x80);

  // maximum valid sizes for wbmp (although 127x127 may be a
  // more accurate one)
  if (!height || !width || height > 2048 || width > 2048) {
    return 0;
  }

  if (!check) {
    (*result)->width = width;
    (*result)->height = height;
  }

  return IMAGE_FILETYPE_WBMP;
}

Project: hhvm
File: ext_mcrypt.cpp

Variant HHVM_FUNCTION(mcrypt_ecb, const String& cipher, const String& key,
                                  const String& data, const Variant& mode,
                                  const Variant& viv /* = null_string */) {
  raise_deprecated("Function mcrypt_ecb() is deprecated");
  String iv = viv.toString();
  return php_mcrypt_do_crypt(cipher, key, data, "ecb", iv, mode.toInt32(),
                             "mcrypt_ecb");
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(str_replace_with_count,
                         const Variant& search, const Variant& replace,
                         const Variant& subject, int64_t& count) {
  return tvReturn(str_replace(search, replace, subject, count));
}

Project: hhvm
File: ext_sockets.cpp

bool HHVM_FUNCTION(socket_getpeername,
                   const Resource& socket,
                   Variant& address,
                   Variant& port) {
  auto sock = cast<Socket>(socket);

  sockaddr_storage sa_storage;
  socklen_t salen = sizeof(sockaddr_storage);
  struct sockaddr *sa = (struct sockaddr *)&sa_storage;
  if (getpeername(sock->fd(), sa, &salen) < 0) {
    SOCKET_ERROR(sock, "unable to retrieve peer name", errno);
    return false;
  }
  Variant a, p;
  auto ret = get_sockaddr(sa, salen, a, p);
  address = a;
  port = p;
  return ret;
}

Project: hhvm
File: preg.cpp

Variant preg_match_all(const StringData* pattern, const StringData* subject,
                       Variant* matches /* = nullptr */,
                       int flags /* = 0 */, int offset /* = 0 */) {
  return preg_match_impl(pattern, subject, matches, flags, offset, true);
}

Project: hhvm
File: ext_openssl.cpp

  bool isPrivate() {
    assertx(m_key);
    switch (EVP_PKEY_id(m_key)) {
#ifndef NO_RSA
    case EVP_PKEY_RSA:
    case EVP_PKEY_RSA2:
      {
        const auto rsa = EVP_PKEY_get0_RSA(m_key);
        assertx(rsa);
        const BIGNUM *p, *q;
        RSA_get0_factors(rsa, &p, &q);
        if (!p || !q) {
          return false;
        }
        break;
      }
#endif
#ifndef NO_DSA
    case EVP_PKEY_DSA:
    case EVP_PKEY_DSA1:
    case EVP_PKEY_DSA2:
    case EVP_PKEY_DSA3:
    case EVP_PKEY_DSA4:
      {
        const auto dsa = EVP_PKEY_get0_DSA(m_key);
        assertx(dsa);
        const BIGNUM *p, *q, *g, *pub_key, *priv_key;
        DSA_get0_pqg(dsa, &p, &q, &g);
        if (!p || !q || !g) {
          return false;
        }
        DSA_get0_key(dsa, &pub_key, &priv_key);
        if (!priv_key) {
          return false;
        }
        break;
      }
#endif
#ifndef NO_DH
    case EVP_PKEY_DH:
      {
        const auto dh = EVP_PKEY_get0_DH(m_key);
        assertx(dh);
        const BIGNUM *p, *q, *g, *pub_key, *priv_key;
        DH_get0_pqg(dh, &p, &q, &g);
        if (!p) {
          return false;
        }
        DH_get0_key(dh, &pub_key, &priv_key);
        if (!priv_key) {
          return false;
        }
        break;
       }
#endif
#ifdef HAVE_EVP_PKEY_EC
    case EVP_PKEY_EC:
      {
        const auto ec_key = EVP_PKEY_get0_EC_KEY(m_key);
        assertx(ec_key);
        if (EC_KEY_get0_private_key(ec_key) == nullptr) {
          return false;
        }
        break;
      }
#endif
    default:
      raise_warning("key type not supported in this PHP build!");
      break;
    }
    return true;
  }

Project: hhvm
File: type-string.h

  explicit FormatValue(const HPHP::String& str) : m_val(str) {}

Project: hhvm
File: string-data-inl.h

inline char* StringData::mutableData() const {
  assertx(!isImmutable());
  return const_cast<char*>(data());
}

Project: hhvm
File: ext_string.cpp

bool WuManberReplacement::initPatterns(const Array& arr) {
  patterns.reserve(arr.size());
  for (ArrayIter iter(arr); iter; ++iter) {
    String pattern = iter.first().toString();
    if (pattern.size() == 0) { // empty string given as pattern
      patterns.clear();
      return false;
    }
    patterns.emplace_back(pattern, iter.second().toString());
  }

  initTables();

  return true;
}

Project: hhvm
File: ext_gd.cpp

static struct gfxinfo *php_handle_tiff(const req::ptr<File>& stream,
                                       int motorola_intel) {
  struct gfxinfo *result = nullptr;
  int i, num_entries;
  unsigned char *dir_entry;
  size_t dir_size, entry_value, width=0, height=0, ifd_addr;
  int entry_tag , entry_type;

  String ifd_ptr = stream->read(4);
  if (ifd_ptr.length() != 4) return nullptr;
  ifd_addr = php_ifd_get32u((void*)ifd_ptr.c_str(), motorola_intel);
  if (!stream->seek(ifd_addr-8, SEEK_CUR)) return nullptr;
  String ifd_data = stream->read(2);
  if (ifd_data.length() != 2) return nullptr;
  num_entries = php_ifd_get16u((void*)ifd_data.c_str(), motorola_intel);
  dir_size = 2/*num dir entries*/ +12/*length of entry*/*
             num_entries +
             4/* offset to next ifd (points to thumbnail or NULL)*/;
  String ifd_data2 = stream->read(dir_size-2);
  if ((size_t)ifd_data2.length() != dir_size-2) return nullptr;
  ifd_data += ifd_data2;
  /* now we have the directory we can look how long it should be */
  for(i=0;i<num_entries;i++) {
    dir_entry = (unsigned char*)ifd_data.c_str()+2+i*12;
    entry_tag = php_ifd_get16u(dir_entry+0, motorola_intel);
    entry_type = php_ifd_get16u(dir_entry+2, motorola_intel);
    switch(entry_type) {
      case TAG_FMT_BYTE:
      case TAG_FMT_SBYTE:
        entry_value = (size_t)(dir_entry[8]);
        break;
      case TAG_FMT_USHORT:
        entry_value = php_ifd_get16u(dir_entry+8, motorola_intel);
        break;
      case TAG_FMT_SSHORT:
        entry_value = php_ifd_get16s(dir_entry+8, motorola_intel);
        break;
      case TAG_FMT_ULONG:
        entry_value = php_ifd_get32u(dir_entry+8, motorola_intel);
        break;
      case TAG_FMT_SLONG:
        entry_value = php_ifd_get32s(dir_entry+8, motorola_intel);
        break;
      default:
        continue;
    }
    switch(entry_tag) {
      case TAG_IMAGEWIDTH:
      case TAG_COMP_IMAGEWIDTH:
        width = entry_value;
        break;
      case TAG_IMAGEHEIGHT:
      case TAG_COMP_IMAGEHEIGHT:
        height = entry_value;
        break;
    }
  }
  if ( width && height) {
    /* not the same when in for-loop */
    result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
    CHECK_ALLOC_R(result, sizeof (struct gfxinfo), nullptr);
    result->height = height;
    result->width = width;
    result->bits = 0;
    result->channels = 0;
    return result;
  }
  return nullptr;
}

Project: hhvm
File: ext_gd.cpp

static int php_vspprintf(char **pbuf, size_t max_len,
                         const char *fmt, ...) {
  va_list arglist;
  char *buf;
  va_start(arglist, fmt);
  int len = vspprintf_ap(&buf, max_len, fmt, arglist);
  if (buf) {
#ifdef IM_MEMORY_CHECK
    *pbuf = php_strndup_impl(buf, len, __LINE__);
#else
    *pbuf = php_strndup_impl(buf, len);
#endif
    free(buf);
  }
  va_end(arglist);
  return len;
}

Project: hhvm
File: ext_std_variable.cpp

bool HHVM_FUNCTION(HH_is_vec_or_varray, const Variant& val) {
  return is_vec_or_varray(val.asTypedValue());
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(quotemeta,
                     const String& str) {
  if (str.empty()) {
    return str;
  }

  return stringForEachBuffered(
    str.size(), str,
    [&](StringBuffer& ret, const char* src, const char* /*end*/) {
      switch (*src) {
        case '.':
        case '\\':
        case '+':
        case '*':
        case '?':
        case '[':
        case ']':
        case '^':
        case '$':
        case '(':
        case ')':
          ret.append('\\');
        /* fall through */
        default:
          ret.append(*src);
      }
    });
}

Project: hhvm
File: preg.cpp

static inline bool pcre_need_log_error(int pcre_code) {
  return RuntimeOption::EnablePregErrorLog &&
         (pcre_code == PCRE_ERROR_MATCHLIMIT ||
          pcre_code == PCRE_ERROR_RECURSIONLIMIT);
}

Project: hhvm
File: ext_string.cpp

Array HHVM_FUNCTION(str_getcsv,
                    const String& str,
                    const String& delimiter /* = "," */,
                    const String& enclosure /* = "\"" */,
                    const String& escape /* = "\\" */) {
  if (str.empty()) {
    return make_varray(uninit_variant);
  }

  auto check_arg = [](const String& arg, char default_arg) {
    return arg.size() > 0 ? arg[0] : default_arg;
  };

  char delimiter_char = check_arg(delimiter, ',');
  char enclosure_char = check_arg(enclosure, '"');
  char escape_char = check_arg(escape, '\\');

  auto dummy = req::make<PlainFile>();
  return dummy->readCSV(0, delimiter_char, enclosure_char, escape_char, &str);
}

Project: hhvm
File: ext_gd.cpp

inline int php_iptc_read_remaining(const req::ptr<File>& file,
                                   int spool,
                                   unsigned char **spoolbuf) {
  while (php_iptc_get1(file, spool, spoolbuf) != EOF) continue;

  return M_EOI;
}

Project: hhvm
File: ext_string.cpp

bool strtr_slow(const Array& arr, StringBuffer& result, String& key,
                const char*s, int& pos, int minlen, int maxlen) {

  memcpy(key.mutableData(), s + pos, maxlen);
  for (int len = maxlen; len >= minlen; len--) {
    key.setSize(len);
    auto const tv = arr->get(key);
    if (tv.is_init()) {
      String replace = tvCastToString(tv);
      if (!replace.empty()) {
        result.append(replace);
      }
      pos += len;
      return true;
    }
  }
  return false;
}

Project: hhvm
File: ext_gd.cpp

bool HHVM_FUNCTION(imagearc, const Resource& image,
    int64_t cx, int64_t cy, int64_t width, int64_t height,
    int64_t start, int64_t end, int64_t color) {
  gdImagePtr im = get_valid_image_resource(image);
  if (!im) return false;
  if (end < 0) end %= 360;
  if (start < 0) start %= 360;
  color = SetupAntiAliasedColor(im, color);
  gdImageArc(im, cx, cy, width, height, start, end, color);
  return true;
}

Project: hhvm
File: type-string.h

  ~StrNR() {
    if (debug) {
      m_px = reinterpret_cast<StringData*>(0xdeadbeeffaceb004);
    }
  }

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecreatefromgd2, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_GD2, "GD2",
                           (gdImagePtr(*)())gdImageCreateFromGd2,
                           (gdImagePtr(*)())gdImageCreateFromGd2Ctx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}

Project: hhvm
File: ext_gd.cpp

static gdImagePtr _php_image_create_from(const String& filename,
                                         int srcX, int srcY,
                                         int width, int height,
                                         int image_type, char *tn,
                                         gdImagePtr(*func_p)(),
                                         gdImagePtr(*ioctx_func_p)()) {
  VMRegAnchor _;
  gdImagePtr im = nullptr;
#ifdef HAVE_GD_JPG
  // long ignore_warning;
#endif

  if (image_type == PHP_GDIMG_TYPE_GD2PART) {
    if (width < 1 || height < 1) {
      raise_warning("Zero width or height not allowed");
      return nullptr;
    }
  }
  auto file = File::Open(filename, "rb");
  if (!file) {
    raise_warning("failed to open stream: %s", filename.c_str());
    return nullptr;
  }

  FILE *fp = nullptr;
  auto plain_file = dyn_cast<PlainFile>(file);
  if (plain_file) {
    fp = plain_file->getStream();
  } else  if (ioctx_func_p) {
    /* we can create an io context */
    gdIOCtx* io_ctx;

    // copy all
    String buff = file->read(8192);
    String str;
    do {
      str = file->read(8192);
      buff += str;
    } while (!str.empty());

    if (buff.empty()) {
      raise_warning("Cannot read image data");
      return nullptr;
    }

    io_ctx = gdNewDynamicCtxEx(buff.length(), (char *)buff.c_str(), 0);
    if (!io_ctx) {
      raise_warning("Cannot allocate GD IO context");
      return nullptr;
    }

    if (image_type == PHP_GDIMG_TYPE_GD2PART) {
      im =
        ((gdImagePtr(*)(gdIOCtx *, int, int, int, int))(ioctx_func_p))
          (io_ctx, srcX, srcY, width, height);
    } else {
      im = ((gdImagePtr(*)(gdIOCtx *))(ioctx_func_p))(io_ctx);
    }
    io_ctx->gd_free(io_ctx);
  }
  else {
    /* TODO: try and force the stream to be FILE* */
    assertx(false);
  }

  if (!im && fp) {
    switch (image_type) {
    case PHP_GDIMG_TYPE_GD2PART:
      im = ((gdImagePtr(*)(FILE *, int, int, int, int))(func_p))
             (fp, srcX, srcY, width, height);
      break;
#if defined(HAVE_GD_XPM) && defined(HAVE_GD_BUNDLED)
    case PHP_GDIMG_TYPE_XPM:
      im = gdImageCreateFromXpm(filename);
      break;
#endif

#ifdef HAVE_GD_JPG
    case PHP_GDIMG_TYPE_JPG:
      im = gdImageCreateFromJpeg(fp);
      break;
#endif

    default:
      im = ((gdImagePtr(*)(FILE*))(func_p))(fp);
      break;
    }

    fflush(fp);
  }

  if (im) {
    file->close();
    return im;
  }

  raise_warning("'%s' is not a valid %s file", filename.c_str(), tn);
  file->close();
  return nullptr;
}

Project: hhvm
File: preg.cpp

static pcre_jit_stack* alloc_jit_stack(void* /*data*/) {
  return tl_pcre_globals->jit_stack;
}

Project: hhvm
File: ext_hotprofiler.cpp

bool ProfilerFactory::start(ProfilerKind kind,
                            long flags,
                            bool beginFrame /* = true */) {
  if (m_profiler != nullptr) {
    return false;
  }

  switch (kind) {
  case ProfilerKind::Hierarchical:
    m_profiler = req::make_raw<HierarchicalProfiler>(flags);
    break;
  case ProfilerKind::Sample:
    m_profiler = req::make_raw<SampleProfiler>();
    break;
  case ProfilerKind::Trace:
    m_profiler = req::make_raw<TraceProfiler>(flags);
    break;
  case ProfilerKind::Memo:
    m_profiler = req::make_raw<MemoProfiler>(flags);
    break;
  case ProfilerKind::External:
    if (g_system_profiler) {
      m_profiler = g_system_profiler->getHotProfiler();
    } else if (m_external_profiler) {
      m_profiler = m_external_profiler;
    } else {
      raise_invalid_argument_warning(
        "ProfilerFactory::setExternalProfiler() not yet called");
      return false;
    }
    break;
  default:
    raise_invalid_argument_warning("level: %d", static_cast<int>(kind));
    return false;
  }
  if (m_profiler && m_profiler->m_successful) {
    // This will be disabled automatically when the thread completes the request
    HPHP::EventHook::Enable();
    RequestInfo::s_requestInfo->m_profiler = m_profiler;
    if (beginFrame) {
      m_profiler->beginFrame("main()");
    }
    return true;
  }
  req::destroy_raw(m_profiler);
  m_profiler = nullptr;
  return false;
}

Project: hhvm
File: ext_hotprofiler.cpp

  void popFrame(TraceIterator tIt, Stats& stats) {
    Frame callee = m_stack.back();
    m_stack.pop_back();
    Frame& caller = m_stack.back();
    char *cp = m_arcBuff;
    memcpy(cp, caller.trace->symbol, caller.len);
    cp += caller.len;
    if (caller.level >= 1) {
      std::pair<char*, int>& lvl = m_recursion[caller.level];
      memcpy(cp, lvl.first, lvl.second);
      cp += lvl.second;
    }
    memcpy(cp, HP_STACK_DELIM, HP_STACK_DELIM_LEN);
    cp += HP_STACK_DELIM_LEN;
    memcpy(cp, callee.trace->symbol, callee.len);
    cp += callee.len;
    if (callee.level >= 1) {
      std::pair<char*, int>& lvl = m_recursion[callee.level];
      memcpy(cp, lvl.first, lvl.second);
      cp += lvl.second;
    }
    *cp = 0;
    incStats(m_arcBuff, tIt, callee, stats);
  }

Project: hhvm
File: ext_string.cpp

  PatAndRepl(const String& pat, const String& repl)
  : pat(pat.data(), pat.size()), repl(repl.data(), repl.size()) { }

Project: hhvm
File: ext_gd.cpp

static int exif_process_IFD_in_JPEG(image_info_type *ImageInfo,
                                    char *dir_start, char *offset_base,
                                    char *end,
                                    size_t IFDlength, size_t displacement,
                                    int section_index) {
  int de;
  int NumDirEntries;
  int NextDirOffset;

  ImageInfo->sections_found |= FOUND_IFD0;

  CHECK_BUFFER_R(dir_start, end, 2, 0);
  NumDirEntries = php_ifd_get16u(dir_start, ImageInfo->motorola_intel);

  if ((dir_start+2+NumDirEntries*12) > (offset_base+IFDlength)) {
    raise_warning("Illegal IFD size: x%04X + 2 + x%04X*12 = x%04X > x%04lX",
                    (int)((size_t)dir_start+2-(size_t)offset_base),
                    NumDirEntries,
                   (int)((size_t)dir_start+2+
                   NumDirEntries*12-(size_t)offset_base), IFDlength);
    return 0;
  }

  for (de=0;de<NumDirEntries;de++) {
    if (!exif_process_IFD_TAG(ImageInfo, dir_start + 2 + 12 * de,
                              offset_base, end, IFDlength, displacement,
                              section_index, 1,
                              exif_get_tag_table(section_index))) {
      return 0;
    }
  }
  /*
   * Ignore IFD2 if it purportedly exists
   */
  if (section_index == SECTION_THUMBNAIL) {
    return true;
  }
  /*
   * Hack to make it process IDF1 I hope
   * There are 2 IDFs, the second one holds the keys (0x0201 and 0x0202)
   * to the thumbnail
   */
  CHECK_BUFFER_R(dir_start+2+12*de, end, 4, 0);
  NextDirOffset =
    php_ifd_get32u(dir_start+2+12*de, ImageInfo->motorola_intel);
  if (NextDirOffset) {
    /* the next line seems false but here IFDlength means
       length of all IFDs */
    if (offset_base + NextDirOffset < offset_base ||
        offset_base + NextDirOffset > offset_base+IFDlength) {
      raise_warning("Illegal IFD offset");
      return 0;
    }
    /* That is the IFD for the first thumbnail */
    if (exif_process_IFD_in_JPEG(ImageInfo, offset_base + NextDirOffset,
                                 offset_base, end, IFDlength, displacement,
                                 SECTION_THUMBNAIL)) {
      if (ImageInfo->Thumbnail.filetype != IMAGE_FILETYPE_UNKNOWN &&
          ImageInfo->Thumbnail.size &&
          ImageInfo->Thumbnail.offset &&
          ImageInfo->read_thumbnail) {
        exif_thumbnail_extract(ImageInfo, offset_base, IFDlength);
      }
      return 1;
    } else {
      return 0;
    }
  }
  return 1;
}

Project: hhvm
File: ext_string.cpp

Variant str_replace(const Variant& search, const Variant& replace,
                    const Variant& subject, int64_t& count, bool caseSensitive) {
  count = 0;
  if (subject.isArray()) {
    Array arr = subject.toArray();
    Array ret = Array::CreateDArray();
    int64_t c;
    for (ArrayIter iter(arr); iter; ++iter) {
      if (iter.second().isArray() || iter.second().is(KindOfObject)) {
        ret.set(iter.first(), iter.second());
        continue;
      }

      auto const replaced = str_replace(
        search, replace, iter.second().toString(), c, caseSensitive
      ).toString();
      ret.set(iter.first(), replaced);
      count += c;
    }
    return ret;
  }
  return str_replace(search, replace, subject.toString(), count,
                     caseSensitive);
}

Project: hhvm
File: ext_gd.cpp

  void *imCalloc(size_t nmemb, size_t size
#ifdef IM_MEMORY_CHECK
, int ln
#endif
  ) {
    assertx(m_mallocSize <= (size_t)RuntimeOption::ImageMemoryMaxBytes);
    size_t bytes = nmemb * size;
    if (bytes > (size_t)RuntimeOption::ImageMemoryMaxBytes ||
        m_mallocSize + bytes > (size_t)RuntimeOption::ImageMemoryMaxBytes) {
      return nullptr;
    }

#ifdef IM_MEMORY_CHECK
    void *ptr = local_malloc(sizeof(ln) + sizeof(size) + bytes);
    if (!ptr) return nullptr;
    memset(ptr, 0, sizeof(ln) + sizeof(size) + bytes);
    memcpy(ptr, &ln, sizeof(ln));
    memcpy((char*)ptr + sizeof(ln), &bytes, sizeof(bytes));
    m_mallocSize += bytes;
    m_alloced.insert(ptr);
    return ((char *)ptr + sizeof(ln) + sizeof(size));
#else
    void *ptr = local_malloc(sizeof(size) + bytes);
    if (!ptr) return nullptr;
    memcpy(ptr, &bytes, sizeof(bytes));
    memset((char *)ptr + sizeof(size), 0, bytes);
    m_mallocSize += bytes;
    return ((char *)ptr + sizeof(size));
#endif
  }

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(ucfirst,
                     const String& str) {
  return stringToCaseFirst(str, toupper, isupper);
}

Project: hhvm
File: ext_string.cpp

Variant HHVM_FUNCTION(explode,
                      const String& delimiter,
                      const String& str,
                      int64_t limit /* = PHP_INT_MAX */) {
  return StringUtil::Explode(str, delimiter, limit);
}

Project: hhvm
File: ext_string.cpp

int64_t HHVM_FUNCTION(ord,
                      const String& str) {
  return (int64_t)(unsigned char)str[0];
}

Project: hhvm
File: type-string.h

  void format(FormatArg& arg, Callback& cb) const {
    FormatValue<HPHP::StringData*>(m_val.get()).format(arg, cb);
  }

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(str_rot13,
                     const String& str) {
  return StringUtil::ROT13(str);
}

Project: hhvm
File: ext_gd.cpp

static int php_get_xbm(const req::ptr<File>& stream, struct gfxinfo **result) {
  String fline;
  char *iname;
  char *type;
  int value;
  unsigned int width = 0, height = 0;

  if (result) {
    *result = nullptr;
  }
  if (!stream->rewind()) {
    return 0;
  }
  while (!(fline = HHVM_FN(fgets)(Resource(stream), 0).toString()).empty()) {
    iname = (char *)IM_MALLOC(fline.size() + 1);
    CHECK_ALLOC_R(iname, (fline.size() + 1), 0);
    if (sscanf(fline.c_str(), "#define %s %d", iname, &value) == 2) {
      if (!(type = strrchr(iname, '_'))) {
        type = iname;
      } else {
        type++;
      }

      if (!strcmp("width", type)) {
        width = (unsigned int)value;
        if (height) {
          IM_FREE(iname);
          break;
        }
      }
      if (!strcmp("height", type)) {
        height = (unsigned int)value;
        if (width) {
          IM_FREE(iname);
          break;
        }
      }
    }
    IM_FREE(iname);
  }

  if (width && height) {
    if (result) {
      *result = (struct gfxinfo *)IM_CALLOC(1, sizeof(struct gfxinfo));
      CHECK_ALLOC_R(*result, sizeof(struct gfxinfo), 0);
      (*result)->width = width;
      (*result)->height = height;
    }
    return IMAGE_FILETYPE_XBM;
  }

  return 0;
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(str_ireplace,
                         const Variant& search, const Variant& replace,
                         const Variant& subject) {
  int64_t count;
  return tvReturn(str_ireplace(search, replace, subject, count));
}

Project: hhvm
File: preg.cpp

ALWAYS_INLINE Variant preg_return_bad_regex_error(Variant&& return_value) {
  *rl_last_error_code = PHP_PCRE_BAD_REGEX_ERROR;
  return std::move(return_value);
}

Project: hhvm
File: ext_openssl.cpp

  static req::ptr<Key> GetHelper(const Variant& var, bool public_key,
                                 const char *passphrase) {
    req::ptr<Certificate> ocert;
    EVP_PKEY *key = nullptr;

    if (var.isResource()) {
      auto cert = dyn_cast_or_null<Certificate>(var);
      auto key = dyn_cast_or_null<Key>(var);
      if (!cert && !key) return nullptr;
      if (key) {
        bool is_priv = key->isPrivate();
        if (!public_key && !is_priv) {
          raise_warning("supplied key param is a public key");
          return nullptr;
        }
        if (public_key && is_priv) {
          raise_warning("Don't know how to get public key from "
                          "this private key");
          return nullptr;
        }
        return key;
      }
      ocert = cert;
    } else {
      /* it's an X509 file/cert of some kind, and we need to extract
         the data from that */
      if (public_key) {
        ocert = Certificate::Get(var);
        if (!ocert) {
          /* not a X509 certificate, try to retrieve public key */
          BIO *in = Certificate::ReadData(var);
          if (in == nullptr) return nullptr;
          key = PEM_read_bio_PUBKEY(in, nullptr,nullptr, nullptr);
          BIO_free(in);
        }
      } else {
        /* we want the private key */
        BIO *in = Certificate::ReadData(var);
        if (in == nullptr) return nullptr;
        key = PEM_read_bio_PrivateKey(in, nullptr,nullptr, (void*)passphrase);
        BIO_free(in);
      }
    }

    if (public_key && ocert && key == nullptr) {
      /* extract public key from X509 cert */
      key = (EVP_PKEY *)X509_get_pubkey(ocert->get());
    }

    if (key) {
      return req::make<Key>(key);
    }

    return nullptr;
  }

Project: hhvm
File: string-data-inl.h

inline const StringData::Proxy* StringData::proxy() const {
  return static_cast<const Proxy*>(payload());
}

Project: hhvm
File: ext_string.cpp

String HHVM_FUNCTION(quoted_printable_encode,
                     const String& str) {
  return StringUtil::QuotedPrintableEncode(str);
}

Project: hhvm
File: ext_std_variable.cpp

bool HHVM_FUNCTION(HH_is_any_array, const Variant& val) {
  if (tvIsClsMeth(val.asTypedValue())) {
    if (RuntimeOption::EvalIsCompatibleClsMethType) {
      if (RuntimeOption::EvalIsVecNotices) {
        raise_notice(Strings::CLSMETH_COMPAT_IS_ANY_ARR);
      }
      return true;
    }
    return false;
  }

  return tvIsArrayLike(val.asTypedValue()) || tvIsClsMeth(val.asTypedValue());
}

Project: hhvm
File: type-string.h

  explicit StrNR(const StringData *sd) : m_px(const_cast<StringData*>(sd)) {}

Project: hhvm
File: ext_std_variable.cpp

void HHVM_FUNCTION(var_dump, const Variant& expression,
                             const Array& _argv /*=null_array */) {

  VariableSerializer vs(VariableSerializer::Type::VarDump, 0, 2);
  do_var_dump(vs, expression);

  auto sz = _argv.size();
  for (int i = 0; i < sz; i++) {
    do_var_dump(vs, _argv[i]);
  }
}

Project: hhvm
File: zend-printf.cpp

inline static void appendchar(char **buffer, int *pos, int *size, char add) {
  if ((*pos + 1) >= *size) {
    *size <<= 1;
    *buffer = (char*)realloc(*buffer, *size);
  }
  (*buffer)[(*pos)++] = add;
}

Project: hhvm
File: ext_gd.cpp

Variant HHVM_FUNCTION(imagecreatefrompng, const String& filename) {
  gdImagePtr im =
    _php_image_create_from(filename, -1, -1, -1, -1,
                           PHP_GDIMG_TYPE_PNG, "PNG",
                           (gdImagePtr(*)())gdImageCreateFromPng,
                           (gdImagePtr(*)())gdImageCreateFromPngCtx);
  if (im == nullptr) {
    return false;
  }
  return Variant(req::make<Image>(im));
}

Project: hhvm
File: ext_gd.cpp

php_image_filter_mean_removal(gdImagePtr im, int /*arg1*/ /* = 0 */,
                              int /*arg2*/ /* = 0 */, int /*arg3*/ /* = 0 */,
                              int /*arg4*/ /* = 0 */) {
  return gdImageMeanRemoval(im) == 1;
}

Project: hhvm
File: ext_gd.cpp

int64_t HHVM_FUNCTION(imagefontheight, int64_t font) {
  return php_imagefontsize(font, 1);
}

Project: hhvm
File: string-data-inl.h

inline const char* StringData::data() const {
  // TODO: t1800106: re-enable this assert
  // assertx(data()[size()] == 0); // all strings must be null-terminated
#ifdef NO_M_DATA
  return reinterpret_cast<const char*>(this + 1);
#else
  return m_data;
#endif
}

Project: hhvm
File: ext_string.cpp

TypedValue HHVM_FUNCTION(strpos,
                         const String& haystack,
                         const Variant& needle,
                         int offset /* = 0 */) {
  if (offset < 0 || offset > haystack.size()) {
    raise_warning("Offset not contained in string");
    return make_tv<KindOfBoolean>(false);
  }
  int pos;
  if (needle.isString()) {
    String n(needle.toString());
    if (n.length() == 0) {
      raise_warning("Empty delimiter");
      return make_tv<KindOfBoolean>(false);
    }
    pos = haystack.find(n, offset);
  } else {
    pos = haystack.find(needle.toByte(), offset);
  }
  if (pos >= 0) return make_tv<KindOfInt64>(pos);
  return make_tv<KindOfBoolean>(false);
}

Project: hhvm
File: type-string.h

  folly::MutableStringPiece bufferSlice() {
    return m_str ? m_str->bufferSlice() : folly::MutableStringPiece();
  }

Project: hhvm
File: ext_mcrypt.cpp

bool HHVM_FUNCTION(mcrypt_module_is_block_algorithm, const String& algorithm,
                                  const String& lib_dir /* = null_string */) {
  String dir = lib_dir.empty() ? String(MCG(algorithms_dir)) : lib_dir;
  return mcrypt_module_is_block_algorithm((char*)algorithm.data(),
                                          (char*)dir.data()) == 1;
}