<commit_before>#include <iostream>

#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <SDL_net.h>

#include <json/writer.h>
#include <json/reader.h>
#include <json/value.h>

#include <grids/define.h>
#include <grids/protocol.h>

namespace Grids {
  void Protocol::setEventCallback(gevent_callback_t cb, void *userData) { eventCallback = cb; eventCallbackUserData = userData; }
  void Protocol::setConnectedCallback(gevent_callback_t cb, void *userData) { connectedCallback = cb; connectedCallbackUserData = userData; }

  int runEventLoopThreadEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->runEventLoop();
	return 0;
  }
  /*
  void *dispatchEventEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->dispatchEvent();
  }
  */
  Protocol::Protocol() {
    sock = 0;
    finishedMutex = SDL_CreateMutex();
    running = 0;
    connectedCallback = NULL;
  }

  Protocol::~Protocol() {
      SDL_DestroyMutex(finishedMutex);
  }

  bool Protocol::connectToNode(const char *address) {
    IPaddress ip;

    if (SDLNet_ResolveHost(&ip, (char *)address, GRIDS_PORT) == -1) {
      printf("Could not resolve hostname %s: %s\n", address, SDLNet_GetError());
      return 0;
    }

    sock = SDLNet_TCP_Open(&ip);
    if (! sock) {
      printf("Failed to connect to host %s: %s\n", address, SDLNet_GetError());
      return 0;
    }

    // hooray we are connnected! initialize protocol
    sendProtocolInitiationString();

    return 1;
  }

  void Protocol::sendProtocolInitiationString() {
      std::string initString = "==Grids/1.0/JSON";
    protocolWrite(initString);
  }

  int Protocol::protocolWrite(std::string &str) {
    uint32_t len = str.size();
    return protocolWrite(str.c_str(), len);
  }

  int Protocol::protocolWrite(const char *str, uint32_t len) {
      if (! sock) {
        std::cerr << "No socket exists but Protocol::protocolWrite was called\n";
        return -1;
      }

    unsigned int outstr_len = len + 4;
    char *outstr = (char *)malloc(outstr_len);

    SDLNet_Write32(len, outstr);
    memcpy((outstr + 4), str, len);

    int ret = SDLNet_TCP_Send(sock, outstr, outstr_len);
    free(outstr);

    if (ret != outstr_len) {
      printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
      // It may be good to disconnect sock because it is likely invalid now.
    }

    return ret;
  }

  void Protocol::closeConnection() {
    SDLNet_TCP_Close(sock);
  }

  // fast(?) version of turning a Grids::Value into a string
  std::string Protocol::stringifyValue(Value *val) {
    Json::FastWriter *writer = new Json::FastWriter();
    return writer->write((Json::Value) val);
  }

  void Protocol::sendRequest(std::string evt) {
    sendRequest(evt, NULL);
  }

  void Protocol::sendRequest(std::string evt, Value *args) {
    if (evt.empty())
      return;

    bool createdVal = 0;

    if (args == NULL) {
      args = new Value();
      createdVal = 1;
    }

    const static std::string methodkey = "_method";
    (*args)[methodkey] = evt;

    std::string valueStr = args->asString();

    protocolWrite(valueStr);

    if (createdVal)
        delete args;
  }

/*
  Json::Value Protocol::mapToJsonValue(gridsmap_t *m) {
    giterator mapIterator;

    Json::Value jsonVal;

    for(mapIterator = m->begin();
        mapIterator != m->end();
        mapIterator++) {

      gridskey_t key = mapIterator->first;
      gridsval_t val = mapIterator->second;

      jsonVal[key] = val;
    }

    return jsonVal;
  }
*/

  void Protocol::runEventLoop() {
    int bytesRead, socketReady;
    uint32_t incomingLength;
    char *buf;
    char *bufIncoming;

    setFinished(0);

    // create a socket set consisting of our socket, so that we can poll for data
    SDLNet_SocketSet sockSet = SDLNet_AllocSocketSet(1);

    int numused = SDLNet_TCP_AddSocket(sockSet, sock);
    if(numused == -1) {
        printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
        SDLNet_FreeSocketSet(sockSet);
        return;
    }

    while (! isFinished() && sock) {
        socketReady = SDLNet_CheckSockets(sockSet, 1000);
        if (socketReady == -1) {
            printf("Error in SDLNet_CheckSockets: %s\n", SDLNet_GetError());
            //most of the time this is a system error, where perror might help.
            perror("SDLNet_CheckSockets");
            break;
        }

        if (socketReady != 1) {
            // nothing to read
            continue;
        }

      // read in 4 byte length of message
      bytesRead = SDLNet_TCP_Recv(sock, &incomingLength, 4);

      if (bytesRead <= 0) {
        std::cerr << "Socket read error " << bytesRead << ": " << SDLNet_GetError() << "\n";
        continue;
      }

      incomingLength = SDLNet_Read32(&incomingLength);

      if (bytesRead != 4) {
        // socket broken most likely
        std::cerr << "failed to read from socket\n";
        // break;
        continue;
      }

      if (incomingLength > 1024 * 1024 * 1024) {
        // not going to read in more than a gig, f that
        std::cerr << "Got incoming message size: " << incomingLength << ". Skipping\n";
        continue;
      }

      // TODO: run in seperate thread

      // allocate space for incoming message + null byte
      buf = (char *)malloc(incomingLength + 1);

      std::cout << "incoming: " << incomingLength << "\n";
      uint32_t bytesRemaining = incomingLength;
      bufIncoming = buf;

      do {
        bytesRead = SDLNet_TCP_Recv(sock, bufIncoming, bytesRemaining);

        if (bytesRead > 0) {
          bytesRemaining -= bytesRead;
          bufIncoming += bytesRead;
        }

      } while ((bytesRead > 0) && bytesRemaining && ! isFinished());
      buf[incomingLength] = '\0';

        if (bytesRead == -1) {
          // o snap read error
          std::cerr << "Socket read error: " << bytesRead << "\n";
          free(buf);
          break;
        }

        if (bytesRead == 0) {
          // not chill
          std::cerr << "Didn't read any data when expecting message of " << incomingLength << " bytes\n";
          free(buf);
          continue;
        }

        if (bytesRead != incomingLength) {
          std::cerr << "Only read " << bytesRead << " bytes when expecting message of "
                    << incomingLength << " bytes\n";
          free(buf);
          continue;
        }

        // TODO: run in seperate thread
        std::string msg = buf;
        handleMessage(msg);

        free(buf);
    }

    SDLNet_FreeSocketSet(sockSet);
  }

  void Protocol::handleMessage(std::string &msg) {
    if (msg.size() < 2) return; // obv. bogus

    if (msg.find("==", 0, 2) == 0) {
      // protocol initiation message

      if (connectedCallback)
        connectedCallback(this, NULL, connectedCallbackUserData);

    } else if (msg.find("--", 0, 2) == 0) {
      // encrypted protocol message
    } else {
      // assume this is json for now
      Value root = parseJson(msg);

      // FIXME: this is slow and lame
      //gridsmap_t rootMap = jsonToMap(root);

      Event *evt = new Event(root["_method"].asString(), root);
      eventCallback(this, evt, eventCallbackUserData);
      delete evt;
    }
  }

/*
  Value Protocol::jsonToMap(Value &root) {
    // ghetto, should fix in the future
    Json::Value::Members memberList = root.getMemberNames();
    std::vector<std::string>::iterator iter;

    Vak outMap;

    for (iter = memberList.begin(); iter != memberList.end(); iter++) {
      Json::Value val = root[*iter];
      outMap[*iter] = val;
    }

    return outMap;
  }
*/

  Value Protocol::parseJson(std::string &msg) {
    Grids::Value root;
    Json::Reader reader;

    if (reader.parse(msg, root))
      return root;

    std::cerr << "Could not parse JSON: " << msg << "\n";
    return Value();
  }

  int Protocol::runEventLoopThreaded() {
    SDL_mutexP(finishedMutex);

    eventLoopThread = SDL_CreateThread(runEventLoopThreadEntryPoint, this);

    uint32_t threadId = SDL_GetThreadID(eventLoopThread);
    threadsFinished[threadId] = 0;

    SDL_mutexV(finishedMutex);

	return 0;
  }

  void Protocol::stopEventLoopThread() {
    setFinished(1);

    if (running) {
      SDL_WaitThread(eventLoopThread, NULL);
      running = 0;
    }
  }

  uint32_t Protocol::getThreadId() {
    return SDL_ThreadID();
  }

  bool Protocol::isFinished() {
    bool isFinished;

    SDL_mutexP(finishedMutex);
    isFinished = threadsFinished[getThreadId()];
    SDL_mutexV(finishedMutex);

    return isFinished;
  }

  void Protocol::setFinished(bool fin) {
    uint32_t threadId = getThreadId();

    if (! threadId)
        return;

    SDL_mutexP(finishedMutex);
    threadsFinished[threadId] = fin;
    SDL_mutexV(finishedMutex);
  }

}
<commit_msg>oops need to pass stuff by value to json::fastwriter not pointers<commit_after>#include <iostream>

#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <SDL_net.h>

#include <json/writer.h>
#include <json/reader.h>
#include <json/value.h>

#include <grids/define.h>
#include <grids/protocol.h>

namespace Grids {
  void Protocol::setEventCallback(gevent_callback_t cb, void *userData) { eventCallback = cb; eventCallbackUserData = userData; }
  void Protocol::setConnectedCallback(gevent_callback_t cb, void *userData) { connectedCallback = cb; connectedCallbackUserData = userData; }

  int runEventLoopThreadEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->runEventLoop();
	return 0;
  }
  /*
  void *dispatchEventEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->dispatchEvent();
  }
  */
  Protocol::Protocol() {
    sock = 0;
    finishedMutex = SDL_CreateMutex();
    running = 0;
    connectedCallback = NULL;
  }

  Protocol::~Protocol() {
      SDL_DestroyMutex(finishedMutex);
  }

  bool Protocol::connectToNode(const char *address) {
    IPaddress ip;

    if (SDLNet_ResolveHost(&ip, (char *)address, GRIDS_PORT) == -1) {
      printf("Could not resolve hostname %s: %s\n", address, SDLNet_GetError());
      return 0;
    }

    sock = SDLNet_TCP_Open(&ip);
    if (! sock) {
      printf("Failed to connect to host %s: %s\n", address, SDLNet_GetError());
      return 0;
    }

    // hooray we are connnected! initialize protocol
    sendProtocolInitiationString();

    return 1;
  }

  void Protocol::sendProtocolInitiationString() {
      std::string initString = "==Grids/1.0/JSON";
    protocolWrite(initString);
  }

  int Protocol::protocolWrite(std::string &str) {
    uint32_t len = str.size();
    return protocolWrite(str.c_str(), len);
  }

  int Protocol::protocolWrite(const char *str, uint32_t len) {
      if (! sock) {
        std::cerr << "No socket exists but Protocol::protocolWrite was called\n";
        return -1;
      }

    unsigned int outstr_len = len + 4;
    char *outstr = (char *)malloc(outstr_len);

    SDLNet_Write32(len, outstr);
    memcpy((outstr + 4), str, len);

    int ret = SDLNet_TCP_Send(sock, outstr, outstr_len);
    free(outstr);

    if (ret != outstr_len) {
      printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError());
      // It may be good to disconnect sock because it is likely invalid now.
    }

    return ret;
  }

  void Protocol::closeConnection() {
    SDLNet_TCP_Close(sock);
  }

  // fast(?) version of turning a Grids::Value into a string
  std::string Protocol::stringifyValue(Value *val) {
    Json::FastWriter *writer = new Json::FastWriter();
    return writer->write(*val);
  }

  void Protocol::sendRequest(std::string evt) {
    sendRequest(evt, NULL);
  }

  void Protocol::sendRequest(std::string evt, Value *args) {
    if (evt.empty())
      return;

    bool createdVal = 0;

    if (args == NULL) {
      args = new Value();
      createdVal = 1;
    }

    const static std::string methodkey = "_method";
    (*args)[methodkey] = evt;

    std::string valueStr = stringifyValue(args);

    protocolWrite(valueStr);

    if (createdVal)
        delete args;
  }

/*
  Json::Value Protocol::mapToJsonValue(gridsmap_t *m) {
    giterator mapIterator;

    Json::Value jsonVal;

    for(mapIterator = m->begin();
        mapIterator != m->end();
        mapIterator++) {

      gridskey_t key = mapIterator->first;
      gridsval_t val = mapIterator->second;

      jsonVal[key] = val;
    }

    return jsonVal;
  }
*/

  void Protocol::runEventLoop() {
    int bytesRead, socketReady;
    uint32_t incomingLength;
    char *buf;
    char *bufIncoming;

    setFinished(0);

    // create a socket set consisting of our socket, so that we can poll for data
    SDLNet_SocketSet sockSet = SDLNet_AllocSocketSet(1);

    int numused = SDLNet_TCP_AddSocket(sockSet, sock);
    if(numused == -1) {
        printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
        SDLNet_FreeSocketSet(sockSet);
        return;
    }

    while (! isFinished() && sock) {
        socketReady = SDLNet_CheckSockets(sockSet, 1000);
        if (socketReady == -1) {
            printf("Error in SDLNet_CheckSockets: %s\n", SDLNet_GetError());
            //most of the time this is a system error, where perror might help.
            perror("SDLNet_CheckSockets");
            break;
        }

        if (socketReady != 1) {
            // nothing to read
            continue;
        }

      // read in 4 byte length of message
      bytesRead = SDLNet_TCP_Recv(sock, &incomingLength, 4);

      if (bytesRead <= 0) {
        std::cerr << "Socket read error " << bytesRead << ": " << SDLNet_GetError() << "\n";
        continue;
      }

      incomingLength = SDLNet_Read32(&incomingLength);

      if (bytesRead != 4) {
        // socket broken most likely
        std::cerr << "failed to read from socket\n";
        // break;
        continue;
      }

      if (incomingLength > 1024 * 1024 * 1024) {
        // not going to read in more than a gig, f that
        std::cerr << "Got incoming message size: " << incomingLength << ". Skipping\n";
        continue;
      }

      // TODO: run in seperate thread

      // allocate space for incoming message + null byte
      buf = (char *)malloc(incomingLength + 1);

      std::cout << "incoming: " << incomingLength << "\n";
      uint32_t bytesRemaining = incomingLength;
      bufIncoming = buf;

      do {
        bytesRead = SDLNet_TCP_Recv(sock, bufIncoming, bytesRemaining);

        if (bytesRead > 0) {
          bytesRemaining -= bytesRead;
          bufIncoming += bytesRead;
        }

      } while ((bytesRead > 0) && bytesRemaining && ! isFinished());
      buf[incomingLength] = '\0';

        if (bytesRead == -1) {
          // o snap read error
          std::cerr << "Socket read error: " << bytesRead << "\n";
          free(buf);
          break;
        }

        if (bytesRead == 0) {
          // not chill
          std::cerr << "Didn't read any data when expecting message of " << incomingLength << " bytes\n";
          free(buf);
          continue;
        }

        if (bytesRead != incomingLength) {
          std::cerr << "Only read " << bytesRead << " bytes when expecting message of "
                    << incomingLength << " bytes\n";
          free(buf);
          continue;
        }

        // TODO: run in seperate thread
        std::string msg = buf;
        handleMessage(msg);

        free(buf);
    }

    SDLNet_FreeSocketSet(sockSet);
  }

  void Protocol::handleMessage(std::string &msg) {
    if (msg.size() < 2) return; // obv. bogus

    if (msg.find("==", 0, 2) == 0) {
      // protocol initiation message

      if (connectedCallback)
        connectedCallback(this, NULL, connectedCallbackUserData);

    } else if (msg.find("--", 0, 2) == 0) {
      // encrypted protocol message
    } else {
      // assume this is json for now
      Value root = parseJson(msg);

      // FIXME: this is slow and lame
      //gridsmap_t rootMap = jsonToMap(root);

      Event *evt = new Event(root["_method"].asString(), root);
      eventCallback(this, evt, eventCallbackUserData);
      delete evt;
    }
  }

/*
  Value Protocol::jsonToMap(Value &root) {
    // ghetto, should fix in the future
    Json::Value::Members memberList = root.getMemberNames();
    std::vector<std::string>::iterator iter;

    Vak outMap;

    for (iter = memberList.begin(); iter != memberList.end(); iter++) {
      Json::Value val = root[*iter];
      outMap[*iter] = val;
    }

    return outMap;
  }
*/

  Value Protocol::parseJson(std::string &msg) {
    Grids::Value root;
    Json::Reader reader;

    if (reader.parse(msg, root))
      return root;

    std::cerr << "Could not parse JSON: " << msg << "\n";
    return Value();
  }

  int Protocol::runEventLoopThreaded() {
    SDL_mutexP(finishedMutex);

    eventLoopThread = SDL_CreateThread(runEventLoopThreadEntryPoint, this);

    uint32_t threadId = SDL_GetThreadID(eventLoopThread);
    threadsFinished[threadId] = 0;

    SDL_mutexV(finishedMutex);

	return 0;
  }

  void Protocol::stopEventLoopThread() {
    setFinished(1);

    if (running) {
      SDL_WaitThread(eventLoopThread, NULL);
      running = 0;
    }
  }

  uint32_t Protocol::getThreadId() {
    return SDL_ThreadID();
  }

  bool Protocol::isFinished() {
    bool isFinished;

    SDL_mutexP(finishedMutex);
    isFinished = threadsFinished[getThreadId()];
    SDL_mutexV(finishedMutex);

    return isFinished;
  }

  void Protocol::setFinished(bool fin) {
    uint32_t threadId = getThreadId();

    if (! threadId)
        return;

    SDL_mutexP(finishedMutex);
    threadsFinished[threadId] = fin;
    SDL_mutexV(finishedMutex);
  }

}
<|endoftext|>


<commit_before>// Copyright 2009 Sebastien Martini (seb@dbzteam.org)
//
// 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 <keyczar/aes_key.h>

#include <keyczar/base/base64w.h>
#include <keyczar/base/logging.h>
#include <keyczar/base/stl_util-inl.h>
#include <keyczar/crypto_factory.h>
#include <keyczar/key_util.h>
#include <keyczar/message_digest_impl.h>
#include <keyczar/rand_impl.h>

namespace {

// This function returns 0 if it fails.
static int GetHMACSizeFromAESSize(int size) {
#ifdef COMPAT_KEYCZAR_06B
  return 160;
#else
  // These choices follow the NIST recommendations, see SP800-57 part1
  // pages 63-64.
  int hmac_size = 0;
  switch (size) {
    case 128:
      hmac_size = 160;
      break;
    case 192:
      hmac_size = 224;
      break;
    case 256:
      hmac_size = 256;
      break;
    default:
      NOTREACHED();
  }
  return hmac_size;
#endif
}

}  // namespace

namespace keyczar {

// static
AESKey* AESKey::CreateFromValue(const Value& root_key) {
  if (!root_key.IsType(Value::TYPE_DICTIONARY))
    return NULL;
  const DictionaryValue* aes_key = static_cast<const DictionaryValue*>(
      &root_key);

  std::string mode;
  if (!aes_key->GetString("mode", &mode))
    return NULL;

  CipherMode::Type cipher_mode = CipherMode::GetTypeFromName(mode);
  if (cipher_mode == CipherMode::UNDEF)
    return NULL;

  base::ScopedSafeString aes_key_string(new std::string());
  if (!util::SafeDeserializeString(*aes_key, "aesKeyString",
                                   aes_key_string.get()))
    return NULL;

  int size;
  if (!aes_key->GetInteger("size", &size))
    return NULL;

  if (size / 8 != static_cast<int>(aes_key_string->size())) {
    LOG(ERROR) << "Mismatch between key string length and declared size";
    return NULL;
  }

  if (!KeyType::IsValidCipherSize(KeyType::AES, size))
    return NULL;

  DictionaryValue* hmac_key_value = NULL;
  if (!aes_key->GetDictionary("hmacKey", &hmac_key_value))
    return NULL;

  if (hmac_key_value == NULL)
    return NULL;

  scoped_ptr<AESImpl> aes_key_impl(
      CryptoFactory::CreateAES(cipher_mode, *aes_key_string));
  if (aes_key_impl.get() == NULL)
    return NULL;

  scoped_refptr<HMACKey> hmac_key = HMACKey::CreateFromValue(*hmac_key_value);
  if (hmac_key == NULL)
    return NULL;

  if (GetHMACSizeFromAESSize(size) != hmac_key->size()) {
    LOG(ERROR) << "Incompatibles key sizes between AES key and HMAC key.";
    return NULL;
  }

  return new AESKey(aes_key_impl.release(), cipher_mode, size, hmac_key);
}

// static
AESKey* AESKey::GenerateKey(int size) {
  if (!KeyType::IsValidCipherSize(KeyType::AES, size))
    return NULL;

  // Currently only CBC mode is supported, so only CBC keys are generated.
  CipherMode::Type cipher_mode = CipherMode::CBC;

  scoped_ptr<AESImpl> aes_key_impl(CryptoFactory::GenerateAES(cipher_mode,
                                                              size));
  if (aes_key_impl.get() == NULL)
    return NULL;

  int hmac_size = GetHMACSizeFromAESSize(size);
  if (hmac_size == 0)
    return NULL;

  HMACKey* hmac_key = HMACKey::GenerateKey(hmac_size);
  if (hmac_key == NULL)
    return NULL;

  return new AESKey(aes_key_impl.release(), cipher_mode, size, hmac_key);
}

Value* AESKey::GetValue() const {
  if (aes_impl_.get() == NULL || hmac_key() == NULL)
    return NULL;

  scoped_ptr<DictionaryValue> aes_key(new DictionaryValue);
  if (aes_key.get() == NULL)
    return NULL;

  std::string mode = CipherMode::GetNameFromType(cipher_mode_);
  if (mode.empty())
    return NULL;
  if (!aes_key->SetString("mode", mode))
    return NULL;

  if (!util::SafeSerializeString(aes_impl_->GetKey(), "aesKeyString",
                                 aes_key.get()))
    return NULL;

  if (!aes_key->SetInteger("size", size()))
    return NULL;

  Value* hmac_key_value = hmac_key()->GetValue();
  if (hmac_key_value == NULL)
    return NULL;

  if (!aes_key->Set("hmacKey", hmac_key_value))
    return NULL;

  return aes_key.release();
}

bool AESKey::ComputeHash(std::string* hash, bool buggy) const {
  if (hash == NULL || aes_impl_.get() == NULL || hmac_key() == NULL ||
      hmac_key()->hmac_impl() == NULL)
    return false;

  // The buggy hash is only distinct from the correct hash when the key
  // has one or more leading zero bytes.
  if (buggy && aes_impl_->GetKey()[0] != 0) {
    return false;
  }

  scoped_ptr<MessageDigestImpl> digest_impl(CryptoFactory::SHA1());
  if (digest_impl.get() == NULL)
    return false;

  digest_impl->Init();
  AddToHash(aes_impl_->GetKey(), *digest_impl, buggy);
  digest_impl->Update(hmac_key()->hmac_impl()->GetKey());
  std::string full_hash;
  digest_impl->Final(&full_hash);
  CHECK_LE(Key::GetHashSize(), static_cast<int>(full_hash.length()));

  base::Base64WEncode(full_hash.substr(0, Key::GetHashSize()), hash);
  return true;
}

bool AESKey::Hash(std::string* hash) const {
  return ComputeHash(hash, false);
}

bool AESKey::BuggyHash(std::string* hash) const {
  return ComputeHash(hash, true);
}

bool AESKey::Encrypt(const std::string& plaintext,
                     std::string* ciphertext) const {
  if (ciphertext == NULL || aes_impl_.get() == NULL || hmac_key() == NULL)
    return false;

  std::string encrypted_plaintext;
  std::string iv;
  if (!aes_impl_->Encrypt(plaintext, &encrypted_plaintext, &iv))
    return false;

  // Fixme: trick to provide same data format than Java implementation.
  // AES use an IV size equals to its block size but for preserving the
  // compatibilty with Java implementation the IV is serialized on the
  // key length bytes. The rightmost bytes should not be considered by
  // mains AES implementations (including OpenSSL). These implementations
  // use a block size of 16 bytes even for AES 192 and AES 256. When an IV
  // of 32 bytes used with AES 256 is transmitted to OpenSSL, only its 16
  // first bytes are effectively, ignoring the last 16 bytes.
  if (iv.length() < aes_impl_->GetKey().length())
    iv.append(aes_impl_->GetKey().length() - iv.length(), '\0');

  std::string header;
  if (!Header(&header))
    return false;

  std::string all_bytes = header + iv + encrypted_plaintext;

  std::string signature;
  if (!hmac_key()->Sign(all_bytes, &signature))
    return false;

  ciphertext->assign(all_bytes + signature);
  return true;
}

bool AESKey::Decrypt(const std::string& ciphertext,
                     std::string* plaintext) const {
  if (plaintext == NULL || aes_impl_.get() == NULL || hmac_key() == NULL)
    return false;

  int key_size = size() / 8;
  int digest_size = hmac_key()->size() / 8;

  std::string data_bytes = ciphertext.substr(Key::GetHeaderSize());
  int data_bytes_len = data_bytes.length();

  std::string iv_bytes = data_bytes.substr(0, key_size);
  std::string aes_bytes = data_bytes.substr(
      key_size, data_bytes_len - digest_size - key_size);
  std::string signature_bytes = data_bytes.substr(data_bytes_len - digest_size);

  if (!hmac_key()->Verify(
          ciphertext.substr(0, ciphertext.length() - digest_size),
          signature_bytes))
    return false;

  if (!aes_impl_->Decrypt(iv_bytes, aes_bytes, plaintext))
    return false;

  return true;
}

}  // namespace keyczar
<commit_msg>fixed aes key IV for aes above 128<commit_after>// Copyright 2009 Sebastien Martini (seb@dbzteam.org)
//
// 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 <keyczar/aes_key.h>

#include <keyczar/base/base64w.h>
#include <keyczar/base/logging.h>
#include <keyczar/base/stl_util-inl.h>
#include <keyczar/crypto_factory.h>
#include <keyczar/key_util.h>
#include <keyczar/message_digest_impl.h>
#include <keyczar/rand_impl.h>

namespace {

// This function returns 0 if it fails.
static int GetHMACSizeFromAESSize(int size) {
#ifdef COMPAT_KEYCZAR_06B
  return 160;
#else
  // These choices follow the NIST recommendations, see SP800-57 part1
  // pages 63-64.
  int hmac_size = 0;
  switch (size) {
    case 128:
      hmac_size = 160;
      break;
    case 192:
      hmac_size = 224;
      break;
    case 256:
      hmac_size = 256;
      break;
    default:
      NOTREACHED();
  }
  return hmac_size;
#endif
}

}  // namespace

namespace keyczar {

// static
AESKey* AESKey::CreateFromValue(const Value& root_key) {
  if (!root_key.IsType(Value::TYPE_DICTIONARY))
    return NULL;
  const DictionaryValue* aes_key = static_cast<const DictionaryValue*>(
      &root_key);

  std::string mode;
  if (!aes_key->GetString("mode", &mode))
    return NULL;

  CipherMode::Type cipher_mode = CipherMode::GetTypeFromName(mode);
  if (cipher_mode == CipherMode::UNDEF)
    return NULL;

  base::ScopedSafeString aes_key_string(new std::string());
  if (!util::SafeDeserializeString(*aes_key, "aesKeyString",
                                   aes_key_string.get()))
    return NULL;

  int size;
  if (!aes_key->GetInteger("size", &size))
    return NULL;

  if (size / 8 != static_cast<int>(aes_key_string->size())) {
    LOG(ERROR) << "Mismatch between key string length and declared size";
    return NULL;
  }

  if (!KeyType::IsValidCipherSize(KeyType::AES, size))
    return NULL;

  DictionaryValue* hmac_key_value = NULL;
  if (!aes_key->GetDictionary("hmacKey", &hmac_key_value))
    return NULL;

  if (hmac_key_value == NULL)
    return NULL;

  scoped_ptr<AESImpl> aes_key_impl(
      CryptoFactory::CreateAES(cipher_mode, *aes_key_string));
  if (aes_key_impl.get() == NULL)
    return NULL;

  scoped_refptr<HMACKey> hmac_key = HMACKey::CreateFromValue(*hmac_key_value);
  if (hmac_key == NULL)
    return NULL;

  if (GetHMACSizeFromAESSize(size) != hmac_key->size()) {
    LOG(ERROR) << "Incompatibles key sizes between AES key and HMAC key.";
    return NULL;
  }

  return new AESKey(aes_key_impl.release(), cipher_mode, size, hmac_key);
}

// static
AESKey* AESKey::GenerateKey(int size) {
  if (!KeyType::IsValidCipherSize(KeyType::AES, size))
    return NULL;

  // Currently only CBC mode is supported, so only CBC keys are generated.
  CipherMode::Type cipher_mode = CipherMode::CBC;

  scoped_ptr<AESImpl> aes_key_impl(CryptoFactory::GenerateAES(cipher_mode,
                                                              size));
  if (aes_key_impl.get() == NULL)
    return NULL;

  int hmac_size = GetHMACSizeFromAESSize(size);
  if (hmac_size == 0)
    return NULL;

  HMACKey* hmac_key = HMACKey::GenerateKey(hmac_size);
  if (hmac_key == NULL)
    return NULL;

  return new AESKey(aes_key_impl.release(), cipher_mode, size, hmac_key);
}

Value* AESKey::GetValue() const {
  if (aes_impl_.get() == NULL || hmac_key() == NULL)
    return NULL;

  scoped_ptr<DictionaryValue> aes_key(new DictionaryValue);
  if (aes_key.get() == NULL)
    return NULL;

  std::string mode = CipherMode::GetNameFromType(cipher_mode_);
  if (mode.empty())
    return NULL;
  if (!aes_key->SetString("mode", mode))
    return NULL;

  if (!util::SafeSerializeString(aes_impl_->GetKey(), "aesKeyString",
                                 aes_key.get()))
    return NULL;

  if (!aes_key->SetInteger("size", size()))
    return NULL;

  Value* hmac_key_value = hmac_key()->GetValue();
  if (hmac_key_value == NULL)
    return NULL;

  if (!aes_key->Set("hmacKey", hmac_key_value))
    return NULL;

  return aes_key.release();
}

bool AESKey::ComputeHash(std::string* hash, bool buggy) const {
  if (hash == NULL || aes_impl_.get() == NULL || hmac_key() == NULL ||
      hmac_key()->hmac_impl() == NULL)
    return false;

  // The buggy hash is only distinct from the correct hash when the key
  // has one or more leading zero bytes.
  if (buggy && aes_impl_->GetKey()[0] != 0) {
    return false;
  }

  scoped_ptr<MessageDigestImpl> digest_impl(CryptoFactory::SHA1());
  if (digest_impl.get() == NULL)
    return false;

  digest_impl->Init();
  AddToHash(aes_impl_->GetKey(), *digest_impl, buggy);
  digest_impl->Update(hmac_key()->hmac_impl()->GetKey());
  std::string full_hash;
  digest_impl->Final(&full_hash);
  CHECK_LE(Key::GetHashSize(), static_cast<int>(full_hash.length()));

  base::Base64WEncode(full_hash.substr(0, Key::GetHashSize()), hash);
  return true;
}

bool AESKey::Hash(std::string* hash) const {
  return ComputeHash(hash, false);
}

bool AESKey::BuggyHash(std::string* hash) const {
  return ComputeHash(hash, true);
}

bool AESKey::Encrypt(const std::string& plaintext,
                     std::string* ciphertext) const {
  if (ciphertext == NULL || aes_impl_.get() == NULL || hmac_key() == NULL)
    return false;

  std::string encrypted_plaintext;
  std::string iv;
  if (!aes_impl_->Encrypt(plaintext, &encrypted_plaintext, &iv))
    return false;

  std::string header;
  if (!Header(&header))
    return false;

  std::string all_bytes = header + iv + encrypted_plaintext;

  std::string signature;
  if (!hmac_key()->Sign(all_bytes, &signature))
    return false;

  ciphertext->assign(all_bytes + signature);
  return true;
}

bool AESKey::Decrypt(const std::string& ciphertext,
                     std::string* plaintext) const {
  if (plaintext == NULL || aes_impl_.get() == NULL || hmac_key() == NULL)
    return false;

  int block_size = 128 / 8;
  int digest_size = hmac_key()->size() / 8;

  std::string data_bytes = ciphertext.substr(Key::GetHeaderSize());
  int data_bytes_len = data_bytes.length();

  std::string iv_bytes = data_bytes.substr(0, block_size);
  std::string aes_bytes = data_bytes.substr(
      block_size, data_bytes_len - digest_size - block_size);
  std::string signature_bytes = data_bytes.substr(data_bytes_len - digest_size);

  if (!hmac_key()->Verify(
          ciphertext.substr(0, ciphertext.length() - digest_size),
          signature_bytes))
    return false;

  if (!aes_impl_->Decrypt(iv_bytes, aes_bytes, plaintext))
    return false;

  return true;
}

}  // namespace keyczar
<|endoftext|>


<commit_before>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <kchashdb.h>
#include "DirectoryEntry.h"
#include "ExecUtil.h"
#include "FileLocker.h"
#include "FileUtil.h"
#include "MarcUtil.h"
#include "MediaTypeUtil.h"
#include "PdfUtil.h"
#include "SmartDownloader.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname << " file_offset marc_input marc_output full_text_db\n\n"
              << "       file_offset  Where to start reading a MARC data set from in marc_input.\n\n";
    std::exit(EXIT_FAILURE);
}

bool GetDocumentAndMediaType(const std::string &url, const unsigned timeout,
                             std::string * const document, std::string * const media_type)
{
    if (not SmartDownload(url, timeout, document)) {
        std::cerr << "Failed to download the document for " << url << " (timeout: " << timeout << " sec)\n";
        return false;
    }

    *media_type = MediaTypeUtil::GetMediaType(*document, /* auto_simplify = */ false);
    if (media_type->empty())
        return false;

    return true;
}

static std::map<std::string, std::string> marc_to_tesseract_language_codes_map {
    { "fre", "fra" },
    { "eng", "eng" },
    { "ger", "deu" },
    { "ita", "ita" },
    { "dut", "nld" },
    { "swe", "swe" },
    { "dan", "dan" },
    { "nor", "nor" },
    { "rus", "rus" },
    { "fin", "fin" },
    { "por", "por" },
    { "pol", "pol" },
    { "slv", "slv" },
    { "hun", "hun" },
    { "cze", "ces" },
    { "bul", "bul" },
};

std::string GetTesseractLanguageCode(const std::vector<DirectoryEntry> &dir_entries,
                                     const std::vector<std::string> &field_data)
{
    const auto map_iter(marc_to_tesseract_language_codes_map.find(
        MarcUtil::GetLanguageCode(dir_entries, field_data)));
    return (map_iter == marc_to_tesseract_language_codes_map.cend()) ? "" : map_iter->second;
}

bool GetTextFromImagePDF(const std::string &document, const std::string &media_type, const std::string &original_url,
                         const std::vector<DirectoryEntry> &dir_entries, const std::vector<std::string> &field_data,
                         const std::string &pdf_images_script, std::string * const extracted_text)
{
    extracted_text->clear();

    if (not StringUtil::StartsWith(media_type, "application/pdf") or not PdfDocContainsNoText(document))
        return false;

    std::cerr << "Found a PDF w/ no text.\n";

    const FileUtil::AutoTempFile auto_temp_file;
    const std::string &input_filename(auto_temp_file.getFilePath());
    if (not FileUtil::WriteString(input_filename, document))
        Error("failed to write the PDF to a temp file!");

    const FileUtil::AutoTempFile auto_temp_file2;
    const std::string &output_filename(auto_temp_file2.getFilePath());
    const std::string language_code(GetTesseractLanguageCode(dir_entries, field_data));
    static constexpr unsigned TIMEOUT(60); // in seconds
    if (ExecUtil::Exec(pdf_images_script, { input_filename, output_filename, language_code }, "",
		       ExecUtil::ExecMode::WAIT, TIMEOUT) != 0)
    {
        Warning("failed to execute conversion script \"" + pdf_images_script + "\" w/in "
                + std::to_string(TIMEOUT) + " seconds ! (original Url: " + original_url + ")");
        return true;
    }

    std::string plain_text;
    if (not ReadFile(output_filename, extracted_text))
        Error("failed to read OCR output!");

    if (extracted_text->empty())
        std::cerr << "Warning: OCR output is empty!\n";
    else
        std::cerr << "Whoohoo, got OCR'ed text.\n";

    return true;
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto _3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->second, "Rezension"))
            return true;
    } else {
        const auto z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->second, "Rezension"))
            return true;
    }

    return false;
}

/** Writes "media_type" and "document" to "db" and returns the unique key that was generated for the write. */
std::string FileLockedWriteDocumentWithMediaType(const std::string &media_type, const std::string &document,
                                                 const std::string &db_filename)
{
    FileLocker file_locker(db_filename, FileLocker::WRITE_ONLY);

    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE
                                 | kyotocabinet::HashDB::OTRUNCATE))
        Error("Failed to open database \"" + db_filename + "\" for writing ("
              + std::string(db.error().message()) + ")!");

    static unsigned key;
    ++key;
    const std::string key_as_string(std::to_string(key));
    db.add(key_as_string, "Content-type: " + media_type + "\r\n\r\n" + document);

    return key_as_string;
}

// Returns true if text has been successfully extrcated, else false.
bool ProcessRecord(FILE * const input, const std::string &marc_output_filename,
		   const std::string &pdf_images_script, const std::string &db_filename)
{
    std::shared_ptr<Leader> leader;
    std::vector<DirectoryEntry> dir_entries;
    std::vector<std::string> field_data;
    std::string err_msg;
    if (not MarcUtil::ReadNextRecord(input, leader, &dir_entries, &field_data, &err_msg))
	Error("failed to read MARC record!");

    ssize_t _856_index(MarcUtil::GetFieldIndex(dir_entries, "856"));
    if (_856_index == -1)
	Error("no 856 tag found!");

    constexpr unsigned PER_DOC_TIMEOUT(20);
    bool succeeded(false);

    const ssize_t dir_entry_count(static_cast<ssize_t>(dir_entries.size()));
    for (/* Empty! */; _856_index < dir_entry_count and dir_entries[_856_index].getTag() == "856"; ++_856_index) {
        Subfields subfields(field_data[_856_index]);
        const auto u_begin_end(subfields.getIterators('u'));
        if (u_begin_end.first == u_begin_end.second) // No subfield 'u'.
            continue;

        if (IsProbablyAReview(subfields))
            continue;

        std::string document, media_type;
        const std::string url(u_begin_end.first->second);
        if (not GetDocumentAndMediaType(url, PER_DOC_TIMEOUT, &document, &media_type))
            continue;

        std::string extracted_text, key;
        if (GetTextFromImagePDF(document, media_type, url, dir_entries, field_data,
                                pdf_images_script, &extracted_text))
            key = FileLockedWriteDocumentWithMediaType("text/plain", extracted_text, db_filename);
        else
            key = FileLockedWriteDocumentWithMediaType(media_type, document, db_filename);

        subfields.addSubfield('e', "http://localhost/cgi-bin/full_text_lookup?id=" + key);
        const std::string new_856_field(subfields.toString());
        MarcUtil::UpdateField(_856_index, new_856_field, leader, &dir_entries, &field_data);

	succeeded = true;
    }

    // Safely append the modified MARC data to the MARC output file:
    FileLocker file_locker(marc_output_filename, FileLocker::WRITE_ONLY);
    FILE * const marc_output(std::fopen(marc_output_filename.c_str(), "ab"));
    if (marc_output == nullptr)
        Error("can't open \"" + marc_output_filename + "\" for appending!");
    MarcUtil::ComposeAndWriteRecord(marc_output, dir_entries, field_data, leader);
    std::fclose(marc_output);

    return succeeded;
}

const std::string BASH_HELPER("pdf_images_to_text.sh");

std::string GetPathToPdfImagesScript(const char * const argv0) {
    char path[std::strlen(argv0) + 1];
    std::strcpy(path, argv0);
    const std::string pdf_images_script_path(ExecUtil::Which(BASH_HELPER));
    if (::access(pdf_images_script_path.c_str(), X_OK) != 0)
        Error("can't execute \"" + pdf_images_script_path + "\"!");
    return pdf_images_script_path;
}

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 5)
	Usage();

    long offset;
    if (not StringUtil::ToNumber(argv[1], &offset))
	Error("file offset must be a number!");

    const std::string marc_input_filename(argv[2]);
    FILE * const marc_input(std::fopen(marc_input_filename.c_str(), "rb"));
    if (marc_input == nullptr)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[3]);

    if (std::fseek(marc_input, offset, SEEK_SET) == -1)
	Error("failed to position " + marc_input_filename + " at offset " + std::to_string(offset)
	      + "! (" + std::to_string(errno) + ")");

    try {
        return ProcessRecord(marc_input, marc_output_filename, GetPathToPdfImagesScript(argv[0]), argv[4])
	       ? EXIT_SUCCESS : EXIT_FAILURE;
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<commit_msg>Don't truncate our Kyotocabinet database every time we add something to it.<commit_after>/** \brief Utility for augmenting MARC records with links to a local full-text database.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015 Universitätsbiblothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <memory>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <kchashdb.h>
#include "DirectoryEntry.h"
#include "ExecUtil.h"
#include "FileLocker.h"
#include "FileUtil.h"
#include "MarcUtil.h"
#include "MediaTypeUtil.h"
#include "PdfUtil.h"
#include "SmartDownloader.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static void Usage() __attribute__((noreturn));

static void Usage() {
    std::cerr << "Usage: " << ::progname << " file_offset marc_input marc_output full_text_db\n\n"
              << "       file_offset  Where to start reading a MARC data set from in marc_input.\n\n";
    std::exit(EXIT_FAILURE);
}

bool GetDocumentAndMediaType(const std::string &url, const unsigned timeout,
                             std::string * const document, std::string * const media_type)
{
    if (not SmartDownload(url, timeout, document)) {
        std::cerr << "Failed to download the document for " << url << " (timeout: " << timeout << " sec)\n";
        return false;
    }

    *media_type = MediaTypeUtil::GetMediaType(*document, /* auto_simplify = */ false);
    if (media_type->empty())
        return false;

    return true;
}

static std::map<std::string, std::string> marc_to_tesseract_language_codes_map {
    { "fre", "fra" },
    { "eng", "eng" },
    { "ger", "deu" },
    { "ita", "ita" },
    { "dut", "nld" },
    { "swe", "swe" },
    { "dan", "dan" },
    { "nor", "nor" },
    { "rus", "rus" },
    { "fin", "fin" },
    { "por", "por" },
    { "pol", "pol" },
    { "slv", "slv" },
    { "hun", "hun" },
    { "cze", "ces" },
    { "bul", "bul" },
};

std::string GetTesseractLanguageCode(const std::vector<DirectoryEntry> &dir_entries,
                                     const std::vector<std::string> &field_data)
{
    const auto map_iter(marc_to_tesseract_language_codes_map.find(
        MarcUtil::GetLanguageCode(dir_entries, field_data)));
    return (map_iter == marc_to_tesseract_language_codes_map.cend()) ? "" : map_iter->second;
}

bool GetTextFromImagePDF(const std::string &document, const std::string &media_type, const std::string &original_url,
                         const std::vector<DirectoryEntry> &dir_entries, const std::vector<std::string> &field_data,
                         const std::string &pdf_images_script, std::string * const extracted_text)
{
    extracted_text->clear();

    if (not StringUtil::StartsWith(media_type, "application/pdf") or not PdfDocContainsNoText(document))
        return false;

    std::cerr << "Found a PDF w/ no text.\n";

    const FileUtil::AutoTempFile auto_temp_file;
    const std::string &input_filename(auto_temp_file.getFilePath());
    if (not FileUtil::WriteString(input_filename, document))
        Error("failed to write the PDF to a temp file!");

    const FileUtil::AutoTempFile auto_temp_file2;
    const std::string &output_filename(auto_temp_file2.getFilePath());
    const std::string language_code(GetTesseractLanguageCode(dir_entries, field_data));
    static constexpr unsigned TIMEOUT(60); // in seconds
    if (ExecUtil::Exec(pdf_images_script, { input_filename, output_filename, language_code }, "",
		       ExecUtil::ExecMode::WAIT, TIMEOUT) != 0)
    {
        Warning("failed to execute conversion script \"" + pdf_images_script + "\" w/in "
                + std::to_string(TIMEOUT) + " seconds ! (original Url: " + original_url + ")");
        return true;
    }

    std::string plain_text;
    if (not ReadFile(output_filename, extracted_text))
        Error("failed to read OCR output!");

    if (extracted_text->empty())
        std::cerr << "Warning: OCR output is empty!\n";
    else
        std::cerr << "Whoohoo, got OCR'ed text.\n";

    return true;
}

// Checks subfields "3" and "z" to see if they start w/ "Rezension".
bool IsProbablyAReview(const Subfields &subfields) {
    const auto _3_begin_end(subfields.getIterators('3'));
    if (_3_begin_end.first != _3_begin_end.second) {
        if (StringUtil::StartsWith(_3_begin_end.first->second, "Rezension"))
            return true;
    } else {
        const auto z_begin_end(subfields.getIterators('z'));
        if (z_begin_end.first != z_begin_end.second
            and StringUtil::StartsWith(z_begin_end.first->second, "Rezension"))
            return true;
    }

    return false;
}

/** Writes "media_type" and "document" to "db" and returns the unique key that was generated for the write. */
std::string FileLockedWriteDocumentWithMediaType(const std::string &media_type, const std::string &document,
                                                 const std::string &db_filename)
{
    FileLocker file_locker(db_filename, FileLocker::WRITE_ONLY);

    kyotocabinet::HashDB db;
    if (not db.open(db_filename, kyotocabinet::HashDB::OWRITER | kyotocabinet::HashDB::OCREATE))
        Error("Failed to open database \"" + db_filename + "\" for writing ("
              + std::string(db.error().message()) + ")!");

    static unsigned key;
    ++key;
    const std::string key_as_string(std::to_string(key));
    db.add(key_as_string, "Content-type: " + media_type + "\r\n\r\n" + document);

    return key_as_string;
}

// Returns true if text has been successfully extrcated, else false.
bool ProcessRecord(FILE * const input, const std::string &marc_output_filename,
		   const std::string &pdf_images_script, const std::string &db_filename)
{
    std::shared_ptr<Leader> leader;
    std::vector<DirectoryEntry> dir_entries;
    std::vector<std::string> field_data;
    std::string err_msg;
    if (not MarcUtil::ReadNextRecord(input, leader, &dir_entries, &field_data, &err_msg))
	Error("failed to read MARC record!");

    ssize_t _856_index(MarcUtil::GetFieldIndex(dir_entries, "856"));
    if (_856_index == -1)
	Error("no 856 tag found!");

    constexpr unsigned PER_DOC_TIMEOUT(20);
    bool succeeded(false);

    const ssize_t dir_entry_count(static_cast<ssize_t>(dir_entries.size()));
    for (/* Empty! */; _856_index < dir_entry_count and dir_entries[_856_index].getTag() == "856"; ++_856_index) {
        Subfields subfields(field_data[_856_index]);
        const auto u_begin_end(subfields.getIterators('u'));
        if (u_begin_end.first == u_begin_end.second) // No subfield 'u'.
            continue;

        if (IsProbablyAReview(subfields))
            continue;

        std::string document, media_type;
        const std::string url(u_begin_end.first->second);
        if (not GetDocumentAndMediaType(url, PER_DOC_TIMEOUT, &document, &media_type))
            continue;

        std::string extracted_text, key;
        if (GetTextFromImagePDF(document, media_type, url, dir_entries, field_data,
                                pdf_images_script, &extracted_text))
            key = FileLockedWriteDocumentWithMediaType("text/plain", extracted_text, db_filename);
        else
            key = FileLockedWriteDocumentWithMediaType(media_type, document, db_filename);

        subfields.addSubfield('e', "http://localhost/cgi-bin/full_text_lookup?id=" + key);
        const std::string new_856_field(subfields.toString());
        MarcUtil::UpdateField(_856_index, new_856_field, leader, &dir_entries, &field_data);

	succeeded = true;
    }

    // Safely append the modified MARC data to the MARC output file:
    FileLocker file_locker(marc_output_filename, FileLocker::WRITE_ONLY);
    FILE * const marc_output(std::fopen(marc_output_filename.c_str(), "ab"));
    if (marc_output == nullptr)
        Error("can't open \"" + marc_output_filename + "\" for appending!");
    MarcUtil::ComposeAndWriteRecord(marc_output, dir_entries, field_data, leader);
    std::fclose(marc_output);

    return succeeded;
}

const std::string BASH_HELPER("pdf_images_to_text.sh");

std::string GetPathToPdfImagesScript(const char * const argv0) {
    char path[std::strlen(argv0) + 1];
    std::strcpy(path, argv0);
    const std::string pdf_images_script_path(ExecUtil::Which(BASH_HELPER));
    if (::access(pdf_images_script_path.c_str(), X_OK) != 0)
        Error("can't execute \"" + pdf_images_script_path + "\"!");
    return pdf_images_script_path;
}

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 5)
	Usage();

    long offset;
    if (not StringUtil::ToNumber(argv[1], &offset))
	Error("file offset must be a number!");

    const std::string marc_input_filename(argv[2]);
    FILE * const marc_input(std::fopen(marc_input_filename.c_str(), "rb"));
    if (marc_input == nullptr)
        Error("can't open \"" + marc_input_filename + "\" for reading!");

    const std::string marc_output_filename(argv[3]);

    if (std::fseek(marc_input, offset, SEEK_SET) == -1)
	Error("failed to position " + marc_input_filename + " at offset " + std::to_string(offset)
	      + "! (" + std::to_string(errno) + ")");

    try {
        return ProcessRecord(marc_input, marc_output_filename, GetPathToPdfImagesScript(argv[0]), argv[4])
	       ? EXIT_SUCCESS : EXIT_FAILURE;
    } catch (const std::exception &e) {
        Error("Caught exception: " + std::string(e.what()));
    }
}
<|endoftext|>


<commit_before>#include <znc/main.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/Chan.h>
#include <znc/IRCSock.h>
#include <znc/version.h>

#include <mutex>
#include <limits>

#include "twitchtmi.h"
#include "jload.h"

#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 7)
#error This module needs at least ZNC 1.7.0 or later.
#endif

TwitchTMI::~TwitchTMI()
{

}

bool TwitchTMI::OnLoad(const CString& sArgsi, CString& sMessage)
{
	OnBoot();

	if(GetNetwork())
	{
		std::list<CString> channels;

		for(CChan *ch: GetNetwork()->GetChans())
		{
			CString sname = ch->GetName().substr(1);
			channels.push_back(sname);

			ch->SetTopic(GetNV("tmi_topic_" + sname));
			ch->SetTopicOwner("jtv");
			ch->SetTopicDate(GetNV("tmi_topic_time_" + sname).ToULong());
		}

		CThreadPool::Get().addJob(new TwitchTMIJob(this, channels));
	}

	if(GetArgs().Token(0) != "FrankerZ")
		lastFrankerZ = std::numeric_limits<decltype(lastFrankerZ)>::max();

	PutIRC("CAP REQ :twitch.tv/membership");
	PutIRC("CAP REQ :twitch.tv/commands");
	PutIRC("CAP REQ :twitch.tv/tags");

	return true;
}

bool TwitchTMI::OnBoot()
{
	initCurl();

	timer = new TwitchTMIUpdateTimer(this);
	AddTimer(timer);

	return true;
}

void TwitchTMI::OnIRCConnected()
{
	PutIRC("CAP REQ :twitch.tv/membership");
	PutIRC("CAP REQ :twitch.tv/commands");
	PutIRC("CAP REQ :twitch.tv/tags");
}

CModule::EModRet TwitchTMI::OnUserRaw(CString &sLine)
{
	if(sLine.Left(5).Equals("WHO #"))
		return CModule::HALT;

	if(sLine.Left(5).Equals("AWAY "))
		return CModule::HALT;

	if(sLine.Left(12).Equals("TWITCHCLIENT"))
		return CModule::HALT;

	if(sLine.Left(9).Equals("JTVCLIENT"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnRawMessage(CMessage &msg)
{
	CString realNick = msg.GetTag("display-name").Trim_n();
	if (realNick == "")
		realNick = msg.GetNick().GetNick();

	if(msg.GetCommand().Equals("HOSTTARGET"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("CLEARCHAT"))
	{
		msg.SetCommand("NOTICE");
		if(msg.GetParam(1) != "")
		{
			msg.SetParam(1, msg.GetParam(1) + " was timed out.");
		}
		else
		{
			msg.SetParam(1, "Chat was cleared by a moderator.");
		}
	}
	else if(msg.GetCommand().Equals("USERSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("ROOMSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("RECONNECT"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("GLOBALUSERSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("WHISPER"))
	{
		msg.SetCommand("PRIVMSG");
	}
	else if(msg.GetCommand().Equals("USERNOTICE"))
	{
		CString msg_id = msg.GetTag("msg-id");
		CString new_msg = "<unknown usernotice " + msg_id + "> from " + realNick;
		if(msg_id == "sub" || msg_id == "resub") {
			CString dur = msg.GetTag("msg-param-months");
			CString plan = msg.GetTag("msg-param-sub-plan-name");
			CString txt = msg.GetParam(1).Trim_n();

			new_msg = realNick + " just ";
			if(msg_id == "sub")
				new_msg += "subscribed with a " + plan + " sub";
			else
				new_msg += "resubscribed with a " + plan + " sub for " + dur + " months";

			if(txt != "")
				new_msg += " saying: " + txt;
			else
				new_msg += "!";

			realNick = "jtv";
		} else if(msg_id == "subgift") {
			CString rec = msg.GetTag("msg-param-recipient-display-name");
			CString plan = msg.GetTag("msg-param-sub-plan-name");

			new_msg = realNick + " gifted a " + plan + " sub to " + rec + "!";
		} else if(msg_id == "raid") {
			CString cnt = msg.GetTag("msg-param-viewerCount");
			CString src = msg.GetTag("msg-param-displayName");

			new_msg = src + " is raiding with a party of " + cnt + "!";
		} else if(msg_id == "ritual") {
			CString rit = msg.GetTag("msg-param-ritual-name");
			CString txt = msg.GetParam(1).Trim_n();

			new_msg = "<unknown ritual " + rit + ">";
			if(rit == "new_chatter")
				new_msg = realNick + " is new here, saying: " + txt;
		} else if(msg_id == "giftpaidupgrade") {
			new_msg = msg.GetTag("system_msg");
		}

		realNick = "ttv";
		new_msg = ">>> " + new_msg + " <<<";
		msg.SetCommand("PRIVMSG");
		msg.SetParam(1, new_msg);
	}

	msg.GetNick().SetNick(realNick);

	if(realNick == "ttv")
		msg.GetNick().Parse("ttv!ttv@ttv.tmi.twitch.tv");

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnUserJoin(CString& sChannel, CString& sKey)
{
	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnPrivTextMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

void TwitchTMI::PutUserChanMessage(CChan *chan, const CString &from, const CString &msg)
{
	std::stringstream ss;
	ss << ":" << from << " PRIVMSG " << chan->GetName() << " :";
	CString s = ss.str();

	PutUser(s + msg);

	if(!chan->AutoClearChanBuffer() || !GetNetwork()->IsUserOnline() || chan->IsDetached())
		chan->AddBuffer(s + "{text}", msg);
}

CModule::EModRet TwitchTMI::OnChanTextMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	if(Message.GetText() == "FrankerZ" && std::time(nullptr) - lastFrankerZ > 10)
	{
		std::stringstream ss;
		CString mynick = GetNetwork()->GetIRCNick().GetNickMask();
		CChan *chan = Message.GetChan();

		ss << "PRIVMSG " << chan->GetName() << " :FrankerZ";
		PutIRC(ss.str());

		CThreadPool::Get().addJob(new GenericJob([]() {}, [this, chan, mynick]()
		{
			PutUserChanMessage(chan, mynick, "FrankerZ");
		}));

		lastFrankerZ = std::time(nullptr);
	}

	return CModule::CONTINUE;
}

bool TwitchTMI::OnServerCapAvailable(const CString &sCap)
{
	if(sCap == "twitch.tv/membership")
		return true;
	else if(sCap == "twitch.tv/tags")
		return true;
	else if(sCap == "twitch.tv/commands")
		return true;

	return false;
}

CModule::EModRet TwitchTMI::OnUserTextMessage(CTextMessage &msg)
{
	if(msg.GetTarget().Left(1).Equals("#"))
		return CModule::CONTINUE;

	msg.SetText(msg.GetText().insert(0, " ").insert(0, msg.GetTarget()).insert(0, "/w "));
	msg.SetTarget("#jtv");

	return CModule::CONTINUE;
}

TwitchTMIUpdateTimer::TwitchTMIUpdateTimer(TwitchTMI *tmod)
	:CTimer(tmod, 30, 0, "TwitchTMIUpdateTimer", "Downloads Twitch information")
	,mod(tmod)
{
}

void TwitchTMIUpdateTimer::RunJob()
{
	if(!mod->GetNetwork())
		return;

	std::list<CString> channels;

	for(CChan *chan: mod->GetNetwork()->GetChans())
	{
		channels.push_back(chan->GetName().substr(1));
	}

	CThreadPool::Get().addJob(new TwitchTMIJob(mod, channels));
}

static std::mutex job_thread_lock;

void TwitchTMIJob::runThread()
{
	std::unique_lock<std::mutex> lock_guard(job_thread_lock, std::try_to_lock);

	if(!lock_guard.owns_lock())
	{
		channels.clear();
		return;
	}

	titles.resize(channels.size());
	lives.resize(channels.size());

	int i = -1;
	for(const CString &channel: channels)
	{
		i++;

		std::stringstream ss, ss2;
		ss << "https://api.twitch.tv/kraken/channels/" << channel;
		ss2 << "https://api.twitch.tv/kraken/streams/" << channel;

		CString url = ss.str();
		CString url2 = ss2.str();

		Json::Value root = getJsonFromUrl(url.c_str(), "Accept: application/vnd.twitchtv.v3+json");
		Json::Value root2 = getJsonFromUrl(url2.c_str(), "Accept: application/vnd.twitchtv.v3+json");

		if(!root.isNull())
		{
			Json::Value &titleVal = root["status"];
			titles[i] = CString();

			if(!titleVal.isString())
				titleVal = root["title"];

			if(titleVal.isString())
			{
				titles[i] = titleVal.asString();
				titles[i].Trim();
			}
		}

		lives[i] = false;

		if(!root2.isNull())
		{
			Json::Value &streamVal = root2["stream"];

			if(!streamVal.isNull())
				lives[i] = true;
		}
    }
}

void TwitchTMIJob::runMain()
{
	int i = -1;
	for(const CString &channel: channels)
	{
		i++;

		CChan *chan = mod->GetNetwork()->FindChan(CString("#") + channel);
		if(!chan)
			continue;

		if(!titles[i].empty() && chan->GetTopic() != titles[i])
		{
			unsigned long topic_time = (unsigned long)time(nullptr);
			chan->SetTopic(titles[i]);
			chan->SetTopicOwner("jtv");
			chan->SetTopicDate(topic_time);

			std::stringstream ss;
			ss << ":jtv TOPIC #" << channel << " :" << titles[i];

			mod->PutUser(ss.str());

			mod->SetNV("tmi_topic_" + channel, titles[i], true);
			mod->SetNV("tmi_topic_time_" + channel, CString(topic_time), true);
		}

		auto it = mod->liveChannels.find(channel);
		if(it != mod->liveChannels.end())
		{
			if(!lives[i])
			{
				mod->liveChannels.erase(it);
				mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went offline! <<<");
			}
		}
		else
		{
			if(lives[i])
			{
				mod->liveChannels.insert(channel);
				mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went live! <<<");
			}
		}
	}
}

template<> void TModInfo<TwitchTMI>(CModInfo &info)
{
	info.SetWikiPage("Twitch");
	info.SetHasArgs(true);
}

NETWORKMODULEDEFS(TwitchTMI, "Twitch IRC helper module")
<commit_msg>Don't use potentially weird given names for sub tiers<commit_after>#include <znc/main.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/Chan.h>
#include <znc/IRCSock.h>
#include <znc/version.h>

#include <mutex>
#include <limits>

#include "twitchtmi.h"
#include "jload.h"

#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 7)
#error This module needs at least ZNC 1.7.0 or later.
#endif

TwitchTMI::~TwitchTMI()
{

}

bool TwitchTMI::OnLoad(const CString& sArgsi, CString& sMessage)
{
	OnBoot();

	if(GetNetwork())
	{
		std::list<CString> channels;

		for(CChan *ch: GetNetwork()->GetChans())
		{
			CString sname = ch->GetName().substr(1);
			channels.push_back(sname);

			ch->SetTopic(GetNV("tmi_topic_" + sname));
			ch->SetTopicOwner("jtv");
			ch->SetTopicDate(GetNV("tmi_topic_time_" + sname).ToULong());
		}

		CThreadPool::Get().addJob(new TwitchTMIJob(this, channels));
	}

	if(GetArgs().Token(0) != "FrankerZ")
		lastFrankerZ = std::numeric_limits<decltype(lastFrankerZ)>::max();

	PutIRC("CAP REQ :twitch.tv/membership");
	PutIRC("CAP REQ :twitch.tv/commands");
	PutIRC("CAP REQ :twitch.tv/tags");

	return true;
}

bool TwitchTMI::OnBoot()
{
	initCurl();

	timer = new TwitchTMIUpdateTimer(this);
	AddTimer(timer);

	return true;
}

void TwitchTMI::OnIRCConnected()
{
	PutIRC("CAP REQ :twitch.tv/membership");
	PutIRC("CAP REQ :twitch.tv/commands");
	PutIRC("CAP REQ :twitch.tv/tags");
}

CModule::EModRet TwitchTMI::OnUserRaw(CString &sLine)
{
	if(sLine.Left(5).Equals("WHO #"))
		return CModule::HALT;

	if(sLine.Left(5).Equals("AWAY "))
		return CModule::HALT;

	if(sLine.Left(12).Equals("TWITCHCLIENT"))
		return CModule::HALT;

	if(sLine.Left(9).Equals("JTVCLIENT"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnRawMessage(CMessage &msg)
{
	CString realNick = msg.GetTag("display-name").Trim_n();
	if (realNick == "")
		realNick = msg.GetNick().GetNick();

	if(msg.GetCommand().Equals("HOSTTARGET"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("CLEARCHAT"))
	{
		msg.SetCommand("NOTICE");
		if(msg.GetParam(1) != "")
		{
			msg.SetParam(1, msg.GetParam(1) + " was timed out.");
		}
		else
		{
			msg.SetParam(1, "Chat was cleared by a moderator.");
		}
	}
	else if(msg.GetCommand().Equals("USERSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("ROOMSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("RECONNECT"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("GLOBALUSERSTATE"))
	{
		return CModule::HALT;
	}
	else if(msg.GetCommand().Equals("WHISPER"))
	{
		msg.SetCommand("PRIVMSG");
	}
	else if(msg.GetCommand().Equals("USERNOTICE"))
	{
		CString msg_id = msg.GetTag("msg-id");
		CString new_msg = "<unknown usernotice " + msg_id + "> from " + realNick;
		if(msg_id == "sub" || msg_id == "resub") {
			CString dur = msg.GetTag("msg-param-months");
			CString plan = msg.GetTag("msg-param-sub-plan").MakeLower();
			CString txt = msg.GetParam(1).Trim_n();

			if(plan == "prime")
				plan = "Twitch Prime";
			else if(plan == "1000")
				plan = "Tier 1";
			else if(plan == "2000")
				plan = "Tier 2";
			else if(plan == "3000");
				plan = "Tier 3";

			new_msg = realNick + " just ";
			if(msg_id == "sub")
				new_msg += "subscribed with a " + plan + " sub";
			else
				new_msg += "resubscribed with a " + plan + " sub for " + dur + " months";

			if(txt != "")
				new_msg += " saying: " + txt;
			else
				new_msg += "!";

			realNick = "jtv";
		} else if(msg_id == "subgift") {
			CString rec = msg.GetTag("msg-param-recipient-display-name");
			CString plan = msg.GetTag("msg-param-sub-plan-name");

			new_msg = realNick + " gifted a " + plan + " sub to " + rec + "!";
		} else if(msg_id == "raid") {
			CString cnt = msg.GetTag("msg-param-viewerCount");
			CString src = msg.GetTag("msg-param-displayName");

			new_msg = src + " is raiding with a party of " + cnt + "!";
		} else if(msg_id == "ritual") {
			CString rit = msg.GetTag("msg-param-ritual-name");
			CString txt = msg.GetParam(1).Trim_n();

			new_msg = "<unknown ritual " + rit + ">";
			if(rit == "new_chatter")
				new_msg = realNick + " is new here, saying: " + txt;
		} else if(msg_id == "giftpaidupgrade") {
			new_msg = msg.GetTag("system_msg");
		}

		realNick = "ttv";
		new_msg = ">>> " + new_msg + " <<<";
		msg.SetCommand("PRIVMSG");
		msg.SetParam(1, new_msg);
	}

	msg.GetNick().SetNick(realNick);

	if(realNick == "ttv")
		msg.GetNick().Parse("ttv!ttv@ttv.tmi.twitch.tv");

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnUserJoin(CString& sChannel, CString& sKey)
{
	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnPrivTextMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

void TwitchTMI::PutUserChanMessage(CChan *chan, const CString &from, const CString &msg)
{
	std::stringstream ss;
	ss << ":" << from << " PRIVMSG " << chan->GetName() << " :";
	CString s = ss.str();

	PutUser(s + msg);

	if(!chan->AutoClearChanBuffer() || !GetNetwork()->IsUserOnline() || chan->IsDetached())
		chan->AddBuffer(s + "{text}", msg);
}

CModule::EModRet TwitchTMI::OnChanTextMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	if(Message.GetText() == "FrankerZ" && std::time(nullptr) - lastFrankerZ > 10)
	{
		std::stringstream ss;
		CString mynick = GetNetwork()->GetIRCNick().GetNickMask();
		CChan *chan = Message.GetChan();

		ss << "PRIVMSG " << chan->GetName() << " :FrankerZ";
		PutIRC(ss.str());

		CThreadPool::Get().addJob(new GenericJob([]() {}, [this, chan, mynick]()
		{
			PutUserChanMessage(chan, mynick, "FrankerZ");
		}));

		lastFrankerZ = std::time(nullptr);
	}

	return CModule::CONTINUE;
}

bool TwitchTMI::OnServerCapAvailable(const CString &sCap)
{
	if(sCap == "twitch.tv/membership")
		return true;
	else if(sCap == "twitch.tv/tags")
		return true;
	else if(sCap == "twitch.tv/commands")
		return true;

	return false;
}

CModule::EModRet TwitchTMI::OnUserTextMessage(CTextMessage &msg)
{
	if(msg.GetTarget().Left(1).Equals("#"))
		return CModule::CONTINUE;

	msg.SetText(msg.GetText().insert(0, " ").insert(0, msg.GetTarget()).insert(0, "/w "));
	msg.SetTarget("#jtv");

	return CModule::CONTINUE;
}

TwitchTMIUpdateTimer::TwitchTMIUpdateTimer(TwitchTMI *tmod)
	:CTimer(tmod, 30, 0, "TwitchTMIUpdateTimer", "Downloads Twitch information")
	,mod(tmod)
{
}

void TwitchTMIUpdateTimer::RunJob()
{
	if(!mod->GetNetwork())
		return;

	std::list<CString> channels;

	for(CChan *chan: mod->GetNetwork()->GetChans())
	{
		channels.push_back(chan->GetName().substr(1));
	}

	CThreadPool::Get().addJob(new TwitchTMIJob(mod, channels));
}

static std::mutex job_thread_lock;

void TwitchTMIJob::runThread()
{
	std::unique_lock<std::mutex> lock_guard(job_thread_lock, std::try_to_lock);

	if(!lock_guard.owns_lock())
	{
		channels.clear();
		return;
	}

	titles.resize(channels.size());
	lives.resize(channels.size());

	int i = -1;
	for(const CString &channel: channels)
	{
		i++;

		std::stringstream ss, ss2;
		ss << "https://api.twitch.tv/kraken/channels/" << channel;
		ss2 << "https://api.twitch.tv/kraken/streams/" << channel;

		CString url = ss.str();
		CString url2 = ss2.str();

		Json::Value root = getJsonFromUrl(url.c_str(), "Accept: application/vnd.twitchtv.v3+json");
		Json::Value root2 = getJsonFromUrl(url2.c_str(), "Accept: application/vnd.twitchtv.v3+json");

		if(!root.isNull())
		{
			Json::Value &titleVal = root["status"];
			titles[i] = CString();

			if(!titleVal.isString())
				titleVal = root["title"];

			if(titleVal.isString())
			{
				titles[i] = titleVal.asString();
				titles[i].Trim();
			}
		}

		lives[i] = false;

		if(!root2.isNull())
		{
			Json::Value &streamVal = root2["stream"];

			if(!streamVal.isNull())
				lives[i] = true;
		}
    }
}

void TwitchTMIJob::runMain()
{
	int i = -1;
	for(const CString &channel: channels)
	{
		i++;

		CChan *chan = mod->GetNetwork()->FindChan(CString("#") + channel);
		if(!chan)
			continue;

		if(!titles[i].empty() && chan->GetTopic() != titles[i])
		{
			unsigned long topic_time = (unsigned long)time(nullptr);
			chan->SetTopic(titles[i]);
			chan->SetTopicOwner("jtv");
			chan->SetTopicDate(topic_time);

			std::stringstream ss;
			ss << ":jtv TOPIC #" << channel << " :" << titles[i];

			mod->PutUser(ss.str());

			mod->SetNV("tmi_topic_" + channel, titles[i], true);
			mod->SetNV("tmi_topic_time_" + channel, CString(topic_time), true);
		}

		auto it = mod->liveChannels.find(channel);
		if(it != mod->liveChannels.end())
		{
			if(!lives[i])
			{
				mod->liveChannels.erase(it);
				mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went offline! <<<");
			}
		}
		else
		{
			if(lives[i])
			{
				mod->liveChannels.insert(channel);
				mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went live! <<<");
			}
		}
	}
}

template<> void TModInfo<TwitchTMI>(CModInfo &info)
{
	info.SetWikiPage("Twitch");
	info.SetHasArgs(true);
}

NETWORKMODULEDEFS(TwitchTMI, "Twitch IRC helper module")
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_unittest.h"
#include "webkit/glue/plugins/plugin_switches.h"

namespace {

// Platform-specific filename relative to the chrome executable.
#if defined(OS_WIN)
const wchar_t library_name[] = L"ppapi_tests.dll";
#elif defined(OS_MACOSX)
const char library_name[] = "ppapi_tests.plugin";
#elif defined(OS_POSIX)
const char library_name[] = "libppapi_tests.so";
#endif

}  // namespace

class PPAPITest : public UITest {
 public:
  PPAPITest() {
    // Append the switch to register the pepper plugin.
    // library name = <out dir>/<test_name>.<library_extension>
    // MIME type = application/x-ppapi-<test_name>
    FilePath plugin_dir;
    PathService::Get(base::DIR_EXE, &plugin_dir);

    FilePath plugin_lib = plugin_dir.Append(library_name);
    EXPECT_TRUE(file_util::PathExists(plugin_lib));

#if defined(OS_WIN)
    std::wstring pepper_plugin = plugin_lib.value();
#else
    std::wstring pepper_plugin = UTF8ToWide(plugin_lib.value());
#endif
    pepper_plugin.append(L";application/x-ppapi-tests");
    launch_arguments_.AppendSwitchWithValue(switches::kRegisterPepperPlugins,
                                            pepper_plugin);

    // The test sends us the result via a cookie.
    launch_arguments_.AppendSwitch(switches::kEnableFileCookies);

    // Some stuff is hung off of the testing interface which is not enabled
    // by default.
    launch_arguments_.AppendSwitch(switches::kEnablePepperTesting);
  }

  void RunTest(const std::string& test_case) {
    FilePath test_path;
    PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
    test_path = test_path.Append(FILE_PATH_LITERAL("third_party"));
    test_path = test_path.Append(FILE_PATH_LITERAL("ppapi"));
    test_path = test_path.Append(FILE_PATH_LITERAL("tests"));
    test_path = test_path.Append(FILE_PATH_LITERAL("test_case.html"));

    // Sanity check the file name.
    EXPECT_TRUE(file_util::PathExists(test_path));

    GURL::Replacements replacements;
    replacements.SetQuery(test_case.c_str(),
                          url_parse::Component(0, test_case.size()));
    GURL test_url = net::FilePathToFileURL(test_path);
    RunTestURL(test_url.ReplaceComponents(replacements));
  }

  void RunTestViaHTTP(const std::string& test_case) {
    const wchar_t kDocRoot[] = L"third_party/ppapi/tests";
    scoped_refptr<HTTPTestServer> server =
        HTTPTestServer::CreateForkingServer(kDocRoot);
    ASSERT_TRUE(server);
    RunTestURL(server->TestServerPage("files/test_case.html?" + test_case));
  }

 private:
  void RunTestURL(const GURL& test_url) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());
    ASSERT_TRUE(tab->NavigateToURL(test_url));
    std::string escaped_value =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "COMPLETION_COOKIE", action_max_timeout_ms());
    EXPECT_STREQ("PASS", escaped_value.c_str());
  }
};

#if defined(OS_MACOSX)
// TODO(brettw) this fails on Mac for unknown reasons.
TEST_F(PPAPITest, DISABLED_DeviceContext2D) {
#else
TEST_F(PPAPITest, DeviceContext2D) {
#endif
  RunTest("DeviceContext2D");
}

#if defined(OS_MACOSX)
// TODO(brettw) this fails on Mac for unknown reasons.
TEST_F(PPAPITest, DISABLED_ImageData) {
#else
TEST_F(PPAPITest, ImageData) {
#endif
  RunTest("ImageData");
}

TEST_F(PPAPITest, Buffer) {
  RunTest("Buffer");
}

#if defined(OS_MACOSX)
// http://crbug.com/48734
TEST_F(PPAPITest, FAILS_URLLoader) {
#else
TEST_F(PPAPITest, URLLoader) {
#endif
  RunTestViaHTTP("URLLoader");
}

TEST_F(PPAPITest, PaintAgggregator) {
  RunTestViaHTTP("PaintAggregator");
}

// http://crbug.com/48544
#if defined(OS_LINUX)
// TODO(jabdelmalek) this fails on Linux for unknown reasons.
TEST_F(PPAPITest, FAILS_Scrollbar) {
#else
TEST_F(PPAPITest, Scrollbar) {
#endif
  RunTest("Scrollbar");
}
<commit_msg>Disable PPAPITest.URLLoader on linux.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_unittest.h"
#include "webkit/glue/plugins/plugin_switches.h"

namespace {

// Platform-specific filename relative to the chrome executable.
#if defined(OS_WIN)
const wchar_t library_name[] = L"ppapi_tests.dll";
#elif defined(OS_MACOSX)
const char library_name[] = "ppapi_tests.plugin";
#elif defined(OS_POSIX)
const char library_name[] = "libppapi_tests.so";
#endif

}  // namespace

class PPAPITest : public UITest {
 public:
  PPAPITest() {
    // Append the switch to register the pepper plugin.
    // library name = <out dir>/<test_name>.<library_extension>
    // MIME type = application/x-ppapi-<test_name>
    FilePath plugin_dir;
    PathService::Get(base::DIR_EXE, &plugin_dir);

    FilePath plugin_lib = plugin_dir.Append(library_name);
    EXPECT_TRUE(file_util::PathExists(plugin_lib));

#if defined(OS_WIN)
    std::wstring pepper_plugin = plugin_lib.value();
#else
    std::wstring pepper_plugin = UTF8ToWide(plugin_lib.value());
#endif
    pepper_plugin.append(L";application/x-ppapi-tests");
    launch_arguments_.AppendSwitchWithValue(switches::kRegisterPepperPlugins,
                                            pepper_plugin);

    // The test sends us the result via a cookie.
    launch_arguments_.AppendSwitch(switches::kEnableFileCookies);

    // Some stuff is hung off of the testing interface which is not enabled
    // by default.
    launch_arguments_.AppendSwitch(switches::kEnablePepperTesting);
  }

  void RunTest(const std::string& test_case) {
    FilePath test_path;
    PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
    test_path = test_path.Append(FILE_PATH_LITERAL("third_party"));
    test_path = test_path.Append(FILE_PATH_LITERAL("ppapi"));
    test_path = test_path.Append(FILE_PATH_LITERAL("tests"));
    test_path = test_path.Append(FILE_PATH_LITERAL("test_case.html"));

    // Sanity check the file name.
    EXPECT_TRUE(file_util::PathExists(test_path));

    GURL::Replacements replacements;
    replacements.SetQuery(test_case.c_str(),
                          url_parse::Component(0, test_case.size()));
    GURL test_url = net::FilePathToFileURL(test_path);
    RunTestURL(test_url.ReplaceComponents(replacements));
  }

  void RunTestViaHTTP(const std::string& test_case) {
    const wchar_t kDocRoot[] = L"third_party/ppapi/tests";
    scoped_refptr<HTTPTestServer> server =
        HTTPTestServer::CreateForkingServer(kDocRoot);
    ASSERT_TRUE(server);
    RunTestURL(server->TestServerPage("files/test_case.html?" + test_case));
  }

 private:
  void RunTestURL(const GURL& test_url) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());
    ASSERT_TRUE(tab->NavigateToURL(test_url));
    std::string escaped_value =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "COMPLETION_COOKIE", action_max_timeout_ms());
    EXPECT_STREQ("PASS", escaped_value.c_str());
  }
};

#if defined(OS_MACOSX)
// TODO(brettw) this fails on Mac for unknown reasons.
TEST_F(PPAPITest, DISABLED_DeviceContext2D) {
#else
TEST_F(PPAPITest, DeviceContext2D) {
#endif
  RunTest("DeviceContext2D");
}

#if defined(OS_MACOSX)
// TODO(brettw) this fails on Mac for unknown reasons.
TEST_F(PPAPITest, DISABLED_ImageData) {
#else
TEST_F(PPAPITest, ImageData) {
#endif
  RunTest("ImageData");
}

TEST_F(PPAPITest, Buffer) {
  RunTest("Buffer");
}

#if defined(OS_MACOSX)
// http://crbug.com/48734
TEST_F(PPAPITest, FAILS_URLLoader) {
#elif defined(OS_POSIX)
// http://crbug.com/48944
TEST_F(PPAPITest, DISABLED_URLLoader) {
#else
TEST_F(PPAPITest, URLLoader) {
#endif
  RunTestViaHTTP("URLLoader");
}

TEST_F(PPAPITest, PaintAgggregator) {
  RunTestViaHTTP("PaintAggregator");
}

// http://crbug.com/48544
#if defined(OS_LINUX)
// TODO(jabdelmalek) this fails on Linux for unknown reasons.
TEST_F(PPAPITest, FAILS_Scrollbar) {
#else
TEST_F(PPAPITest, Scrollbar) {
#endif
  RunTest("Scrollbar");
}
<|endoftext|>


<commit_before>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa 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 Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef CPPA_FIXED_VECTOR_HPP
#define CPPA_FIXED_VECTOR_HPP

#include <iterator>
#include <algorithm>
#include <stdexcept>
#include <type_traits>
#include <initializer_list>

#include "cppa/config.hpp"

namespace cppa { namespace util {

/**
 * @brief A vector with a fixed maximum size.
 *
 * This implementation is highly optimized for arithmetic types and does
 * not call constructors or destructors properly.
 */
template<typename T, size_t MaxSize>
class fixed_vector {

    //static_assert(std::is_arithmetic<T>::value || std::is_pointer<T>::value,
    //              "T must be an arithmetic or pointer type");

    size_t m_size;

    // support for MaxSize == 0
    T m_data[(MaxSize > 0) ? MaxSize : 1];

 public:

    typedef T                   value_type;
    typedef size_t              size_type;
    typedef ptrdiff_t           difference_type;
    typedef value_type&         reference;
    typedef const value_type&   const_reference;
    typedef value_type*         pointer;
    typedef const value_type*   const_pointer;

    typedef T*                                      iterator;
    typedef const T*                                const_iterator;
    typedef std::reverse_iterator<iterator>         reverse_iterator;
    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;

    inline fixed_vector() : m_size(0) { }

    fixed_vector(const fixed_vector& other) : m_size(other.m_size) {
        std::copy(other.begin(), other.end(), begin());
    }

    fixed_vector& operator=(const fixed_vector& other) {
        resize(other.size());
        std::copy(other.begin(), other.end(), begin());
        return *this;
    }

    void resize(size_type s) {
        CPPA_REQUIRE(s <= MaxSize);
        m_size = s;
    }

    fixed_vector(std::initializer_list<T> init) : m_size(init.size()) {
        CPPA_REQUIRE(init.size() <= MaxSize);
        std::copy(init.begin(), init.end(), begin());
    }

    void assign(size_type count, const_reference value) {
        resize(count);
        std::fill(begin(), end(), value);
    }

    template<typename InputIterator>
    void assign(InputIterator first, InputIterator last,
                // dummy SFINAE argument
                typename std::iterator_traits<InputIterator>::pointer = 0) {
        resize(std::distance(first, last));
        std::copy(first, last, begin());
    }

    inline size_type size() const {
        return m_size;
    }

    inline size_type max_size() const {
        return MaxSize;
    }

    inline size_type capacity() const {
        return max_size() - size();
    }

    inline void clear() {
        m_size = 0;
    }

    inline bool empty() const {
        return m_size == 0;
    }

    inline bool not_empty() const {
        return m_size > 0;
    }

    inline bool full() const {
        return m_size == MaxSize;
    }

    inline void push_back(const_reference what) {
        CPPA_REQUIRE(!full());
        m_data[m_size++] = what;
    }

    inline void pop_back() {
        CPPA_REQUIRE(not_empty());
        --m_size;
    }

    inline reference at(size_type pos) {
        CPPA_REQUIRE(pos < m_size);
        return m_data[pos];
    }

    inline const_reference at(size_type pos) const {
        CPPA_REQUIRE(pos < m_size);
        return m_data[pos];
    }

    inline reference operator[](size_type pos) {
        return at(pos);
    }

    inline const_reference operator[](size_type pos) const {
        return at(pos);
    }

    inline iterator begin() {
        return m_data;
    }

    inline const_iterator begin() const {
        return m_data;
    }

    inline const_iterator cbegin() const {
        return begin();
    }

    inline iterator end() {
        return begin() + m_size;
    }

    inline const_iterator end() const {
        return begin() + m_size;
    }

    inline const_iterator cend() const {
        return end();
    }

    inline reverse_iterator rbegin() {
        return reverse_iterator(end());
    }

    inline const_reverse_iterator rbegin() const {
        return reverse_iterator(end());
    }

    inline const_reverse_iterator crbegin() const {
        return rbegin();
    }

    inline reverse_iterator rend() {
        return reverse_iterator(begin());
    }

    inline const_reverse_iterator rend() const {
        return reverse_iterator(begin());
    }

    inline const_reverse_iterator crend() const {
        return rend();
    }

    inline reference front() {
        CPPA_REQUIRE(!empty());
        return m_data[0];
    }

    inline const_reference front() const {
        CPPA_REQUIRE(!empty());
        return m_data[0];
    }

    inline reference back() {
        CPPA_REQUIRE(!empty());
        return m_data[m_size - 1];
    }

    inline const_reference back() const {
        CPPA_REQUIRE(!empty());
        return m_data[m_size - 1];
    }

    inline T* data() {
        return m_data;
    }

    inline const T* data() const {
        return m_data;
    }

    /**
     * @brief Inserts elements before the element pointed to by @p pos.
     * @throw std::length_error if
     *        <tt>size() + distance(first, last) > MaxSize</tt>
     */
    template<class InputIterator>
    inline void insert(iterator pos, InputIterator first, InputIterator last) {
        auto num_elements = std::distance(first, last);
        if ((size() + num_elements) > MaxSize) {
            throw std::length_error("fixed_vector::insert: too much elements");
        }
        if (pos == end()) {
            resize(size() + num_elements);
            std::copy(first, last, pos);
        }
        else {
            // move elements
            auto old_end = end();
            resize(size() + num_elements);
            std::copy_backward(pos, old_end, end());
            // insert new elements
            std::copy(first, last, pos);
        }
    }

};

} } // namespace cppa::util

#endif // CPPA_FIXED_VECTOR_HPP
<commit_msg>updated documentation<commit_after>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa 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 Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef CPPA_FIXED_VECTOR_HPP
#define CPPA_FIXED_VECTOR_HPP

#include <iterator>
#include <algorithm>
#include <stdexcept>
#include <type_traits>
#include <initializer_list>

#include "cppa/config.hpp"

namespace cppa { namespace util {

/**
 * @brief A vector with a fixed maximum size (uses an array internally).
 * @warning This implementation is highly optimized for arithmetic types and
 *          does not call constructors or destructors properly.
 */
template<typename T, size_t MaxSize>
class fixed_vector {

    //static_assert(std::is_arithmetic<T>::value || std::is_pointer<T>::value,
    //              "T must be an arithmetic or pointer type");

    size_t m_size;

    // support for MaxSize == 0
    T m_data[(MaxSize > 0) ? MaxSize : 1];

 public:

    typedef T                   value_type;
    typedef size_t              size_type;
    typedef ptrdiff_t           difference_type;
    typedef value_type&         reference;
    typedef const value_type&   const_reference;
    typedef value_type*         pointer;
    typedef const value_type*   const_pointer;

    typedef T*                                      iterator;
    typedef const T*                                const_iterator;
    typedef std::reverse_iterator<iterator>         reverse_iterator;
    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;

    inline fixed_vector() : m_size(0) { }

    fixed_vector(const fixed_vector& other) : m_size(other.m_size) {
        std::copy(other.begin(), other.end(), begin());
    }

    fixed_vector& operator=(const fixed_vector& other) {
        resize(other.size());
        std::copy(other.begin(), other.end(), begin());
        return *this;
    }

    void resize(size_type s) {
        CPPA_REQUIRE(s <= MaxSize);
        m_size = s;
    }

    fixed_vector(std::initializer_list<T> init) : m_size(init.size()) {
        CPPA_REQUIRE(init.size() <= MaxSize);
        std::copy(init.begin(), init.end(), begin());
    }

    void assign(size_type count, const_reference value) {
        resize(count);
        std::fill(begin(), end(), value);
    }

    template<typename InputIterator>
    void assign(InputIterator first, InputIterator last,
                // dummy SFINAE argument
                typename std::iterator_traits<InputIterator>::pointer = 0) {
        resize(std::distance(first, last));
        std::copy(first, last, begin());
    }

    inline size_type size() const {
        return m_size;
    }

    inline size_type max_size() const {
        return MaxSize;
    }

    inline size_type capacity() const {
        return max_size() - size();
    }

    inline void clear() {
        m_size = 0;
    }

    inline bool empty() const {
        return m_size == 0;
    }

    inline bool not_empty() const {
        return m_size > 0;
    }

    inline bool full() const {
        return m_size == MaxSize;
    }

    inline void push_back(const_reference what) {
        CPPA_REQUIRE(!full());
        m_data[m_size++] = what;
    }

    inline void pop_back() {
        CPPA_REQUIRE(not_empty());
        --m_size;
    }

    inline reference at(size_type pos) {
        CPPA_REQUIRE(pos < m_size);
        return m_data[pos];
    }

    inline const_reference at(size_type pos) const {
        CPPA_REQUIRE(pos < m_size);
        return m_data[pos];
    }

    inline reference operator[](size_type pos) {
        return at(pos);
    }

    inline const_reference operator[](size_type pos) const {
        return at(pos);
    }

    inline iterator begin() {
        return m_data;
    }

    inline const_iterator begin() const {
        return m_data;
    }

    inline const_iterator cbegin() const {
        return begin();
    }

    inline iterator end() {
        return begin() + m_size;
    }

    inline const_iterator end() const {
        return begin() + m_size;
    }

    inline const_iterator cend() const {
        return end();
    }

    inline reverse_iterator rbegin() {
        return reverse_iterator(end());
    }

    inline const_reverse_iterator rbegin() const {
        return reverse_iterator(end());
    }

    inline const_reverse_iterator crbegin() const {
        return rbegin();
    }

    inline reverse_iterator rend() {
        return reverse_iterator(begin());
    }

    inline const_reverse_iterator rend() const {
        return reverse_iterator(begin());
    }

    inline const_reverse_iterator crend() const {
        return rend();
    }

    inline reference front() {
        CPPA_REQUIRE(!empty());
        return m_data[0];
    }

    inline const_reference front() const {
        CPPA_REQUIRE(!empty());
        return m_data[0];
    }

    inline reference back() {
        CPPA_REQUIRE(!empty());
        return m_data[m_size - 1];
    }

    inline const_reference back() const {
        CPPA_REQUIRE(!empty());
        return m_data[m_size - 1];
    }

    inline T* data() {
        return m_data;
    }

    inline const T* data() const {
        return m_data;
    }

    /**
     * @brief Inserts elements before the element pointed to by @p pos.
     * @throw std::length_error if
     *        <tt>size() + distance(first, last) > MaxSize</tt>
     */
    template<class InputIterator>
    inline void insert(iterator pos, InputIterator first, InputIterator last) {
        auto num_elements = std::distance(first, last);
        if ((size() + num_elements) > MaxSize) {
            throw std::length_error("fixed_vector::insert: too much elements");
        }
        if (pos == end()) {
            resize(size() + num_elements);
            std::copy(first, last, pos);
        }
        else {
            // move elements
            auto old_end = end();
            resize(size() + num_elements);
            std::copy_backward(pos, old_end, end());
            // insert new elements
            std::copy(first, last, pos);
        }
    }

};

} } // namespace cppa::util

#endif // CPPA_FIXED_VECTOR_HPP
<|endoftext|>