blob: 8141e176bad1517473878bb69390755601a538cf [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <rights/Ro.h>
#include <rights/Constraint.h>
#include <rights/OperationPermission.h>
#include <util/xml/DomExpatAgent.h>
#include <util/domcore/DOMString.h>
#include <utils/Log.h>
#include <uassert.h>
#include <time.h>
#include <ofstream.h>
using namespace ustl;
const char *STR_RO_RIGHTS = "o-ex:rights";
const char *STR_RO_CONTEXT = "o-ex:context";
const char *STR_RO_AGREEMENT = "o-ex:agreement";
const char *STR_RO_ASSET = "o-ex:asset";
const char *STR_RO_INHERIT = "o-ex:inherit";
const char *STR_RO_DIGEST = "o-ex:digest";
const char *STR_RO_KEYINFO = "ds:KeyInfo";
const char *STR_RO_PERMISSION = "o-ex:permission";
const char *STR_RO_ASSET_ID = "o-ex:id";
const char *STR_RO_ASSET_IDREF = "o-ex:idref";
const char *STR_RO_CONTEXT_ID = "o-dd:uid";
const char *STR_RO_CONTEXT_VERSION = "o-dd:version";
const char *STR_RO_DIGEST_VALUE = "ds:DigestValue";
const char *STR_RO_CIPHER_VALUE = "xenc:CipherValue";
const char *STR_RO_RETRIEVAL_METHOD = "ds:RetrievalMethod";
const char *STR_RO_PLAY = "o-dd:play";
const char *STR_RO_DISPLAY = "o-dd:display";
const char *STR_RO_EXECUTE = "o-dd:execute";
const char *STR_RO_PRINT = "o-dd:print";
const char *STR_RO_EXPORT = "o-dd:export";
const char *STR_RO_CONSTRAINT = "o-ex:constraint";
const char *STR_RO_COUNT = "o-dd:count";
const char *STR_RO_TIMEDCOUNT = "o-dd:timed-count";
const char *STR_RO_TIMER = "oma-dd:timer";
const char *STR_RO_INTERVAL = "o-dd:interval";
const char *STR_RO_DATETIME = "o-dd:datetime";
const char *STR_RO_START = "o-dd:start";
const char *STR_RO_END = "o-dd:end";
const char *STR_RO_ACCUMULATED = "o-dd:accumulated";
const char *STR_RO_INDIVIDUAL = "o-dd:individual";
const char *STR_RO_SYSTEM = "o-dd:system";
/** see Ro.h */
Ro::Ro()
{
mDoc = new XMLDocumentImpl();
mProperRight = NULL;
}
/** see Ro.h */
Ro::~Ro()
{
for (vector<Right*>::iterator itr = mRightList.begin(); itr != mRightList.end(); itr++)
{
delete(*itr);
}
mRightList.clear();
for (vector<Asset*>::iterator ita = mAssetList.begin(); ita != mAssetList.end(); ita++)
{
delete(*ita);
}
mAssetList.clear();
mProperRight = NULL;
delete mDoc;
}
/** see Ro.h */
void Ro::setRoID(string& id)
{
mRoID = id;
}
/** see Ro.h */
const string& Ro::getRoID() const
{
return mRoID;
}
/** see Ro.h */
void Ro::setRoVersion(string& version)
{
mRoVersion = version;
}
/** see Ro.h */
void Ro::addAsset(Asset* asset)
{
mAssetList.push_back(asset);
}
/** see Ro.h */
void Ro::addRight(Right* right)
{
mRightList.push_back(right);
}
/** see Ro.h */
bool Ro::save()
{
LOGI("==============Ro save.=================");
return true;
}
/** see Ro.h */
Ro::ERRCODE Ro::parse(istringstream *roStream)
{
DomExpatAgent xmlAgent(mDoc);
if (NULL == roStream)
{
LOGI("NULL stream");
return RO_NULL_STREAM;
}
if (xmlAgent.generateDocumentFromXML(roStream) == false)
{
LOGI("generate xml doc error");
return RO_ERR_BAD_XML;
}
handleDocument(mDoc);
return RO_OK;
}
/** see Ro.h */
bool Ro::handleDocument(const XMLDocumentImpl* doc)
{
assert(doc != NULL);
NodeImpl* node = doc->getDocumentElement();
return handleRights(node);
}
/** see Ro.h */
bool Ro::handleRights(const NodeImpl *curNode)
{
assert(curNode != NULL);
NodeImpl *node = curNode->getFirstChild();
while (NULL != node)
{
const DOMString* name;
name = static_cast<const XMLElementImpl*>(node)->getTagName();
if (name->compare(STR_RO_CONTEXT) == 0)
{
LOGI("rights context");
const DOMString *token = NULL;
token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_ID);
if (token)
{
LOGI(*token);
mRoID = *token;
}
token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_VERSION);
if (token)
{
LOGI(*token);
mRoVersion = *token;
}
}
if (name->compare(STR_RO_AGREEMENT) == 0)
{
LOGI("rights agreement");
if (handleAgreement(node) == false)
{
return false;
}
}
node = node->getNextSibling();
}
return true;
}
/** see Ro.h */
bool Ro::handleAgreement(const NodeImpl *curNode)
{
assert(curNode != NULL);
NodeImpl *node = curNode->getFirstChild();
while (NULL != node)
{
const DOMString* name;
name = static_cast<const XMLElementImpl*>(node)->getTagName();
if (name->compare(STR_RO_ASSET) == 0)
{
// do something about asset.
LOGI("asset");
if (handleAsset(node) == false)
{
return false;
}
}
if (name->compare(STR_RO_PERMISSION) == 0)
{
// do something about permission.
LOGI("permission");
if (handlePermission(node) == false)
{
return false;
}
}
node = node->getNextSibling();
}
return true;
}
/** see Ro.h */
bool Ro::handleAsset(const NodeImpl *curNode)
{
assert(curNode != NULL);
Asset *asset = new Asset();
const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
if (curElement->hasAttributes())
{
DOMString assetID(STR_RO_ASSET_ID);
LOGI("asset id:");
const DOMString *id = curElement->getAttribute(&assetID);
if (id)
{
asset->setID(*id);
}
}
NodeImpl* node = curNode->getFirstChild();
const DOMString *name = NULL;
const string *token = NULL;
while (NULL != node)
{
curElement = static_cast<const XMLElementImpl*>(node);
name = curElement->getTagName();
if (name->compare(STR_RO_CONTEXT) == 0 ||
name->compare(STR_RO_INHERIT) == 0)
{
LOGI("asset context");
token = curElement->getSoloText(STR_RO_CONTEXT_ID);
if (token)
{
LOGI(*token);
if (name->compare(STR_RO_CONTEXT) == 0)
{
asset->setContentID(*token);
}
else
{
//parent ID.
asset->setParentContentID(*token);
}
}
}
if (name->compare(STR_RO_DIGEST) == 0)
{
LOGI("asset digest");
//digest method is fixed value:
//http://www.w3.org/2000/09/xmldisig#sha1
token = curElement->getSoloText(STR_RO_DIGEST_VALUE);
if (token)
{
LOGI(*token);
asset->setDCFDigest(*token);
}
}
if (name->compare(STR_RO_KEYINFO) == 0)
{
LOGI("asset keyinfo");
token = curElement->getSoloText(STR_RO_CIPHER_VALUE);
if (token)
{
LOGI(*token);
asset->setEncryptedKey(*token);
}
const XMLElementImpl *node = curElement->getSoloElement(STR_RO_RETRIEVAL_METHOD);
if (node)
{
if (node->hasAttributes())
{
DOMString uri("URI");
token = node->getAttribute(&uri);
if (token)
{
LOGI(*token);
asset->setKeyRetrievalMethod(*token);
}
}
}
}
node = node->getNextSibling();
}
this->addAsset(asset);
return true;
}
/** see Ro.h */
bool Ro::handlePermission(const NodeImpl *curNode)
{
assert(curNode != NULL);
Right *right = new Right();
const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
NodeImpl* node = curNode->getFirstChild();
while (NULL != node)
{
const DOMString *name = NULL;
NodeListImpl *nodeList = NULL;
const string *token = NULL;
curElement = static_cast<const XMLElementImpl*>(node);
name = curElement->getTagName();
if (name->compare(STR_RO_ASSET) == 0)
{
LOGI("permission asset");
if (curElement->hasAttributes())
{
DOMString assetID(STR_RO_ASSET_IDREF);
const DOMString *id = curElement->getAttribute(&assetID);
if (id)
{
right->addAssetID(*id);
LOGI(*id);
}
}
}
OperationPermission::OPERATION type = OperationPermission::NONE;
if (name->compare(STR_RO_PLAY) == 0)
{
LOGI("permission play constraint");
type = OperationPermission::PLAY;
}
if (name->compare(STR_RO_DISPLAY) == 0)
{
LOGI("permission display costraint");
type = OperationPermission::DISPLAY;
}
if (name->compare(STR_RO_EXECUTE) == 0)
{
LOGI("permission execute constraint");
type = OperationPermission::EXECUTE;
}
if (name->compare(STR_RO_EXPORT) == 0)
{
LOGI("permission export constraint");
type = OperationPermission::EXPORT;
}
if (name->compare(STR_RO_PRINT) == 0)
{
LOGI("permission print constraint");
type = OperationPermission::PRINT;
}
Constraint *cst = NULL;
if (name->compare(STR_RO_CONSTRAINT) == 0)
{
LOGI("permission common constraint");
type = OperationPermission::COMMON;
}
cst = getConstraint(curElement);
if (cst)
{
OperationPermission *op = new OperationPermission(type, cst);
right->addOperationPermission(op);
}
node = node->getNextSibling();
}
this->addRight(right);
return true;
}
/** see Ro.h */
long Ro::convertISO8601DateTimeToLong(const char* ts)
{
if (NULL == ts)
{
return -1;
}
struct tm time;
memset(&time, 0, sizeof(struct tm));
strptime(ts, "%FT%T%z", &time);
//need timezone support: return mktime(&time) - timezone;
//It seems android-sooner doesn't support timezone function.
//line below is just for building, value would be wrong if no timezone minus.
return mktime(&time);
}
/** see Ro.h */
long Ro::convertISO8601PeriodToLong(const char* ts)
{
if (NULL == ts)
{
return -1;
}
int date, hour, min, sec;
sscanf(ts, "P%dDT%dH%dM%dS", &date, &hour, &min, &sec);
LOGI("%d %d %d %d", date, hour, min, sec);
return (date*24*60*60 + hour*60*60 + min*60 + sec);
}
/** see Ro.h */
Constraint* Ro::getConstraint(const NodeImpl* curNode)
{
assert(curNode != NULL);
Constraint *constraint = new Constraint();
const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
const string *name = NULL;
const string *token = NULL;
if ((token = curElement->getSoloText(STR_RO_COUNT)))
{
LOGI(*token);
constraint->setCount(atoi(token->c_str()));
}
if ((token = curElement->getSoloText(STR_RO_START)))
{
LOGI(*token);
//start Time
constraint->setStartTime(convertISO8601DateTimeToLong(token->c_str()));
}
if ((token = curElement->getSoloText(STR_RO_END)))
{
LOGI(*token);
//end Time
constraint->setEndTime(convertISO8601DateTimeToLong(token->c_str()));
}
if ((token = curElement->getSoloText(STR_RO_INTERVAL)))
{
LOGI(*token);
constraint->setInterval(atoi(token->c_str()));
}
if ((token = curElement->getSoloText(STR_RO_ACCUMULATED)))
{
LOGI(*token);
//Period
constraint->setAccumulated(convertISO8601PeriodToLong(token->c_str()));
}
if ((token = curElement->getSoloText(STR_RO_TIMEDCOUNT)))
{
LOGI(*token);
constraint->setTimedCount(atoi(token->c_str()));
const XMLElementImpl *node = curElement->getSoloElement(STR_RO_TIMEDCOUNT);
if (node)
{
if (node->hasAttributes())
{
DOMString timer(STR_RO_TIMER);
token = node->getAttribute(&timer);
if (token)
{
LOGI(*token);
constraint->setTimer(atoi(token->c_str()));
}
}
}
}
return constraint;
}
/** see Ro.h */
void Ro::loadRights(const string& contentID)
{
for (vector<Right*>::iterator it = this->mRightList.begin();
it != this->mRightList.end(); it++)
{
if ((*it)->mAssetNameList.empty())
{
mContentRightList.push_back(*it);
}
else
{
for (vector<Asset*>::iterator ita = this->mAssetList.begin();
ita != this->mAssetList.end(); ita++)
{
for (vector<string>::iterator its = (*it)->mAssetNameList.begin();
its != (*it)->mAssetNameList.end(); its++)
{
if ((*its).compare((*ita)->getID()) == 0)
{
if (contentID.compare((*ita)->getContentID()) == 0)
{
LOGI("find content right");
mContentRightList.push_back(*it);
}
}
}
}
}
}
}
/** see Ro.h */
void Ro::freeRights()
{
mContentRightList.clear();
}
/** see Ro.h */
bool Ro::checkPermission(OperationPermission::OPERATION type,
const string& contentID)
{
loadRights(contentID);
for (vector<Right*>::iterator it = mContentRightList.begin(); it != mContentRightList.end(); it++)
{
if ((*it)->checkPermission(type))
{
freeRights();
return true;
}
}
freeRights();
return false;
}
/** see Ro.h */
Ro::ERRCODE Ro::consume(OperationPermission::OPERATION type,
const string& contentID)
{
loadRights(contentID);
//check in mRightList
vector<Right*>::iterator it;
vector<Right*> tmpList;
vector<Right*> retList;
Constraint *constraint = NULL;
long ealiestEnd = -1;
bool hasCommonConstraint = false;
bool hasUnconstraint = false;
bool hasDateTimeConstraint = false;
bool hasTimedCountConstraint = false;
bool hasIntervalConstraint = false;
//apply the RO rule, if do not satisfy the constraint, .
//proper right select process
for (it = mContentRightList.begin(); it != mContentRightList.end(); it++)
{
if ((*it)->checkPermission(type))
{
constraint = (*it)->getConstraint(OperationPermission::COMMON);
if (constraint)
{
if (!constraint->isValid(time(NULL)))
{
continue;
}
hasCommonConstraint = true;
tmpList.push_back(*it);
}
constraint = (*it)->getConstraint(type);
assert(constraint != NULL);
if (!constraint->isValid(time(NULL)))
{
continue;
}
if (constraint->isUnConstraint())
{
//use uncontrainted firstly.
hasUnconstraint = true;
tmpList.push_back(*it);
break;
}
if (constraint->isDateTimeConstraint())
{
//use datetime constraint in high priority.
//if contain multipe constraints, use the earliest expire time.
hasDateTimeConstraint = true;
tmpList.push_back(*it);
continue;
}
if (constraint->isTimedCountConstraint())
{
//illegal Operation when time counted
if (type == OperationPermission::PRINT ||
type == OperationPermission::EXPORT)
{
continue;
}
hasTimedCountConstraint = true;
tmpList.push_back(*it);
continue;
}
if (constraint->isIntervalConstraint())
{
hasIntervalConstraint = true;
tmpList.push_back(*it);
continue;
}
tmpList.push_back(*it);
}
}
for (it = tmpList.begin(); it != tmpList.end(); it++)
{
if (hasUnconstraint == true)
{
//delete other constraint
constraint = (*it)->getConstraint(type);
if (constraint)
{
if (constraint->isUnConstraint())
{
retList.push_back(*it);
break;
}
}
continue;
}
if (hasDateTimeConstraint == true)
{
//delete other constraint
constraint = (*it)->getConstraint(type);
if (constraint)
{
if (constraint->isDateTimeConstraint())
{
long tt = constraint->getEndTime();
if (ealiestEnd == -1)
{
ealiestEnd = tt;
retList.push_back(*it);
}
else if (ealiestEnd > tt)
{
ealiestEnd = tt;
retList.pop_back();
retList.push_back(*it);
}
}
}
continue;
}
if (hasIntervalConstraint == true)
{
//delete other constraint
constraint = (*it)->getConstraint(type);
if (constraint)
{
if (constraint->isIntervalConstraint())
{
retList.push_back(*it);
}
}
continue;
}
if (hasTimedCountConstraint == true)
{
constraint = (*it)->getConstraint(type);
if (constraint)
{
if (constraint->isTimedCountConstraint())
{
retList.push_back(*it);
}
}
continue;
}
retList.push_back(*it);
}
if (retList.size() == 0)
{
freeRights();
return RO_BAD;
}
LOGI("Proper right has %d", retList.size());
assert(retList.size() == 1);
mProperRight = retList[0];
constraint = retList[0]->getConstraint(OperationPermission::COMMON);
if (constraint)
{
if (constraint->consume() == false)
{
freeRights();
return RO_BAD;
}
}
constraint = retList[0]->getConstraint(type);
if (constraint)
{
if (constraint->consume() == false)
{
freeRights();
return RO_BAD;
}
}
//update the constraint
freeRights();
return RO_OK;
}
/** see Ro.h */
string Ro::getContentCek(const string& contentID)
{
for (vector<Asset*>::iterator it = mAssetList.begin();
it != mAssetList.end(); it++)
{
if (contentID.compare((*it)->getContentID()) == 0)
{
return (*it)->getCek();
}
}
return "";
}
/** see Ro.h */
string Ro::getContentHash(const string& contentID)
{
for (vector<Asset*>::iterator it = mAssetList.begin();
it != mAssetList.end(); it++)
{
if (contentID.compare((*it)->getContentID()) == 0)
{
return (*it)->getDCFDigest();
}
}
return "";
}