Пример #1
0
static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
                           apr_int32_t wanted)
{ 
    finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK 
                    | APR_FINFO_OWNER | APR_FINFO_PROT;

    finfo->protection = apr_unix_mode2perms(info->st_mode);
    finfo->filetype = filetype_from_mode(info->st_mode);
    finfo->user = info->st_uid;
    finfo->group = info->st_gid;
    finfo->size = info->st_size;
    finfo->inode = info->st_ino;
    finfo->device = info->st_dev;
    finfo->nlink = info->st_nlink;

    apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
    apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
    apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);

#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
#ifdef DEV_BSIZE
    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
#else
    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
#endif
    finfo->valid |= APR_FINFO_CSIZE;
#endif
}
Пример #2
0
static void fill_out_finfo(apr_finfo_t *finfo, struct_stat *info,
                           apr_int32_t wanted)
{ 
    finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK
                 | APR_FINFO_OWNER | APR_FINFO_PROT;
    finfo->protection = apr_unix_mode2perms(info->st_mode);
    finfo->filetype = filetype_from_mode(info->st_mode);
    finfo->user = info->st_uid;
    finfo->group = info->st_gid;
    finfo->size = info->st_size;
    finfo->device = info->st_dev;
    finfo->nlink = info->st_nlink;

    /* Check for overflow if storing a 64-bit st_ino in a 32-bit
     * apr_ino_t for LFS builds: */
    if (sizeof(apr_ino_t) >= sizeof(info->st_ino)
        || (apr_ino_t)info->st_ino == info->st_ino) {
        finfo->inode = info->st_ino;
    } else {
        finfo->valid &= ~APR_FINFO_INODE;
    }

    apr_time_ansi_put(&finfo->atime, info->st_atime);
#ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
    finfo->atime += info->st_atim.tv_nsec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
    finfo->atime += info->st_atimensec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_ATIME_N)
    finfo->ctime += info->st_atime_n / APR_TIME_C(1000);
#endif

    apr_time_ansi_put(&finfo->mtime, info->st_mtime);
#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
    finfo->mtime += info->st_mtim.tv_nsec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
    finfo->mtime += info->st_mtimensec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_MTIME_N)
    finfo->ctime += info->st_mtime_n / APR_TIME_C(1000);
#endif

    apr_time_ansi_put(&finfo->ctime, info->st_ctime);
#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
    finfo->ctime += info->st_ctim.tv_nsec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
    finfo->ctime += info->st_ctimensec / APR_TIME_C(1000);
#elif defined(HAVE_STRUCT_STAT_ST_CTIME_N)
    finfo->ctime += info->st_ctime_n / APR_TIME_C(1000);
#endif

#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
#ifdef DEV_BSIZE
    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)DEV_BSIZE;
#else
    finfo->csize = (apr_off_t)info->st_blocks * (apr_off_t)512;
#endif
    finfo->valid |= APR_FINFO_CSIZE;
#endif
}
Пример #3
0
static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
{
    mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
    struct sqlite_conn *conn;
    sqlite3_stmt *stmt;
    int ret;
    mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,1);
    if (GC_HAS_ERROR(ctx)) {
        if(tile->tileset->read_only || !tile->tileset->source) {
            mapcache_sqlite_release_conn(ctx, pc);
            return MAPCACHE_FAILURE;
        } else {
            /* not an error in this case, as the db file may not have been created yet */
            ctx->clear_errors(ctx);
            mapcache_sqlite_release_conn(ctx, pc);
            return MAPCACHE_CACHE_MISS;
        }
    }
    conn = SQLITE_CONN(pc);
    stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
    if(!stmt) {
        sqlite3_prepare(conn->handle, cache->get_stmt.sql, -1, &conn->prepared_statements[GET_TILE_STMT_IDX], NULL);
        stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
    }
    cache->bind_stmt(ctx, stmt, cache, tile);
    do {
        ret = sqlite3_step(stmt);
        if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
            ctx->set_error(ctx, 500, "sqlite backend failed on get: %s", sqlite3_errmsg(conn->handle));
            sqlite3_reset(stmt);
            mapcache_sqlite_release_conn(ctx, pc);
            return MAPCACHE_FAILURE;
        }
    } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
    if (ret == SQLITE_DONE) {
        sqlite3_reset(stmt);
        mapcache_sqlite_release_conn(ctx, pc);
        return MAPCACHE_CACHE_MISS;
    } else {
        const void *blob = sqlite3_column_blob(stmt, 0);
        int size = sqlite3_column_bytes(stmt, 0);
        if(size>0 && ((char*)blob)[0] == '#') {
            tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,blob,&tile->nodata);
        } else {
            tile->encoded_data = mapcache_buffer_create(size, ctx->pool);
            memcpy(tile->encoded_data->buf, blob, size);
            tile->encoded_data->size = size;
        }
        if (sqlite3_column_count(stmt) > 1) {
            time_t mtime = sqlite3_column_int64(stmt, 1);
            apr_time_ansi_put(&(tile->mtime), mtime);
        }
        sqlite3_reset(stmt);
        mapcache_sqlite_release_conn(ctx, pc);
        return MAPCACHE_SUCCESS;
    }
}
Пример #4
0
static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_tile *tile) {
   mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)tile->tileset->cache;
   sqlite3 *handle;
   sqlite3_stmt *stmt;
   int ret;
   if(cache->hitstats) {
      handle = _get_conn(ctx,tile,0);
   } else {
      handle = _get_conn(ctx,tile,1);
   }
   if(GC_HAS_ERROR(ctx)) {
      sqlite3_close(handle);
      return MAPCACHE_FAILURE;
   }
   sqlite3_prepare(handle,cache->get_stmt.sql,-1,&stmt,NULL);
   _bind_sqlite_params(ctx,stmt,tile);
   do {
      ret = sqlite3_step(stmt);
      if(ret!=SQLITE_DONE && ret != SQLITE_ROW && ret!=SQLITE_BUSY && ret !=SQLITE_LOCKED) {
         ctx->set_error(ctx,500,"sqlite backend failed on get: %s",sqlite3_errmsg(handle));
         sqlite3_finalize(stmt);
         sqlite3_close(handle);
         return MAPCACHE_FAILURE;
      }
   } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
   if(ret == SQLITE_DONE) {
      sqlite3_finalize(stmt);
      sqlite3_close(handle);
      return MAPCACHE_CACHE_MISS;
   } else {
      const void *blob = sqlite3_column_blob(stmt,0);
      int size = sqlite3_column_bytes(stmt, 0);
      tile->encoded_data = mapcache_buffer_create(size,ctx->pool);
      memcpy(tile->encoded_data->buf, blob,size);
      tile->encoded_data->size = size;
      if(sqlite3_column_count(stmt) > 1) {
         time_t mtime = sqlite3_column_int64(stmt, 1);
         apr_time_ansi_put(&(tile->mtime),mtime);
      }
      sqlite3_finalize(stmt);

      /* update the hitstats if we're configured for that */
      if(cache->hitstats) {
         sqlite3_stmt *hitstmt;
         sqlite3_prepare(handle,cache->hitstat_stmt.sql,-1,&hitstmt,NULL);
         _bind_sqlite_params(ctx,stmt,tile);
         sqlite3_step(hitstmt); /* we ignore the return value , TODO?*/
         sqlite3_finalize(hitstmt);
      }

      sqlite3_close(handle);
      return MAPCACHE_SUCCESS;
   }
}
Пример #5
0
static void fill_out_finfo(apr_finfo_t *finfo, struct stat *info,
                           apr_int32_t wanted)
{ 
    finfo->valid = APR_FINFO_MIN | APR_FINFO_IDENT | APR_FINFO_NLINK 
                    | APR_FINFO_OWNER | APR_FINFO_PROT;
    finfo->protection = apr_unix_mode2perms(info->st_mode);
    finfo->filetype = filetype_from_mode(info->st_mode);
    finfo->user = info->st_uid;
    finfo->group = info->st_gid;
    finfo->size = info->st_size;
    finfo->inode = info->st_ino;
    finfo->device = info->st_dev;
    finfo->nlink = info->st_nlink;
    apr_time_ansi_put(&finfo->atime, info->st_atime.tv_sec);
    apr_time_ansi_put(&finfo->mtime, info->st_mtime.tv_sec);
    apr_time_ansi_put(&finfo->ctime, info->st_ctime.tv_sec);
    /* ### needs to be revisited  
     * if (wanted & APR_FINFO_CSIZE) {
     *   finfo->csize = info->st_blocks * 512;
     *   finfo->valid |= APR_FINFO_CSIZE;
     * }
     */
}
Пример #6
0
  bool
  TransferRevisionFromWindow(int no, bool enabled, svn::Revision & revision)
  {
    struct RevisionControls * revCtrls = &revCtrlsArray[no];

    // first we enable/disable controls according to settings
    revCtrls->radioUseRevision->Enable(enabled);
    bool useRevision = enabled && revCtrls->radioUseRevision->GetValue();
    revCtrls->checkUseLatest->Enable(useRevision);
    bool useLatest = useRevision && revCtrls->checkUseLatest->GetValue();
    revCtrls->textRevision->Enable(enabled && !useLatest);

    revCtrls->radioUseDate->Enable(enabled);
    bool useDate = enabled && revCtrls->radioUseDate->GetValue();
    revCtrls->datePicker->Enable(useDate);

    bool enablePath = enabled && enableUrl;
    revCtrls->checkUsePath->Enable(enablePath);
    bool usePath = enablePath && revCtrls->checkUsePath->GetValue();
    revCtrls->comboPath->Enable(usePath);

    // now transfer values
    bool isValid = true;
    if (revCtrls->radioUseRevision->GetValue())
    {
      if (revCtrls->checkUseLatest->GetValue())
        revision = svn::Revision(svn::Revision::HEAD);
      else
      {
        svn_revnum_t revnum;
        bool isValidRevision = ParseRevision(
                                 revCtrls->textRevision->GetValue(), revnum);
        revision = svn::Revision(revnum);

        if (enabled && !isValidRevision)
          isValid = false;
      }
    }
    else
    {
      apr_time_t time=0;
      apr_time_ansi_put(
        &time, revCtrls->datePicker->GetValue().GetTicks());
      svn::DateTime datetime(time);
      revision =  svn::Revision(datetime);
    }

    return isValid;
  }
Пример #7
0
static void
badge_emit_timestamp(request_rec * r, const char * fromto, time_t value,
	int rdonly, badge_timestamp * ts, int errflags, int yearerr,
	int montherr, int dayerr, int hourerr, int minerr, int secerr)

{
	apr_time_exp_t exptime;
	apr_time_t aprtime;
	apr_size_t len;
	char buf[100];

	/**
	***	Emit a timestamp in an html table row, either as normal text
	***		from `value' or as input fields with values from `*ts'
	***		according to the `rdonly' flag.
	**/

	ap_rvputs(r, "<tr><td>Valid ", fromto, "</td><td>", NULL);

	if (rdonly) {
		apr_time_ansi_put(&aprtime, value);
		apr_time_exp_tz(&exptime, aprtime, 0);
		apr_strftime(buf, &len, sizeof buf - 1, "%c", &exptime);
		buf[len] = '\0';
		ap_rvputs(r, ap_escape_html(r->pool, buf), NULL);
		}
	else {
		ap_rvputs(r, "Y<input type=\"text\" name=\"", fromto,
		    "-year\" value=\"",
		    ts->year? ap_escape_html(r->pool, ts->year): "",
		    "\"", badge_arg_err(errflags & yearerr), " />", NULL);
		badge_emit_range_select(r, "M", fromto,
		    "month", 1, 12, ts->month, errflags & montherr);
		badge_emit_range_select(r, "D", fromto,
		    "day", 1, 31, ts->day, errflags & dayerr);
		badge_emit_range_select(r, "H", fromto,
		    "hour", 0, 23, ts->hour, errflags & hourerr);
		badge_emit_range_select(r, "M", fromto,
		    "min", 0, 59, ts->min, errflags & minerr);
		badge_emit_range_select(r, "S", fromto,
		    "sec", 0, 59, ts->sec, errflags & secerr);
		}

	ap_rvputs(r, "</td></tr>\n", NULL);
}
static void ngx_http_mapcache_write_response(mapcache_context *ctx, ngx_http_request_t *r,
      mapcache_http_response *response) {
   if(response->mtime) {
      time_t  if_modified_since;
      if(r->headers_in.if_modified_since) {
         if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
               r->headers_in.if_modified_since->value.len);
         if (if_modified_since != NGX_ERROR) {
            apr_time_t apr_if_m_s;
            apr_time_ansi_put	(	&apr_if_m_s, if_modified_since);
            if(apr_if_m_s<response->mtime) {
               r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
               ngx_http_send_header(r);
               return;
            }
         }
      }
      char *datestr;
      datestr = apr_palloc(ctx->pool, APR_RFC822_DATE_LEN);
      apr_rfc822_date(datestr, response->mtime);
      apr_table_setn(response->headers,"Last-Modified",datestr);
   }
   if(response->headers && !apr_is_empty_table(response->headers)) {
      const apr_array_header_t *elts = apr_table_elts(response->headers);
      int i;
      for(i=0;i<elts->nelts;i++) {
         apr_table_entry_t entry = APR_ARRAY_IDX(elts,i,apr_table_entry_t);
         if(!strcasecmp(entry.key,"Content-Type")) {
            r->headers_out.content_type.len = strlen(entry.val);
            r->headers_out.content_type.data = (u_char*)entry.val;
         } else {
            ngx_table_elt_t   *h;
            h = ngx_list_push(&r->headers_out.headers);
            if (h == NULL) {
               return;
            }
            h->key.len = strlen(entry.key) ;
            h->key.data = (u_char*)entry.key ;
            h->value.len = strlen(entry.val) ;
            h->value.data = (u_char*)entry.val ;
            h->hash = 1;
         }
      }
   }
   if(response->data) {
      r->headers_out.content_length_n = response->data->size;
   }
   int rc;
   r->headers_out.status = response->code;
   rc = ngx_http_send_header(r);
   if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
      return;
   }

   if(response->data) {
      ngx_buf_t    *b;
      ngx_chain_t   out;
      b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
      if (b == NULL) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 
               "Failed to allocate response buffer.");
         return;
      }

      b->pos = ngx_pcalloc(r->pool,response->data->size);
      memcpy(b->pos,response->data->buf,response->data->size);
      b->last = b->pos + response->data->size;
      b->memory = 1;
      b->last_buf = 1;
      b->flush = 1;
      out.buf = b;
      out.next = NULL;
      ngx_http_output_filter(r, &out);
   }

}
Пример #9
0
/*
 * process an assertion using the hosted verifier.
 *
 * TODO: local verification
 */
VerifyResult processAssertion(request_rec *r, const char *verifier_url,
                              const char *assertion)
{
  VerifyResult res = apr_pcalloc(r->pool, sizeof(struct _VerifyResult));
  json_tokener *tok = json_tokener_new();
  json_object *jobj = NULL;
  enum json_tokener_error jerr;

  char *assertionResult =
    verifyAssertionRemote(r, verifier_url, (char *) assertion);

  if (assertionResult) {
    jobj =
      json_tokener_parse_ex(tok, assertionResult, strlen(assertionResult));
    jerr = json_tokener_get_error(tok);

    if (json_tokener_success != jerr) {

      res->errorResponse = apr_psprintf(r->pool, jsonErrorResponse,
                                        "malformed payload",
                                        json_tokener_error_desc(jerr));
      json_tokener_free(tok);
      return res;
    }

    ap_log_rerror(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r,
                  ERRTAG
                  "Assertion (parsed) recieved is : %s",
                  json_object_to_json_string(jobj));
  }
  else {
    // XXX: verifyAssertionRemote should return specific error message.
    res->errorResponse = apr_psprintf(r->pool, jsonErrorResponse,
                                      "communication error",
                                      "can't contact verification server");
    return res;
  }

  struct json_object_iterator it = json_object_iter_begin(jobj);
  struct json_object_iterator itEnd = json_object_iter_end(jobj);
  const char *reason = NULL;
  const char *status = "unknown";
  int success = 0;

  while (!json_object_iter_equal(&it, &itEnd)) {
    const char *key = json_object_iter_peek_name(&it);
    json_object *val = json_object_iter_peek_value(&it);

    if (strncmp("email", key, 6) == 0) {
      res->verifiedEmail = apr_pstrdup(r->pool, json_object_get_string(val));
    }
    else if (strncmp("issuer", key, 7) == 0) {
      res->identityIssuer = apr_pstrdup(r->pool, json_object_get_string(val));
    }
    else if (strncmp("audience", key, 9) == 0) {
      res->audience = apr_pstrdup(r->pool, json_object_get_string(val));
    }
    else if (strncmp("expires", key, 8) == 0) {
      apr_time_ansi_put(&res->expires, json_object_get_int64(val) / 1000);
    }
    else if (strncmp("reason", key, 7) == 0) {
      reason = json_object_get_string(val);
    }
    else if (strncmp("status", key, 7) == 0) {
      status = json_object_get_string(val);
      if (strncmp("okay", status, 5) == 0) {
        success = 1;
      }
    }
    json_object_iter_next(&it);
  }

  json_tokener_free(tok);

  // XXX: This is bad, doesn't catch multiple missing bits
  if (!res->verifiedEmail) {
    res->errorResponse = apr_pstrdup(r->pool, "Missing e-mail in assertion");
  }
  if (!res->identityIssuer) {
    res->errorResponse = apr_pstrdup(r->pool, "Missing issuer in assertion");
  }
  if (res->audience
      && strncmp(res->audience, r->server->server_hostname,
                 strlen(r->server->server_hostname)) != 0) {
    res->errorResponse =
      apr_psprintf(r->pool, "Audience %s doesn't match %s", res->audience,
                   r->server->server_hostname);
  }

  apr_time_t now = apr_time_now();
  if (res->expires && res->expires <= now) {
    char exp_time[APR_RFC822_DATE_LEN];
    apr_rfc822_date(exp_time, res->expires);
    res->errorResponse =
      apr_psprintf(r->pool, "Assertion expired on %s", exp_time);
  }

  if (!success) {
    if (reason) {
      res->errorResponse = apr_pstrdup(r->pool, reason);
    }
    else {
      res->errorResponse =
        apr_psprintf(r->pool, "Assertion failed with status '%s'", status);
    }
  }

  return res;
}
Пример #10
0
static int verify_auth_token(cdn_conf *cfg, request_rec *r)
{
  char *last, *uri_base, *query_string, *vox_timestamp_decoded, *forwarded_list;
  const char *token, *vox_sig, *vox_timestamp, *ip;
  apr_table_t *params;
  struct tm ctime;
  long remote_east_of_utc, local_east_of_utc;
  time_t raw;
  apr_time_t vox_ts;

  /* URI must have '?' in it */
  if (!strchr(r->unparsed_uri, '?')) {
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                 "verify_auth_token: URI %s missing '?'",
                 r->unparsed_uri);
    return DECLINED;
  }

  /* The base URL of the request, without the query arguments. */
  uri_base = apr_strtok(r->unparsed_uri, "?", &last);
  uri_base = apr_pstrcat(r->pool, cfg->to_server, uri_base, NULL);

  /* All of the query string parameters */
  query_string = apr_strtok(NULL, "", &last);

  /* Convert the query string to a table */
  params = apr_table_make(r->pool, 1);
  qs_to_table(query_string, params, r->pool);

  /* Get vox_sig from the parameters */
  vox_sig = apr_table_get(params, "vox_sig");
  if (!vox_sig) {
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                 "verify_auth_token: vox_sig missing");
    return DECLINED;
  }

  /* Remove vox_sig from params so it can be regenerated */
  apr_table_unset(params, "vox_sig");

  /* Get vox_timestamp from the parameters */
  vox_timestamp = apr_table_get(params, "vox_timestamp");
  if (!vox_timestamp) {
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                 "verify_auth_token: vox_timestamp missing");
    return DECLINED;
  }

  /* Parse vox_timestamp, which must be in ISO 8601 format */
  memset(&ctime, 0, sizeof(struct tm));
  vox_timestamp_decoded = apr_pstrdup(r->pool, vox_timestamp);
  ap_unescape_url(vox_timestamp_decoded);
  strptime(vox_timestamp_decoded, "%FT%T%z", &ctime);
  remote_east_of_utc = ctime.tm_gmtoff; /* have to save because mktime overwrites */
  ctime.tm_isdst = -1; /* mktime needs to auto-determine DST */
  raw = mktime(&ctime);
  local_east_of_utc = ctime.tm_gmtoff;
  apr_time_ansi_put(&vox_ts, raw - remote_east_of_utc + local_east_of_utc);

  /* verify vox_timestamp is in the future; if not, authentication has failed */
  if (vox_ts < apr_time_now()) {
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
                 "Authenticated URL %s is expired (expired %ld, now %ld, raw %ld, "
                 "remote_east_of_utc %ld, local_east_of_utc %ld)",
                 r->uri,
                 apr_time_sec(vox_ts),
                 apr_time_sec(apr_time_now()),
                 raw, remote_east_of_utc, local_east_of_utc);
    return DECLINED;
  }

  /* compute the correct auth token using the connection's remote IP */
  token = make_auth_token(r, r->connection->remote_ip, uri_base, params,
                          cfg->auth_key);

  ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
               "verify_auth_token: comparing vox_sig %s with token %s "
               "using remote_ip %s", vox_sig, token, r->connection->remote_ip);

  if (!strncasecmp(token, vox_sig, SHA1_STR_SIZE))
    return OK;

  /* if that IP didn't work, try every IP in the X-Forwarded-For header in order */
  forwarded_list = (char *)apr_table_get(r->headers_in, "X-Forwarded-For");
  if (forwarded_list && (forwarded_list = apr_pstrdup(r->pool, forwarded_list))) {
    last = NULL;
    if (!(ip = apr_strtok(forwarded_list, ", \t", &last)))
      return DECLINED;
    do {
      token = make_auth_token(r, ip, uri_base, params, cfg->auth_key);

      ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
                   "verify_auth_token: comparing vox_sig %s with token %s "
                   "using X-Forwarded-For ip %s", vox_sig, token, ip);

      if (!strncasecmp(token, vox_sig, SHA1_STR_SIZE))
        return OK;
    } while ((ip = apr_strtok(NULL, ", \t", &last)));
  }

  return DECLINED;
}