<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/* cacheurl.cc - Plugin to modify the URL used as a cache key for certain
 * requests, without modifying the URL used for actually fetching data from
 * the origin server.
 */

#include <stdio.h>
#include <string.h>

#include "ts/ts.h"
#include "ts/remap.h"
#include "ink_defs.h"

#include <memory>
#include <string>
#include <vector>

#ifdef HAVE_PCRE_PCRE_H
#include <pcre/pcre.h>
#else
#include <pcre.h>
#endif

#define TOKENCOUNT 10
#define OVECOUNT 30
#define PLUGIN_NAME "cacheurl"

struct regex_info {
  pcre *re;          /* Compiled regular expression */
  int tokcount;      /* Token count */
  char *pattern;     /* Pattern string */
  char *replacement; /* Replacement string */
  int *tokens;       /* Array of $x token values */
  int *tokenoffset;  /* Array of $x token offsets */
};

struct pr_list {
  std::vector<regex_info *> pr;

  pr_list() {}

  ~pr_list()
  {
    for (std::vector<regex_info *>::iterator info = this->pr.begin(); info != this->pr.end(); ++info) {
      TSfree((*info)->tokens);
      TSfree((*info)->tokenoffset);
      pcre_free((*info)->re);
      TSfree(*info);
    }
  }
};

static int
regex_substitute(char **buf, char *str, regex_info *info)
{
  int matchcount;
  int ovector[OVECOUNT]; /* Locations of matches in regex */

  int replacelen; /* length of replacement string */
  int i;
  int offset;
  int prev;

  /* Perform the regex matching */
  matchcount = pcre_exec(info->re, NULL, str, strlen(str), 0, 0, ovector, OVECOUNT);
  if (matchcount < 0) {
    switch (matchcount) {
    case PCRE_ERROR_NOMATCH:
      break;
    default:
      TSError("[%s] Matching error: %d\n", PLUGIN_NAME, matchcount);
      break;
    }
    return 0;
  }

  /* Verify the replacement has the right number of matching groups */
  for (i = 0; i < info->tokcount; i++) {
    if (info->tokens[i] >= matchcount) {
      TSError("[%s] Invalid reference int replacement: $%d\n", PLUGIN_NAME, info->tokens[i]);
      return 0;
    }
  }

  /* malloc the replacement string */
  replacelen = strlen(info->replacement);
  replacelen -= info->tokcount * 2; /* Subtract $1, $2 etc... */
  for (i = 0; i < info->tokcount; i++) {
    replacelen += (ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
  }
  replacelen++; /* Null terminator */
  *buf = (char *)TSmalloc(replacelen);

  /* perform string replacement */
  offset = 0; /* Where we are adding new data in the string */
  prev = 0;
  for (i = 0; i < info->tokcount; i++) {
    memcpy(*buf + offset, info->replacement + prev, info->tokenoffset[i] - prev);
    offset += (info->tokenoffset[i] - prev);
    prev = info->tokenoffset[i] + 2;

    memcpy(*buf + offset, str + ovector[info->tokens[i] * 2], ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
    offset += (ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
  }
  memcpy(*buf + offset, info->replacement + prev, strlen(info->replacement) - prev);
  offset += strlen(info->replacement) - prev;
  (*buf)[offset] = 0; /* Null termination */
  return 1;
}

static int
regex_compile(regex_info **buf, char *pattern, char *replacement)
{
  const char *reerror; /* Error string from pcre */
  int reerroffset;     /* Offset where any pcre error occured */

  int tokcount;
  int *tokens = NULL;
  int *tokenoffset = NULL;

  int status = 1; /* Status (return value) of the function */
  regex_info *info = (regex_info *)TSmalloc(sizeof(regex_info));

  /* Precompile the regular expression */
  info->re = pcre_compile(pattern, 0, &reerror, &reerroffset, NULL);
  if (!info->re) {
    TSError("[%s] Compilation of regex '%s' failed at char %d: %s\n", PLUGIN_NAME, pattern, reerroffset, reerror);
    status = 0;
  }

  /* Precalculate the location of $X tokens in the replacement */
  tokcount = 0;
  if (status) {
    tokens = (int *)TSmalloc(sizeof(int) * TOKENCOUNT);
    tokenoffset = (int *)TSmalloc(sizeof(int) * TOKENCOUNT);
    for (unsigned i = 0; i < strlen(replacement); i++) {
      if (replacement[i] == '$') {
        if (tokcount >= TOKENCOUNT) {
          TSError("[%s] Error: too many tokens in replacement "
                  "string: %s\n",
                  PLUGIN_NAME, replacement);
          status = 0;
          break;
        } else if (replacement[i + 1] < '0' || replacement[i + 1] > '9') {
          TSError("[%s] Error: Invalid replacement token $%c in %s: should be $0 - $9\n", PLUGIN_NAME, replacement[i + 1],
                  replacement);
          status = 0;
          break;
        } else {
          /* Store the location of the replacement */
          /* Convert '0' to 0 */
          tokens[tokcount] = replacement[i + 1] - '0';
          tokenoffset[tokcount] = i;
          tokcount++;
          /* Skip the next char */
          i++;
        }
      }
    }
  }

  if (status) {
    /* Everything went OK */
    info->tokcount = tokcount;
    info->tokens = tokens;
    info->tokenoffset = tokenoffset;

    info->pattern = TSstrdup(pattern);
    info->replacement = TSstrdup(replacement);

    *buf = info;
  } else {
    /* Something went wrong, clean up */
    TSfree(tokens);
    TSfree(tokenoffset);
    if (info->re)
      pcre_free(info->re);
    TSfree(info);
  }
  return status;
}

static pr_list *
load_config_file(const char *config_file)
{
  char buffer[1024];
  std::string path;
  TSFile fh;
  std::auto_ptr<pr_list> prl(new pr_list());

  /* locations in a config file line, end of line, split start, split end */
  char *eol, *spstart, *spend;
  int lineno = 0;
  int retval;
  regex_info *info = 0;

  if (config_file == NULL) {
    /* Default config file of plugins/cacheurl.config */
    path = TSPluginDirGet();
    path += "/cacheurl.config";
  } else if (*config_file != '/') {
    // Relative paths are relative to the config directory
    path = TSConfigDirGet();
    path += "/";
    path += config_file;
  } else {
    // Absolute path ...
    path = config_file;
  }

  TSDebug(PLUGIN_NAME, "Opening config file: %s", path.c_str());
  fh = TSfopen(path.c_str(), "r");

  if (!fh) {
    TSError("[%s] Unable to open %s. No patterns will be loaded\n", PLUGIN_NAME, path.c_str());
    return NULL;
  }

  while (TSfgets(fh, buffer, sizeof(buffer) - 1)) {
    lineno++;
    if (*buffer == '#') {
      /* # Comments, only at line beginning */
      continue;
    }
    eol = strstr(buffer, "\n");
    if (eol) {
      *eol = 0; /* Terminate string at newline */
    } else {
      /* Malformed line - skip */
      continue;
    }
    /* Split line into two parts based on whitespace */
    /* Find first whitespace */
    spstart = strstr(buffer, " ");
    if (!spstart) {
      spstart = strstr(buffer, "\t");
    }
    if (!spstart) {
      TSError("[%s] ERROR: Invalid format on line %d. Skipping\n", PLUGIN_NAME, lineno);
      TSfclose(fh);
      return NULL;
    }
    /* Find part of the line after any whitespace */
    spend = spstart + 1;
    while (*spend == ' ' || *spend == '\t') {
      spend++;
    }
    if (*spend == 0) {
      /* We reached the end of the string without any non-whitepace */
      TSError("[%s] ERROR: Invalid format on line %d. Skipping\n", PLUGIN_NAME, lineno);
      TSfclose(fh);
      return NULL;
    }

    *spstart = 0;
    /* We have the pattern/replacement, now do precompilation.
     * buffer is the first part of the line. spend is the second part just
     * after the whitespace */
    TSDebug(PLUGIN_NAME, "Adding pattern/replacement pair: '%s' -> '%s'", buffer, spend);
    retval = regex_compile(&info, buffer, spend);
    if (!retval) {
      TSError("[%s] Error precompiling regex/replacement. Skipping.\n", PLUGIN_NAME);
      TSfclose(fh);
      return NULL;
    }

    prl->pr.push_back(info);
  }
  TSfclose(fh);

  if (prl->pr.empty()) {
    TSError("[%s] No regular expressions loaded.\n", PLUGIN_NAME);
  }

  TSDebug(PLUGIN_NAME, "loaded %u regexes", (unsigned)prl->pr.size());
  return prl.release();
}

static int
rewrite_cacheurl(pr_list *prl, TSHttpTxn txnp)
{
  int ok = 1;
  char *newurl = 0;
  int retval;
  char *url;
  int url_length;

  url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_length);
  if (!url) {
    TSError("[%s] couldn't retrieve request url\n", PLUGIN_NAME);
    ok = 0;
  }

  if (ok) {
    for (std::vector<regex_info *>::iterator info = prl->pr.begin(); info != prl->pr.end(); ++info) {
      retval = regex_substitute(&newurl, url, *info);
      if (retval) {
        /* Successful match/substitution */
        break;
      }
    }
    if (newurl) {
      TSDebug(PLUGIN_NAME, "Rewriting cache URL for %s to %s", url, newurl);
      if (TSCacheUrlSet(txnp, newurl, strlen(newurl)) != TS_SUCCESS) {
        TSError("[%s] Unable to modify cache url from "
                "%s to %s\n",
                PLUGIN_NAME, url, newurl);
        ok = 0;
      }
    }
  }

  /* Clean up */
  if (url)
    TSfree(url);
  if (newurl)
    TSfree(newurl);

  return ok;
}

static int
handle_hook(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = (TSHttpTxn)edata;
  pr_list *prl;
  int ok = 1;

  prl = (pr_list *)TSContDataGet(contp);

  switch (event) {
  case TS_EVENT_HTTP_READ_REQUEST_HDR:
    ok = rewrite_cacheurl(prl, txnp);
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    break;
  default:
    TSAssert(!"Unexpected event");
    ok = 0;
    break;
  }

  return ok;
}

/* Generic error message function for errors in plugin initialization */
static void
initialization_error(const char *msg)
{
  TSError("[%s] %s\n", PLUGIN_NAME, msg);
  TSError("[%s] Unable to initialize plugin (disabled).\n", PLUGIN_NAME);
}

TSReturnCode
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
{
  if (!api_info) {
    strncpy(errbuf, "[tsremap_init] Invalid TSRemapInterface argument", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->size < sizeof(TSRemapInterface)) {
    strncpy(errbuf, "[tsremap_init] Incorrect size of TSRemapInterface structure", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->tsremap_version < TSREMAP_VERSION) {
    snprintf(errbuf, errbuf_size - 1, "[tsremap_init] Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
             (api_info->tsremap_version & 0xffff));
    return TS_ERROR;
  }

  TSDebug(PLUGIN_NAME, "remap plugin is successfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf ATS_UNUSED, int errbuf_size ATS_UNUSED)
{
  *ih = load_config_file(argc > 2 ? argv[2] : NULL);
  return (NULL == *ih) ? TS_ERROR : TS_SUCCESS;
}

void
TSRemapDeleteInstance(void *ih)
{
  pr_list *prl = (pr_list *)ih;

  TSDebug(PLUGIN_NAME, "Deleting remap instance");

  delete prl;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri ATS_UNUSED)
{
  int ok;

  ok = rewrite_cacheurl((pr_list *)ih, rh);
  if (ok) {
    return TSREMAP_NO_REMAP;
  } else {
    return TSREMAP_ERROR;
  }
}

void
TSPluginInit(int argc, const char *argv[])
{
  TSPluginRegistrationInfo info;
  TSCont contp;
  pr_list *prl;

  info.plugin_name = (char *)PLUGIN_NAME;
  info.vendor_name = (char *)"Apache Software Foundation";
  info.support_email = (char *)"dev@trafficserver.apache.org";

  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
    initialization_error("Plugin registration failed.");
    return;
  }

  prl = load_config_file(argc > 1 ? argv[1] : NULL);
  if (prl) {
    contp = TSContCreate((TSEventFunc)handle_hook, NULL);
    /* Store the pattern replacement list in the continuation */
    TSContDataSet(contp, prl);
    TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, contp);
  }
}
<commit_msg>[TS-3531]: Ignore blank lines in the plugin config file<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/* cacheurl.cc - Plugin to modify the URL used as a cache key for certain
 * requests, without modifying the URL used for actually fetching data from
 * the origin server.
 */

#include <stdio.h>
#include <string.h>

#include "ts/ts.h"
#include "ts/remap.h"
#include "ink_defs.h"

#include <memory>
#include <string>
#include <vector>

#ifdef HAVE_PCRE_PCRE_H
#include <pcre/pcre.h>
#else
#include <pcre.h>
#endif

#define TOKENCOUNT 10
#define OVECOUNT 30
#define PLUGIN_NAME "cacheurl"

struct regex_info {
  pcre *re;          /* Compiled regular expression */
  int tokcount;      /* Token count */
  char *pattern;     /* Pattern string */
  char *replacement; /* Replacement string */
  int *tokens;       /* Array of $x token values */
  int *tokenoffset;  /* Array of $x token offsets */
};

struct pr_list {
  std::vector<regex_info *> pr;

  pr_list() {}

  ~pr_list()
  {
    for (std::vector<regex_info *>::iterator info = this->pr.begin(); info != this->pr.end(); ++info) {
      TSfree((*info)->tokens);
      TSfree((*info)->tokenoffset);
      pcre_free((*info)->re);
      TSfree(*info);
    }
  }
};

static int
regex_substitute(char **buf, char *str, regex_info *info)
{
  int matchcount;
  int ovector[OVECOUNT]; /* Locations of matches in regex */

  int replacelen; /* length of replacement string */
  int i;
  int offset;
  int prev;

  /* Perform the regex matching */
  matchcount = pcre_exec(info->re, NULL, str, strlen(str), 0, 0, ovector, OVECOUNT);
  if (matchcount < 0) {
    switch (matchcount) {
    case PCRE_ERROR_NOMATCH:
      break;
    default:
      TSError("[%s] Matching error: %d\n", PLUGIN_NAME, matchcount);
      break;
    }
    return 0;
  }

  /* Verify the replacement has the right number of matching groups */
  for (i = 0; i < info->tokcount; i++) {
    if (info->tokens[i] >= matchcount) {
      TSError("[%s] Invalid reference int replacement: $%d\n", PLUGIN_NAME, info->tokens[i]);
      return 0;
    }
  }

  /* malloc the replacement string */
  replacelen = strlen(info->replacement);
  replacelen -= info->tokcount * 2; /* Subtract $1, $2 etc... */
  for (i = 0; i < info->tokcount; i++) {
    replacelen += (ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
  }
  replacelen++; /* Null terminator */
  *buf = (char *)TSmalloc(replacelen);

  /* perform string replacement */
  offset = 0; /* Where we are adding new data in the string */
  prev = 0;
  for (i = 0; i < info->tokcount; i++) {
    memcpy(*buf + offset, info->replacement + prev, info->tokenoffset[i] - prev);
    offset += (info->tokenoffset[i] - prev);
    prev = info->tokenoffset[i] + 2;

    memcpy(*buf + offset, str + ovector[info->tokens[i] * 2], ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
    offset += (ovector[info->tokens[i] * 2 + 1] - ovector[info->tokens[i] * 2]);
  }
  memcpy(*buf + offset, info->replacement + prev, strlen(info->replacement) - prev);
  offset += strlen(info->replacement) - prev;
  (*buf)[offset] = 0; /* Null termination */
  return 1;
}

static int
regex_compile(regex_info **buf, char *pattern, char *replacement)
{
  const char *reerror; /* Error string from pcre */
  int reerroffset;     /* Offset where any pcre error occured */

  int tokcount;
  int *tokens = NULL;
  int *tokenoffset = NULL;

  int status = 1; /* Status (return value) of the function */
  regex_info *info = (regex_info *)TSmalloc(sizeof(regex_info));

  /* Precompile the regular expression */
  info->re = pcre_compile(pattern, 0, &reerror, &reerroffset, NULL);
  if (!info->re) {
    TSError("[%s] Compilation of regex '%s' failed at char %d: %s\n", PLUGIN_NAME, pattern, reerroffset, reerror);
    status = 0;
  }

  /* Precalculate the location of $X tokens in the replacement */
  tokcount = 0;
  if (status) {
    tokens = (int *)TSmalloc(sizeof(int) * TOKENCOUNT);
    tokenoffset = (int *)TSmalloc(sizeof(int) * TOKENCOUNT);
    for (unsigned i = 0; i < strlen(replacement); i++) {
      if (replacement[i] == '$') {
        if (tokcount >= TOKENCOUNT) {
          TSError("[%s] Error: too many tokens in replacement "
                  "string: %s\n",
                  PLUGIN_NAME, replacement);
          status = 0;
          break;
        } else if (replacement[i + 1] < '0' || replacement[i + 1] > '9') {
          TSError("[%s] Error: Invalid replacement token $%c in %s: should be $0 - $9\n", PLUGIN_NAME, replacement[i + 1],
                  replacement);
          status = 0;
          break;
        } else {
          /* Store the location of the replacement */
          /* Convert '0' to 0 */
          tokens[tokcount] = replacement[i + 1] - '0';
          tokenoffset[tokcount] = i;
          tokcount++;
          /* Skip the next char */
          i++;
        }
      }
    }
  }

  if (status) {
    /* Everything went OK */
    info->tokcount = tokcount;
    info->tokens = tokens;
    info->tokenoffset = tokenoffset;

    info->pattern = TSstrdup(pattern);
    info->replacement = TSstrdup(replacement);

    *buf = info;
  } else {
    /* Something went wrong, clean up */
    TSfree(tokens);
    TSfree(tokenoffset);
    if (info->re)
      pcre_free(info->re);
    TSfree(info);
  }
  return status;
}

static pr_list *
load_config_file(const char *config_file)
{
  char buffer[1024];
  std::string path;
  TSFile fh;
  std::auto_ptr<pr_list> prl(new pr_list());

  /* locations in a config file line, end of line, split start, split end */
  char *eol, *spstart, *spend;
  int lineno = 0;
  int retval;
  regex_info *info = 0;

  if (config_file == NULL) {
    /* Default config file of plugins/cacheurl.config */
    path = TSPluginDirGet();
    path += "/cacheurl.config";
  } else if (*config_file != '/') {
    // Relative paths are relative to the config directory
    path = TSConfigDirGet();
    path += "/";
    path += config_file;
  } else {
    // Absolute path ...
    path = config_file;
  }

  TSDebug(PLUGIN_NAME, "Opening config file: %s", path.c_str());
  fh = TSfopen(path.c_str(), "r");

  if (!fh) {
    TSError("[%s] Unable to open %s. No patterns will be loaded\n", PLUGIN_NAME, path.c_str());
    return NULL;
  }

  while (TSfgets(fh, buffer, sizeof(buffer) - 1)) {
    lineno++;

    // make sure line was not bigger than buffer
    if ((eol = strchr(buffer, '\n')) == NULL && (eol = strstr(buffer, "\r\n")) == NULL) {
      // Malformed line - skip
      TSError("%s: config line too long, did not get a good line in cfg, skipping, line: %s", PLUGIN_NAME, buffer);
      memset(buffer, 0, sizeof(buffer));
      continue;
    } else {
      *eol = 0;
    }
    // make sure line has something useful on it
    // or allow # Comments, only at line beginning
    if (eol - buffer < 2 || buffer[0] == '#') {
      memset(buffer, 0, sizeof(buffer));
      continue;
    }

    /* Split line into two parts based on whitespace */
    /* Find first whitespace */
    spstart = strstr(buffer, " ");
    if (!spstart) {
      spstart = strstr(buffer, "\t");
    }
    if (!spstart) {
      TSError("[%s] ERROR: Invalid format on line %d. Skipping\n", PLUGIN_NAME, lineno);
      TSfclose(fh);
      return NULL;
    }
    /* Find part of the line after any whitespace */
    spend = spstart + 1;
    while (*spend == ' ' || *spend == '\t') {
      spend++;
    }
    if (*spend == 0) {
      /* We reached the end of the string without any non-whitepace */
      TSError("[%s] ERROR: Invalid format on line %d. Skipping\n", PLUGIN_NAME, lineno);
      TSfclose(fh);
      return NULL;
    }

    *spstart = 0;
    /* We have the pattern/replacement, now do precompilation.
     * buffer is the first part of the line. spend is the second part just
     * after the whitespace */
    TSDebug(PLUGIN_NAME, "Adding pattern/replacement pair: '%s' -> '%s'", buffer, spend);
    retval = regex_compile(&info, buffer, spend);
    if (!retval) {
      TSError("[%s] Error precompiling regex/replacement. Skipping.\n", PLUGIN_NAME);
      TSfclose(fh);
      return NULL;
    }

    prl->pr.push_back(info);
  }
  TSfclose(fh);

  if (prl->pr.empty()) {
    TSError("[%s] No regular expressions loaded.\n", PLUGIN_NAME);
  }

  TSDebug(PLUGIN_NAME, "loaded %u regexes", (unsigned)prl->pr.size());
  return prl.release();
}

static int
rewrite_cacheurl(pr_list *prl, TSHttpTxn txnp)
{
  int ok = 1;
  char *newurl = 0;
  int retval;
  char *url;
  int url_length;

  url = TSHttpTxnEffectiveUrlStringGet(txnp, &url_length);
  if (!url) {
    TSError("[%s] couldn't retrieve request url\n", PLUGIN_NAME);
    ok = 0;
  }

  if (ok) {
    for (std::vector<regex_info *>::iterator info = prl->pr.begin(); info != prl->pr.end(); ++info) {
      retval = regex_substitute(&newurl, url, *info);
      if (retval) {
        /* Successful match/substitution */
        break;
      }
    }
    if (newurl) {
      TSDebug(PLUGIN_NAME, "Rewriting cache URL for %s to %s", url, newurl);
      if (TSCacheUrlSet(txnp, newurl, strlen(newurl)) != TS_SUCCESS) {
        TSError("[%s] Unable to modify cache url from "
                "%s to %s\n",
                PLUGIN_NAME, url, newurl);
        ok = 0;
      }
    }
  }

  /* Clean up */
  if (url)
    TSfree(url);
  if (newurl)
    TSfree(newurl);

  return ok;
}

static int
handle_hook(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = (TSHttpTxn)edata;
  pr_list *prl;
  int ok = 1;

  prl = (pr_list *)TSContDataGet(contp);

  switch (event) {
  case TS_EVENT_HTTP_READ_REQUEST_HDR:
    ok = rewrite_cacheurl(prl, txnp);
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    break;
  default:
    TSAssert(!"Unexpected event");
    ok = 0;
    break;
  }

  return ok;
}

/* Generic error message function for errors in plugin initialization */
static void
initialization_error(const char *msg)
{
  TSError("[%s] %s\n", PLUGIN_NAME, msg);
  TSError("[%s] Unable to initialize plugin (disabled).\n", PLUGIN_NAME);
}

TSReturnCode
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
{
  if (!api_info) {
    strncpy(errbuf, "[tsremap_init] Invalid TSRemapInterface argument", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->size < sizeof(TSRemapInterface)) {
    strncpy(errbuf, "[tsremap_init] Incorrect size of TSRemapInterface structure", errbuf_size - 1);
    return TS_ERROR;
  }

  if (api_info->tsremap_version < TSREMAP_VERSION) {
    snprintf(errbuf, errbuf_size - 1, "[tsremap_init] Incorrect API version %ld.%ld", api_info->tsremap_version >> 16,
             (api_info->tsremap_version & 0xffff));
    return TS_ERROR;
  }

  TSDebug(PLUGIN_NAME, "remap plugin is successfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf ATS_UNUSED, int errbuf_size ATS_UNUSED)
{
  *ih = load_config_file(argc > 2 ? argv[2] : NULL);
  return (NULL == *ih) ? TS_ERROR : TS_SUCCESS;
}

void
TSRemapDeleteInstance(void *ih)
{
  pr_list *prl = (pr_list *)ih;

  TSDebug(PLUGIN_NAME, "Deleting remap instance");

  delete prl;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri ATS_UNUSED)
{
  int ok;

  ok = rewrite_cacheurl((pr_list *)ih, rh);
  if (ok) {
    return TSREMAP_NO_REMAP;
  } else {
    return TSREMAP_ERROR;
  }
}

void
TSPluginInit(int argc, const char *argv[])
{
  TSPluginRegistrationInfo info;
  TSCont contp;
  pr_list *prl;

  info.plugin_name = (char *)PLUGIN_NAME;
  info.vendor_name = (char *)"Apache Software Foundation";
  info.support_email = (char *)"dev@trafficserver.apache.org";

  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
    TSDebug(PLUGIN_NAME, "ERROR, Plugin registration failed");
    initialization_error("Plugin registration failed.");
    return;
  }

  prl = load_config_file(argc > 1 ? argv[1] : NULL);
  if (prl) {
    contp = TSContCreate((TSEventFunc)handle_hook, NULL);
    /* Store the pattern replacement list in the continuation */
    TSContDataSet(contp, prl);
    TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, contp);
  } else {
    TSDebug(PLUGIN_NAME, "ERROR, Plugin config load failed.");
    initialization_error("Plugin config load failed.");
    return;
  }
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2000-2006 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cssysdef.h"
#include "csutil/sysfunc.h"
#include "csutil/array.h"
#include "plugins/engine/3d/region.h"
#include "plugins/engine/3d/engine.h"
#include "iengine/sector.h"
#include "iengine/campos.h"
#include "iengine/texture.h"
#include "iengine/material.h"
#include "iengine/engine.h"
#include "iengine/mesh.h"

//---------------------------------------------------------------------------

csRegion::csRegion (csEngine *e)
  : scfImplementationType (this), engine (e)
{
}

csRegion::~csRegion ()
{
  Clear ();
}

// If you enable this then after deleting a region it will
// print out which objects were not removed and their ref count.
#define REGION_CHECK

void csRegion::DeleteAll ()
{
  // First we need to copy the objects to a vector to avoid
  // messing up the iterator while we are deleting them.
  csArray<iObject*> copy (1024, 256);
  csRef<iObjectIterator> iter = GetIterator ();
  while (iter->HasNext ())
  {
    iObject *o = iter->Next ();
    copy.Push (o);
  }

  size_t total = copy.GetSize ();

  // Now we iterate over all objects in the 'copy' vector and
  // delete them. This will release them as csObject children
  // from this region parent.
  size_t i;

#ifdef REGION_CHECK
  struct rcStruct { csString n; csWeakRef<iBase> weakb; };
  rcStruct* rc = new rcStruct[total];
#endif

  // The first loop is the most general one where we just use
  // engine->RemoveObject().
  for (i = 0; i < copy.GetSize (); i++)
  {
    iBase* b = (iBase*)copy[i];
#ifdef REGION_CHECK
    rc[i].weakb = b;
    rc[i].n = copy[i]->GetName ();
#endif
    if (engine->RemoveObject (b))
    {
      copy[i] = 0;
      total--;
    }
  }

#ifdef REGION_CHECK
  for (i = 0 ; i < copy.GetSize () ; i++)
    if (rc[i].weakb != 0)
      printf ("Not Deleted %p '%s' ref=%d\n",
	  (iBase*)rc[i].weakb, (const char*)rc[i].n,
	  rc[i].weakb->GetRefCount ());
  fflush (stdout);
  delete[] rc;
#endif

  if (!total) return;

  if (total > 0)
  {
    // Sanity check. There should be no more
    // non-0 references in the copy array now.
    for (i = 0; i < copy.GetSize (); i++)
      if (copy[i])
      {
        iObject *o = copy[i];
        engine->ReportBug ("There is still an object in the array after "
          "deleting region contents!\nObject name is '%s'", o->GetName ());
      }
    CS_ASSERT (false);
  }
}

bool csRegion::PrepareTextures ()
{
  csRef<iObjectIterator> iter;
  iTextureManager *txtmgr = engine->G3D->GetTextureManager ();

  // First register all textures to the texture manager.
  iter = GetIterator ();
  while (iter->HasNext ())
  {
    csRef<iTextureWrapper> csth (scfQueryInterface<iTextureWrapper> (
        iter->Next ()));
    if (csth)
    {
      if (!csth->GetTextureHandle ()) csth->Register (txtmgr);
      if (!csth->KeepImage ()) csth->SetImageFile (0);
    }
  }

  return true;
}

bool csRegion::ShineLights ()
{
  engine->ShineLights (this);
  return true;
}

bool csRegion::Prepare ()
{
  if (!PrepareTextures ()) return false;
  if (!ShineLights ()) return false;
  return true;
}

iObject *csRegion::QueryObject ()
{
  return this;
}

void csRegion::Clear ()
{
  ObjRemoveAll ();
}

iSector *csRegion::FindSector (const char *iName)
{
  csRef<iSector> sector (CS_GET_NAMED_CHILD_OBJECT (
      this, iSector, iName));
  return sector;	// DecRef is ok here.
}

iMeshWrapper *csRegion::FindMeshObject (const char *Name)
{
  char const* p = strchr (Name, ':');
  if (p)
  {
    char* cname = csStrNew (Name);
    char* p2 = strchr (cname, ':');
    *p2 = 0;
    csRef<iMeshWrapper> m = CS_GET_NAMED_CHILD_OBJECT (
        this, iMeshWrapper, cname);
    delete[] cname;
    if (m)
    {
      return m->FindChildByName (p+1);
    }
    return 0;
  }
  else
  {
    csRef<iMeshWrapper> m = CS_GET_NAMED_CHILD_OBJECT (
        this, iMeshWrapper, Name);
    return m;	// DecRef is ok here.
  }
}

iMeshFactoryWrapper *csRegion::FindMeshFactory (const char *iName)
{
  csRef<iMeshFactoryWrapper> mf (CS_GET_NAMED_CHILD_OBJECT (
      this, iMeshFactoryWrapper, iName));
  return mf;	// DecRef is ok here.
}

iTextureWrapper *csRegion::FindTexture (const char *iName)
{
  csRef<iTextureWrapper> t (CS_GET_NAMED_CHILD_OBJECT (
      this, iTextureWrapper, iName));
  return t;	// DecRef is ok here.
}

iMaterialWrapper *csRegion::FindMaterial (const char *iName)
{
  csRef<iMaterialWrapper> m (CS_GET_NAMED_CHILD_OBJECT (
      this, iMaterialWrapper, iName));
  return m;	// DecRef is ok here.
}

iCameraPosition *csRegion::FindCameraPosition (const char *iName)
{
  csRef<iCameraPosition> cp (CS_GET_NAMED_CHILD_OBJECT (
      this, iCameraPosition, iName));
  return cp;	// DecRef is ok here.
}

bool csRegion::IsInRegion (iObject *iobj)
{
  return iobj->GetObjectParent () == this;
}

// ---------------------------------------------------------------------------

csRegionList::csRegionList ()
  : scfImplementationType (this),
  regionList (16, 16)
{
}

csRegionList::~csRegionList()
{
}

int csRegionList::GetCount () const
{
  return (int)regionList.GetSize ();
}

iRegion *csRegionList::Get (int n) const
{
  return regionList.Get (n);
}

int csRegionList::Add (iRegion *obj)
{
  return (int)regionList.Push (obj);
}

bool csRegionList::Remove (iRegion *obj)
{
  return regionList.Delete (obj);
}

bool csRegionList::Remove (int n)
{
  return regionList.DeleteIndex (n);
}

void csRegionList::RemoveAll ()
{
  regionList.DeleteAll ();
}

int csRegionList::Find (iRegion *obj) const
{
  return (int)regionList.Find (obj);
}

iRegion *csRegionList::FindByName (const char *Name) const
{
  return regionList.FindByName (Name);
}
<commit_msg>res made some VC warning fixes.<commit_after>/*
    Copyright (C) 2000-2006 by Jorrit Tyberghein

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cssysdef.h"
#include "csutil/sysfunc.h"
#include "csutil/array.h"
#include "plugins/engine/3d/region.h"
#include "plugins/engine/3d/engine.h"
#include "iengine/sector.h"
#include "iengine/campos.h"
#include "iengine/texture.h"
#include "iengine/material.h"
#include "iengine/engine.h"
#include "iengine/mesh.h"

//---------------------------------------------------------------------------

csRegion::csRegion (csEngine *e)
  : scfImplementationType (this), engine (e)
{
}

csRegion::~csRegion ()
{
  Clear ();
}

// If you enable this then after deleting a region it will
// print out which objects were not removed and their ref count.
#define REGION_CHECK

void csRegion::DeleteAll ()
{
  // First we need to copy the objects to a vector to avoid
  // messing up the iterator while we are deleting them.
  csArray<iObject*> copy (1024, 256);
  csRef<iObjectIterator> iter = GetIterator ();
  while (iter->HasNext ())
  {
    iObject *o = iter->Next ();
    copy.Push (o);
  }

  size_t total = copy.GetSize ();

  // Now we iterate over all objects in the 'copy' vector and
  // delete them. This will release them as csObject children
  // from this region parent.
  size_t i;

#ifdef REGION_CHECK
  struct rcStruct { csString n; csWeakRef<iBase> weakb; };
  rcStruct* rc = new rcStruct[total];
#endif

  // The first loop is the most general one where we just use
  // engine->RemoveObject().
  for (i = 0; i < copy.GetSize (); i++)
  {
    iBase* b = (iBase*)copy[i];
#ifdef REGION_CHECK
    rc[i].weakb = b;
    rc[i].n = copy[i]->GetName ();
#endif
    if (engine->RemoveObject (b))
    {
      copy[i] = 0;
      total--;
    }
  }

#ifdef REGION_CHECK
  for (i = 0 ; i < copy.GetSize () ; i++)
    if (rc[i].weakb != 0)
      printf ("Not Deleted %p '%s' ref=%d\n",
	  (iBase*)rc[i].weakb, (const char*)rc[i].n,
	  rc[i].weakb->GetRefCount ());
  fflush (stdout);
  delete[] rc;
#endif

  if (!total) return;

  if (total > 0)
  {
    // Sanity check. There should be no more
    // non-0 references in the copy array now.
    for (i = 0; i < copy.GetSize (); i++)
      if (copy[i])
      {
        iObject *o = copy[i];
        engine->ReportBug ("There is still an object in the array after "
          "deleting region contents!\nObject name is '%s'", o->GetName ());
      }
    CS_ASSERT (false);
  }
}

bool csRegion::PrepareTextures ()
{
  csRef<iObjectIterator> iter;
  iTextureManager *txtmgr = engine->G3D->GetTextureManager ();

  // First register all textures to the texture manager.
  iter = GetIterator ();
  while (iter->HasNext ())
  {
    csRef<iTextureWrapper> csth (scfQueryInterface<iTextureWrapper> (
        iter->Next ()));
    if (csth)
    {
      if (!csth->GetTextureHandle ()) csth->Register (txtmgr);
      if (!csth->KeepImage ()) csth->SetImageFile (0);
    }
  }

  return true;
}

bool csRegion::ShineLights ()
{
  engine->ShineLights (this);
  return true;
}

bool csRegion::Prepare ()
{
  if (!PrepareTextures ()) return false;
  if (!ShineLights ()) return false;
  return true;
}

iObject *csRegion::QueryObject ()
{
  return this;
}

void csRegion::Clear ()
{
  ObjRemoveAll ();
}

iSector *csRegion::FindSector (const char *iName)
{
  csRef<iSector> sector (CS::GetNamedChildObject<iSector> (
      this, iName));
  return sector;	// DecRef is ok here.
}

iMeshWrapper *csRegion::FindMeshObject (const char *Name)
{
  char const* p = strchr (Name, ':');
  if (p)
  {
    char* cname = csStrNew (Name);
    char* p2 = strchr (cname, ':');
    *p2 = 0;
    csRef<iMeshWrapper> m = CS::GetNamedChildObject<iMeshWrapper> (
        this, cname);
    delete[] cname;
    if (m)
    {
      return m->FindChildByName (p+1);
    }
    return 0;
  }
  else
  {
    csRef<iMeshWrapper> m = CS::GetNamedChildObject<iMeshWrapper> (
        this, Name);
    return m;	// DecRef is ok here.
  }
}

iMeshFactoryWrapper *csRegion::FindMeshFactory (const char *iName)
{
  csRef<iMeshFactoryWrapper> mf (
    CS::GetNamedChildObject<iMeshFactoryWrapper> (this, iName));
  return mf;	// DecRef is ok here.
}

iTextureWrapper *csRegion::FindTexture (const char *iName)
{
  csRef<iTextureWrapper> t (CS::GetNamedChildObject<iTextureWrapper> (
      this, iName));
  return t;	// DecRef is ok here.
}

iMaterialWrapper *csRegion::FindMaterial (const char *iName)
{
  csRef<iMaterialWrapper> m (CS::GetNamedChildObject<iMaterialWrapper> (
      this, iName));
  return m;	// DecRef is ok here.
}

iCameraPosition *csRegion::FindCameraPosition (const char *iName)
{
  csRef<iCameraPosition> cp (CS::GetNamedChildObject<iCameraPosition> (
      this, iName));
  return cp;	// DecRef is ok here.
}

bool csRegion::IsInRegion (iObject *iobj)
{
  return iobj->GetObjectParent () == this;
}

// ---------------------------------------------------------------------------

csRegionList::csRegionList ()
  : scfImplementationType (this),
  regionList (16, 16)
{
}

csRegionList::~csRegionList()
{
}

int csRegionList::GetCount () const
{
  return (int)regionList.GetSize ();
}

iRegion *csRegionList::Get (int n) const
{
  return regionList.Get (n);
}

int csRegionList::Add (iRegion *obj)
{
  return (int)regionList.Push (obj);
}

bool csRegionList::Remove (iRegion *obj)
{
  return regionList.Delete (obj);
}

bool csRegionList::Remove (int n)
{
  return regionList.DeleteIndex (n);
}

void csRegionList::RemoveAll ()
{
  regionList.DeleteAll ();
}

int csRegionList::Find (iRegion *obj) const
{
  return (int)regionList.Find (obj);
}

iRegion *csRegionList::FindByName (const char *Name) const
{
  return regionList.FindByName (Name);
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Arista Networks, Inc.  All rights reserved.
// Arista Networks, Inc. Confidential and Proprietary.

#include "eos/class_map.h"
#include "eos/exception.h"
#include "impl.h"

namespace std {

size_t hash<eos::policy_map_action_t>::operator() (eos::policy_map_action_t
                                                   const & action) const {
   // TODO: no op impl
   return 0;
}

size_t hash<eos::policy_map_key_t>::operator() (eos::policy_map_key_t
                                                const & key) const {
   // TODO: no op impl
   return 0;
}

}

namespace eos {

policy_map_handler::policy_map_handler(policy_map_mgr * mgr) :
      base_handler<policy_map_mgr, policy_map_handler>(mgr) {
}

void
policy_map_handler::watch_policy_map(policy_map_key_t const & key,
                                         bool interest) {
   // TODO: no op impl.
}

void
policy_map_handler::watch_policy_map(policy_map_key_t const & key,
                                     std::string const & name,
                                     bool interest) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_sync_fail(policy_map_key_t const & key,
                                            std::string const & message) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_sync(policy_map_key_t const & key) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_config_set(policy_map_key_t const & name) {
   // TODO: no op impl.
}

class policy_map_mgr_impl : public policy_map_mgr {
 public:
   policy_map_mgr_impl() {
   }

   void resync_init() {
   }

   void resync_complete() {
   }

   bool exists(policy_map_key_t const & key) const {
      return false;
   }

   policy_map_t policy_map(policy_map_key_t const & key) const {
      return policy_map_t();
   }

   void policy_map_is(policy_map_t const & policy_map) {
   }

   void policy_map_del(policy_map_key_t const & key) {
   }

   policy_map_iter_t policy_map_iter(policy_feature_t) const {
      policy_map_iter_t * nop = 0;
      return *nop;
   }

   void policy_map_apply(policy_map_key_t const &, intf_id_t,
                         acl_direction_t, bool apply) {
   }

   void handleInputConfig(std::string const & name) const {
   }
};

DEFINE_STUB_MGR_CTOR(policy_map_mgr)

}
<commit_msg>@13892585 EosSdk Policy Map: "ri" mount "pbr/input/pmap/config" and "pbr/input/intf/config"<commit_after>// Copyright (c) 2014 Arista Networks, Inc.  All rights reserved.
// Arista Networks, Inc. Confidential and Proprietary.

#include "eos/class_map.h"
#include "eos/exception.h"
#include "impl.h"

namespace std {

size_t hash<eos::policy_map_action_t>::operator() (eos::policy_map_action_t
                                                   const & action) const {
   // TODO: no op impl
   return 0;
}

size_t hash<eos::policy_map_key_t>::operator() (eos::policy_map_key_t
                                                const & key) const {
   // TODO: no op impl
   return 0;
}

}

namespace eos {

policy_map_handler::policy_map_handler(policy_map_mgr * mgr) :
      base_handler<policy_map_mgr, policy_map_handler>(mgr) {
}

void
policy_map_handler::watch_policy_map(policy_map_key_t const & key,
                                         bool interest) {
   // TODO: no op impl.
}

void
policy_map_handler::watch_policy_map(policy_map_key_t const & key,
                                     std::string const & name,
                                     bool interest) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_sync_fail(policy_map_key_t const & key,
                                            std::string const & message) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_sync(policy_map_key_t const & key) {
   // TODO: no op impl.
}

void
policy_map_handler::on_policy_map_config_set(policy_map_key_t const & name) {
   // TODO: no op impl.
}

class policy_map_mgr_impl : public policy_map_mgr {
 public:
   policy_map_mgr_impl() {
   }

   void resync_init() {
   }

   void resync_complete() {
   }

   bool exists(policy_map_key_t const & key) const {
      return false;
   }

   policy_map_t policy_map(policy_map_key_t const & key) const {
      return policy_map_t();
   }

   void policy_map_is(policy_map_t const & policy_map) {
   }

   void policy_map_del(policy_map_key_t const & key) {
   }

   policy_map_iter_t policy_map_iter(policy_feature_t) const {
      policy_map_iter_t * nop = 0;
      return *nop;
   }

   void policy_map_apply(policy_map_key_t const &, intf_id_t,
                         acl_direction_t, bool apply) {
   }
};

DEFINE_STUB_MGR_CTOR(policy_map_mgr)

}
<|endoftext|>


<commit_before>extern "C" {

	#include <png.h>

}

#include <hx/CFFI.h>
#include <graphics/PNG.h>
#include <setjmp.h>

namespace lime {

	static int id_data;
	static int id_height;
	static int id_width;
	static bool init = false;

	/*static void user_error_fn (png_structp png_ptr, png_const_charp error_msg) {

		longjmp (png_ptr->jmpbuf, 1);

	}

	static void user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) { }
	static void user_read_data_fn (png_structp png_ptr, png_bytep data, png_size_t length) {

		png_voidp buffer = png_get_io_ptr (png_ptr);
		((ReadBuf *)buffer)->Read (data, length);

	}

	void user_write_data (png_structp png_ptr, png_bytep data, png_size_t length) {

		QuickVec<unsigned char> *buffer = (QuickVec<unsigned char> *)png_get_io_ptr (png_ptr);
		buffer->append ((unsigned char *)data, (int)length);

	}

	void user_flush_data (png_structp png_ptr) { }*/

	void PNG::Decode (ByteArray bytes, value imageData) {

		/*if (!init) {

			id_data = val_id ("data");
			id_height = val_id ("height");
			id_width = val_id ("width");
			init = true;

		}

		png_structp png_ptr;
		png_infop info_ptr;
		png_uint_32 width, height;
		int bit_depth, color_type, interlace_type;

		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, user_error_fn, user_warning_fn);

		if (png_ptr == NULL)
			return;

		info_ptr = png_create_info_struct (png_ptr);

		if (info_ptr == NULL) {

			png_destroy_read_struct (&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
			return;

		}

		unsigned char* bytes[width * height * 4];
		RenderTarget target;

		if (setjmp (png_jmpbuf (png_ptr))) {

			if (bytes) {

				delete bytes;

			}

			png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
			return;

		}

		ReadBuf buffer (inData, inDataLen);

		//if (inFile) {

			//png_init_io (png_ptr, inFile);

		//} else {

			png_set_read_fn (png_ptr, (void *)&buffer, user_read_data_fn);

		//}

		png_read_info (png_ptr, info_ptr);
		png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);

		bool has_alpha = (color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS));

		png_set_expand (png_ptr);
		png_set_filler (png_ptr, 0xff, PNG_FILLER_AFTER);

		png_set_palette_to_rgb (png_ptr);
		png_set_gray_to_rgb (png_ptr);

		if (bit_depth == 16)
			png_set_strip_16 (png_ptr);

		png_set_bgr (png_ptr);

		//result = new ImageData ();
		//result.width = width;
		//result.height = height;
		//result.data = uint8[width * height * 4];

		png_read_png (png_ptr, (png_bytepp)&bytes);

		png_read_end (png_ptr, info_ptr);
		png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);

		value object = (KeyEvent::eventObject ? KeyEvent::eventObject->get () : alloc_empty_object ());

			alloc_field (object, id_code, alloc_int (event->code));
			alloc_field (object, id_type, alloc_int (event->type));

		alloc_field (imageData, id_width, alloc_int (width));
		alloc_field (imageData, id_height, alloc_int (height));
		alloc_field (imageData, id_data, alloc_int (bytes));

		return result;*/

	}

	static bool Encode (ImageData *imageData, ByteArray *bytes) {

		return true;

		/*png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, user_error_fn, user_warning_fn);

		if (!png_ptr)
			return false;

		png_infop info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr)
			return false;

		if (setjmp(png_jmpbuf(png_ptr)))
		{
			png_destroy_write_struct(&png_ptr, &info_ptr );
			return false;
		}

		QuickVec<uint8> out_buffer;

		png_set_write_fn(png_ptr, &out_buffer, user_write_data, user_flush_data);

		int w = inSurface->Width();
		int h = inSurface->Height();

		int bit_depth = 8;
		int color_type = (inSurface->Format()&pfHasAlpha) ?
		PNG_COLOR_TYPE_RGB_ALPHA :
		PNG_COLOR_TYPE_RGB;
		png_set_IHDR(png_ptr, info_ptr, w, h,
		bit_depth, color_type, PNG_INTERLACE_NONE,
		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

		png_write_info(png_ptr, info_ptr);

		bool do_alpha = color_type==PNG_COLOR_TYPE_RGBA;

		{
		QuickVec<uint8> row_data(w*4);
		png_bytep row = &row_data[0];
		for(int y=0;y<h;y++)
		{
		uint8 *buf = &row_data[0];
		const uint8 *src = (const uint8 *)inSurface->Row(y);
		for(int x=0;x<w;x++)
		{
		buf[0] = src[2];
		buf[1] = src[1];
		buf[2] = src[0];
		src+=3;
		buf+=3;
		if (do_alpha)
		*buf++ = *src;
		src++;
		}
		png_write_rows(png_ptr, &row, 1);
		}
		}

		png_write_end(png_ptr, NULL);

		*outBytes = ByteArray(out_buffer);

		return true;*/
	}

}<commit_msg>Don't require png.h for the moment<commit_after>extern "C" {

	//#include <png.h>

}

#include <hx/CFFI.h>
#include <graphics/PNG.h>
#include <setjmp.h>

namespace lime {

	static int id_data;
	static int id_height;
	static int id_width;
	static bool init = false;

	/*static void user_error_fn (png_structp png_ptr, png_const_charp error_msg) {

		longjmp (png_ptr->jmpbuf, 1);

	}

	static void user_warning_fn (png_structp png_ptr, png_const_charp warning_msg) { }
	static void user_read_data_fn (png_structp png_ptr, png_bytep data, png_size_t length) {

		png_voidp buffer = png_get_io_ptr (png_ptr);
		((ReadBuf *)buffer)->Read (data, length);

	}

	void user_write_data (png_structp png_ptr, png_bytep data, png_size_t length) {

		QuickVec<unsigned char> *buffer = (QuickVec<unsigned char> *)png_get_io_ptr (png_ptr);
		buffer->append ((unsigned char *)data, (int)length);

	}

	void user_flush_data (png_structp png_ptr) { }*/

	void PNG::Decode (ByteArray bytes, value imageData) {

		/*if (!init) {

			id_data = val_id ("data");
			id_height = val_id ("height");
			id_width = val_id ("width");
			init = true;

		}

		png_structp png_ptr;
		png_infop info_ptr;
		png_uint_32 width, height;
		int bit_depth, color_type, interlace_type;

		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, user_error_fn, user_warning_fn);

		if (png_ptr == NULL)
			return;

		info_ptr = png_create_info_struct (png_ptr);

		if (info_ptr == NULL) {

			png_destroy_read_struct (&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
			return;

		}

		unsigned char* bytes[width * height * 4];
		RenderTarget target;

		if (setjmp (png_jmpbuf (png_ptr))) {

			if (bytes) {

				delete bytes;

			}

			png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
			return;

		}

		ReadBuf buffer (inData, inDataLen);

		//if (inFile) {

			//png_init_io (png_ptr, inFile);

		//} else {

			png_set_read_fn (png_ptr, (void *)&buffer, user_read_data_fn);

		//}

		png_read_info (png_ptr, info_ptr);
		png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);

		bool has_alpha = (color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA || png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS));

		png_set_expand (png_ptr);
		png_set_filler (png_ptr, 0xff, PNG_FILLER_AFTER);

		png_set_palette_to_rgb (png_ptr);
		png_set_gray_to_rgb (png_ptr);

		if (bit_depth == 16)
			png_set_strip_16 (png_ptr);

		png_set_bgr (png_ptr);

		//result = new ImageData ();
		//result.width = width;
		//result.height = height;
		//result.data = uint8[width * height * 4];

		png_read_png (png_ptr, (png_bytepp)&bytes);

		png_read_end (png_ptr, info_ptr);
		png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);

		value object = (KeyEvent::eventObject ? KeyEvent::eventObject->get () : alloc_empty_object ());

			alloc_field (object, id_code, alloc_int (event->code));
			alloc_field (object, id_type, alloc_int (event->type));

		alloc_field (imageData, id_width, alloc_int (width));
		alloc_field (imageData, id_height, alloc_int (height));
		alloc_field (imageData, id_data, alloc_int (bytes));

		return result;*/

	}

	static bool Encode (ImageData *imageData, ByteArray *bytes) {

		return true;

		/*png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, user_error_fn, user_warning_fn);

		if (!png_ptr)
			return false;

		png_infop info_ptr = png_create_info_struct(png_ptr);
		if (!info_ptr)
			return false;

		if (setjmp(png_jmpbuf(png_ptr)))
		{
			png_destroy_write_struct(&png_ptr, &info_ptr );
			return false;
		}

		QuickVec<uint8> out_buffer;

		png_set_write_fn(png_ptr, &out_buffer, user_write_data, user_flush_data);

		int w = inSurface->Width();
		int h = inSurface->Height();

		int bit_depth = 8;
		int color_type = (inSurface->Format()&pfHasAlpha) ?
		PNG_COLOR_TYPE_RGB_ALPHA :
		PNG_COLOR_TYPE_RGB;
		png_set_IHDR(png_ptr, info_ptr, w, h,
		bit_depth, color_type, PNG_INTERLACE_NONE,
		PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

		png_write_info(png_ptr, info_ptr);

		bool do_alpha = color_type==PNG_COLOR_TYPE_RGBA;

		{
		QuickVec<uint8> row_data(w*4);
		png_bytep row = &row_data[0];
		for(int y=0;y<h;y++)
		{
		uint8 *buf = &row_data[0];
		const uint8 *src = (const uint8 *)inSurface->Row(y);
		for(int x=0;x<w;x++)
		{
		buf[0] = src[2];
		buf[1] = src[1];
		buf[2] = src[0];
		src+=3;
		buf+=3;
		if (do_alpha)
		*buf++ = *src;
		src++;
		}
		png_write_rows(png_ptr, &row, 1);
		}
		}

		png_write_end(png_ptr, NULL);

		*outBytes = ByteArray(out_buffer);

		return true;*/
	}

}<|endoftext|>


<commit_before>/*
 * 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.
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <json/reader.h>
#include <json/writer.h>
#include <slog2.h>

// minizip
#include "unzip.h"
#include "state2json.h"

#include "extractzipfile_ndk.hpp"
#include "extractzipfile_js.hpp"

static void ExtractZipFileNDK_mkpath(char *path) {
	size_t len = strlen(path);

	// ensure path is a string
	char last_char = path[len - 1];
	path[len - 1] = '\0';

	// mkdir() foreach except last folder
	for (char *upto = path + 1; *upto; upto++) {
		if (*upto == '/') {
			*upto = '\0';
			mkdir(path, 0x777);
			*upto = '/';
		}
	}

	// mkdir() last folder
	mkdir(path, 0x777);

	// restore input path
	path[len - 1] = last_char;
}

namespace webworks {

ExtractZipFileNDK::ExtractZipFileNDK(ExtractZipFileJS *parent) {
	m_pParent = parent;
}

ExtractZipFileNDK::~ExtractZipFileNDK() {
}

// ->extractFile
// Returns a json obejct with "result" set to int ret code
// ret code is < 0 on error.
// and "result_message" a description of the error or success
void ExtractZipFileNDK::extractFile(const std::string& callbackId, const std::string& inputString) {
	#define extractReturn(x,y) \
		do {retval["result"] = x; \
		retval["result_message"] = y; \
		m_pParent->NotifyEvent(callbackId + " " + writer.write(retval)); \
		return;} while (0)

	// Tune this and reduce mem usage
	#define EZIPBUFSIZE 1024
	#define MAX_FILENAME 1024

	// Parse the arg string as JSON
	Json::FastWriter writer;
	Json::Reader reader;
	Json::Value root;
	Json::Value retval;
	s2jInit(retval);

	bool parse = reader.parse(inputString, root);
	if (!parse) {
		extractReturn(-1, "Cannot parse internal JSON object");
	}

	// -- Parse Input
	// callbackToken
	std::string requestedToken = root["callbackToken"].asString();
	if (requestedToken == "")
		requestedToken = root["zip"];
	retval["callbackToken"] = requestedToken;

	// destination
	std::string dest_root = root["destination"].asString();
	if (dest_root == "")
		dest_root = "./";
	if (dest_root[dest_root.size() - 1] != '/')
		dest_root += "/";

	// zip
	std::string src_zip = root["zip"].asString();
	if (src_zip == "")
		extractReturn(-1, "zip argument must not be empty");

	// tarBombProtection
	// ensures everything is extracted into a single folder
	bool prevent_tar_bomb = root["tarBombProtection"].asString() == "true";
	if (prevent_tar_bomb) {
		unsigned filename_start = 1 + src_zip.find_last_of("/");
		unsigned filename_end = src_zip.find_last_of(".");
		std::string filename = src_zip.substr(filename_start, filename_end);
		dest_root += filename + "/";
	}

	// overwriteFiles
	bool overwrite_files = !(root["overwriteFiles"].asString() == "false");

	// -- Extract Zip
	const char *zip_path = src_zip.c_str();
	unzFile zipFile = unzOpen(zip_path);
	if (zipFile == NULL)
		extractReturn(-1, "Failed to open zip file.");

	// get zip metadata
	unz_global_info zipInfo;
	if (unzGetGlobalInfo(zipFile, &zipInfo) != UNZ_OK) {
		unzClose(zipFile);
		extractReturn(-1, "Failed to parse zip metadata.");
	}

	// fixed size buf on stack, WATCH USAGE!
	char fileBuffer[EZIPBUFSIZE];

	// Ensure destination exists
	ExtractZipFileNDK_mkpath(dest_root);

	int filesExtracted = 0;
	int files_skipped = 0;
	for (int i = 0; i < zipInfo.number_entry; i++) {
		s2jIncre("entries");

		// single file metadata
		unz_file_info fileInfo;
		char filename[MAX_FILENAME + 1];

		// get metadata on specific file
		if (unzGetCurrentFileInfo(
					zipFile,
					&fileInfo,
					filename,
					MAX_FILENAME,
					NULL, 0, NULL, 0) != UNZ_OK) {
			unzClose(zipFile);
			extractReturn(-1, "Failed to parse a file's metadata.");
		}
		filename[MAX_FILENAME] = '\0'; // ensure string termination
		s2jInsert("files", filename);

		// Handle Directories
		if (filename[strlen(filename) - 1] == '/') {
			// Directory creation cannot lose data
			// so we do not care if a dir already
			// exists
			ExtractZipFileNDK_mkpath((dest_root + filename).c_str(), 0x777);
			s2jIncre("directories");
			// Note: The zip format does store permissions
			// except these are all in platform specific
			// formats. I talked with the guy responsible
			// for window's file navigator's zip lib.
			// His stories have scared me into the thinking
			// that providing a default permission is more
			// user friendly than missing obscure edge cases
			// which could leave files in "magical" states.

		// Handle Files
		} else {

			// Note: This opens zipFile's "current" file
			//       "current" acts as an interator
			if (unzOpenCurrentFile(zipFile) != UNZ_OK) {
				unzClose(zipFile);
				extractReturn(-1, "Failed to extract file");
			}

			// Open destination file in file system
			std::string dest_file_path = dest_root + filename;

			// Check for overwriting
			if (!overwrite_files && access(dest_file_path.c_str(), F_OK) != -1) {
				// file exists and we are not allowed to overwrite
				s2jIncre("files_skipped");

			} else {
				FILE *destFile = fopen(dest_file_path.c_str(), "wb");
				if (destFile == NULL) {
					unzCloseCurrentFile(zipFile);
					unzClose(zipFile);
					extractReturn(-1, "Failed to open destination file");
				}

				// Ferry data into destination
				int readResult = UNZ_OK; // is 0
				do {
					// Read
					readResult = unzReadCurrentFile(zipFile, fileBuffer, EZIPBUFSIZE);
					if (readResult < 0) {
						unzCloseCurrentFile(zipFile);
						unzClose(zipFile);
						fclose(destFile);
						extractReturn(-1, "Failed to read compressed file");
					}

					// Write
					if (readResult > 0) {
						int writeResult = fwrite(fileBuffer, readResult, 1, destFile);
						if (writeResult != 1) {
							// Note: we asked for the full buffer
							// to be written at once, so the 1
							// return value is not "true" but
							// the number blocks writen
							unzCloseCurrentFile(zipFile);
							unzClose(zipFile);
							fclose(destFile);
							extractReturn(-1, "Failed to write to destination file");
						}
					}
				} while (readResult > 0);

				fclose(destFile);
				filesExtracted++;
			}
			s2jIncre("files");
		}

		unzCloseCurrentFile(zipFile);

		// Increment "current" file interator if any more files
		unzGoToNextFile(zipFile);
	}

	unzClose(zipFile);

	// Success!
	extractReturn(filesExtracted, "Extraction successful.");
}

} /* namespace webworks */
<commit_msg>Add state logging to default builds<commit_after>/*
 * 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.
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <json/reader.h>
#include <json/writer.h>
#include <slog2.h>

// minizip
#include "unzip.h"
#define S2J_ENABLED 1
#include "state2json.h"

#include "extractzipfile_ndk.hpp"
#include "extractzipfile_js.hpp"

static void ExtractZipFileNDK_mkpath(const char *path_raw) {
	size_t len = strlen(path_raw);

	// copy path so we can mangle it
	char *path = (char *)malloc(len + 1);
	snprintf(path, len + 1, "%s", path_raw);

	char last_char = path[len - 1];
	if (last_char == '/')
		path[len - 1] = '\0';

	// mkdir() foreach except last folder
	for (char *upto = path + 1; *upto != '\0'; upto++) {
		if (*upto == '/') {
			*upto = '\0';
			mkdir(path, 0x777);
			*upto = '/';
		}
	}

	// mkdir() last folder
	mkdir(path, 0x777);
	free(path);
}

namespace webworks {

ExtractZipFileNDK::ExtractZipFileNDK(ExtractZipFileJS *parent) {
	m_pParent = parent;
}

ExtractZipFileNDK::~ExtractZipFileNDK() {
}

// ->extractFile
// Returns a json obejct with "result" set to int ret code
// ret code is < 0 on error.
// and "result_message" a description of the error or success
void ExtractZipFileNDK::extractFile(const std::string& callbackId, const std::string& inputString) {
	#define extractReturn(x,y) \
		do {retval["result"] = x; \
		retval["result_message"] = y; \
		m_pParent->NotifyEvent(callbackId + " " + writer.write(retval)); \
		return;} while (0)

	// Tune this and reduce mem usage
	#define EZIPBUFSIZE 1024
	#define MAX_FILENAME 1024

	// Parse the arg string as JSON
	Json::FastWriter writer;
	Json::Reader reader;
	Json::Value root;
	Json::Value retval;
	s2jInit(retval);

	bool parse = reader.parse(inputString, root);
	if (!parse) {
		extractReturn(-1, "Cannot parse internal JSON object");
	}

	// -- Parse Input
	// callbackToken
	std::string requestedToken = root["callbackToken"].asString();
	if (requestedToken == "")
		requestedToken = root["zip"].asString();
	retval["callbackToken"] = requestedToken;

	// destination
	std::string dest_root = root["destination"].asString();
	if (dest_root == "")
		dest_root = "./";
	if (dest_root[dest_root.size() - 1] != '/')
		dest_root += "/";

	// zip
	std::string src_zip = root["zip"].asString();
	if (src_zip == "")
		extractReturn(-1, "zip argument must not be empty");

	// tarBombProtection
	// ensures everything is extracted into a single folder
	bool prevent_tar_bomb = root["tarBombProtection"].asString() == "true";
	if (prevent_tar_bomb) {
		unsigned filename_start = 1 + src_zip.find_last_of("/");
		unsigned filename_end = src_zip.find_last_of(".");
		std::string filename = src_zip.substr(filename_start, filename_end);
		dest_root += filename + "/";
	}

	// overwriteFiles
	bool overwrite_files = !(root["overwriteFiles"].asString() == "false");

	// -- Extract Zip
	const char *zip_path = src_zip.c_str();
	unzFile zipFile = unzOpen(zip_path);
	if (zipFile == NULL)
		extractReturn(-1, "Failed to open zip file.");

	// get zip metadata
	unz_global_info zipInfo;
	if (unzGetGlobalInfo(zipFile, &zipInfo) != UNZ_OK) {
		unzClose(zipFile);
		extractReturn(-1, "Failed to parse zip metadata.");
	}

	// fixed size buf on stack, WATCH USAGE!
	char fileBuffer[EZIPBUFSIZE];

	// Ensure destination exists
	ExtractZipFileNDK_mkpath(dest_root.c_str());

	int filesExtracted = 0;
	int files_skipped = 0;
	for (int i = 0; i < zipInfo.number_entry; i++) {
		s2jIncre("entries");

		// single file metadata
		unz_file_info fileInfo;
		char filename[MAX_FILENAME + 1];

		// get metadata on specific file
		if (unzGetCurrentFileInfo(
					zipFile,
					&fileInfo,
					filename,
					MAX_FILENAME,
					NULL, 0, NULL, 0) != UNZ_OK) {
			unzClose(zipFile);
			extractReturn(-1, "Failed to parse a file's metadata.");
		}
		filename[MAX_FILENAME] = '\0'; // ensure string termination

		// Handle Directories
		if (filename[strlen(filename) - 1] == '/') {
			// Directory creation cannot lose data
			// so we do not care if a dir already
			// exists
			ExtractZipFileNDK_mkpath((dest_root + filename).c_str());
			s2jIncre("directories");
			// Note: The zip format does store permissions
			// except these are all in platform specific
			// formats. I talked with the guy responsible
			// for window's file navigator's zip lib.
			// His stories have scared me into the thinking
			// that providing a default permission is more
			// user friendly than missing obscure edge cases
			// which could leave files in "magical" states.

		// Handle Files
		} else {

			// Note: This opens zipFile's "current" file
			//       "current" acts as an interator
			if (unzOpenCurrentFile(zipFile) != UNZ_OK) {
				unzClose(zipFile);
				extractReturn(-1, "Failed to extract file");
			}

			// Open destination file in file system
			std::string dest_file_path = dest_root + filename;

			// Check for overwriting
			if (!overwrite_files && access(dest_file_path.c_str(), F_OK) != -1) {
				// file exists and we are not allowed to overwrite
				s2jIncre("files_skipped");

			} else {
				FILE *destFile = fopen(dest_file_path.c_str(), "wb");
				if (destFile == NULL) {
					unzCloseCurrentFile(zipFile);
					unzClose(zipFile);
					extractReturn(-1, "Failed to open destination file");
				}

				// Ferry data into destination
				int readResult = UNZ_OK; // is 0
				do {
					// Read
					readResult = unzReadCurrentFile(zipFile, fileBuffer, EZIPBUFSIZE);
					if (readResult < 0) {
						unzCloseCurrentFile(zipFile);
						unzClose(zipFile);
						fclose(destFile);
						extractReturn(-1, "Failed to read compressed file");
					}

					// Write
					if (readResult > 0) {
						int writeResult = fwrite(fileBuffer, readResult, 1, destFile);
						if (writeResult != 1) {
							// Note: we asked for the full buffer
							// to be written at once, so the 1
							// return value is not "true" but
							// the number blocks writen
							unzCloseCurrentFile(zipFile);
							unzClose(zipFile);
							fclose(destFile);
							extractReturn(-1, "Failed to write to destination file");
						}
					}
				} while (readResult > 0);

				fclose(destFile);
				filesExtracted++;
			}
			s2jIncre("files");
		}

		unzCloseCurrentFile(zipFile);

		// Increment "current" file interator if any more files
		unzGoToNextFile(zipFile);
	}

	unzClose(zipFile);

	// Success!
	extractReturn(filesExtracted, "Extraction successful.");
}

} /* namespace webworks */
<|endoftext|>


<commit_before>/*
    msncontact.cpp - MSN Contact

    Copyright (c) 2002 Duncan Mac-Vicar Prett <duncan@kde.org>
              (c) 2002 Ryan Cumming           <bodnar42@phalynx.dhs.org>
              (c) 2002 Martijn Klingens       <klingens@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kaction.h>
#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>

#include "kopete.h"
#include "kopetestdaction.h"
#include "msncontact.h"
#include "msnprotocol.h"

// Constructor for no-groups
MSNContact::MSNContact( QString &protocolId, const QString &msnId, const QString &displayName,
	const QString &group, KopeteMetaContact *parent )
	: KopeteContact( protocolId, parent )
{
	initContact( msnId, displayName, group );
}

void MSNContact::initContact( const QString &msnId, const QString &displayName,
	const QString &group )
{
	m_actionRemove = 0L;
	m_actionRemoveFromGroup = 0L;
	m_actionChat = 0L;
	m_actionInfo = 0L;
	m_actionHistory = 0L;
	m_actionMove = 0L;
	m_actionCopy = 0L;
	m_actionBlock = 0L;

	m_status = MSNProtocol::FLN;

	m_deleted = false;
	m_allowed = false;
	m_blocked = false;

	historyDialog = 0L;

	m_msnId = msnId;
	if( !group.isEmpty() )
		m_groups = group;
	hasLocalGroup = false;

	connect ( this, SIGNAL( chatToUser( QString ) ),
		MSNProtocol::protocol(),
		SLOT( slotStartChatSession( QString ) ) );

	setDisplayName( displayName );
}

void MSNContact::showContextMenu(QPoint point, QString /*group*/)
{
	KPopupMenu *popup = new KPopupMenu();
	popup->insertTitle( i18n( "%1 (%2)" ).arg( displayName() ).arg( msnId() ) );

	// Chat with user
	if( !m_actionChat )
	{
		m_actionChat = KopeteStdAction::sendMessage(
			this, SLOT( slotChatThisUser() ), this, "m_actionChat" );
	}
	m_actionChat->plug( popup );
	popup->insertSeparator();

	// View History
	if( !m_actionHistory )
	{
		m_actionHistory = KopeteStdAction::viewHistory( this,
			SLOT( slotViewHistory() ), this, "m_actionHistory" );
	}
	m_actionHistory->plug( popup );
	popup->insertSeparator();

	// Move Contact
	if( !m_actionMove )
	{
		m_actionMove = KopeteStdAction::moveContact( this,
			SLOT( slotMoveThisUser() ), this, "m_actionMove" );
	}
	m_actionMove->plug( popup );

	// Copy Contact
	if( !m_actionCopy )
	{
		m_actionCopy = new KListAction( i18n( "Copy Contact" ), "editcopy", 0,
			this, SLOT( slotCopyThisUser() ), this, "m_actionCopy" );
	}
	m_actionCopy->setItems( MSNProtocol::protocol()->groups() );
	m_actionCopy->plug( popup );

	// Remove From Group
	if( !m_actionRemoveFromGroup )
	{
		m_actionRemoveFromGroup = new KAction( i18n( "Remove From Group" ),
			"edittrash", 0, this, SLOT( slotRemoveFromGroup() ),
			this, "m_actionRemoveFromGroup" );
	}
	m_actionRemoveFromGroup->plug( popup );

	// Remove Contact
	if( !m_actionRemove )
	{
		m_actionRemove = KopeteStdAction::deleteContact( this,
			SLOT( slotRemoveThisUser() ), this, "m_actionRemove" );
	}
	m_actionRemove->plug( popup );

	popup->insertSeparator();

	// Block/unblock Contact
	delete m_actionBlock;
	QString label = isBlocked() ?
		i18n( "Unblock User" ) : i18n( "Block User" );
	m_actionBlock = new KAction( label,
		0, this, SLOT( slotBlockUser() ),
		this, "m_actionBlock" );
	m_actionBlock->plug( popup );

	popup->exec( point );
	delete popup;
}

void MSNContact::execute()
{
//	( new KopeteChatWindow( this, this, QString::null, 0 ) )->show(); // Debug, Martijn
	emit chatToUser( m_msnId );
}

QString MSNContact::id() const
{
	return m_msnId;
}

QString MSNContact::data() const
{
	return m_msnId;
}

void MSNContact::slotChatThisUser()
{
	return m_msnId;
}

void MSNContact::slotRemoveThisUser()
{
	MSNProtocol::protocol()->removeContact( this );
//	delete this;
}

void MSNContact::slotRemoveFromGroup()
{
	// FIXME: This slot needs to know to remove from WHICH group!
	// for now, remove from the first group...
	MSNProtocol::protocol()->removeFromGroup( this, m_groups.first() );
}

void MSNContact::moveToGroup( const QString &from, const QString &to )
{
	MSNProtocol::protocol()->moveContact( this, from, to );
}

void MSNContact::slotMoveThisUser()
{
	// FIXME: originating group should also be provided!
	if( m_actionMove )
	{
		//kdDebug() << "***** MOVE: Groups: " << m_groups.join( ", " ) << endl;
		if( m_movingToGroup == m_actionMove->currentText() )
		{
			kdDebug() << "MSNContact::slotMoveThisUser: Suppressing second "
				<< "slot invocation. Yes, I know this is ugly!" << endl;
		}
		else
			moveToGroup( m_groups.first(), m_actionMove->currentText() );
	}
}

void MSNContact::slotCopyThisUser()
{
	if( m_actionCopy )
		MSNProtocol::protocol()->copyContact( this, m_actionCopy->currentText() );
}

void MSNContact::slotBlockUser()
{
	if( isBlocked() )
		MSNProtocol::protocol()->contactUnBlock( m_msnId );
	else
		MSNProtocol::protocol()->slotBlockContact( m_msnId );
}

void MSNContact::slotViewHistory()
{
	kdDebug() << "MSN Plugin: slotViewHistory()" << endl;

	if (historyDialog != 0L)
	{
		historyDialog->raise();
	}
	else
	{
		historyDialog = new KopeteHistoryDialog(QString("msn_logs/%1.log").arg(m_msnId), displayName(), true, 50, 0, "MSNHistoryDialog");

		connect ( historyDialog, SIGNAL(closing()), this, SLOT(slotCloseHistoryDialog()) );
		connect ( historyDialog, SIGNAL(destroyed()), this, SLOT(slotHistoryDialogClosing()) );
	}
}

void MSNContact::slotCloseHistoryDialog()
{
	kdDebug() << "MSN Plugin: slotCloseHistoryDialog()" << endl;
	delete historyDialog;
}

void MSNContact::slotHistoryDialogClosing()
{
	kdDebug() << "MSN Plugin: slotHistoryDialogClosing()" << endl;
	if (historyDialog != 0L)
	{
		historyDialog = 0L;
	}
}

MSNContact::ContactStatus MSNContact::status() const
{
	switch ( m_status )
	{
		case MSNProtocol::NLN: // Online
		{
			return Online;
			break;
		}
		case MSNProtocol::BSY: // Busy
		case MSNProtocol::IDL: // Idle
		case MSNProtocol::AWY: // Away from computer
		case MSNProtocol::PHN: // On the phone
		case MSNProtocol::BRB: // Be right back
		case MSNProtocol::LUN: // Out to lunch
		{
			return Away;
			break;
		}

		default:
		{
			return Offline;
			break;
		}
	}
}

QString MSNContact::statusText() const
{
	switch ( m_status )
	{
		case MSNProtocol::BLO: // blocked
		{
			return i18n("Blocked");
			break;
		}
		case MSNProtocol::NLN: // Online
		{
			return i18n("Online");
			break;
		}
		case MSNProtocol::BSY: // Busy
		{
			return i18n("Busy");
			break;
		}
		case MSNProtocol::IDL: // Idle
		{
			return i18n("Idle");
			break;
		}
		case MSNProtocol::AWY: // Away from computer
		{
			return i18n("Away From Computer");
			break;
		}
		case MSNProtocol::PHN: // On the phone
		{
			return i18n("On The Phone");
			break;
		}
		case MSNProtocol::BRB: // Be right back
		{
			return i18n("Be Right Back");
			break;
		}
		case MSNProtocol::LUN: // Out to lunch
		{
			return i18n("Out To Lunch");
			break;
		}

		default:
		{
			return i18n("Offline");
		}
	}
}

QString MSNContact::statusIcon() const
{
	switch ( m_status )
	{
		case MSNProtocol::NLN: // Online
		{
			return "msn_online";
			break;
		}
		case MSNProtocol::BSY: // Busy
		case MSNProtocol::PHN: // On the phone
		{
			return "msn_na";
			break;
		}
		case MSNProtocol::IDL: // Idle
		case MSNProtocol::AWY: // Away from computer
		case MSNProtocol::BRB: // Be right back
		case MSNProtocol::LUN: // Out to lunch
		{
			return "msn_away";
			break;
		}
		default:
			return "msn_offline";
	}
}

int MSNContact::importance() const
{
	switch ( m_status )
	{
		case MSNProtocol::BLO: // blocked
		{
			return 1;
			break;
		}
		case MSNProtocol::NLN: // Online
		{
			return 20;
			break;
		}
		case MSNProtocol::BSY: // Busy
		{
			return 13;
			break;
		}
		case MSNProtocol::IDL: // Idle
		{
			return 15;
			break;
		}
		case MSNProtocol::AWY: // Away from computer
		{
			return 10;
			break;
		}
		case MSNProtocol::PHN: // On the phone
		{
			return 12;
			break;
		}
		case MSNProtocol::BRB: // Be right back
		{
			return 14;
			break;
		}
		case MSNProtocol::LUN: // Out to lunch
		{
			return 11;
			break;
		}

		default:
		{
			return 0;
		}
	}
}

QString MSNContact::msnId() const
{
	return m_msnId;
}

void MSNContact::setMsnId( const QString &id )
{
	m_msnId = id;
}

MSNProtocol::Status MSNContact::msnStatus() const
{
	return m_status;
}

void MSNContact::setMsnStatus( MSNProtocol::Status status )
{
	if( m_status == status )
		return;

	kdDebug() << "MSNContact::setMsnStatus: Setting status for " << m_msnId <<
		" to " << status << endl;
	m_status = status;

	emit statusChanged( this, MSNContact::status() );
}

bool MSNContact::isBlocked() const
{
	return m_blocked;
}

void MSNContact::setBlocked( bool blocked )
{
	if( m_blocked != blocked )
	{
		m_blocked = blocked;
		emit statusChanged( this, MSNContact::status() );
	}
}

bool MSNContact::isDeleted() const
{
	return m_deleted;
}

void MSNContact::setDeleted( bool deleted )
{
	m_deleted = deleted;
}

bool MSNContact::isAllowed() const
{
	return m_allowed;
}

void MSNContact::setAllowed( bool allowed )
{
	m_allowed = allowed;
}

QStringList MSNContact::groups()
{
	return m_groups;
}

void MSNContact::addToGroup( const QString &group )
{
	m_groups.append( group );
	if( m_movingToGroup == group )
	{
		kopeteapp->contactList()->moveContact( this, m_movingFromGroup, group );

		m_movingToGroup = QString::null;
		m_movingFromGroup = QString::null;
	}
}

void MSNContact::removeFromGroup( const QString &group )
{
	m_groups.remove( group );
}

#include "msncontact.moc"

// vim: noet ts=4 sts=4 sw=4:

<commit_msg>void MSNContact::slotChatThisUser() { 	return m_msnId; }<commit_after>/*
    msncontact.cpp - MSN Contact

    Copyright (c) 2002 Duncan Mac-Vicar Prett <duncan@kde.org>
              (c) 2002 Ryan Cumming           <bodnar42@phalynx.dhs.org>
              (c) 2002 Martijn Klingens       <klingens@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <kaction.h>
#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>

#include "kopete.h"
#include "kopetestdaction.h"
#include "msncontact.h"
#include "msnprotocol.h"

// Constructor for no-groups
MSNContact::MSNContact( QString &protocolId, const QString &msnId, const QString &displayName,
	const QString &group, KopeteMetaContact *parent )
	: KopeteContact( protocolId, parent )
{
	initContact( msnId, displayName, group );
}

void MSNContact::initContact( const QString &msnId, const QString &displayName,
	const QString &group )
{
	m_actionRemove = 0L;
	m_actionRemoveFromGroup = 0L;
	m_actionChat = 0L;
	m_actionInfo = 0L;
	m_actionHistory = 0L;
	m_actionMove = 0L;
	m_actionCopy = 0L;
	m_actionBlock = 0L;

	m_status = MSNProtocol::FLN;

	m_deleted = false;
	m_allowed = false;
	m_blocked = false;

	historyDialog = 0L;

	m_msnId = msnId;
	if( !group.isEmpty() )
		m_groups = group;
	hasLocalGroup = false;

	connect ( this, SIGNAL( chatToUser( QString ) ),
		MSNProtocol::protocol(),
		SLOT( slotStartChatSession( QString ) ) );

	setDisplayName( displayName );
}

void MSNContact::showContextMenu(QPoint point, QString /*group*/)
{
	KPopupMenu *popup = new KPopupMenu();
	popup->insertTitle( i18n( "%1 (%2)" ).arg( displayName() ).arg( msnId() ) );

	// Chat with user
	if( !m_actionChat )
	{
		m_actionChat = KopeteStdAction::sendMessage(
			this, SLOT( slotChatThisUser() ), this, "m_actionChat" );
	}
	m_actionChat->plug( popup );
	popup->insertSeparator();

	// View History
	if( !m_actionHistory )
	{
		m_actionHistory = KopeteStdAction::viewHistory( this,
			SLOT( slotViewHistory() ), this, "m_actionHistory" );
	}
	m_actionHistory->plug( popup );
	popup->insertSeparator();

	// Move Contact
	if( !m_actionMove )
	{
		m_actionMove = KopeteStdAction::moveContact( this,
			SLOT( slotMoveThisUser() ), this, "m_actionMove" );
	}
	m_actionMove->plug( popup );

	// Copy Contact
	if( !m_actionCopy )
	{
		m_actionCopy = new KListAction( i18n( "Copy Contact" ), "editcopy", 0,
			this, SLOT( slotCopyThisUser() ), this, "m_actionCopy" );
	}
	m_actionCopy->setItems( MSNProtocol::protocol()->groups() );
	m_actionCopy->plug( popup );

	// Remove From Group
	if( !m_actionRemoveFromGroup )
	{
		m_actionRemoveFromGroup = new KAction( i18n( "Remove From Group" ),
			"edittrash", 0, this, SLOT( slotRemoveFromGroup() ),
			this, "m_actionRemoveFromGroup" );
	}
	m_actionRemoveFromGroup->plug( popup );

	// Remove Contact
	if( !m_actionRemove )
	{
		m_actionRemove = KopeteStdAction::deleteContact( this,
			SLOT( slotRemoveThisUser() ), this, "m_actionRemove" );
	}
	m_actionRemove->plug( popup );

	popup->insertSeparator();

	// Block/unblock Contact
	delete m_actionBlock;
	QString label = isBlocked() ?
		i18n( "Unblock User" ) : i18n( "Block User" );
	m_actionBlock = new KAction( label,
		0, this, SLOT( slotBlockUser() ),
		this, "m_actionBlock" );
	m_actionBlock->plug( popup );

	popup->exec( point );
	delete popup;
}

void MSNContact::execute()
{
//	( new KopeteChatWindow( this, this, QString::null, 0 ) )->show(); // Debug, Martijn
	emit chatToUser( m_msnId );
}

QString MSNContact::id() const
{
	return m_msnId;
}

QString MSNContact::data() const
{
	return m_msnId;
}

void MSNContact::slotChatThisUser()
{
#warning "FIXME: what am I supposed to do"
	//return m_msnId;
}

void MSNContact::slotRemoveThisUser()
{
	MSNProtocol::protocol()->removeContact( this );
//	delete this;
}

void MSNContact::slotRemoveFromGroup()
{
	// FIXME: This slot needs to know to remove from WHICH group!
	// for now, remove from the first group...
	MSNProtocol::protocol()->removeFromGroup( this, m_groups.first() );
}

void MSNContact::moveToGroup( const QString &from, const QString &to )
{
	MSNProtocol::protocol()->moveContact( this, from, to );
}

void MSNContact::slotMoveThisUser()
{
	// FIXME: originating group should also be provided!
	if( m_actionMove )
	{
		//kdDebug() << "***** MOVE: Groups: " << m_groups.join( ", " ) << endl;
		if( m_movingToGroup == m_actionMove->currentText() )
		{
			kdDebug() << "MSNContact::slotMoveThisUser: Suppressing second "
				<< "slot invocation. Yes, I know this is ugly!" << endl;
		}
		else
			moveToGroup( m_groups.first(), m_actionMove->currentText() );
	}
}

void MSNContact::slotCopyThisUser()
{
	if( m_actionCopy )
		MSNProtocol::protocol()->copyContact( this, m_actionCopy->currentText() );
}

void MSNContact::slotBlockUser()
{
	if( isBlocked() )
		MSNProtocol::protocol()->contactUnBlock( m_msnId );
	else
		MSNProtocol::protocol()->slotBlockContact( m_msnId );
}

void MSNContact::slotViewHistory()
{
	kdDebug() << "MSN Plugin: slotViewHistory()" << endl;

	if (historyDialog != 0L)
	{
		historyDialog->raise();
	}
	else
	{
		historyDialog = new KopeteHistoryDialog(QString("msn_logs/%1.log").arg(m_msnId), displayName(), true, 50, 0, "MSNHistoryDialog");

		connect ( historyDialog, SIGNAL(closing()), this, SLOT(slotCloseHistoryDialog()) );
		connect ( historyDialog, SIGNAL(destroyed()), this, SLOT(slotHistoryDialogClosing()) );
	}
}

void MSNContact::slotCloseHistoryDialog()
{
	kdDebug() << "MSN Plugin: slotCloseHistoryDialog()" << endl;
	delete historyDialog;
}

void MSNContact::slotHistoryDialogClosing()
{
	kdDebug() << "MSN Plugin: slotHistoryDialogClosing()" << endl;
	if (historyDialog != 0L)
	{
		historyDialog = 0L;
	}
}

MSNContact::ContactStatus MSNContact::status() const
{
	switch ( m_status )
	{
		case MSNProtocol::NLN: // Online
		{
			return Online;
			break;
		}
		case MSNProtocol::BSY: // Busy
		case MSNProtocol::IDL: // Idle
		case MSNProtocol::AWY: // Away from computer
		case MSNProtocol::PHN: // On the phone
		case MSNProtocol::BRB: // Be right back
		case MSNProtocol::LUN: // Out to lunch
		{
			return Away;
			break;
		}

		default:
		{
			return Offline;
			break;
		}
	}
}

QString MSNContact::statusText() const
{
	switch ( m_status )
	{
		case MSNProtocol::BLO: // blocked
		{
			return i18n("Blocked");
			break;
		}
		case MSNProtocol::NLN: // Online
		{
			return i18n("Online");
			break;
		}
		case MSNProtocol::BSY: // Busy
		{
			return i18n("Busy");
			break;
		}
		case MSNProtocol::IDL: // Idle
		{
			return i18n("Idle");
			break;
		}
		case MSNProtocol::AWY: // Away from computer
		{
			return i18n("Away From Computer");
			break;
		}
		case MSNProtocol::PHN: // On the phone
		{
			return i18n("On The Phone");
			break;
		}
		case MSNProtocol::BRB: // Be right back
		{
			return i18n("Be Right Back");
			break;
		}
		case MSNProtocol::LUN: // Out to lunch
		{
			return i18n("Out To Lunch");
			break;
		}

		default:
		{
			return i18n("Offline");
		}
	}
}

QString MSNContact::statusIcon() const
{
	switch ( m_status )
	{
		case MSNProtocol::NLN: // Online
		{
			return "msn_online";
			break;
		}
		case MSNProtocol::BSY: // Busy
		case MSNProtocol::PHN: // On the phone
		{
			return "msn_na";
			break;
		}
		case MSNProtocol::IDL: // Idle
		case MSNProtocol::AWY: // Away from computer
		case MSNProtocol::BRB: // Be right back
		case MSNProtocol::LUN: // Out to lunch
		{
			return "msn_away";
			break;
		}
		default:
			return "msn_offline";
	}
}

int MSNContact::importance() const
{
	switch ( m_status )
	{
		case MSNProtocol::BLO: // blocked
		{
			return 1;
			break;
		}
		case MSNProtocol::NLN: // Online
		{
			return 20;
			break;
		}
		case MSNProtocol::BSY: // Busy
		{
			return 13;
			break;
		}
		case MSNProtocol::IDL: // Idle
		{
			return 15;
			break;
		}
		case MSNProtocol::AWY: // Away from computer
		{
			return 10;
			break;
		}
		case MSNProtocol::PHN: // On the phone
		{
			return 12;
			break;
		}
		case MSNProtocol::BRB: // Be right back
		{
			return 14;
			break;
		}
		case MSNProtocol::LUN: // Out to lunch
		{
			return 11;
			break;
		}

		default:
		{
			return 0;
		}
	}
}

QString MSNContact::msnId() const
{
	return m_msnId;
}

void MSNContact::setMsnId( const QString &id )
{
	m_msnId = id;
}

MSNProtocol::Status MSNContact::msnStatus() const
{
	return m_status;
}

void MSNContact::setMsnStatus( MSNProtocol::Status status )
{
	if( m_status == status )
		return;

	kdDebug() << "MSNContact::setMsnStatus: Setting status for " << m_msnId <<
		" to " << status << endl;
	m_status = status;

	emit statusChanged( this, MSNContact::status() );
}

bool MSNContact::isBlocked() const
{
	return m_blocked;
}

void MSNContact::setBlocked( bool blocked )
{
	if( m_blocked != blocked )
	{
		m_blocked = blocked;
		emit statusChanged( this, MSNContact::status() );
	}
}

bool MSNContact::isDeleted() const
{
	return m_deleted;
}

void MSNContact::setDeleted( bool deleted )
{
	m_deleted = deleted;
}

bool MSNContact::isAllowed() const
{
	return m_allowed;
}

void MSNContact::setAllowed( bool allowed )
{
	m_allowed = allowed;
}

QStringList MSNContact::groups()
{
	return m_groups;
}

void MSNContact::addToGroup( const QString &group )
{
	m_groups.append( group );
	if( m_movingToGroup == group )
	{
		kopeteapp->contactList()->moveContact( this, m_movingFromGroup, group );

		m_movingToGroup = QString::null;
		m_movingFromGroup = QString::null;
	}
}

void MSNContact::removeFromGroup( const QString &group )
{
	m_groups.remove( group );
}

#include "msncontact.moc"

// vim: noet ts=4 sts=4 sw=4:

<|endoftext|>


<commit_before>/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2012-2013 Phusion
 *
 *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

/*
 * Sets given environment variables, dumps the entire environment to
 * a given file (for diagnostics purposes), then execs the given command.
 *
 * This is a separate executable because it does quite
 * some non-async-signal-safe stuff that we can't do after
 * fork()ing from the Spawner and before exec()ing.
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <Utils/Base64.h>

using namespace std;
using namespace Passenger;

extern "C" {
	extern char **environ;
}

static void
changeWorkingDir(const char *dir) {
	int ret = chdir(dir);
	if (ret == 0) {
		setenv("PWD", dir, 1);
	} else {
		int e = errno;
		printf("!> Error\n");
		printf("!> \n");
		printf("Unable to change working directory to '%s': %s (errno=%d)\n",
			dir, strerror(e), e);
		fflush(stdout);
		exit(1);
	}
}

static void
setGivenEnvVars(const char *envvarsData) {
	string envvars = Base64::decode(envvarsData);
	const char *key = envvars.data();
	const char *end = envvars.data() + envvars.size();

	while (key < end) {
		const char *keyEnd = (const char *) memchr(key, '\0', end - key);
		if (keyEnd != NULL) {
			const char *value = keyEnd + 1;
			if (value < end) {
				const char *valueEnd = (const char *) memchr(value, '\0', end - value);
				if (valueEnd != NULL) {
					setenv(key, value, 1);
					key = valueEnd + 1;
				} else {
					break;
				}
			} else {
				break;
			}
		} else {
			break;
		}
	}
}

static void
dumpInformation() {
	const char *c_dir;
	if ((c_dir = getenv("PASSENGER_DEBUG_DIR")) == NULL) {
		return;
	}

	FILE *f;
	string dir = c_dir;

	f = fopen((dir + "/envvars").c_str(), "w");
	if (f != NULL) {
		int i = 0;
		while (environ[i] != NULL) {
			fputs(environ[i], f);
			putc('\n', f);
			i++;
		}
		fclose(f);
	}

	f = fopen((dir + "/user_info").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("id", "id", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	#ifdef __linux__
		// TODO: call helper-scripts/system-memory-stats.py
		f = fopen((dir + "/sysmemory").c_str(), "w");
		if (f != NULL) {
			pid_t pid = fork();
			if (pid == 0) {
				dup2(fileno(f), 1);
				execlp("free", "free", "-m", (char *) 0);
				_exit(1);
			} else if (pid == -1) {
				int e = errno;
				fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
					strerror(e), e);
			} else {
				waitpid(pid, NULL, 0);
			}
			fclose(f);
		}
	#endif
}

// Usage: SpawnPreparer <working directory> <envvars> <executable> <exec args...>
int
main(int argc, char *argv[]) {
	if (argc < 5) {
		fprintf(stderr, "Too few arguments.\n");
		exit(1);
	}

	const char *workingDir = argv[1];
	const char *envvars = argv[2];
	const char *executable = argv[3];
	char **execArgs = &argv[4];

	changeWorkingDir(workingDir);
	setGivenEnvVars(envvars);
	dumpInformation();

	// Print a newline just in case whatever executed us printed data
	// without a newline. Otherwise the next process's "!> I have control"
	// command will not be properly recognized.
	// https://code.google.com/p/phusion-passenger/issues/detail?id=842#c16
	printf("\n");
	fflush(stdout);

	execvp(executable, (char * const *) execArgs);
	int e = errno;
	fprintf(stderr, "*** ERROR ***: Cannot execute %s: %s (%d)\n",
		executable, strerror(e), e);
	return 1;
}
<commit_msg>SpawnPreparer.cpp should include stdlib.h for setenv(). Fixes issue #998<commit_after>/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2012-2013 Phusion
 *
 *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

/*
 * Sets given environment variables, dumps the entire environment to
 * a given file (for diagnostics purposes), then execs the given command.
 *
 * This is a separate executable because it does quite
 * some non-async-signal-safe stuff that we can't do after
 * fork()ing from the Spawner and before exec()ing.
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <Utils/Base64.h>

using namespace std;
using namespace Passenger;

extern "C" {
	extern char **environ;
}

static void
changeWorkingDir(const char *dir) {
	int ret = chdir(dir);
	if (ret == 0) {
		setenv("PWD", dir, 1);
	} else {
		int e = errno;
		printf("!> Error\n");
		printf("!> \n");
		printf("Unable to change working directory to '%s': %s (errno=%d)\n",
			dir, strerror(e), e);
		fflush(stdout);
		exit(1);
	}
}

static void
setGivenEnvVars(const char *envvarsData) {
	string envvars = Base64::decode(envvarsData);
	const char *key = envvars.data();
	const char *end = envvars.data() + envvars.size();

	while (key < end) {
		const char *keyEnd = (const char *) memchr(key, '\0', end - key);
		if (keyEnd != NULL) {
			const char *value = keyEnd + 1;
			if (value < end) {
				const char *valueEnd = (const char *) memchr(value, '\0', end - value);
				if (valueEnd != NULL) {
					setenv(key, value, 1);
					key = valueEnd + 1;
				} else {
					break;
				}
			} else {
				break;
			}
		} else {
			break;
		}
	}
}

static void
dumpInformation() {
	const char *c_dir;
	if ((c_dir = getenv("PASSENGER_DEBUG_DIR")) == NULL) {
		return;
	}

	FILE *f;
	string dir = c_dir;

	f = fopen((dir + "/envvars").c_str(), "w");
	if (f != NULL) {
		int i = 0;
		while (environ[i] != NULL) {
			fputs(environ[i], f);
			putc('\n', f);
			i++;
		}
		fclose(f);
	}

	f = fopen((dir + "/user_info").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("id", "id", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	f = fopen((dir + "/ulimit").c_str(), "w");
	if (f != NULL) {
		pid_t pid = fork();
		if (pid == 0) {
			dup2(fileno(f), 1);
			execlp("ulimit", "ulimit", "-a", (char *) 0);
			_exit(1);
		} else if (pid == -1) {
			int e = errno;
			fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
				strerror(e), e);
		} else {
			waitpid(pid, NULL, 0);
		}
		fclose(f);
	}

	#ifdef __linux__
		// TODO: call helper-scripts/system-memory-stats.py
		f = fopen((dir + "/sysmemory").c_str(), "w");
		if (f != NULL) {
			pid_t pid = fork();
			if (pid == 0) {
				dup2(fileno(f), 1);
				execlp("free", "free", "-m", (char *) 0);
				_exit(1);
			} else if (pid == -1) {
				int e = errno;
				fprintf(stderr, "Error: cannot fork a new process: %s (errno=%d)\n",
					strerror(e), e);
			} else {
				waitpid(pid, NULL, 0);
			}
			fclose(f);
		}
	#endif
}

// Usage: SpawnPreparer <working directory> <envvars> <executable> <exec args...>
int
main(int argc, char *argv[]) {
	if (argc < 5) {
		fprintf(stderr, "Too few arguments.\n");
		exit(1);
	}

	const char *workingDir = argv[1];
	const char *envvars = argv[2];
	const char *executable = argv[3];
	char **execArgs = &argv[4];

	changeWorkingDir(workingDir);
	setGivenEnvVars(envvars);
	dumpInformation();

	// Print a newline just in case whatever executed us printed data
	// without a newline. Otherwise the next process's "!> I have control"
	// command will not be properly recognized.
	// https://code.google.com/p/phusion-passenger/issues/detail?id=842#c16
	printf("\n");
	fflush(stdout);

	execvp(executable, (char * const *) execArgs);
	int e = errno;
	fprintf(stderr, "*** ERROR ***: Cannot execute %s: %s (%d)\n",
		executable, strerror(e), e);
	return 1;
}
<|endoftext|>