c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen#ifndef MAIL_NAMESPACE_H
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen#define MAIL_NAMESPACE_H
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen#include "mail-user.h"
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstruct mail_storage_callbacks;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainenenum mail_namespace_type {
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen MAIL_NAMESPACE_TYPE_PRIVATE = 0x01,
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen MAIL_NAMESPACE_TYPE_SHARED = 0x02,
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen MAIL_NAMESPACE_TYPE_PUBLIC = 0x04
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen#define MAIL_NAMESPACE_TYPE_MASK_ALL \
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen (MAIL_NAMESPACE_TYPE_PRIVATE | MAIL_NAMESPACE_TYPE_SHARED | \
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen MAIL_NAMESPACE_TYPE_PUBLIC)
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen};
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
296dca49e4fe6046e0328c67ef1cf4b8077dec9cTimo Sirainenenum namespace_flags {
7fd51f7b0b4d990ec3cfef4e60ee685bf9fb32deTimo Sirainen /* Namespace contains the user's INBOX mailbox. Normally only a single
7fd51f7b0b4d990ec3cfef4e60ee685bf9fb32deTimo Sirainen namespace has this flag set, but when using alias_for for the INBOX
7fd51f7b0b4d990ec3cfef4e60ee685bf9fb32deTimo Sirainen namespace the flag gets copied to the alias namespace as well */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_INBOX_USER = 0x01,
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen /* Namespace contains someone's INBOX. This is set for both user's
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen INBOX namespace and also for any other users' shared namespaces. */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_INBOX_ANY = 0x02,
296dca49e4fe6046e0328c67ef1cf4b8077dec9cTimo Sirainen /* Namespace is visible only by explicitly using its full prefix */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_HIDDEN = 0x04,
4ac2d38239cea8090154e17faefd77de5a71d882Timo Sirainen /* Namespace prefix is visible with LIST */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_LIST_PREFIX = 0x08,
4ac2d38239cea8090154e17faefd77de5a71d882Timo Sirainen /* Namespace prefix isn't visible with LIST, but child mailboxes are */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_LIST_CHILDREN = 0x10,
32e1554df9abca74fef0af2ba2e4c37e90a06cd0Timo Sirainen /* Namespace uses its own subscriptions. */
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen NAMESPACE_FLAG_SUBSCRIPTIONS = 0x20,
32e1554df9abca74fef0af2ba2e4c37e90a06cd0Timo Sirainen
a988c3fd9251806e38931a011aaa4006dd081cbdTimo Sirainen /* Namespace was created automatically (for shared mailboxes) */
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen NAMESPACE_FLAG_AUTOCREATED = 0x1000,
b337d3b6871b878d6467d7d8ed600433af5da5a1Timo Sirainen /* Namespace has at least some usable mailboxes. Autocreated namespaces
b337d3b6871b878d6467d7d8ed600433af5da5a1Timo Sirainen that don't have usable mailboxes may be removed automatically. */
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen NAMESPACE_FLAG_USABLE = 0x2000,
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen /* Automatically created namespace for a user that doesn't exist. */
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen NAMESPACE_FLAG_UNUSABLE = 0x4000,
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen /* Don't track quota for this namespace */
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen NAMESPACE_FLAG_NOQUOTA = 0x8000,
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen /* Don't enforce ACLs for this namespace */
16db188cfddce117500a161302f17ae691b4500eTimo Sirainen NAMESPACE_FLAG_NOACL = 0x10000
296dca49e4fe6046e0328c67ef1cf4b8077dec9cTimo Sirainen};
296dca49e4fe6046e0328c67ef1cf4b8077dec9cTimo Sirainen
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenstruct mail_namespace {
2b95b7a9f4f06e7640ef431d9e6efc2423cacf1aTimo Sirainen /* Namespaces are sorted by their prefix length, "" comes first */
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen struct mail_namespace *next;
6145bd3b17b9135b77b0b42409a0cc3fa0d1b946Timo Sirainen int refcount;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen enum mail_namespace_type type;
296dca49e4fe6046e0328c67ef1cf4b8077dec9cTimo Sirainen enum namespace_flags flags;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen char *prefix;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen size_t prefix_len;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen /* If non-NULL, this points to a namespace with identical mail location
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen and it should be considered as the primary way to access the
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen mailboxes. This allows for example FTS plugin to avoid duplicating
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen indexes for same mailboxes when they're accessed via different
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen namespaces. */
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen struct mail_namespace *alias_for;
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen /* alias_for->alias_chain_next starts each chain. The chain goes
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen through all namespaces that have the same alias_for. */
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen struct mail_namespace *alias_chain_next;
af3f857bb3166ed99595e11a9d18e5b5cc670e1aTimo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen struct mail_user *user, *owner;
c58906589cafc32df4c04ffbef933baadd3f2276Timo Sirainen struct mailbox_list *list;
47ede56f4e6eebfe631a1f0febf74d7adcdbcd00Timo Sirainen struct mail_storage *storage; /* default storage */
47ede56f4e6eebfe631a1f0febf74d7adcdbcd00Timo Sirainen ARRAY(struct mail_storage *) all_storages;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen /* This may point to user->set, but it may also point to
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen namespace-specific settings. When accessing namespace-specific
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen settings it should be done through here instead of through the
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen mail_user. */
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen struct mail_user_settings *user_set;
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen
c396c5cdd510d09aa35875ccfd643c5c21ed1f89Timo Sirainen const struct mail_namespace_settings *set, *unexpanded_set;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const struct mail_storage_settings *mail_set;
6145bd3b17b9135b77b0b42409a0cc3fa0d1b946Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool special_use_mailboxes:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool destroyed:1;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen};
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen/* Allocate a new namespace, and fill it based on the passed in settings.
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen This is the most low-level namespace creation function. The storage isn't
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen initialized for the namespace.
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen user_all_settings normally points to user->set. If you want to override
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen settings for the created namespace, you can duplicate the user's settings
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen and provide a pointer to it here. Note that the pointer must contain
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen ALL the settings, including the dynamic driver-specific settings, so it
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen needs to created via settings-parser API. */
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainenint mail_namespace_alloc(struct mail_user *user,
5b82f3b2f544cf891a390083f1bcf60409be20b8Timo Sirainen void *user_all_settings,
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen struct mail_namespace_settings *ns_set,
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen struct mail_namespace_settings *unexpanded_set,
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen struct mail_namespace **ns_r,
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen const char **error_r);
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen/* Add and initialize namespaces to user based on namespace settings. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenint mail_namespaces_init(struct mail_user *user, const char **error_r);
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen/* Add and initialize INBOX namespace to user based on the given location. */
d9250ee7e2815bb2116134b58f7c860578148d6cTimo Sirainenint mail_namespaces_init_location(struct mail_user *user, const char *location,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen const char **error_r) ATTR_NULL(2);
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen/* Add an empty namespace to user. */
092018b35bb1dc5bd61848a38189fe6ac8f791ddTimo Sirainenstruct mail_namespace *mail_namespaces_init_empty(struct mail_user *user);
7327394e30c1020b9a2a49c72a7e3d0f7803e680Timo Sirainen/* Deinitialize all namespaces. mail_user_deinit() calls this automatically
7327394e30c1020b9a2a49c72a7e3d0f7803e680Timo Sirainen for user's namespaces. */
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenvoid mail_namespaces_deinit(struct mail_namespace **namespaces);
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen/* Allocate a new namespace and initialize it. This is called automatically by
aafbaa45a60ec50815e0c985690a828617013a8fTimo Sirainen mail_namespaces_init(). */
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainenint mail_namespaces_init_add(struct mail_user *user,
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainen struct mail_namespace_settings *ns_set,
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainen struct mail_namespace_settings *unexpanded_ns_set,
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainen struct mail_namespace **ns_p, const char **error_r);
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainenint mail_namespaces_init_finish(struct mail_namespace *namespaces,
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainen const char **error_r);
90804278df6586cceaf1b1b07a44713c01694048Timo Sirainen
6145bd3b17b9135b77b0b42409a0cc3fa0d1b946Timo Sirainenvoid mail_namespace_ref(struct mail_namespace *ns);
6145bd3b17b9135b77b0b42409a0cc3fa0d1b946Timo Sirainenvoid mail_namespace_unref(struct mail_namespace **ns);
6145bd3b17b9135b77b0b42409a0cc3fa0d1b946Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen/* Set storage callback functions to use in all namespaces. */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_storage_callbacks *callbacks,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen void *context);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen/* Add a new storage to namespace. */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid mail_namespace_add_storage(struct mail_namespace *ns,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_storage *storage);
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen/* Destroy a single namespace and remove it from user's namespaces list. */
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainenvoid mail_namespace_destroy(struct mail_namespace *ns);
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen/* Returns the default storage to use for newly created mailboxes. */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstruct mail_storage *
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenmail_namespace_get_default_storage(struct mail_namespace *ns);
ef50336eefcb9ba99f73c6af37420eaf8857a39bTimo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen/* Return namespace's hierarchy separator. */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenchar mail_namespace_get_sep(struct mail_namespace *ns);
14376e0584c178306c400750352869cf2aaf6feeTimo Sirainen/* Returns the hierarchy separator for mailboxes that are listed at root. */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenchar mail_namespaces_get_root_sep(struct mail_namespace *namespaces)
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen ATTR_PURE;
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen/* Returns namespace based on the mailbox name's prefix. Note that there is
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen always a prefix="" namespace, so for this function NULL is never returned. */
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenstruct mail_namespace *
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenmail_namespace_find(struct mail_namespace *namespaces, const char *mailbox);
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen/* Same as mail_namespace_find(), but if the namespace has alias_for set,
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen return that namespace instead and change mailbox name to be a valid
288d6ef592719f2be3cad9f034e9be05f9839785Timo Sirainen inside it. */
288d6ef592719f2be3cad9f034e9be05f9839785Timo Sirainenstruct mail_namespace *
288d6ef592719f2be3cad9f034e9be05f9839785Timo Sirainenmail_namespace_find_unalias(struct mail_namespace *namespaces,
288d6ef592719f2be3cad9f034e9be05f9839785Timo Sirainen const char **mailbox);
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen/* Like mail_namespace_find(), but ignore hidden namespaces. */
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenstruct mail_namespace *
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenmail_namespace_find_visible(struct mail_namespace *namespaces,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen const char *mailbox);
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen/* Like mail_namespace_find(), but find only from namespaces with
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen subscriptions=yes. */
32e1554df9abca74fef0af2ba2e4c37e90a06cd0Timo Sirainenstruct mail_namespace *
32e1554df9abca74fef0af2ba2e4c37e90a06cd0Timo Sirainenmail_namespace_find_subscribable(struct mail_namespace *namespaces,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen const char *mailbox);
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen/* Like mail_namespace_find(), but find only from namespaces with
9b706b345064ce8e8a657f54633f009a101298eaTimo Sirainen subscriptions=no. */
a988c3fd9251806e38931a011aaa4006dd081cbdTimo Sirainenstruct mail_namespace *
a988c3fd9251806e38931a011aaa4006dd081cbdTimo Sirainenmail_namespace_find_unsubscribable(struct mail_namespace *namespaces,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen const char *mailbox);
bb1da4a6320eec11890c852e74a08868837e7be3Timo Sirainen/* Returns the INBOX namespace. It always exists, so NULL is never returned. */
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainenstruct mail_namespace *
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainenmail_namespace_find_inbox(struct mail_namespace *namespaces);
9874ad56b94788297fdac4eae7cba5d651b48222Timo Sirainen/* Find a namespace with given prefix. */
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenstruct mail_namespace *
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainenmail_namespace_find_prefix(struct mail_namespace *namespaces,
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen const char *prefix);
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainen/* Like _find_prefix(), but ignore trailing separator */
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainenstruct mail_namespace *
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainenmail_namespace_find_prefix_nosep(struct mail_namespace *namespaces,
957d09e495c33ad1180f82152e5e87e6b51ab04bTimo Sirainen const char *prefix);
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen/* Called internally by mailbox_list_create(). */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenvoid mail_namespace_finish_list_init(struct mail_namespace *ns,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mailbox_list *list);
a3f6d0302a83270253ff9d2ebd4fea0003e9ac95Timo Sirainen
14b551180cb4ac7acac8b048d8d6d7278541d1f6Timo Sirainen/* Returns TRUE if this is the root of a type=shared namespace that is actually
14b551180cb4ac7acac8b048d8d6d7278541d1f6Timo Sirainen used for accessing shared users' mailboxes (as opposed to marking a
14b551180cb4ac7acac8b048d8d6d7278541d1f6Timo Sirainen type=public namespace "wrong"). */
14b551180cb4ac7acac8b048d8d6d7278541d1f6Timo Sirainenbool mail_namespace_is_shared_user_root(struct mail_namespace *ns);
14b551180cb4ac7acac8b048d8d6d7278541d1f6Timo Sirainen
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen/* Returns TRUE if namespace includes INBOX that should be \Noinferiors.
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen This happens when the namespace has a prefix, which is not empty and not
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen "INBOX". This happens, because if storage_name=INBOX/foo it would be
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen converted to vname=prefix/INBOX/foo. */
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainenstatic inline bool
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainenmail_namespace_is_inbox_noinferiors(struct mail_namespace *ns)
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen{
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen return (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen ns->prefix_len > 0 &&
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen strncmp(ns->prefix, "INBOX", ns->prefix_len-1) != 0;
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen}
9a2149cccfab52ca88cd6b5eecec776a0c3584ecTimo Sirainen
e8490a52a1bc71bc53034e68f464435684ad810fTimo Sirainen#endif