Andrew Baumann wrote:
> Hi,
>=20
> On Sunday 10 September 2006 20:04, Armin Bauer wrote:
>> Lets say we write a "categories" filter, which only allows changes whi=
ch
>> match a given category. So a module registers this filter with the nam=
e
>> "categories". Then a user decides, he wants to use this filter. So in
>> the member config, there should be a entry:
>>
>> <config>
>> ...
>> <filter>
>> <name>categories</name>
>> <rules>
>> Information about how the filter should be applied (which objtypes,=
>> direction etc)
>> </rules>
>> <config>PRIVATE,BUSINESS</config>
>> <action>DROP</action>
>> </filter>
>> </config>
>>
>> This entry tell opensync to use the categories filter, look at the rul=
es
>> to see if a object matches, and if it matches it calls the filter with=
>> the config given. If the filter returns TRUE (=3D=3D filter matches), =
the
>> action is executed.
>=20
> Ok, that sounds fair enough. But do you really want the filter function=
to be=20
> parsing the configuration string each time? I would expect that filters=
, like=20
> other plugins, get their configuration data once at load time, parse it=
into=20
> some internal format, and then get invoked with that each time. The cos=
t of=20
> parsing a configuration string that could be XML or whatever seems too =
high=20
> to be doing it on every filter invocation.
ok. then we would need to have some sort of initialize_filter and
finalize_filter functions. Sounds like a good idea.
>=20
>> Andrew Baumann wrote:
>> This is most likely a bug. Lets look at the categories filter from
>> above. We would probably register this filter for the format
>> "xml-contact" since we need to look at the xml tree to see the
>> categories. If a object gets passed to use with a different format, t=
he
>> filter would probably crash.
>=20
> Well, the format is specified again later in osync_filter_add_custom, a=
nd this=20
> one is used.=20
>=20
>> So that NULL works for you is probably luck and not intended. :)
>>
>> again, see the categories example. the config needs to be a string to =
be
>> able to save it in the member config.
>>
>> As i understand you, you want to pass around a pointer to a object. I
>> think for this we can add a user_data pointer. do you think this would=
>> work for you?
>=20
> Yes, that would be fine for me. However I'd also need to get a callback=
when=20
> the filter is removed to be able to free the pointer, unless opensync=20
> requires that only the plugin/member that installed a filter will remov=
e it.
this could be done in finalize_filter
>=20
>> Maybe it could work like this:
>>
>> in the python module, opensync calls initialize. then the module loads=
>> all python scripts using the register_plugin function. I think this
>> should be changed so that it either registers a plugin or optionally a=
>> filter (using a get_filter function in the python script).
>=20
> That sounds good, except I'd want to allow a module to register a plugi=
n and=20
> multiple filters.
>=20
> Let me explain where I'm coming from: in moto-sync there are some entri=
es that=20
> we can't store on the phone, for example a lot of recurring events can'=
t be=20
> represented with the correct recurrence. I want to have a filter to rem=
ove=20
> these so that opensync doesn't try to write them to the phone. This fil=
ter=20
> wouldn't be configured separately to moto-sync, and it's not really=20
> optional -- it would always be installed by the moto-sync plugin.
>=20
> I could implement the same thing in the plugin directly (ie. just ignor=
e=20
> objects that can't be handled), but this will confuse opensync the next=
time=20
> we sync and that object is gone. I was hoping/expecting that by using a=
=20
> filter, opensync would remember that the object had been filtered out, =
and=20
> not expect to see it on the next run. Is that actually true? Is there a=
way=20
> of reporting the same thing to opensync from within the plugin's=20
> commit_change method? In that case I wouldn't need to bother with filte=
rs at=20
> all.
when exactly would opensync expect this object the next time?
in the case of a fast-sync, you just dont report the object anymore.
in the case of a slow-sync, opensync would not be able to read the
object. so it would try to write it again, which would again be filtered.=
>=20
>> The name of the filter should not be set to "python filter wrapper"
>> since this would only allow a single filter in python to be registered=
=2E
>> The name has to be set by the python script.
>=20
> No, that's not true. I'm running multiple python filters through the si=
ngle=20
> wrapper function. I call osync_env_register_filter_function once to reg=
ister=20
> the wrapper function with that name, and then later I can call=20
> osync_filter_add_custom as many times as I like, and give each instance=
of=20
> the "python filter wrapper" a different config string.
>=20
> Maybe this isn't what you intended, but it's what the code does at the =
moment.=20
> As I said, it doesn't make a lot of sense to me that we first register =
the=20
> filter function by name, and then later create multiple filters for tha=
t=20
> function using its name.
Ok. i see that i can work like this. but this is really not what i
intended :)
the name is just used so that we can save the name in the member config
and later know again which filter to instanciate.
>=20
>> So the initialize process of the filter should be similar to the plugi=
ns
>> (if we add the user_data pointer to env_register_filter_function).
>=20
> See above -- I think the filter should be given its config string (if o=
ne=20
> exists) and be able to parse it into a user_data pointer. This should h=
appen=20
> only once when the filter is installed/registered/whatever.
>=20
>> maybe we should seperate the matching filters and the actions. So that=
>> you can register a filter which returns TRUE or FALSE by looking at th=
e
>> data and actions. There would be default actions coming with opensync
>> like ALLOW, DROP, etc. But if you want to modify the data through the
>> filter, you can write your own action.
>=20
> Hmm, what is an action, if not just a change to the contents of the ch=
ange=20
> object? On IRC you told me:
> <abauer> abtronic: you need to write a custom filter which triggers on=
all=20
> changes going to your plugin. then you need to check the recurrence
> <abauer> abtronic: if it should be deleted, you need to replace the ch=
ange=20
> with a deleted change
> If a filter can do this already (replace the change), I'm not sure ther=
e's a=20
> need for separate actions. Of course it would probably be good for code=
reuse=20
> if the actions gets more complicated, I don't mind if you want to do th=
is.
The idea is just to seperate the detection and the action. For example:
you would require a filter to detect if the change has a recurrence.
Your action would be to delete (or drop) it on the device (not on all
devices!). The action will probably also be useful to other people as
well, so it could be provided with opensync itself.
>=20
> =3D=3D=3D
>=20
> I think the cleanest model would be something where filters are a more =
> fundamental part of opensync like plugins. Let's say that each filter i=
s=20
> registered with a couple of functions (the names/variables are somewhat=
=20
> arbitrary here, I'm just trying to give you the idea):
>=20
> void osync_filter_set_userdata(OSyncFilter *filter, void *data);
> void osync_filter_get_userdata(OSyncFilter *filter, void *data);
>=20
> void osync_filter_set_config(OSyncFilter *filter, const char *data);
> void osync_filter_get_config(OSyncFilter *filter, const char *data);
>=20
> typedef OSyncFilterAction (*OSyncFilterFunction)(OSyncFilter*, OSyncCha=
nge*);
> typedef void (*OSyncFilterParseConfig)(OSyncFilter *);
> typedef void (*OSyncFilterCleanup)(OSyncFilter *);
>=20
> osync_bool osync_filter_register_function(const char *name,=20
> OSyncFilterFunction, OSyncFilterParseConfig, OSyncFilterCleanup);
> OSyncFilter *osync_filter_add_custom(OSyncGroup *group,
> OSyncMember *sourcemember, OSyncMember *destmember,
> const char *sourceobjtype, const char *destobjtype,
> const char *detectobjtype, const char *function_name);
>=20
> The OSyncFilterParseConfig functions would be expected to parse the con=
fig and=20
> call the osync_filter_set_userdata function on the filter. Because the =
filter=20
> object itself is passed to the filter function, we could store other th=
ings=20
> in there that might be useful (like the config data pointer, or the=20
> OSyncPlugin that loaded it).
>=20
> If opensync was loading a filter automatically because the user had it =
> configured for that sync group, it would do a call to osync_filter_add_=
custom=20
> followed by osync_filter_set_config (which would call the=20
> OSyncFilterParseConfig function if one was registered for that function=
).
>=20
> If I (as a plugin writer) need to load a special filter as part of my p=
lugin,=20
> I'd do something like:
> osync_filter_register_function("some name", function, NULL, cleanup_fun=
ction);
> f =3D osync_filter_add_custom(mygroup, NULL, mymember, NULL, myobjtype,=
=20
> NULL, "some name");
> osync_filter_set_userdata(f, my data pointer);
> ... so it's desirable that osync_filter_register_function be able to be=
called=20
> later than plugin load time.
>=20
> Does this make sense? Do you understand where I'm coming from? Maybe fi=
lters=20
> aren't even the right thing for what I want to do, but I think the curr=
ent=20
> API isn't ideal. IMHO you don't want filter functions parsing textual c=
onfig=20
> strings each time they're invoked, and the current setup requires this.=
agreed. yes i think i understand what your trying to accomplish. The
only part im not yet sure about is: Your plugin always requires this
filter to be present to be working. So i have to give a option to tell
opensync during initialize, which filters should be loaded (together
with a optional config string).
Armin
>=20
> Cheers,
> Andrew
>=20
|