Hi,
Sling uses the JSONObject and friends for json parsing. Its fine for
small JSON fragments but not as fast for huge streams as its memory
based.

JSONObject o = new JSONObject(jsonString)

will parse a JSON string into an object.

Thanks for doing the other things.

Best Regards
Ian

On 22 September 2013 19:03, Dishara Wijewardana <[email protected]> wrote:
> Hi Ian,
> I changed the logic to correct with my misunderstandings as suggested and
> refactored all code to use apache commons Base64 for encoding and rest of
> the code review suggestions. And it works.  Hence I commited the code. And
> also upload the code to the JIRA at this milestone (without json parser).
>
> I also spent more time trying to move the existing logics to json parser
> since there are several places that I need to change and need to make sure
> my working impl not break from this refactor. I am new to json. I used
>  com.google.gson.*;
>
> {"key1":{"principal":"dishara","grant":"false","permission":"1"},"key2":{"principal":"dishara2","grant":"false","permission":"7"}}
>
> Here when I add  a ACE, I have to add a property along with the json child
> object. i.e key1. So above is the json file of a ACL from the code what I
> tried.  But in this case, when try to update a ACL with a new ACE, we have
> to provide a unique key always. I believe there will be a better ways of
> doing this. Does sling has a JSON parser ? I saw only JSONObject.
>
>
> On Sat, Sep 21, 2013 at 10:16 AM, Ian Boston <[email protected]> wrote:
>
>> Hi,
>> You need to publish the comments before anyone else can see them,
>> thats why they say draft on them.
>>
>> I'll reply here, but if you could publish them it would keep the
>> thread going on code review.
>>
>> On 21 September 2013 02:53, Dishara Wijewardana <[email protected]>
>> wrote:
>> > Hi Ian,
>> > I could see them when I logged in. Did you checked whether you logged in
>> to
>> > the google project? When I was not logged in I was not able to see my
>> > comments also.
>> >
>> > But in case if it is still not viewable, I am copying the 3 comments I
>> > added.
>> >
>> >  *
>> > Ian Boston* 2013/09/18 08:18:57
>> >  I think this should be getPrivilegeFromACE. READ, WRITE, DELETE are
>> > privileges
>> > *Reply* *Done*
>> >  *(Draft)* 2013/09/18 18:36:19
>> > As per explanation of yours in the mail, read=0x01,write=0x02 and etc are
>> > categorized under Principals. Can you confirm that?
>>
>> no.
>>
>> read, write, delete are categorised as privileges.
>> ieb, dishara are categorised as principals.
>>
>>
>> On 2013/09/18 08:18:57,
>> > Ian Boston wrote:
>> > Show quoted text
>> > *Edit
>> >
>> > *
>> >  *Ian Boston* 2013/09/18 08:18:57
>> >  I think this impl might be wrong ? Principals are things like ieb,
>> > dishara, I cant see how you could represent those in an int. I think it
>> > should be boolean isUserHasPrincipal(String principal, Set<String>
>> > userPrincipals) { return userPrincipals.contains(principal); }
>> > *Reply* *Done*
>> >  *(Draft)* 2013/09/18 18:43:48
>> > According to the mail ACE is something like
>> > <order>_<principal>_<allow|deny> where i.e 0_everyone_allow : 0x01. As
>> per
>> > you, user name is the "principle" ? Initially I did it in that way, but I
>> > thought principle is read, write etc as per last comment of mine, and
>> hence
>> > the values of it can be represent in a integer bitmap. But if READ,WRITE
>> > are priviledges my impl is wrong. Can you please confirm what I think is
>> > correct or not ?
>>
>> If the ACE is 0_everyone_allow: 0x01 then the fields are
>> <order>_<principal>_<grant|deny> : <privileges>
>>
>>
>>
>>
>>  On 2013/09/18 08:18:57, Ian Boston wrote:
>> > Show quoted text
>> > *Edit
>> >
>> > *
>> >  *Ian Boston* 2013/09/18 08:18:57
>> >  the int userPrinciples should be a Set<String> userPrincipals
>> > *Reply* *Done*
>> >  *(Draft)* 2013/09/19 21:29:51
>> > Can you provide an example set of principals. According to me, it can be
>> > the values of i.e read=0x01, write=0x02 so the set should be <0x01>
>> <0x02>
>> > or a single integer 0x03 which represents above. But as per you I am
>> wrong.
>>
>>
>> Principals identify a user or a group, and every user or group has a
>> principal.
>>
>> eg
>> ieb is the Principal that identifies the user ieb.
>> everyone is the Principal that identifies the group everyone,
>> containing all users.
>> anonymous is the Principal that identifies the anonymous user who has
>> not logged in.
>>
>> The concept of principal is widely used and in java is frequently
>> implemented extending the interface java.lang.Principal. If you go
>> into Eclipse and show Type Hierarchy you will see some examples,
>> including the Jackrabbit User and Group implementations.
>>
>> In the ACEs we just need the name of the principal to identify who the
>> ACE applies to.
>>
>> Finally, in addition to a User and Group being identified by a
>> Principal, they will have Principals by virtue of the fact they are
>> members of other groups. A user will always have the principal
>> "everyone" since they are part of the group "everyone".
>>
>> HTH
>>
>> Best Regards
>> Ian
>>
>>
>>
>> > So please correct me. On 2013/09/18 08:18:57, Ian Boston wrote:
>> > Show quoted text
>> > *Edit
>> >
>> >
>> > **
>> >
>> > *
>> >
>> >
>> > On Fri, Sep 20, 2013 at 4:44 PM, Ian Boston <[email protected]> wrote:
>> >
>> >> Hi,
>> >>
>> >> Did you publish your replies to my comments at [1]. I cant see
>> >> anything other than what I said. Be sure to click on each comment to
>> >> exand it to full size.
>> >>
>> >> Ian
>> >>
>> >> 1
>> >>
>> https://codereview.appspot.com/13396052/diff/1/main/cassandra/src/main/java/org/apache/sling/cassandra/resource/provider/security/AccessControlUtil.java
>> >>
>> >> On 19 September 2013 22:38, Dishara Wijewardana <
>> [email protected]>
>> >> wrote:
>> >> > Hi Ian,
>> >> > If you find some time, appreciate if you can add some reply to the
>> >> comments
>> >> > on the code review basically regarding the issues you raised in the
>> >> > algorithm and the conflict of principals and privileges as per my
>> >> > understanding. I will cleanup the code with your clarification in this
>> >> > weekend.
>> >> >
>> >> >
>> >> > On Thu, Sep 19, 2013 at 12:16 AM, Dishara Wijewardana <
>> >> > [email protected]> wrote:
>> >> >
>> >> >> Hi Ian,
>> >> >> Thank you very much for the valuable feedbacks as always. I added
>> some
>> >> >> comments to clarify couple of cases. Appreciate your response, so
>> that I
>> >> >> can proceed with the improvements.
>> >> >>
>> >> >>
>> >> >> On Wed, Sep 18, 2013 at 1:19 PM, Ian Boston <[email protected]> wrote:
>> >> >>
>> >> >>> Hi Dishara,
>> >> >>> Looking good, I have some comments, so I did a code review at [1]
>> >> >>> Ian
>> >> >>>
>> >> >>> 1 https://codereview.appspot.com/13396052/
>> >> >>>
>> >> >>>
>> >> >>> On 18 September 2013 04:13, Dishara Wijewardana <
>> >> [email protected]>
>> >> >>> wrote:
>> >> >>> > Hi Ian,
>> >> >>> > Sorry for the delay of updating the thread. I had to to some
>> >> >>> > experiment(writing dummy tests iteratively) to figure out what you
>> >> >>> exactly
>> >> >>> > meant. And finally was able to implement what you said. I have
>> >> commited
>> >> >>> the
>> >> >>> > src under a new package called "security". Currently it is a util
>> >> class
>> >> >>> > which is executable and we can test.
>> >> >>> >
>> >> >>> > Now i.e following can be done and returns accurate results.
>> >> >>> >
>> >> >>> >         String path = "/content/cassandra/p2/c2";
>> >> >>> >         String policy="0_dishara_allow :0x01 ";
>> >> >>> >
>> >> >>> >             accessControlUtil.addACE(path,policy);
>> >> >>> >
>> >> >>> >             int results[] =
>> >> accessControlUtil.buildAtLevel(0x04,path);
>> >> >>> >             System.out.println("GRANT" +results[0]);
>> >> >>> >             System.out.println("DENY" +results[1]);
>> >> >>> >
>> >> >>> >
>> >> >>> >
>> >> >>> >
>> >> >>> >
>> >> >>> > On Mon, Sep 16, 2013 at 2:28 PM, Ian Boston <[email protected]>
>> wrote:
>> >> >>> >
>> >> >>> >> Hi,
>> >> >>> >>
>> >> >>> >> Yes you could store the ACL (ordered list of ACE's) with the
>> >> resource
>> >> >>> >> itself, although you will then have to add additional code to
>> >> protect
>> >> >>> >> access to that property which will complicate the
>> CassandraProvider,
>> >> >>> >> which is why I was sugesting that you do ACL storage in a
>> completely
>> >> >>> >> separate Column Family.
>> >> >>> >>
>> >> >>> >> If you choose to do it that way (storing ACLs with the resource),
>> >> then
>> >> >>> >> you might consider storing the ACL as a JSON string.
>> >> >>> >>
>> >> >>> >> By default, permissions inherit from the permissions on the
>> parent
>> >> >>> >> resource, so there is no need to create an ACL on creation, but
>> the
>> >> >>> >> implementation of the ResourceAccessGate will need compute access
>> >> >>> >> control including parent ACLs.
>> >> >>> >>
>> >> >>> >> How you implement ACL crud is entirely upto you. I suggest you
>> use
>> >> >>> >> Hector directly to do this.
>> >> >>> >>
>> >> >>> >> This will make your CassandraResourceAccessGate completely
>> separate
>> >> >>> >> from you CassandraResourceProvider.
>> >> >>> >>
>> >> >>> >> Best Regards
>> >> >>> >> Ian
>> >> >>> >>
>> >> >>> >> On 13 September 2013 13:21, Dishara Wijewardana <
>> >> >>> [email protected]>
>> >> >>> >> wrote:
>> >> >>> >> > On Thu, Sep 12, 2013 at 8:37 PM, Ian Boston <[email protected]>
>> >> wrote:
>> >> >>> >> >
>> >> >>> >> >> Hi
>> >> >>> >> >>
>> >> >>> >> >> On 12 September 2013 13:24, Dishara Wijewardana <
>> >> >>> >> [email protected]>
>> >> >>> >> >> wrote:
>> >> >>> >> >> > Hi Ian
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >> > On Thu, Sep 12, 2013 at 1:50 PM, Ian Boston <[email protected]>
>> >> >>> wrote:
>> >> >>> >> >> >
>> >> >>> >> >> >> Hi Dishara,
>> >> >>> >> >> >> To make the Cassandra Resource Provider really useful I
>> think
>> >> we
>> >> >>> need
>> >> >>> >> >> >> to add access control. I think the best way of doing this
>> is
>> >> to
>> >> >>> >> borrow
>> >> >>> >> >> >> some concepts from Jackrabbit access control.
>> >> >>> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >> > The following algorithms, and etc does sling already have
>> any
>> >> >>> >> >> > implementation of it. If so I can reuse them. Since sling
>> has
>> >> few
>> >> >>> >> >> providers
>> >> >>> >> >> > I believe they probably have some common interface.
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >> There is not, or at least, not in a way that is exposed. There
>> >> is an
>> >> >>> >> >> implementation inside Jackrabbit, but it is tightly coupled to
>> >> >>> >> >> Jackrabbit and more complex than the simple system below.
>> >> >>> >> >>
>> >> >>> >> >> Once implemented, this access control system may be exposed
>> as a
>> >> >>> >> >> ResourceAccessGate, but since (IIRC) imposing anything other
>> than
>> >> >>> read
>> >> >>> >> >> access control has not been done, the
>> CassandraResourceProvider
>> >> may
>> >> >>> >> >> use the implementation directly to impose write and delete.
>> >> >>> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >> >> Take a deep breath, and you will see why I left this till
>> >> last.
>> >> >>> >> >> >>
>> >> >>> >> >> >> I think we should provide path base access control, which
>> >> >>> inherits
>> >> >>> >> >> >> from parent resources in the path. At every level there is
>> a
>> >> an
>> >> >>> >> >> >> ordered list of access control entries each access control
>> >> entry
>> >> >>> >> (ACE)
>> >> >>> >> >> >> being either an allow entry or a deny entry. What is
>> allowed
>> >> or
>> >> >>> >> denied
>> >> >>> >> >> >> is defined in a 32bit bitmap with each bit representing 1
>> >> >>> permission,
>> >> >>> >> >> >> so we can have upto 32 permissions. Each ACE specifies a
>> >> single
>> >> >>> >> >> >> principal. So an ACL consists of a ordered list of ACE's
>> each
>> >> one
>> >> >>> >> >> >> bound to a principal.
>> >> >>> >> >> >>
>> >> >>> >> >> >> A user has a set of principals, so to resolve the ACL at
>> any
>> >> one
>> >> >>> path
>> >> >>> >> >> >> for a user the global ACL is filtered to contain only the
>> >> ACE's
>> >> >>> with
>> >> >>> >> >> >> principals that the user has.
>> >> >>> >> >> >>
>> >> >>> >> >> >> Computing a final access control bitmap for a user at a
>> >> location
>> >> >>> >> >> >> requires ordered processing of all the ACEs relevant to the
>> >> user
>> >> >>> at
>> >> >>> >> >> >> the current path and then all ancestors.
>> >> >>> >> >> >>
>> >> >>> >> >> >> The pseudo algorithm to calculate the a grant bitmap and a
>> >> deny
>> >> >>> >> bitmap
>> >> >>> >> >> >> at any level is:
>> >> >>> >> >> >>
>> >> >>> >> >> >> function getCurrentLevelBitmaps(currentPath):
>> >> >>> >> >> >>       int grants = 0;
>> >> >>> >> >> >>       int denies = 0;
>> >> >>> >> >> >>       for all ACEs in the ACL at the currentPath:
>> >> >>> >> >> >>             if the user has the principal of the current
>> ACE:
>> >> >>> >> >> >>                   int toGrant = 0;
>> >> >>> >> >> >>                   int toDeny = 0;
>> >> >>> >> >> >>                   if the ACE is a grant:
>> >> >>> >> >> >>                         toGrant = the ACE bitmap;
>> >> >>> >> >> >>                   else:
>> >> >>> >> >> >>                         toDeny = the ACE bitmap;
>> >> >>> >> >> >>                   toGrant = toGrant & ~denies;
>> >> >>> >> >> >>                   toDeny = toDeny & ~grants;
>> >> >>> >> >> >>                   grants = grants | toGrant;
>> >> >>> >> >> >>                   denied = denies | toDenies;
>> >> >>> >> >> >>       return (grants, denies);
>> >> >>> >> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> > - Can you please tell me how to calculate the ACE bitmap ?
>> >> >>> >> >>
>> >> >>> >> >> That is just the 32bit integer stored in the cassandra column
>> >> >>> >> >> representing the grant or deny for the principal
>> >> >>> >> >>
>> >> >>> >> >> eg
>> >> >>> >> >> Cassandra rowID : base64(sha1(/content/cassandra/foo/bar ))
>> >> >>> >> >> Columns: 0_everyone_allow : 0x01, 1_admin_allow : 0x07
>> >> >>> >> >>
>> >> >>> >> >> Allows everyone read and admin everything at the path
>> /cassandra
>> >> >>> >> >>
>> >> >>> >> >> > - Also I will be more clear if you can provide a sample
>> value
>> >> for
>> >> >>> the
>> >> >>> >> >> input
>> >> >>> >> >> > and output of this function ? i.e When currentPath=
>> >> >>> >> >> > /content/cassandra/foo/bar  it returns grants=? denies=?
>> some
>> >> >>> actual
>> >> >>> >> >> values
>> >> >>> >> >> > just for my understanding.
>> >> >>> >> >>
>> >> >>> >> >> Using the above for a random user (ie has the everyone
>> >> principal):
>> >> >>> >> >> grants = 0x01
>> >> >>> >> >> denies - 0x0
>> >> >>> >> >>
>> >> >>> >> >> For the following values
>> >> >>> >> >> Cassandra rowID : base64(sha1(/content/cassandra/foo/bar ))
>> >> >>> >> >> Columns: 0_everyone_allow : 0x01, 1_admin_allow : 0x07,
>> >> 2_ieb_deny :
>> >> >>> >> 0x01
>> >> >>> >> >>
>> >> >>> >> >> results in
>> >> >>> >> >> dishara : grants = 0x01, denies = 0x00
>> >> >>> >> >> ieb : grants = 0x00, denies = 0x01
>> >> >>> >> >> admin: grants = 0x07, denies = 0x00
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >> >> To combine what is granted at the child level with what is
>> >> >>> granted at
>> >> >>> >> >> >> a parent level we need to mask the parent level with the
>> deny
>> >> at
>> >> >>> the
>> >> >>> >> >> >> child level.
>> >> >>> >> >> >>
>> >> >>> >> >> >> eg
>> >> >>> >> >> >>          toGrant = grantedAtParent & ~denies;
>> >> >>> >> >> >>          toDeny = deniedAtParent & ~grants;
>> >> >>> >> >> >>          grants = grants | toGrant;
>> >> >>> >> >> >>          denied = denies | toDenies;
>> >> >>> >> >> >>
>> >> >>> >> >> >> The simplest way of achieving this is to use recursion
>> again
>> >> in
>> >> >>> >> pseudo
>> >> >>> >> >> >> code:
>> >> >>> >> >> >>
>> >> >>> >> >> >>    function buildAtLevel():
>> >> >>> >> >> >>             if not root level:
>> >> >>> >> >> >>                  (grantedAtParent, deniedAtParent) =
>> >> >>> >> >> >> buildAtLevel(getParentLevel(currentLevel));
>> >> >>> >> >> >>             (grants, denies) =
>> >> >>> getCurrentLevelBitmaps(currentLevel);
>> >> >>> >> >> >>             toGrant = grantedAtParent & ~denies;
>> >> >>> >> >> >>            toDeny = deniedAtParent & ~grants;
>> >> >>> >> >> >>            grants = grants | toGrant;
>> >> >>> >> >> >>            denied = denies | toDenies;
>> >> >>> >> >> >>            return (grants, denied);
>> >> >>> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >> >> There are some optimisations you can apply here, and there
>> are
>> >> >>> plenty
>> >> >>> >> >> >> of opportunities to cache intermediate bitmaps in memory.
>> Just
>> >> >>> >> caching
>> >> >>> >> >> >> the ACL reduces resolution to bitwise operations.
>> >> >>> >> >> >>
>> >> >>> >> >> >> Principals
>> >> >>> >> >> >> ----------------
>> >> >>> >> >> >> Initially keep it simple.
>> >> >>> >> >> >>
>> >> >>> >> >> >> read = 0x01
>> >> >>> >> >> >> write = 0x02
>> >> >>> >> >> >> delete = 0x04
>> >> >>> >> >> >>
>> >> >>> >> >> >> Storage of ACLs.
>> >> >>> >> >> >> -------------------------
>> >> >>> >> >> >> I suggest you store ACLs in their own Column Family, where
>> the
>> >> >>> rowID
>> >> >>> >> is
>> >> >>> >> >> >> base64(sha1(path)) or whatever path -> rowid encoding you
>> have
>> >> >>> >> >> currently.
>> >> >>> >> >> >>
>> >> >>> >> >> >> IIRC Cassandra columns come out in the natural order of
>> >> Strings
>> >> >>> >> >> >> <order>_<principal>_<allow|deny> and the value is the
>> bitmap
>> >> of
>> >> >>> >> >> >> permissions.
>> >> >>> >> >> >>
>> >> >>> >> >> >> - If I understand you correctly is
>> >> >>> >> <order>_<principal>_<allow|deny> is
>> >> >>> >> >> > one ACE ?
>> >> >>> >> >>
>> >> >>> >> >> yes
>> >> >>> >> >>
>> >> >>> >> >> If per row there can be a ACL, there should be one additional
>> >> >>> >> >> > column by default called "ACL" and it will have a comma
>> >> separated
>> >> >>> >> string
>> >> >>> >> >> > which are set of ACEs.
>> >> >>> >> >>
>> >> >>> >> >> not necessary as the order of columns is returned in the
>> natural
>> >> >>> >> >> string sort order (IIRC) so 0_* will be the first followed by
>> 1_
>> >> >>> >> >>
>> >> >>> >> >> However, if thats not the case a column called ACL will be
>> >> required
>> >> >>> to
>> >> >>> >> >> list the ACLs in order.
>> >> >>> >> >>
>> >> >>> >> >>  Correct me if I am wrong.
>> >> >>> >> >> > - Who stores these ACEs ? any API?
>> >> >>> >> >>
>> >> >>> >> >> Not determined yet. It would need an API able to modify ACLs
>> >> >>> >> >>
>> >> >>> >> >
>> >> >>> >> > OK, I am trying to understand. Can you please clarify
>> following.
>> >> >>> >> >
>> >> >>> >> > To start with, as I feel I should start storing data to
>> cassandra
>> >> >>> with an
>> >> >>> >> > updated column which stores a string which is a comma separated
>> >> ACE
>> >> >>> list.
>> >> >>> >> >  i.e when the provider stores a resource at
>> >> >>> /content/cassandra/foo/bar
>> >> >>> >> the
>> >> >>> >> > ACL column can be empty. Does permissions added upon node
>> >> creation ?
>> >> >>> Or
>> >> >>> >> > there is some interface,and when it get called,
>> (path,ACE-list) I
>> >> >>> will
>> >> >>> >> > update the row with the new value for the ACL column. If that
>> is
>> >> the
>> >> >>> case
>> >> >>> >> > permission should be granted to a path only of the path
>> exists. OR
>> >> >>> upon
>> >> >>> >> > node creation does  it suppose to store parent resolved ACE
>> list
>> >> by
>> >> >>> >> > default. If that is so it will be more complex for Provider and
>> >> have
>> >> >>> to
>> >> >>> >> > change that to do so.
>> >> >>> >> >
>> >> >>> >> >
>> >> >>> >> >
>> >> >>> >> >> > - i.e <order> is a auto increment number we have to do a
>> >> >>> additional
>> >> >>> >> read
>> >> >>> >> >> > before storing ACE to check what is the last number for
>> >> <order>.
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >> Ok, that indicates having an ACE column might be better, which
>> >> would
>> >> >>> >> >> indicate that no count is required.
>> >> >>> >> >>
>> >> >>> >> >> Best Regards
>> >> >>> >> >> Ian
>> >> >>> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> > Where <order> is 000 to 999 ( I really doubt that a single
>> ACL
>> >> >>> will
>> >> >>> >> >> >> have 1000 ACEs ever)
>> >> >>> >> >> >>
>> >> >>> >> >> >> Once you have this working, we can wire it into the
>> >> >>> ResourceProvider
>> >> >>> >> >> >> or another Sling API.
>> >> >>> >> >> >>
>> >> >>> >> >> >> Does that make sense ?
>> >> >>> >> >> >> Ian
>> >> >>> >> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >> > --
>> >> >>> >> >> > Thanks
>> >> >>> >> >> > /Dishara
>> >> >>> >> >>
>> >> >>> >> >
>> >> >>> >> >
>> >> >>> >> >
>> >> >>> >> > --
>> >> >>> >> > Thanks
>> >> >>> >> > /Dishara
>> >> >>> >>
>> >> >>> >
>> >> >>> >
>> >> >>> >
>> >> >>> > --
>> >> >>> > Thanks
>> >> >>> > /Dishara
>> >> >>>
>> >> >>
>> >> >>
>> >> >>
>> >> >> --
>> >> >> Thanks
>> >> >> /Dishara
>> >> >>
>> >> >
>> >> >
>> >> >
>> >> > --
>> >> > Thanks
>> >> > /Dishara
>> >>
>> >
>> >
>> >
>> > --
>> > Thanks
>> > /Dishara
>>
>
>
>
> --
> Thanks
> /Dishara

Reply via email to