text:
/* See LICENSE for licensing information */

/**
 * \file rendcommon.c
 * \brief Rendezvous implementation: shared code between
 * introducers, services, clients, and rendezvous points.
 **/

#include "or.h"
#include "circuitbuild.h"
#include "config.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
#include "rendservice.h"
#include "rephist.h"
#include "routerlist.h"
#include "routerparse.h"

/** Return 0 if one and two are the same service ids, else -1 or 1 */
int
rend_cmp_service_ids(const char *one, const char *two)
{
  return strcasecmp(one,two);
}

/** Free the storage held by the service descriptor desc.
 */
void
rend_service_descriptor_free(rend_service_descriptor_t *desc)
{
  if (!desc)
    return;
  if (desc->pk)
    crypto_pk_free(desc->pk);
  if (desc->intro_nodes) {
    SMARTLIST_FOREACH(desc->intro_nodes, rend_intro_point_t *, intro,
      rend_intro_point_free(intro););
    smartlist_free(desc->intro_nodes);
  }
  if (desc->successful_uploads) {
    SMARTLIST_FOREACH(desc->successful_uploads, char *, c, tor_free(c););
    smartlist_free(desc->successful_uploads);
  }
  tor_free(desc);
}

/** Length of the descriptor cookie that is used for versioned hidden
 * service descriptors. */
#define REND_DESC_COOKIE_LEN 16

/** Length of the replica number that is used to determine the secret ID
 * part of versioned hidden service descriptors. */
#define REND_REPLICA_LEN 1

/** Compute the descriptor ID for service_id of length
 * REND_SERVICE_ID_LEN and secret_id_part of length
 * DIGEST_LEN, and write it to descriptor_id_out of length
 * DIGEST_LEN. */
void
rend_get_descriptor_id_bytes(char *descriptor_id_out,
                             const char *service_id,
                             const char *secret_id_part)
{
  crypto_digest_t *digest = crypto_digest_new();
  crypto_digest_add_bytes(digest, service_id, REND_SERVICE_ID_LEN);
  crypto_digest_add_bytes(digest, secret_id_part, DIGEST_LEN);
  crypto_digest_get_digest(digest, descriptor_id_out, DIGEST_LEN);
  crypto_digest_free(digest);
}

/** Compute the secret ID part for time_period,
 * a descriptor_cookie of length
 * REND_DESC_COOKIE_LEN which may also be NULL if no
 * descriptor_cookie shall be used, and replica, and write it to
 * secret_id_part of length DIGEST_LEN. */
static void
get_secret_id_part_bytes(char *secret_id_part, uint32_t time_period,
                         const char *descriptor_cookie, uint8_t replica)
{
  crypto_digest_t *digest = crypto_digest_new();
  time_period = htonl(time_period);
  crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t));
  if (descriptor_cookie) {
    crypto_digest_add_bytes(digest, descriptor_cookie,
                            REND_DESC_COOKIE_LEN);
  }
  crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN);
  crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN);
  crypto_digest_free(digest);
}

/** Return the time period for time now plus a potentially
 * intended deviation of one or more periods, based on the first byte
 * of service_id. */
static uint32_t
get_time_period(time_t now, uint8_t deviation, const char *service_id)
{
  /* The time period is the number of REND_TIME_PERIOD_V2_DESC_VALIDITY
   * intervals that have passed since the epoch, offset slightly so that
   * each service's time periods start and end at a fraction of that
   * period based on their first byte. */
  return (uint32_t)
    (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
    / REND_TIME_PERIOD_V2_DESC_VALIDITY + deviation;
}

/** Compute the time in seconds that a descriptor that is generated
 * now for service_id will be valid. */
static uint32_t
get_seconds_valid(time_t now, const char *service_id)
{
  uint32_t result = REND_TIME_PERIOD_V2_DESC_VALIDITY -
    ((uint32_t)
     (now + ((uint8_t) *service_id) * REND_TIME_PERIOD_V2_DESC_VALIDITY / 256)
     % REND_TIME_PERIOD_V2_DESC_VALIDITY);
  return result;
}

/** Compute the binary desc_id_out (DIGEST_LEN bytes long) for a given
 * base32-encoded service_id and optional unencoded
 * descriptor_cookie of length REND_DESC_COOKIE_LEN,
 * at time now for replica number
 * replica. desc_id needs to have DIGEST_LEN bytes
 * free. Return 0 for success, -1 otherwise. */
int
rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
                        const char *descriptor_cookie, time_t now,
                        uint8_t replica)
{
  char service_id_binary[REND_SERVICE_ID_LEN];
  char secret_id_part[DIGEST_LEN];
  uint32_t time_period;
  if (!service_id ||
      strlen(service_id) != REND_SERVICE_ID_LEN_BASE32) {
    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
                      "Illegal service ID: %s",
             safe_str(service_id));
    return -1;
  }
  if (replica >= REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) {
    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
                      "Replica number out of range: %d", replica);
    return -1;
  }
  /* Convert service ID to binary. */
  if (base32_decode(service_id_binary, REND_SERVICE_ID_LEN,
                    service_id, REND_SERVICE_ID_LEN_BASE32) < 0) {
    log_warn(LD_REND, "Could not compute v2 descriptor ID: "
                      "Illegal characters in service ID: %s",
             safe_str_client(service_id));
    return -1;
  }
  /* Calculate current time-period. */
  time_period = get_time_period(now, 0, service_id_binary);
  /* Calculate secret-id-part = h(time-period + replica). */
  get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
                           replica);
  /* Calculate descriptor ID. */
  rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part);
  return 0;
}

/** Encode the introduction points in desc and write the result to a
 * newly allocated string pointed to by encoded. Return 0 for
 * success, -1 otherwise. */
static int
rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
{
  size_t unenc_len;
  char *unenc = NULL;
  size_t unenc_written = 0;
  int i;
  int r = -1;
  /* Assemble unencrypted list of introduction points. */
  unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */
  unenc = (char *)tor_malloc_zero(unenc_len);
  for (i = 0; i < smartlist_len(desc->intro_nodes); i++) {
    char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1];
    char *onion_key = NULL;
    size_t onion_key_len;
    crypto_pk_t *intro_key;
    char *service_key = NULL;
    char *address = NULL;
    size_t service_key_len;
    int res;
    rend_intro_point_t *intro = (rend_intro_point_t *)smartlist_get(desc->intro_nodes, i);
    /* Obtain extend info with introduction point details. */
    extend_info_t *info = intro->extend_info;
    /* Encode introduction point ID. */
    base32_encode(id_base32, sizeof(id_base32),
                  info->identity_digest, DIGEST_LEN);
    /* Encode onion key. */
    if (crypto_pk_write_public_key_to_string(info->onion_key, &onion_key,
                                             &onion_key_len) < 0) {
      log_warn(LD_REND, "Could not write onion key.");
      goto done;
    }
    /* Encode intro key. */
    intro_key = intro->intro_key;
    if (!intro_key ||
      crypto_pk_write_public_key_to_string(intro_key, &service_key,
                                           &service_key_len) < 0) {
      log_warn(LD_REND, "Could not write intro key.");
      tor_free(onion_key);
      goto done;
    }
    /* Assemble everything for this introduction point. */
    address = tor_dup_addr(&info->addr);
    res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
                         "introduction-point %s\n"
                         "ip-address %s\n"
                         "onion-port %d\n"
                         "onion-key\n%s"
                         "service-key\n%s",
                       id_base32,
                       address,
                       info->port,
                       onion_key,
                       service_key);
    tor_free(address);
    tor_free(onion_key);
    tor_free(service_key);
    if (res < 0) {
      log_warn(LD_REND, "Not enough space for writing introduction point "
                        "string.");
      goto done;
    }
    /* Update total number of written bytes for unencrypted intro points. */
    unenc_written += res;
  }
  /* Finalize unencrypted introduction points. */
  if (unenc_len < unenc_written + 2) {
    log_warn(LD_REND, "Not enough space for finalizing introduction point "
                      "string.");
    goto done;
  }
  unenc[unenc_written++] = '\n';
  unenc[unenc_written++] = 0;
  *encoded = unenc;
  r = 0;
 done:
  if (r<0)
    tor_free(unenc);
  return r;
}

/** Encrypt the encoded introduction points in encoded using
 * authorization type  'basic' with client_cookies and write the
 * result to a newly allocated string pointed to by encrypted_out of
 * length encrypted_len_out. Return 0 for success, -1 otherwise. */
static int
rend_encrypt_v2_intro_points_basic(char **encrypted_out,
                                   size_t *encrypted_len_out,
                                   const char *encoded,
                                   smartlist_t *client_cookies)
{
  int r = -1, i, pos, enclen, client_blocks;
  size_t len, client_entries_len;
  char *enc = NULL, iv[CIPHER_IV_LEN], *client_part = NULL,
       session_key[CIPHER_KEY_LEN];
  smartlist_t *encrypted_session_keys = NULL;
  crypto_digest_t *digest;
  crypto_cipher_t *cipher;
  tor_assert(encoded);
  tor_assert(client_cookies && smartlist_len(client_cookies) > 0);

  /* Generate session key. */
  if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) {
    log_warn(LD_REND, "Unable to generate random session key to encrypt "
                      "introduction point string.");
    goto done;
  }

  /* Determine length of encrypted introduction points including session
   * keys. */
  client_blocks = 1 + ((smartlist_len(client_cookies) - 1) /
                       REND_BASIC_AUTH_CLIENT_MULTIPLE);
  client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
                       REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
  len = 2 + client_entries_len + CIPHER_IV_LEN + strlen(encoded);
  if (client_blocks >= 256) {
    log_warn(LD_REND, "Too many clients in introduction point string.");
    goto done;
  }
  enc = (char *)tor_malloc_zero(len);
  enc[0] = 0x01; /* type of authorization. */
  enc[1] = (uint8_t)client_blocks;

  /* Encrypt with random session key. */
  enclen = crypto_cipher_encrypt_with_iv(session_key,
      enc + 2 + client_entries_len,
      CIPHER_IV_LEN + strlen(encoded), encoded, strlen(encoded));

  if (enclen < 0) {
    log_warn(LD_REND, "Could not encrypt introduction point string.");
    goto done;
  }
  memcpy(iv, enc + 2 + client_entries_len, CIPHER_IV_LEN);

  /* Encrypt session key for cookies, determine client IDs, and put both
   * in a smartlist. */
  encrypted_session_keys = smartlist_new();
  SMARTLIST_FOREACH_BEGIN(client_cookies, const char *, cookie) {
    client_part = (char *)tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
    /* Encrypt session key. */
    cipher = crypto_cipher_new(cookie);
    if (crypto_cipher_encrypt(cipher, client_part +
                                  REND_BASIC_AUTH_CLIENT_ID_LEN,
                              session_key, CIPHER_KEY_LEN) < 0) {
      log_warn(LD_REND, "Could not encrypt session key for client.");
      crypto_cipher_free(cipher);
      tor_free(client_part);
      goto done;
    }
    crypto_cipher_free(cipher);

    /* Determine client ID. */
    digest = crypto_digest_new();
    crypto_digest_add_bytes(digest, cookie, REND_DESC_COOKIE_LEN);
    crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
    crypto_digest_get_digest(digest, client_part,
                             REND_BASIC_AUTH_CLIENT_ID_LEN);
    crypto_digest_free(digest);

    /* Put both together. */
    smartlist_add(encrypted_session_keys, client_part);
  } SMARTLIST_FOREACH_END(cookie);

  /* Add some fake client IDs and encrypted session keys. */
  for (i = (smartlist_len(client_cookies) - 1) %
           REND_BASIC_AUTH_CLIENT_MULTIPLE;
       i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) {
    client_part = (char *)tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
    if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) {
      log_warn(LD_REND, "Unable to generate fake client entry.");
      tor_free(client_part);
      goto done;
    }
    smartlist_add(encrypted_session_keys, client_part);
  }
  /* Sort smartlist and put elements in result in order. */
  smartlist_sort_digests(encrypted_session_keys);
  pos = 2;
  SMARTLIST_FOREACH(encrypted_session_keys, const char *, entry, {
    memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN);
    pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
  });
  *encrypted_out = enc;
  *encrypted_len_out = len;
  enc = NULL; /* prevent free. */
  r = 0;
 done:
  tor_free(enc);
  if (encrypted_session_keys) {
    SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d););
    smartlist_free(encrypted_session_keys);
  }
  return r;
}

/** Encrypt the encoded introduction points in encoded using
 * authorization type 'stealth' with descriptor_cookie of length
 * REND_DESC_COOKIE_LEN and write the result to a newly allocated string
 * pointed to by encrypted_out of length encrypted_len_out.
 * Return 0 for success, -1 otherwise. */
static int
rend_encrypt_v2_intro_points_stealth(char **encrypted_out,
                                     size_t *encrypted_len_out,
                                     const char *encoded,
                                     const char *descriptor_cookie)
{
  int r = -1, enclen;
  char *enc;
  tor_assert(encoded);
  tor_assert(descriptor_cookie);

  enc = (char *)tor_malloc_zero(1 + CIPHER_IV_LEN + strlen(encoded));
  enc[0] = 0x02; /* Auth type */
  enclen = crypto_cipher_encrypt_with_iv(descriptor_cookie,
                                         enc + 1,
                                         CIPHER_IV_LEN+strlen(encoded),
                                         encoded, strlen(encoded));
  if (enclen < 0) {
    log_warn(LD_REND, "Could not encrypt introduction point string.");
    goto done;
  }
  *encrypted_out = enc;
  *encrypted_len_out = enclen;
  enc = NULL; /* prevent free */
  r = 0;
 done:
  tor_free(enc);
  return r;
}

/** Attempt to parse the given desc_str and return true if this
 * succeeds, false otherwise. */
static int
rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc)
{
  rend_service_descriptor_t *test_parsed = NULL;
  char test_desc_id[DIGEST_LEN];
  char *test_intro_content = NULL;
  size_t test_intro_size;
  size_t test_encoded_size;
  const char *test_next;
  int res = rend_parse_v2_service_descriptor(&test_parsed, test_desc_id,
                                         &test_intro_content,
                                         &test_intro_size,
                                         &test_encoded_size,
                                         &test_next, desc->desc_str);
  rend_service_descriptor_free(test_parsed);
  tor_free(test_intro_content);
  return (res >= 0);
}

/** Free the storage held by an encoded v2 service descriptor. */
void
rend_encoded_v2_service_descriptor_free(
  rend_encoded_v2_service_descriptor_t *desc)
{
  if (!desc)
    return;
  tor_free(desc->desc_str);
  tor_free(desc);
}

/** Free the storage held by an introduction point info. */
void
rend_intro_point_free(rend_intro_point_t *intro)
{
  if (!intro)
    return;

  extend_info_free(intro->extend_info);
  crypto_pk_free(intro->intro_key);

  if (intro->accepted_intro_rsa_parts != NULL) {
    replaycache_free(intro->accepted_intro_rsa_parts);
  }

  tor_free(intro);
}

/** Encode a set of rend_encoded_v2_service_descriptor_t's for desc
 * at time now using service_key, depending on
 * auth_type a descriptor_cookie and a list of
 * client_cookies (which are both NULL if no client
 * authorization is performed), and period (e.g. 0 for the current
 * period, 1 for the next period, etc.) and add them to the existing list
 * descs_out; return the number of seconds that the descriptors will
 * be found by clients, or -1 if the encoding was not successful. */
int
rend_encode_v2_descriptors(smartlist_t *descs_out,
                           rend_service_descriptor_t *desc, time_t now,
                           uint8_t period, rend_auth_type_t auth_type,
                           crypto_pk_t *client_key,
                           smartlist_t *client_cookies)
{
  char service_id[DIGEST_LEN];
  uint32_t time_period;
  char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
       *descriptor_cookie = NULL;
  size_t ipos_len = 0, ipos_encrypted_len = 0;
  int k;
  uint32_t seconds_valid;
  crypto_pk_t *service_key;
  if (!desc) {
    log_warn(LD_BUG, "Could not encode v2 descriptor: No desc given.");
    return -1;
  }
  service_key = (auth_type == REND_STEALTH_AUTH) ? client_key : desc->pk;
  tor_assert(service_key);
  if (auth_type == REND_STEALTH_AUTH) {
    descriptor_cookie = (char *)smartlist_get(client_cookies, 0);
    tor_assert(descriptor_cookie);
  }
  /* Obtain service_id from public key. */
  crypto_pk_get_digest(service_key, service_id);
  /* Calculate current time-period. */
  time_period = get_time_period(now, period, service_id);
  /* Determine how many seconds the descriptor will be valid. */
  seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY +
                  get_seconds_valid(now, service_id);
  /* Assemble, possibly encrypt, and encode introduction points. */
  if (smartlist_len(desc->intro_nodes) > 0) {
    if (rend_encode_v2_intro_points(&ipos, desc) < 0) {
      log_warn(LD_REND, "Encoding of introduction points did not succeed.");
      return -1;
    }
    switch (auth_type) {
      case REND_NO_AUTH:
        ipos_len = strlen(ipos);
        break;
      case REND_BASIC_AUTH:
        if (rend_encrypt_v2_intro_points_basic(&ipos_encrypted,
                                               &ipos_encrypted_len, ipos,
                                               client_cookies) < 0) {
          log_warn(LD_REND, "Encrypting of introduction points did not "
                            "succeed.");
          tor_free(ipos);
          return -1;
        }
        tor_free(ipos);
        ipos = ipos_encrypted;
        ipos_len = ipos_encrypted_len;
        break;
      case REND_STEALTH_AUTH:
        if (rend_encrypt_v2_intro_points_stealth(&ipos_encrypted,
                                                 &ipos_encrypted_len, ipos,
                                                 descriptor_cookie) < 0) {
          log_warn(LD_REND, "Encrypting of introduction points did not "
                            "succeed.");
          tor_free(ipos);
          return -1;
        }
        tor_free(ipos);
        ipos = ipos_encrypted;
        ipos_len = ipos_encrypted_len;
        break;
      default:
        log_warn(LD_REND|LD_BUG, "Unrecognized authorization type %d",
                 (int)auth_type);
        tor_free(ipos);
        return -1;
    }
    /* Base64-encode introduction points. */
    ipos_base64 = (char *)tor_malloc_zero(ipos_len * 2);
    if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) {
      log_warn(LD_REND, "Could not encode introduction point string to "
               "base64. length=%d", (int)ipos_len);
      tor_free(ipos_base64);
      tor_free(ipos);
      return -1;
    }
    tor_free(ipos);
  }
  /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
  for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
    char secret_id_part[DIGEST_LEN];
    char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1];
    char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
    char *permanent_key = NULL;
    size_t permanent_key_len;
    char published[ISO_TIME_LEN+1];
    int i;
    char protocol_versions_string[16]; /* max len: "0,1,2,3,4,5,6,7\0" */
    size_t protocol_versions_written;
    size_t desc_len;
    char *desc_str = NULL;
    int result = 0;
    size_t written = 0;
    char desc_digest[DIGEST_LEN];
    rend_encoded_v2_service_descriptor_t *enc =
      (rend_encoded_v2_service_descriptor_t *)tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
    /* Calculate secret-id-part = h(time-period + cookie + replica). */
    get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
                             k);
    base32_encode(secret_id_part_base32, sizeof(secret_id_part_base32),
                  secret_id_part, DIGEST_LEN);
    /* Calculate descriptor ID. */
    rend_get_descriptor_id_bytes(enc->desc_id, service_id, secret_id_part);
    base32_encode(desc_id_base32, sizeof(desc_id_base32),
                  enc->desc_id, DIGEST_LEN);
    /* PEM-encode the public key */
    if (crypto_pk_write_public_key_to_string(service_key, &permanent_key,
                                             &permanent_key_len) < 0) {
      log_warn(LD_BUG, "Could not write public key to string.");
      rend_encoded_v2_service_descriptor_free(enc);
      goto err;
    }
    /* Encode timestamp. */
    format_iso_time(published, desc->timestamp);
    /* Write protocol-versions bitmask to comma-separated value string. */
    protocol_versions_written = 0;
    for (i = 0; i < 8; i++) {
      if (desc->protocols & 1 <intro_nodes) * 1000; /* far too long,
                                                                  but okay.*/
    enc->desc_str = desc_str = (char *)tor_malloc_zero(desc_len);
    result = tor_snprintf(desc_str, desc_len,
             "rendezvous-service-descriptor %s\n"
             "version 2\n"
             "permanent-key\n%s"
             "secret-id-part %s\n"
             "publication-time %s\n"
             "protocol-versions %s\n",
        desc_id_base32,
        permanent_key,
        secret_id_part_base32,
        published,
        protocol_versions_string);
    tor_free(permanent_key);
    if (result < 0) {
      log_warn(LD_BUG, "Descriptor ran out of room.");
      rend_encoded_v2_service_descriptor_free(enc);
      goto err;
    }
    written = result;
    /* Add introduction points. */
    if (ipos_base64) {
      result = tor_snprintf(desc_str + written, desc_len - written,
                            "introduction-points\n"
                            "-----BEGIN MESSAGE-----\n%s"
                            "-----END MESSAGE-----\n",
                            ipos_base64);
      if (result < 0) {
        log_warn(LD_BUG, "could not write introduction points.");
        rend_encoded_v2_service_descriptor_free(enc);
        goto err;
      }
      written += result;
    }
    /* Add signature. */
    strlcpy(desc_str + written, "signature\n", desc_len - written);
    written += strlen(desc_str + written);
    if (crypto_digest(desc_digest, desc_str, written) < 0) {
      log_warn(LD_BUG, "could not create digest.");
      rend_encoded_v2_service_descriptor_free(enc);
      goto err;
    }
    if (router_append_dirobj_signature(desc_str + written,
                                       desc_len - written,
                                       desc_digest, DIGEST_LEN,
                                       service_key) < 0) {
      log_warn(LD_BUG, "Couldn't sign desc.");
      rend_encoded_v2_service_descriptor_free(enc);
      goto err;
    }
    written += strlen(desc_str+written);
    if (written+2 > desc_len) {
        log_warn(LD_BUG, "Could not finish desc.");
        rend_encoded_v2_service_descriptor_free(enc);
        goto err;
    }
    desc_str[written++] = '\n';
    desc_str[written++] = 0;
    /* Check if we can parse our own descriptor. */
    if (!rend_desc_v2_is_parsable(enc)) {
      log_warn(LD_BUG, "Could not parse my own descriptor: %s", desc_str);
      rend_encoded_v2_service_descriptor_free(enc);
      goto err;
    }
    smartlist_add(descs_out, enc);
  }

  log_info(LD_REND, "Successfully encoded a v2 descriptor and "
                    "confirmed that it is parsable.");
  goto done;

 err:
  SMARTLIST_FOREACH(descs_out, rend_encoded_v2_service_descriptor_t *, d,
                    rend_encoded_v2_service_descriptor_free(d););
  smartlist_clear(descs_out);
  seconds_valid = -1;

 done:
  tor_free(ipos_base64);
  return seconds_valid;
}

/** Sets out to the first 10 bytes of the digest of pk,
 * base32 encoded.  NUL-terminates out.  (We use this string to
 * identify services in directory requests and .onion URLs.)
 */
int
rend_get_service_id(crypto_pk_t *pk, char *out)
{
  char buf[DIGEST_LEN];
  tor_assert(pk);
  if (crypto_pk_get_digest(pk, buf) < 0)
    return -1;
  base32_encode(out, REND_SERVICE_ID_LEN_BASE32+1, buf, REND_SERVICE_ID_LEN);
  return 0;
}

/* ==== Rendezvous service descriptor cache. */

/** How old do we let hidden service descriptors get before discarding
 * them as too old? */
#define REND_CACHE_MAX_AGE (2*24*60*60)
/** How wrong do we assume our clock may be when checking whether hidden
 * services are too old or too new? */
#define REND_CACHE_MAX_SKEW (24*60*60)

/** Map from service id (as generated by rend_get_service_id) to
 * rend_cache_entry_t. */
static strmap_t *rend_cache = NULL;

/** Map from descriptor id to rend_cache_entry_t; only for hidden service
 * directories. */
static digestmap_t *rend_cache_v2_dir = NULL;

/** Initializes the service descriptor cache.
 */
void
rend_cache_init(void)
{
  rend_cache = strmap_new();
  rend_cache_v2_dir = digestmap_new();
}

/** Helper: free storage held by a single service descriptor cache entry. */
static void
rend_cache_entry_free(rend_cache_entry_t *e)
{
  if (!e)
    return;
  rend_service_descriptor_free(e->parsed);
  tor_free(e->desc);
  tor_free(e);
}

/** Helper: deallocate a rend_cache_entry_t.  (Used with strmap_free(), which
 * requires a function pointer whose argument is void*). */
static void
rend_cache_entry_free_(void *p)
{
  rend_cache_entry_free((rend_cache_entry_t *)p);
}

/** Free all storage held by the service descriptor cache. */
void
rend_cache_free_all(void)
{
  strmap_free(rend_cache, rend_cache_entry_free_);
  digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
  rend_cache = NULL;
  rend_cache_v2_dir = NULL;
}

/** Removes all old entries from the service descriptor cache.
 */
void
rend_cache_clean(time_t now)
{
  strmap_iter_t *iter;
  const char *key;
  void *val;
  rend_cache_entry_t *ent;
  time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
  for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
    strmap_iter_get(iter, &key, &val);
    ent = (rend_cache_entry_t*)val;
    if (ent->parsed->timestamp < cutoff) {
      iter = strmap_iter_next_rmv(rend_cache, iter);
      rend_cache_entry_free(ent);
    } else {
      iter = strmap_iter_next(rend_cache, iter);
    }
  }
}

/** Remove ALL entries from the rendezvous service descriptor cache.
 */
void
rend_cache_purge(void)
{
  if (rend_cache) {
    log_info(LD_REND, "Purging HS descriptor cache");
    strmap_free(rend_cache, rend_cache_entry_free_);
  }
  rend_cache = strmap_new();
}

/** Remove all old v2 descriptors and those for which this hidden service
 * directory is not responsible for any more. */
void
rend_cache_clean_v2_descs_as_dir(time_t now)
{
  digestmap_iter_t *iter;
  time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
  for (iter = digestmap_iter_init(rend_cache_v2_dir);
       !digestmap_iter_done(iter); ) {
    const char *key;
    void *val;
    rend_cache_entry_t *ent;
    digestmap_iter_get(iter, &key, &val);
    ent = (rend_cache_entry_t *)val;
    if (ent->parsed->timestamp < cutoff ||
        !hid_serv_responsible_for_desc_id(key)) {
      char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
      base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
      log_info(LD_REND, "Removing descriptor with ID '%s' from cache",
               safe_str_client(key_base32));
      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
      rend_cache_entry_free(ent);
    } else {
      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
    }
  }
}

/** Determines whether a is in the interval of b (excluded) and
 * c (included) in a circular digest ring; returns 1 if this is the
 * case, and 0 otherwise.
 */
int
rend_id_is_in_interval(const char *a, const char *b, const char *c)
{
  int a_b, b_c, c_a;
  tor_assert(a);
  tor_assert(b);
  tor_assert(c);

  /* There are five cases in which a is outside the interval ]b,c]: */
  a_b = tor_memcmp(a,b,DIGEST_LEN);
  if (a_b == 0)
    return 0; /* 1. a == b (b is excluded) */
  b_c = tor_memcmp(b,c,DIGEST_LEN);
  if (b_c == 0)
    return 0; /* 2. b == c (interval is empty) */
  else if (a_b <= 0 && b_c < 0)
    return 0; /* 3. a b c */
  c_a = tor_memcmp(c,a,DIGEST_LEN);
  if (c_a < 0 && a_b <= 0)
    return 0; /* 4. c a b */
  else if (b_c < 0 && c_a < 0)
    return 0; /* 5. b c a */

  /* In the other cases (a c b; b a c; c b a), a is inside the interval. */
  return 1;
}

/** Return true iff query is a syntactically valid service ID (as
 * generated by rend_get_service_id).  */
int
rend_valid_service_id(const char *query)
{
  if (strlen(query) != REND_SERVICE_ID_LEN_BASE32)
    return 0;

  if (strspn(query, BASE32_CHARS) != REND_SERVICE_ID_LEN_BASE32)
    return 0;

  return 1;
}

/** If we have a cached rend_cache_entry_t for the service ID query
 * with version, set *e to that entry and return 1.
 * Else return 0. If version is nonnegative, only return an entry
 * in that descriptor format version. Otherwise (if version is
 * negative), return the most recent format we have.
 */
int
rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
{
  char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */
  tor_assert(rend_cache);
  if (!rend_valid_service_id(query))
    return -1;
  *e = NULL;
  if (version != 0) {
    tor_snprintf(key, sizeof(key), "2%s", query);
    *e = (rend_cache_entry_t *)strmap_get_lc(rend_cache, key);
  }
  if (!*e && version != 2) {
    tor_snprintf(key, sizeof(key), "0%s", query);
    *e = (rend_cache_entry_t *)strmap_get_lc(rend_cache, key);
  }
  if (!*e)
    return 0;
  tor_assert((*e)->parsed && (*e)->parsed->intro_nodes);
  /* XXX023 hack for now, to return "not found" if there are no intro
   * points remaining. See bug 997. */
  if (! rend_client_any_intro_points_usable(*e))
    return 0;
  return 1;
}

/** Lookup the v2 service descriptor with base32-encoded desc_id and
 * copy the pointer to it to *desc.  Return 1 on success, 0 on
 * well-formed-but-not-found, and -1 on failure.
 */
int
rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
{
  rend_cache_entry_t *e;
  char desc_id_digest[DIGEST_LEN];
  tor_assert(rend_cache_v2_dir);
  if (base32_decode(desc_id_digest, DIGEST_LEN,
                    desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) {
    log_fn(LOG_PROTOCOL_WARN, LD_REND,
           "Rejecting v2 rendezvous descriptor request -- descriptor ID "
           "contains illegal characters: %s",
           safe_str(desc_id));
    return -1;
  }
  /* Lookup descriptor and return. */
  e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id_digest);
  if (e) {
    *desc = e->desc;
    return 1;
  }
  return 0;
}

/* Do not allow more than this many introduction points in a hidden service
 * descriptor */
#define MAX_INTRO_POINTS 10

/** Parse the v2 service descriptor(s) in desc and store it/them to the
 * local rend cache. Don't attempt to decrypt the included list of introduction
 * points (as we don't have a descriptor cookie for it).
 *
 * If we have a newer descriptor with the same ID, ignore this one.
 * If we have an older descriptor with the same ID, replace it.
 *
 * Return an appropriate rend_cache_store_status_t.
 */
rend_cache_store_status_t
rend_cache_store_v2_desc_as_dir(const char *desc)
{
  rend_service_descriptor_t *parsed;
  char desc_id[DIGEST_LEN];
  char *intro_content;
  size_t intro_size;
  size_t encoded_size;
  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
  int number_parsed = 0, number_stored = 0;
  const char *current_desc = desc;
  const char *next_desc;
  rend_cache_entry_t *e;
  time_t now = time(NULL);
  tor_assert(rend_cache_v2_dir);
  tor_assert(desc);
  if (!hid_serv_acting_as_directory()) {
    /* Cannot store descs, because we are (currently) not acting as
     * hidden service directory. */
    log_info(LD_REND, "Cannot store descs: Not acting as hs dir");
    return RCS_NOTDIR;
  }
  while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
                                          &intro_size, &encoded_size,
                                          &next_desc, current_desc) >= 0) {
    number_parsed++;
    /* We don't care about the introduction points. */
    tor_free(intro_content);
    /* For pretty log statements. */
    base32_encode(desc_id_base32, sizeof(desc_id_base32),
                  desc_id, DIGEST_LEN);
    /* Is desc ID in the range that we are (directly or indirectly) responsible
     * for? */
    if (!hid_serv_responsible_for_desc_id(desc_id)) {
      log_info(LD_REND, "Service descriptor with desc ID %s is not in "
                        "interval that we are responsible for.",
               safe_str_client(desc_id_base32));
      goto skip;
    }
    /* Is descriptor too old? */
    if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
      log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
               safe_str(desc_id_base32));
      goto skip;
    }
    /* Is descriptor too far in the future? */
    if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
      log_info(LD_REND, "Service descriptor with desc ID %s is too far in the "
                        "future.",
               safe_str(desc_id_base32));
      goto skip;
    }
    /* Do we already have a newer descriptor? */
    e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id);
    if (e && e->parsed->timestamp > parsed->timestamp) {
      log_info(LD_REND, "We already have a newer service descriptor with the "
                        "same desc ID %s and version.",
               safe_str(desc_id_base32));
      goto skip;
    }
    /* Do we already have this descriptor? */
    if (e && !strcmp(desc, e->desc)) {
      log_info(LD_REND, "We already have this service descriptor with desc "
                        "ID %s.", safe_str(desc_id_base32));
      e->received = time(NULL);
      goto skip;
    }
    /* Store received descriptor. */
    if (!e) {
      e = (rend_cache_entry_t*)tor_malloc_zero(sizeof(rend_cache_entry_t));
      digestmap_set(rend_cache_v2_dir, desc_id, e);
    } else {
      rend_service_descriptor_free(e->parsed);
      tor_free(e->desc);
    }
    e->received = time(NULL);
    e->parsed = parsed;
    e->desc = tor_strndup(current_desc, encoded_size);
    e->len = encoded_size;
    log_info(LD_REND, "Successfully stored service descriptor with desc ID "
                      "'%s' and len %d.",
             safe_str(desc_id_base32), (int)encoded_size);
    number_stored++;
    goto advance;
  skip:
    rend_service_descriptor_free(parsed);
  advance:
    /* advance to next descriptor, if available. */
    current_desc = next_desc;
    /* check if there is a next descriptor. */
    if (!current_desc ||
        strcmpstart(current_desc, "rendezvous-service-descriptor "))
      break;
  }
  if (!number_parsed) {
    log_info(LD_REND, "Could not parse any descriptor.");
    return RCS_BADDESC;
  }
  log_info(LD_REND, "Parsed %d and added %d descriptor%s.",
           number_parsed, number_stored, number_stored != 1 ? "s" : "");
  return RCS_OKAY;
}

/** Parse the v2 service descriptor in desc, decrypt the included list
 * of introduction points with descriptor_cookie (which may also be
 * NULL if decryption is not necessary), and store the descriptor to
 * the local cache under its version and service id.
 *
 * If we have a newer v2 descriptor with the same ID, ignore this one.
 * If we have an older descriptor with the same ID, replace it.
 * If the descriptor's service ID does not match
 * rend_query-\>onion_address, reject it.
 *
 * Return an appropriate rend_cache_store_status_t.
 */
rend_cache_store_status_t
rend_cache_store_v2_desc_as_client(const char *desc,
                                   const rend_data_t *rend_query)
{
  /*XXXX this seems to have a bit of duplicate code with
   * rend_cache_store_v2_desc_as_dir().  Fix that. */
  /* Though having similar elements, both functions were separated on
   * purpose:
   * - dirs don't care about encoded/encrypted introduction points, clients
   *   do.
   * - dirs store descriptors in a separate cache by descriptor ID, whereas
   *   clients store them by service ID; both caches are different data
   *   structures and have different access methods.
   * - dirs store a descriptor only if they are responsible for its ID,
   *   clients do so in every way (because they have requested it before).
   * - dirs can process multiple concatenated descriptors which is required
   *   for replication, whereas clients only accept a single descriptor.
   * Thus, combining both methods would result in a lot of if statements
   * which probably would not improve, but worsen code readability. -KL */
  rend_service_descriptor_t *parsed = NULL;
  char desc_id[DIGEST_LEN];
  char *intro_content = NULL;
  size_t intro_size;
  size_t encoded_size;
  const char *next_desc;
  time_t now = time(NULL);
  char key[REND_SERVICE_ID_LEN_BASE32+2];
  char service_id[REND_SERVICE_ID_LEN_BASE32+1];
  rend_cache_entry_t *e;
  rend_cache_store_status_t retval = RCS_BADDESC;
  tor_assert(rend_cache);
  tor_assert(desc);
  /* Parse the descriptor. */
  if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
                                       &intro_size, &encoded_size,
                                       &next_desc, desc) < 0) {
    log_warn(LD_REND, "Could not parse descriptor.");
    goto err;
  }
  /* Compute service ID from public key. */
  if (rend_get_service_id(parsed->pk, service_id)<0) {
    log_warn(LD_REND, "Couldn't compute service ID.");
    goto err;
  }
  if (strcmp(rend_query->onion_address, service_id)) {
    log_warn(LD_REND, "Received service descriptor for service ID %s; "
             "expected descriptor for service ID %s.",
             service_id, safe_str(rend_query->onion_address));
    goto err;
  }
  /* Decode/decrypt introduction points. */
  if (intro_content) {
    int n_intro_points;
    if (rend_query->auth_type != REND_NO_AUTH &&
        !tor_mem_is_zero(rend_query->descriptor_cookie,
                         sizeof(rend_query->descriptor_cookie))) {
      char *ipos_decrypted = NULL;
      size_t ipos_decrypted_size;
      if (rend_decrypt_introduction_points(&ipos_decrypted,
                                           &ipos_decrypted_size,
                                           rend_query->descriptor_cookie,
                                           intro_content,
                                           intro_size) < 0) {
        log_warn(LD_REND, "Failed to decrypt introduction points. We are "
                 "probably unable to parse the encoded introduction points.");
      } else {
        /* Replace encrypted with decrypted introduction points. */
        log_info(LD_REND, "Successfully decrypted introduction points.");
        tor_free(intro_content);
        intro_content = ipos_decrypted;
        intro_size = ipos_decrypted_size;
      }
    }
    n_intro_points = rend_parse_introduction_points(parsed, intro_content,
                                                    intro_size);
    if (n_intro_points <= 0) {
      log_warn(LD_REND, "Failed to parse introduction points. Either the "
               "service has published a corrupt descriptor or you have "
               "provided invalid authorization data.");
      goto err;
    } else if (n_intro_points > MAX_INTRO_POINTS) {
      log_warn(LD_REND, "Found too many introduction points on a hidden "
               "service descriptor for %s. This is probably a (misguided) "
               "attempt to improve reliability, but it could also be an "
               "attempt to do a guard enumeration attack. Rejecting.",
               safe_str_client(rend_query->onion_address));

      goto err;
    }
  } else {
    log_info(LD_REND, "Descriptor does not contain any introduction points.");
    parsed->intro_nodes = smartlist_new();
  }
  /* We don't need the encoded/encrypted introduction points any longer. */
  tor_free(intro_content);
  /* Is descriptor too old? */
  if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
    log_warn(LD_REND, "Service descriptor with service ID %s is too old.",
             safe_str_client(service_id));
    goto err;
  }
  /* Is descriptor too far in the future? */
  if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
    log_warn(LD_REND, "Service descriptor with service ID %s is too far in "
                      "the future.", safe_str_client(service_id));
    goto err;
  }
  /* Do we already have a newer descriptor? */
  tor_snprintf(key, sizeof(key), "2%s", service_id);
  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
  if (e && e->parsed->timestamp > parsed->timestamp) {
    log_info(LD_REND, "We already have a newer service descriptor for "
                      "service ID %s with the same desc ID and version.",
             safe_str_client(service_id));
    goto okay;
  }
  /* Do we already have this descriptor? */
  if (e && !strcmp(desc, e->desc)) {
    log_info(LD_REND,"We already have this service descriptor %s.",
             safe_str_client(service_id));
    e->received = time(NULL);
    goto okay;
  }
  if (!e) {
    e = (rend_cache_entry_t *)tor_malloc_zero(sizeof(rend_cache_entry_t));
    strmap_set_lc(rend_cache, key, e);
  } else {
    rend_service_descriptor_free(e->parsed);
    tor_free(e->desc);
  }
  e->received = time(NULL);
  e->parsed = parsed;
  e->desc = (char *)tor_malloc_zero(encoded_size + 1);
  strlcpy(e->desc, desc, encoded_size + 1);
  e->len = encoded_size;
  log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
            safe_str_client(service_id), (int)encoded_size);
  return RCS_OKAY;

 okay:
  retval = RCS_OKAY;

 err:
  rend_service_descriptor_free(parsed);
  tor_free(intro_content);
  return retval;
}

/** Called when we get a rendezvous-related relay cell on circuit
 * circ.  Dispatch on rendezvous relay command. */
void
rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
                        int command, size_t length,
                        const uint8_t *payload)
{
  or_circuit_t *or_circ = NULL;
  origin_circuit_t *origin_circ = NULL;
  int r = -2;
  if (CIRCUIT_IS_ORIGIN(circ)) {
    origin_circ = TO_ORIGIN_CIRCUIT(circ);
    if (!layer_hint || layer_hint != origin_circ->cpath->prev) {
      log_fn(LOG_PROTOCOL_WARN, LD_APP,
             "Relay cell (rend purpose %d) from wrong hop on origin circ",
             command);
      origin_circ = NULL;
    }
  } else {
    or_circ = TO_OR_CIRCUIT(circ);
  }

  switch (command) {
    case RELAY_COMMAND_ESTABLISH_INTRO:
      if (or_circ)
        r = rend_mid_establish_intro(or_circ,payload,length);
      break;
    case RELAY_COMMAND_ESTABLISH_RENDEZVOUS:
      if (or_circ)
        r = rend_mid_establish_rendezvous(or_circ,payload,length);
      break;
    case RELAY_COMMAND_INTRODUCE1:
      if (or_circ)
        r = rend_mid_introduce(or_circ,payload,length);
      break;
    case RELAY_COMMAND_INTRODUCE2:
      if (origin_circ)
        r = rend_service_introduce(origin_circ,payload,length);
      break;
    case RELAY_COMMAND_INTRODUCE_ACK:
      if (origin_circ)
        r = rend_client_introduction_acked(origin_circ,payload,length);
      break;
    case RELAY_COMMAND_RENDEZVOUS1:
      if (or_circ)
        r = rend_mid_rendezvous(or_circ,payload,length);
      break;
    case RELAY_COMMAND_RENDEZVOUS2:
      if (origin_circ)
        r = rend_client_receive_rendezvous(origin_circ,payload,length);
      break;
    case RELAY_COMMAND_INTRO_ESTABLISHED:
      if (origin_circ)
        r = rend_service_intro_established(origin_circ,payload,length);
      break;
    case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
      if (origin_circ)
        r = rend_client_rendezvous_acked(origin_circ,payload,length);
      break;
    default:
      tor_fragile_assert();
  }

  if (r == -2)
    log_info(LD_PROTOCOL, "Dropping cell (type %d) for wrong circuit type.",
             command);
}

/** Allocate and return a new rend_data_t with the same
 * contents as query. */
rend_data_t *
rend_data_dup(const rend_data_t *data)
{
  tor_assert(data);
  return (rend_data_t *)tor_memdup(data, sizeof(rend_data_t));
}


text:
layout: post
title: 深入理解 RecyclerView 系列之一:ItemDecoration
tags:
    - 安卓开发
    - RecyclerView
    - PoC
---

RecyclerView 已经推出了一年多了,日常开发中也已经彻底从 ListView 迁移到了 RecyclerView,但前两天有人在一个安卓群里面问了个关于最顶上的 item view 加蒙层的问题,被人用 ItemDecoration 完美解决。此时我发现自己对 RecyclerView 的使用一直太过基本,更深入更强大的功能完全没有涉及,像 ItemDecoration, ItemAnimator, SmoothScroller, OnItemTouchListener, LayoutManager 之类,以及 RecyclerView 重用 view 的原理。网上也有很多对 RecyclerView 使用的讲解博客,要么讲的内容非常少,要么提到了高级功能,但是并没讲代码为什么这样写,每个方法和参数的含义是什么,像[张鸿洋的博客](http://blog.csdn.net/lmj623565791/article/details/45059587){:target="_blank"},也讲了 ItemDecoration 的使用,但是看了仍然云里雾里,只能把他的代码拿来用,并不能根据自己的需求编写自己的 ItemDecoration。

在这个系列中,我将对上述各个部分进行深入研究,目标就是看了这一系列的文章之后,开发者可以清楚快捷的根据自己的需求,编写自己需要的各个高级模块。本系列第一篇就聚焦在:RecyclerView.ItemDecoration。本文涉及到的完整代码可以在[ Github 获取](https://github.com/Piasy/AndroidPlayground/blob/master/effect/RecyclerViewAdvancedDemo){:target="_blank"}。

## TL; DR
+ getItemOffsets 中为 outRect 设置的4个方向的值,将被计算进所有 decoration 的尺寸中,而这个尺寸,被计入了 RecyclerView 每个 item view 的 padding 中
+ 在 onDraw 为 divider 设置绘制范围,并绘制到 canvas 上,而这个绘制范围可以超出在 getItemOffsets 中设置的范围,但由于 decoration 是绘制在 child view 的底下,所以并不可见,但是会存在 overdraw
+ decoration 的 onDraw,child view 的 onDraw,decoration 的 onDrawOver,这三者是依次发生的
+ onDrawOver 是绘制在最上层的,所以它的绘制位置并不受限制

## RecyclerView.ItemDecoration
这个类包含*三个方法* [^three-method]:

+ `onDraw(Canvas c, RecyclerView parent, State state)`
+ `onDrawOver(Canvas c, RecyclerView parent, State state)`
+ `getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)`

## getItemOffsets
[官方样例的 `DividerItemDecoration` ](https://android.googlesource.com/platform/development/+/master/samples/Support7Demos/src/com/example/android/supportv7/widget/decorator/DividerItemDecoration.java#101){:target="_blank"}里面是这样实现的:

``` java
if (mOrientation == VERTICAL_LIST) {
    outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
    outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
```

这个outRect设置的四个值是什么意思呢?先来看看它是在哪里调用的,它在RecyclerView中唯一被调用的地方就是 `getItemDecorInsetsForChild(View child)` 函数。

``` java
Rect getItemDecorInsetsForChild(View child) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    if (!lp.mInsetsDirty) {
        return lp.mDecorInsets;
    }

    final Rect insets = lp.mDecorInsets;
    insets.set(0, 0, 0, 0);
    final int decorCount = mItemDecorations.size();
    for (int i = 0; i < decorCount; i++) {
        mTempRect.set(0, 0, 0, 0);
        mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
        insets.left += mTempRect.left;
        insets.top += mTempRect.top;
        insets.right += mTempRect.right;
        insets.bottom += mTempRect.bottom;
    }
    lp.mInsetsDirty = false;
    return insets;
}
```

可以看到,`getItemOffsets` 函数中设置的值被加到了 `insets` 变量中,并被该函数返回,那么 insets 又是啥呢?

### insets 是啥?
根据[Inset Drawable文档](http://developer.android.com/guide/topics/resources/drawable-resource.html#Inset){:target="_blank"},它的使用场景是:当一个view需要的背景小于它的边界时。例如按钮图标较小,但是我们希望按钮有较大的点击热区,一种做法是使用ImageButton,设置`background="@null"`,把图标资源设置给`src`属性,这样ImageButton可以大于图标,而不会导致图标也跟着拉伸到ImageButton那么大。那么使用Inset drawable也能达到这样的目的。但是相比之下有什么优势呢?`src`属性也能设置selector drawable,所以点击态也不是问题。也许唯一的优势就是更"优雅"吧 :)

回到正题,getItemDecorInsetsForChild 函数中会重置 insets 的值,并重新计算,计算方式就是把所有 ItemDecoration 的 getItemOffsets 中设置的值*累加起来* [^add-together],而这个 insets 实际上是 RecyclerView 的 child 的 LayoutParams 中的一个属性,它会在 `getTopDecorationHeight`, `getBottomDecorationHeight` 等函数中被返回,那么这个 insets 的意义就很明显了,它记录的是所有 ItemDecoration *所需要的* [^space-needed]尺寸的总和。

而在 RecyclerView 的 `measureChild(View child, int widthUsed, int heightUsed)` 函数中,调用了 getItemDecorInsetsForChild,并把它算在了 child view 的 padding 中。

``` java
public void measureChild(View child, int widthUsed, int heightUsed) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();

    final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
    widthUsed += insets.left + insets.right;
    heightUsed += insets.top + insets.bottom;
    final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
            getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
            canScrollHorizontally());
    final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
            getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
            canScrollVertically());
    if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
        child.measure(widthSpec, heightSpec);
    }
}
```

上面这段代码中调用 `getChildMeasureSpec` 函数的第三个参数就是 child view 的 padding,而这个参数就把 insets 的值算进去了。那么现在就可以确认了,**getItemOffsets 中为 outRect 设置的4个方向的值,将被计算进所有 decoration 的尺寸中,而这个尺寸,被用来计算 RecyclerView 每个 item view 的大小(包括 item view 的宽高,padding,以及这个 insets)**。

### PoC
这一步测试主要是对 getItemOffsets 函数传入的 outRect 参数各个值的设置,以证实上述分析的结论。

可以看到,当 left, top, right, bottom 全部设置为50时,RecyclerView 的每个 item view 各个方向的 padding 都增加了,对比各种情况,确实 getItemOffsets 中为 outRect 设置的值都将被计入 RecyclerView 每个 item view 的 padding 中。

## onDraw
先来看看[官方样例的 `DividerItemDecoration` ](https://android.googlesource.com/platform/development/%2B/master/samples/Support7Demos/src/com/example/android/supportv7/widget/decorator/DividerItemDecoration.java#66){:target="_blank"}实现:

``` java
public void drawVertical(Canvas c, RecyclerView parent) {
    final int left = parent.getPaddingLeft();
    final int right = parent.getWidth() - parent.getPaddingRight();
    final int childCount = parent.getChildCount();
    for (int i = 0; i < childCount; i++) {
        final View child = parent.getChildAt(i);
        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                .getLayoutParams();
        final int top = child.getBottom() + params.bottomMargin +
                Math.round(ViewCompat.getTranslationY(child));
        final int bottom = top + mDivider.getIntrinsicHeight();
        mDivider.setBounds(left, top, right, bottom);
        mDivider.draw(c);
    }
}
```

drawVertical 是为纵向的 RecyclerView 绘制 divider,遍历每个 *child view* [^each-child-view] ,把 divider 绘制到 canvas 上,而 `mDivider.setBounds` 则设置了 divider 的绘制范围。其中,left 设置为 parent.getPaddingLeft(),也就是左边是 parent 也就是 RecyclerView 的左边界加上 paddingLeft 之后的位置,而 right 则设置为了 RecyclerView 的右边界减去 paddingRight 之后的位置,那这里左右边界就是 RecyclerView 的*内容区域* [^content-area]了。top 设置为了 child 的 bottom 加上 marginBottom 再加上 translationY,这其实就是 child view 的*下边界* [^child-bottom],bottom 就是 divider 绘制的下边界了,它就是简单地 top 加上 divider 的高度。

### PoC
这一步测试主要是对 onDraw 函数中对 divider 的绘制边界的设置。

可以看到,当我们把 left, right, top [^top-no-translation] 设置得和官方样例一样,bottom 设置为 `top + 25`,注意,这里 getItemOffsets 对 outSets 的设置只有 `bottom = 50`,也就是 decoration 高度为50,我们可以看到,decoration 的上半部分就绘制为黑色了,下半部分没有绘制。而如果设置`top = child.getBottom() + params.bottomMargin - 25`,`bottom = top + 50`,就会发现 child view 的底部出现了 overdraw。所以这里我们可以得出结论:**在 onDraw 为 divider 设置绘制范围,并绘制到 canvas 上,而这个绘制范围可以超出在 getItemOffsets 中设置的范围,但由于 decoration 是绘制在 child view 的底下,所以并不可见,但是会存在 overdraw**。

## onDrawOver
有一点需要注意:**decoration 的 onDraw,child view 的 onDraw,decoration 的 onDrawOver,这三者是依次发生的**。而由于 **onDrawOver 是绘制在最上层的,所以它的绘制位置并不受限制**(当然,decoration 的 onDraw 绘制范围也不受限制,只不过不可见),所以利用 onDrawOver 可以做很多事情,例如为 RecyclerView 整体顶部绘制一个蒙层,或者为特定的 item view 绘制蒙层。这里就不单独进行测试了,请见下一节的整体效果。

## All in together
实现的效果:除了最后一个 item view,底部都有一个高度为25的黑色 divider,为整个 RecyclerView 的顶部绘制了一个渐变的蒙层。效果图如下:

## 小结
+ getItemOffsets 中为 outRect 设置的4个方向的值,将被计算进所有 decoration 的尺寸中,而这个尺寸,被计入了 RecyclerView 每个 item view 的 padding 中
+ 在 onDraw 为 divider 设置绘制范围,并绘制到 canvas 上,而这个绘制范围可以超出在 getItemOffsets 中设置的范围,但由于 decoration 是绘制在 child view 的底下,所以并不可见,但是会存在 overdraw
+ decoration 的 onDraw,child view 的 onDraw,decoration 的 onDrawOver,这三者是依次发生的
+ onDrawOver 是绘制在最上层的,所以它的绘制位置并不受限制

## 完整可用的 divider 代码

2016.10.03 更新

``` java
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class LinearLayoutColorDivider extends RecyclerView.ItemDecoration {
    private final Drawable mDivider;
    private final int mSize;
    private final int mOrientation;

    public LinearLayoutColorDivider(Resources resources, @ColorRes int color,
            @DimenRes int size, int orientation) {
        mDivider = new ColorDrawable(resources.getColor(color));
        mSize = resources.getDimensionPixelSize(size);
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left;
        int right;
        int top;
        int bottom;
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            top = parent.getPaddingTop();
            bottom = parent.getHeight() - parent.getPaddingBottom();
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount - 1; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params =
                        (RecyclerView.LayoutParams) child.getLayoutParams();
                left = child.getRight() + params.rightMargin;
                right = left + mSize;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        } else {
            left = parent.getPaddingLeft();
            right = parent.getWidth() - parent.getPaddingRight();
            final int childCount = parent.getChildCount();
            for (int i = 0; i < childCount - 1; i++) {
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params =
                        (RecyclerView.LayoutParams) child.getLayoutParams();
                top = child.getBottom() + params.bottomMargin;
                bottom = top + mSize;
                mDivider.setBounds(left, top, right, bottom);
                mDivider.draw(c);
            }
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
        if (mOrientation == LinearLayoutManager.HORIZONTAL) {
            outRect.set(0, 0, mSize, 0);
        } else {
            outRect.set(0, 0, 0, mSize);
        }
    }
}
```

## 脚注
[^three-method]: 不算被 Deprecated 的方法
[^add-together]: 把 left, top, right, bottom 4个属性分别累加
[^space-needed]: 也就是在 getItemOffsets 函数中为 outRect 参数设置的4个属性值
[^each-child-view]: child view,并不是 adapter 的每一个 item,只有可见的 item 才会绘制,才是 RecyclerView 的 child view
[^content-area]: 可以类比 CSS 的盒子模型,一个 view 包括 content, padding, margin 三个部分,content 和 padding 加起来就是 view 的尺寸,而 margin 不会增加 view 的尺寸,但是会影响和其他 view 的位置间距,但是安卓的 view 没有 margin 的合并
[^child-bottom]: bottom 就是 content 的下边界加上 paddingBottom,而为了不"吃掉" child view 的底部边距,所以就加上 marginBottom,而 view 还能设置 translation 属性,用于 layout 完成之后的再次偏移,同理,为了不"吃掉"这个偏移,所以也要加上 translationY
[^top-no-translation]: 这里由于并没有对 child view 设置 translation,为了代码简短,就没有减去 translationY,实际上是需要的

redpajama_set_name: RedPajamaGithub


text:
/**
 * Gulpfile
 *
 * This Node script is executed when you run `gulp` or `sails lift`.
 * It's purpose is to load the Gulp tasks in your project's `tasks`
 * folder, and allow you to add and remove tasks as you see fit.
 * For more information on how this works, check out the `README.md`
 * file that was generated in your `tasks` folder. Module gulp-load-plugins is now needed.
 *
 * WARNING:
 * Unless you know what you're doing, you shouldn't change this file.
 * Check out the `tasks` directory instead.
 */
var gulp = require('gulp'),
	plugins = require('gulp-load-plugins')({
						pattern: ['gulp-*', 'merge-*', 'run-*', 'main-*'], // the glob to search for
						replaceString: /\bgulp[\-.]|run[\-.]|merge[\-.]|main[\-.]/, // what to remove from the name of the module when adding it to the context
                        camelizePluginName: true, // if true, transforms hyphenated plugins names to camel case
						lazy: true // whether the plugins should be lazy loaded on demand
					}),
	path = require('path'),
	growl = false;

//module.exports = function(gulp) {

	// Load the include-all library in order to require all of our grunt
	// configurations and task registrations dynamically.
	var includeAll;
	try {
		includeAll = require('include-all');
	} catch (e0) {
		try {
			includeAll = require('sails/node_modules/include-all');
		}
		catch(e1) {
			console.error('Could not find `include-all` module.');
			console.error('Skipping grunt tasks...');
			console.error('To fix this, please run:');
			console.error('npm install include-all --save`');
			console.error();

			gulp.task('default', []);
			return;
		}
	}

	/**
	 * Loads Gulp configuration modules from the specified
	 * relative path. These modules should export a function
	 * that, when run, should either load/configure or register
	 * a Gulp task.
	 */
	function loadTasks(relPath) {
		return includeAll({
			dirname: require('path').resolve(__dirname, relPath),
			filter: /(.+)\.js$/
		}) || {};
	}

	/**
	 * Invokes the function from a Grunt configuration module with
	 * a single argument - the `grunt` object.
	 */
	function invokeConfigFn(tasks) {
		for (var taskName in tasks) {
			if (tasks.hasOwnProperty(taskName)) {
				tasks[taskName](gulp, plugins, growl, path);
			}
		}
	}

	// Load task functions
	var taskConfigurations = loadTasks('./tasks/config'),
		registerDefinitions = loadTasks('./tasks/register');

	// (ensure that a default task exists)
	if (!registerDefinitions.default) {
		registerDefinitions.default = function (gulp) { gulp.task('default', []); };
	}

	// Run task functions to configure Gulp.
	invokeConfigFn(taskConfigurations);
	invokeConfigFn(registerDefinitions);

//};


text:
package eu.meshuga.pudelek.toolbar;

import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import eu.meshuga.pudelek.fs.PudelekVirtualFile;
import eu.meshuga.pudelek.model.Article;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;

public class PudelekWindow {
    private String idPlaceHolder;
    private JPanel mainPanel;
    private JTree tree;

    private DefaultMutableTreeNode top;
    private Project project;

    public PudelekWindow(Project project) {
        this.project = project;

        top = new DefaultMutableTreeNode("Pudelek library");
        TreeModel treeModel = new DefaultTreeModel(top);
        tree.setModel(treeModel);
        tree.getSelectionModel().setSelectionMode
                (TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent me) {
                super.mouseClicked(me);
                if (me.getClickCount() == 2) {
                    createArticle(me);
                }
            }
        });
    }

    private void createArticle(MouseEvent me) {
        TreePath tp = tree.getPathForLocation(me.getX(), me.getY());
        if (tp != null) {
            Object lastPathComponent = tp.getLastPathComponent();
            if (lastPathComponent != null && lastPathComponent instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) lastPathComponent;
                Object userObject = treeNode.getUserObject();
                if (userObject instanceof Article) {
                    Article article = (Article) userObject;
                    FileEditorManager editorManager = FileEditorManager.getInstance(project);
                    PudelekVirtualFile file = new PudelekVirtualFile(article);
                    editorManager.openFile(file, true, true);
                }
            }
        }
    }

    public JPanel getMainPanel() {
        return mainPanel;
    }

    public void setData(List<Article> articles) {
        for (Article article : articles) {
            top.add(new DefaultMutableTreeNode(article));
        }
    }
}


text:
<?php

namespace Weathermap\Integrations;

/**
 * Some day we'll migrate to using this for the managed maps, and the various mutators below will be
 * methods here instead.
 */
class ManagedMap
{

    public $id;
    public $sortOrder;
    public $groupId;
    public $active;
    public $configFile;
    public $imageFile;
    public $htmlFile;
    public $titleCache;
    public $fileHash;
    public $warnCount;
    public $config;
    public $thumbWidth;
    public $thumbHeight;
    public $schedule;
    public $archiving;

    public function checkAccess($userId)
    {
    }

    public function delete()
    {
    }

    public function update()
    {
    }

    public function enable()
    {
    }

    public function disable()
    {
    }

    public function enableArchiving()
    {
    }

    public function disableArchiving()
    {
    }

    public function updateTitleCache()
    {
    }

    public function setDebug($newState)
    {
    }

    public function setGroup($groupId)
    {
    }

    public function addAccess($userId)
    {
    }

    public function removeAccess($userId)
    {
    }
}


text:
from numpy.testing import assert_approx_equal
from UliEngineering.Electronics.OpAmp import *
from UliEngineering.EngineerIO import auto_format
import unittest

class TestOpAmp(unittest.TestCase):
    def test_summing_amplifier_noninv(self):
        v = summing_amplifier_noninv("2.5V", "500mV", "1kΩ", "1kΩ", "1kΩ", "1kΩ")
        assert_approx_equal(v, 3.0)
        v = summing_amplifier_noninv(2.5, 0.5, 1e3, 1e3, 300, 1e3)
        assert_approx_equal(v, 6.5)
        self.assertEqual(auto_format(summing_amplifier_noninv, 2.5, 0.5, 1e3, 1e3, 300, 1e3), "6.50 V")


text:
package domain

//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

// RegistrantProfile is a nested struct in domain response
type RegistrantProfile struct {
	RegistrantProfileId      int    `json:"RegistrantProfileId" xml:"RegistrantProfileId"`
	CreateTime               string `json:"CreateTime" xml:"CreateTime"`
	UpdateTime               string `json:"UpdateTime" xml:"UpdateTime"`
	DefaultRegistrantProfile bool   `json:"DefaultRegistrantProfile" xml:"DefaultRegistrantProfile"`
	RegistrantName           string `json:"RegistrantName" xml:"RegistrantName"`
	RegistrantOrganization   string `json:"RegistrantOrganization" xml:"RegistrantOrganization"`
	Country                  string `json:"Country" xml:"Country"`
	Province                 string `json:"Province" xml:"Province"`
	City                     string `json:"City" xml:"City"`
	Address                  string `json:"Address" xml:"Address"`
	Email                    string `json:"Email" xml:"Email"`
	PostalCode               string `json:"PostalCode" xml:"PostalCode"`
	TelArea                  string `json:"TelArea" xml:"TelArea"`
	Telephone                string `json:"Telephone" xml:"Telephone"`
	TelExt                   string `json:"TelExt" xml:"TelExt"`
	EmailVerificationStatus  int    `json:"EmailVerificationStatus" xml:"EmailVerificationStatus"`
	ZhRegistrantName         string `json:"ZhRegistrantName" xml:"ZhRegistrantName"`
	ZhRegistrantOrganization string `json:"ZhRegistrantOrganization" xml:"ZhRegistrantOrganization"`
	ZhProvince               string `json:"ZhProvince" xml:"ZhProvince"`
	ZhCity                   string `json:"ZhCity" xml:"ZhCity"`
	ZhAddress                string `json:"ZhAddress" xml:"ZhAddress"`
	RegistrantType           string `json:"RegistrantType" xml:"RegistrantType"`
	RealNameStatus           string `json:"RealNameStatus" xml:"RealNameStatus"`
}


text:
#-*- coding:utf-8 -*-
import urllib
import urllib2
import cookielib
import time
import json
import traceback
import poster
from poster.encode import multipart_encode, MultipartParam

cj=cookielib.LWPCookieJar()
opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
urllib2.install_opener(opener)

paras={'username':'xjmjyqxy@sina.com','pwd':'b3ca2251f5f48978f9e9c32aeb5fde26','imgcode':'','f':'json'}
req1=urllib2.Request('https://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN\\',urllib.urlencode(paras))
req1.add_header('Accept','*/*')
req1.add_header('Accept-Encoding','gzip, deflate')
req1.add_header('Accept-Language','zh-CN,zh;q=0.8')
req1.add_header('Connection','keep-alive')
req1.add_header('Content-Length','81')
req1.add_header('Content-Type','application/x-www-form-urlencoded; charset=UTF-8')
req1.add_header('Host','mp.weixin.qq.com')
req1.add_header('Origin','https://mp.weixin.qq.com')
req1.add_header('Referer','https://mp.weixin.qq.com/cgi-bin/loginpage?t=wxm2-login&lang=zh_CN')
req1.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36')
req1.add_header('X-Requested-With','XMLHttpRequest')
ret=urllib2.urlopen(req1)
retread=ret.read()
print retread
token=json.loads(retread)
print token

tokenNum='2134338630'
ticket_id='AboutLoveCollege'
ticket='3e7154c5d869211a97c1dc605bcc437aaf2a2742'
img=r'/home/pythonDir/test2.jpg'
requestUrl='https://mp.weixin.qq.com/cgi-bin/filetransfer?action=upload_material' \
			'&f=json&scene=1&writetype=doublewrite&groupid=1&ticket_id=AboutLoveCollege&ticket=3e7154c5d869211a97c1dc605bcc437aaf2a2742&svr_time=1448268300&token=2134338630&lang=zh_CN'

#buld post body data
boundary = '------WebKitFormBoundarysaBYvQAFs3QrkSu2'
print '%s' %(boundary)
data = []
data.append('%s' % boundary)

data.append('Content-Disposition: form-data; name="%s"\r\n' % 'id')
data.append('WU_FILE_0')
data.append('%s' % boundary)

data.append('Content-Disposition: form-data; name="%s"\r\n' % 'name')
data.append('test2.jpg')
data.append('%s' % boundary)

data.append('Content-Disposition: form-data; name="%s"\r\n' % 'type')
data.append('image/jpeg')
data.append('%s' % boundary)

data.append('Content-Disposition: form-data; name="%s"\r\n' % 'lastModifiedDate')
data.append('Mon Nov 23 2015 15:18:47 GMT+0800')
data.append('%s' % boundary)

data.append('Content-Disposition: form-data; name="%s"\r\n' % 'size')
data.append('199415')
data.append('%s' % boundary)

fr=open(img,'rb')
data.append('Content-Disposition: form-data; name="%s"; filename="test2.jpg"' % 'file')
data.append('Content-Type: %s\r\n' % 'image/jpeg')
data.append(fr.read())
fr.close()
data.append('--%s--\r\n' % boundary)
http_body='\r\n'.join(data)
print type(http_body)

try:
	req=urllib2.Request(requestUrl, data=http_body)
	req.add_header('Accept','*/*')
	req.add_header('Accept-Encoding','gzip, deflate')
	req.add_header('Accept-Language','zh-CN,zh;q=0.8')
	req.add_header('Connection','keep-alive')
	req.add_header('Content-Type','multipart/form-data; boundary=----WebKitFormBoundarylQm5tNRV1bhEjDpb')
	req.add_header('Host','mp.weixin.qq.com')
	req.add_header('Origin','https://mp.weixin.qq.com')
	req.add_header('Referer','https://mp.weixin.qq.com/cgi-bin/filepage?type=2&begin=0&count=12&t=media/img_list&group_id=1&token=2134338630&lang=zh_CN')
	req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36')
    #post data to server
	resp = urllib2.urlopen(req, timeout=5)
    #get response
	qrcont=resp.read()
	print qrcont
except Exception,e:
	print e


text:
<?xml version="1.0" encoding="UTF-8"?>
<change-set xmlns='http://drools.org/drools-5.0/change-set'
            xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'
            xs:schemaLocation='http://drools.org/drools-5.0 change-set.xsd'>
    <add>
        <resource source='classpath:META-INF/rules/drl/ValidateAgeRule.drl' type='DRL'/>
        <resource source='classpath:META-INF/rules/drl/ValidateApplicationRule.drl' type='DRL'/>
    </add>

</change-set>


text:
package org.apereo.cas.services;

import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.util.RegexUtils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.ObjectUtils;

import javax.persistence.PostLoad;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * This is {@link DefaultRegisteredServiceAccessStrategy}
 * that allows the following rules:
 *

 *
A service may be disallowed to use CAS for authentication

 *
A service may be disallowed to take part in CAS single sign-on such that
 * presentation of credentials would always be required.

 *
A service may be prohibited from receiving a service ticket
 * if the existing principal attributes don't contain the required attributes
 * that otherwise grant access to the service.

 *

 *
 * @author Misagh Moayyed
 * @since 4.1
 */
@Slf4j
@ToString
@Getter
@EqualsAndHashCode
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DefaultRegisteredServiceAccessStrategy implements RegisteredServiceAccessStrategy {

    private static final long serialVersionUID = 1245279151345635245L;

    /**
     * The sorting/execution order of this strategy.
     */
    protected int order;

    /**
     * Is the service allowed at all?
     */
    protected boolean enabled = true;

    /**
     * Is the service allowed to use SSO?
     */
    protected boolean ssoEnabled = true;

    /**
     * The Unauthorized redirect url.
     */
    protected URI unauthorizedRedirectUrl;

    /**
     * The delegated authn policy.
     */
    protected RegisteredServiceDelegatedAuthenticationPolicy delegatedAuthenticationPolicy =
        new DefaultRegisteredServiceDelegatedAuthenticationPolicy();

    /**
     * Defines the attribute aggregation behavior when checking for required attributes.
     * Default requires that all attributes be present and match the principal's.
     */
    protected boolean requireAllAttributes = true;

    /**
     * Collection of required attributes
     * for this service to proceed.
     */
    protected Map<String, Set<String>> requiredAttributes = new HashMap<>();

    /**
     * Collection of attributes
     * that will be rejected which will cause this
     * policy to refuse access.
     */
    protected Map<String, Set<String>> rejectedAttributes = new HashMap<>();

    /**
     * Indicates whether matching on required attribute values
     * should be done in a case-insensitive manner.
     */
    protected boolean caseInsensitive;

    public DefaultRegisteredServiceAccessStrategy() {
        this(true, true);
    }

    public DefaultRegisteredServiceAccessStrategy(final boolean enabled, final boolean ssoEnabled) {
        this.enabled = enabled;
        this.ssoEnabled = ssoEnabled;
    }

    public DefaultRegisteredServiceAccessStrategy(final Map<String, Set<String>> requiredAttributes, final Map<String, Set<String>> rejectedAttributes) {
        this();
        this.requiredAttributes = requiredAttributes;
        this.rejectedAttributes = rejectedAttributes;
    }

    public DefaultRegisteredServiceAccessStrategy(final Map<String, Set<String>> requiredAttributes) {
        this();
        this.requiredAttributes = requiredAttributes;
    }

    /**
     * Post load.
     */
    @PostLoad
    public void postLoad() {
        this.delegatedAuthenticationPolicy = ObjectUtils.defaultIfNull(this.delegatedAuthenticationPolicy,
            new DefaultRegisteredServiceDelegatedAuthenticationPolicy());
    }

    /**
     * Expose underlying attributes for auditing purposes.
     *
     * @return required attributes
     */
    @Override
    public Map<String, Set<String>> getRequiredAttributes() {
        return requiredAttributes;
    }

    @JsonIgnore
    @Override
    public boolean isServiceAccessAllowedForSso() {
        if (!this.ssoEnabled) {
            LOGGER.trace("Service is not authorized to participate in SSO.");
            return false;
        }
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isServiceAccessAllowed() {
        if (!this.enabled) {
            LOGGER.trace("Service is not enabled in service registry.");
            return false;
        }
        return true;
    }

    @JsonIgnore
    @Override
    public void setServiceAccessAllowed(final boolean value) {
        this.enabled = value;
    }

    @Override
    public boolean doPrincipalAttributesAllowServiceAccess(final String principal, final Map<String, Object> principalAttributes) {
        if (this.rejectedAttributes.isEmpty() && this.requiredAttributes.isEmpty()) {
            LOGGER.trace("Skipping access strategy policy, since no attributes rules are defined");
            return true;
        }
        if (!enoughAttributesAvailableToProcess(principal, principalAttributes)) {
            LOGGER.debug("Access is denied. There are not enough attributes available to satisfy requirements");
            return false;
        }
        if (doRejectedAttributesRefusePrincipalAccess(principalAttributes)) {
            LOGGER.debug("Access is denied. The principal carries attributes that would reject service access");
            return false;
        }
        if (!doRequiredAttributesAllowPrincipalAccess(principalAttributes, this.requiredAttributes)) {
            LOGGER.debug("Access is denied. The principal does not have the required attributes [{}] specified by this strategy", this.requiredAttributes);
            return false;
        }
        return true;
    }

    /**
     * Do required attributes allow principal access boolean.
     *
     * @param principalAttributes the principal attributes
     * @param requiredAttributes  the required attributes
     * @return the boolean
     */
    protected boolean doRequiredAttributesAllowPrincipalAccess(final Map<String, Object> principalAttributes, final Map<String, Set<String>> requiredAttributes) {
        LOGGER.debug("These required attributes [{}] are examined against [{}] before service can proceed.", requiredAttributes, principalAttributes);
        if (requiredAttributes.isEmpty()) {
            return true;
        }
        return requiredAttributesFoundInMap(principalAttributes, requiredAttributes);
    }

    /**
     * Do rejected attributes refuse principal access boolean.
     *
     * @param principalAttributes the principal attributes
     * @return the boolean
     */
    protected boolean doRejectedAttributesRefusePrincipalAccess(final Map<String, Object> principalAttributes) {
        LOGGER.debug("These rejected attributes [{}] are examined against [{}] before service can proceed.", rejectedAttributes, principalAttributes);
        if (rejectedAttributes.isEmpty()) {
            return false;
        }
        return requiredAttributesFoundInMap(principalAttributes, rejectedAttributes);
    }

    /**
     * Enough attributes available to process? Check collection sizes and determine
     * if we have enough data to move on.
     *
     * @param principal           the principal
     * @param principalAttributes the principal attributes
     * @return true /false
     */
    protected boolean enoughAttributesAvailableToProcess(final String principal, final Map<String, Object> principalAttributes) {
        if (!enoughRequiredAttributesAvailableToProcess(principalAttributes, this.requiredAttributes)) {
            return false;
        }
        if (principalAttributes.size() < this.rejectedAttributes.size()) {
            LOGGER.debug("The size of the principal attributes that are [{}] does not match defined rejected attributes, "
                + "which means the principal is not carrying enough data to grant authorization", principalAttributes);
            return false;
        }
        return true;
    }

    /**
     * Enough required attributes available to process? Check collection sizes and determine
     * if we have enough data to move on.
     *
     * @param principalAttributes the principal attributes
     * @param requiredAttributes  the required attributes
     * @return true /false
     */
    protected boolean enoughRequiredAttributesAvailableToProcess(final Map<String, Object> principalAttributes, final Map<String, Set<String>> requiredAttributes) {
        if (principalAttributes.isEmpty() && !requiredAttributes.isEmpty()) {
            LOGGER.debug("No principal attributes are found to satisfy defined attribute requirements");
            return false;
        }
        if (principalAttributes.size() < requiredAttributes.size()) {
            LOGGER.debug("The size of the principal attributes that are [{}] does not match defined required attributes, "
                + "which indicates the principal is not carrying enough data to grant authorization", principalAttributes);
            return false;
        }
        return true;
    }

    /**
     * Check whether required attributes are found in the given map.
     *
     * @param principalAttributes the principal attributes
     * @param requiredAttributes  the attributes
     * @return the boolean
     */
    protected boolean requiredAttributesFoundInMap(final Map<String, Object> principalAttributes, final Map<String, Set<String>> requiredAttributes) {
        val difference = requiredAttributes.keySet().stream().filter(a -> principalAttributes.keySet().contains(a)).collect(Collectors.toSet());
        LOGGER.debug("Difference of checking required attributes: [{}]", difference);
        if (this.requireAllAttributes && difference.size() < requiredAttributes.size()) {
            return false;
        }
        if (this.requireAllAttributes) {
            return difference.stream().allMatch(key -> requiredAttributeFound(key, principalAttributes, requiredAttributes));
        }
        return difference.stream().anyMatch(key -> requiredAttributeFound(key, principalAttributes, requiredAttributes));
    }

    private boolean requiredAttributeFound(final String attributeName, final Map<String, Object> principalAttributes, final Map<String, Set<String>> requiredAttributes) {
        val values = requiredAttributes.get(attributeName);
        val availableValues = CollectionUtils.toCollection(principalAttributes.get(attributeName));
        val pattern = RegexUtils.concatenate(values, this.caseInsensitive);
        LOGGER.debug("Checking [{}] against [{}] with pattern [{}] for attribute [{}]", values, availableValues, pattern, attributeName);
        if (pattern != RegexUtils.MATCH_NOTHING_PATTERN) {
            return availableValues.stream().map(Object::toString).anyMatch(pattern.asPredicate());
        }
        return availableValues.stream().anyMatch(values::contains);
    }
}


text:
<?php

require 'config.php';

header('Content-Type: text/plain;charset=UTF-8');

$url = isset($_GET['url']) ? urldecode(trim($_GET['url'])) : '';
$s = isset($_GET['s']) ? true : false;
$domain = "http://". $_SERVER['HTTP_HOST'] ."/";

if (in_array($url, array('', 'about:blank', 'undefined', 'http://localhost/'))) {
	die('Enter a URL.');
}

// If the URL is already a short URL on this domain, don't re-shorten it
if (strpos($url, $domain) === 0) {
	die($url);
}

$db = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE);
$db->set_charset('utf8mb4');

$url = $db->real_escape_string($url);

function randString($length = 8) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

$result = $db->query('SELECT slug FROM redirect WHERE url = "' . $url . '" LIMIT 1');
if ($result && $result->num_rows > 0) { // If there's already a short URL for this URL
	if ($s) {
		die($result->fetch_object()->slug);
	} else {
		die($domain . $result->fetch_object()->slug);
	}
} else {
	$slug = randString(SLUG_SIZE);
	$is_unique = false;

	while (!$is_unique) {
    	$result = $db->query('SELECT slug FROM redirect WHERE slug = "'. $slug .'" LIMIT 1');
    	if ($result->num_rows == 0) {   // if you don't get a result, then you're good
			$is_unique = true;
			if ($db->query('INSERT INTO redirect (slug, url, date, hits) VALUES ("' . $slug . '", "' . $url . '", NOW(), 0)')) {
				header('HTTP/1.1 201 Created');
				if ($s) {
					echo $slug;
				} else {
					echo $domain . $slug;
				}
				$db->query('OPTIMIZE TABLE `redirect`');
			}
		} else {                   // if you DO get a result, keep trying
        	$slug = randString(SLUG_SIZE);
		}
	}
}
?>


text:
<?php

namespace Biz\Article\Dao;

use Codeages\Biz\Framework\Dao\GeneralDaoInterface;

interface ArticleDao extends GeneralDaoInterface
{
    public function getPrevious($categoryId, $createdTime);

    public function getNext($categoryId, $createdTime);

    public function findAll();

    public function findByIds(array $ids);

    public function searchByCategoryIds(array $categoryIds, $start, $limit);

    public function countByCategoryIds(array $categoryIds);

    public function waveArticle($id, $field, $diff);
}


text:
import os

import IECore

moduleSearchPath = IECore.SearchPath( os.environ["PYTHONPATH"] )
if moduleSearchPath.find( "IECoreUSD" ) :
	# Import IECoreUSD so that we get the USD SceneInterface registered,
	# providing USD functionality to both the SceneReader and SceneWriter.
	import IECoreUSD


text:
#pragma once
#include <aws/mobileanalytics/MobileAnalytics_EXPORTS.h>
#include <aws/mobileanalytics/MobileAnalyticsErrors.h>
#include <aws/core/client/AWSError.h>
#include <aws/core/client/ClientConfiguration.h>
#include <aws/core/client/AWSClient.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/core/utils/json/JsonSerializer.h>
#include <aws/core/NoResult.h>
#include <aws/core/client/AsyncCallerContext.h>
#include <aws/core/http/HttpTypes.h>
#include <future>
#include <functional>

namespace Aws
{

namespace Http
{
  class HttpClient;
  class HttpClientFactory;
} // namespace Http

namespace Utils
{
  template< typename R, typename E> class Outcome;

namespace Threading
{
  class Executor;
} // namespace Threading

namespace Json
{
  class JsonValue;
} // namespace Json
} // namespace Utils

namespace Auth
{
  class AWSCredentials;
  class AWSCredentialsProvider;
} // namespace Auth

namespace Client
{
  class RetryStrategy;
} // namespace Client

namespace MobileAnalytics
{

namespace Model
{
        class PutEventsRequest;

        typedef Aws::Utils::Outcome<NoResult, Aws::Client::AWSError<MobileAnalyticsErrors>> PutEventsOutcome;

        typedef std::future<PutEventsOutcome> PutEventsOutcomeCallable;
} // namespace Model

  class MobileAnalyticsClient;

    typedef std::function<void(const MobileAnalyticsClient*, const Model::PutEventsRequest&, const Model::PutEventsOutcome&, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) > PutEventsResponseReceivedHandler;

  /**
   *
Amazon Mobile Analytics is a service for collecting, visualizing, and
   * understanding app usage data at scale.

   */
  class AWS_MOBILEANALYTICS_API MobileAnalyticsClient : public Aws::Client::AWSJsonClient
  {
    public:
      typedef Aws::Client::AWSJsonClient BASECLASS;

       /**
        * Initializes client to use DefaultCredentialProviderChain, with default http client factory, and optional client config. If client config
        * is not specified, it will be initialized to default values.
        */
        MobileAnalyticsClient(const Client::ClientConfiguration& clientConfiguration = Client::ClientConfiguration());

       /**
        * Initializes client to use SimpleAWSCredentialsProvider, with default http client factory, and optional client config. If client config
        * is not specified, it will be initialized to default values.
        */
        MobileAnalyticsClient(const Auth::AWSCredentials& credentials, const Client::ClientConfiguration& clientConfiguration = Client::ClientConfiguration());

       /**
        * Initializes client to use specified credentials provider with specified client config. If http client factory is not supplied,
        * the default http client factory will be used
        */
        MobileAnalyticsClient(const std::shared_ptr<Auth::AWSCredentialsProvider>& credentialsProvider,
            const Client::ClientConfiguration& clientConfiguration = Client::ClientConfiguration());

        virtual ~MobileAnalyticsClient();

        /**
         *
The PutEvents operation records one or more events. You can have up to 1,500
         * unique custom events per app, any combination of up to 40 attributes and metrics
         * per custom event, and any number of attribute or metric values.

         */
        virtual Model::PutEventsOutcome PutEvents(const Model::PutEventsRequest& request) const;

        /**
         *
The PutEvents operation records one or more events. You can have up to 1,500
         * unique custom events per app, any combination of up to 40 attributes and metrics
         * per custom event, and any number of attribute or metric values.

         *
         * returns a future to the operation so that it can be executed in parallel to other requests.
         */
        virtual Model::PutEventsOutcomeCallable PutEventsCallable(const Model::PutEventsRequest& request) const;

        /**
         *
The PutEvents operation records one or more events. You can have up to 1,500
         * unique custom events per app, any combination of up to 40 attributes and metrics
         * per custom event, and any number of attribute or metric values.

         *
         * Queues the request into a thread executor and triggers associated callback when operation has finished.
         */
        virtual void PutEventsAsync(const Model::PutEventsRequest& request, const PutEventsResponseReceivedHandler& handler, const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr) const;

    private:
      void init(const Client::ClientConfiguration& clientConfiguration);

        /**Async helpers**/
        void PutEventsAsyncHelper(const Model::PutEventsRequest& request, const PutEventsResponseReceivedHandler& handler, const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context) const;

      Aws::String m_uri;
      std::shared_ptr<Aws::Utils::Threading::Executor> m_executor;
  };

} // namespace MobileAnalytics
} // namespace Aws


text:
<title>  Aswin van den Berg  </title>

 Aswin van den Berg

 Graduate Student

aswin@cs.cornell.edu

Office: 4153, Upson Hall
        phone: (607) 255-1372
Home:
	 19 Skyacre Drive.
	Ithaca, NY 14850
	phone: (607) 257-2694

I am interested in:

 Programming languages

 Program Transformation

 The  FUN transformation system

My personal life.

 Family Album

 Anneke's WWW Page

 Swing Dancing

 The Swing Dance Server

  The Jean & Aswin Swing Dance Page

  DeeJayer's Guide for ISDN Record Hops

  My Personal Swing Pictures

 De Nederlandse Club van Cornell or

The Cornell Dutch Club

I am a graduate student at
Cornell


text:
<group>

Index

Prev

Next

VL_FIGASPECT(A) sets the current figure aspect ratio to A. It
leaves the length of the shortest side unaltered. Both the
screen and paper positions are affected.

See also: VL_PRINTSIZE(), VL_HELP().

</group>


text:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

	<modelVersion>4.0.0</modelVersion>

	<groupId>jeffsbox.net</groupId>
	<artifactId>hello-fx</artifactId>
	<name>hello-fx</name>

	<packaging>jar</packaging>
	<version>1.0-SNAPSHOT</version>

	<organization>
		<name>Hello FX</name>
	</organization>

	<build>

		<finalName>hello-fx</finalName>

		<plugins>
			<plugin>
				<groupId>com.zenjava</groupId>
				<artifactId>javafx-maven-plugin</artifactId>
				<version>2.0</version>
				<configuration>
					<mainClass>jeffsbox.hellofx.HelloFx</mainClass>
					<vendor>jeffsbox.net</vendor>

					<!-- only required if signing the jar file -->
					<keyStoreAlias>example-user</keyStoreAlias>
					<keyStorePassword>example-password</keyStorePassword>
					<allPermissions>true</allPermissions>
				</configuration>
                <executions>
                    <execution>
                        <phase>verify</phase>
                        <goals>
                            <goal>native</goal>
                        </goals>
                    </execution>

                </executions>
			</plugin>
		</plugins>
	</build>

	<dependencies>

	</dependencies>

</project>


text:
Thanks For Your Time!

    <button type="submit" class="btn btn-danger">Submit</button>


text:
var Utils = require("../../utils")

module.exports = (function() {
  var Query = function(client, callee, options) {
    var self = this

    this.client = client
    this.callee = callee
    this.options = Utils._.extend({
      logging: console.log,
      plain: false,
      raw: false
    }, options || {})

    if(this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    if(this.options.logging == console.log) {
      // using just console.log will break in node < 0.6
      this.options.logging = function(s) { console.log(s) }
    }

    this.bindClientFunction = function(err) { onFailure.call(self, err) }
  }
  Utils._.extend(Query.prototype, require("../query").prototype)

  Query.prototype.run = function(sql) {
    var self = this

    this.sql = sql

    bindClient.call(this)

    if(this.options.logging !== false)
      this.options.logging('Executing: ' + this.sql)

    this.client.query(this.sql, function(err, results, fields) {
      self.emit('sql', self.sql)
      err ? onFailure.call(self, err) : onSuccess.call(self, results, fields)
    }).setMaxListeners(100)

    return this
  }

  //private

  var bindClient = function() {
    this.client.on('error', this.bindClientFunction)
  }

  var unbindClient = function() {
    this.client.removeListener('error', this.bindClientFunction)
  }

  var onSuccess = function(results, fields) {
    var result = this.callee
      , self   = this

    // add the inserted row id to the instance
    if (this.callee && (this.sql.indexOf('INSERT INTO') == 0) && (results.hasOwnProperty('insertId')))
      this.callee[this.callee.__factory.autoIncrementField] = results.insertId

    if (this.sql.indexOf('SELECT') == 0) {
      // transform results into real model instances
      // return the first real model instance if options.plain is set (e.g. Model.find)
      if(this.options.raw) {
        result = results
      } else {
        result = results.map(function(result) {
          return self.callee.build(result, { isNewRecord: false })
        })
      }

      if(this.options.plain)
        result = (result.length == 0) ? null : result[0]
    } else if(this.sql.indexOf('SHOW TABLES') == 0) {
      result = Utils._.flatten(results.map(function(resultSet) {
        return Utils._.values(resultSet)
      }))
    } else if((this.sql.indexOf('SHOW') == 0) || (this.sql.indexOf('DESCRIBE') == 0)) {
      result = results
    }

    unbindClient.call(this)
    this.emit('success', result)

  }

  var onFailure = function(err) {
    unbindClient.call(this)
    this.emit('error', err, this.callee)
  }

  return Query
})()