Ro.cpp revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <rights/Ro.h>
18#include <rights/Constraint.h>
19#include <rights/OperationPermission.h>
20#include <util/xml/DomExpatAgent.h>
21#include <util/domcore/DOMString.h>
22#include <utils/Log.h>
23
24#include <uassert.h>
25#include <time.h>
26#include <ofstream.h>
27using namespace ustl;
28
29const char *STR_RO_RIGHTS = "o-ex:rights";
30const char *STR_RO_CONTEXT = "o-ex:context";
31const char *STR_RO_AGREEMENT = "o-ex:agreement";
32const char *STR_RO_ASSET = "o-ex:asset";
33const char *STR_RO_INHERIT = "o-ex:inherit";
34const char *STR_RO_DIGEST = "o-ex:digest";
35const char *STR_RO_KEYINFO = "ds:KeyInfo";
36const char *STR_RO_PERMISSION = "o-ex:permission";
37const char *STR_RO_ASSET_ID = "o-ex:id";
38const char *STR_RO_ASSET_IDREF = "o-ex:idref";
39const char *STR_RO_CONTEXT_ID = "o-dd:uid";
40const char *STR_RO_CONTEXT_VERSION = "o-dd:version";
41const char *STR_RO_DIGEST_VALUE = "ds:DigestValue";
42const char *STR_RO_CIPHER_VALUE = "xenc:CipherValue";
43const char *STR_RO_RETRIEVAL_METHOD = "ds:RetrievalMethod";
44const char *STR_RO_PLAY = "o-dd:play";
45const char *STR_RO_DISPLAY = "o-dd:display";
46const char *STR_RO_EXECUTE = "o-dd:execute";
47const char *STR_RO_PRINT = "o-dd:print";
48const char *STR_RO_EXPORT = "o-dd:export";
49const char *STR_RO_CONSTRAINT = "o-ex:constraint";
50const char *STR_RO_COUNT = "o-dd:count";
51const char *STR_RO_TIMEDCOUNT = "o-dd:timed-count";
52const char *STR_RO_TIMER = "oma-dd:timer";
53const char *STR_RO_INTERVAL = "o-dd:interval";
54const char *STR_RO_DATETIME = "o-dd:datetime";
55const char *STR_RO_START = "o-dd:start";
56const char *STR_RO_END = "o-dd:end";
57const char *STR_RO_ACCUMULATED = "o-dd:accumulated";
58const char *STR_RO_INDIVIDUAL = "o-dd:individual";
59const char *STR_RO_SYSTEM = "o-dd:system";
60
61/** see Ro.h */
62Ro::Ro()
63{
64    mDoc = new XMLDocumentImpl();
65    mProperRight = NULL;
66}
67
68/** see Ro.h */
69Ro::~Ro()
70{
71     for (vector<Right*>::iterator itr = mRightList.begin(); itr != mRightList.end(); itr++)
72     {
73        delete(*itr);
74     }
75
76     mRightList.clear();
77
78     for (vector<Asset*>::iterator ita = mAssetList.begin(); ita != mAssetList.end(); ita++)
79     {
80        delete(*ita);
81     }
82
83     mAssetList.clear();
84
85     mProperRight = NULL;
86     delete mDoc;
87
88}
89
90/** see Ro.h */
91void Ro::setRoID(string& id)
92{
93    mRoID = id;
94}
95
96/** see Ro.h */
97const string& Ro::getRoID() const
98{
99    return mRoID;
100}
101
102/** see Ro.h */
103void Ro::setRoVersion(string& version)
104{
105    mRoVersion = version;
106}
107
108/** see Ro.h */
109void Ro::addAsset(Asset* asset)
110{
111    mAssetList.push_back(asset);
112}
113
114/** see Ro.h */
115void Ro::addRight(Right* right)
116{
117    mRightList.push_back(right);
118}
119
120/** see Ro.h */
121bool Ro::save()
122{
123     LOGI("==============Ro save.=================");
124
125     return true;
126}
127
128/** see Ro.h */
129Ro::ERRCODE Ro::parse(istringstream *roStream)
130{
131    DomExpatAgent xmlAgent(mDoc);
132
133    if (NULL == roStream)
134    {
135        LOGI("NULL stream");
136        return RO_NULL_STREAM;
137    }
138
139    if (xmlAgent.generateDocumentFromXML(roStream) == false)
140    {
141        LOGI("generate xml doc error");
142        return RO_ERR_BAD_XML;
143    }
144
145    handleDocument(mDoc);
146
147    return RO_OK;
148}
149
150/** see Ro.h */
151bool Ro::handleDocument(const XMLDocumentImpl* doc)
152{
153    assert(doc != NULL);
154
155    NodeImpl* node = doc->getDocumentElement();
156
157    return handleRights(node);
158}
159
160/** see Ro.h */
161bool Ro::handleRights(const NodeImpl *curNode)
162{
163    assert(curNode != NULL);
164
165    NodeImpl *node = curNode->getFirstChild();
166
167    while (NULL != node)
168    {
169        const DOMString* name;
170
171        name = static_cast<const XMLElementImpl*>(node)->getTagName();
172
173        if (name->compare(STR_RO_CONTEXT) == 0)
174        {
175            LOGI("rights context");
176            const DOMString *token = NULL;
177            token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_ID);
178
179            if (token)
180            {
181                LOGI(*token);
182                mRoID = *token;
183            }
184
185            token = static_cast<const XMLElementImpl*>(node)->getSoloText(STR_RO_CONTEXT_VERSION);
186            if (token)
187            {
188                LOGI(*token);
189                mRoVersion = *token;
190            }
191        }
192
193        if (name->compare(STR_RO_AGREEMENT) == 0)
194        {
195
196            LOGI("rights agreement");
197            if (handleAgreement(node) == false)
198            {
199                return false;
200            }
201        }
202
203        node = node->getNextSibling();
204    }
205    return true;
206}
207
208/** see Ro.h */
209bool Ro::handleAgreement(const NodeImpl *curNode)
210{
211    assert(curNode != NULL);
212
213    NodeImpl *node = curNode->getFirstChild();
214
215    while (NULL != node)
216    {
217        const DOMString* name;
218
219        name = static_cast<const XMLElementImpl*>(node)->getTagName();
220
221        if (name->compare(STR_RO_ASSET) == 0)
222        {
223            // do something about asset.
224            LOGI("asset");
225
226            if (handleAsset(node) == false)
227            {
228                return false;
229            }
230        }
231
232        if (name->compare(STR_RO_PERMISSION) == 0)
233        {
234            // do something about permission.
235            LOGI("permission");
236
237            if (handlePermission(node) == false)
238            {
239                return false;
240            }
241        }
242
243        node = node->getNextSibling();
244    }
245
246    return true;
247}
248
249/** see Ro.h */
250bool Ro::handleAsset(const NodeImpl *curNode)
251{
252    assert(curNode != NULL);
253
254    Asset *asset = new Asset();
255
256    const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
257
258    if (curElement->hasAttributes())
259    {
260        DOMString assetID(STR_RO_ASSET_ID);
261        LOGI("asset id:");
262
263        const DOMString *id = curElement->getAttribute(&assetID);
264
265        if (id)
266        {
267            asset->setID(*id);
268        }
269
270    }
271
272    NodeImpl* node = curNode->getFirstChild();
273
274    const DOMString *name = NULL;
275    const string *token = NULL;
276
277    while (NULL != node)
278    {
279        curElement = static_cast<const XMLElementImpl*>(node);
280        name = curElement->getTagName();
281
282        if (name->compare(STR_RO_CONTEXT) == 0 ||
283            name->compare(STR_RO_INHERIT) == 0)
284        {
285            LOGI("asset context");
286
287            token = curElement->getSoloText(STR_RO_CONTEXT_ID);
288            if (token)
289            {
290                LOGI(*token);
291
292                if (name->compare(STR_RO_CONTEXT) == 0)
293                {
294                    asset->setContentID(*token);
295                }
296                else
297                {
298                    //parent ID.
299                    asset->setParentContentID(*token);
300                }
301            }
302        }
303
304        if (name->compare(STR_RO_DIGEST) == 0)
305        {
306            LOGI("asset digest");
307            //digest method is fixed value:
308            //http://www.w3.org/2000/09/xmldisig#sha1
309            token = curElement->getSoloText(STR_RO_DIGEST_VALUE);
310            if (token)
311            {
312                LOGI(*token);
313                asset->setDCFDigest(*token);
314            }
315        }
316
317        if (name->compare(STR_RO_KEYINFO) == 0)
318        {
319            LOGI("asset keyinfo");
320
321            token = curElement->getSoloText(STR_RO_CIPHER_VALUE);
322            if (token)
323            {
324                LOGI(*token);
325                asset->setEncryptedKey(*token);
326            }
327
328            const XMLElementImpl *node = curElement->getSoloElement(STR_RO_RETRIEVAL_METHOD);
329
330            if (node)
331            {
332                if (node->hasAttributes())
333                {
334                    DOMString uri("URI");
335                    token = node->getAttribute(&uri);
336                    if (token)
337                    {
338                        LOGI(*token);
339                        asset->setKeyRetrievalMethod(*token);
340                    }
341                }
342            }
343        }
344
345        node = node->getNextSibling();
346    }
347
348    this->addAsset(asset);
349    return true;
350}
351
352/** see Ro.h */
353bool Ro::handlePermission(const NodeImpl *curNode)
354{
355    assert(curNode != NULL);
356
357    Right *right = new Right();
358
359    const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
360
361    NodeImpl* node = curNode->getFirstChild();
362
363    while (NULL != node)
364    {
365        const DOMString *name = NULL;
366        NodeListImpl *nodeList = NULL;
367
368        const string *token = NULL;
369        curElement = static_cast<const XMLElementImpl*>(node);
370        name = curElement->getTagName();
371
372        if (name->compare(STR_RO_ASSET) == 0)
373        {
374            LOGI("permission asset");
375            if (curElement->hasAttributes())
376            {
377                DOMString assetID(STR_RO_ASSET_IDREF);
378                const DOMString *id = curElement->getAttribute(&assetID);
379                if (id)
380                {
381                    right->addAssetID(*id);
382                    LOGI(*id);
383                }
384            }
385        }
386
387        OperationPermission::OPERATION type = OperationPermission::NONE;
388
389        if (name->compare(STR_RO_PLAY) == 0)
390        {
391            LOGI("permission play constraint");
392            type = OperationPermission::PLAY;
393        }
394
395        if (name->compare(STR_RO_DISPLAY) == 0)
396        {
397            LOGI("permission display costraint");
398            type = OperationPermission::DISPLAY;
399        }
400
401        if (name->compare(STR_RO_EXECUTE) == 0)
402        {
403            LOGI("permission execute constraint");
404            type = OperationPermission::EXECUTE;
405        }
406
407        if (name->compare(STR_RO_EXPORT) == 0)
408        {
409            LOGI("permission export constraint");
410            type = OperationPermission::EXPORT;
411        }
412
413        if (name->compare(STR_RO_PRINT) == 0)
414        {
415            LOGI("permission print constraint");
416            type = OperationPermission::PRINT;
417        }
418
419        Constraint *cst = NULL;
420
421        if (name->compare(STR_RO_CONSTRAINT) == 0)
422        {
423            LOGI("permission common constraint");
424            type = OperationPermission::COMMON;
425        }
426
427        cst = getConstraint(curElement);
428        if (cst)
429        {
430            OperationPermission *op = new OperationPermission(type, cst);
431            right->addOperationPermission(op);
432        }
433
434        node = node->getNextSibling();
435    }
436
437    this->addRight(right);
438    return true;
439}
440
441/** see Ro.h */
442long Ro::convertISO8601DateTimeToLong(const char* ts)
443{
444    if (NULL == ts)
445    {
446        return -1;
447    }
448
449    struct tm time;
450    memset(&time, 0, sizeof(struct tm));
451
452    strptime(ts, "%FT%T%z", &time);
453
454//need timezone support:  return mktime(&time) - timezone;
455//It seems android-sooner doesn't support timezone function.
456//line below is just for building, value would be wrong if no timezone minus.
457    return mktime(&time);
458}
459
460/** see Ro.h */
461long Ro::convertISO8601PeriodToLong(const char* ts)
462{
463    if (NULL == ts)
464    {
465        return -1;
466    }
467
468    int date, hour, min, sec;
469    sscanf(ts, "P%dDT%dH%dM%dS", &date, &hour, &min, &sec);
470    LOGI("%d %d %d %d", date, hour, min, sec);
471    return (date*24*60*60 + hour*60*60 + min*60 + sec);
472}
473
474/** see Ro.h */
475Constraint* Ro::getConstraint(const NodeImpl* curNode)
476{
477    assert(curNode != NULL);
478
479    Constraint *constraint = new Constraint();
480
481    const XMLElementImpl *curElement = static_cast<const XMLElementImpl*>(curNode);
482
483    const string *name = NULL;
484    const string *token = NULL;
485
486    if ((token = curElement->getSoloText(STR_RO_COUNT)))
487    {
488        LOGI(*token);
489        constraint->setCount(atoi(token->c_str()));
490    }
491
492    if ((token = curElement->getSoloText(STR_RO_START)))
493    {
494        LOGI(*token);
495        //start Time
496        constraint->setStartTime(convertISO8601DateTimeToLong(token->c_str()));
497    }
498
499    if ((token = curElement->getSoloText(STR_RO_END)))
500    {
501        LOGI(*token);
502        //end Time
503        constraint->setEndTime(convertISO8601DateTimeToLong(token->c_str()));
504    }
505
506    if ((token = curElement->getSoloText(STR_RO_INTERVAL)))
507    {
508        LOGI(*token);
509        constraint->setInterval(atoi(token->c_str()));
510    }
511
512    if ((token = curElement->getSoloText(STR_RO_ACCUMULATED)))
513    {
514        LOGI(*token);
515        //Period
516        constraint->setAccumulated(convertISO8601PeriodToLong(token->c_str()));
517    }
518
519    if ((token = curElement->getSoloText(STR_RO_TIMEDCOUNT)))
520    {
521        LOGI(*token);
522        constraint->setTimedCount(atoi(token->c_str()));
523
524        const XMLElementImpl *node = curElement->getSoloElement(STR_RO_TIMEDCOUNT);
525
526        if (node)
527        {
528            if (node->hasAttributes())
529            {
530                DOMString timer(STR_RO_TIMER);
531                token = node->getAttribute(&timer);
532                if (token)
533                {
534                    LOGI(*token);
535                    constraint->setTimer(atoi(token->c_str()));
536                }
537            }
538        }
539
540    }
541
542    return constraint;
543}
544
545/** see Ro.h */
546void Ro::loadRights(const string& contentID)
547{
548    for (vector<Right*>::iterator it = this->mRightList.begin();
549        it != this->mRightList.end(); it++)
550    {
551        if ((*it)->mAssetNameList.empty())
552        {
553            mContentRightList.push_back(*it);
554        }
555        else
556        {
557            for (vector<Asset*>::iterator ita = this->mAssetList.begin();
558                 ita != this->mAssetList.end(); ita++)
559            {
560                for (vector<string>::iterator its = (*it)->mAssetNameList.begin();
561                     its != (*it)->mAssetNameList.end(); its++)
562                {
563                    if ((*its).compare((*ita)->getID()) == 0)
564                    {
565                        if (contentID.compare((*ita)->getContentID()) == 0)
566                        {
567                            LOGI("find content right");
568                            mContentRightList.push_back(*it);
569                        }
570                    }
571                }
572            }
573        }
574
575
576    }
577
578}
579
580/** see Ro.h */
581void Ro::freeRights()
582{
583    mContentRightList.clear();
584}
585
586/** see Ro.h */
587bool Ro::checkPermission(OperationPermission::OPERATION type,
588                                const string& contentID)
589{
590    loadRights(contentID);
591
592    for (vector<Right*>::iterator it = mContentRightList.begin(); it != mContentRightList.end(); it++)
593     {
594        if ((*it)->checkPermission(type))
595        {
596            freeRights();
597            return true;
598        }
599
600     }
601    freeRights();
602    return false;
603}
604
605/** see Ro.h */
606Ro::ERRCODE Ro::consume(OperationPermission::OPERATION type,
607                         const string& contentID)
608{
609    loadRights(contentID);
610
611    //check in mRightList
612    vector<Right*>::iterator it;
613    vector<Right*> tmpList;
614    vector<Right*> retList;
615    Constraint *constraint = NULL;
616    long ealiestEnd = -1;
617    bool hasCommonConstraint = false;
618    bool hasUnconstraint = false;
619    bool hasDateTimeConstraint = false;
620    bool hasTimedCountConstraint = false;
621    bool hasIntervalConstraint = false;
622
623
624    //apply the RO rule, if do not satisfy the constraint, .
625    //proper right select process
626
627    for (it = mContentRightList.begin(); it != mContentRightList.end(); it++)
628    {
629        if ((*it)->checkPermission(type))
630        {
631            constraint = (*it)->getConstraint(OperationPermission::COMMON);
632            if (constraint)
633            {
634                if (!constraint->isValid(time(NULL)))
635                {
636                    continue;
637                }
638
639                hasCommonConstraint = true;
640                tmpList.push_back(*it);
641            }
642
643            constraint = (*it)->getConstraint(type);
644            assert(constraint != NULL);
645
646            if (!constraint->isValid(time(NULL)))
647            {
648                continue;
649            }
650
651            if (constraint->isUnConstraint())
652            {
653                //use uncontrainted firstly.
654                hasUnconstraint = true;
655                tmpList.push_back(*it);
656                break;
657            }
658
659            if (constraint->isDateTimeConstraint())
660            {
661                //use datetime constraint in high priority.
662                //if contain multipe constraints, use the earliest expire time.
663                hasDateTimeConstraint = true;
664                tmpList.push_back(*it);
665                continue;
666            }
667
668            if (constraint->isTimedCountConstraint())
669            {
670            //illegal Operation when time counted
671                if (type == OperationPermission::PRINT ||
672                    type == OperationPermission::EXPORT)
673                {
674                    continue;
675                }
676
677                hasTimedCountConstraint = true;
678                tmpList.push_back(*it);
679                continue;
680            }
681
682            if (constraint->isIntervalConstraint())
683            {
684                hasIntervalConstraint = true;
685                tmpList.push_back(*it);
686                continue;
687            }
688
689            tmpList.push_back(*it);
690        }
691    }
692
693
694    for (it = tmpList.begin(); it != tmpList.end(); it++)
695    {
696        if (hasUnconstraint == true)
697        {
698            //delete other constraint
699            constraint = (*it)->getConstraint(type);
700            if (constraint)
701            {
702                if (constraint->isUnConstraint())
703                {
704                    retList.push_back(*it);
705                    break;
706                }
707            }
708            continue;
709        }
710
711        if (hasDateTimeConstraint == true)
712        {
713            //delete other constraint
714            constraint = (*it)->getConstraint(type);
715            if (constraint)
716            {
717                if (constraint->isDateTimeConstraint())
718                {
719                    long tt = constraint->getEndTime();
720
721                    if (ealiestEnd == -1)
722                    {
723                        ealiestEnd = tt;
724                        retList.push_back(*it);
725                    }
726                    else if (ealiestEnd > tt)
727                    {
728                        ealiestEnd = tt;
729                        retList.pop_back();
730                        retList.push_back(*it);
731                    }
732                }
733            }
734            continue;
735        }
736
737        if (hasIntervalConstraint == true)
738        {
739            //delete other constraint
740            constraint = (*it)->getConstraint(type);
741            if (constraint)
742            {
743                if (constraint->isIntervalConstraint())
744                {
745                    retList.push_back(*it);
746                }
747            }
748            continue;
749        }
750
751        if (hasTimedCountConstraint == true)
752        {
753            constraint = (*it)->getConstraint(type);
754            if (constraint)
755            {
756                if (constraint->isTimedCountConstraint())
757                {
758                    retList.push_back(*it);
759                }
760            }
761            continue;
762        }
763
764        retList.push_back(*it);
765    }
766
767    if (retList.size() == 0)
768    {
769        freeRights();
770        return RO_BAD;
771    }
772
773    LOGI("Proper right has %d", retList.size());
774
775    assert(retList.size() == 1);
776
777    mProperRight = retList[0];
778    constraint = retList[0]->getConstraint(OperationPermission::COMMON);
779    if (constraint)
780    {
781        if (constraint->consume() == false)
782        {
783            freeRights();
784            return RO_BAD;
785        }
786    }
787
788    constraint = retList[0]->getConstraint(type);
789    if (constraint)
790    {
791        if (constraint->consume() == false)
792        {
793            freeRights();
794            return RO_BAD;
795        }
796    }
797
798    //update the constraint
799    freeRights();
800    return RO_OK;
801}
802
803/** see Ro.h */
804string Ro::getContentCek(const string& contentID)
805{
806    for (vector<Asset*>::iterator it = mAssetList.begin();
807        it != mAssetList.end(); it++)
808    {
809        if (contentID.compare((*it)->getContentID()) == 0)
810        {
811            return (*it)->getCek();
812        }
813    }
814
815    return "";
816}
817
818/** see Ro.h */
819string Ro::getContentHash(const string& contentID)
820{
821    for (vector<Asset*>::iterator it = mAssetList.begin();
822        it != mAssetList.end(); it++)
823    {
824        if (contentID.compare((*it)->getContentID()) == 0)
825        {
826            return (*it)->getDCFDigest();
827        }
828    }
829
830    return "";
831}
832