Mixe for Privacy and Anonymity in the Internet
Classes | Static Public Member Functions | Static Public Attributes | Private Types | Private Member Functions | Static Private Member Functions | Private Attributes | Static Private Attributes | Friends
CAAccountingInstance Class Reference

This is the AI (accounting instance or abrechnungsinstanz in german) class. More...

#include <CAAccountingInstance.hpp>

Collaboration diagram for CAAccountingInstance:
[legend]

List of all members.

Classes

struct  t_aiqueueitem

Static Public Member Functions

static SINT32 init (CAFirstMix *callingMix)
 Returns a reference to the Singleton instance.
static SINT32 clean ()
static UINT32 getAuthFlags (fmHashTableEntry *pHashEntry)
static SINT32 cleanupTableEntry (fmHashTableEntry *pHashEntry)
 This should always be called when closing a JAP connection to cleanup the data structures.
static SINT32 initTableEntry (fmHashTableEntry *pHashEntry)
 This must be called whenever a JAP is connecting to init our per-user data structures.
static SINT32 handleJapPacket (fmHashTableEntry *pHashEntry, bool a_bControlMessage, bool a_bMessageToJAP)
 This should be called by the FirstMix for every incoming Jap packet.
static SINT32 isIPAddressBlocked (const UINT8 ip[4])
 Check if an IP address is temporarily blocked by the accounting instance.
static SINT32 processJapMessage (fmHashTableEntry *pHashEntry, const XERCES_CPP_NAMESPACE::DOMDocument *a_DomDoc)
 Handle a user (xml) message sent to us by the Jap through the ControlChannel.
static UINT32 getNrOfUsers ()
static SINT32 loginProcessStatus (fmHashTableEntry *pHashEntry)
static SINT32 finishLoginProcess (fmHashTableEntry *pHashEntry)
 this method is for the corresponding CAFirstMix login thread to verify the result of the settlement.
static SINT32 settlementTransaction ()
static SettleEntry__handleSettleResult (CAXMLCostConfirmation *pCC, CAXMLErrorMessage *pErrMsg, CAAccountingDBInterface *dbInterface, UINT64 a_iSettlementTransactionNr)
static void __commitSettlementToDatabase (SettleEntry *entryList, CAAccountingDBInterface *dbInterface)
 only for internal use during the settleTransaction because the global settlement lock is not acquired
static void __commitSettlementToLoginTable (SettleEntry *entryList)
 only for internal use during the settleTransaction because no login table locks are acquired
static SINT32 newSettlementTransaction ()
static SINT32 __newSettlementTransaction (UINT32 *nrOfSettledCCs)

Static Public Attributes

static const SINT32 HANDLE_PACKET_CONNECTION_OK = 1
static const SINT32 HANDLE_PACKET_CONNECTION_UNCHECKED = 4
static const SINT32 HANDLE_PACKET_HOLD_CONNECTION = 0
static const SINT32 HANDLE_PACKET_PREPARE_FOR_CLOSING_CONNECTION = 2
static const SINT32 HANDLE_PACKET_CLOSE_CONNECTION = 3
static const UINT32 MAX_SETTLED_CCS = 10

Private Types

typedef struct t_aiqueueitem aiQueueItem

Private Member Functions

 CAAccountingInstance (CAFirstMix *callingMix)
 private Constructor
 ~CAAccountingInstance ()
 private destructor
UINT32 handleCostConfirmation (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Handles a cost confirmation sent by a jap.
UINT32 handleCostConfirmation_internal (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Handles a cost confirmation sent by a jap.
UINT32 handleAccountCertificate (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Handles an account certificate of a newly connected Jap.
UINT32 handleAccountCertificate_internal (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Handles an account certificate of a newly connected Jap.
UINT32 handleChallengeResponse (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Checks the response of the challenge-response auth.
UINT32 handleChallengeResponse_internal (tAiAccountingInfo *pAccInfo, DOMElement *root)
 Handles the response to our challenge.
bool cascadeMatchesCC (CAXMLCostConfirmation *pCC)
SINT32 prepareCCRequest (CAMix *callingMix, UINT8 *a_AiName)
 creating the xml of a new CC is really the responsability of the CAXMLCostConfirmation class knowledge about the structure of a CC's XML should be encapsulated in it TODO: add constructor to that class that takes accountnumber, transferredbytes etc as params (should use a template internally into which it only inserts accNumber and bytes,to speed things up TODO: add toXMLElement method then replace manually building the xml here with contructing a CAXMLCostConfirmation and just add the xml returned by its toXMLElement method

Static Private Member Functions

static SINT32 handleJapPacket_internal (fmHashTableEntry *pHashEntry, bool a_bControlMessage, bool a_bMessageToJAP)
 Called by FirstMix for each incoming JAP packet.
static UINT64 unlockLogin (fmHashTableEntry *ownerRef)
 release login (particularly for use in error case) this function is thread-safe.
static void processJapMessageLoginHelper (fmHashTableEntry *pHashEntry, UINT32 handlerReturnvalue, bool finishLogin)
static SINT32 getPrepaidBytes (tAiAccountingInfo *pAccInfos)
static SINT32 makeInitialCCRequest (CAXMLCostConfirmation *pCC, XERCES_CPP_NAMESPACE::DOMDocument *&doc, SINT32 prepaidBytes)
 new initialCCRequest containing the last CC and the prepaid bytes (this is a replacement for sending the prepaid with the challenge which is now deprecated).
static SINT32 makeCCRequest (const UINT64 accountNumber, const UINT64 transferredBytes, XERCES_CPP_NAMESPACE::DOMDocument *&doc)
static SINT32 sendInitialCCRequest (tAiAccountingInfo *pAccInfo, CAXMLCostConfirmation *pCC, SINT32 prepaidBytes)
static SINT32 sendCCRequest (tAiAccountingInfo *pAccInfo)
static SINT32 makeAccountRequest (XERCES_CPP_NAMESPACE::DOMDocument *&doc)
static SINT32 sendAILoginConfirmation (tAiAccountingInfo *pAccInfo, const UINT32 code, UINT8 *message)
static SINT32 returnKickout (tAiAccountingInfo *pAccInfo)
 When receiving this message, the Mix should kick the user out immediately.
static SINT32 returnPrepareKickout (tAiAccountingInfo *pAccInfo, CAXMLErrorMessage *a_error)
static void setPrepaidBytesToZero (tAiAccountingInfo *pAccInfo)
static void setPrepaidBytesToZero_internal (tAiAccountingInfo *pAccInfo)
static THREAD_RETURN processThread (void *a_param)
 The main loop of the AI thread - reads messages from the queue and starts process threads for these messages.

Private Attributes

CAThreadPoolm_aiThreadPool
 reads messages from the queue and processes them
CAMutexm_pMutex
 this is for synchronizing the write access to the HashEntries
Hashtablem_certHashCC
Hashtablem_currentAccountsHashtable
 Stores the account number of all users currently logged in.
CAFirstMixm_mix
UINT8m_AiName
 the name of this accounting instance
UINT8m_currentCascade
 current cascade (identified by the concatenated hash values of the price certificates)
UINT8 ** m_allHashes
 The hash values of the Mixes ordered beginning with the AI Mix.
UINT32 m_allHashesLen
CAAccountingBIInterfacem_pPiInterface
 the interface to the database
UINT32 m_iSoftLimitBytes
UINT32 m_iHardLimitBytes
CATempIPBlockListm_pIPBlockList
 Users that get kicked out because they sent no authentication certificate get their IP appended to this list.
CAAccountingSettleThreadm_pSettleThread
 Signature verifying instance for BI signatures.
bool m_bThreadRunning
volatile UINT64 m_nextSettleNr
volatile UINT64 m_settleWaitNr
CAConditionVariablem_pSettlementMutex
volatile UINT32 m_seqBIConnErrors

Static Private Attributes

static const UINT64 PACKETS_BEFORE_NEXT_CHECK = 100
static const UINT32 MAX_TOLERATED_MULTIPLE_LOGINS = 10
static
XERCES_CPP_NAMESPACE::DOMDocument * 
m_preparedCCRequest
static CAAccountingInstancems_pInstance = NULL
 Singleton: This is the reference to the only instance of this class.
static SINT32 m_prepaidBytesMinimum = 0
static volatile UINT64 m_iCurrentSettleTransactionNr = 0

Friends

class CAFirstMixA
class CAFirstMix

Detailed Description

This is the AI (accounting instance or abrechnungsinstanz in german) class.

Its purpose is to count packets for every user and to decide wether the connection should be closed (e.g. when a user is betraying us, or simply when the account is empty and the user refuses to charge it).

It is a singleton class, only one instance exists at a time. On the first call to getInstance() the initialization is performed.

Definition at line 112 of file CAAccountingInstance.hpp.


Member Typedef Documentation

Definition at line 215 of file CAAccountingInstance.hpp.


Constructor & Destructor Documentation

private Constructor

Definition at line 77 of file CAAccountingInstance.cpp.

References Hashtable::compareUINT64(), CACmdLnOptions::getAiID(), CALibProxytest::getOptions(), CACmdLnOptions::getPaymentHardLimit(), CACmdLnOptions::getPaymentSoftLimit(), Hashtable::hashUINT64(), m_AiName, m_aiThreadPool, m_allHashes, m_allHashesLen, m_certHashCC, m_currentAccountsHashtable, m_currentCascade, m_iHardLimitBytes, m_iSoftLimitBytes, m_mix, m_nextSettleNr, m_pMutex, m_pPiInterface, m_pSettlementMutex, m_pSettleThread, m_seqBIConnErrors, m_settleWaitNr, MAX_LOGIN_QUEUE, NUM_LOGIN_WORKER_TRHEADS, prepareCCRequest(), CAMsg::printMsg(), Hashtable::put(), CAAccountingBIInterface::setPIServerConfiguration(), Hashtable::stringCompare(), and Hashtable::stringHash().

Referenced by init().

  {
    CAMsg::printMsg( LOG_DEBUG, "AccountingInstance initialising\n" );
    m_seqBIConnErrors = 0;
    m_pMutex = new CAMutex();
    //m_pIPBlockList = new CATempIPBlockList(60000);
    m_pSettlementMutex = new CAConditionVariable();
    //m_loginHashTableChangeInProgress = false; //for synchronizing settlementTransactions of loginThreads and SettlementThread.
    m_nextSettleNr = 0; //The next thread's number to alter the login hash table
    m_settleWaitNr = 0; //The wait number when it is equal to nextSettle number it's the thread's turn has to to
              //alter the login hash table
    // initialize Database connection
    //m_dbInterface = new CAAccountingDBInterface();
    m_pPiInterface = new CAAccountingBIInterface();
    m_mix = callingMix;
    /*if(m_dbInterface->initDBConnection() != E_SUCCESS)
    {
      CAMsg::printMsg( LOG_ERR, "**************** AccountingInstance: Could not connect to DB!\n");
      exit(1);
    }*/

    // initialize JPI signature tester
    m_AiName = new UINT8[256];
    CALibProxytest::getOptions()->getAiID(m_AiName, 256);
    if (CALibProxytest::getOptions()->getBI() != NULL)
    {
      //m_pJpiVerifyingInstance = CALibProxytest::getOptions()->getBI()->getVerifier();
      m_pPiInterface->setPIServerConfiguration(CALibProxytest::getOptions()->getBI());
    }
    m_iHardLimitBytes = CALibProxytest::getOptions()->getPaymentHardLimit();
    m_iSoftLimitBytes = CALibProxytest::getOptions()->getPaymentSoftLimit();

    prepareCCRequest(callingMix, m_AiName);

    m_currentAccountsHashtable =
      new Hashtable((UINT32 (*)(void *))Hashtable::hashUINT64, (SINT32 (*)(void *,void *))Hashtable::compareUINT64, 2000);

    m_certHashCC =
      new Hashtable((UINT32 (*)(void *))Hashtable::stringHash, (SINT32 (*)(void *,void *))Hashtable::stringCompare);
    for (UINT32 i = 0; i < m_allHashesLen; i++)
    {
      m_certHashCC->put(m_allHashes[i], m_allHashes[i]);
    }

    // launch BI settleThread
    m_pSettleThread = new CAAccountingSettleThread(m_currentAccountsHashtable,
                            m_currentCascade);

    m_aiThreadPool = new CAThreadPool(NUM_LOGIN_WORKER_TRHEADS, MAX_LOGIN_QUEUE, false);
  }

Here is the call graph for this function:

private destructor

Definition at line 132 of file CAAccountingInstance.cpp.

References BEGIN_STACK, Hashtable::clear(), FINISH_STACK, Hashtable::getMutex(), HASH_EMPTY_DELETE, HASH_EMPTY_NONE, INIT_STACK, CAMutex::lock(), m_AiName, m_aiThreadPool, m_allHashes, m_allHashesLen, m_certHashCC, m_currentAccountsHashtable, m_currentCascade, m_pMutex, m_pPiInterface, m_pSettlementMutex, m_pSettleThread, CAMsg::printMsg(), Hashtable::remove(), and CAMutex::unlock().

  {
    INIT_STACK;
    BEGIN_STACK("~CAAccountingInstance");

    /*
     * avoid calling this desctructor concurrently!
     */
    m_pMutex->lock();

    CAMsg::printMsg( LOG_DEBUG, "AccountingInstance dying\n" );
    if (m_pSettleThread)
    {
      CAMsg::printMsg( LOG_DEBUG, "deleting m_pSettleThread\n" );
      //m_pSettleThread->settle();
      delete m_pSettleThread;
      m_pSettleThread = NULL;
    }

    if (m_aiThreadPool)
    {
      CAMsg::printMsg( LOG_DEBUG, "deleting m_aiThreadPool\n" );
      delete m_aiThreadPool;
      m_aiThreadPool = NULL;
    }

    /*if (m_dbInterface)
    {
      CAMsg::printMsg( LOG_DEBUG, "termintaing dbConnection\n" );
      m_dbInterface->terminateDBConnection();
      delete m_dbInterface;
    }
    m_dbInterface = NULL;*/


    delete m_pPiInterface;
    m_pPiInterface = NULL;

    delete m_pSettlementMutex;
    m_pSettlementMutex = NULL;
    //delete m_pIPBlockList;
    //m_pIPBlockList = NULL;


    delete[] m_AiName;
    m_AiName = NULL;

    if (m_currentAccountsHashtable)
    {
      m_currentAccountsHashtable->getMutex()->lock();
      CAMsg::printMsg( LOG_DEBUG, "CAAccountingInstance: Clearing accounts hashtable...\n");
      m_currentAccountsHashtable->clear(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
      CAMsg::printMsg( LOG_DEBUG, "CAAccountingInstance: Deleting accounts hashtable...\n" );
      m_currentAccountsHashtable->getMutex()->unlock();
      delete m_currentAccountsHashtable;
      m_currentAccountsHashtable = NULL;
    }
    m_currentAccountsHashtable = NULL;
    CAMsg::printMsg( LOG_DEBUG, "CAAccountingInstance: Accounts hashtable deleted.\n" );

    delete[] m_currentCascade;
    m_currentCascade = NULL;

    if(m_certHashCC != NULL)
    {
      for (UINT32 i = 0; i < m_allHashesLen; i++)
      {
        UINT8* certHash = (UINT8*)m_certHashCC->remove(m_allHashes[i]);
      }
      m_certHashCC->clear(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
      delete m_certHashCC;
      m_certHashCC = NULL;
    }

    if (m_allHashes)
    {
      for (UINT32 i = 0; i < m_allHashesLen; i++)
      {
        delete m_allHashes[i];
        m_allHashes[i] = NULL;
      }
      delete[] m_allHashes;
    }
    m_allHashes = NULL;

    m_pMutex->unlock();

    delete m_pMutex;
    m_pMutex = NULL;
    FINISH_STACK("~CAAccountingInstance");

    CAMsg::printMsg( LOG_DEBUG, "AccountingInstance dying finished.\n" );
  }

Here is the call graph for this function:


Member Function Documentation

only for internal use during the settleTransaction because the global settlement lock is not acquired

Definition at line 3198 of file CAAccountingInstance.cpp.

References SettleEntry::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_INVALID_ACCOUNT, AUTH_UNKNOWN, SettleEntry::authFlags, SettleEntry::authRemoveFlags, CAAccountingDBInterface::clearAccountStatus(), SettleEntry::diffBytes, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAAccountingDBInterface::getPrepaidAmount(), m_currentCascade, ms_pInstance, SettleEntry::nextEntry, CAMsg::printMsg(), SettleEntry::storedStatus, and CAAccountingDBInterface::storePrepaidAmount().

Referenced by __newSettlementTransaction().

{
  SettleEntry *entry = entryList, *nextEntry = NULL;
  while (entry != NULL && dbInterface != NULL)
  {
    if (entry->authFlags & (AUTH_INVALID_ACCOUNT | AUTH_UNKNOWN))
    {
      dbInterface->storePrepaidAmount(
          entry->accountNumber, 0, ms_pInstance->m_currentCascade);
    }
    else if (entry->diffBytes)
    {
      // user is currently not logged in; set correct prepaid bytes in DB
      SINT32 prepaidBytes =
        dbInterface->getPrepaidAmount(entry->accountNumber,
            ms_pInstance->m_currentCascade, true);
      if (prepaidBytes > 0)
      {
        if (entry->diffBytes < 0 || entry->diffBytes >= (UINT64)prepaidBytes)
        {
          prepaidBytes = 0;
        }
        else
        {
          prepaidBytes -= entry->diffBytes;
        }
        dbInterface->storePrepaidAmount(
          entry->accountNumber, prepaidBytes, ms_pInstance->m_currentCascade);
      }
    }

    if( !(entry->authFlags & (AUTH_ACCOUNT_EMPTY | AUTH_BLOCKED | AUTH_INVALID_ACCOUNT)) &&
      entry->storedStatus != 0)
    {
      dbInterface->clearAccountStatus(entry->accountNumber);
      switch (entry->storedStatus)
      {
        case CAXMLErrorMessage::ERR_ACCOUNT_EMPTY:
        {
          CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account empty status for %llu\n",
              entry->accountNumber);
          entry->authRemoveFlags |= AUTH_ACCOUNT_EMPTY;
          break;
        }
        case CAXMLErrorMessage::ERR_BLOCKED:
        {
          CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account blocked status for %llu\n",
              entry->accountNumber);
          entry->authRemoveFlags |= AUTH_BLOCKED;
          break;
        }
        case CAXMLErrorMessage::ERR_KEY_NOT_FOUND:
        {
          CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account invalid status for %llu\n",
              entry->accountNumber);
          entry->authRemoveFlags |= AUTH_INVALID_ACCOUNT;
          break;
        }
      }
    }
    nextEntry = entry->nextEntry;
    entry = nextEntry;
  }
}

Here is the call graph for this function:

only for internal use during the settleTransaction because no login table locks are acquired

Definition at line 3264 of file CAAccountingInstance.cpp.

References SettleEntry::accountNumber, AccountLoginHashEntry::authFlags, SettleEntry::authFlags, AccountLoginHashEntry::authRemoveFlags, SettleEntry::authRemoveFlags, AccountLoginHashEntry::confirmedBytes, SettleEntry::confirmedBytes, Hashtable::getValue(), m_currentAccountsHashtable, ms_pInstance, and SettleEntry::nextEntry.

Referenced by __newSettlementTransaction().

{
  SettleEntry *entry = entryList, *nextEntry = NULL;
  while (entry != NULL)
  {
    AccountLoginHashEntry* loginEntry =
            (AccountLoginHashEntry*) (ms_pInstance->m_currentAccountsHashtable->getValue(&(entry->accountNumber)));
    if (loginEntry)
    {
      // the user is currently logged in
      loginEntry->authFlags |= entry->authFlags;
      loginEntry->authRemoveFlags |= entry->authRemoveFlags;
      if (entry->confirmedBytes > 0 &&
        loginEntry->confirmedBytes < entry->confirmedBytes)
      {
        loginEntry->confirmedBytes = entry->confirmedBytes;
      }
    }
    nextEntry = entry->nextEntry;
    entry = nextEntry;
  }
}

Here is the call graph for this function:

SettleEntry * CAAccountingInstance::__handleSettleResult ( CAXMLCostConfirmation pCC,
CAXMLErrorMessage pErrMsg,
CAAccountingDBInterface dbInterface,
UINT64  a_iSettlementTransactionNr 
) [static]

Definition at line 3024 of file CAAccountingInstance.cpp.

References SettleEntry::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_INVALID_ACCOUNT, AUTH_OUTDATED_CC, AUTH_SETTLED_ONCE, AUTH_UNKNOWN, AUTH_WAITING_FOR_FIRST_SETTLED_CC, SettleEntry::authFlags, SettleEntry::authRemoveFlags, SettleEntry::confirmedBytes, CAAccountingDBInterface::deleteCC(), SettleEntry::diffBytes, E_SUCCESS, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_DATABASE_ERROR, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAXMLErrorMessage::ERR_OK, CAXMLErrorMessage::ERR_OUTDATED_CC, CAXMLErrorMessage::ERR_SUCCESS_BUT_WITH_ERRORS, CAXMLCostConfirmation::getAccountNumber(), CAAccountingDBInterface::getAccountStatus(), CAXMLErrorMessage::getDescription(), CAXMLErrorMessage::getErrorCode(), CAXMLErrorMessage::getMessageObject(), CAXMLCostConfirmation::getTransferredBytes(), m_currentCascade, CAAccountingDBInterface::markAsSettled(), ms_pInstance, print64(), CAMsg::printMsg(), CAAccountingDBInterface::storeAccountStatus(), CAAccountingDBInterface::storeCostConfirmation(), and SettleEntry::storedStatus.

Referenced by __newSettlementTransaction().

{
  bool bDeleteCC = false;
  UINT32 authFlags = 0;
  UINT32 authRemoveFlags = 0;
  UINT64 confirmedBytes = 0;
  UINT64 diffBytes = 0;
  UINT32 storedStatus = 0;
  SettleEntry *entry = NULL;

  dbInterface->getAccountStatus(pCC->getAccountNumber(), storedStatus);

  // check returncode
  if(pErrMsg == NULL)  //no returncode -> connection error
  {
    authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC; // no fault of the client
    CAMsg::printMsg(LOG_ERR, "Settlement transaction: Communication with BI failed!\n");
  }
  else if(pErrMsg->getErrorCode() != pErrMsg->ERR_OK)  //BI reported error
  {
    CAMsg::printMsg(LOG_WARNING, "Settlement transaction: BI reported error no. %d (%s)\n",
      pErrMsg->getErrorCode(), pErrMsg->getDescription() );
    if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_KEY_NOT_FOUND)
    {
      authFlags |= AUTH_INVALID_ACCOUNT;
      //dbConn.storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_KEY_NOT_FOUND);
      bDeleteCC = true;
    }
    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_ACCOUNT_EMPTY)
    {
      authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
      authFlags |= AUTH_ACCOUNT_EMPTY;
      UINT64* msgConfirmedBytes = (UINT64*)pErrMsg->getMessageObject();
      if (msgConfirmedBytes)
      {
        confirmedBytes = *msgConfirmedBytes;
        if (confirmedBytes < pCC->getTransferredBytes())
        {
          diffBytes = pCC->getTransferredBytes() - confirmedBytes;
        }
        UINT8 tmp[32];
        print64(tmp, confirmedBytes);
        UINT8 tmp2[32];
        print64(tmp2, diffBytes);
        UINT8 tmp3[32];
        print64(tmp3, pCC->getTransferredBytes());
        CAMsg::printMsg(LOG_INFO, "Settlement transaction: Received %s confirmed bytes and %s diff bytes while having %s transferred bytes!\n", tmp, tmp2, tmp3);
      }
      else
      {
        CAMsg::printMsg(LOG_INFO, "Settlement transaction: Account empty, no message object received. User will be kicked out.\n");
      }

      dbInterface->storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_ACCOUNT_EMPTY);
      authFlags |= AUTH_SETTLED_ONCE;
      dbInterface->markAsSettled(pCC->getAccountNumber(), ms_pInstance->m_currentCascade,
                    pCC->getTransferredBytes());
//#ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: settling %llu bytes for account %llu\n",
                    pCC->getTransferredBytes(), pCC->getAccountNumber());
//#endif
    }
    /*
    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_INVALID_PRICE_CERT)
    {
      // this should never happen; the price certs in this CC do not fit to the ones of the cascade
      // bDeleteCC = true;
    }*/
    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_OUTDATED_CC)
    {
      authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC; // this is a Mix not a client error

      //get attached CC from error message
      CAXMLCostConfirmation* attachedCC = (CAXMLCostConfirmation*) pErrMsg->getMessageObject();
      if (attachedCC)
      {
        authFlags |= AUTH_OUTDATED_CC;
        CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: tried outdated CC, received last valid CC back\n");
        //store it in DB
        if (dbInterface->storeCostConfirmation(*attachedCC,
            ms_pInstance->m_currentCascade) == E_SUCCESS)
        {
          authFlags |= AUTH_SETTLED_ONCE;
          if (dbInterface->markAsSettled(attachedCC->getAccountNumber(), ms_pInstance->m_currentCascade,
                      attachedCC->getTransferredBytes()) != E_SUCCESS)
          {
            CAMsg::printMsg(LOG_ERR, "Settlement transaction: Could not mark last valid CC as settled."
              "Maybe a new CC has been added meanwhile?\n");
          }
        }
        else
        {
          CAMsg::printMsg(LOG_ERR, "Settlement transaction: storing last valid CC in db failed!\n");
        }
        // set the confirmed bytes to the value of the CC got from the PI
        confirmedBytes = attachedCC->getTransferredBytes();
      }
      else
      {
        CAMsg::printMsg(LOG_INFO, "Settlement transaction: Did not receive last valid CC - maybe old Payment instance?\n");
      }
    }
    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_BLOCKED)
    {
      authFlags |= AUTH_BLOCKED;
      bDeleteCC = true;

      dbInterface->storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_BLOCKED);
    }

    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_DATABASE_ERROR)
    {
      //authFlags |= AUTH_DATABASE;
      // the user is not responsible for this!
      authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
    }
    else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR ||
         pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_SUCCESS_BUT_WITH_ERRORS)
    {
      // kick out the user and store the CC
      authFlags |= AUTH_UNKNOWN;
    }
    else
    {
      // an unknown error leads to user kickout
      CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: Setting unknown kickout error no. %d.\n", pErrMsg->getErrorCode());
      authFlags |= AUTH_UNKNOWN;
      bDeleteCC = true;
    }

    if (bDeleteCC)
    {
      //delete costconfirmation to avoid trying to settle an unusable CC again and again
      if(dbInterface->deleteCC(pCC->getAccountNumber(), ms_pInstance->m_currentCascade) == E_SUCCESS)
      {
        CAMsg::printMsg(LOG_ERR, "Settlement transaction: unusable cost confirmation was deleted\n");
      }
      else
      {
        CAMsg::printMsg(LOG_ERR, "Settlement transaction: cost confirmation is unusable, but could not delete it from database\n");
      }
    }
  }
  else //settling was OK, so mark account as settled
  {
    authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
    authFlags |= AUTH_SETTLED_ONCE;
    if (dbInterface->markAsSettled(pCC->getAccountNumber(),
                ms_pInstance->m_currentCascade,
                pCC->getTransferredBytes()) != E_SUCCESS)
     {
      CAMsg::printMsg(LOG_ERR, "Settlement transaction: Could not mark CC as settled. Maybe a new CC has been added meanwhile?\n");
     }
#ifdef DEBUG
    CAMsg::printMsg(LOG_INFO, "Settlement transaction %llu: CC OK for account %llu with %llu transferred bytes!\n", 
      a_iCurrentSettleTransactionNr, pCC->getAccountNumber(), pCC->getTransferredBytes());
#endif
  }

  if (authFlags || authRemoveFlags)
  {
    entry = new SettleEntry;
    entry->accountNumber = pCC->getAccountNumber();
    entry->authFlags = authFlags;
    entry->authRemoveFlags = authRemoveFlags;
    entry->confirmedBytes = confirmedBytes;
    entry->diffBytes = diffBytes;
    entry->storedStatus = storedStatus;
  }
  return entry;
}

Here is the call graph for this function:

Definition at line 2774 of file CAAccountingInstance.cpp.

References __commitSettlementToDatabase(), __commitSettlementToLoginTable(), __handleSettleResult(), cleanup(), CRITICAL_SUBSEQUENT_BI_CONN_ERRORS, E_NOT_CONNECTED, E_SUCCESS, E_UNKNOWN, CAAccountingDBInterface::getConnection(), Hashtable::getMutex(), CAAccountingDBInterface::getUnsettledCostConfirmations(), CAAccountingBIInterface::initBIConnection(), CAMutex::lock(), m_currentAccountsHashtable, m_currentCascade, m_iCurrentSettleTransactionNr, m_nextSettleNr, m_pPiInterface, m_pSettlementMutex, m_seqBIConnErrors, m_settleWaitNr, MAX_SETTLED_CCS, MONITORING_FIRE_PAY_EVENT, ms_pInstance, SettleEntry::nextEntry, CAMsg::printMsg(), CAAccountingDBInterface::releaseConnection(), CAAccountingBIInterface::settleAll(), CAConditionVariable::signal(), CAAccountingBIInterface::terminateBIConnection(), CAMutex::unlock(), and CAConditionVariable::wait().

Referenced by newSettlementTransaction().

{

  CAXMLErrorMessage **pErrMsgs = NULL, *settleException = NULL;
  CAXMLCostConfirmation **allUnsettledCCs = NULL;
  SettleEntry *entry = NULL, *nextEntry = NULL;
  CAAccountingDBInterface *dbInterface = NULL;

  UINT32 nrOfCCs = 0, i = 0;
  SINT32 biConnectionStatus = 0, ret = E_SUCCESS;
  UINT64 myWaitNr = 0;


  //settlement transactions need to be synchronized globally because the settlement thread as well as all login threads
  //may start a settlement transaction concurrently.
  ms_pInstance->m_pSettlementMutex->lock();

  UINT64 iCurrentSettleTransactionNr = (++m_iCurrentSettleTransactionNr) % 50;

  dbInterface = CAAccountingDBInterface::getConnection();
  if(dbInterface == NULL)
  {
    ms_pInstance->m_pSettlementMutex->unlock();
    CAMsg::printMsg(LOG_ERR, "Settlement transaction: could not connect to Database. Retry later...\n");
    ret = E_NOT_CONNECTED;
    goto cleanup;
  }
  

  /* First part get unsettled CCs from the AI database */
  #ifdef DEBUG
  CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: DB connections established!\n");
  #endif

    dbInterface->getUnsettledCostConfirmations(&allUnsettledCCs, ms_pInstance->m_currentCascade, &nrOfCCs, MAX_SETTLED_CCS);
    *nrOfSettledCCs = nrOfCCs;
    //no unsettled CCs found.
    if (allUnsettledCCs == NULL)
    {
      CAMsg::printMsg(LOG_INFO, "Settlement transaction %llu: thread %x looked for unsettled CCs, found no CCs to settle\n", iCurrentSettleTransactionNr, pthread_self());
      ms_pInstance->m_pSettlementMutex->unlock();
      ret = E_SUCCESS;
      goto cleanup;
    }


    CAMsg::printMsg(LOG_DEBUG, "Settlement transaction %llu: thread %x looked for unsettled CCs, found %u cost confirmations to settle\n", iCurrentSettleTransactionNr, pthread_self(), nrOfCCs);

    //connect to the PI
    biConnectionStatus = ms_pInstance->m_pPiInterface->initBIConnection();
    if(biConnectionStatus != E_SUCCESS)
    {
      ms_pInstance->m_seqBIConnErrors++;
      if(ms_pInstance->m_seqBIConnErrors >= CRITICAL_SUBSEQUENT_BI_CONN_ERRORS)
      {
        //transition to critical BI conn payment state
        MONITORING_FIRE_PAY_EVENT(ev_pay_biConnectionCriticalSubseqFailures);
      }
      else
      {
        MONITORING_FIRE_PAY_EVENT(ev_pay_biConnectionFailure);
      }
      ms_pInstance->m_pPiInterface->terminateBIConnection(); // make sure the socket is closed
      
      CAAccountingDBInterface::releaseConnection(dbInterface);
      dbInterface = NULL;
      ms_pInstance->m_pSettlementMutex->unlock();

      CAMsg::printMsg(LOG_WARNING, "Settlement transaction: could not connect to BI. Try later...\n");
      //ret = E_SUCCESS;
      ret = E_NOT_CONNECTED;
      goto cleanup;
    }
    else
    {
      pErrMsgs = ms_pInstance->m_pPiInterface->settleAll(allUnsettledCCs, nrOfCCs, &settleException);
      ms_pInstance->m_pPiInterface->terminateBIConnection();
    }

  //workaround because usage of exceptions is not allowed!
  if(settleException != NULL)
  {
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  
    ms_pInstance->m_pSettlementMutex->unlock();
    CAMsg::printMsg(LOG_ERR, "Settlement transaction: BI reported settlement not successful: "
            "code: %i, %s \n", settleException->getErrorCode(),
              (settleException->getDescription() != NULL ?
                settleException->getDescription() : (UINT8*) "<no description given>") );
    ret = E_UNKNOWN;
    goto cleanup;
  }

  if(pErrMsgs == NULL)
  {
    //this must never happen because in any case where NULL is returned for pErrMsgs
    //'settleException' must not be NULL.
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
    
    ms_pInstance->m_pSettlementMutex->unlock();
    CAMsg::printMsg(LOG_CRIT, "Settlement transaction: ErrorMessages are null.\n");
    ret = E_UNKNOWN;
    goto cleanup;
  }
  else
  {
    for(i = 0; i < nrOfCCs; i++)
    {
      nextEntry = __handleSettleResult(allUnsettledCCs[i], pErrMsgs[i], dbInterface, iCurrentSettleTransactionNr);
      if(nextEntry != NULL)
      {
        nextEntry->nextEntry = entry;
        entry = nextEntry;
      }
    }
  }

  //after settling: apply the corresponding account state changes reported from the PI.
  if (entry)
  {
    //wait numbers are used to obtain a synchronisation with FCFS assertion.
    //This additional synchronisation is necessary because before acquiring the login hashtable
    //locks the settlementMutex should be released. (Nested locking should be avoided).
    if(ms_pInstance->m_nextSettleNr == ms_pInstance->m_settleWaitNr)
    {
      //CAMsg::printMsg(LOG_INFO, "Thread %x: resetting the wait numbers.\n", pthread_self() );
      //no one is waiting, we use this occasion to reset the wait numbers
      ms_pInstance->m_nextSettleNr = 0;
      ms_pInstance->m_settleWaitNr = 1;
    }
    else
    {
      //get global wait number and wait but release the DBConnection first.
      CAAccountingDBInterface::releaseConnection(dbInterface);
      dbInterface = NULL;
      myWaitNr = ms_pInstance->m_settleWaitNr;
      ms_pInstance->m_settleWaitNr++;
      while(myWaitNr != ms_pInstance->m_nextSettleNr)
      {
        CAMsg::printMsg(LOG_INFO, "Settlement transaction %llu: Thread %x must wait to alter login table after settling (1): %llu before him in the queue\n", 
            iCurrentSettleTransactionNr, pthread_self(), (myWaitNr - ms_pInstance->m_nextSettleNr));
        ms_pInstance->m_pSettlementMutex->wait();
      }
      
      CAMsg::printMsg(LOG_INFO, "Settlement transaction %llu: Thread %x may continue (1).\n", iCurrentSettleTransactionNr, pthread_self());
      dbInterface = CAAccountingDBInterface::getConnection();
    }
    
    bool debugWarn = false;
    if (iCurrentSettleTransactionNr != (m_iCurrentSettleTransactionNr % 50))
    {
      debugWarn = true;
      // Write debug message in order to find deadlock...
      CAMsg::printMsg(LOG_WARNING, "Settlement transaction %llu: Apply changes to database. Other settlement transactions seem to wait...\n", iCurrentSettleTransactionNr);
    }

    //first apply changes to the database ...
    __commitSettlementToDatabase(entry, dbInterface);
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
    
    
    if (debugWarn)
    {
      // Write debug message in order to find deadlock...
      CAMsg::printMsg(LOG_WARNING, "Settlement transaction %llu: Removing settlement mutex lock. Other settlement transactions seem to wait...\n", iCurrentSettleTransactionNr);
    }
    
    ms_pInstance->m_pSettlementMutex->unlock();
    
    
    if (debugWarn)
    {
      // Write debug message in order to find deadlock...
      CAMsg::printMsg(LOG_WARNING, "Settlement transaction %llu: Entering login hashtable. Other settlement transactions seem to wait...\n", iCurrentSettleTransactionNr);    
    }

    //... then to the login hashtable after releasing the global settlement lock.
    ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
    CAMsg::printMsg(LOG_DEBUG, "Settlement transaction %llu: thread %x with wait nr %llu alters hashtable.\n", iCurrentSettleTransactionNr, pthread_self(), myWaitNr);
    __commitSettlementToLoginTable(entry);
    ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();

    if (debugWarn)
    {
      // Write debug message in order to find deadlock...
      CAMsg::printMsg(LOG_WARNING, "Settlement transaction %llu: After commiting to login hashtable, before last lock. Other settlement transactions seem to wait...\n", iCurrentSettleTransactionNr);    
    }
    
    //indicate that the next thread may proceed with its settlement changes by incrementing the nextSettleNr.
    ms_pInstance->m_pSettlementMutex->lock();
    ms_pInstance->m_nextSettleNr++;
// TODO: seems to be a bug; if this "if" is set, some locks are never released
//if(ms_pInstance->m_settleWaitNr != ms_pInstance->m_nextSettleNr)
    {
      //There are threads waiting for applying their settlement results.
      if(ms_pInstance->m_settleWaitNr != ms_pInstance->m_nextSettleNr)
      CAMsg::printMsg(LOG_INFO, "Thread %x waking up next Thread. %llu are still waiting.\n", pthread_self(),
              (ms_pInstance->m_settleWaitNr - ms_pInstance->m_nextSettleNr));
      ms_pInstance->m_pSettlementMutex->signal();
    }
  }
  ms_pInstance->m_pSettlementMutex->unlock();

cleanup:
  if(dbInterface != NULL)
  {
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  }

  if(allUnsettledCCs != NULL)
  {
    for(i = 0; i < nrOfCCs; i++)
    {
      delete allUnsettledCCs[i];
      allUnsettledCCs[i] = NULL;
    }
    delete [] allUnsettledCCs;
    allUnsettledCCs = NULL;
  }

  if(pErrMsgs != NULL)
  {
    for(i = 0; i < nrOfCCs; i++)
    {
      delete pErrMsgs[i];
      pErrMsgs[i] = NULL;
    }
    pErrMsgs = NULL;
  }

  while (entry != NULL)
  {
    nextEntry = entry->nextEntry;
    delete entry;
    entry = nextEntry;
  }

  delete settleException;
  settleException = NULL;

  
  CAMsg::printMsg(LOG_INFO, "Settlement transaction %llu finished.\n", iCurrentSettleTransactionNr);

  return ret;
}

Here is the call graph for this function:

Definition at line 843 of file CAAccountingInstance.cpp.

References CAXMLCostConfirmation::getNumberOfHashes(), CAXMLCostConfirmation::getPriceCertHash(), Hashtable::getValue(), m_allHashesLen, m_certHashCC, and CAMsg::printMsg().

Referenced by handleChallengeResponse_internal(), and handleCostConfirmation_internal().

{

    UINT8* certHash;
    if(m_allHashesLen !=  pCC->getNumberOfHashes() )
    {
      return false;
    }

    for (UINT32 i = 0; i < pCC->getNumberOfHashes(); i++)
    {
      certHash = pCC->getPriceCertHash(i);
      if ((certHash = (UINT8*)m_certHashCC->getValue(certHash)) != NULL)
      {
#ifdef DEBUG
        CAMsg::printMsg( LOG_INFO, "CC1: %s\n", certHash);
#endif
      }
      else
      {
#ifdef DEBUG
        CAMsg::printMsg(LOG_DEBUG, "CC do not match current cascade.\n");
#endif
        return false;
      }
    }
#ifdef DEBUG
    CAMsg::printMsg(LOG_DEBUG, "CC matches current Cascade.\n");
#endif
    return true;
}

Here is the call graph for this function:

static SINT32 CAAccountingInstance::clean ( ) [inline, static]

Definition at line 128 of file CAAccountingInstance.hpp.

References E_SUCCESS, m_preparedCCRequest, MONITORING_FIRE_PAY_EVENT, and ms_pInstance.

    {
      if(ms_pInstance != NULL)
      {
        delete ms_pInstance;
        ms_pInstance=NULL;
      }
      if(m_preparedCCRequest != NULL)
      {
        m_preparedCCRequest->release();
        m_preparedCCRequest = NULL;
      }
      MONITORING_FIRE_PAY_EVENT(ev_pay_aiShutdown);
      return E_SUCCESS;
    }

This should always be called when closing a JAP connection to cleanup the data structures.

This should always be called when closing a JAP connection to cleanup the payment data structures and store prepaid bytes.

(pAccInfo->authFlags & AUTH_WAITING_FOR_FIRST_SETTLED_CC))

Definition at line 2559 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_ACCOUNT_OK, AUTH_DELETE_ENTRY, AccountLoginHashEntry::authFlags, t_accountinginfo::authFlags, BEGIN_STACK, t_accountinginfo::clientVersion, AccountLoginHashEntry::confirmedBytes, t_accountinginfo::confirmedBytes, E_SUCCESS, E_UNKNOWN, FINISH_STACK, CAAccountingDBInterface::getConnection(), Hashtable::getMutex(), CALibProxytest::getOptions(), getPrepaidBytes(), CACmdLnOptions::getPrepaidInterval(), Hashtable::getValue(), INIT_STACK, isLoginOngoing(), CAMutex::lock(), m_currentAccountsHashtable, m_currentCascade, ms_pInstance, t_accountinginfo::mutex, t_accountinginfo::nrInQueue, AccountLoginHashEntry::ownerLock, AccountLoginHashEntry::ownerRef, t_fmhashtableentry::pAccountingInfo, t_accountinginfo::pChallenge, t_fmhashtableentry::peerIP, t_accountinginfo::pPublicKey, print64(), CAMsg::printMsg(), t_accountinginfo::pstrBIID, CAAccountingDBInterface::releaseConnection(), Hashtable::remove(), SAVE_STACK, CAAccountingDBInterface::storePrepaidAmount(), testLoginEntryOwner(), and CAMutex::unlock().

Referenced by CAAccountingControlChannel::~CAAccountingControlChannel().

  {
    INIT_STACK;
    BEGIN_STACK("CAAccountingInstance::cleanupTableEntry");

    //ms_pInstance->m_pMutex->lock();
    tAiAccountingInfo* pAccInfo = pHashEntry->pAccountingInfo;
    AccountLoginHashEntry* loginEntry;
    SINT32 prepaidBytes = 0;
    SINT32 prepaidInterval = CALibProxytest::getOptions()->getPrepaidInterval();

    if (pAccInfo == NULL)
    {
      SAVE_STACK("CAAccountingInstance::cleanupTableEntry", "acc info null");
      //ms_pInstance->m_pMutex->unlock();
      return E_UNKNOWN;
    }

    //pAccInfo->mutex->lock();

    pHashEntry->pAccountingInfo=NULL;

    if (pAccInfo->accountNumber)
    {
      CAMsg::printMsg(LOG_INFO, "cleaning up entry %p of accountno. %llu (pAccInfo ref: %p)\n",
              pHashEntry, pAccInfo->accountNumber, pAccInfo);
      if (pAccInfo->authFlags & AUTH_ACCOUNT_OK)
      {
        // remove login
        ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
        loginEntry = (AccountLoginHashEntry*)ms_pInstance->m_currentAccountsHashtable->getValue(&(pAccInfo->accountNumber));

        if (loginEntry)
        {
          // test: delete CC!!!
          //ms_pInstance->m_dbInterface->deleteCC(pAccInfo->accountNumber, ms_pInstance->m_currentCascade);
          if (testLoginEntryOwner(loginEntry, pHashEntry))// &&
          {
            if (loginEntry->authFlags & AUTH_ACCOUNT_EMPTY)
            {
              // make sure to store the correct number of prepaid bytes
              pAccInfo->confirmedBytes = loginEntry->confirmedBytes;
            }
            //store prepaid bytes in database, so the user wont lose the prepaid amount by disconnecting
            prepaidBytes = getPrepaidBytes(pAccInfo);
            if (prepaidBytes > 0)
            {
              if (prepaidBytes > prepaidInterval)
              {
                UINT8 tmp[32];
                print64(tmp, pAccInfo->accountNumber);
                /* Client paid more than the prepaid interval -
                 * this is beyond specification and not allowed!
                 */
                CAMsg::printMsg(LOG_ERR,
                  "PrepaidBytes of %d for account %s are higher than prepaid interval! "
                  "The client (owner %x) did not behave according to specification. "
                  "Deleting prepaid bytes!\n", prepaidBytes, tmp, pHashEntry);
                  //"Deleting %d bytes...\n", prepaidBytes, tmp, pHashEntry, prepaidBytes - prepaidInterval);

                //prepaidBytes = prepaidInterval;
                prepaidBytes = 0;
              }
            }

            /*if (ms_pInstance->m_dbInterface)
            {
              ms_pInstance->m_dbInterface->storePrepaidAmount(pAccInfo->accountNumber,prepaidBytes, ms_pInstance->m_currentCascade);
            }*/
            CAAccountingDBInterface *dbInterface = CAAccountingDBInterface::getConnection();
            if(dbInterface != NULL)
            {
              dbInterface->storePrepaidAmount(pAccInfo->accountNumber,
                              prepaidBytes,
                              ms_pInstance->m_currentCascade);
              CAAccountingDBInterface::releaseConnection(dbInterface);
              dbInterface = NULL;
            }
            if(!isLoginOngoing(loginEntry, pHashEntry))
            {
              ms_pInstance->m_currentAccountsHashtable->remove(&(pAccInfo->accountNumber));
              delete loginEntry->ownerLock;
              delete loginEntry;
              loginEntry = NULL;
            }
            else
            {
              CAMsg::printMsg(LOG_INFO, "Cleaning: Leaving loginEntry %x cleanup of owner %x to the next owner due to double-login of a user.\n",
                  loginEntry, loginEntry->ownerRef);
            }
          }

          /*if (loginEntry->count <= 1)
          {
            if (loginEntry->count < 1)
            {
              CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Cleanup found non-positive number of user login hash entries (%d)!\n", loginEntry->count);
            }
            // this is the last active user connection; delete the entry
            ms_pInstance->m_currentAccountsHashtable->remove(&(pAccInfo->accountNumber));
            delete loginEntry->ownerLock;
            delete loginEntry;
            loginEntry = NULL;
          }
          else
          {
            // there are other connections from this user
            loginEntry->count--;
          }*/
        }
        else
        {
          CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Cleanup did not find user login hash entry!\n");
        }
        ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();
      }
    }
    else
    {
      CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Cleanup method found account zero.\n");
    }

    //free memory of pAccInfo

    delete pAccInfo->pPublicKey;
    pAccInfo->pPublicKey = NULL;


    delete [] pAccInfo->pChallenge;
    pAccInfo->pChallenge = NULL;

    delete [] pAccInfo->pstrBIID;
    pAccInfo->pstrBIID = NULL;

    delete [] pAccInfo->clientVersion;
    pAccInfo->clientVersion = NULL;

    pHashEntry->pAccountingInfo=NULL;

    if (pAccInfo->nrInQueue > 0)
    {
      /*
      if (pAccInfo->accountNumber == 0)
      {
        CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: AI queue entries found for account zero: %u!\n", pAccInfo->nrInQueue);
      }
      else if (!(pAccInfo->authFlags & AUTH_ACCOUNT_OK))
      {
        UINT8 accountNrAsString[32];
        print64(accountNrAsString, pAccInfo->accountNumber);
        CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: AI queue entries found for unauthorized account %s: %u!\n", accountNrAsString, pAccInfo->nrInQueue);
      }
      else*/
      {
        // there are still entries in the ai queue; empty it before deletion; we cannot delete it now
        pAccInfo->authFlags |= AUTH_DELETE_ENTRY;
      }
    }
    else if (pAccInfo->nrInQueue < 0)
    {
      CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Cleanup method found negative handle queue!\n");
    }
    //pAccInfo->mutex->unlock();

    if (!(pAccInfo->authFlags & AUTH_DELETE_ENTRY))
    {
      // there are no handles for this entry in the queue, we can savely delete it now
      delete pAccInfo->mutex;
      pAccInfo->mutex = NULL;
      delete [] pAccInfo->clientVersion;
      pAccInfo->clientVersion = NULL;
      
#ifdef LOG_CRIME
      UINT64 accountNumber = pAccInfo->accountNumber;
      UINT64* surveillanceAccounts = CALibProxytest::getOptions()->getCrimeSurveillanceAccounts();
      UINT32 nrOfSurveillanceAccounts = CALibProxytest::getOptions()->getNrOfCrimeSurveillanceAccounts();
      const UINT8* peerIP = pHashEntry->peerIP;
    
      for (UINT32 iAccount = 0; iAccount < nrOfSurveillanceAccounts; iAccount++)
      {
        if (accountNumber == surveillanceAccounts[iAccount])
        {
          CAMsg::printMsg(LOG_CRIT,"Crime detection: User logged out with account %llu has IP %u.%u.%u.%u\n",accountNumber, peerIP[0], peerIP[1], peerIP[2], peerIP[3]);
          break;
        }
      }
#endif
      
      delete pAccInfo;
      pAccInfo = NULL;
    }
    else
    {
      CAMsg::printMsg(LOG_INFO, "CAAccountingInstance: Cleanup method sent account deletion request to AI thread!\n");
    }
    //ms_pInstance->m_pMutex->unlock();

    FINISH_STACK("CAAccountingInstance::cleanupTableEntry");

    return E_SUCCESS;
  }

Here is the call graph for this function:

this method is for the corresponding CAFirstMix login thread to verify the result of the settlement.

Definition at line 1325 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_INVALID_ACCOUNT, AUTH_LOGIN_FAILED, AUTH_LOGIN_NOT_FINISHED, AUTH_LOGIN_SKIP_SETTLEMENT, AUTH_SETTLED_ONCE, AUTH_UNKNOWN, AccountLoginHashEntry::authFlags, t_accountinginfo::authFlags, AccountLoginHashEntry::authRemoveFlags, AccountLoginHashEntry::confirmedBytes, t_accountinginfo::confirmedBytes, CRITICAL_SETTLE_FLAGS, E_SUCCESS, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_NO_BALANCE, CAXMLErrorMessage::ERR_NO_ERROR_GIVEN, CAXMLErrorMessage::ERR_OK, CAAccountingDBInterface::getAccountStatus(), CAAccountingDBInterface::getConnection(), Hashtable::getMutex(), getPrepaidBytes(), Hashtable::getValue(), CAMutex::lock(), m_currentAccountsHashtable, ms_pInstance, t_accountinginfo::mutex, t_fmhashtableentry::pAccountingInfo, t_accountinginfo::pControlChannel, CAMsg::printMsg(), CAAccountingDBInterface::releaseConnection(), sendAILoginConfirmation(), CAAbstractControlChannel::sendXMLMessage(), CAAbstractXMLEncodable::toXmlDocument(), t_accountinginfo::transferredBytes, and CAMutex::unlock().

Referenced by CAFirstMix::doUserLogin_internal().

{
  SINT32 ret = 0;
  UINT64 accountNumber = 0;
  AccountLoginHashEntry *loginEntry;
  tAiAccountingInfo* pAccInfo = pHashEntry->pAccountingInfo;

  if(ms_pInstance==NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(ms_pInstance->m_currentAccountsHashtable == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(ms_pInstance->m_currentAccountsHashtable->getMutex() == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(pHashEntry == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(pHashEntry->pAccountingInfo == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(pHashEntry->pAccountingInfo->mutex == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  pAccInfo->mutex->lock();
  accountNumber = pAccInfo->accountNumber;

  //reset login flags
  pAccInfo->authFlags &= (~AUTH_LOGIN_SKIP_SETTLEMENT & ~AUTH_LOGIN_NOT_FINISHED);

  if (!(pAccInfo->authFlags & AUTH_SETTLED_ONCE))
  {
    UINT32 statusCode = 0;
    CAAccountingDBInterface* dbInterface = CAAccountingDBInterface::getConnection();
    if (dbInterface != NULL && dbInterface->getAccountStatus(accountNumber, statusCode) == E_SUCCESS)
    {
      if (statusCode > 0)
      {
        CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: No CC was settled for account %llu. Using DB status %u.\n",
          accountNumber, statusCode);
      }
      pAccInfo->authFlags |= statusCode;
    }
    else
    {
      CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: No CC was settled for account %llu. Could not fetch status from DB!.\n");
    }
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  }

  ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
  loginEntry = (AccountLoginHashEntry*)ms_pInstance->m_currentAccountsHashtable->getValue(&accountNumber);

  if (loginEntry)
  {
    if(loginEntry->authRemoveFlags)
    pAccInfo->authFlags &= ~(loginEntry->authRemoveFlags);
    pAccInfo->authFlags |= (loginEntry->authFlags & CRITICAL_SETTLE_FLAGS);
  }
  else
  {
    pAccInfo->authFlags |= AUTH_INVALID_ACCOUNT;
  }
  /*ret = pHashEntry->pAccountingInfo->authFlags &
    (AUTH_LOGIN_NOT_FINISHED | AUTH_LOGIN_FAILED);*/


  CAXMLErrorMessage *err = NULL;
  XERCES_CPP_NAMESPACE::DOMDocument *errDoc = NULL;
  /* Instead of using special login confirmation messages
   * we rather send XMLErrorMessages for backward compatibility reasons
   * because old JAPs (version <= 00.09.021) can handle them
   */
  if(pAccInfo->authFlags & AUTH_BLOCKED )
  {
    err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_BLOCKED,
                  (UINT8 *) "AI login: access denied because your account is blocked");
    CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: Account %llu seems blocked.\n", accountNumber);
  }
  else if(pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY )
  {
    err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_ACCOUNT_EMPTY,
                  (UINT8 *) "AI login: access denied because your account is empty");
    if ((pAccInfo->authFlags & AUTH_SETTLED_ONCE))
    {
      pAccInfo->confirmedBytes = loginEntry->confirmedBytes;
      if (pAccInfo->confirmedBytes < pAccInfo->transferredBytes)
      {
        // this account is really empty; prevent an overflow in the prepaid bytes calculation
        pAccInfo->transferredBytes = pAccInfo->confirmedBytes;
      }
      if (getPrepaidBytes(pAccInfo) > 0)
      {
        // the user may user his last bytes and is kicked out after that
        delete err;
        err = NULL;
      }
    }
    else
    {
      // TODO I am not sure whether this is enough; check this later
      loginEntry->confirmedBytes = pAccInfo->confirmedBytes;
      pAccInfo->transferredBytes = pAccInfo->confirmedBytes;
    }
    CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: Account %llu seems empty.\n", accountNumber);
  }
  else if(pAccInfo->authFlags & AUTH_INVALID_ACCOUNT )
  {
    err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_NO_BALANCE,
                  (UINT8 *) "AI login: access denied because your account is not valid");
    CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: Account %llu seems invalid.\n", accountNumber);
  }
  else if(pAccInfo->authFlags & AUTH_UNKNOWN )
  {
    err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_NO_ERROR_GIVEN,
                  (UINT8 *) "AI login: error occured while connecting, access denied");
    CAMsg::printMsg(LOG_WARNING, "CAAccountingInstance::finishLoginProcess: Unknown error was found for account %llu.\n", accountNumber);
  }

  if(err != NULL)
  {
    err->toXmlDocument(errDoc);
    pHashEntry->pAccountingInfo->pControlChannel->sendXMLMessage(errDoc);
    delete err;
    err = NULL;
    if(errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    ret |= AUTH_LOGIN_FAILED;
  }
  else
  {

  //send login confirmation to user, but if the message could not be send, login will fail
  /* These login confirmation messages are necessary for the new AI login protocol to indicate
   * that the process is finished after a settlement.
   * They won't bother old JAPs (version <= 00.09.021) because they will ignore
   * these confirmations.
   */
    if(sendAILoginConfirmation(pAccInfo,
                  CAXMLErrorMessage::ERR_OK,
                  (UINT8*) "AI login successful") != E_SUCCESS)
    {
      ret |= AUTH_LOGIN_FAILED;
    }
  }

  /* unlock the loginEntry object for other login threads */
  //resetLoginOngoing(loginEntry, pHashEntry);
  ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();
  pAccInfo->mutex->unlock();
  return ret;
}

Here is the call graph for this function:

Returns:
the payment auth flags of the given connecttion or 0 if no auth flags are set

Definition at line 226 of file CAAccountingInstance.cpp.

References t_accountinginfo::authFlags, and t_fmhashtableentry::pAccountingInfo.

Referenced by CAFirstMixA::checkUserConnections().

{
  if (pHashEntry == NULL)
  {
    return 0;
  }

  tAiAccountingInfo* pAccInfo = pHashEntry->pAccountingInfo;

  if (pAccInfo == NULL)
  {
    return 0;
  }

  return pAccInfo->authFlags;
}

Definition at line 243 of file CAAccountingInstance.cpp.

References Hashtable::getSize(), CAMutex::lock(), m_currentAccountsHashtable, m_pMutex, ms_pInstance, CAMsg::printMsg(), and CAMutex::unlock().

{
  UINT32 users = 0;

  if (ms_pInstance != NULL)
  {
    ms_pInstance->m_pMutex->lock();
    if(ms_pInstance->m_currentAccountsHashtable != NULL)
    {
      // getting the size is an atomic operation and does not need synchronization
      users = ms_pInstance->m_currentAccountsHashtable->getSize();
    }
    else
    {
      CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Trying to access Hashtable after it has been disposed!!.\n");
    }
    ms_pInstance->m_pMutex->unlock();
  }

  return users;
}

Here is the call graph for this function:

Definition at line 661 of file CAAccountingInstance.cpp.

References t_accountinginfo::confirmedBytes, CAMsg::printMsg(), and t_accountinginfo::transferredBytes.

Referenced by cleanupTableEntry(), finishLoginProcess(), handleCostConfirmation_internal(), handleJapPacket_internal(), and processJapMessage().

{
  if (pAccInfo == NULL)
  {
    return 0;
  }

  //most unlikely that either transferred bytes or confirmed bytes
  //are > 0x8000000000000000

  SINT64 prepaidBytes = pAccInfo->confirmedBytes - pAccInfo->transferredBytes;
  //difference must be a value that fits into a signed 32 bit integer.
  if ((prepaidBytes > 0) && (prepaidBytes & 0x7FFFFFFF00000000LL))
  {
    CAMsg::printMsg(LOG_CRIT, "PrepaidBytes overflow: %lld\n", prepaidBytes);
    CAMsg::printMsg(LOG_INFO, "TransferredBytes: %llu  ConfirmedBytes: %llu\n", pAccInfo->transferredBytes, pAccInfo->confirmedBytes);
  }
  return (SINT32) prepaidBytes;

  /*SINT32 prepaidBytes;
#ifdef DEBUG
  CAMsg::printMsg(LOG_INFO, "Calculating TransferredBytes: %llu  ConfirmedBytes: %llu\n",
      pAccInfo->transferredBytes, pAccInfo->confirmedBytes);
#endif
  if (pAccInfo->confirmedBytes > pAccInfo->transferredBytes)
  {
    prepaidBytes = pAccInfo->confirmedBytes - pAccInfo->transferredBytes;
    if (prepaidBytes < 0)
    {
      // PrepaidBytes should be greater than 0 !!!
      UINT8 tmp[32], tmp2[32];
      print64(tmp,pAccInfo->transferredBytes);
      print64(tmp2,pAccInfo->confirmedBytes);

      CAMsg::printMsg(LOG_CRIT, "PrepaidBytes are way to high! Maybe a hacker attack? Or CC did get lost?\n");
      CAMsg::printMsg(LOG_INFO, "TransferredBytes: %s  ConfirmedBytes: %s\n", tmp, tmp2);
      UINT32 prepaidInterval = CALibProxytest::getOptions()->getPrepaidInterval();
      prepaidBytes = (SINT32)prepaidInterval;
      pAccInfo->transferredBytes = pAccInfo->confirmedBytes - prepaidInterval;
    }
  }
  else
  {
    prepaidBytes = pAccInfo->transferredBytes - pAccInfo->confirmedBytes;
    prepaidBytes *= -1;
  }

  return prepaidBytes;*/
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleAccountCertificate ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Handles an account certificate of a newly connected Jap.

Definition at line 1489 of file CAAccountingInstance.cpp.

References FINISH_STACK, handleAccountCertificate_internal(), and INIT_STACK.

Referenced by processJapMessage().

{
  return handleAccountCertificate_internal(pAccInfo, root);

  INIT_STACK;
  FINISH_STACK("CAAccountingInstance::handleAccountCertificate");
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleAccountCertificate_internal ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Handles an account certificate of a newly connected Jap.

Parses accountnumber and publickey, checks the signature and generates and sends a challenge XML structure to the Jap. TODO: think about switching account without changing mixcascade (receive a new acc.cert. though we already have one)

Definition at line 1506 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_CHALLENGE_SENT, AUTH_DELETE_ENTRY, AUTH_FAKE, AUTH_GOT_ACCOUNTCERT, AUTH_TIMEOUT_STARTED, t_accountinginfo::authFlags, BEGIN_STACK, t_accountinginfo::challengeSentSeconds, createDOMDocument(), createDOMElement(), DOM_Output::dumpToMem(), E_SUCCESS, CABase64::encode(), CAXMLErrorMessage::ERR_BAD_REQUEST, CAXMLErrorMessage::ERR_BAD_SIGNATURE, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAXMLErrorMessage::ERR_NO_ERROR_GIVEN, CAXMLErrorMessage::ERR_NO_RECORD_FOUND, CAXMLErrorMessage::ERR_OK, CAXMLErrorMessage::ERR_WRONG_FORMAT, CAAccountingDBInterface::getConnection(), getcurrentTime(), getDOMChildByName(), getDOMElementValue(), CALibProxytest::getOptions(), CAAccountingDBInterface::getPrepaidAmount(), getRandom(), INIT_STACK, len, CAMutex::lock(), m_currentCascade, t_accountinginfo::mutex, t_accountinginfo::pChallenge, t_accountinginfo::pControlChannel, t_accountinginfo::pPublicKey, CAMsg::printMsg(), t_accountinginfo::pstrBIID, CAAccountingDBInterface::releaseConnection(), CAAbstractControlChannel::sendXMLMessage(), setDOMElementAttribute(), setDOMElementValue(), CASignature::setVerifyKey(), CAAbstractXMLEncodable::toXmlDocument(), CAMutex::unlock(), and CAMultiSignature::verifyXML().

Referenced by handleAccountCertificate().

  {
    INIT_STACK;
    BEGIN_STACK("CAAccountingInstance::handleAccountCertificate");

    //CAMsg::printMsg( LOG_ERR, "CAAccountingInstance::handleAccountCertificate start\n");

    //CAMsg::printMsg(LOG_DEBUG, "started method handleAccountCertificate\n");
    DOMElement* elGeneral=NULL;
    timespec now;
    getcurrentTime(now);

    // check authstate of this user
    if (pAccInfo == NULL)
    {
      return CAXMLErrorMessage::ERR_NO_RECORD_FOUND;
    }

    pAccInfo->mutex->lock();

    if (pAccInfo->authFlags & AUTH_DELETE_ENTRY)
    {
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_NO_ERROR_GIVEN;
    }

    if (pAccInfo->authFlags & AUTH_GOT_ACCOUNTCERT)
    {
      #ifdef DEBUG
        CAMsg::printMsg(LOG_DEBUG, "Already got an account cert. Ignoring...");
      #endif
      CAXMLErrorMessage err(
          CAXMLErrorMessage::ERR_BAD_REQUEST,
          (UINT8*)"You have already sent an Account Certificate"
        );
      XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
      err.toXmlDocument(errDoc);
      pAccInfo->pControlChannel->sendXMLMessage(errDoc);
      if (errDoc != NULL)
      {
        errDoc->release();
        errDoc = NULL;
      }
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_BAD_REQUEST;
    }

    // parse & set accountnumber
    if (getDOMChildByName( root, "AccountNumber", elGeneral, false ) != E_SUCCESS ||
      getDOMElementValue( elGeneral, pAccInfo->accountNumber ) != E_SUCCESS)
    {
      CAMsg::printMsg( LOG_ERR, "AccountCertificate has wrong or no accountnumber. Ignoring...\n");
      CAXMLErrorMessage err(CAXMLErrorMessage::ERR_WRONG_FORMAT);
      XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
      err.toXmlDocument(errDoc);
      pAccInfo->pControlChannel->sendXMLMessage(errDoc);
      if (errDoc != NULL)
      {
        errDoc->release();
        errDoc = NULL;
      }
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_WRONG_FORMAT;
    }

    // parse & set payment instance id
    UINT32 len=256;
    pAccInfo->pstrBIID=new UINT8[256];
    if ( getDOMChildByName( root,"BiID", elGeneral, false ) != E_SUCCESS ||
       getDOMElementValue( elGeneral,pAccInfo->pstrBIID, &len ) != E_SUCCESS)
      {
        delete[] pAccInfo->pstrBIID;
        pAccInfo->pstrBIID = NULL;
        CAMsg::printMsg( LOG_ERR, "AccountCertificate has no Payment Instance ID. Ignoring...\n");
        CAXMLErrorMessage err(CAXMLErrorMessage::ERR_WRONG_FORMAT);
        XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
        err.toXmlDocument(errDoc);
        pAccInfo->pControlChannel->sendXMLMessage(errDoc);
        if (errDoc != NULL)
        {
          errDoc->release();
          errDoc = NULL;
        }
        pAccInfo->mutex->unlock();
        return CAXMLErrorMessage::ERR_WRONG_FORMAT;
      }
    #ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "Stored payment instance ID: %s\n", pAccInfo->pstrBIID);
    #endif


  // parse & set public key
    if ( getDOMChildByName( root, "JapPublicKey", elGeneral, false ) != E_SUCCESS )
    {
      CAMsg::printMsg( LOG_ERR, "AccountCertificate contains no public key. Ignoring...\n");
      CAXMLErrorMessage err(CAXMLErrorMessage::ERR_KEY_NOT_FOUND);
      XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
      err.toXmlDocument(errDoc);
      pAccInfo->pControlChannel->sendXMLMessage(errDoc);
      if (errDoc != NULL)
      {
        errDoc->release();
        errDoc = NULL;
      }
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_KEY_NOT_FOUND;
    }
  #ifdef DEBUG
    UINT8* aij;
    UINT32 aijsize;
    aij = DOM_Output::dumpToMem(elGeneral, &aijsize);
    aij[aijsize-1]=0;
    CAMsg::printMsg( LOG_DEBUG, "Setting user public key %s>\n", aij );
    delete[] aij;
    aij = NULL;
  #endif
  pAccInfo->pPublicKey = new CASignature();
  if ( pAccInfo->pPublicKey->setVerifyKey( elGeneral ) != E_SUCCESS )
  {
    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR);
    XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);
    if (errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR;
  }

  //if ((!m_pJpiVerifyingInstance) ||
    //(m_pJpiVerifyingInstance->verifyXML( root, (CACertStore *)NULL ) != E_SUCCESS ))
  if(CAMultiSignature::verifyXML(root, CALibProxytest::getOptions()->getBI()->getCertificate()))
  {
    // signature invalid. mark this user as bad guy
    CAMsg::printMsg( LOG_INFO, "CAAccountingInstance::handleAccountCertificate(): Bad Jpi signature\n" );
    pAccInfo->authFlags |= AUTH_FAKE | AUTH_GOT_ACCOUNTCERT | AUTH_TIMEOUT_STARTED;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_BAD_SIGNATURE;
  }


  UINT8 * arbChallenge;
  UINT8 b64Challenge[ 512 ];
  UINT32 b64Len = 512;

  //CAMsg::printMsg(LOG_DEBUG, "Almost finished handleAccountCertificate, preparing challenge\n");

  // generate random challenge data and Base64 encode it
  arbChallenge = new UINT8[222];
  getRandom( arbChallenge, 222 );
  CABase64::encode( arbChallenge, 222, b64Challenge, &b64Len );

  delete[] pAccInfo->pChallenge;
  pAccInfo->pChallenge = arbChallenge; // store challenge for later..

  // generate XML challenge structure
  XERCES_CPP_NAMESPACE::DOMDocument* doc = createDOMDocument();
  DOMElement* elemRoot = createDOMElement(doc, "Challenge" );
  DOMElement* elemPanic = createDOMElement(doc, "DontPanic" );
  DOMElement* elemPrepaid = createDOMElement(doc, "PrepaidBytes" );
  setDOMElementAttribute(elemPanic, "version",(UINT8*) "1.0" );
  doc->appendChild( elemRoot );
  elemRoot->appendChild( elemPanic );
  elemRoot->appendChild( elemPrepaid );
  setDOMElementValue( elemPanic, b64Challenge );
  SINT32 prepaidAmount = 0;  //m_dbInterface->getPrepaidAmount(pAccInfo->accountNumber, m_currentCascade, false);

  CAAccountingDBInterface *dbInterface = CAAccountingDBInterface::getConnection();

  //TODO: this is not a good moment to send the prepaid-bytes to the JonDo.
  //we cannot be sure that the database contains a consistent value here.
  if(dbInterface != NULL)
  {
    prepaidAmount = dbInterface->getPrepaidAmount(pAccInfo->accountNumber, m_currentCascade, false);
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  }


  SINT32 prepaidIvalLowerBound = 0; //(-1*(SINT32)CALibProxytest::getOptions()->getPrepaidInterval()); /* EXPERIMENTAL: transmit negative prepaid bytes (but not less than -PREPAID_BYTES) */
  if (prepaidAmount <  prepaidIvalLowerBound)
  {
    prepaidAmount = prepaidIvalLowerBound;
  }
  //CAMsg::printMsg( LOG_DEBUG, "handleAccountCertificate read %i prepaid bytes\n", prepaidAmount);
  setDOMElementValue( elemPrepaid, prepaidAmount);

  // send XML struct to Jap & set auth flags
  pAccInfo->pControlChannel->sendXMLMessage(doc);
  if (doc != NULL)
  {
    doc->release();
    doc = NULL;
  }
  pAccInfo->authFlags |= AUTH_CHALLENGE_SENT | AUTH_GOT_ACCOUNTCERT | AUTH_TIMEOUT_STARTED;
  pAccInfo->challengeSentSeconds = time(NULL);
  //CAMsg::printMsg("Last Account Certificate request seconds: for IP %u%u%u%u", (UINT8)pHashEntry->peerIP[0], (UINT8)pHashEntry->peerIP[1],(UINT8) pHashEntry->peerIP[2], (UINT8)pHashEntry->peerIP[3]);

  //CAMsg::printMsg( LOG_ERR, "CAAccountingInstance::handleAccountCertificate stop\n");


  pAccInfo->mutex->unlock();
  return CAXMLErrorMessage::ERR_OK;
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleChallengeResponse ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Checks the response of the challenge-response auth.

Definition at line 1715 of file CAAccountingInstance.cpp.

References FINISH_STACK, handleChallengeResponse_internal(), and INIT_STACK.

Referenced by processJapMessage().

{
  return handleChallengeResponse_internal(pAccInfo, root);
  INIT_STACK;
  FINISH_STACK("CAAccountingInstance::handleChallengeResponse");
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleChallengeResponse_internal ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Handles the response to our challenge.

Checks the validity of the response and sets the user's authFlags Also gets the last CC of the user, and sends it to the JAP accordingly.

Todo:
We need this trick so that the program does not freeze with active AI ThreadPool!!!!
Todo:
Dangerous, as this may collide with previous accounts that have been used and deleted before... There should be something like an expiration date for the account status, e.g. 1 month
Todo:
We need this trick so that the program does not freeze with active AI ThreadPool!!!!

Definition at line 1729 of file CAAccountingInstance.cpp.

References AccountLoginHashEntry::accountNumber, t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_ACCOUNT_OK, AUTH_BLOCKED, AUTH_CHALLENGE_SENT, AUTH_DELETE_ENTRY, AUTH_FAKE, AUTH_GOT_ACCOUNTCERT, AUTH_INVALID_ACCOUNT, AUTH_MULTIPLE_LOGIN, AUTH_WAITING_FOR_FIRST_SETTLED_CC, AccountLoginHashEntry::authFlags, t_accountinginfo::authFlags, AccountLoginHashEntry::authRemoveFlags, BEGIN_STACK, t_accountinginfo::bytesToConfirm, cascadeMatchesCC(), t_fmhashtableentry::cleanupNotifier, CLIENT_VERSION_STR_LEN, t_accountinginfo::clientVersion, AccountLoginHashEntry::confirmedBytes, t_accountinginfo::confirmedBytes, AccountLoginHashEntry::count, CABase64::decode(), E_SUCCESS, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BAD_SIGNATURE, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAXMLErrorMessage::ERR_MULTIPLE_LOGIN, CAXMLErrorMessage::ERR_NO_ERROR_GIVEN, CAXMLErrorMessage::ERR_NO_RECORD_FOUND, CAXMLErrorMessage::ERR_OK, CAXMLErrorMessage::ERR_WRONG_FORMAT, CAFirstMix::forceKickout(), CAAccountingDBInterface::getAccountStatus(), CAAccountingDBInterface::getConnection(), CAAccountingDBInterface::getCostConfirmation(), getDOMChildByName(), getDOMElementValue(), CAFirstMix::getLoginMutex(), Hashtable::getMutex(), CALibProxytest::getOptions(), CAAccountingDBInterface::getPrepaidAmount(), CACmdLnOptions::getPrepaidInterval(), CAXMLCostConfirmation::getTransferredBytes(), Hashtable::getValue(), CAXMLCostConfirmation::getXMLDocument(), INIT_STACK, CAMutex::lock(), AccountLoginHashEntry::loginOngoing, m_currentAccountsHashtable, m_currentCascade, m_mix, ms_pInstance, t_accountinginfo::mutex, AccountLoginHashEntry::ownerLock, AccountLoginHashEntry::ownerRef, t_accountinginfo::ownerRef, t_accountinginfo::pChallenge, t_accountinginfo::pControlChannel, t_accountinginfo::pPublicKey, PREPAID_PROTO_CLIENT_VERSION, print64(), CAMsg::printMsg(), Hashtable::put(), CAAccountingDBInterface::releaseConnection(), Hashtable::remove(), sendCCRequest(), sendInitialCCRequest(), CAAbstractControlChannel::sendXMLMessage(), testAndSetLoginOwner(), CAAbstractXMLEncodable::toXmlDocument(), t_accountinginfo::transferredBytes, CAMutex::unlock(), AccountLoginHashEntry::userID, t_accountinginfo::userID, CASignature::verifyDER(), and CAConditionVariable::wait().

Referenced by handleChallengeResponse().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::handleChallengeResponse");


  //CAMsg::printMsg( LOG_ERR, "CAAccountingInstance::handleChallengeResponse start\n");


  UINT8 decodeBuffer[ 512 ];
  UINT32 decodeBufferLen = 512;
  UINT32 usedLen;
  /* DOMElement* elemPanic=NULL;
  DSA_SIG * pDsaSig=NULL; */
  SINT32 prepaidAmount = 0;
  AccountLoginHashEntry* loginEntry;
  CAXMLCostConfirmation* pCC = NULL;
  bool bSendCCRequest = true;
  UINT32 status;

  // check current authstate

  if (pAccInfo == NULL)
  {
    return CAXMLErrorMessage::ERR_NO_RECORD_FOUND;
  }

  pAccInfo->mutex->lock();

  if (pAccInfo->authFlags & AUTH_DELETE_ENTRY)
  {
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_NO_ERROR_GIVEN;
  }

  if( (!(pAccInfo->authFlags & AUTH_GOT_ACCOUNTCERT)) ||
      (!(pAccInfo->authFlags & AUTH_CHALLENGE_SENT))
    )
  {
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_NO_ERROR_GIVEN;
  }
  pAccInfo->authFlags &= ~AUTH_CHALLENGE_SENT;

  // get raw bytes of response
  if ( getDOMElementValue( root, decodeBuffer, &decodeBufferLen ) != E_SUCCESS )
  {
    CAMsg::printMsg( LOG_DEBUG, "ChallengeResponse has wrong XML format. Ignoring\n" );
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_WRONG_FORMAT;
  }
  DOMElement *elemClientVersion = NULL;
  getDOMChildByName(root, "ClientVersion", elemClientVersion, false);
  if(elemClientVersion != NULL)
  {
    UINT32 clientVersionStrLen = CLIENT_VERSION_STR_LEN;
    UINT8 *clientVersionStr = new UINT8[clientVersionStrLen];
    memset(clientVersionStr, 0, clientVersionStrLen);
    if( getDOMElementValue(elemClientVersion, clientVersionStr, &clientVersionStrLen) != E_SUCCESS)
    {
      delete [] clientVersionStr;
      clientVersionStr = NULL;
    }
#ifdef DEBUG
    CAMsg::printMsg(LOG_DEBUG, "Client Version (account %llu): %s\n",
        pAccInfo->accountNumber,
        (clientVersionStr != NULL ? clientVersionStr : (UINT8*) "<not set>"));
#endif
    pAccInfo->clientVersion = clientVersionStr;
  }
  else
  {
    pAccInfo->clientVersion = NULL;
  }
  decodeBuffer[decodeBufferLen] = 0;

  usedLen = decodeBufferLen;
  decodeBufferLen = 512;
  CABase64::decode( decodeBuffer, usedLen, decodeBuffer, &decodeBufferLen );

  /*
  UINT8 b64Challenge[ 512 ];
  UINT32 b64Len = 512;
  CABase64::encode(pHashEntry->pAccountingInfo->pChallenge, 222, b64Challenge, &b64Len);
  CAMsg::printMsg(LOG_DEBUG, "Challenge:\n%s\n", b64Challenge);
  */

  // check signature
  //pDsaSig = DSA_SIG_new();
  CASignature * sigTester = pAccInfo->pPublicKey;
  //sigTester->decodeRS( decodeBuffer, decodeBufferLen, pDsaSig );
  if ( sigTester->verifyDER( pAccInfo->pChallenge, 222, decodeBuffer, decodeBufferLen )
    != E_SUCCESS )
  {
    UINT8 accountNrAsString[32];
    print64(accountNrAsString, pAccInfo->accountNumber);
    CAMsg::printMsg(LOG_ERR, "Challenge-response authentication failed for account %s!\n", accountNrAsString);
    pAccInfo->authFlags |= AUTH_FAKE;
    pAccInfo->authFlags &= ~AUTH_ACCOUNT_OK;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_BAD_SIGNATURE;
  }

  pAccInfo->authFlags |= AUTH_ACCOUNT_OK; // authentication successful

  //EXPERIMENTAL NEW CODE for intercepting multiple login attempts
  m_currentAccountsHashtable->getMutex()->lock();
  loginEntry = (AccountLoginHashEntry*)m_currentAccountsHashtable->getValue(&(pAccInfo->accountNumber));
  struct t_fmhashtableentry *ownerRef = NULL;
  if(loginEntry != NULL) // (other) user (with same account) already logged in
  {
    ownerRef  = loginEntry->ownerRef;
    if(ownerRef != NULL)
    {
      bool isLoginFree = testAndSetLoginOwner(loginEntry, ownerRef);
      //obtaining ownership and setting loginProcessOngoing also means to cleanup the
      //old login entry after the connection belonging to the old login is closed.
      //So it can be assured that no other login-thread will find a NULL entry at the above if-statement
      m_currentAccountsHashtable->getMutex()->unlock();
      //abort if another thread is logging in but ...
      if(!isLoginFree)
      {
        CAMsg::printMsg(LOG_DEBUG, "Exiting because login is occupied for owner %p of account %llu.\n", ownerRef);
        pAccInfo->authFlags |= AUTH_MULTIPLE_LOGIN;
        pAccInfo->mutex->unlock();
        return CAXMLErrorMessage::ERR_MULTIPLE_LOGIN;
      }
      //...if the former login is finished and the connection is in use: force the previous login-connection to be kicked out.
      m_mix->getLoginMutex()->lock();
      CAXMLErrorMessage kickoutMsg(CAXMLErrorMessage::ERR_MULTIPLE_LOGIN);
      XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
      kickoutMsg.toXmlDocument(errDoc);
      //Note: the ownerRef hashEntry can already be cleared at that point.
      if(  m_mix->forceKickout(ownerRef, errDoc)  )
      {
        CAMsg::printMsg(LOG_DEBUG, "Kickout was requested for owner %p of account %llu, waiting...\n", ownerRef,
            pAccInfo->accountNumber);
        //Synchronize until the main thread can sure that the connection is closed. (After FirstMixA::checkConnections()
        // in main loop)
        //not dangerous if ensured that the cleanup notifier is always locked after loginCV
        //but it is necessary to lock cleanupNotifier before releasing loginCV. otherwise we might lose
        //the signal from cleanupNotifier. This can't happen if the main thread that
        //peforms the cleanup is still blokced by loginCV before it can acquire cleanupNotifier during the cleanup.
        ownerRef->cleanupNotifier->lock();
        m_mix->getLoginMutex()->unlock();
        ownerRef->cleanupNotifier->wait();
        ownerRef->cleanupNotifier->unlock();
      }
      else
      {
        m_mix->getLoginMutex()->unlock();
        //if the forceKickout returns false the ownerRef was already cleared.
        //no need to wait any further.
        CAMsg::printMsg(LOG_INFO, "ownerRef %p of account %llu already kicked out.\n", ownerRef, pAccInfo->accountNumber);
      }
      errDoc->release();
      errDoc = NULL;
      //obtain hashtable lock again.
      m_currentAccountsHashtable->getMutex()->lock();
      if(loginEntry != NULL)
      {
        //When login ownership was obtained: cleanup the former entry.
        CAMsg::printMsg(LOG_INFO, "finally cleaning up loginEntry %p for former owner %p of account %llu\n",
                          loginEntry, loginEntry->ownerRef, pAccInfo->accountNumber);
        ms_pInstance->m_currentAccountsHashtable->remove(&(pAccInfo->accountNumber));
        delete loginEntry->ownerLock;
        delete loginEntry;
        loginEntry = NULL;
      }
    }
    else
    {
      //Impossible or Bug
      CAMsg::printMsg(LOG_CRIT, "BUG: ownerRef of an active login entry MUST NOT be null. Please report.\n");
      m_currentAccountsHashtable->getMutex()->unlock();
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR;
    }
  }
  //POST CONDITION: old login entry cleared.
  loginEntry = new AccountLoginHashEntry;
  loginEntry->accountNumber = pAccInfo->accountNumber;
  loginEntry->count = 1;
  loginEntry->confirmedBytes = 0;
  loginEntry->authRemoveFlags = 0;
  loginEntry->authFlags = 0;
  loginEntry->userID = pAccInfo->userID;
  loginEntry->ownerRef = pAccInfo->ownerRef;
  loginEntry->loginOngoing = true;
  loginEntry->ownerLock = new CAMutex();
  m_currentAccountsHashtable->put(&(loginEntry->accountNumber), loginEntry);

  //m_currentAccountsHashtable->getMutex()->unlock();

  // fetch cost confirmation from last session if available, and retrieve information; synchronized with settle thread
  bool bSettled;
  CAAccountingDBInterface *dbInterface = CAAccountingDBInterface::getConnection();
#ifdef DEBUG
  CAMsg::printMsg(LOG_DEBUG, "Checking database for previously prepaid bytes...\n");
#endif

  if(dbInterface != NULL)
  {
    prepaidAmount = dbInterface->getPrepaidAmount(pAccInfo->accountNumber, m_currentCascade, false);
    dbInterface->getCostConfirmation(pAccInfo->accountNumber, m_currentCascade, &pCC, bSettled);
  }
  else
  {
    prepaidAmount = 0;
  }


  if (pCC != NULL)
  {
    if(!cascadeMatchesCC(pCC))
    {
      delete pCC;
      pCC = NULL;
      CAMsg::printMsg(LOG_INFO, "CC do not match current Cascade. Discarding CC.\n");
    }
  }

  if (pCC != NULL)
  {
    if (bSettled)
    {
      pAccInfo->authFlags &= ~AUTH_WAITING_FOR_FIRST_SETTLED_CC;
    }
#ifdef DEBUG
    CAMsg::printMsg(LOG_DEBUG, "pAccInfo->transferredBytes is %llu, confirmedBytes: %llu, pCC->transferredBytes is %llu\n",
        pAccInfo->transferredBytes, pAccInfo->confirmedBytes, pCC->getTransferredBytes());
#endif
    pAccInfo->transferredBytes += pCC->getTransferredBytes();
    pAccInfo->confirmedBytes = pCC->getTransferredBytes();
    #ifdef DEBUG
      UINT8 tmp[32];
      print64(tmp,pAccInfo->transferredBytes);
      CAMsg::printMsg(LOG_DEBUG, "Setting confirmedBytes to %llu, pAccInfo->transferredBytes is now %s\n",
          pAccInfo->confirmedBytes, tmp);
    #endif
    //delete pCC;
  }
  else
  {
    UINT8 tmp[32];
    print64(tmp,pAccInfo->accountNumber);
    CAMsg::printMsg(LOG_INFO, "CAAccountingInstance: Cost confirmation for account %s not found in database. This seems to be a new user.\n", tmp);
  }

  //pAccInfo->mutex->unlock();
//  m_currentAccountsHashtable->getMutex()->lock();
//  pAccInfo->authFlags |= AUTH_ACCOUNT_OK; // authentication successful
//
//  loginEntry = (AccountLoginHashEntry*)m_currentAccountsHashtable->getValue(&(pAccInfo->accountNumber));
//  //now loginEntry == NULL must be asserted
//  if (!loginEntry)
//  {
//    // remember that this user is logged in at least once
//    loginEntry = new AccountLoginHashEntry;
//    loginEntry->accountNumber = pAccInfo->accountNumber;
//    loginEntry->count = 1;
//    loginEntry->confirmedBytes = 0;
//    loginEntry->authRemoveFlags = 0;
//    loginEntry->authFlags = 0;
//    loginEntry->userID = pAccInfo->userID;
//    m_currentAccountsHashtable->put(&(loginEntry->accountNumber), loginEntry);
//    if (!(AccountLoginHashEntry*)m_currentAccountsHashtable->getValue(&(pAccInfo->accountNumber)))
//    {
//      UINT8 accountNrAsString[32];
//      print64(accountNrAsString, pAccInfo->accountNumber);
//      CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Could not insert login entry for account %s!", accountNrAsString);
//    }
//  }
//  else
//  {
//    loginEntry->count++;
//  }
//  if (loginEntry->count > 1)
//  {
//    /*
//     * There already is a user logged in with this account.
//     */
//    UINT8 accountNrAsString[32];
//    print64(accountNrAsString, pAccInfo->accountNumber);
//    if (loginEntry->count < MAX_TOLERATED_MULTIPLE_LOGINS)
//    {
//      // There is now more than one user logged in with this account; kick out the other users!
//      CAMsg::printMsg(LOG_INFO,
//              "CAAccountingInstance: Multiple logins (%d) of user with account %s detected! Kicking out other users with this account...\n",
//              loginEntry->count, accountNrAsString);
//      loginEntry->userID = pAccInfo->userID; // this is the current user; kick out the others
//    }
//    else
//    {
//      /* The maximum of tolerated concurrent logins for this user is exceeded.
//       * He won't get any new access again before the old connections have been closed!
//       * @mod: in this case not more than one login is allowed at a time. The User has to wait until
//       * the old login will be deleted.
//       */
//      CAMsg::printMsg(LOG_INFO,
//              "CAAccountingInstance: Maximum of multiple logins exceeded (%d) for user with account %s! Kicking out this user!\n",
//              loginEntry->count, accountNrAsString);
//      bSendCCRequest = false; // not needed...
//      pAccInfo->authFlags |= AUTH_MULTIPLE_LOGIN;
//
//      delete pCC;
//      pCC = NULL;
//      m_currentAccountsHashtable->getMutex()->unlock();
//      pAccInfo->mutex->unlock();
//      return CAXMLErrorMessage::ERR_MULTIPLE_LOGIN;
//    }
//  }

  UINT8 tmp[32];
  print64(tmp,pAccInfo->accountNumber);
  if (prepaidAmount > 0)
  {
    CAMsg::printMsg(LOG_INFO, "CAAccountingInstance: Got %d prepaid bytes for account nr. %s.\n",prepaidAmount, tmp);

    //pAccInfo->authFlags &= ~AUTH_WAITING_FOR_FIRST_SETTLED_CC;

    if (pAccInfo->transferredBytes >= (UINT32)prepaidAmount)
    {
      pAccInfo->transferredBytes -= prepaidAmount;
    }
    else
    {
      UINT8 tmp2[32];
      print64(tmp2, pAccInfo->transferredBytes);
      CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: Transferred bytes of %s for account %s are lower than prepaid amount! "
                  "Maybe we lost a CC?\n",tmp2, tmp);
      prepaidAmount = 0;
    }
  }
  else
  {
    prepaidAmount = 0;
    CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: No database record for prepaid bytes found for account nr. %s.\n", tmp);
  }
  //CAMsg::printMsg(LOG_DEBUG, "Number of prepaid (confirmed-transferred) bytes : %d \n",pAccInfo->confirmedBytes-pAccInfo->transferredBytes);

   SINT32 dbRet;
  if(dbInterface != NULL);
  {
    dbRet = dbInterface->getAccountStatus(pAccInfo->accountNumber, status);
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  }

  if (dbRet != E_SUCCESS)
  {
    UINT8 tmp[32];
    print64(tmp,pAccInfo->accountNumber);
    CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: Could not check status for account %s!\n", tmp);
  }
  else if (status > CAXMLErrorMessage::ERR_OK)
  {
    //UINT32 authFlags = 0;
    //the auth flags are set after this check, but they need to be verified during a forced settlement
    //at the end of the login. (see finishLoginProcess())
    CAMsg::printMsg(LOG_INFO, "CAAccountingInstance: Illegal status %u for account %llu found.\n",
        status, pAccInfo->accountNumber);
    if (status == CAXMLErrorMessage::ERR_BLOCKED)
    {
      pAccInfo->authFlags |= AUTH_BLOCKED;
    }
    else if (status == CAXMLErrorMessage::ERR_KEY_NOT_FOUND)
    {
      pAccInfo->authFlags |= AUTH_INVALID_ACCOUNT;
    }
    else if (status == CAXMLErrorMessage::ERR_ACCOUNT_EMPTY)
    {
      pAccInfo->authFlags |= AUTH_ACCOUNT_EMPTY;
    }

    /*if (authFlags)
    {
      pAccInfo->authFlags |= AUTH_BLOCKED;
      if (loginEntry->confirmedBytes == 0)
      {
        loginEntry->confirmedBytes = pAccInfo->confirmedBytes;
      }
    }*/
  }
  m_currentAccountsHashtable->getMutex()->unlock();

  //pAccInfo->mutex->lock();
  SINT32 sendStatus = E_SUCCESS;
  if (bSendCCRequest)
  {
    // fetch cost confirmation from last session if available, and send it
    //CAXMLCostConfirmation * pCC = NULL;
    //m_dbInterface->getCostConfirmation(pAccInfo->accountNumber, m_currentCascade, &pCC);
    if(pCC != NULL)
    {
#ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Sending pcc to sign with %llu transferred bytes\n", pCC->getTransferredBytes());
#endif
      // the typical case; the user had logged in before
      /* there shouldn't be any counting synchronisation problems with the JAP
       * because the new login protocol doesn't permit JAPs
       * to exchange data before login is finished.
       */
      UINT32 prepaidIval = CALibProxytest::getOptions()->getPrepaidInterval();
      pAccInfo->bytesToConfirm = (prepaidIval - prepaidAmount) + pCC->getTransferredBytes();
#ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: before CC request, bytesToConfirm: %llu, prepaidIval: %u, "
          "prepaidAmount: %d, transferred bytes: %llu\n",
          pAccInfo->bytesToConfirm, prepaidIval, prepaidAmount, pAccInfo->transferredBytes);

#endif
      if( (pAccInfo->clientVersion == NULL) ||
        (strncmp((char*)pAccInfo->clientVersion, PREPAID_PROTO_CLIENT_VERSION, CLIENT_VERSION_STR_LEN) < 0) )
      {
        //old CC without payRequest and prepaid bytes.
        //CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Account %llu: Old prepaid proto version.\n", pAccInfo->accountNumber);
        sendStatus = pAccInfo->pControlChannel->sendXMLMessage(pCC->getXMLDocument());

      }
      else
      {
        //CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Account %llu: New prepaid proto version. prepaid bytes: %d\n",
        //    pAccInfo->accountNumber, prepaidAmount);
        sendStatus = sendInitialCCRequest(pAccInfo, pCC, prepaidAmount);
      }
    }
    else
    {
      // there is no CC in the database; typically this is the first connection of this user
      if (prepaidAmount > 0)
      {
        // Delete any previously stored prepaid amount; there should not be any! CC lost?
        pAccInfo->transferredBytes += prepaidAmount;
      }
      sendStatus = sendCCRequest(pAccInfo);
    }
  }


  delete pCC;
  pCC = NULL;


  if ( pAccInfo->pChallenge != NULL ) // free mem
  {
    delete[] pAccInfo->pChallenge;
    pAccInfo->pChallenge = NULL;
  }
  //CAMsg::printMsg( LOG_ERR, "CAAccountingInstance::handleChallengeResponse stop\n");

  pAccInfo->mutex->unlock();
  if(sendStatus == E_SUCCESS)
  {
    return CAXMLErrorMessage::ERR_OK;
  }
  else
  {
    return CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR;
  }
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleCostConfirmation ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Handles a cost confirmation sent by a jap.

Definition at line 2197 of file CAAccountingInstance.cpp.

References FINISH_STACK, handleCostConfirmation_internal(), and INIT_STACK.

Referenced by processJapMessage().

{
  return handleCostConfirmation_internal(pAccInfo, root);
  INIT_STACK;
  FINISH_STACK("CAAccountingInstance::handleCostConfirmation");
}

Here is the call graph for this function:

UINT32 CAAccountingInstance::handleCostConfirmation_internal ( tAiAccountingInfo pAccInfo,
DOMElement *  root 
) [private]

Handles a cost confirmation sent by a jap.

Definition at line 2207 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_ACCOUNT_OK, AUTH_DATABASE, AUTH_DELETE_ENTRY, AUTH_FAKE, AUTH_GOT_ACCOUNTCERT, AUTH_HARD_LIMIT_REACHED, AUTH_LOGIN_NOT_FINISHED, AUTH_LOGIN_SKIP_SETTLEMENT, AUTH_SENT_CC_REQUEST, AUTH_WAITING_FOR_FIRST_SETTLED_CC, t_accountinginfo::authFlags, BEGIN_STACK, t_accountinginfo::bytesToConfirm, cascadeMatchesCC(), t_accountinginfo::confirmedBytes, E_SUCCESS, E_UNKNOWN, CAXMLErrorMessage::ERR_BAD_REQUEST, CAXMLErrorMessage::ERR_BAD_SIGNATURE, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_NO_ERROR_GIVEN, CAXMLErrorMessage::ERR_NO_RECORD_FOUND, CAXMLErrorMessage::ERR_OK, CAXMLErrorMessage::ERR_WRONG_DATA, CAXMLErrorMessage::ERR_WRONG_FORMAT, CAXMLCostConfirmation::getAccountNumber(), CAAccountingDBInterface::getConnection(), getcurrentTimeMillis(), CAXMLCostConfirmation::getInstance(), CAXMLCostConfirmation::getNumberOfHashes(), CALibProxytest::getOptions(), getPrepaidBytes(), CACmdLnOptions::getPrepaidInterval(), CAXMLCostConfirmation::getTransferredBytes(), INIT_STACK, t_accountinginfo::lastHardLimitSeconds, CAMutex::lock(), m_allHashesLen, m_currentCascade, m_pSettleThread, t_accountinginfo::mutex, t_accountinginfo::pControlChannel, t_accountinginfo::pPublicKey, print64(), CAMsg::printMsg(), CAAccountingDBInterface::releaseConnection(), CAAbstractControlChannel::sendXMLMessage(), CAAccountingSettleThread::settle(), CAAccountingDBInterface::storeCostConfirmation(), CAAbstractXMLEncodable::toXmlDocument(), t_accountinginfo::transferredBytes, CAMutex::unlock(), and CASignature::verifyXML().

Referenced by handleCostConfirmation().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::handleCostConfirmation");

  if (pAccInfo == NULL)
  {
    return CAXMLErrorMessage::ERR_NO_RECORD_FOUND;
  }

  pAccInfo->mutex->lock();

  if ( (pAccInfo->authFlags & AUTH_DELETE_ENTRY) ||
     (!(pAccInfo->authFlags & AUTH_LOGIN_NOT_FINISHED ) && (pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY )) )
  {
    CAMsg::printMsg(LOG_ERR,
          "CAAccountingInstance::handleCostConfirmation Ignoring CC, restricted flags set: %s %s\n",
          ((pAccInfo->authFlags & AUTH_DELETE_ENTRY) ? "AUTH_DELETE_ENTRY" : "") ,
          ((pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY) ? "AUTH_ACCOUNT_EMPTY" : ""));
    // ignore CCs for this account!
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_NO_ERROR_GIVEN;
  }

  // check authstate
  if (!(pAccInfo->authFlags & AUTH_GOT_ACCOUNTCERT) ||
    !(pAccInfo->authFlags & AUTH_ACCOUNT_OK) ||
    !(pAccInfo->authFlags & AUTH_SENT_CC_REQUEST))
  {
    CAMsg::printMsg(LOG_ERR,
      "CAAccountingInstance::handleCostConfirmation CC was received but has not been requested! Ignoring...\n");

    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_NO_ERROR_GIVEN;
  }

  CAXMLCostConfirmation* pCC = CAXMLCostConfirmation::getInstance(root);
  if(pCC==NULL)
  {
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR;
  }

  // for debugging only: test signature the oldschool way
  // warning this removes the signature from doc!!!
  if (pAccInfo->pPublicKey==NULL||
    pAccInfo->pPublicKey->verifyXML( root ) != E_SUCCESS)
  {
    // wrong signature
    CAMsg::printMsg( LOG_INFO, "CostConfirmation has INVALID SIGNATURE!\n" );
    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_BAD_SIGNATURE, (UINT8*)"CostConfirmation has bad signature");
    XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);
    if (errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    delete pCC;
    pCC = NULL;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_BAD_SIGNATURE;
  }
  #ifdef DEBUG
  else
    {
      CAMsg::printMsg( LOG_DEBUG, "CostConfirmation Signature is OK.\n");
    }
  #endif


  if (pCC->getNumberOfHashes() != m_allHashesLen)
  {
    CAMsg::printMsg( LOG_INFO, "CostConfirmation has illegal number of price cert hashes!\n" );
    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_BAD_REQUEST,
      (UINT8*)"CostConfirmation has illegal number of price cert hashes");
    XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);
    if (errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    delete pCC;
    pCC = NULL;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_WRONG_FORMAT;
  }

  /*Hashtable* certHashCC =
    new Hashtable((UINT32 (*)(void *))Hashtable::stringHash, (SINT32 (*)(void *,void *))Hashtable::stringCompare);
  UINT8* certHash;*/
  //bool bFailed = false;
  //for (UINT32 i = 0; i < pCC->getNumberOfHashes(); i++)
  //{
  //  certHash = pCC->getPriceCertHash(i);
  //  certHashCC->put(certHash, certHash);
    /*
    if ((certHash = (UINT8*)certHashCC->getValue(certHash)) != NULL)
    {
      CAMsg::printMsg( LOG_INFO, "CC1: %s\n", certHash);
    }*/
  //}
  //for (UINT32 i = 0; i < m_allHashesLen; i++)
  //{
    //CAMsg::printMsg( LOG_INFO, "CA:  %s\n", m_allHashes[i]);
  //  certHash = (UINT8*)certHashCC->remove(m_allHashes[i]);
  //  if (certHash == NULL)
  //  {
  //    bFailed = true;
  //    break;
  //  }
  //  else
  //  {
  //    delete[] certHash;
  //  }
  //}
  //certHashCC->clear(HASH_EMPTY_NONE, HASH_EMPTY_DELETE);
  //delete certHashCC;

  if (!cascadeMatchesCC(pCC))
  {
    CAMsg::printMsg( LOG_INFO, "CostConfirmation has invalid price cert hashes!\n" );
    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_BAD_REQUEST,
      (UINT8*)"CostConfirmation has invalid price cert hashes");
    XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);
    if (errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    delete pCC;
    pCC = NULL;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_WRONG_DATA;
  }



  // parse & set transferredBytes
  //when using Prepayment, this check is outdated, but left in to notice the most crude errors/cheats
  //The CC's transferredBytes should be equivalent to
  //AccInfo's confirmed bytes + the Config's PrepaidInterval - the number of bytes transferred between
  //requesting and receiving the CC
#ifdef DEBUG
  CAMsg::printMsg( LOG_DEBUG, "received cost confirmation for  %llu transferred bytes where confirmed bytes are %llu, we need %llu bytes to confirm"
      ", mix already counted %llu transferred bytes\n",
      pCC->getTransferredBytes(), pAccInfo->confirmedBytes, pAccInfo->bytesToConfirm,
      pAccInfo->transferredBytes);
#endif

  if(pCC->getTransferredBytes() > (pAccInfo->transferredBytes+CALibProxytest::getOptions()->getPrepaidInterval()) )
  {
    CAMsg::printMsg( LOG_ERR, "Warning: ignoring this CC for account %llu "
        "because it tries to confirm %lld prepaid bytes where only %u prepaid bytes are allowed (cc->tranferredbytes: %llu, accInfo->tranferredBytes: %llu)\n",
        pAccInfo->accountNumber, (pCC->getTransferredBytes() - pAccInfo->transferredBytes),
        CALibProxytest::getOptions()->getPrepaidInterval(),
        pCC->getTransferredBytes(), pAccInfo->transferredBytes);

    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_WRONG_DATA,
      (UINT8*)"More bytes confirmed than allowed.");
    XERCES_CPP_NAMESPACE::DOMDocument* errDoc=NULL;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);
    if (errDoc != NULL)
    {
      errDoc->release();
      errDoc = NULL;
    }
    //mark as account empty has the effect is that the user can empty his prepaid amount and then will be kicked out.
    pAccInfo->authFlags |= AUTH_FAKE;
    delete pCC;
    pCC = NULL;
    pAccInfo->mutex->unlock();
    return CAXMLErrorMessage::ERR_WRONG_DATA;
  }

  if (pCC->getTransferredBytes() < pAccInfo->confirmedBytes)
  {

    CAMsg::printMsg( LOG_ERR, "CostConfirmation has insufficient number of bytes:\n");
    CAMsg::printMsg( LOG_ERR, "CC->transferredBytes: %llu < confirmedBytesBytes: %llu\n", pCC->getTransferredBytes(), pAccInfo->confirmedBytes);

    if(pAccInfo->authFlags & AUTH_LOGIN_NOT_FINISHED)
    {
      //@todo: perhaps we should use another flag to indicate that this user should be kicked out.
      pAccInfo->authFlags |= AUTH_FAKE;
      delete pCC;
      pCC = NULL;
      pAccInfo->mutex->unlock();
      return CAXMLErrorMessage::ERR_WRONG_DATA;
    }

    /*
    CAXMLErrorMessage err(CAXMLErrorMessage::ERR_WRONG_DATA,
      (UINT8*)"Your CostConfirmation has a wrong number of transferred bytes");
    DOM_Document errDoc;
    err.toXmlDocument(errDoc);
    pAccInfo->pControlChannel->sendXMLMessage(errDoc);*/
  }
  else if (pCC->getTransferredBytes() == pAccInfo->confirmedBytes && getPrepaidBytes(pAccInfo) != CALibProxytest::getOptions()->getPrepaidInterval())
  {
    CAMsg::printMsg(LOG_WARNING, "Received CostConfirmation for account %llu has no difference in bytes to current CC (%llu bytes).\n", 
      pAccInfo->accountNumber, pCC->getTransferredBytes());
  }
  else
  {
    /*
    UINT8 tmp[32];
    print64(tmp,pCC->getTransferredBytes());
    CAMsg::printMsg( LOG_ERR, "Transferredbytes in CC: %s\n", tmp);
    */
    SINT32 dbRet = E_UNKNOWN;
    CAAccountingDBInterface *dbInterface = CAAccountingDBInterface::getConnection();
    if(dbInterface != NULL)
    {
      dbRet = dbInterface->storeCostConfirmation(*pCC, m_currentCascade);
      CAAccountingDBInterface::releaseConnection(dbInterface);
      dbInterface = NULL;
    }
    if (dbRet != E_SUCCESS)
    {
      UINT8 tmp[32];
      print64(tmp,pCC->getAccountNumber());
      CAMsg::printMsg( LOG_INFO, "CostConfirmation for account %s could not be stored in database!\n", tmp );
      pAccInfo->authFlags |= AUTH_DATABASE;
    }
    else
    {
#ifdef DEBUG
      CAMsg::printMsg( LOG_INFO, "Handle CC: pCC->transBytes: %llu\n", pCC->getTransferredBytes() );
#endif
      pAccInfo->confirmedBytes = pCC->getTransferredBytes();

      if (pAccInfo->authFlags & AUTH_WAITING_FOR_FIRST_SETTLED_CC)
      {
        // initiate immediate settling
#ifdef DEBUG
        UINT64 currentMillis;
        UINT8 tmpStrCurrentMillis[50];
        getcurrentTimeMillis(currentMillis);
        print64(tmpStrCurrentMillis,currentMillis);
        CAMsg::printMsg(LOG_DEBUG, "AccountingSettleThread: Settle ini: %s\n", tmpStrCurrentMillis);
#endif
        m_pSettleThread->settle();

      }
    }
  }
#ifdef DEBUG
  CAMsg::printMsg(LOG_ERR, "Handle CC request: pAccInfo->confirmedBytes: %llu, ppAccInfo->transferredBytes: %llu\n",
      pAccInfo->confirmedBytes, pAccInfo->transferredBytes);
#endif
  if (pAccInfo->confirmedBytes >= pAccInfo->bytesToConfirm)
  {
    // the user confirmed everything we wanted; if a timeout has been set, it should be reset
    pAccInfo->authFlags &= ~AUTH_HARD_LIMIT_REACHED;
    pAccInfo->lastHardLimitSeconds = time(NULL);
  }
  else
  {
    /*UINT8 tmp[32], tmp2[32], tmp3[32];
    print64(tmp, pCC->getTransferredBytes());
    print64(tmp2, pCC->getAccountNumber());
    print64(tmp3, pAccInfo->bytesToConfirm);*/
    CAMsg::printMsg(LOG_ERR, "AccountingSettleThread: Requested CC value has NOT been confirmed by account nr %llu! "
                 "Received Bytes: %llu/%llu "
                "Client should not be allowed to login.\n",
                pCC->getAccountNumber(),
                pCC->getTransferredBytes(),
                pAccInfo->bytesToConfirm);

    if(pAccInfo->authFlags & AUTH_LOGIN_SKIP_SETTLEMENT)
    {
      CAMsg::printMsg( LOG_INFO, "Skip settlement revoked.\n");
      pAccInfo->authFlags &= ~AUTH_LOGIN_SKIP_SETTLEMENT;
    }
    /*delete pCC;
    pCC = NULL;
    pAccInfo->mutex->unlock();
    pAccInfo->authFlags |= AUTH_FAKE;
    return CAXMLErrorMessage::ERR_WRONG_DATA;*/
    //m_pSettleThread->settle();

  }

  pAccInfo->bytesToConfirm = 0;
  pAccInfo->authFlags &= ~AUTH_SENT_CC_REQUEST;

  delete pCC;
  pCC = NULL;
  pAccInfo->mutex->unlock();

  return CAXMLErrorMessage::ERR_OK;
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::handleJapPacket ( fmHashTableEntry pHashEntry,
bool  a_bControlMessage,
bool  a_bMessageToJAP 
) [static]

This should be called by the FirstMix for every incoming Jap packet.

Definition at line 315 of file CAAccountingInstance.cpp.

References FINISH_STACK, handleJapPacket_internal(), and INIT_STACK.

Referenced by CAFirstMixA::accountTrafficDownstream(), CAFirstMixA::accountTrafficUpstream(), and CAFirstMixB::loop().

{
  SINT32 ret = handleJapPacket_internal(pHashEntry, a_bControlMessage, a_bMessageToJAP);

  INIT_STACK;
  FINISH_STACK("CAAccountingInstance::handleJapPacket");

  return ret;
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::handleJapPacket_internal ( fmHashTableEntry pHashEntry,
bool  a_bControlMessage,
bool  a_bMessageToJAP 
) [static, private]

Called by FirstMix for each incoming JAP packet.

Determines whether the packet should be let through or not

Possible return values, and FirstMix's reaction:

Returns:
1: everything is OK, forward packet to next mix
2: we need something (cert, CC,...) from the user, hold packet and start timeout
3: fatal error, or timeout exceeded -> kick the user out

Do not make further checkings. Let the client use the remaining prepaid bytes, and then disconnect him afterwards. As the confirmedBytes are set to zero when the client connects and the account has been empty before, no other (unauthenticated) client may use these bytes.

Todo:
We need this trick so that the program does not freeze with active AI ThreadPool!!!!

Definition at line 335 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_DATABASE, AUTH_DELETE_ENTRY, AUTH_FATAL_ERROR, AUTH_HARD_LIMIT_REACHED, AUTH_INVALID_ACCOUNT, AUTH_LOGIN_NOT_FINISHED, AUTH_MULTIPLE_LOGIN, AUTH_OUTDATED_CC, AUTH_SENT_CC_REQUEST, AUTH_TIMEOUT_STARTED, AUTH_UNKNOWN, AUTH_WAITING_FOR_FIRST_SETTLED_CC, AccountLoginHashEntry::authFlags, t_accountinginfo::authFlags, AccountLoginHashEntry::authRemoveFlags, BEGIN_STACK, AccountLoginHashEntry::confirmedBytes, t_accountinginfo::confirmedBytes, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_DATABASE_ERROR, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAXMLErrorMessage::ERR_MULTIPLE_LOGIN, CAXMLErrorMessage::ERR_NO_CONFIRMATION, CAAccountingDBInterface::getConnection(), CAAccountingDBInterface::getCostConfirmation(), Hashtable::getMutex(), CALibProxytest::getOptions(), getPrepaidBytes(), CACmdLnOptions::getPrepaidInterval(), Hashtable::getValue(), CAXMLCostConfirmation::getXMLDocument(), HANDLE_PACKET_CLOSE_CONNECTION, HANDLE_PACKET_CONNECTION_OK, HANDLE_PACKET_CONNECTION_UNCHECKED, HANDLE_PACKET_PREPARE_FOR_CLOSING_CONNECTION, HARD_LIMIT_TIMEOUT, INIT_STACK, t_accountinginfo::lastHardLimitSeconds, CAMutex::lock(), m_currentAccountsHashtable, m_currentCascade, m_iHardLimitBytes, m_iSoftLimitBytes, MIXPACKET_SIZE, ms_pInstance, t_accountinginfo::mutex, t_accountinginfo::ownerRef, t_fmhashtableentry::pAccountingInfo, PACKETS_BEFORE_NEXT_CHECK, t_accountinginfo::pControlChannel, print64(), CAMsg::printMsg(), CAAccountingDBInterface::releaseConnection(), returnKickout(), returnPrepareKickout(), SAVE_STACK, sendCCRequest(), CAAbstractControlChannel::sendXMLMessage(), t_accountinginfo::sessionPackets, t_accountinginfo::transferredBytes, and CAMutex::unlock().

Referenced by handleJapPacket().

  {
    INIT_STACK;
    BEGIN_STACK("CAAccountingInstance::handleJapPacket");

    CAAccountingDBInterface *dbInterface = NULL;

    if (pHashEntry == NULL || pHashEntry->pAccountingInfo == NULL)
    {
      return HANDLE_PACKET_CLOSE_CONNECTION;
    }

    tAiAccountingInfo* pAccInfo = pHashEntry->pAccountingInfo;
    AccountLoginHashEntry* loginEntry = NULL;
    CAXMLErrorMessage* err = NULL;

    pAccInfo->mutex->lock();

    if (pAccInfo->authFlags & AUTH_DELETE_ENTRY)
    {
      pAccInfo->mutex->unlock();
      return HANDLE_PACKET_CLOSE_CONNECTION;
    }

    //Should never happen since control flow assert that this method cannot be invoked
    //when a login is not finished.
    if ( (pAccInfo->authFlags & AUTH_LOGIN_NOT_FINISHED) && !a_bMessageToJAP )
    {
      CAMsg::printMsg(LOG_ERR, "CAAccountingInstance:  User violates login protocol");
      pAccInfo->mutex->unlock();
      return HANDLE_PACKET_CLOSE_CONNECTION;
    }

    //still preparing to close connection -> no data is forwarded upstream and only control messages are
    //sent downstream if a user connection is in this state.
    if ( pAccInfo->authFlags & AUTH_FATAL_ERROR )
    {
      pAccInfo->mutex->unlock();
      return HANDLE_PACKET_PREPARE_FOR_CLOSING_CONNECTION;
    }

    if (a_bControlMessage)
    {
      pAccInfo->mutex->unlock();
      return HANDLE_PACKET_CONNECTION_UNCHECKED;
    }
    else
    {
       // count the packet and continue checkings
      pAccInfo->transferredBytes += MIXPACKET_SIZE;
      pAccInfo->sessionPackets++;
#ifdef SDTFA
      IncrementShmPacketCount();
#endif
    }

    if (pAccInfo->authFlags & AUTH_MULTIPLE_LOGIN)
    {
      return returnPrepareKickout(pAccInfo, new CAXMLErrorMessage(CAXMLErrorMessage::ERR_MULTIPLE_LOGIN, (UINT8*)"Only one login per account is allowed!"));
    }

    // do the following tests after a lot of Mix packets only (gain speed...)
    if (!(pAccInfo->authFlags & (AUTH_TIMEOUT_STARTED | AUTH_HARD_LIMIT_REACHED | AUTH_ACCOUNT_EMPTY | AUTH_WAITING_FOR_FIRST_SETTLED_CC)) &&
      pAccInfo->sessionPackets % PACKETS_BEFORE_NEXT_CHECK != 0)
    {
      //CAMsg::printMsg( LOG_DEBUG, "Now we gain some speed after %d session packets..., auth-flags: %x\n", pAccInfo->sessionPackets, pAccInfo->authFlags);
      pAccInfo->mutex->unlock();
      return HANDLE_PACKET_CONNECTION_UNCHECKED;
    }


    SAVE_STACK("CAAccountingInstance::handleJapPacket", "before accounts hash");

    if (!ms_pInstance->m_currentAccountsHashtable)
    {
      // accounting instance is dying...
      return returnKickout(pAccInfo);
    }

    ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
    //Suppose, this section checks the flags set after settlement */
    loginEntry = (AccountLoginHashEntry*)ms_pInstance->m_currentAccountsHashtable->getValue(&(pAccInfo->accountNumber));
    if (loginEntry)
    {
      pAccInfo->authFlags &= ~loginEntry->authRemoveFlags;
      //CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Remove flag: %d\n", loginEntry->authRemoveFlags);


      /*if (loginEntry->ownerRef != pHashEntry)
      {
        // this is not the latest connection of this user; kick him out...
        pAccInfo->authFlags |= AUTH_MULTIPLE_LOGIN;
        ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();
        return returnPrepareKickout(pAccInfo, new CAXMLErrorMessage(CAXMLErrorMessage::ERR_MULTIPLE_LOGIN, (UINT8*)"Only one login per account is allowed!"));
      }
      else */
      if (loginEntry->authFlags & AUTH_OUTDATED_CC)
      {
        loginEntry->authFlags &= ~AUTH_OUTDATED_CC;

        UINT8 tmp[32];
        print64(tmp,pAccInfo->accountNumber);
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Fixing bytes from outdated CC for account %s...\n", tmp);
        // we had stored an outdated CC; insert confirmed bytes from current CC here and also update client
        CAXMLCostConfirmation * pCC = NULL;
        bool bSettled;

        dbInterface = CAAccountingDBInterface::getConnection();//new CAAccountingDBInterface();
        if(dbInterface != NULL)
        {
          dbInterface->getCostConfirmation(pAccInfo->accountNumber,
                           ms_pInstance->m_currentCascade,
                           &pCC,
                           bSettled);
          CAAccountingDBInterface::releaseConnection(dbInterface);
          dbInterface = NULL;
        }
        /*ms_pInstance->m_dbInterface->getCostConfirmation(pAccInfo->accountNumber,
          ms_pInstance->m_currentCascade, &pCC, bSettled);*/

        if (pCC != NULL)
        {
          if (bSettled)
          {
            pAccInfo->transferredBytes += loginEntry->confirmedBytes - pAccInfo->confirmedBytes;
            pAccInfo->confirmedBytes = loginEntry->confirmedBytes;
            loginEntry->confirmedBytes = 0;
            pAccInfo->authFlags |= AUTH_SENT_CC_REQUEST;
            pAccInfo->pControlChannel->sendXMLMessage(pCC->getXMLDocument());
          }
          else
          {
            CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: While trying to fix bytes from outdated CC,"
              "another CC was received! Waiting for settlement... \n");
          }
          delete pCC;
          pCC = NULL;
        }
        else
        {
          CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: Bytes from outdated CC could not be fixed!\n");
        }
      }
      else if (loginEntry->authFlags & AUTH_ACCOUNT_EMPTY)
      {
        if(!(pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY))
        {
          // Do not reset the flag, so that the confirmedBytes are always reset.
          //loginEntry->authFlags &= ~AUTH_ACCOUNT_EMPTY;
          pAccInfo->authFlags |= AUTH_ACCOUNT_EMPTY;
          /* confirmedBytes = 0 leads to immediate disconnection.
           * If confirmedBytes > 0,  any remaining prepaid bytes may be used.
           */
          pAccInfo->confirmedBytes = loginEntry->confirmedBytes;
          if (pAccInfo->confirmedBytes < pAccInfo->transferredBytes)
          {
            // this account is really empty; prevent an overflow in the prepaid bytes calculation
             pAccInfo->transferredBytes = pAccInfo->confirmedBytes;
          }

          //CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: Account %llu is empty!\n", pAccInfo->accountNumber);
          CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Account %llu empty with %d prepaid bytes, (transferred bytes: %llu, confirmed bytes: %llu)!\n",
                      pAccInfo->accountNumber, getPrepaidBytes(pAccInfo), pAccInfo->transferredBytes, pAccInfo->confirmedBytes);
        }

      }
      else if (loginEntry->authFlags & AUTH_INVALID_ACCOUNT)
      {
        loginEntry->authFlags &= ~AUTH_INVALID_ACCOUNT;
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Found invalid account %llu ! Kicking out user...\n", pAccInfo->accountNumber);
        err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_KEY_NOT_FOUND);
      }
      else if (loginEntry->authFlags & AUTH_BLOCKED)
      {
        loginEntry->authFlags &= ~AUTH_BLOCKED;
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Kicking out user with blocked account %llu !\n", pAccInfo->accountNumber);
        err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_BLOCKED);
      }
      else if (loginEntry->authFlags & AUTH_DATABASE)
      {
        loginEntry->authFlags &= ~AUTH_DATABASE;
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Kicking out user with account %llu due to database error...\n", pAccInfo->accountNumber);
        err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_DATABASE_ERROR);
      }
      else if (loginEntry->authFlags & AUTH_UNKNOWN)
      {
        loginEntry->authFlags &= ~AUTH_UNKNOWN;
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Unknown error, account %llu! Kicking out user...\n", pAccInfo->accountNumber);
        err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR);
      }
    }
    else
    {
      CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: handleJapPacket %s,%s did not find user login hash entry for account %llu, owner/accInfo: %p/%p!\n",
          (a_bMessageToJAP ? "downstream" : "upstream"),
          (a_bControlMessage ? "ctl" : "data"),
          pAccInfo->accountNumber, pAccInfo->ownerRef, pHashEntry->pAccountingInfo);
      ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();
      return returnKickout(pAccInfo);
    }

    ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();

    if (pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY)
    {
      // There should be no time limit. The connections is simply closed after all prepaid bytes are gone.
      pAccInfo->lastHardLimitSeconds = time(NULL);

      //#ifdef DEBUG

      //#endif

      if (getPrepaidBytes(pAccInfo) <= 0)
      {
        CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: Account %llu empty! Kicking out user...\n",
            pAccInfo->accountNumber);
        err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_ACCOUNT_EMPTY);
      }
      else
      {
        pAccInfo->mutex->unlock();
        return HANDLE_PACKET_CONNECTION_OK;
      }
    }

    //pAccInfo->mutex->lock();

    SAVE_STACK("CAAccountingInstance::handleJapPacket", "before err");

    if (err)
    {
      return returnPrepareKickout(pAccInfo, err);
    }

    //----------------------------------------------------------
    // ******     Hardlimit cost confirmation check **********
    //counting unconfirmed bytes is not necessary anymore, since we deduct from prepaid bytes
    //UINT32 unconfirmedBytes=diff64(pAccInfo->transferredBytes,pAccInfo->confirmedBytes);

    //confirmed and transferred bytes are cumulative, so they use UINT64 to store potentially huge values
    //prepaid Bytes as the difference will be much smaller, but might be negative, so we cast to signed int
    SINT32 prepaidBytes = getPrepaidBytes(pAccInfo);

    if (prepaidBytes < 0 ||  prepaidBytes <= (SINT32) ms_pInstance->m_iHardLimitBytes)
    {
      UINT32 prepaidInterval = CALibProxytest::getOptions()->getPrepaidInterval();

      if ((pAccInfo->authFlags & AUTH_HARD_LIMIT_REACHED) == 0)
      {
        pAccInfo->lastHardLimitSeconds = time(NULL);
        pAccInfo->authFlags |= AUTH_HARD_LIMIT_REACHED;
      }

#ifdef DEBUG
      CAMsg::printMsg(LOG_ERR, "CAAccountingInstance: Hard limit of %d bytes triggered in %d seconds \n",
              ms_pInstance->m_iHardLimitBytes,
              (pAccInfo->lastHardLimitSeconds + HARD_LIMIT_TIMEOUT - time(NULL)));
#endif

      if ( ( (time(NULL) >= pAccInfo->lastHardLimitSeconds + HARD_LIMIT_TIMEOUT) && (prepaidBytes <= 0) )
           || (prepaidBytes < 0 && (UINT32)(prepaidBytes * (-1)) >= prepaidInterval) )
      {
//#ifdef DEBUG
        char* strReason;
        if (time(NULL) >= pAccInfo->lastHardLimitSeconds + HARD_LIMIT_TIMEOUT)
        {
          strReason = "timeout";
        }
        else
        {
          strReason = "negative prepaid interval exceeded";
        }
        CAMsg::printMsg( LOG_INFO, "Accounting instance: User refused "
                "to send cost confirmation (HARDLIMIT EXCEEDED, %s). "
                "PrepaidBytes were: %d\n", strReason, prepaidBytes);
//#endif

        //ms_pInstance->m_pIPBlockList->insertIP( pHashEntry->peerIP );
        pAccInfo->lastHardLimitSeconds = 0;
        return returnPrepareKickout(pAccInfo, new CAXMLErrorMessage(CAXMLErrorMessage::ERR_NO_CONFIRMATION));
      }
      else
      {
        if( !(pAccInfo->authFlags & AUTH_SENT_CC_REQUEST) )
        {
          sendCCRequest(pAccInfo);
        }
      }
    }
    else
    {
      pAccInfo->authFlags &= ~AUTH_HARD_LIMIT_REACHED;
    }

    //-------------------------------------------------------
    // *** SOFT LIMIT CHECK *** is it time to request a new cost confirmation?
    if ( prepaidBytes < 0 || prepaidBytes <= (SINT32) ms_pInstance->m_iSoftLimitBytes )
    {
#ifdef DEBUG
      CAMsg::printMsg(LOG_ERR, "soft limit of %d bytes triggered \n",ms_pInstance->m_iSoftLimitBytes);
#endif
      if( !(pAccInfo->authFlags & AUTH_SENT_CC_REQUEST) )
      {//we have sent a first CC request
        // no CC request sent yet --> send a first CC request
        sendCCRequest(pAccInfo);
      }
    }// end of soft limit exceeded

    //everything is fine! let the packet pass thru
    pAccInfo->mutex->unlock();
    return HANDLE_PACKET_CONNECTION_OK;
  }

Here is the call graph for this function:

static SINT32 CAAccountingInstance::init ( CAFirstMix callingMix) [inline, static]

Returns a reference to the Singleton instance.

Definition at line 121 of file CAAccountingInstance.hpp.

References CAAccountingInstance(), E_SUCCESS, MONITORING_FIRE_PAY_EVENT, and ms_pInstance.

    {
        ms_pInstance = new CAAccountingInstance(callingMix);
        MONITORING_FIRE_PAY_EVENT(ev_pay_aiInited);
        return E_SUCCESS;
    }

Here is the call graph for this function:

This must be called whenever a JAP is connecting to init our per-user data structures.

Definition at line 2511 of file CAAccountingInstance.cpp.

References AUTH_HARD_LIMIT_REACHED, AUTH_LOGIN_NOT_FINISHED, AUTH_SENT_ACCOUNT_REQUEST, AUTH_SENT_CC_REQUEST, AUTH_TIMEOUT_STARTED, AUTH_WAITING_FOR_FIRST_SETTLED_CC, t_accountinginfo::authFlags, t_accountinginfo::authTimeoutStartSeconds, BEGIN_STACK, t_accountinginfo::bytesToConfirm, t_accountinginfo::clientVersion, t_accountinginfo::confirmedBytes, E_SUCCESS, E_UNKNOWN, FINISH_STACK, t_fmhashtableentry::id, INIT_STACK, t_accountinginfo::lastHardLimitSeconds, t_accountinginfo::mutex, t_accountinginfo::nrInQueue, t_accountinginfo::ownerRef, t_fmhashtableentry::pAccountingInfo, SAVE_STACK, t_accountinginfo::sessionPackets, t_accountinginfo::transferredBytes, and t_accountinginfo::userID.

Referenced by CAAccountingControlChannel::CAAccountingControlChannel().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::initTableEntry");

  //ms_pInstance->m_pMutex->lock();

  if (pHashEntry == NULL)
  {
    FINISH_STACK("CAAccountingInstance::initTableEntry:NULL");
    return E_UNKNOWN;
  }

  pHashEntry->pAccountingInfo = new tAiAccountingInfo;
  memset( pHashEntry->pAccountingInfo, 0, sizeof( tAiAccountingInfo ) );

  SAVE_STACK("CAAccountingInstance::initTableEntry", "After memset");

  pHashEntry->pAccountingInfo->authFlags =
    AUTH_SENT_ACCOUNT_REQUEST | AUTH_TIMEOUT_STARTED |
    AUTH_HARD_LIMIT_REACHED | AUTH_WAITING_FOR_FIRST_SETTLED_CC |
    AUTH_SENT_CC_REQUEST | AUTH_LOGIN_NOT_FINISHED; // prevents multiple CC requests on login
  pHashEntry->pAccountingInfo->authTimeoutStartSeconds = time(NULL);
  pHashEntry->pAccountingInfo->lastHardLimitSeconds = time(NULL);
  pHashEntry->pAccountingInfo->sessionPackets = 0;
  pHashEntry->pAccountingInfo->transferredBytes = 0;
  pHashEntry->pAccountingInfo->confirmedBytes = 0;
  pHashEntry->pAccountingInfo->bytesToConfirm = 0;
  pHashEntry->pAccountingInfo->nrInQueue = 0;
  pHashEntry->pAccountingInfo->userID = pHashEntry->id;
  pHashEntry->pAccountingInfo->ownerRef = pHashEntry;
  pHashEntry->pAccountingInfo->clientVersion = NULL;
  pHashEntry->pAccountingInfo->mutex = new CAMutex;
  //ms_pInstance->m_pMutex->unlock();


  FINISH_STACK("CAAccountingInstance::initTableEntry");

  return E_SUCCESS;
}
static SINT32 CAAccountingInstance::isIPAddressBlocked ( const UINT8  ip[4]) [inline, static]

Check if an IP address is temporarily blocked by the accounting instance.

This should be called by the FirstMix when a JAP is connecting.

Return values:
1if the given IP is blocked
0if it is not blocked

Definition at line 167 of file CAAccountingInstance.hpp.

  {
    //return ms_pInstance->m_pIPBlockList->checkIP(ip);
    return 0;
  }

Definition at line 1299 of file CAAccountingInstance.cpp.

References AUTH_LOGIN_FAILED, AUTH_LOGIN_NOT_FINISHED, AUTH_LOGIN_SKIP_SETTLEMENT, AUTH_MULTIPLE_LOGIN, t_accountinginfo::authFlags, CAMutex::lock(), t_accountinginfo::mutex, t_fmhashtableentry::pAccountingInfo, and CAMutex::unlock().

Referenced by CAFirstMix::doUserLogin_internal().

{
  SINT32 ret = 0;
  if(pHashEntry == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(pHashEntry->pAccountingInfo == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  if(pHashEntry->pAccountingInfo->mutex == NULL)
  {
    return ret |= AUTH_LOGIN_FAILED;
  }
  pHashEntry->pAccountingInfo->mutex->lock();
  ret = pHashEntry->pAccountingInfo->authFlags &
    (AUTH_LOGIN_NOT_FINISHED | AUTH_LOGIN_FAILED | AUTH_LOGIN_SKIP_SETTLEMENT | AUTH_MULTIPLE_LOGIN);
  pHashEntry->pAccountingInfo->mutex->unlock();
  return ret;
}

Here is the call graph for this function:

static SINT32 CAAccountingInstance::makeAccountRequest ( XERCES_CPP_NAMESPACE::DOMDocument *&  doc) [static, private]
SINT32 CAAccountingInstance::makeCCRequest ( const UINT64  accountNumber,
const UINT64  transferredBytes,
XERCES_CPP_NAMESPACE::DOMDocument *&  doc 
) [static, private]

Definition at line 1046 of file CAAccountingInstance.cpp.

References BEGIN_STACK, createDOMDocument(), createDOMElement(), E_SUCCESS, FINISH_STACK, getDOMChildByName(), INIT_STACK, m_preparedCCRequest, and setDOMElementValue().

Referenced by sendCCRequest().

  {
    INIT_STACK;
    BEGIN_STACK("CAAccountingInstance::makeCCRequest");

    DOMNode* elemCC=NULL;

    doc = createDOMDocument();
    doc->appendChild(doc->importNode(m_preparedCCRequest->getDocumentElement(),true));

    getDOMChildByName(doc->getDocumentElement(),"CC",elemCC);

    DOMElement* elemAccount = createDOMElement(doc,"AccountNumber");
    setDOMElementValue(elemAccount, accountNumber);
    elemCC->appendChild(elemAccount);
    DOMElement* elemBytes = createDOMElement(doc,"TransferredBytes");
    setDOMElementValue(elemBytes, transferredBytes);
    elemCC->appendChild(elemBytes);

    FINISH_STACK("CAAccountingInstance::makeCCRequest");

    return E_SUCCESS;
  }

Here is the call graph for this function:

SINT32 CAAccountingInstance::makeInitialCCRequest ( CAXMLCostConfirmation pCC,
XERCES_CPP_NAMESPACE::DOMDocument *&  doc,
SINT32  prepaidBytes 
) [static, private]

new initialCCRequest containing the last CC and the prepaid bytes (this is a replacement for sending the prepaid with the challenge which is now deprecated).

Definition at line 1020 of file CAAccountingInstance.cpp.

References createDOMDocument(), createDOMElement(), E_SUCCESS, E_UNKNOWN, getDOMChildByName(), CAXMLCostConfirmation::getXMLDocument(), m_preparedCCRequest, CAMsg::printMsg(), setDOMElementAttribute(), and setDOMElementValue().

Referenced by sendInitialCCRequest().

  {
    if( (pCC == NULL) || (pCC->getXMLDocument() == NULL) ||
      (pCC->getXMLDocument()->getDocumentElement() == NULL) )
    {
      CAMsg::printMsg(LOG_ERR, "Error creating initial CCrequest (pCC ref: %p)\n", pCC);
      return E_UNKNOWN;
    }
    DOMNode* elemCC=NULL;

    doc = createDOMDocument();
    DOMNode *ccRoot = doc->importNode(pCC->getXMLDocument()->getDocumentElement(),true);
    doc->appendChild(doc->importNode(m_preparedCCRequest->getDocumentElement(),true));
    setDOMElementAttribute(doc->getDocumentElement(), "initialCC", true);
    DOMElement *elemPrepaidBytes = createDOMElement(doc, "PrepaidBytes");
    setDOMElementValue(elemPrepaidBytes, prepaidBytes);
    getDOMChildByName(doc->getDocumentElement(),"CC",elemCC);
    if(elemCC == NULL)
    {
      return E_UNKNOWN;
    }
    doc->getDocumentElement()->replaceChild(ccRoot, elemCC);
    doc->getDocumentElement()->appendChild(elemPrepaidBytes);
    return E_SUCCESS;
  }

Here is the call graph for this function:

Definition at line 2762 of file CAAccountingInstance.cpp.

References __newSettlementTransaction(), E_SUCCESS, and MAX_SETTLED_CCS.

Referenced by CAFirstMix::doUserLogin_internal(), and CAAccountingSettleThread::mainLoop().

{
  UINT32 settledCCs = 0;
  SINT32 retVal;
  do
  {
    retVal = __newSettlementTransaction(&settledCCs);
  }
  while(settledCCs >= MAX_SETTLED_CCS && retVal == E_SUCCESS);
  return E_SUCCESS; // change to retVal if you want to block new users on failure; but remember this is not sufficient because prepaid bytes are stored even on failure!
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::prepareCCRequest ( CAMix callingMix,
UINT8 a_AiName 
) [private]

creating the xml of a new CC is really the responsability of the CAXMLCostConfirmation class knowledge about the structure of a CC's XML should be encapsulated in it TODO: add constructor to that class that takes accountnumber, transferredbytes etc as params (should use a template internally into which it only inserts accNumber and bytes,to speed things up TODO: add toXMLElement method then replace manually building the xml here with contructing a CAXMLCostConfirmation and just add the xml returned by its toXMLElement method

Parameters:
callingMix,:the Mix instance to which the AI belongs (needed to get cascadeInfo to extract the price certificates to include in cost confirmations)

Definition at line 886 of file CAAccountingInstance.cpp.

References createDOMDocument(), createDOMElement(), E_SUCCESS, E_UNKNOWN, CABase64::encode(), getDOMChildByName(), getElementsByTagName(), CAMix::getMixCascadeInfo(), len, m_allHashes, m_allHashesLen, m_currentCascade, m_preparedCCRequest, DOM_Output::makeCanonical(), CAMsg::printMsg(), setDOMElementAttribute(), setDOMElementValue(), and strtrim().

Referenced by CAAccountingInstance().

{
  m_preparedCCRequest = createDOMDocument();

  DOMElement* elemRoot = createDOMElement(m_preparedCCRequest,"PayRequest");
  setDOMElementAttribute(elemRoot,"version",(UINT8*) "1.0");
  m_preparedCCRequest->appendChild(elemRoot);
  DOMElement* elemCC = createDOMElement(m_preparedCCRequest,"CC");
  setDOMElementAttribute(elemCC,"version",(UINT8*) "1.2");
  elemRoot->appendChild(elemCC);
  DOMElement* elemAiName = createDOMElement(m_preparedCCRequest,"AiID");
  setDOMElementValue(elemAiName, a_AiName);
  elemCC->appendChild(elemAiName);

  //extract price certificate elements from cascadeInfo
  //get cascadeInfo from CAMix(which makeCCRequest needs to extract the price certs
  XERCES_CPP_NAMESPACE::DOMDocument* cascadeInfoDoc=NULL;
  callingMix->getMixCascadeInfo(cascadeInfoDoc);

  DOMElement* cascadeInfoElem = cascadeInfoDoc->getDocumentElement();
  DOMNodeList* allMixes = getElementsByTagName(cascadeInfoElem,"Mix");
  UINT32 nrOfMixes = allMixes->getLength();
  DOMNode** mixNodes = new DOMNode*[nrOfMixes]; //so we can use separate loops for extracting, hashing and appending

  DOMNode* curMixNode=NULL;
  for (UINT32 i = 0, j = 0, count = nrOfMixes; i < count; i++, j++){
    //cant use getDOMChildByName from CAUtil here yet, since it will always return the first child
    curMixNode = allMixes->item(i);
    if (getDOMChildByName(curMixNode,"PriceCertificate",mixNodes[j],true) != E_SUCCESS)
    {
      j--;
      nrOfMixes--;
    }
  }

  //hash'em, and get subjectkeyidentifiers
    UINT8 digest[SHA_DIGEST_LENGTH];
    m_allHashes=new UINT8*[nrOfMixes];
    m_allHashesLen = nrOfMixes;
    UINT8** allSkis=new UINT8*[nrOfMixes];
    DOMNode* skiNode=NULL;
    for (UINT32 i = 0; i < nrOfMixes; i++){
      UINT8* out=new UINT8[5000];
      UINT32 outlen=5000;

      DOM_Output::makeCanonical(mixNodes[i],out,&outlen);
      out[outlen] = 0;
#ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "price cert to be hashed: %s",out);
#endif
      SHA1(out,outlen,digest);
      delete[] out;
      out = NULL;

      UINT32 len = 1024;
      UINT8* tmpBuff = new UINT8[len+1];
      memset(tmpBuff, 0, len+1);
      if(CABase64::encode(digest,SHA_DIGEST_LENGTH,tmpBuff,&len)!=E_SUCCESS)
        return E_UNKNOWN;
      //tmpBuff[len]=0;


      //line breaks might have been added, and would lead to database problems
      strtrim(tmpBuff); //return value ohny significant for NULL or all-whitespace string, ignore

      m_allHashes[i] = tmpBuff;
      CAMsg::printMsg(LOG_DEBUG,"Price certificate hash of Mix %u is: %s\n", i, m_allHashes[i]);
      //do not delete tmpBuff here, since we're using allHashes below

      if (getDOMChildByName(mixNodes[i],"SubjectKeyIdentifier",skiNode,true) != E_SUCCESS)
        {
          CAMsg::printMsg(LOG_CRIT,"Could not get mix id from price cert");
        }

      allSkis[i] =  (UINT8*) XMLString::transcode(skiNode->getFirstChild()->getNodeValue());

  }
  //concatenate the hashes, and store for future reference to identify the cascade
    m_currentCascade = new UINT8[256];
    memset(m_currentCascade, 0, (sizeof(UINT8)*256 ));
    for (UINT32 j = 0; j < nrOfMixes; j++)
    {
        //check for hash value size (should always be OK)
        if (strlen((const char*)m_currentCascade) > ( 256 - strlen((const char*)m_allHashes[j]) )   )
        {
          CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance::prepareCCRequest: "
              "Too many/too long hash values, ran out of allocated memory\n");
          return E_UNKNOWN;
        }
        if (j == 0)
        {
            m_currentCascade = (UINT8*) strcpy( (char*) m_currentCascade,(const char*)m_allHashes[j]);
        } else
        {
            m_currentCascade = (UINT8*) strcat((char*)m_currentCascade,(char*)m_allHashes[j]);
        }
    }

    //and append to CC
  DOMElement* elemPriceCerts = createDOMElement(m_preparedCCRequest,"PriceCertificates");
  DOMElement* elemCert=NULL;
  for (UINT32 i = 0; i < nrOfMixes; i++)
  {
    elemCert = createDOMElement(m_preparedCCRequest,"PriceCertHash");
    //CAMsg::printMsg(LOG_DEBUG,"hash to be inserted in cc: index %d, value %s\n",i,m_allHashes[i]);
    setDOMElementValue(elemCert,m_allHashes[i]);
    //delete[] allHashes[i];
    setDOMElementAttribute(elemCert,"id",allSkis[i]);
    setDOMElementAttribute(elemCert, "position", i);
    if (i == 0)
    {
      setDOMElementAttribute(elemCert,"isAI",(UINT8*)"true");
    }
    elemPriceCerts->appendChild(elemCert);
  }
  elemCC->appendChild(elemPriceCerts);
#ifdef DEBUG
  CAMsg::printMsg(LOG_DEBUG, "finished method makeCCRequest\n");
#endif

  delete[] mixNodes;
  mixNodes = NULL;
  //delete[] allHashes;
  delete[] allSkis;
  allSkis = NULL;
  return E_SUCCESS;

}

Here is the call graph for this function:

SINT32 CAAccountingInstance::processJapMessage ( fmHashTableEntry pHashEntry,
const XERCES_CPP_NAMESPACE::DOMDocument *  a_DomDoc 
) [static]

Handle a user (xml) message sent to us by the Jap through the ControlChannel.

Handle a user (xml) message sent to us by the Jap.

This function determines what type of message we have and sends the appropriate handle...() function to the ai thread.

This function is running inside the AiThread. It determines what type of message we have and calls the appropriate handle...() function

Todo:
this does not work yet due to errors in CAMutex!!! if (handleFunc) { pItem = new aiQueueItem; pItem->pDomDoc = new DOM_Document(a_DomDoc); pItem->pAccInfo = pHashEntry->pAccountingInfo; pItem->handleFunc = handleFunc;

pHashEntry->pAccountingInfo->mutex->lock(); pItem->pAccInfo->nrInQueue++; ret = ms_pInstance->m_aiThreadPool->addRequest(processThread, pItem); if (ret !=E_SUCCESS) { pItem->pAccInfo->nrInQueue--; CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: Process could not add to AI thread pool!\n" ); delete pItem->pDomDoc; delete pItem; } pHashEntry->pAccountingInfo->mutex->unlock(); return ret; }

Definition at line 1106 of file CAAccountingInstance.cpp.

References AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_INVALID_ACCOUNT, AUTH_LOGIN_SKIP_SETTLEMENT, t_accountinginfo::authFlags, BEGIN_STACK, E_SUCCESS, E_UNKNOWN, CAXMLErrorMessage::ERR_OK, FINISH_STACK, getPrepaidBytes(), handleAccountCertificate(), handleChallengeResponse(), handleCostConfirmation(), INIT_STACK, ms_pInstance, t_fmhashtableentry::pAccountingInfo, CAMsg::printMsg(), processJapMessageLoginHelper(), and SAVE_STACK.

Referenced by CAAccountingControlChannel::processXMLMessage().

  {
    INIT_STACK;
    BEGIN_STACK("CAAccountingInstance::processJapMessage");

    if (pHashEntry == NULL)
    {
      return E_UNKNOWN;
    }

    DOMElement* root = a_DomDoc->getDocumentElement();
    if(root == NULL)
    {
      CAMsg::printMsg(LOG_DEBUG, "ProcessJapMessage: getDocument Element is null!!!\n" );
      return E_UNKNOWN;
    }
    char* docElementName = XMLString::transcode(root->getTagName());
    SINT32 hf_ret = 0;


    // what type of message is it?
    if ( strcmp( docElementName, "AccountCertificate" ) == 0 )
      {
        #ifdef DEBUG
          CAMsg::printMsg( LOG_DEBUG, "Received an AccountCertificate. Calling handleAccountCertificate()\n" );
        #endif
        //handleFunc = &CAAccountingInstance::handleAccountCertificate;
        hf_ret = ms_pInstance->handleAccountCertificate( pHashEntry->pAccountingInfo, root );
        processJapMessageLoginHelper(pHashEntry, hf_ret, false);
      }
    else if ( strcmp( docElementName, "Response" ) == 0)
      {
        #ifdef DEBUG
          CAMsg::printMsg( LOG_DEBUG, "Received a Response (challenge-response)\n" );
        #endif
        //handleFunc = &CAAccountingInstance::handleChallengeResponse;
        hf_ret = ms_pInstance->handleChallengeResponse( pHashEntry->pAccountingInfo, root );
        processJapMessageLoginHelper(pHashEntry, hf_ret, false);
        /*if(hf_ret != CAXMLErrorMessage::ERR_OK)
        {
          unlockLogin(pHashEntry);
        }
        else*/
        if(hf_ret == (SINT32) CAXMLErrorMessage::ERR_OK)
        {
          //CAMsg::printMsg( LOG_DEBUG, "Prepaid bytes are: %d\n", getPrepaidBytes(pHashEntry->pAccountingInfo));

          if( (getPrepaidBytes(pHashEntry->pAccountingInfo) > 0) &&
            !(pHashEntry->pAccountingInfo->authFlags &
              (AUTH_ACCOUNT_EMPTY | AUTH_BLOCKED | AUTH_INVALID_ACCOUNT)) )
          {
            pHashEntry->pAccountingInfo->authFlags |= AUTH_LOGIN_SKIP_SETTLEMENT;
          }
        }
      }
    else if ( strcmp( docElementName, "CC" ) == 0 )
      {
        #ifdef DEBUG
          CAMsg::printMsg( LOG_DEBUG, "Received a CC. Calling handleCostConfirmation()\n" );
        #endif
        //handleFunc = &CAAccountingInstance::handleCostConfirmation;
        hf_ret = ms_pInstance->handleCostConfirmation( pHashEntry->pAccountingInfo, root );
        processJapMessageLoginHelper(pHashEntry, hf_ret, true);
        /*if(hf_ret != CAXMLErrorMessage::ERR_OK)
        {
          unlockLogin(pHashEntry);
        }*/
      }
    else
    {
      CAMsg::printMsg( LOG_ERR,
          "AI Received XML message with unknown root element \"%s\". This is not accepted!\n",
                      docElementName
                    );

      SAVE_STACK("CAAccountingInstance::processJapMessage", "error");
      XMLString::release(&docElementName);
      return E_UNKNOWN;
    }


    XMLString::release(&docElementName);

    // remove these lines if AI thread pool is used (see @todo above)
    //(ms_pInstance->*handleFunc)(pHashEntry->pAccountingInfo, root );

    FINISH_STACK("CAAccountingInstance::processJapMessage");
    return E_SUCCESS;
  }

Here is the call graph for this function:

void CAAccountingInstance::processJapMessageLoginHelper ( fmHashTableEntry pHashEntry,
UINT32  handlerReturnvalue,
bool  finishLogin 
) [static, private]

Definition at line 1218 of file CAAccountingInstance.cpp.

References AUTH_FAKE, AUTH_LOGIN_FAILED, AUTH_LOGIN_NOT_FINISHED, AUTH_MULTIPLE_LOGIN, t_accountinginfo::authFlags, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_MULTIPLE_LOGIN, CAXMLErrorMessage::ERR_OK, CAMutex::lock(), t_accountinginfo::mutex, t_fmhashtableentry::pAccountingInfo, t_accountinginfo::pControlChannel, CAAbstractControlChannel::sendXMLMessage(), CAAbstractXMLEncodable::toXmlDocument(), and CAMutex::unlock().

Referenced by processJapMessage().

{
  if(pHashEntry->pAccountingInfo != NULL)
  {
    if(pHashEntry->pAccountingInfo->mutex == NULL)
    {
      return;
    }
    pHashEntry->pAccountingInfo->mutex->lock();
    if(pHashEntry->pAccountingInfo->authFlags & AUTH_LOGIN_NOT_FINISHED)
    {
      if(handlerReturnValue != CAXMLErrorMessage::ERR_OK)
      {
        pHashEntry->pAccountingInfo->authFlags &= ~AUTH_LOGIN_NOT_FINISHED;
        pHashEntry->pAccountingInfo->authFlags |= AUTH_LOGIN_FAILED;

        CAXMLErrorMessage *err = NULL;
        XERCES_CPP_NAMESPACE::DOMDocument *errDoc = NULL;

        /*if(pHashEntry->pAccountingInfo->authFlags & AUTH_BLOCKED )
        {
          err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_BLOCKED,
                        (UINT8 *) "AI login: access denied because your account is blocked");
        }
        else if(pHashEntry->pAccountingInfo->authFlags & AUTH_ACCOUNT_EMPTY )
        {
          err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_ACCOUNT_EMPTY,
                        (UINT8 *) "AI login: access denied because your account is empty");
        }
        else if(pHashEntry->pAccountingInfo->authFlags & AUTH_INVALID_ACCOUNT )
        {
          err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_NO_BALANCE,
                        (UINT8 *) "AI login: access denied because your account is not valid");
        }
        else */ if(pHashEntry->pAccountingInfo->authFlags & AUTH_FAKE )
        {
          err = new CAXMLErrorMessage(handlerReturnValue);

        }
        else if(pHashEntry->pAccountingInfo->authFlags & AUTH_MULTIPLE_LOGIN )
        {
          err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_MULTIPLE_LOGIN,
                        (UINT8*)"You are already logged in.");

        }
        else
        {
          err = new CAXMLErrorMessage(CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR,
                        (UINT8 *) "AI login: error occured while connecting, access denied");
        }


        if(err != NULL)
        {
          err->toXmlDocument(errDoc);
          pHashEntry->pAccountingInfo->pControlChannel->sendXMLMessage(errDoc);
          delete err;
          err = NULL;
        }
        if(errDoc != NULL)
        {
          errDoc->release();
          errDoc = NULL;
        }
        /*sendAILoginConfirmation(pHashEntry->pAccountingInfo,
                    CAXMLErrorMessage::ERR_BLOCKED,
                    (UINT8*) "AI access denied");*/
      }
      else if(lastLoginMessage)
      {
        //CAMsg::printMsg( LOG_ERR, "User successfully logged in\n");
        pHashEntry->pAccountingInfo->authFlags &= ~AUTH_LOGIN_NOT_FINISHED;
      }

    }
    pHashEntry->pAccountingInfo->mutex->unlock();
  }
}

Here is the call graph for this function:

THREAD_RETURN CAAccountingInstance::processThread ( void *  a_param) [static, private]

The main loop of the AI thread - reads messages from the queue and starts process threads for these messages.

Processes JAP messages asynchronously by calls to the appropriate handlers.

Definition at line 266 of file CAAccountingInstance.cpp.

References AUTH_DELETE_ENTRY, t_accountinginfo::authFlags, BEGIN_STACK, FINISH_STACK, CAAccountingInstance::t_aiqueueitem::handleFunc, INIT_STACK, CAMutex::lock(), ms_pInstance, t_accountinginfo::mutex, t_accountinginfo::nrInQueue, CAAccountingInstance::t_aiqueueitem::pAccInfo, CAAccountingInstance::t_aiqueueitem::pDomDoc, CAMsg::printMsg(), THREAD_RETURN_SUCCESS, and CAMutex::unlock().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::processThread");

  aiQueueItem* item = (aiQueueItem*)a_param;
  bool bDelete = false;
  DOMElement *elem = item->pDomDoc->getDocumentElement();

  // call the handle function
  (ms_pInstance->*(item->handleFunc))(item->pAccInfo, elem);

  item->pAccInfo->mutex->lock();
  item->pAccInfo->nrInQueue--;
  if (item->pAccInfo->authFlags & AUTH_DELETE_ENTRY &&
    item->pAccInfo->nrInQueue == 0)
  {
    /*
     * There is no more entry of this connection in the queue,
     * and the connection is closed. We have to delete the entry.
     */
    bDelete = true;
    CAMsg::printMsg(LOG_INFO, "CAAccountingInstance: Deleting account entry from AI thread.\n");
  }

  if (item->pAccInfo->nrInQueue < 0)
  {
    CAMsg::printMsg(LOG_CRIT, "CAAccountingInstance: AI thread found negative handle queue!\n");
  }
  item->pAccInfo->mutex->unlock();

  if (bDelete)
  {
    delete item->pAccInfo->mutex;
    item->pAccInfo->mutex = NULL;
    delete item->pAccInfo;
    item->pAccInfo = NULL;
  }

  delete item->pDomDoc;
  item->pDomDoc = NULL;
  delete item;
  item = NULL;

  FINISH_STACK("CAAccountingInstance::processThread");

  THREAD_RETURN_SUCCESS;
}

Here is the call graph for this function:

When receiving this message, the Mix should kick the user out immediately.

Definition at line 714 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, HANDLE_PACKET_CLOSE_CONNECTION, t_accountinginfo::mutex, print64(), CAMsg::printMsg(), setPrepaidBytesToZero_internal(), and CAMutex::unlock().

Referenced by handleJapPacket_internal().

{
  UINT8 tmp[32];
  print64(tmp,pAccInfo->accountNumber);
  CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: should kick out user with account %s now...\n", tmp);
  setPrepaidBytesToZero_internal(pAccInfo);
  pAccInfo->mutex->unlock();
  return HANDLE_PACKET_CLOSE_CONNECTION;
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::returnPrepareKickout ( tAiAccountingInfo pAccInfo,
CAXMLErrorMessage a_error 
) [static, private]

Definition at line 744 of file CAAccountingInstance.cpp.

References AUTH_FATAL_ERROR, t_accountinginfo::authFlags, HANDLE_PACKET_PREPARE_FOR_CLOSING_CONNECTION, t_accountinginfo::mutex, t_accountinginfo::pControlChannel, CAMsg::printMsg(), CAAbstractControlChannel::sendXMLMessage(), CAAbstractXMLEncodable::toXmlDocument(), and CAMutex::unlock().

Referenced by handleJapPacket_internal().

{
  pAccInfo->authFlags |= AUTH_FATAL_ERROR;

  if (a_error)
  {
    //CAMsg::printMsg(LOG_CRIT, "AccountingInstance: Sending error message...\n");
    XERCES_CPP_NAMESPACE::DOMDocument* doc=NULL;
    a_error->toXmlDocument(doc);
    delete a_error;
    a_error = NULL;
    //pAccInfo->sessionPackets = 0; // allow some pakets to pass by to send the control message
    CAMsg::printMsg(LOG_DEBUG, "CAAccountingInstance: before prepare Kickout send...\n");
    pAccInfo->pControlChannel->sendXMLMessage(doc);
    if (doc != NULL)
    {
      doc->release();
      doc = NULL;
    }
  }
  else
  {
    CAMsg::printMsg(LOG_CRIT, "AccountingInstance: Should send error message, but none is available!\n");
  }

  pAccInfo->mutex->unlock();
  return HANDLE_PACKET_PREPARE_FOR_CLOSING_CONNECTION;
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::sendAILoginConfirmation ( tAiAccountingInfo pAccInfo,
const UINT32  code,
UINT8 message 
) [static, private]

Definition at line 1071 of file CAAccountingInstance.cpp.

References createDOMDocument(), createDOMElement(), DOM_Output::dumpToMem(), E_SUCCESS, t_accountinginfo::pControlChannel, CAMsg::printMsg(), CAAbstractControlChannel::sendXMLMessage(), setDOMElementAttribute(), and setDOMElementValue().

Referenced by finishLoginProcess().

  {
    SINT32 sendSuccess = E_SUCCESS;
    XERCES_CPP_NAMESPACE::DOMDocument* doc = createDOMDocument();
    DOMElement *elemRoot = createDOMElement(doc, "LoginConfirmation");
    setDOMElementAttribute(elemRoot, "code", code);
    setDOMElementValue(elemRoot, message);
    doc->appendChild(elemRoot);

#ifdef DEBUG
    UINT32 debuglen = 3000;
    UINT8 debugout[3000];
    DOM_Output::dumpToMem(doc,debugout,&debuglen);
    debugout[debuglen] = 0;
    CAMsg::printMsg(LOG_DEBUG, "the AILogin Confirmation sent looks like this: %s \n",debugout);
#endif
    sendSuccess = pAccInfo->pControlChannel->sendXMLMessage(doc);
    if (doc != NULL)
    {
      doc->release();
      doc = NULL;
    }
    return sendSuccess;
  }

Here is the call graph for this function:

Definition at line 799 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_SENT_CC_REQUEST, t_accountinginfo::authFlags, BEGIN_STACK, t_accountinginfo::bytesToConfirm, DOM_Output::dumpToMem(), E_SUCCESS, CALibProxytest::getOptions(), CACmdLnOptions::getPrepaidInterval(), INIT_STACK, makeCCRequest(), t_accountinginfo::pControlChannel, CAMsg::printMsg(), CAAbstractControlChannel::sendXMLMessage(), and t_accountinginfo::transferredBytes.

Referenced by handleChallengeResponse_internal(), and handleJapPacket_internal().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::sendCCRequest");

  XERCES_CPP_NAMESPACE::DOMDocument* doc=NULL;
    UINT32 prepaidInterval = CALibProxytest::getOptions()->getPrepaidInterval();

    pAccInfo->authFlags |= AUTH_SENT_CC_REQUEST;

    if (pAccInfo->authFlags & AUTH_ACCOUNT_EMPTY)
    {
      // do not send further CC requests for this account
      return E_SUCCESS;
    }

    // prepaid bytes are "confirmed bytes - transfered bytes"
    //UINT64 bytesToConfirm = pAccInfo->confirmedBytes + (prepaidInterval) - (pAccInfo->confirmedBytes - pAccInfo->transferredBytes);
    pAccInfo->bytesToConfirm = (prepaidInterval) + pAccInfo->transferredBytes;
  makeCCRequest(pAccInfo->accountNumber, pAccInfo->bytesToConfirm, doc);
  //pAccInfo->authFlags |= AUTH_SENT_CC_REQUEST;
#ifdef DEBUG
  CAMsg::printMsg(LOG_DEBUG, "CC request sent for %llu bytes, transferrred bytes: %llu bytes.\n",pAccInfo->bytesToConfirm, pAccInfo->transferredBytes);
  CAMsg::printMsg(LOG_DEBUG, "prepaid Interval: %u \n",prepaidInterval);

  UINT32 debuglen = 3000;
  UINT8 debugout[3000];
  DOM_Output::dumpToMem(doc,debugout,&debuglen);
  debugout[debuglen] = 0;
  CAMsg::printMsg(LOG_DEBUG, "the CC sent looks like this: %s \n",debugout);
#endif

  //FINISH_STACK("CAAccountingInstance::sendCCRequest");

  SINT32 ret = pAccInfo->pControlChannel->sendXMLMessage(doc);
  if (doc != NULL)
  {
    doc->release();
    doc = NULL;
  }
  return ret;
}

Here is the call graph for this function:

SINT32 CAAccountingInstance::sendInitialCCRequest ( tAiAccountingInfo pAccInfo,
CAXMLCostConfirmation pCC,
SINT32  prepaidBytes 
) [static, private]

Definition at line 773 of file CAAccountingInstance.cpp.

References DOM_Output::dumpToMem(), E_SUCCESS, E_UNKNOWN, makeInitialCCRequest(), t_accountinginfo::pControlChannel, CAMsg::printMsg(), and CAAbstractControlChannel::sendXMLMessage().

Referenced by handleChallengeResponse_internal().

{
  XERCES_CPP_NAMESPACE::DOMDocument* doc=NULL;

  SINT32 ret = makeInitialCCRequest(pCC, doc, prepaidBytes);
  if( (ret != E_SUCCESS) || (doc == NULL))
  {
    CAMsg::printMsg(LOG_ERR, "cannot send initial CC request, ret: %d\n", ret);
    return E_UNKNOWN;
  }
#ifdef DEBUG
  UINT32 debuglen = 3000;
  UINT8 debugout[3000];
  DOM_Output::dumpToMem(doc,debugout,&debuglen);
  debugout[debuglen] = 0;
  CAMsg::printMsg(LOG_DEBUG, "the CC sent looks like this: %s \n",debugout);
#endif
  ret = pAccInfo->pControlChannel->sendXMLMessage(doc);
  if (doc != NULL)
  {
    doc->release();
    doc = NULL;
  }
  return ret;
}

Here is the call graph for this function:

void CAAccountingInstance::setPrepaidBytesToZero ( tAiAccountingInfo pAccInfo) [static, private]

Definition at line 724 of file CAAccountingInstance.cpp.

References CAMutex::lock(), t_accountinginfo::mutex, setPrepaidBytesToZero_internal(), and CAMutex::unlock().

{
  pAccInfo->mutex->lock();
  setPrepaidBytesToZero_internal(pAccInfo);
  pAccInfo->mutex->unlock();
}

Here is the call graph for this function:

void CAAccountingInstance::setPrepaidBytesToZero_internal ( tAiAccountingInfo pAccInfo) [inline, static, private]

Definition at line 3290 of file CAAccountingInstance.cpp.

References SettleEntry::accountNumber, AUTH_ACCOUNT_EMPTY, AUTH_BLOCKED, AUTH_INVALID_ACCOUNT, AUTH_OUTDATED_CC, AUTH_SETTLED_ONCE, AUTH_UNKNOWN, AUTH_WAITING_FOR_FIRST_SETTLED_CC, AccountLoginHashEntry::authFlags, SettleEntry::authFlags, AccountLoginHashEntry::authRemoveFlags, SettleEntry::authRemoveFlags, BEGIN_STACK, CAAccountingDBInterface::clearAccountStatus(), AccountLoginHashEntry::confirmedBytes, SettleEntry::confirmedBytes, CRITICAL_SUBSEQUENT_BI_CONN_ERRORS, CAAccountingDBInterface::deleteCC(), SettleEntry::diffBytes, E_NOT_CONNECTED, E_SUCCESS, E_UNKNOWN, CAXMLErrorMessage::ERR_ACCOUNT_EMPTY, CAXMLErrorMessage::ERR_BLOCKED, CAXMLErrorMessage::ERR_DATABASE_ERROR, CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR, CAXMLErrorMessage::ERR_KEY_NOT_FOUND, CAXMLErrorMessage::ERR_OK, CAXMLErrorMessage::ERR_OUTDATED_CC, CAXMLErrorMessage::ERR_SUCCESS_BUT_WITH_ERRORS, FINISH_STACK, CAXMLCostConfirmation::getAccountNumber(), CAAccountingDBInterface::getAccountStatus(), CAAccountingDBInterface::getConnection(), CAXMLErrorMessage::getDescription(), CAXMLErrorMessage::getErrorCode(), CAXMLErrorMessage::getMessageObject(), Hashtable::getMutex(), CAAccountingDBInterface::getPrepaidAmount(), CAXMLCostConfirmation::getTransferredBytes(), CAAccountingDBInterface::getUnsettledCostConfirmations(), Hashtable::getValue(), INIT_STACK, CAAccountingBIInterface::initBIConnection(), CAMutex::lock(), m_currentAccountsHashtable, m_currentCascade, m_nextSettleNr, m_pPiInterface, m_pSettlementMutex, m_seqBIConnErrors, m_settleWaitNr, CAAccountingDBInterface::markAsSettled(), MAX_SETTLED_CCS, MONITORING_FIRE_PAY_EVENT, ms_pInstance, SettleEntry::nextEntry, print64(), CAMsg::printMsg(), CAAccountingDBInterface::releaseConnection(), SAVE_STACK, CAAccountingBIInterface::settle(), CAConditionVariable::signal(), CAAccountingDBInterface::storeAccountStatus(), CAAccountingDBInterface::storeCostConfirmation(), SettleEntry::storedStatus, CAAccountingDBInterface::storePrepaidAmount(), CAAccountingBIInterface::terminateBIConnection(), CAMutex::unlock(), and CAConditionVariable::wait().

{
  INIT_STACK;
  BEGIN_STACK("CAAccountingInstance::settlementTransaction");

  SINT32 ret = 0;
  CAXMLErrorMessage * pErrMsg = NULL;
  CAXMLCostConfirmation * pCC = NULL;
  //CAQueue q;
  CAXMLCostConfirmation **allUnsettledCCs = NULL;
  UINT32 /*size, qSize, */ nrOfCCs, i;
  SettleEntry *entry = NULL, *nextEntry = NULL;

  CAAccountingDBInterface *dbInterface = NULL;

  /* This should never happen */
  if(ms_pInstance == NULL)
  {
    FINISH_STACK("CAAccountingInstance::settlementTransaction");
    return E_UNKNOWN;
  }
  /* This should never happen */
  if(ms_pInstance->m_pSettlementMutex == NULL)
  {
    FINISH_STACK("CAAccountingInstance::settlementTransaction");
    return E_UNKNOWN;
  }
  //sleep(5);
  dbInterface = CAAccountingDBInterface::getConnection();
  if(dbInterface == NULL)
  {
    CAMsg::printMsg(LOG_ERR, "Settlement transaction: could not connect to Database. Retry later...\n");
    //MONITORING_FIRE_PAY_EVENT(ev_pay_dbConnectionFailure);
    FINISH_STACK("CAAccountingInstance::settlementTransaction");
    return E_NOT_CONNECTED;
  }
  //MONITORING_FIRE_PAY_EVENT(ev_pay_dbConnectionSuccess);
  ms_pInstance->m_pSettlementMutex->lock();
  /* First part get unsettled CCs from the AI database */
  #ifdef DEBUG
  CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: DB connections established!\n");
  #endif

  dbInterface->getUnsettledCostConfirmations(&allUnsettledCCs, ms_pInstance->m_currentCascade, &nrOfCCs, MAX_SETTLED_CCS);
  if (nrOfCCs <= 0)
  {
    CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: finished gettings CCs, found no CCs to settle\n");
    if(dbInterface != NULL)
    {
      CAAccountingDBInterface::releaseConnection(dbInterface);
      dbInterface = NULL;
    }
    ms_pInstance->m_pSettlementMutex->unlock();
    FINISH_STACK("CAAccountingInstance::settlementTransaction");
    return E_SUCCESS;
  }
  //qSize = q.getSize();
  //nrOfCCs = qSize / sizeof(pCC);
  CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: finished gettings CCs, found %u cost confirmations to settle\n",nrOfCCs);

  SAVE_STACK("CAAccountingInstance::settlementTransaction", "After getting unsettled CCs");
  /* Second part: We found unsettled CCs. Now contact the Payment Instance to settle them */

  //while(!q.isEmpty())
  for(i = 0; i < nrOfCCs; i++)
  {
    // get the next CC from the queue
    /*size = sizeof(pCC);
    ret = q.get((UINT8*)(&pCC), &size);
    if(ret != E_SUCCESS)
    {
      CAMsg::printMsg(LOG_ERR, "Settlement transaction: could not get next item from queue\n");
      q.clean();
      if(dbInterface != NULL)
      {
        CAAccountingDBInterface::releaseConnection(dbInterface);
        dbInterface = NULL;
      }
      ms_pInstance->m_pSettlementMutex->unlock();
      FINISH_STACK("CAAccountingInstance::settlementTransaction");
      return ret;
      //break;
    }*/
    pCC = allUnsettledCCs[i];
    entry = NULL;
    nextEntry = NULL;
    if (!pCC)
    {
      CAMsg::printMsg(LOG_CRIT, "Settlement transaction: Cost confirmation is NULL!\n");
      continue;
    }

#ifdef DEBUG
    CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: Connecting to payment instance...\n");
#endif
    ret = ms_pInstance->m_pPiInterface->initBIConnection();
    if(ret != E_SUCCESS)
    {
      ms_pInstance->m_seqBIConnErrors++;
      if(ms_pInstance->m_seqBIConnErrors >= CRITICAL_SUBSEQUENT_BI_CONN_ERRORS)
      {
        //transition to critical BI conn payment state
        MONITORING_FIRE_PAY_EVENT(ev_pay_biConnectionCriticalSubseqFailures);
      }
      else
      {
        MONITORING_FIRE_PAY_EVENT(ev_pay_biConnectionFailure);
      }
      CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: could not connect to BI. Try later...\n");
      //q.clean();
      pErrMsg = NULL; // continue in order to tell AUTH_WAITING_FOR_FIRST_SETTLED_CC for all accounts
      ms_pInstance->m_pPiInterface->terminateBIConnection(); // make sure the socket is closed
    }
    else
    {
      MONITORING_FIRE_PAY_EVENT(ev_pay_biConnectionSuccess);
#ifdef DEBUG
      CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: successfully connected to payment instance");
#endif
      ms_pInstance->m_seqBIConnErrors = 0;
      pErrMsg = ms_pInstance->m_pPiInterface->settle( *pCC );
      ms_pInstance->m_pPiInterface->terminateBIConnection();
      CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: settle done!\n");
    }

    bool bDeleteCC = false;
    UINT32 authFlags = 0;
    UINT32 authRemoveFlags = 0;
    UINT64 confirmedBytes = 0;
    UINT64 diffBytes = 0;
    UINT32 storedStatus = 0;

    dbInterface->getAccountStatus(pCC->getAccountNumber(), storedStatus);

    // check returncode
    if(pErrMsg == NULL)  //no returncode -> connection error
    {
      authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC; // no fault of the client
      CAMsg::printMsg(LOG_ERR, "Settlement transaction: Communication with BI failed!\n");
    }
    else if(pErrMsg->getErrorCode() != pErrMsg->ERR_OK)  //BI reported error
    {
      CAMsg::printMsg(LOG_WARNING, "Settlement transaction: BI reported error no. %d (%s)\n",
        pErrMsg->getErrorCode(), pErrMsg->getDescription() );
      if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_KEY_NOT_FOUND)
      {
        authFlags |= AUTH_INVALID_ACCOUNT;
        //dbConn.storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_KEY_NOT_FOUND);
        bDeleteCC = true;
      }
      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_ACCOUNT_EMPTY)
      {
        authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
        authFlags |= AUTH_ACCOUNT_EMPTY;
        UINT64* msgConfirmedBytes = (UINT64*)pErrMsg->getMessageObject();
        if (msgConfirmedBytes)
        {
          confirmedBytes = *msgConfirmedBytes;
          if (confirmedBytes < pCC->getTransferredBytes())
          {
            diffBytes = pCC->getTransferredBytes() - confirmedBytes;
          }
          UINT8 tmp[32];
          print64(tmp, confirmedBytes);
          UINT8 tmp2[32];
          print64(tmp2, diffBytes);
          UINT8 tmp3[32];
          print64(tmp3, pCC->getTransferredBytes());
          CAMsg::printMsg(LOG_INFO, "Settlement transaction: Received %s confirmed bytes and %s diff bytes while having %s transferred bytes!\n", tmp, tmp2, tmp3);
        }
        else
        {
          CAMsg::printMsg(LOG_ERR, "Settlement transaction: Account empty, but no message object received! "
              "This may lead to too much prepaid bytes!\n");
        }

        dbInterface->storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_ACCOUNT_EMPTY);
        authFlags |= AUTH_SETTLED_ONCE;
        dbInterface->markAsSettled(pCC->getAccountNumber(), ms_pInstance->m_currentCascade,
                      pCC->getTransferredBytes());
//#ifdef DEBUG
        CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: settling %llu bytes for account %llu\n",
                      pCC->getTransferredBytes(), pCC->getAccountNumber());
//#endif
      }
      /*
      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_INVALID_PRICE_CERT)
      {
        // this should never happen; the price certs in this CC do not fit to the ones of the cascade
        // bDeleteCC = true;
      }*/
      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_OUTDATED_CC)
      {
        authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC; // this is a Mix not a client error

        //get attached CC from error message
        CAXMLCostConfirmation* attachedCC = (CAXMLCostConfirmation*) pErrMsg->getMessageObject();
        if (attachedCC)
        {
          authFlags |= AUTH_OUTDATED_CC;
          CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: tried outdated CC, received last valid CC back\n");
          //store it in DB
          if (dbInterface->storeCostConfirmation(*attachedCC,
              ms_pInstance->m_currentCascade) == E_SUCCESS)
          {
            authFlags |= AUTH_SETTLED_ONCE;
            if (dbInterface->markAsSettled(attachedCC->getAccountNumber(), ms_pInstance->m_currentCascade,
                        attachedCC->getTransferredBytes()) != E_SUCCESS)
            {
              CAMsg::printMsg(LOG_ERR, "Settlement transaction: Could not mark last valid CC as settled."
                "Maybe a new CC has been added meanwhile?\n");
            }
          }
          else
          {
            CAMsg::printMsg(LOG_ERR, "Settlement transaction: storing last valid CC in db failed!\n");
          }
          // set the confirmed bytes to the value of the CC got from the PI
          confirmedBytes = attachedCC->getTransferredBytes();
        }
        else
        {
          CAMsg::printMsg(LOG_INFO, "Settlement transaction: Did not receive last valid CC - maybe old Payment instance?\n");
        }
      }
      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_BLOCKED)
      {
        authFlags |= AUTH_BLOCKED;
        bDeleteCC = true;

        dbInterface->storeAccountStatus(pCC->getAccountNumber(), CAXMLErrorMessage::ERR_BLOCKED);
      }

      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_DATABASE_ERROR)
      {
        //authFlags |= AUTH_DATABASE;
        // the user is not responsible for this!
        authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
      }
      else if (pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_INTERNAL_SERVER_ERROR ||
             pErrMsg->getErrorCode() == CAXMLErrorMessage::ERR_SUCCESS_BUT_WITH_ERRORS)
      {
        // kick out the user and store the CC
        authFlags |= AUTH_UNKNOWN;
      }
      else
      {
        // an unknown error leads to user kickout
        CAMsg::printMsg(LOG_DEBUG, "Settlement transaction: Setting unknown kickout error no. %d.\n", pErrMsg->getErrorCode());
        authFlags |= AUTH_UNKNOWN;
        bDeleteCC = true;
      }

      if (bDeleteCC)
      {
        //delete costconfirmation to avoid trying to settle an unusable CC again and again
        if(dbInterface->deleteCC(pCC->getAccountNumber(), ms_pInstance->m_currentCascade) == E_SUCCESS)
        {
          CAMsg::printMsg(LOG_ERR, "Settlement transaction: unusable cost confirmation was deleted\n");
        }
        else
        {
          CAMsg::printMsg(LOG_ERR, "Settlement transaction: cost confirmation is unusable, but could not delete it from database\n");
        }
      }
    }
    else //settling was OK, so mark account as settled
    {
      authRemoveFlags |= AUTH_WAITING_FOR_FIRST_SETTLED_CC;
      authFlags |= AUTH_SETTLED_ONCE;
      if (dbInterface->markAsSettled(pCC->getAccountNumber(),
                  ms_pInstance->m_currentCascade,
                  pCC->getTransferredBytes()) != E_SUCCESS)
       {
        CAMsg::printMsg(LOG_ERR, "Settlement transaction: Could not mark CC as settled. Maybe a new CC has been added meanwhile?\n");
       }
      CAMsg::printMsg(LOG_INFO, "Settlement transaction: CC OK!\n");
    }


    if (authFlags || authRemoveFlags)
    {
      nextEntry = new SettleEntry;
      nextEntry->accountNumber = pCC->getAccountNumber();
      nextEntry->authFlags = authFlags;
      nextEntry->authRemoveFlags = authRemoveFlags;
      nextEntry->confirmedBytes = confirmedBytes;
      nextEntry->diffBytes = diffBytes;
      nextEntry->nextEntry = entry;
      nextEntry->storedStatus = storedStatus;
      entry = nextEntry;
    }

    /*if (pCC != NULL)
    {
      delete pCC;
      pCC = NULL;
    }*/
    if (pErrMsg != NULL)
    {
      delete pErrMsg;
      pErrMsg = NULL;
    }
  }

  if(allUnsettledCCs != NULL)
  {
    for(i = 0; i < nrOfCCs; i++)
    {
      delete allUnsettledCCs[i];
      allUnsettledCCs[i] = NULL;
    }
    delete [] allUnsettledCCs;
    allUnsettledCCs = NULL;
  }

  SAVE_STACK("CAAccountingInstance::settlementTransaction", "After settling unsettled CCs with BI");

  SettleEntry *first = entry;
  UINT64 myWaitNr = 0;
  if (entry)
  {
    //wait numbers are used to obtain a synchronisation with FCFS assertion.
    //This additional synchronisation is necessary because before acquiring the login hashtable
    //locks the settlementMutex should be released. (Nested locking should be avoided).
    if(ms_pInstance->m_nextSettleNr == ms_pInstance->m_settleWaitNr)
    {
      //CAMsg::printMsg(LOG_INFO, "Thread %x: resetting the wait numbers.\n", pthread_self() );
      //no one is waiting, we use this occasion to reset the wait numbers
      ms_pInstance->m_nextSettleNr = 0;
      ms_pInstance->m_settleWaitNr = 1;
    }
    else
    {
      SAVE_STACK("CAAccountingInstance::settlementTransaction", "wait for altering hashtable");
      //get global wait number and wait but release the DBConnection first.
      CAAccountingDBInterface::releaseConnection(dbInterface);
      dbInterface = NULL;
      myWaitNr = ms_pInstance->m_settleWaitNr;
      ms_pInstance->m_settleWaitNr++;
      while(myWaitNr != ms_pInstance->m_nextSettleNr)
      {
        CAMsg::printMsg(LOG_INFO, "Thread %x must wait to alter login table after settling (2): %llu before him in the queue\n", pthread_self(),
                    (myWaitNr - ms_pInstance->m_nextSettleNr));
        ms_pInstance->m_pSettlementMutex->wait();
      }
      CAMsg::printMsg(LOG_INFO, "Thread %x may continue (2).\n", pthread_self());
      dbInterface = CAAccountingDBInterface::getConnection();
    }
    SAVE_STACK("CAAccountingInstance::settlementTransaction", "altering DB entries");
    while (entry != NULL && dbInterface != NULL)
    {
      if (entry->authFlags & (AUTH_INVALID_ACCOUNT | AUTH_UNKNOWN))
      {
        dbInterface->storePrepaidAmount(
            entry->accountNumber, 0, ms_pInstance->m_currentCascade);
      }
      else if (entry->diffBytes)
      {
        // user is currently not logged in; set correct prepaid bytes in DB
        SINT32 prepaidBytes =
          dbInterface->getPrepaidAmount(entry->accountNumber,
              ms_pInstance->m_currentCascade, true);
        if (prepaidBytes > 0)
        {
          if (entry->diffBytes < 0 || entry->diffBytes >= (UINT64)prepaidBytes)
          {
            prepaidBytes = 0;
          }
          else
          {
            prepaidBytes -= entry->diffBytes;
          }
          dbInterface->storePrepaidAmount(
            entry->accountNumber, prepaidBytes, ms_pInstance->m_currentCascade);
        }
      }

      if( !(entry->authFlags & (AUTH_ACCOUNT_EMPTY | AUTH_BLOCKED | AUTH_INVALID_ACCOUNT)) &&
        entry->storedStatus != 0)
      {
        dbInterface->clearAccountStatus(entry->accountNumber);
        switch (entry->storedStatus)
        {
          case CAXMLErrorMessage::ERR_ACCOUNT_EMPTY:
          {
            CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account empty status for %llu\n",
                entry->accountNumber);
            entry->authRemoveFlags |= AUTH_ACCOUNT_EMPTY;
            break;
          }
          case CAXMLErrorMessage::ERR_BLOCKED:
          {
            CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account blocked status for %llu\n",
                entry->accountNumber);
            entry->authRemoveFlags |= AUTH_BLOCKED;
            break;
          }
          case CAXMLErrorMessage::ERR_KEY_NOT_FOUND:
          {
            CAMsg::printMsg(LOG_INFO, "Settlement transaction: clearing account invalid status for %llu\n",
                entry->accountNumber);
            entry->authRemoveFlags |= AUTH_INVALID_ACCOUNT;
            break;
          }
        }
      }

      nextEntry = entry->nextEntry;
      entry = nextEntry;
    }
  }
  CAAccountingDBInterface::releaseConnection(dbInterface);
  dbInterface = NULL;
  ms_pInstance->m_pSettlementMutex->unlock();

  if(first != NULL)
  {
    SAVE_STACK("CAAccountingInstance::settlementTransaction", "altering hashtable");
#ifdef DEBUG
    CAMsg::printMsg(LOG_DEBUG, "Settlement thread with wait nr %llu alters hashtable.\n", myWaitNr);
#endif
    entry = first;
    ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
    while (entry != NULL)
    {
      AccountLoginHashEntry* loginEntry =
              (AccountLoginHashEntry*) (ms_pInstance->m_currentAccountsHashtable->getValue(&(entry->accountNumber)));
      if (loginEntry)
      {
        // the user is currently logged in
        loginEntry->authFlags |= entry->authFlags;
        loginEntry->authRemoveFlags |= entry->authRemoveFlags;
        if (entry->confirmedBytes > 0 &&
          loginEntry->confirmedBytes < entry->confirmedBytes)
        {
          loginEntry->confirmedBytes = entry->confirmedBytes;
        }
      }
      nextEntry = entry->nextEntry;
      delete entry;
      entry = nextEntry;
    }
    ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();

    ms_pInstance->m_pSettlementMutex->lock();
    ms_pInstance->m_nextSettleNr++;
// TODO: seems to be a bug; if this "if" is set, some locks are never released
//if(ms_pInstance->m_settleWaitNr != ms_pInstance->m_nextSettleNr)
    {
      SAVE_STACK("CAAccountingInstance::settlementTransaction", "waking up waiting threads for altering hashtable");
      //There are threads waiting for applying their settlement results.
      if(ms_pInstance->m_settleWaitNr != ms_pInstance->m_nextSettleNr)
      CAMsg::printMsg(LOG_INFO, "Thread %x Waking up next Thread %llu are waiting.\n", pthread_self(),
              (ms_pInstance->m_settleWaitNr - ms_pInstance->m_nextSettleNr));   
      ms_pInstance->m_pSettlementMutex->signal();
    }
    ms_pInstance->m_pSettlementMutex->unlock();
  }

  /*if(dbInterface != NULL)
  {
    CAAccountingDBInterface::releaseConnection(dbInterface);
    dbInterface = NULL;
  }*/
  FINISH_STACK("CAAccountingInstance::settlementTransaction");
  return E_SUCCESS;
}

Here is the call graph for this function:

UINT64 CAAccountingInstance::unlockLogin ( fmHashTableEntry ownerRef) [static, private]

release login (particularly for use in error case) this function is thread-safe.

Definition at line 3786 of file CAAccountingInstance.cpp.

References t_accountinginfo::accountNumber, Hashtable::getMutex(), Hashtable::getValue(), CAMutex::lock(), m_currentAccountsHashtable, ms_pInstance, t_fmhashtableentry::pAccountingInfo, resetLoginOngoing(), and CAMutex::unlock().

Referenced by CAFirstMix::doUserLogin_internal().

{
  if(ownerRef == NULL || ownerRef->pAccountingInfo == NULL)
  {
    return 0;
  }
  UINT64 accountNumber = ownerRef->pAccountingInfo->accountNumber;
  ms_pInstance->m_currentAccountsHashtable->getMutex()->lock();
  AccountLoginHashEntry *loginEntry = (AccountLoginHashEntry*)ms_pInstance->m_currentAccountsHashtable->getValue(&accountNumber);
  if (loginEntry != NULL)
  {
    resetLoginOngoing(loginEntry, ownerRef);
  }
  ms_pInstance->m_currentAccountsHashtable->getMutex()->unlock();
  return accountNumber;
}

Here is the call graph for this function:


Friends And Related Function Documentation

friend class CAFirstMix [friend]

Definition at line 115 of file CAAccountingInstance.hpp.

friend class CAFirstMixA [friend]

Definition at line 114 of file CAAccountingInstance.hpp.


Member Data Documentation

Definition at line 197 of file CAAccountingInstance.hpp.

Referenced by handleJapPacket_internal().

Definition at line 198 of file CAAccountingInstance.hpp.

the name of this accounting instance

Definition at line 289 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), and ~CAAccountingInstance().

reads messages from the queue and processes them

Definition at line 277 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), and ~CAAccountingInstance().

The hash values of the Mixes ordered beginning with the AI Mix.

Definition at line 297 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), prepareCCRequest(), and ~CAAccountingInstance().

Definition at line 331 of file CAAccountingInstance.hpp.

Definition at line 342 of file CAAccountingInstance.hpp.

Referenced by __newSettlementTransaction().

Definition at line 305 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), and handleJapPacket_internal().

Definition at line 304 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), and handleJapPacket_internal().

Users that get kicked out because they sent no authentication certificate get their IP appended to this list.

Connections from IP Addresses contained in this list get blocked, so that evil JAP users can't use the mix cascade without paying

Definition at line 313 of file CAAccountingInstance.hpp.

this is for synchronizing the write access to the HashEntries

Definition at line 280 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), getNrOfUsers(), and ~CAAccountingInstance().

the interface to the database

Definition at line 302 of file CAAccountingInstance.hpp.

Referenced by __newSettlementTransaction(), CAAccountingInstance(), settlementTransaction(), and ~CAAccountingInstance().

Definition at line 329 of file CAAccountingInstance.hpp.

XERCES_CPP_NAMESPACE::DOMDocument * CAAccountingInstance::m_preparedCCRequest [static, private]

Signature verifying instance for BI signatures.

Todo:
initialize this member

this thread sends cost confirmations to the BI in regular intervals

Definition at line 327 of file CAAccountingInstance.hpp.

Referenced by CAAccountingInstance(), handleCostConfirmation_internal(), and ~CAAccountingInstance().

Definition at line 272 of file CAAccountingInstance.hpp.

Definition at line 270 of file CAAccountingInstance.hpp.

Referenced by handleJapPacket_internal().


The documentation for this class was generated from the following files: